Skip to main content
aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
Diffstat (limited to 'org.eclipse.egit.ui/src/org/eclipse/egit/ui/internal/merge')
-rw-r--r--org.eclipse.egit.ui/src/org/eclipse/egit/ui/internal/merge/GitMergeEditorInput.java219
1 files changed, 156 insertions, 63 deletions
diff --git a/org.eclipse.egit.ui/src/org/eclipse/egit/ui/internal/merge/GitMergeEditorInput.java b/org.eclipse.egit.ui/src/org/eclipse/egit/ui/internal/merge/GitMergeEditorInput.java
index 12f8125a50..a685f3b137 100644
--- a/org.eclipse.egit.ui/src/org/eclipse/egit/ui/internal/merge/GitMergeEditorInput.java
+++ b/org.eclipse.egit.ui/src/org/eclipse/egit/ui/internal/merge/GitMergeEditorInput.java
@@ -12,6 +12,8 @@ package org.eclipse.egit.ui.internal.merge;
import java.io.IOException;
import java.lang.reflect.InvocationTargetException;
+import java.net.URI;
+import java.nio.charset.Charset;
import java.text.MessageFormat;
import java.time.Instant;
import java.util.ArrayList;
@@ -32,13 +34,21 @@ import org.eclipse.compare.structuremergeviewer.IDiffElement;
import org.eclipse.core.resources.IFile;
import org.eclipse.core.resources.IProject;
import org.eclipse.core.resources.IResource;
+import org.eclipse.core.resources.IWorkspace;
+import org.eclipse.core.resources.IWorkspaceRunnable;
+import org.eclipse.core.resources.ResourcesPlugin;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IPath;
import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.Path;
+import org.eclipse.core.runtime.Status;
+import org.eclipse.core.runtime.SubMonitor;
+import org.eclipse.core.runtime.jobs.Job;
import org.eclipse.egit.core.RepositoryUtil;
import org.eclipse.egit.core.internal.CompareCoreUtils;
import org.eclipse.egit.core.internal.CoreText;
+import org.eclipse.egit.core.internal.efs.HiddenResources;
import org.eclipse.egit.core.internal.storage.GitFileRevision;
import org.eclipse.egit.core.internal.util.ResourceUtil;
import org.eclipse.egit.core.util.RevCommitUtils;
@@ -48,7 +58,6 @@ import org.eclipse.egit.ui.internal.UIText;
import org.eclipse.egit.ui.internal.revision.EditableRevision;
import org.eclipse.egit.ui.internal.revision.FileRevisionTypedElement;
import org.eclipse.egit.ui.internal.revision.GitCompareFileRevisionEditorInput.EmptyTypedElement;
-import org.eclipse.egit.ui.internal.revision.LocationEditableRevision;
import org.eclipse.egit.ui.internal.revision.ResourceEditableRevision;
import org.eclipse.egit.ui.internal.synchronize.compare.LocalNonWorkspaceTypedElement;
import org.eclipse.jface.operation.IRunnableContext;
@@ -73,13 +82,12 @@ import org.eclipse.swt.widgets.Display;
import org.eclipse.team.core.history.IFileRevision;
import org.eclipse.team.internal.ui.synchronize.EditableSharedDocumentAdapter.ISharedDocumentAdapterListener;
import org.eclipse.team.internal.ui.synchronize.LocalResourceTypedElement;
-import org.eclipse.team.ui.synchronize.SaveableCompareEditorInput;
import org.eclipse.ui.ISharedImages;
import org.eclipse.ui.PlatformUI;
import org.eclipse.ui.ide.IDE.SharedImages;
/**
- * A Git-specific {@link CompareEditorInput}
+ * A Git-specific {@link CompareEditorInput} for merging conflicting files.
*/
@SuppressWarnings("restriction")
public class GitMergeEditorInput extends CompareEditorInput {
@@ -95,6 +103,8 @@ public class GitMergeEditorInput extends CompareEditorInput {
private final IPath[] locations;
+ private List<IFile> toDelete;
+
/**
* @param useWorkspace
* if <code>true</code>, use the workspace content (i.e. the
@@ -131,6 +141,90 @@ public class GitMergeEditorInput extends CompareEditorInput {
return super.getAdapter(adapter);
}
+ @Override
+ protected void contentsCreated() {
+ super.contentsCreated();
+ // select the first conflict
+ getNavigator().selectChange(true);
+ }
+
+ @Override
+ protected void handleDispose() {
+ super.handleDispose();
+ // We do NOT dispose the images, as these are shared.
+ //
+ // We need to remove the temporary resources. A CompareEditorInput is
+ // supposed to be the very last thing that is disposed in a compare
+ // viewer, but this is not always true. If content merge viewers add
+ // additional widgets, for instance for the Java structure comparison,
+ // we're suddenly no longer the last item to be disposed. The various
+ // viewers (left, right, structure, and so on) are all disposed when
+ // their widgets are disposed. Widget disposal happens recursively
+ // top-down on the UI thread, so an asyncExec should be safe here to
+ // ensure that we remove the files only once everything else has been
+ // disposed of. If we delete temporary resources before all viewers had
+ // disconnected the Document, some might not disconnect because
+ // SharedDocumentAdapter.getDocumentKey() returns null if the file has
+ // been deleted. If this happens the framework will find that still
+ // connected document the next time this resource is opened and show
+ // that instead of the true resource contents. This is wrong and is very
+ // annoying if this cached document is dirty: one can open only this
+ // dirty version from then on, until the next restart of Eclipse.
+ PlatformUI.getWorkbench().getDisplay().asyncExec(this::cleanUp);
+ }
+
+ private void cleanUp() {
+ if (toDelete == null || toDelete.isEmpty()) {
+ return;
+ }
+ List<IFile> toClean = toDelete;
+ toDelete = null;
+ // Don't clean up if the workbench is shutting down; we would exit with
+ // unsaved workspace changes. Instead, EGit core cleans the project on
+ // start.
+ Job job = new Job(UIText.GitMergeEditorInput_ResourceCleanupJobName) {
+
+ @Override
+ public boolean shouldSchedule() {
+ return super.shouldSchedule()
+ && !PlatformUI.getWorkbench().isClosing();
+ }
+
+ @Override
+ public boolean shouldRun() {
+ return super.shouldRun()
+ && !PlatformUI.getWorkbench().isClosing();
+ }
+
+ @Override
+ protected IStatus run(IProgressMonitor monitor) {
+ IWorkspaceRunnable remove = m -> {
+ SubMonitor progress = SubMonitor.convert(m, toClean.size());
+ for (IFile tmp : toClean) {
+ if (PlatformUI.getWorkbench().isClosing()) {
+ return;
+ }
+ try {
+ tmp.delete(true, progress.newChild(1));
+ } catch (CoreException e) {
+ // Ignore
+ }
+ }
+ };
+ try {
+ ResourcesPlugin.getWorkspace().run(remove, null,
+ IWorkspace.AVOID_UPDATE, monitor);
+ } catch (CoreException e) {
+ return e.getStatus();
+ }
+ return Status.OK_STATUS;
+ }
+ };
+ job.setSystem(true);
+ job.setUser(false);
+ job.schedule();
+ }
+
private static boolean isUIThread() {
return Display.getCurrent() != null;
}
@@ -269,19 +363,6 @@ public class GitMergeEditorInput extends CompareEditorInput {
}
}
- @Override
- protected void contentsCreated() {
- super.contentsCreated();
- // select the first conflict
- getNavigator().selectChange(true);
- }
-
- @Override
- protected void handleDispose() {
- super.handleDispose();
- // we do NOT dispose the images, as these are shared
- }
-
@SuppressWarnings("unused")
private IDiffContainer buildDiffContainer(Repository repository,
RevCommit headCommit, RevCommit ancestorCommit,
@@ -381,20 +462,22 @@ public class GitMergeEditorInput extends CompareEditorInput {
.equals(dirCacheEntry.getLastModifiedInstant());
}
if (useWorkingTree) {
+ LocalResourceTypedElement item;
if (file != null) {
- left = SaveableCompareEditorInput
- .createFileElement(file);
+ item = new LocalResourceTypedElement(file);
} else {
- left = new LocalNonWorkspaceTypedElement(repository,
+ item = new LocalNonWorkspaceTypedElement(repository,
location);
}
- if (left instanceof LocalResourceTypedElement) {
- ((LocalResourceTypedElement) left)
- .setSharedDocumentListener(
- new LocalResourceSaver(
- (LocalResourceTypedElement) left));
- }
+ item.setSharedDocumentListener(
+ new LocalResourceSaver(item));
+ left = item;
} else {
+ IFile rsc = file != null ? file
+ : createHiddenResource(location.toFile().toURI(),
+ tw.getNameString(), null);
+ assert rsc != null;
+ // Stage 2 from index with backing IResource
rev = GitFileRevision.inIndex(repository, gitPath,
DirCacheEntry.STAGE_2);
IRunnableContext runnableContext = getContainer();
@@ -403,48 +486,16 @@ public class GitMergeEditorInput extends CompareEditorInput {
.getProgressService();
assert runnableContext != null;
}
- if (file != null) {
- left = new ResourceEditableRevision(rev, file,
- runnableContext);
- } else {
- left = new LocationEditableRevision(rev, location,
- runnableContext);
- }
+ left = new ResourceEditableRevision(rev, rsc,
+ runnableContext);
// 'left' saves to the working tree. Update the index entry
// with the current time. Normal conflict stages have a
// timestamp of zero, so this is a non-invasive fully
// compatible way to mark this conflict stage so that the
// next time we do take the file contents.
- ((EditableRevision) left)
- .addContentChangeListener(source -> {
- DirCache cache = null;
- try {
- cache = repository.lockDirCache();
- DirCacheEditor editor = cache.editor();
- editor.add(new PathEdit(gitPath) {
-
- private boolean done;
-
- @Override
- public void apply(DirCacheEntry ent) {
- if (!done && ent.getStage() > 0) {
- ent.setLastModified(
- Instant.now());
- done = true;
- }
- }
- });
- editor.commit();
- } catch (RuntimeException | IOException e) {
- Activator.logError(MessageFormat.format(
- UIText.GitMergeEditorInput_ErrorUpdatingIndex,
- gitPath), e);
- } finally {
- if (cache != null) {
- cache.unlock();
- }
- }
- });
+ ((EditableRevision) left).addContentChangeListener(
+ source -> updateIndexTimestamp(repository,
+ gitPath));
// make sure we don't need a round trip later
try {
((EditableRevision) left).cacheContents(monitor);
@@ -487,6 +538,49 @@ public class GitMergeEditorInput extends CompareEditorInput {
}
}
+ private IFile createHiddenResource(URI uri, String name, Charset encoding)
+ throws IOException {
+ try {
+ IFile tmp = HiddenResources.INSTANCE.createFile(uri, name, encoding,
+ null);
+ if (toDelete == null) {
+ toDelete = new ArrayList<>();
+ }
+ toDelete.add(tmp);
+ return tmp;
+ } catch (CoreException e) {
+ throw new IOException(e.getMessage(), e);
+ }
+ }
+
+ private void updateIndexTimestamp(Repository repository, String gitPath) {
+ DirCache cache = null;
+ try {
+ cache = repository.lockDirCache();
+ DirCacheEditor editor = cache.editor();
+ editor.add(new PathEdit(gitPath) {
+
+ private boolean done;
+
+ @Override
+ public void apply(DirCacheEntry ent) {
+ if (!done && ent.getStage() > 0) {
+ ent.setLastModified(Instant.now());
+ done = true;
+ }
+ }
+ });
+ editor.commit();
+ } catch (IOException e) {
+ Activator.logError(MessageFormat.format(
+ UIText.GitMergeEditorInput_ErrorUpdatingIndex, gitPath), e);
+ } finally {
+ if (cache != null) {
+ cache.unlock();
+ }
+ }
+ }
+
private IDiffContainer getFileParent(IDiffContainer root,
IPath repositoryPath, IFile file, IPath location) {
int projectSegment = -1;
@@ -580,6 +674,5 @@ public class GitMergeEditorInput extends CompareEditorInput {
public void handleDocumentSaved() {
// Nothing
}
-
}
}

Back to the top