mirror of
https://github.com/NationalSecurityAgency/ghidra
synced 2024-10-06 02:09:57 +00:00
GP-2496 edit shared project info improvements
This commit is contained in:
parent
09d326ddbb
commit
52d1097c5b
|
@ -335,6 +335,7 @@ src/main/help/help/topics/FrontEndPlugin/images/ConnectTools.png||GHIDRA||||END|
|
||||||
src/main/help/help/topics/FrontEndPlugin/images/DeleteProject.png||GHIDRA||||END|
|
src/main/help/help/topics/FrontEndPlugin/images/DeleteProject.png||GHIDRA||||END|
|
||||||
src/main/help/help/topics/FrontEndPlugin/images/EditPluginPath.png||GHIDRA||||END|
|
src/main/help/help/topics/FrontEndPlugin/images/EditPluginPath.png||GHIDRA||||END|
|
||||||
src/main/help/help/topics/FrontEndPlugin/images/EditProjectAccessList.png||GHIDRA||||END|
|
src/main/help/help/topics/FrontEndPlugin/images/EditProjectAccessList.png||GHIDRA||||END|
|
||||||
|
src/main/help/help/topics/FrontEndPlugin/images/EditProjectAccessPanel.png||GHIDRA||||END|
|
||||||
src/main/help/help/topics/FrontEndPlugin/images/MemoryUsage.png||GHIDRA||||END|
|
src/main/help/help/topics/FrontEndPlugin/images/MemoryUsage.png||GHIDRA||||END|
|
||||||
src/main/help/help/topics/FrontEndPlugin/images/NonSharedProjectInfo.png||GHIDRA||||END|
|
src/main/help/help/topics/FrontEndPlugin/images/NonSharedProjectInfo.png||GHIDRA||||END|
|
||||||
src/main/help/help/topics/FrontEndPlugin/images/OpenProject.png||GHIDRA||||END|
|
src/main/help/help/topics/FrontEndPlugin/images/OpenProject.png||GHIDRA||||END|
|
||||||
|
@ -361,6 +362,7 @@ src/main/help/help/topics/FrontEndPlugin/images/VersionedFileCOnoServer.png||GHI
|
||||||
src/main/help/help/topics/FrontEndPlugin/images/VersionedFileCOwithServer.png||GHIDRA||||END|
|
src/main/help/help/topics/FrontEndPlugin/images/VersionedFileCOwithServer.png||GHIDRA||||END|
|
||||||
src/main/help/help/topics/FrontEndPlugin/images/VersionedFileIcon.png||GHIDRA||||END|
|
src/main/help/help/topics/FrontEndPlugin/images/VersionedFileIcon.png||GHIDRA||||END|
|
||||||
src/main/help/help/topics/FrontEndPlugin/images/ViewOtherProjects.png||GHIDRA||||END|
|
src/main/help/help/topics/FrontEndPlugin/images/ViewOtherProjects.png||GHIDRA||||END|
|
||||||
|
src/main/help/help/topics/FrontEndPlugin/images/ViewProjectAccessPanel.png||GHIDRA||||END|
|
||||||
src/main/help/help/topics/FrontEndPlugin/images/closedBookBlue.png||GHIDRA||reviewed||END|
|
src/main/help/help/topics/FrontEndPlugin/images/closedBookBlue.png||GHIDRA||reviewed||END|
|
||||||
src/main/help/help/topics/FrontEndPlugin/images/connected.gif||GHIDRA||||END|
|
src/main/help/help/topics/FrontEndPlugin/images/connected.gif||GHIDRA||||END|
|
||||||
src/main/help/help/topics/FrontEndPlugin/images/disconnected.gif||GHIDRA||||END|
|
src/main/help/help/topics/FrontEndPlugin/images/disconnected.gif||GHIDRA||||END|
|
||||||
|
|
|
@ -119,11 +119,20 @@
|
||||||
|
|
||||||
<H2><A name="Change_Shared_Project_Info"></A>Changing Shared Project Information</H2>
|
<H2><A name="Change_Shared_Project_Info"></A>Changing Shared Project Information</H2>
|
||||||
|
|
||||||
|
<P>Changing shared project details may become neccessary when a server's IP address or name
|
||||||
|
has changed. While other cases are supported, these may cause some issues with private and
|
||||||
|
checked-out project files. Any checked-out file which does not match-up properly will be
|
||||||
|
renamed to a private <I>.keep</I> file within the project and a checkin will no longer be
|
||||||
|
possible. In addition, when switching to and a different repository private files may conflict
|
||||||
|
with those in the repository resulting in
|
||||||
|
<A href="Ghidra_Front_end.htm#HijackedFile">hijacked files</A>.
|
||||||
|
|
||||||
<BLOCKQUOTE>
|
<BLOCKQUOTE>
|
||||||
<P>To update repository information:</P>
|
<P>To update repository information:</P>
|
||||||
|
|
||||||
<OL>
|
<OL>
|
||||||
<LI>Close all open files and make sure that all files are checked in.</LI>
|
<LI>Close all open files. Closing all of your active tools (e.g., CodeBrowser) may be
|
||||||
|
the simplest way to accomplish this.</LI>
|
||||||
|
|
||||||
<LI>In <I>Project Information</I>, click on the <B>Change Shared Project Info...</B> button
|
<LI>In <I>Project Information</I>, click on the <B>Change Shared Project Info...</B> button
|
||||||
to start the <I>Change Shared Project Information</I> wizard. </LI>
|
to start the <I>Change Shared Project Information</I> wizard. </LI>
|
||||||
|
@ -201,23 +210,27 @@
|
||||||
and modify user privileges by choosing the <B>Project</B><IMG border="0" src=
|
and modify user privileges by choosing the <B>Project</B><IMG border="0" src=
|
||||||
"../../shared/arrow.gif"><B><A href=
|
"../../shared/arrow.gif"><B><A href=
|
||||||
"Ghidra_Front_end.htm#Edit_Project_Access_List">Edit Project Access List...</A></B> <A
|
"Ghidra_Front_end.htm#Edit_Project_Access_List">Edit Project Access List...</A></B> <A
|
||||||
href="Ghidra_Front_end.htm">option</A>. </P>
|
href="Ghidra_Front_end.htm"></A> option. </P>
|
||||||
</BLOCKQUOTE>
|
</BLOCKQUOTE>
|
||||||
|
|
||||||
<OL start="8">
|
<OL start="8">
|
||||||
<LI>Select the <B>Finish</B> button. </LI>
|
<LI>Select the <B>Finish</B> button. </LI>
|
||||||
|
|
||||||
<LI><A name="Step9"></A>A confirmation dialog is displayed; select the <B>Update</B> button
|
<LI><A name="Step9"></A>A confirmation dialog is displayed; select the <B>Update</B> button
|
||||||
to complete the <I>Change Shared Project Information</I> process.</LI>
|
to start the <I>Change Shared Project Information</I> process.</LI>
|
||||||
</OL>
|
</OL>
|
||||||
</BLOCKQUOTE>
|
</BLOCKQUOTE>
|
||||||
|
|
||||||
<BLOCKQUOTE>
|
<BLOCKQUOTE>
|
||||||
<P><IMG border="0" src="../../shared/note.png"> After you have updated
|
<P><IMG border="0" src="../../shared/warning.png">
|
||||||
your project information, you may end up with <A href=
|
If one or more checked-out files do not match-up properly with the new repository you will
|
||||||
"Ghidra_Front_end.htm#HijackedFile">hijacked files</A> if a file of the same name exists in
|
be prompted to allow these checkouts to be terminated and converted to private <I>.keep</I>
|
||||||
the repository. </P>
|
files. Such file conversion will prevent such files from ever being checked-in and
|
||||||
</BLOCKQUOTE>
|
should be avoided when possible. Click <B>Terminate Checkouts and Continue</B> to proceed
|
||||||
|
with change or <B>Cancel</B> to abort change. The conversion of these files to private .keep
|
||||||
|
files can not be undone.
|
||||||
|
</P>
|
||||||
|
</BLOCKQUOTE>
|
||||||
|
|
||||||
<H2> </H2>
|
<H2> </H2>
|
||||||
|
|
||||||
|
@ -225,8 +238,12 @@
|
||||||
|
|
||||||
<BLOCKQUOTE>
|
<BLOCKQUOTE>
|
||||||
<P>The image below shows project information for a project that is not shared. Note that the
|
<P>The image below shows project information for a project that is not shared. Note that the
|
||||||
repository information is disabled. Before you can convert your project, you must first close
|
repository information will not be displayed for a private project. If repository information
|
||||||
any files that you have opened, and check in any files that you have checked out.</P>
|
is displayed this is already a shared project.</P>
|
||||||
|
|
||||||
|
<P>Before you can convert your project, you must first close
|
||||||
|
any files that you have opened. Closing all of your active tools (e.g., CodeBrowser) may be
|
||||||
|
the simplest way to accomplish this.</P>
|
||||||
|
|
||||||
<P><IMG border="0" src="../../shared/warning.png"> You will lose all
|
<P><IMG border="0" src="../../shared/warning.png"> You will lose all
|
||||||
version history for files under local <A href=
|
version history for files under local <A href=
|
||||||
|
@ -241,7 +258,7 @@
|
||||||
|
|
||||||
<BLOCKQUOTE>
|
<BLOCKQUOTE>
|
||||||
<P>The steps to converting your project are the same as those described for <A href=
|
<P>The steps to converting your project are the same as those described for <A href=
|
||||||
"#Change_Shared_Project_Info">changing your repository information</A>. However, at <A href=
|
"#Change_Shared_Project_Info">Change Shared Project Information</A>. However, at <A href=
|
||||||
"#Step9">Step 9</A> above you will get a dialog that warns you about losing version history
|
"#Step9">Step 9</A> above you will get a dialog that warns you about losing version history
|
||||||
on versioned files. From the warning dialog, click on the <B>Convert</B> button to complete
|
on versioned files. From the warning dialog, click on the <B>Convert</B> button to complete
|
||||||
the conversion process. </P>
|
the conversion process. </P>
|
||||||
|
|
Binary file not shown.
After Width: | Height: | Size: 28 KiB |
Binary file not shown.
After Width: | Height: | Size: 15 KiB |
|
@ -256,6 +256,7 @@ public interface RepositoryHandle {
|
||||||
* @param parentPath parent folder path
|
* @param parentPath parent folder path
|
||||||
* @param itemName name of item
|
* @param itemName name of item
|
||||||
* @return checkout data list
|
* @return checkout data list
|
||||||
|
* @throws FileNotFoundException if folder item not found
|
||||||
* @throws IOException if an IO error occurs
|
* @throws IOException if an IO error occurs
|
||||||
*/
|
*/
|
||||||
ItemCheckoutStatus[] getCheckouts(String parentPath, String itemName) throws IOException;
|
ItemCheckoutStatus[] getCheckouts(String parentPath, String itemName) throws IOException;
|
||||||
|
|
|
@ -379,6 +379,11 @@ public class DomainFileProxy implements DomainFile {
|
||||||
throw new UnsupportedOperationException("undoCheckout() unsupported for DomainFileProxy");
|
throw new UnsupportedOperationException("undoCheckout() unsupported for DomainFileProxy");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void undoCheckout(boolean keep, boolean force) throws IOException {
|
||||||
|
throw new UnsupportedOperationException("undoCheckout() unsupported for DomainFileProxy");
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public ChangeSet getChangesByOthersSinceCheckout() throws IOException {
|
public ChangeSet getChangesByOthersSinceCheckout() throws IOException {
|
||||||
return null;
|
return null;
|
||||||
|
|
|
@ -28,7 +28,6 @@ import ghidra.util.InvalidNameException;
|
||||||
import ghidra.util.ReadOnlyException;
|
import ghidra.util.ReadOnlyException;
|
||||||
import ghidra.util.exception.*;
|
import ghidra.util.exception.*;
|
||||||
import ghidra.util.task.TaskMonitor;
|
import ghidra.util.task.TaskMonitor;
|
||||||
import ghidra.util.task.TaskMonitorAdapter;
|
|
||||||
|
|
||||||
public class GhidraFile implements DomainFile {
|
public class GhidraFile implements DomainFile {
|
||||||
|
|
||||||
|
@ -90,6 +89,7 @@ public class GhidraFile implements DomainFile {
|
||||||
/**
|
/**
|
||||||
* Reassign a new file-ID to resolve file-ID conflict.
|
* Reassign a new file-ID to resolve file-ID conflict.
|
||||||
* Conflicts can occur as a result of a cancelled check-out.
|
* Conflicts can occur as a result of a cancelled check-out.
|
||||||
|
* @throws IOException if an IO error occurs
|
||||||
*/
|
*/
|
||||||
void resetFileID() throws IOException {
|
void resetFileID() throws IOException {
|
||||||
getFileData().resetFileID();
|
getFileData().resetFileID();
|
||||||
|
@ -176,21 +176,21 @@ public class GhidraFile implements DomainFile {
|
||||||
public DomainObject getDomainObject(Object consumer, boolean okToUpgrade, boolean okToRecover,
|
public DomainObject getDomainObject(Object consumer, boolean okToUpgrade, boolean okToRecover,
|
||||||
TaskMonitor monitor) throws VersionException, IOException, CancelledException {
|
TaskMonitor monitor) throws VersionException, IOException, CancelledException {
|
||||||
return getFileData().getDomainObject(consumer, okToUpgrade, okToRecover,
|
return getFileData().getDomainObject(consumer, okToUpgrade, okToRecover,
|
||||||
monitor != null ? monitor : TaskMonitorAdapter.DUMMY_MONITOR);
|
monitor != null ? monitor : TaskMonitor.DUMMY);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public DomainObject getReadOnlyDomainObject(Object consumer, int version, TaskMonitor monitor)
|
public DomainObject getReadOnlyDomainObject(Object consumer, int version, TaskMonitor monitor)
|
||||||
throws VersionException, IOException, CancelledException {
|
throws VersionException, IOException, CancelledException {
|
||||||
return getFileData().getReadOnlyDomainObject(consumer, version,
|
return getFileData().getReadOnlyDomainObject(consumer, version,
|
||||||
monitor != null ? monitor : TaskMonitorAdapter.DUMMY_MONITOR);
|
monitor != null ? monitor : TaskMonitor.DUMMY);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public DomainObject getImmutableDomainObject(Object consumer, int version, TaskMonitor monitor)
|
public DomainObject getImmutableDomainObject(Object consumer, int version, TaskMonitor monitor)
|
||||||
throws VersionException, IOException, CancelledException {
|
throws VersionException, IOException, CancelledException {
|
||||||
return getFileData().getImmutableDomainObject(consumer, version,
|
return getFileData().getImmutableDomainObject(consumer, version,
|
||||||
monitor != null ? monitor : TaskMonitorAdapter.DUMMY_MONITOR);
|
monitor != null ? monitor : TaskMonitor.DUMMY);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -205,7 +205,7 @@ public class GhidraFile implements DomainFile {
|
||||||
if (isReadOnly()) {
|
if (isReadOnly()) {
|
||||||
throw new ReadOnlyException("Cannot save to read-only file");
|
throw new ReadOnlyException("Cannot save to read-only file");
|
||||||
}
|
}
|
||||||
dobj.save(null, monitor != null ? monitor : TaskMonitorAdapter.DUMMY_MONITOR);
|
dobj.save(null, monitor != null ? monitor : TaskMonitor.DUMMY);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -428,21 +428,21 @@ public class GhidraFile implements DomainFile {
|
||||||
public boolean checkout(boolean exclusive, TaskMonitor monitor) throws IOException,
|
public boolean checkout(boolean exclusive, TaskMonitor monitor) throws IOException,
|
||||||
CancelledException {
|
CancelledException {
|
||||||
return getFileData().checkout(exclusive,
|
return getFileData().checkout(exclusive,
|
||||||
monitor != null ? monitor : TaskMonitorAdapter.DUMMY_MONITOR);
|
monitor != null ? monitor : TaskMonitor.DUMMY);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void checkin(CheckinHandler checkinHandler, boolean okToUpgrade, TaskMonitor monitor)
|
public void checkin(CheckinHandler checkinHandler, boolean okToUpgrade, TaskMonitor monitor)
|
||||||
throws IOException, VersionException, CancelledException {
|
throws IOException, VersionException, CancelledException {
|
||||||
getFileData().checkin(checkinHandler, okToUpgrade,
|
getFileData().checkin(checkinHandler, okToUpgrade,
|
||||||
monitor != null ? monitor : TaskMonitorAdapter.DUMMY_MONITOR);
|
monitor != null ? monitor : TaskMonitor.DUMMY);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void merge(boolean okToUpgrade, TaskMonitor monitor) throws IOException,
|
public void merge(boolean okToUpgrade, TaskMonitor monitor) throws IOException,
|
||||||
VersionException, CancelledException {
|
VersionException, CancelledException {
|
||||||
getFileData().merge(okToUpgrade,
|
getFileData().merge(okToUpgrade,
|
||||||
monitor != null ? monitor : TaskMonitorAdapter.DUMMY_MONITOR);
|
monitor != null ? monitor : TaskMonitor.DUMMY);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -450,6 +450,11 @@ public class GhidraFile implements DomainFile {
|
||||||
getFileData().undoCheckout(keep, false);
|
getFileData().undoCheckout(keep, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void undoCheckout(boolean keep, boolean force) throws IOException {
|
||||||
|
getFileData().undoCheckout(keep, force, false);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void terminateCheckout(long checkoutId) throws IOException {
|
public void terminateCheckout(long checkoutId) throws IOException {
|
||||||
getFileData().terminateCheckout(checkoutId);
|
getFileData().terminateCheckout(checkoutId);
|
||||||
|
@ -486,7 +491,7 @@ public class GhidraFile implements DomainFile {
|
||||||
CancelledException {
|
CancelledException {
|
||||||
GhidraFolder newGhidraParent = (GhidraFolder) newParent; // assumes single implementation
|
GhidraFolder newGhidraParent = (GhidraFolder) newParent; // assumes single implementation
|
||||||
return getFileData().copyTo(newGhidraParent.getFolderData(),
|
return getFileData().copyTo(newGhidraParent.getFolderData(),
|
||||||
monitor != null ? monitor : TaskMonitorAdapter.DUMMY_MONITOR);
|
monitor != null ? monitor : TaskMonitor.DUMMY);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -494,17 +499,19 @@ public class GhidraFile implements DomainFile {
|
||||||
throws IOException, CancelledException {
|
throws IOException, CancelledException {
|
||||||
GhidraFolder destGhidraFolder = (GhidraFolder) destFolder; // assumes single implementation
|
GhidraFolder destGhidraFolder = (GhidraFolder) destFolder; // assumes single implementation
|
||||||
return getFileData().copyVersionTo(version, destGhidraFolder.getFolderData(),
|
return getFileData().copyVersionTo(version, destGhidraFolder.getFolderData(),
|
||||||
monitor != null ? monitor : TaskMonitorAdapter.DUMMY_MONITOR);
|
monitor != null ? monitor : TaskMonitor.DUMMY);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Copy this file to make a private file if it is versioned. This method should be called
|
* Copy this file to make a private file if it is versioned. This method should be called
|
||||||
* only when a non shared project is being converted to a shared project.
|
* only when a non shared project is being converted to a shared project.
|
||||||
* @throws IOException
|
* @param monitor task monitor
|
||||||
|
* @throws IOException if an IO error occurs
|
||||||
|
* @throws CancelledException if task cancelled
|
||||||
*/
|
*/
|
||||||
void convertToPrivateFile(TaskMonitor monitor) throws IOException, CancelledException {
|
void convertToPrivateFile(TaskMonitor monitor) throws IOException, CancelledException {
|
||||||
getFileData().convertToPrivateFile(
|
getFileData().convertToPrivateFile(
|
||||||
monitor != null ? monitor : TaskMonitorAdapter.DUMMY_MONITOR);
|
monitor != null ? monitor : TaskMonitor.DUMMY);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -542,7 +549,7 @@ public class GhidraFile implements DomainFile {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void packFile(File file, TaskMonitor monitor) throws IOException, CancelledException {
|
public void packFile(File file, TaskMonitor monitor) throws IOException, CancelledException {
|
||||||
getFileData().packFile(file, monitor != null ? monitor : TaskMonitorAdapter.DUMMY_MONITOR);
|
getFileData().packFile(file, monitor != null ? monitor : TaskMonitor.DUMMY);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
@ -1217,6 +1217,10 @@ public class GhidraFileData {
|
||||||
}
|
}
|
||||||
|
|
||||||
void undoCheckout(boolean keep, boolean inUseOK) throws IOException {
|
void undoCheckout(boolean keep, boolean inUseOK) throws IOException {
|
||||||
|
undoCheckout(keep, false, inUseOK);
|
||||||
|
}
|
||||||
|
|
||||||
|
void undoCheckout(boolean keep, boolean force, boolean inUseOK) throws IOException {
|
||||||
synchronized (fileSystem) {
|
synchronized (fileSystem) {
|
||||||
if (fileSystem.isReadOnly()) {
|
if (fileSystem.isReadOnly()) {
|
||||||
throw new ReadOnlyException("undoCheckout permitted within writeable project only");
|
throw new ReadOnlyException("undoCheckout permitted within writeable project only");
|
||||||
|
@ -1224,16 +1228,23 @@ public class GhidraFileData {
|
||||||
if (!inUseOK) {
|
if (!inUseOK) {
|
||||||
checkInUse();
|
checkInUse();
|
||||||
}
|
}
|
||||||
if (!versionedFileSystem.isOnline()) {
|
boolean doForce = false;
|
||||||
throw new NotConnectedException("Not connected to repository server");
|
boolean isOnline = versionedFileSystem.isOnline();
|
||||||
|
if (!isOnline) {
|
||||||
|
if (!force) {
|
||||||
|
throw new NotConnectedException("Not connected to repository server");
|
||||||
|
}
|
||||||
|
doForce = true;
|
||||||
}
|
}
|
||||||
if (!isCheckedOut()) {
|
if (!isCheckedOut()) {
|
||||||
throw new IOException("File not checked out");
|
throw new IOException("File not checked out");
|
||||||
}
|
}
|
||||||
verifyRepoUser("undo-checkout");
|
if (!doForce) {
|
||||||
long checkoutId = folderItem.getCheckoutId();
|
verifyRepoUser("undo-checkout");
|
||||||
|
long checkoutId = folderItem.getCheckoutId();
|
||||||
|
versionedFolderItem.terminateCheckout(checkoutId, true);
|
||||||
|
}
|
||||||
String keepName = getKeepName();
|
String keepName = getKeepName();
|
||||||
versionedFolderItem.terminateCheckout(checkoutId, true);
|
|
||||||
if (keep) {
|
if (keep) {
|
||||||
folderItem.clearCheckout();
|
folderItem.clearCheckout();
|
||||||
try {
|
try {
|
||||||
|
|
|
@ -27,6 +27,7 @@ import ghidra.framework.remote.User;
|
||||||
import ghidra.framework.store.*;
|
import ghidra.framework.store.*;
|
||||||
import ghidra.framework.store.FileSystem;
|
import ghidra.framework.store.FileSystem;
|
||||||
import ghidra.framework.store.local.LocalFileSystem;
|
import ghidra.framework.store.local.LocalFileSystem;
|
||||||
|
import ghidra.framework.store.local.LocalFolderItem;
|
||||||
import ghidra.framework.store.remote.RemoteFileSystem;
|
import ghidra.framework.store.remote.RemoteFileSystem;
|
||||||
import ghidra.util.*;
|
import ghidra.util.*;
|
||||||
import ghidra.util.exception.CancelledException;
|
import ghidra.util.exception.CancelledException;
|
||||||
|
@ -384,7 +385,7 @@ public class ProjectFileManager implements ProjectData {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public DomainFolder getFolder(String path) {
|
public GhidraFolder getFolder(String path) {
|
||||||
int len = path.length();
|
int len = path.length();
|
||||||
if (len == 0 || path.charAt(0) != FileSystem.SEPARATOR_CHAR) {
|
if (len == 0 || path.charAt(0) != FileSystem.SEPARATOR_CHAR) {
|
||||||
throw new IllegalArgumentException(
|
throw new IllegalArgumentException(
|
||||||
|
@ -565,33 +566,158 @@ public class ProjectFileManager implements ProjectData {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void updateRepositoryInfo(RepositoryAdapter newRepository, TaskMonitor monitor)
|
public void updateRepositoryInfo(RepositoryAdapter newRepository, boolean force,
|
||||||
|
TaskMonitor monitor)
|
||||||
throws IOException, CancelledException {
|
throws IOException, CancelledException {
|
||||||
// 1) check for checked out files
|
|
||||||
findCheckedOutFiles(getRootFolder(), monitor);
|
newRepository.connect();
|
||||||
|
if (!newRepository.isConnected()) {
|
||||||
|
throw new IOException("new respository not connected");
|
||||||
|
}
|
||||||
|
|
||||||
// 2) Update the properties with server info
|
// Terminate any local checkouts which are not valid with newRepository
|
||||||
|
List<DomainFile> checkoutFiles = findCheckedOutFiles(monitor);
|
||||||
|
List<DomainFile> invalidCheckoutFiles =
|
||||||
|
findInvalidCheckouts(checkoutFiles, newRepository, monitor);
|
||||||
|
undoCheckouts(invalidCheckoutFiles, true, force, monitor);
|
||||||
|
|
||||||
|
// Update the properties with server info
|
||||||
updatePropertiesFile(newRepository);
|
updatePropertiesFile(newRepository);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void findCheckedOutFiles(DomainFolder folder, TaskMonitor monitor)
|
private boolean hasInvalidCheckout(DomainFile df, RepositoryAdapter newRepository)
|
||||||
throws IOException, CancelledException {
|
throws IOException {
|
||||||
|
try {
|
||||||
DomainFile[] files = folder.getFiles();
|
LocalFolderItem item = fileSystem.getItem(df.getParent().getPathname(), df.getName());
|
||||||
for (DomainFile file : files) {
|
if (item == null) {
|
||||||
if (monitor.isCancelled()) {
|
return false;
|
||||||
throw new CancelledException();
|
|
||||||
}
|
}
|
||||||
if (file.isCheckedOut()) {
|
|
||||||
throw new IOException("File " + file.getPathname() + " is checked out.");
|
// TODO: this is not bulletproof since we have limited data to validate checkout.
|
||||||
|
long checkoutId = item.getCheckoutId();
|
||||||
|
int checkoutVersion = item.getCheckoutVersion();
|
||||||
|
|
||||||
|
ItemCheckoutStatus otherCheckoutStatus = newRepository.getCheckout(
|
||||||
|
df.getParent().getPathname(), df.getName(), checkoutId);
|
||||||
|
|
||||||
|
if (!newRepository.getUser().getName().equals(otherCheckoutStatus.getUser())) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (checkoutVersion != otherCheckoutStatus.getCheckoutVersion()) {
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
DomainFolder[] folders = folder.getFolders();
|
catch (FileNotFoundException e) {
|
||||||
for (DomainFolder folder2 : folders) {
|
return true;
|
||||||
if (monitor.isCancelled()) {
|
}
|
||||||
throw new CancelledException();
|
catch (NotConnectedException e) {
|
||||||
|
throw e;
|
||||||
|
}
|
||||||
|
catch (IOException e) {
|
||||||
|
// skip file
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Determine if any domain files listed does not correspond to a checkout in the specified
|
||||||
|
* newRespository.
|
||||||
|
* @param checkoutList project domain files to check
|
||||||
|
* @param newRepository repository to check against before updating
|
||||||
|
* @param monitor task monitor
|
||||||
|
* @return true if one or more files are not valid checkouts in newRepository
|
||||||
|
* @throws IOException if IO error occurs
|
||||||
|
* @throws CancelledException if task cancelled
|
||||||
|
*/
|
||||||
|
public boolean hasInvalidCheckouts(List<DomainFile> checkoutList,
|
||||||
|
RepositoryAdapter newRepository, TaskMonitor monitor)
|
||||||
|
throws IOException, CancelledException {
|
||||||
|
for (DomainFile df : checkoutList) {
|
||||||
|
monitor.checkCanceled();
|
||||||
|
if (hasInvalidCheckout(df, newRepository)) {
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
findCheckedOutFiles(folder2, monitor);
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Find those domain files listed which do not correspond to checkouts in the specified
|
||||||
|
* newRespository.
|
||||||
|
* @param checkoutList project domain files to check
|
||||||
|
* @param newRepository repository to check against before updating
|
||||||
|
* @param monitor task monitor
|
||||||
|
* @return list of domain files not checked-out in repo
|
||||||
|
* @throws IOException if IO error occurs
|
||||||
|
* @throws CancelledException if task cancelled
|
||||||
|
*/
|
||||||
|
private List<DomainFile> findInvalidCheckouts(List<DomainFile> checkoutList,
|
||||||
|
RepositoryAdapter newRepository, TaskMonitor monitor)
|
||||||
|
throws IOException, CancelledException {
|
||||||
|
List<DomainFile> list = new ArrayList<>();
|
||||||
|
for (DomainFile df : checkoutList) {
|
||||||
|
monitor.checkCanceled();
|
||||||
|
if (hasInvalidCheckout(df, newRepository)) {
|
||||||
|
list.add(df);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return list;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Undo checkouts for all domain files listed.
|
||||||
|
* @param files list of files to undo checkout
|
||||||
|
* @param keep if a .keep copy of any checked-out file should be retained in the local file.
|
||||||
|
* @param force if not connected to the repository the local checkout file will be removed.
|
||||||
|
* Warning: forcing undo checkout will leave a stale checkout in place for the associated
|
||||||
|
* repository if not connected.
|
||||||
|
* @param monitor task monitor
|
||||||
|
* @throws IOException if an IO error occurs
|
||||||
|
* @throws CancelledException if task cancelled
|
||||||
|
*/
|
||||||
|
private void undoCheckouts(List<DomainFile> files, boolean keep, boolean force,
|
||||||
|
TaskMonitor monitor) throws IOException, CancelledException {
|
||||||
|
for (DomainFile df : files) {
|
||||||
|
monitor.checkCanceled();
|
||||||
|
if (df.isCheckedOut()) {
|
||||||
|
df.undoCheckout(keep, force);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Find all project files which are currently checked-out
|
||||||
|
* @param monitor task monitor (no progress updates)
|
||||||
|
* @return list of current checkout files
|
||||||
|
* @throws IOException if IO error occurs
|
||||||
|
* @throws CancelledException if task cancelled
|
||||||
|
*/
|
||||||
|
public List<DomainFile> findCheckedOutFiles(TaskMonitor monitor)
|
||||||
|
throws IOException, CancelledException {
|
||||||
|
List<DomainFile> list = new ArrayList<>();
|
||||||
|
findCheckedOutFiles("/", list, monitor);
|
||||||
|
return list;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void findCheckedOutFiles(String folderPath, List<DomainFile> checkoutList,
|
||||||
|
TaskMonitor monitor)
|
||||||
|
throws IOException, CancelledException {
|
||||||
|
|
||||||
|
for (String name : fileSystem.getItemNames(folderPath)) {
|
||||||
|
monitor.checkCanceled();
|
||||||
|
LocalFolderItem item = fileSystem.getItem(folderPath, name);
|
||||||
|
if (item.getCheckoutId() != FolderItem.DEFAULT_CHECKOUT_ID) {
|
||||||
|
checkoutList.add(new GhidraFile(getFolder(folderPath), name));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!folderPath.endsWith(FileSystem.SEPARATOR)) {
|
||||||
|
folderPath += FileSystem.SEPARATOR;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (String subfolder : fileSystem.getFolderNames(folderPath)) {
|
||||||
|
monitor.checkCanceled();
|
||||||
|
findCheckedOutFiles(folderPath + subfolder, checkoutList, monitor);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -19,6 +19,8 @@ import java.awt.BorderLayout;
|
||||||
import java.awt.FlowLayout;
|
import java.awt.FlowLayout;
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
import javax.swing.*;
|
import javax.swing.*;
|
||||||
import javax.swing.border.BevelBorder;
|
import javax.swing.border.BevelBorder;
|
||||||
|
@ -33,6 +35,7 @@ import docking.wizard.WizardManager;
|
||||||
import ghidra.app.util.GenericHelpTopics;
|
import ghidra.app.util.GenericHelpTopics;
|
||||||
import ghidra.framework.client.*;
|
import ghidra.framework.client.*;
|
||||||
import ghidra.framework.data.ConvertFileSystem;
|
import ghidra.framework.data.ConvertFileSystem;
|
||||||
|
import ghidra.framework.data.TransientDataManager;
|
||||||
import ghidra.framework.model.*;
|
import ghidra.framework.model.*;
|
||||||
import ghidra.framework.plugintool.PluginTool;
|
import ghidra.framework.plugintool.PluginTool;
|
||||||
import ghidra.framework.remote.User;
|
import ghidra.framework.remote.User;
|
||||||
|
@ -337,10 +340,17 @@ public class ProjectInfoDialog extends DialogComponentProvider {
|
||||||
}
|
}
|
||||||
|
|
||||||
private void updateSharedProjectInfo() {
|
private void updateSharedProjectInfo() {
|
||||||
if (filesAreOpen()) {
|
int openCount = getOpenFileCount();
|
||||||
Msg.showInfo(getClass(), getComponent(), "Cannot Change Project Info with Open Files",
|
if (openCount != 0) {
|
||||||
"Before your project info can be updated, you must close\n" +
|
Msg.showInfo(getClass(), getComponent(),
|
||||||
"files in running tools and make sure you have no files\n" + "checked out.");
|
"Cannot Change Project Info with Open Files",
|
||||||
|
"Found " + openCount + " open project file(s).\n" +
|
||||||
|
"Before your project info can be updated, you must\n" +
|
||||||
|
"close all open project files and tools.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!checkToolsClose()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -356,8 +366,10 @@ public class ProjectInfoDialog extends DialogComponentProvider {
|
||||||
currentRepository.getName().equals(rep.getName())) {
|
currentRepository.getName().equals(rep.getName())) {
|
||||||
Msg.showInfo(getClass(), getComponent(), "No Changes Made",
|
Msg.showInfo(getClass(), getComponent(), "No Changes Made",
|
||||||
"No changes were made to the shared project information.");
|
"No changes were made to the shared project information.");
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
else if (OptionDialog.showOptionDialog(getComponent(), "Update Shared Project Info",
|
|
||||||
|
if (OptionDialog.showOptionDialog(getComponent(), "Update Shared Project Info",
|
||||||
"Are you sure you want to update your shared project information?", "Update",
|
"Are you sure you want to update your shared project information?", "Update",
|
||||||
OptionDialog.QUESTION_MESSAGE) == OptionDialog.OPTION_ONE) {
|
OptionDialog.QUESTION_MESSAGE) == OptionDialog.OPTION_ONE) {
|
||||||
|
|
||||||
|
@ -376,12 +388,29 @@ public class ProjectInfoDialog extends DialogComponentProvider {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private boolean checkToolsClose() {
|
||||||
|
PluginTool[] runningTools = project.getToolManager().getRunningTools();
|
||||||
|
for (PluginTool runningTool : runningTools) {
|
||||||
|
if (!runningTool.canClose(false)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
runningTool.close();
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
private void convertToIndexedFilesystem() {
|
private void convertToIndexedFilesystem() {
|
||||||
if (filesAreOpen()) {
|
int openCount = getOpenFileCount();
|
||||||
|
if (openCount != 0) {
|
||||||
Msg.showInfo(getClass(), getComponent(),
|
Msg.showInfo(getClass(), getComponent(),
|
||||||
"Cannot Convert/Upgrade Project Storage with Open Files",
|
"Cannot Convert/Upgrade Project Storage with Open Files",
|
||||||
|
"Found " + openCount + " open project file(s).\n" +
|
||||||
"Before your project can be converted, you must close\n" +
|
"Before your project can be converted, you must close\n" +
|
||||||
"files in running tools.");
|
"all open project files and tools.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!checkToolsClose()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -415,10 +444,18 @@ public class ProjectInfoDialog extends DialogComponentProvider {
|
||||||
}
|
}
|
||||||
|
|
||||||
private void convertToShared() {
|
private void convertToShared() {
|
||||||
if (filesAreOpen()) {
|
|
||||||
Msg.showInfo(getClass(), getComponent(), "Cannot Convert Project with Open Files",
|
int openCount = getOpenFileCount();
|
||||||
|
if (openCount != 0) {
|
||||||
|
Msg.showInfo(getClass(), getComponent(),
|
||||||
|
"Cannot Convert Project with Open Files",
|
||||||
|
"Found " + openCount + " open project file(s).\n" +
|
||||||
"Before your project can be converted, you must close\n" +
|
"Before your project can be converted, you must close\n" +
|
||||||
"files in running tools and make sure you have no files\n" + "checked out.");
|
"all open project files and tools.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!checkToolsClose()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -430,7 +467,7 @@ public class ProjectInfoDialog extends DialogComponentProvider {
|
||||||
if (rep != null) {
|
if (rep != null) {
|
||||||
StringBuffer confirmMsg = new StringBuffer();
|
StringBuffer confirmMsg = new StringBuffer();
|
||||||
confirmMsg.append("All version history on your files will be\n" +
|
confirmMsg.append("All version history on your files will be\n" +
|
||||||
"lost after your project is converted.\n" +
|
"lost after your project is converted and checkouts terminated.\n" +
|
||||||
"Do you want to convert your project?\n");
|
"Do you want to convert your project?\n");
|
||||||
confirmMsg.append(" \n");
|
confirmMsg.append(" \n");
|
||||||
confirmMsg.append("WARNING: Convert CANNOT be undone!");
|
confirmMsg.append("WARNING: Convert CANNOT be undone!");
|
||||||
|
@ -442,11 +479,12 @@ public class ProjectInfoDialog extends DialogComponentProvider {
|
||||||
ConvertProjectTask task = new ConvertProjectTask(rep);
|
ConvertProjectTask task = new ConvertProjectTask(rep);
|
||||||
new TaskLauncher(task, getComponent(), 500);
|
new TaskLauncher(task, getComponent(), 500);
|
||||||
// block until task completes
|
// block until task completes
|
||||||
|
ProjectLocator projectLocator = project.getProjectLocator();
|
||||||
if (task.getStatus()) {
|
if (task.getStatus()) {
|
||||||
close();
|
close();
|
||||||
FileActionManager actionMgr = plugin.getFileActionManager();
|
FileActionManager actionMgr = plugin.getFileActionManager();
|
||||||
actionMgr.closeProject(false);
|
actionMgr.closeProject(false);
|
||||||
actionMgr.openProject(project.getProjectLocator());
|
actionMgr.openProject(projectLocator);
|
||||||
plugin.getProjectActionManager().showProjectInfo();
|
plugin.getProjectActionManager().showProjectInfo();
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
|
@ -456,36 +494,27 @@ public class ProjectInfoDialog extends DialogComponentProvider {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean filesAreOpen() {
|
private int getOpenFileCount() {
|
||||||
PluginTool[] tools = project.getToolManager().getRunningTools();
|
List<DomainFile> openFiles = new ArrayList<>();
|
||||||
|
project.getProjectData().findOpenFiles(openFiles);
|
||||||
if (tools.length > 0) {
|
TransientDataManager.getTransients(openFiles);
|
||||||
for (PluginTool tool : tools) {
|
return openFiles.size();
|
||||||
if (tool.getDomainFiles().length > 0) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private class ConvertProjectTask extends Task {
|
private class ConvertProjectTask extends Task {
|
||||||
private RepositoryAdapter taskRepository;
|
private RepositoryAdapter newRepository;
|
||||||
private boolean status;
|
private boolean status;
|
||||||
|
|
||||||
ConvertProjectTask(RepositoryAdapter repository) {
|
ConvertProjectTask(RepositoryAdapter repository) {
|
||||||
super("Convert Project to Shared", true, false, true);
|
super("Convert Project to Shared", true, false, true);
|
||||||
this.taskRepository = repository;
|
this.newRepository = repository;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* (non-Javadoc)
|
|
||||||
* @see ghidra.util.task.Task#run(ghidra.util.task.TaskMonitor)
|
|
||||||
*/
|
|
||||||
@Override
|
@Override
|
||||||
public void run(TaskMonitor monitor) {
|
public void run(TaskMonitor monitor) {
|
||||||
try {
|
try {
|
||||||
project.getProjectData().convertProjectToShared(taskRepository, monitor);
|
newRepository.connect();
|
||||||
|
project.getProjectData().convertProjectToShared(newRepository, monitor);
|
||||||
status = true;
|
status = true;
|
||||||
}
|
}
|
||||||
catch (IOException e) {
|
catch (IOException e) {
|
||||||
|
@ -515,9 +544,6 @@ public class ProjectInfoDialog extends DialogComponentProvider {
|
||||||
this.projectLocator = projectLocator;
|
this.projectLocator = projectLocator;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* (non-Javadoc)
|
|
||||||
* @see ghidra.util.task.Task#run(ghidra.util.task.TaskMonitor)
|
|
||||||
*/
|
|
||||||
@Override
|
@Override
|
||||||
public void run(TaskMonitor monitor) {
|
public void run(TaskMonitor monitor) {
|
||||||
try {
|
try {
|
||||||
|
@ -544,22 +570,20 @@ public class ProjectInfoDialog extends DialogComponentProvider {
|
||||||
}
|
}
|
||||||
|
|
||||||
private class UpdateInfoTask extends Task {
|
private class UpdateInfoTask extends Task {
|
||||||
private RepositoryAdapter taskRepository;
|
private RepositoryAdapter newRepository;
|
||||||
private boolean status;
|
private boolean status;
|
||||||
|
|
||||||
UpdateInfoTask(RepositoryAdapter repository) {
|
UpdateInfoTask(RepositoryAdapter repository) {
|
||||||
super("Update Shared Project Info", true, false, true);
|
super("Update Shared Project Info", true, false, true);
|
||||||
this.taskRepository = repository;
|
this.newRepository = repository;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* (non-Javadoc)
|
|
||||||
* @see ghidra.util.task.Task#run(ghidra.util.task.TaskMonitor)
|
|
||||||
*/
|
|
||||||
@Override
|
@Override
|
||||||
public void run(TaskMonitor monitor) {
|
public void run(TaskMonitor monitor) {
|
||||||
try {
|
try {
|
||||||
// NOTE: conversion of non-shared project will lose version history
|
newRepository.connect();
|
||||||
project.getProjectData().updateRepositoryInfo(taskRepository, monitor);
|
boolean force = useForcedCheckoutTransition(monitor);
|
||||||
|
project.getProjectData().updateRepositoryInfo(newRepository, force, monitor);
|
||||||
status = true;
|
status = true;
|
||||||
}
|
}
|
||||||
catch (IOException e) {
|
catch (IOException e) {
|
||||||
|
@ -575,6 +599,36 @@ public class ProjectInfoDialog extends DialogComponentProvider {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private boolean useForcedCheckoutTransition(TaskMonitor monitor) throws CancelledException, IOException {
|
||||||
|
if (repository == null) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
ProjectData projectData = project.getProjectData();
|
||||||
|
List<DomainFile> checkoutFiles = projectData.findCheckedOutFiles(monitor);
|
||||||
|
if (checkoutFiles.isEmpty() ||
|
||||||
|
!projectData.hasInvalidCheckouts(checkoutFiles, newRepository, monitor)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (OptionDialog.showOptionDialog(getComponent(), "Terminate Unrecognized Checkouts",
|
||||||
|
"One or more project file checkouts are not recognized by the selected repository.\n" +
|
||||||
|
"These checkouts will be terminated and a local .keep file created." +
|
||||||
|
(repository.isConnected() ? ""
|
||||||
|
: " Doing this\n" +
|
||||||
|
"will abandon such checkouts on the old repository since you are not connected.") +
|
||||||
|
"\n\n" +
|
||||||
|
"Are you sure you want to continue changing your shared project information?",
|
||||||
|
"Terminate Checkouts and Continue",
|
||||||
|
OptionDialog.QUESTION_MESSAGE) != OptionDialog.OPTION_ONE) {
|
||||||
|
|
||||||
|
throw new CancelledException();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Must force termination if not connected to current repository
|
||||||
|
return !repository.isConnected();
|
||||||
|
}
|
||||||
|
|
||||||
boolean getStatus() {
|
boolean getStatus() {
|
||||||
return status;
|
return status;
|
||||||
}
|
}
|
||||||
|
|
|
@ -21,6 +21,7 @@ import java.util.Map;
|
||||||
|
|
||||||
import javax.swing.Icon;
|
import javax.swing.Icon;
|
||||||
|
|
||||||
|
import ghidra.framework.client.NotConnectedException;
|
||||||
import ghidra.framework.data.CheckinHandler;
|
import ghidra.framework.data.CheckinHandler;
|
||||||
import ghidra.framework.store.*;
|
import ghidra.framework.store.*;
|
||||||
import ghidra.util.InvalidNameException;
|
import ghidra.util.InvalidNameException;
|
||||||
|
@ -403,11 +404,26 @@ public interface DomainFile extends Comparable<DomainFile> {
|
||||||
* Undo "checked-out" file. The original repository file is restored.
|
* Undo "checked-out" file. The original repository file is restored.
|
||||||
* @param keep if true, the private database will be renamed with a .keep
|
* @param keep if true, the private database will be renamed with a .keep
|
||||||
* extension.
|
* extension.
|
||||||
|
* @throws NotConnectedException if shared project and not connected to repository
|
||||||
* @throws FileInUseException if this file is in-use / checked-out.
|
* @throws FileInUseException if this file is in-use / checked-out.
|
||||||
* @throws IOException thrown if file is not checked-out or an IO / access error occurs.
|
* @throws IOException thrown if file is not checked-out or an IO / access error occurs.
|
||||||
*/
|
*/
|
||||||
public void undoCheckout(boolean keep) throws IOException;
|
public void undoCheckout(boolean keep) throws IOException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Undo "checked-out" file. The original repository file is restored.
|
||||||
|
* @param keep if true, the private database will be renamed with a .keep
|
||||||
|
* extension.
|
||||||
|
* @param force if not connected to the repository the local checkout file will be removed.
|
||||||
|
* Warning: forcing undo checkout will leave a stale checkout in place for the associated
|
||||||
|
* repository if not connected.
|
||||||
|
* @throws NotConnectedException if shared project and not connected to repository and
|
||||||
|
* force is false
|
||||||
|
* @throws FileInUseException if this file is in-use / checked-out.
|
||||||
|
* @throws IOException thrown if file is not checked-out or an IO / access error occurs.
|
||||||
|
*/
|
||||||
|
public void undoCheckout(boolean keep, boolean force) throws IOException;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Forcefully terminate a checkout for the associated versioned file.
|
* Forcefully terminate a checkout for the associated versioned file.
|
||||||
* The user must be the owner of the checkout or have administrator privilege
|
* The user must be the owner of the checkout or have administrator privilege
|
||||||
|
|
|
@ -15,6 +15,10 @@
|
||||||
*/
|
*/
|
||||||
package ghidra.framework.model;
|
package ghidra.framework.model;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.net.URL;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
import ghidra.framework.client.RepositoryAdapter;
|
import ghidra.framework.client.RepositoryAdapter;
|
||||||
import ghidra.framework.remote.User;
|
import ghidra.framework.remote.User;
|
||||||
import ghidra.framework.store.local.LocalFileSystem;
|
import ghidra.framework.store.local.LocalFileSystem;
|
||||||
|
@ -22,10 +26,6 @@ import ghidra.util.InvalidNameException;
|
||||||
import ghidra.util.exception.CancelledException;
|
import ghidra.util.exception.CancelledException;
|
||||||
import ghidra.util.task.TaskMonitor;
|
import ghidra.util.task.TaskMonitor;
|
||||||
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.net.URL;
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The ProjectData interface provides access to all the data files and folders
|
* The ProjectData interface provides access to all the data files and folders
|
||||||
* in a project.
|
* in a project.
|
||||||
|
@ -79,6 +79,30 @@ public interface ProjectData {
|
||||||
*/
|
*/
|
||||||
public void findOpenFiles(List<DomainFile> list);
|
public void findOpenFiles(List<DomainFile> list);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Find all project files which are currently checked-out to this project
|
||||||
|
* @param monitor task monitor (no progress updates)
|
||||||
|
* @return list of current checkout files
|
||||||
|
* @throws IOException if IO error occurs
|
||||||
|
* @throws CancelledException if task cancelled
|
||||||
|
*/
|
||||||
|
public List<DomainFile> findCheckedOutFiles(TaskMonitor monitor)
|
||||||
|
throws IOException, CancelledException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Determine if any domain files listed do not correspond to a checkout in the specified
|
||||||
|
* newRespository prior to invoking {@link #updateRepositoryInfo(RepositoryAdapter, boolean, TaskMonitor)}.
|
||||||
|
* @param checkoutList project domain files to check
|
||||||
|
* @param newRepository repository to check against before updating
|
||||||
|
* @param monitor task monitor
|
||||||
|
* @return true if one or more files are not valid checkouts in newRepository
|
||||||
|
* @throws IOException if IO error occurs
|
||||||
|
* @throws CancelledException if task cancelled
|
||||||
|
*/
|
||||||
|
public boolean hasInvalidCheckouts(List<DomainFile> checkoutList,
|
||||||
|
RepositoryAdapter newRepository, TaskMonitor monitor)
|
||||||
|
throws IOException, CancelledException;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get domain file specified by its unique fileID.
|
* Get domain file specified by its unique fileID.
|
||||||
* @param fileID domain file ID
|
* @param fileID domain file ID
|
||||||
|
@ -157,16 +181,19 @@ public interface ProjectData {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Update the repository for this project; the server may have changed or a different
|
* Update the repository for this project; the server may have changed or a different
|
||||||
* repository is being used. NOTE: The project should be closed and then reopened after this
|
* repository is being used. Any existing checkout which is not recognized/valid by
|
||||||
* method is called.
|
* newRepository will be terminated and a local .keep file created.
|
||||||
* @param repository new repository to use
|
* NOTE: The project should be closed and then reopened after this method is called.
|
||||||
|
* @param newRepository new repository to use
|
||||||
|
* @param force if true any existing local checkout which is not recognized/valid
|
||||||
|
* for newRepository will be forceably terminated if offline with old repository.
|
||||||
* @param monitor task monitor
|
* @param monitor task monitor
|
||||||
* @throws IOException thrown if files are still checked out, or if there was a problem accessing
|
* @throws IOException thrown if files are still checked out, or if there was a problem accessing
|
||||||
* the filesystem
|
* the filesystem
|
||||||
* @throws CancelledException if the user canceled the update
|
* @throws CancelledException if the user canceled the update
|
||||||
*/
|
*/
|
||||||
public void updateRepositoryInfo(RepositoryAdapter repository, TaskMonitor monitor)
|
public void updateRepositoryInfo(RepositoryAdapter newRepository, boolean force,
|
||||||
throws IOException, CancelledException;
|
TaskMonitor monitor) throws IOException, CancelledException;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Close the project storage associated with this project data object.
|
* Close the project storage associated with this project data object.
|
||||||
|
|
|
@ -1207,7 +1207,7 @@ public abstract class PluginTool extends AbstractDockingTool {
|
||||||
else {
|
else {
|
||||||
beep();
|
beep();
|
||||||
Msg.showInfo(getClass(), getToolFrame(), "Tool Busy",
|
Msg.showInfo(getClass(), getToolFrame(), "Tool Busy",
|
||||||
"You must stop all background tasks before exiting.");
|
"You must stop all background tasks before tool may close.");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -287,6 +287,11 @@ public class TestDummyDomainFile implements DomainFile {
|
||||||
throw new UnsupportedOperationException();
|
throw new UnsupportedOperationException();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void undoCheckout(boolean keep, boolean force) throws IOException {
|
||||||
|
throw new UnsupportedOperationException();
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void terminateCheckout(long checkoutId) throws IOException {
|
public void terminateCheckout(long checkoutId) throws IOException {
|
||||||
throw new UnsupportedOperationException();
|
throw new UnsupportedOperationException();
|
||||||
|
|
|
@ -63,6 +63,21 @@ public class TestDummyProjectData implements ProjectData {
|
||||||
// stub
|
// stub
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<DomainFile> findCheckedOutFiles(TaskMonitor monitor)
|
||||||
|
throws IOException, CancelledException {
|
||||||
|
// stub
|
||||||
|
return List.of();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean hasInvalidCheckouts(List<DomainFile> checkoutList,
|
||||||
|
RepositoryAdapter newRepository, TaskMonitor monitor)
|
||||||
|
throws IOException, CancelledException {
|
||||||
|
// stub
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public DomainFile getFileByID(String fileID) {
|
public DomainFile getFileByID(String fileID) {
|
||||||
// stub
|
// stub
|
||||||
|
@ -121,8 +136,8 @@ public class TestDummyProjectData implements ProjectData {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void updateRepositoryInfo(RepositoryAdapter repository, TaskMonitor monitor)
|
public void updateRepositoryInfo(RepositoryAdapter repository, boolean force,
|
||||||
throws IOException, CancelledException {
|
TaskMonitor monitor) throws IOException, CancelledException {
|
||||||
// stub
|
// stub
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -329,7 +329,7 @@ public class FrontEndPluginScreenShots extends GhidraScreenShotGenerator {
|
||||||
FrontEndPlugin plugin = getPlugin(tool, FrontEndPlugin.class);
|
FrontEndPlugin plugin = getPlugin(tool, FrontEndPlugin.class);
|
||||||
JComponent projectDataPanel = (JComponent) getInstanceField("projectDataPanel", plugin);
|
JComponent projectDataPanel = (JComponent) getInstanceField("projectDataPanel", plugin);
|
||||||
JTabbedPane tabbedPane =
|
JTabbedPane tabbedPane =
|
||||||
(JTabbedPane) getInstanceField("projectTabPanel", projectDataPanel);
|
(JTabbedPane) getInstanceField("projectTab", projectDataPanel);
|
||||||
tabbedPane.setSelectedIndex(1);
|
tabbedPane.setSelectedIndex(1);
|
||||||
setToolSize(800, 600);
|
setToolSize(800, 600);
|
||||||
captureComponent(projectDataPanel);
|
captureComponent(projectDataPanel);
|
||||||
|
|
|
@ -25,7 +25,6 @@ import javax.swing.*;
|
||||||
import org.junit.*;
|
import org.junit.*;
|
||||||
import org.junit.experimental.categories.Category;
|
import org.junit.experimental.categories.Category;
|
||||||
|
|
||||||
import docking.AbstractErrDialog;
|
|
||||||
import docking.action.DockingActionIf;
|
import docking.action.DockingActionIf;
|
||||||
import docking.widgets.OptionDialog;
|
import docking.widgets.OptionDialog;
|
||||||
import docking.wizard.WizardManager;
|
import docking.wizard.WizardManager;
|
||||||
|
@ -278,7 +277,8 @@ public class ProjectInfoDialogTest extends AbstractGhidraHeadedIntegrationTest {
|
||||||
waitForTasks();
|
waitForTasks();
|
||||||
|
|
||||||
// check out file from shared project
|
// check out file from shared project
|
||||||
rootFolder = getProject().getProjectData().getRootFolder();
|
Project oldProject = getProject();
|
||||||
|
rootFolder = oldProject.getProjectData().getRootFolder();
|
||||||
DomainFile df = rootFolder.getFile("testA");
|
DomainFile df = rootFolder.getFile("testA");
|
||||||
df.addToVersionControl("test", true, TaskMonitor.DUMMY);
|
df.addToVersionControl("test", true, TaskMonitor.DUMMY);
|
||||||
assertTrue(df.isCheckedOut());
|
assertTrue(df.isCheckedOut());
|
||||||
|
@ -299,11 +299,34 @@ public class ProjectInfoDialogTest extends AbstractGhidraHeadedIntegrationTest {
|
||||||
assertNotNull(opt);
|
assertNotNull(opt);
|
||||||
assertEquals("Update Shared Project Info", opt.getTitle());
|
assertEquals("Update Shared Project Info", opt.getTitle());
|
||||||
pressButtonByText(opt, "Update");
|
pressButtonByText(opt, "Update");
|
||||||
|
|
||||||
|
opt = waitForDialogComponent(OptionDialog.class);
|
||||||
|
assertNotNull(opt);
|
||||||
|
assertEquals("Terminate Unrecognized Checkouts", opt.getTitle());
|
||||||
|
pressButtonByText(opt, "Terminate Checkouts and Continue");
|
||||||
waitForTasks();
|
waitForTasks();
|
||||||
|
|
||||||
AbstractErrDialog errorDialog = waitForErrorDialog();
|
dialog = waitForDialogComponent(ProjectInfoDialog.class);
|
||||||
assertEquals("Failed to Update Shared Project Info", errorDialog.getTitle());
|
assertNotNull(dialog);
|
||||||
close(errorDialog);
|
pressButtonByText(dialog, "Dismiss");
|
||||||
|
|
||||||
|
Project updatedProject = getProject();
|
||||||
|
assertNotNull(updatedProject);
|
||||||
|
assertTrue(updatedProject != oldProject);
|
||||||
|
|
||||||
|
RepositoryAdapter rep = updatedProject.getRepository();
|
||||||
|
assertNotNull(rep);
|
||||||
|
assertEquals("AnotherRepository", rep.getName());
|
||||||
|
|
||||||
|
ProjectData updatedProjectData = updatedProject.getProjectData();
|
||||||
|
|
||||||
|
rootFolder = updatedProjectData.getRootFolder();
|
||||||
|
assertNull(rootFolder.getFile("testA"));
|
||||||
|
df = rootFolder.getFile("testA.keep");
|
||||||
|
assertNotNull(df);
|
||||||
|
assertFalse(df.isVersioned());
|
||||||
|
assertFalse(df.isCheckedOut());
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void checkProjectInfo(String expectedRepName) {
|
private void checkProjectInfo(String expectedRepName) {
|
||||||
|
|
Loading…
Reference in a new issue