Skip to main content
aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorThomas Wolf2021-04-02 16:41:45 +0000
committerThomas Wolf2021-04-03 17:40:47 +0000
commit91406c444aa2d07c1efc329bfebf47e17e5e4bf7 (patch)
tree09ae1058a7c7bb0b157ee7612f1d58fad697b2f6
parent95d93d37082bf36efb7996851636e7d77ce7b40f (diff)
downloadegit-91406c444aa2d07c1efc329bfebf47e17e5e4bf7.tar.gz
egit-91406c444aa2d07c1efc329bfebf47e17e5e4bf7.tar.xz
egit-91406c444aa2d07c1efc329bfebf47e17e5e4bf7.zip
Add "Replace With->Ours/Theirs" sub-menus for IFiles
Conflicting files are decorated in the package or project explorer. Give them also context menu actions to resolve the conflict via checking out the "ours" or "theirs" revision. Bug: 399982 Change-Id: Ie037f345b80c1c670f1b7ab99a5831f1035e621b Signed-off-by: Thomas Wolf <thomas.wolf@paranor.ch>
-rw-r--r--org.eclipse.egit.core/src/org/eclipse/egit/core/RevUtils.java33
-rw-r--r--org.eclipse.egit.ui.test/src/org/eclipse/egit/ui/test/stagview/StagingViewTest.java82
-rw-r--r--org.eclipse.egit.ui/plugin.properties4
-rw-r--r--org.eclipse.egit.ui/plugin.xml42
-rw-r--r--org.eclipse.egit.ui/src/org/eclipse/egit/ui/internal/actions/ReplaceConflictActionHandler.java231
-rw-r--r--org.eclipse.egit.ui/src/org/eclipse/egit/ui/internal/actions/ReplaceWithOursActionHandler.java26
-rw-r--r--org.eclipse.egit.ui/src/org/eclipse/egit/ui/internal/actions/ReplaceWithTheirsActionHandler.java26
-rw-r--r--org.eclipse.egit.ui/src/org/eclipse/egit/ui/internal/selection/SelectionPropertyTester.java47
-rw-r--r--org.eclipse.egit.ui/src/org/eclipse/egit/ui/internal/staging/ReplaceConflictMenu.java85
9 files changed, 491 insertions, 85 deletions
diff --git a/org.eclipse.egit.core/src/org/eclipse/egit/core/RevUtils.java b/org.eclipse.egit.core/src/org/eclipse/egit/core/RevUtils.java
index 886bb6158a..d910c44edd 100644
--- a/org.eclipse.egit.core/src/org/eclipse/egit/core/RevUtils.java
+++ b/org.eclipse.egit.core/src/org/eclipse/egit/core/RevUtils.java
@@ -75,6 +75,39 @@ public class RevUtils {
}
/**
+ * Get the 'theirs' commit in a conflict state.
+ *
+ * @param repository
+ * to get the commits from
+ * @return the commit
+ * @throws IOException
+ * if the commit cannot be determined
+ */
+ public static RevCommit getTheirs(Repository repository)
+ throws IOException {
+ try (RevWalk walk = new RevWalk(repository)) {
+ RepositoryState state = repository.getRepositoryState();
+ if (state == RepositoryState.REBASING
+ || state == RepositoryState.CHERRY_PICKING) {
+ ObjectId cherryPickHead = repository.readCherryPickHead();
+ if (cherryPickHead != null) {
+ RevCommit cherryPickCommit = walk
+ .parseCommit(cherryPickHead);
+ return cherryPickCommit;
+ }
+ } else if (state == RepositoryState.MERGING) {
+ List<ObjectId> mergeHeads = repository.readMergeHeads();
+ Assert.isNotNull(mergeHeads);
+ if (mergeHeads.size() == 1) {
+ ObjectId mergeHead = mergeHeads.get(0);
+ return walk.parseCommit(mergeHead);
+ }
+ }
+ return null;
+ }
+ }
+
+ /**
* @param repository
* @param path
* repository-relative path of file with conflicts
diff --git a/org.eclipse.egit.ui.test/src/org/eclipse/egit/ui/test/stagview/StagingViewTest.java b/org.eclipse.egit.ui.test/src/org/eclipse/egit/ui/test/stagview/StagingViewTest.java
index 55f4d671b4..62a85e1803 100644
--- a/org.eclipse.egit.ui.test/src/org/eclipse/egit/ui/test/stagview/StagingViewTest.java
+++ b/org.eclipse.egit.ui.test/src/org/eclipse/egit/ui/test/stagview/StagingViewTest.java
@@ -240,8 +240,8 @@ public class StagingViewTest extends AbstractStagingViewTestCase {
}
/**
- * Tests resolving a modify-delete conflict via "Replace with theirs", which
- * should remove the file and stage the result.
+ * Tests resolving a modify-delete conflict via "Replace with theirs" in the
+ * staging view, which should remove the file and stage the result.
*
* @throws Exception
* on errors
@@ -320,6 +320,84 @@ public class StagingViewTest extends AbstractStagingViewTestCase {
TestUtil.getHeadCommit(repository).getShortMessage());
}
+ /**
+ * Tests resolving a modify-delete conflict via "Replace with theirs" in the
+ * project explorer, which should remove the file and stage the result.
+ *
+ * @throws Exception
+ * on errors
+ */
+ @Test
+ public void testModifyDeleteConflict2() throws Exception {
+ IFile file = ResourcesPlugin.getWorkspace().getRoot().getProject(PROJ1)
+ .getFolder(FOLDER).getFile(FILE1);
+ RevCommit side;
+ try (Git git = new Git(repository)) {
+ git.checkout().setCreateBranch(true).setName("side").call();
+ assertTrue(file.exists());
+ file.delete(true, null);
+ assertFalse(file.exists());
+ git.rm().addFilepattern(FILE1_PATH).call();
+ TestUtil.joinJobs(JobFamilies.INDEX_DIFF_CACHE_UPDATE);
+ side = git.commit().setMessage("File deleted").call();
+ TestUtil.waitForJobs(50, 5000);
+
+ git.checkout().setName("master").call();
+ commitOneFileChange("on master");
+
+ git.merge().include(repository.findRef("side")).call();
+ }
+ TestUtil.joinJobs(JobFamilies.INDEX_DIFF_CACHE_UPDATE);
+ assertEquals(RepositoryState.MERGING, repository.getRepositoryState());
+
+ StagingViewTester stagingView = StagingViewTester.openStagingView();
+ assertEquals("", stagingView.getCommitMessage());
+ stagingView.assertCommitEnabled(false);
+
+ SWTBotView view = stagingView.getView();
+ SWTBot viewBot = view.bot();
+
+ SWTBotTree explorer = TestUtil.getExplorerTree();
+ SWTBotTreeItem item = TestUtil.navigateTo(explorer, PROJ1, FOLDER,
+ FILE1);
+ item.select();
+
+ JobJoiner jobJoiner = JobJoiner.startListening(
+ org.eclipse.egit.core.JobFamilies.INDEX_DIFF_CACHE_UPDATE, 30,
+ TimeUnit.SECONDS);
+
+ ContextMenuHelper.clickContextMenu(explorer,
+ UIText.StagingView_ReplaceWith,
+ formatCommit(
+ UIText.ReplaceWithOursTheirsMenu_TheirsWithCommitLabel,
+ side));
+
+ jobJoiner.join();
+
+ assertFalse(file.exists());
+
+ assertEquals(RepositoryState.MERGING_RESOLVED,
+ repository.getRepositoryState());
+ String expectedMessage = "Merge branch 'side'";
+ assertThat(stagingView.getCommitMessage(), startsWith(expectedMessage));
+
+ SWTBotTree stagedTree = viewBot.tree(1);
+ TestUtil.waitUntilTreeHasNodeContainsText(viewBot, stagedTree,
+ FILE1_PATH, 10000);
+ SWTBotTreeItem item2 = TestUtil.getNode(stagedTree.getAllItems(),
+ FILE1_PATH);
+ Object data = UIThreadRunnable.syncExec(() -> item2.widget.getData());
+ assertEquals(StagingEntry.class, data.getClass());
+ StagingEntry entry = (StagingEntry) data;
+ assertEquals(StagingEntry.State.REMOVED, entry.getState());
+
+ stagingView.commit();
+ assertEquals(RepositoryState.SAFE, repository.getRepositoryState());
+
+ assertEquals(expectedMessage,
+ TestUtil.getHeadCommit(repository).getShortMessage());
+ }
+
private static String formatCommit(String format, RevCommit commit) {
String message = Utils.shortenText(commit.getShortMessage(), 60);
return NLS.bind(format, Utils.getShortObjectId(commit), message);
diff --git a/org.eclipse.egit.ui/plugin.properties b/org.eclipse.egit.ui/plugin.properties
index 30e2f3850b..8b2f768bc9 100644
--- a/org.eclipse.egit.ui/plugin.properties
+++ b/org.eclipse.egit.ui/plugin.properties
@@ -48,6 +48,8 @@ ReplaceWithHeadAction_label=&HEAD Revision
ReplaceWithCommitAction_label=&Commit...
ReplaceWithPreviousVersionAction.label = &Previous Revision
ReplaceWithRefAction_label=&Branch, Tag, or Reference...
+ReplaceWithOursAction_label=&Our Revision
+ReplaceWithTheirsAction_label=&Their Revision
FetchAction_label=&Fetch From...
FetchAction_tooltip=Fetch changes from upstream
PushAction_label=&Push...
@@ -209,6 +211,8 @@ CompareWithCommitCommand.name = Compare with Commit...
CompareWithHistoryCommand.name = Compare with Branch, Tag or Reference...
ReplaceWithMenu.label=Rep&lace With
ReplaceWithPreviousCommand.name = Replace with Previous Revision
+ReplaceWithOursCommand.name = Replace Conflicting Files with Our Revision
+ReplaceWithTheirsCommand.name = Replace Conflicting Files with Their Revision
OpenCommand.name = Open
CompareModeCommandParameter.name = Compare mode
CreatePatchCommand.name = Create Patch...
diff --git a/org.eclipse.egit.ui/plugin.xml b/org.eclipse.egit.ui/plugin.xml
index a6118946a5..fc38e2a02c 100644
--- a/org.eclipse.egit.ui/plugin.xml
+++ b/org.eclipse.egit.ui/plugin.xml
@@ -2377,6 +2377,24 @@
</activeWhen>
</handler>
<handler
+ commandId="org.eclipse.egit.ui.team.ReplaceWithOurs">
+ <class
+ class="org.eclipse.egit.ui.internal.actions.ReplaceWithOursActionHandler">
+ </class>
+ <activeWhen>
+ <test property="GitSelection.conflictsInSingleRepository" />
+ </activeWhen>
+ </handler>
+ <handler
+ commandId="org.eclipse.egit.ui.team.ReplaceWithTheirs">
+ <class
+ class="org.eclipse.egit.ui.internal.actions.ReplaceWithTheirsActionHandler">
+ </class>
+ <activeWhen>
+ <test property="GitSelection.conflictsInSingleRepository" />
+ </activeWhen>
+ </handler>
+ <handler
commandId="org.eclipse.egit.ui.RepositoriesViewImportProjects">
<class
class="org.eclipse.egit.ui.internal.repository.tree.command.ImportProjectsCommand">
@@ -6129,6 +6147,18 @@
</and>
</visibleWhen>
</command>
+ <command
+ commandId="org.eclipse.egit.ui.team.ReplaceWithOurs"
+ label="%ReplaceWithOursAction_label"
+ style="push">
+ <visibleWhen checkEnabled="true" />
+ </command>
+ <command
+ commandId="org.eclipse.egit.ui.team.ReplaceWithTheirs"
+ label="%ReplaceWithTheirsAction_label"
+ style="push">
+ <visibleWhen checkEnabled="true" />
+ </command>
</menuContribution>
<menuContribution
allPopups="false"
@@ -6796,7 +6826,7 @@
class="org.eclipse.egit.ui.internal.selection.SelectionPropertyTester"
id="org.eclipse.egit.ui.SelectionTester"
namespace="GitSelection"
- properties="projectsSingleRepository,projectsWithRepositories,resourcesSingleRepository,resourcesMultipleRepositories,fileOrFolderInRepository,resourcesAllInRepository,selectionSingleRepository,selectionMultipleRepositories"
+ properties="conflictsInSingleRepository,projectsSingleRepository,projectsWithRepositories,resourcesSingleRepository,resourcesMultipleRepositories,fileOrFolderInRepository,resourcesAllInRepository,selectionSingleRepository,selectionMultipleRepositories"
type="java.util.Collection">
</propertyTester>
<propertyTester
@@ -7010,6 +7040,16 @@
</command>
<command
categoryId="org.eclipse.egit.ui.commandCategory"
+ id="org.eclipse.egit.ui.team.ReplaceWithOurs"
+ name="%ReplaceWithOursCommand.name">
+ </command>
+ <command
+ categoryId="org.eclipse.egit.ui.commandCategory"
+ id="org.eclipse.egit.ui.team.ReplaceWithTheirs"
+ name="%ReplaceWithTheirsCommand.name">
+ </command>
+ <command
+ categoryId="org.eclipse.egit.ui.commandCategory"
defaultHandler="org.eclipse.egit.ui.internal.actions.IgnoreActionHandler"
id="org.eclipse.egit.ui.team.Ignore"
name="%IgnoreCommand.name">
diff --git a/org.eclipse.egit.ui/src/org/eclipse/egit/ui/internal/actions/ReplaceConflictActionHandler.java b/org.eclipse.egit.ui/src/org/eclipse/egit/ui/internal/actions/ReplaceConflictActionHandler.java
new file mode 100644
index 0000000000..b77f837282
--- /dev/null
+++ b/org.eclipse.egit.ui/src/org/eclipse/egit/ui/internal/actions/ReplaceConflictActionHandler.java
@@ -0,0 +1,231 @@
+/*******************************************************************************
+ * Copyright (C) 2021 Thomas Wolf <thomas.wolf@paranor.ch> and others.
+ *
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License 2.0
+ * which accompanies this distribution, and is available at
+ * https://www.eclipse.org/legal/epl-2.0/
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ *******************************************************************************/
+package org.eclipse.egit.ui.internal.actions;
+
+import java.io.IOException;
+import java.text.MessageFormat;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.List;
+import java.util.Map;
+import java.util.Map.Entry;
+
+import org.eclipse.core.commands.ExecutionEvent;
+import org.eclipse.core.commands.ExecutionException;
+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.jobs.ISchedulingRule;
+import org.eclipse.egit.core.EclipseGitProgressTransformer;
+import org.eclipse.egit.core.RevUtils;
+import org.eclipse.egit.core.internal.Utils;
+import org.eclipse.egit.core.internal.indexdiff.IndexDiffCache;
+import org.eclipse.egit.core.internal.indexdiff.IndexDiffCacheEntry;
+import org.eclipse.egit.core.internal.indexdiff.IndexDiffData;
+import org.eclipse.egit.core.internal.job.JobUtil;
+import org.eclipse.egit.core.internal.job.RuleUtil;
+import org.eclipse.egit.core.internal.util.ResourceUtil;
+import org.eclipse.egit.core.op.DiscardChangesOperation;
+import org.eclipse.egit.core.op.DiscardChangesOperation.Stage;
+import org.eclipse.egit.core.op.IEGitOperation;
+import org.eclipse.egit.ui.Activator;
+import org.eclipse.egit.ui.JobFamilies;
+import org.eclipse.egit.ui.internal.UIText;
+import org.eclipse.egit.ui.internal.selection.SelectionRepositoryStateCache;
+import org.eclipse.egit.ui.internal.selection.SelectionUtils;
+import org.eclipse.jgit.api.CheckoutCommand;
+import org.eclipse.jgit.api.Git;
+import org.eclipse.jgit.api.RmCommand;
+import org.eclipse.jgit.api.errors.GitAPIException;
+import org.eclipse.jgit.lib.IndexDiff.StageState;
+import org.eclipse.jgit.lib.Repository;
+import org.eclipse.jgit.revwalk.RevCommit;
+import org.eclipse.ui.PlatformUI;
+import org.eclipse.ui.commands.IElementUpdater;
+import org.eclipse.ui.menus.UIElement;
+import org.eclipse.ui.services.IEvaluationService;
+
+/**
+ * Action handler base class for replacing conflicting files with a particular
+ * index stage.
+ */
+public abstract class ReplaceConflictActionHandler
+ extends RepositoryActionHandler implements IElementUpdater {
+
+ private final DiscardChangesOperation.Stage stage;
+
+ /**
+ * Creates new instance.
+ *
+ * @param stage
+ * to check out
+ */
+ protected ReplaceConflictActionHandler(
+ DiscardChangesOperation.Stage stage) {
+ this.stage = stage;
+ }
+
+ @Override
+ public Object execute(ExecutionEvent event) throws ExecutionException {
+ IPath[] locations = getSelectedLocations(event);
+ if (locations == null || locations.length == 0) {
+ return null;
+ }
+ Map<Repository, Collection<String>> pathsByRepository = ResourceUtil
+ .splitPathsByRepository(Arrays.asList(locations));
+ if (pathsByRepository.size() != 1) {
+ return null;
+ }
+ Entry<Repository, Collection<String>> entry = pathsByRepository
+ .entrySet().iterator().next();
+ Repository repository = entry.getKey();
+ IndexDiffCacheEntry indexDiff = IndexDiffCache.getInstance()
+ .getIndexDiffCacheEntry(repository);
+ if (indexDiff == null) {
+ return null;
+ }
+ IndexDiffData data = indexDiff.getIndexDiff();
+ if (data == null) {
+ return null;
+ }
+ Map<String, StageState> conflictStates = data.getConflictStates();
+ List<String> toCheckout = new ArrayList<>();
+ List<String> toRemove = new ArrayList<>();
+ for (String path : entry.getValue()) {
+ StageState state = conflictStates.get(path);
+ if (StageState.DELETED_BY_THEM == state && stage == Stage.THEIRS
+ || StageState.DELETED_BY_US == state
+ && stage == Stage.OURS) {
+ toRemove.add(path);
+ } else {
+ toCheckout.add(path);
+ }
+ }
+ replaceWithStage(repository, stage, toCheckout, toRemove);
+ return null;
+ }
+
+ /**
+ * Check out or remove files from the given repository using the given index
+ * stage.
+ *
+ * @param repository
+ * to operate on
+ * @param stage
+ * to check out
+ * @param toCheckout
+ * repository-relative git paths of files to check out
+ * @param toRemove
+ * repository-relative git paths of files to remove
+ */
+ public static void replaceWithStage(Repository repository, Stage stage,
+ Collection<String> toCheckout, Collection<String> toRemove) {
+ if (toRemove.isEmpty()) {
+ DiscardChangesOperation operation = new DiscardChangesOperation(
+ repository, toCheckout);
+ operation.setStage(stage);
+ JobUtil.scheduleUserWorkspaceJob(operation,
+ UIText.DiscardChangesAction_discardChanges,
+ JobFamilies.DISCARD_CHANGES);
+ } else {
+ IEGitOperation operation = new IEGitOperation() {
+
+ @Override
+ public ISchedulingRule getSchedulingRule() {
+ return RuleUtil.getRule(repository);
+ }
+
+ @Override
+ public void execute(IProgressMonitor monitor)
+ throws CoreException {
+ IWorkspaceRunnable action = new IWorkspaceRunnable() {
+
+ @Override
+ public void run(IProgressMonitor progress)
+ throws CoreException {
+ ResourceUtil.saveLocalHistory(repository);
+ try (Git git = new Git(repository)) {
+ if (!toCheckout.isEmpty()) {
+ CheckoutCommand checkout = git.checkout()
+ .setProgressMonitor(
+ new EclipseGitProgressTransformer(
+ progress));
+
+ checkout.setStage(stage == Stage.OURS
+ ? CheckoutCommand.Stage.OURS
+ : CheckoutCommand.Stage.THEIRS);
+ for (String path : toCheckout) {
+ checkout.addPath(path);
+ }
+ checkout.call();
+ }
+ if (!toRemove.isEmpty()) {
+ RmCommand rm = git.rm();
+ for (String path : toRemove) {
+ rm.addFilepattern(path);
+ }
+ rm.call();
+ }
+ } catch (GitAPIException e) {
+ throw new CoreException(
+ Activator.createErrorStatus(
+ e.getLocalizedMessage(), e));
+ }
+ }
+ };
+ ResourcesPlugin.getWorkspace().run(action,
+ getSchedulingRule(), IWorkspace.AVOID_UPDATE,
+ monitor);
+ }
+ };
+ JobUtil.scheduleUserWorkspaceJob(operation,
+ UIText.DiscardChangesAction_discardChanges,
+ JobFamilies.DISCARD_CHANGES);
+ }
+ }
+
+ @Override
+ public void updateElement(UIElement element, Map parameters) {
+ Repository repository = SelectionUtils.getRepository(
+ PlatformUI.getWorkbench().getService(IEvaluationService.class)
+ .getCurrentState());
+ if (DiscardChangesOperation.Stage.OURS == stage) {
+ RevCommit commit = SelectionRepositoryStateCache.INSTANCE
+ .getHeadCommit(repository);
+ if (commit != null) {
+ element.setText(formatCommit(
+ UIText.ReplaceWithOursTheirsMenu_OursWithCommitLabel,
+ commit));
+ }
+ } else {
+ try {
+ RevCommit commit = RevUtils.getTheirs(repository);
+ if (commit != null) {
+ element.setText(formatCommit(
+ UIText.ReplaceWithOursTheirsMenu_TheirsWithCommitLabel,
+ commit));
+ }
+ } catch (IOException e) {
+ Activator.logError(e.getLocalizedMessage(), e);
+ }
+ }
+ }
+
+ private static String formatCommit(String format, RevCommit commit) {
+ String message = Utils.shortenText(commit.getShortMessage(), 60);
+ return MessageFormat.format(format, Utils.getShortObjectId(commit),
+ message);
+ }
+}
diff --git a/org.eclipse.egit.ui/src/org/eclipse/egit/ui/internal/actions/ReplaceWithOursActionHandler.java b/org.eclipse.egit.ui/src/org/eclipse/egit/ui/internal/actions/ReplaceWithOursActionHandler.java
new file mode 100644
index 0000000000..d318d3b2c2
--- /dev/null
+++ b/org.eclipse.egit.ui/src/org/eclipse/egit/ui/internal/actions/ReplaceWithOursActionHandler.java
@@ -0,0 +1,26 @@
+/*******************************************************************************
+ * Copyright (C) 2021 Thomas Wolf <thomas.wolf@paranor.ch> and others.
+ *
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License 2.0
+ * which accompanies this distribution, and is available at
+ * https://www.eclipse.org/legal/epl-2.0/
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ *******************************************************************************/
+package org.eclipse.egit.ui.internal.actions;
+
+import org.eclipse.egit.core.op.DiscardChangesOperation;
+
+/**
+ * Action handler to replace selected conflicting files with 'our' version.
+ */
+public class ReplaceWithOursActionHandler extends ReplaceConflictActionHandler {
+
+ /**
+ * Creates a new instance.
+ */
+ public ReplaceWithOursActionHandler() {
+ super(DiscardChangesOperation.Stage.OURS);
+ }
+}
diff --git a/org.eclipse.egit.ui/src/org/eclipse/egit/ui/internal/actions/ReplaceWithTheirsActionHandler.java b/org.eclipse.egit.ui/src/org/eclipse/egit/ui/internal/actions/ReplaceWithTheirsActionHandler.java
new file mode 100644
index 0000000000..68fdbd3d05
--- /dev/null
+++ b/org.eclipse.egit.ui/src/org/eclipse/egit/ui/internal/actions/ReplaceWithTheirsActionHandler.java
@@ -0,0 +1,26 @@
+/*******************************************************************************
+ * Copyright (C) 2021 Thomas Wolf <thomas.wolf@paranor.ch> and others.
+ *
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License 2.0
+ * which accompanies this distribution, and is available at
+ * https://www.eclipse.org/legal/epl-2.0/
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ *******************************************************************************/
+package org.eclipse.egit.ui.internal.actions;
+
+import org.eclipse.egit.core.op.DiscardChangesOperation;
+
+/**
+ * Action handler to replace selected conflicting files with 'their' version.
+ */
+public class ReplaceWithTheirsActionHandler extends ReplaceConflictActionHandler {
+
+ /**
+ * Creates a new instance.
+ */
+ public ReplaceWithTheirsActionHandler() {
+ super(DiscardChangesOperation.Stage.THEIRS);
+ }
+}
diff --git a/org.eclipse.egit.ui/src/org/eclipse/egit/ui/internal/selection/SelectionPropertyTester.java b/org.eclipse.egit.ui/src/org/eclipse/egit/ui/internal/selection/SelectionPropertyTester.java
index 9ff536a22e..b17acbbb72 100644
--- a/org.eclipse.egit.ui/src/org/eclipse/egit/ui/internal/selection/SelectionPropertyTester.java
+++ b/org.eclipse.egit.ui/src/org/eclipse/egit/ui/internal/selection/SelectionPropertyTester.java
@@ -29,7 +29,12 @@ import org.eclipse.core.resources.IProject;
import org.eclipse.core.resources.IResource;
import org.eclipse.core.runtime.Adapters;
import org.eclipse.core.runtime.IAdaptable;
+import org.eclipse.core.runtime.IPath;
import org.eclipse.egit.core.AdapterUtils;
+import org.eclipse.egit.core.internal.indexdiff.IndexDiffCache;
+import org.eclipse.egit.core.internal.indexdiff.IndexDiffCacheEntry;
+import org.eclipse.egit.core.internal.indexdiff.IndexDiffData;
+import org.eclipse.egit.core.internal.util.ResourceUtil;
import org.eclipse.egit.core.project.RepositoryMapping;
import org.eclipse.egit.ui.internal.ResourcePropertyTester;
import org.eclipse.egit.ui.internal.expressions.AbstractPropertyTester;
@@ -90,6 +95,48 @@ public class SelectionPropertyTester extends AbstractPropertyTester {
Repository repository = getRepositoryOfResources(resources);
return testRepositoryProperties(repository, args);
+ } else if ("conflictsInSingleRepository".equals(property)) { //$NON-NLS-1$
+ IStructuredSelection selection = getStructuredSelection(collection);
+ IResource[] resources = SelectionUtils
+ .getSelectedResources(selection);
+ Repository repository = getRepositoryOfResources(resources);
+ if (repository == null
+ || !testRepositoryProperties(repository, args)) {
+ return false;
+ }
+ IndexDiffCacheEntry indexDiff = IndexDiffCache.getInstance()
+ .getIndexDiffCacheEntry(repository);
+ if (indexDiff == null) {
+ return false;
+ }
+ IndexDiffData data = indexDiff.getIndexDiff();
+ if (data == null) {
+ return false;
+ }
+ Set<String> conflicts = data.getConflicting();
+ if (conflicts.isEmpty()) {
+ return false;
+ }
+ for (IResource rsc : resources) {
+ IFile file = Adapters.adapt(rsc, IFile.class);
+ if (file == null) {
+ return false;
+ }
+ IPath location = file.getLocation();
+ if (location == null) {
+ return false;
+ }
+ IPath relativePath = ResourceUtil
+ .getRepositoryRelativePath(location, repository);
+ if (relativePath == null || relativePath.isEmpty()) {
+ return false;
+ }
+ if (!conflicts.contains(relativePath.toString())) {
+ return false;
+ }
+ }
+ return true;
+
} else if ("fileOrFolderInRepository".equals(property)) { //$NON-NLS-1$
if (collection.size() != 1)
return false;
diff --git a/org.eclipse.egit.ui/src/org/eclipse/egit/ui/internal/staging/ReplaceConflictMenu.java b/org.eclipse.egit.ui/src/org/eclipse/egit/ui/internal/staging/ReplaceConflictMenu.java
index efb90d2b04..da841a873d 100644
--- a/org.eclipse.egit.ui/src/org/eclipse/egit/ui/internal/staging/ReplaceConflictMenu.java
+++ b/org.eclipse.egit.ui/src/org/eclipse/egit/ui/internal/staging/ReplaceConflictMenu.java
@@ -15,32 +15,16 @@ import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
-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.IProgressMonitor;
-import org.eclipse.core.runtime.jobs.ISchedulingRule;
-import org.eclipse.egit.core.EclipseGitProgressTransformer;
import org.eclipse.egit.core.RevUtils;
import org.eclipse.egit.core.RevUtils.ConflictCommits;
import org.eclipse.egit.core.internal.Utils;
-import org.eclipse.egit.core.internal.job.JobUtil;
-import org.eclipse.egit.core.internal.job.RuleUtil;
-import org.eclipse.egit.core.internal.util.ResourceUtil;
-import org.eclipse.egit.core.op.DiscardChangesOperation;
import org.eclipse.egit.core.op.DiscardChangesOperation.Stage;
-import org.eclipse.egit.core.op.IEGitOperation;
import org.eclipse.egit.ui.Activator;
-import org.eclipse.egit.ui.JobFamilies;
import org.eclipse.egit.ui.internal.UIText;
+import org.eclipse.egit.ui.internal.actions.ReplaceConflictActionHandler;
import org.eclipse.jface.action.Action;
import org.eclipse.jface.action.ActionContributionItem;
import org.eclipse.jface.action.IContributionItem;
-import org.eclipse.jgit.api.CheckoutCommand;
-import org.eclipse.jgit.api.Git;
-import org.eclipse.jgit.api.RmCommand;
-import org.eclipse.jgit.api.errors.GitAPIException;
import org.eclipse.jgit.lib.IndexDiff.StageState;
import org.eclipse.jgit.lib.Repository;
import org.eclipse.jgit.revwalk.RevCommit;
@@ -187,71 +171,8 @@ public class ReplaceConflictMenu extends CompoundContributionItem {
toCheckout.add(entry.getPath());
}
}
- if (toRemove.isEmpty()) {
- DiscardChangesOperation operation = new DiscardChangesOperation(
- repository, toCheckout);
- operation.setStage(stage);
- JobUtil.scheduleUserWorkspaceJob(operation,
- UIText.DiscardChangesAction_discardChanges,
- JobFamilies.DISCARD_CHANGES);
- } else {
- IEGitOperation operation = new IEGitOperation() {
-
- @Override
- public ISchedulingRule getSchedulingRule() {
- return RuleUtil.getRule(repository);
- }
-
- @Override
- public void execute(IProgressMonitor monitor)
- throws CoreException {
- IWorkspaceRunnable action = new IWorkspaceRunnable() {
-
- @Override
- public void run(IProgressMonitor progress)
- throws CoreException {
- ResourceUtil.saveLocalHistory(repository);
- try (Git git = new Git(repository)) {
- if (!toCheckout.isEmpty()) {
- CheckoutCommand checkout = git
- .checkout().setProgressMonitor(
- new EclipseGitProgressTransformer(
- progress));
-
- checkout
- .setStage(stage == Stage.OURS
- ? CheckoutCommand.Stage.OURS
- : CheckoutCommand.Stage.THEIRS);
- for (String path : toCheckout) {
- checkout.addPath(path);
- }
- checkout.call();
- }
- if (!toRemove.isEmpty()) {
- RmCommand rm = git.rm();
- for (String path : toRemove) {
- rm.addFilepattern(path);
- }
- rm.call();
- }
- } catch (GitAPIException e) {
- throw new CoreException(
- Activator.createErrorStatus(
- e.getLocalizedMessage(),
- e));
- }
- }
- };
- ResourcesPlugin.getWorkspace().run(action,
- getSchedulingRule(), IWorkspace.AVOID_UPDATE,
- monitor);
- }
- };
- JobUtil.scheduleUserWorkspaceJob(operation,
- UIText.DiscardChangesAction_discardChanges,
- JobFamilies.DISCARD_CHANGES);
-
- }
+ ReplaceConflictActionHandler.replaceWithStage(repository, stage,
+ toCheckout, toRemove);
}
}
}

Back to the top