Skip to main content
aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorLuís Copetti2018-09-20 03:24:42 +0000
committerLuís Copetti2018-10-05 03:23:26 +0000
commit7ae6201ee08ac03749488509af1ccac0755e5c43 (patch)
tree3944f5fc64c1a3e1626082787897ee6055d9cb99
parent096d9b869f5876379b6353241327a45f2230a195 (diff)
downloadegit-7ae6201ee08ac03749488509af1ccac0755e5c43.tar.gz
egit-7ae6201ee08ac03749488509af1ccac0755e5c43.tar.xz
egit-7ae6201ee08ac03749488509af1ccac0755e5c43.zip
Multi branch checkout operations are performed as a single eclipse job
The running launches, the branch project tracker and refreshing of affected projects afterwards are respected and happen in a single eclipse job instance. Change-Id: If03d5a122f5157b8d4f46c265b2590007bbf331c Signed-off-by: Luís Copetti <lhcopetti@gmail.com>
-rw-r--r--org.eclipse.egit.core/src/org/eclipse/egit/core/op/BaseOperation.java92
-rw-r--r--org.eclipse.egit.core/src/org/eclipse/egit/core/op/BranchOperation.java129
-rw-r--r--org.eclipse.egit.gitflow/src/org/eclipse/egit/gitflow/op/FeatureCheckoutOperation.java6
-rw-r--r--org.eclipse.egit.gitflow/src/org/eclipse/egit/gitflow/op/FeatureTrackOperation.java8
-rw-r--r--org.eclipse.egit.gitflow/src/org/eclipse/egit/gitflow/op/GitFlowOperation.java13
-rw-r--r--org.eclipse.egit.ui/src/org/eclipse/egit/ui/internal/UIText.java42
-rw-r--r--org.eclipse.egit.ui/src/org/eclipse/egit/ui/internal/actions/SwitchToMenu.java29
-rw-r--r--org.eclipse.egit.ui/src/org/eclipse/egit/ui/internal/branch/BranchOperationUI.java329
-rw-r--r--org.eclipse.egit.ui/src/org/eclipse/egit/ui/internal/branch/BranchProjectTracker.java62
-rw-r--r--org.eclipse.egit.ui/src/org/eclipse/egit/ui/internal/branch/MultiBranchOperationResultDialog.java254
-rw-r--r--org.eclipse.egit.ui/src/org/eclipse/egit/ui/internal/uitext.properties20
11 files changed, 704 insertions, 280 deletions
diff --git a/org.eclipse.egit.core/src/org/eclipse/egit/core/op/BaseOperation.java b/org.eclipse.egit.core/src/org/eclipse/egit/core/op/BaseOperation.java
deleted file mode 100644
index 481bf4a1ef..0000000000
--- a/org.eclipse.egit.core/src/org/eclipse/egit/core/op/BaseOperation.java
+++ /dev/null
@@ -1,92 +0,0 @@
-/******************************************************************************
- * Copyright (c) 2012 GitHub Inc.
- * 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
- *
- * Contributors:
- * Kevin Sawicki (GitHub Inc.) - initial API and implementation
- * Stephan Hackstedt <stephan.hackstedt@googlemail.com> - Bug 477695
- *****************************************************************************/
-package org.eclipse.egit.core.op;
-
-import java.util.ArrayList;
-import java.util.Collection;
-
-import org.eclipse.core.runtime.CoreException;
-import org.eclipse.core.runtime.IProgressMonitor;
-import org.eclipse.core.runtime.SubMonitor;
-import org.eclipse.jgit.lib.Repository;
-
-/**
- * Base operation that supports adding pre/post tasks
- */
-abstract class BaseOperation implements IEGitOperation {
-
- protected final Repository repository;
-
- protected Collection<PreExecuteTask> preTasks;
-
- protected Collection<PostExecuteTask> postTasks;
-
- BaseOperation(final Repository repository) {
- this.repository = repository;
- }
-
- /**
- * Invoke all pre-execute tasks
- *
- * @param monitor
- * @throws CoreException
- */
- protected void preExecute(IProgressMonitor monitor) throws CoreException {
- synchronized (this) {
- if (preTasks != null) {
- SubMonitor progress = SubMonitor.convert(monitor,
- preTasks.size());
- for (PreExecuteTask task : preTasks)
- task.preExecute(repository, progress.newChild(1));
- }
- }
- }
-
- /**
- * Invoke all post-execute tasks
- *
- * @param monitor
- * @throws CoreException
- */
- protected void postExecute(IProgressMonitor monitor) throws CoreException {
- synchronized (this) {
- if (postTasks != null) {
- SubMonitor progress = SubMonitor.convert(monitor,
- postTasks.size());
- for (PostExecuteTask task : postTasks)
- task.postExecute(repository, progress.newChild(1));
- }
- }
- }
-
- /**
- * @param task
- * to be performed before execution
- */
- public synchronized void addPreExecuteTask(final PreExecuteTask task) {
- if (preTasks == null)
- preTasks = new ArrayList<PreExecuteTask>();
- preTasks.add(task);
- }
-
- /**
- * @param task
- * to be performed after execution
- */
- public synchronized void addPostExecuteTask(PostExecuteTask task) {
- if (postTasks == null)
- postTasks = new ArrayList<PostExecuteTask>();
- postTasks.add(task);
- }
-}
diff --git a/org.eclipse.egit.core/src/org/eclipse/egit/core/op/BranchOperation.java b/org.eclipse.egit.core/src/org/eclipse/egit/core/op/BranchOperation.java
index 562f2b69f1..64eb511af5 100644
--- a/org.eclipse.egit.core/src/org/eclipse/egit/core/op/BranchOperation.java
+++ b/org.eclipse.egit.core/src/org/eclipse/egit/core/op/BranchOperation.java
@@ -19,9 +19,12 @@ import java.io.File;
import java.io.IOException;
import java.text.MessageFormat;
import java.util.ArrayList;
+import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
+import java.util.Map.Entry;
+import java.util.stream.Stream;
import org.eclipse.core.resources.IProject;
import org.eclipse.core.resources.IProjectDescription;
@@ -33,7 +36,6 @@ import org.eclipse.core.runtime.IPath;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.SubMonitor;
import org.eclipse.core.runtime.jobs.ISchedulingRule;
-import org.eclipse.egit.core.Activator;
import org.eclipse.egit.core.EclipseGitProgressTransformer;
import org.eclipse.egit.core.internal.CoreText;
import org.eclipse.egit.core.internal.job.RuleUtil;
@@ -64,11 +66,13 @@ import org.eclipse.osgi.util.NLS;
* This class implements checkouts of a specific revision. A check is made that
* this can be done without data loss.
*/
-public class BranchOperation extends BaseOperation {
+public class BranchOperation implements IEGitOperation {
private final String target;
- private @NonNull CheckoutResult result = CheckoutResult.NOT_TRIED_RESULT;
+ private Repository[] repositories;
+
+ private @NonNull Map<Repository, CheckoutResult> results = new HashMap<>();
private boolean delete;
@@ -94,7 +98,18 @@ public class BranchOperation extends BaseOperation {
* them
*/
public BranchOperation(Repository repository, String target, boolean delete) {
- super(repository);
+ this(new Repository[] { repository }, target, delete);
+ }
+
+ /**
+ *
+ * @param repositories
+ * @param target
+ * @param delete
+ */
+ public BranchOperation(Repository[] repositories, String target,
+ boolean delete) {
+ this.repositories = repositories;
this.target = target;
this.delete = delete;
}
@@ -106,11 +121,28 @@ public class BranchOperation extends BaseOperation {
@Override
public void run(IProgressMonitor pm) throws CoreException {
SubMonitor progress = SubMonitor.convert(pm, 4);
- preExecute(progress.newChild(1));
- closeProjectsMissingAfterCheckout(progress);
- try (Git git = new Git(repository)) {
+ for (Repository repository : repositories) {
+
+ CheckoutResult result = checkoutRepository(repository,
+ progress);
+
+ if (result.getStatus() == Status.NONDELETED) {
+ retryDelete(repository, result.getUndeletedList());
+ }
+
+ results.put(repository, result);
+ }
+ refreshAffectedProjects(progress);
+ }
+
+ public CheckoutResult checkoutRepository(Repository repo,
+ SubMonitor progress) throws CoreException {
+
+ closeProjectsMissingAfterCheckout(repo, progress);
+
+ try (Git git = new Git(repo)) {
CheckoutCommand co = git.checkout().setProgressMonitor(
new EclipseGitProgressTransformer(
progress.newChild(1)));
@@ -118,30 +150,23 @@ public class BranchOperation extends BaseOperation {
try {
co.call();
+ // The below exceptions are handled by the
+ // CheckoutCommand's result status which is returned
} catch (CheckoutConflictException e) {
- return;
+ // ignore
} catch (JGitInternalException e) {
- throw new CoreException(
- Activator.error(e.getMessage(), e));
+ // ignore
} catch (GitAPIException e) {
- throw new CoreException(
- Activator.error(e.getMessage(), e));
- } finally {
- result = co.getResult();
+ // ignore
}
- if (result.getStatus() == Status.NONDELETED) {
- retryDelete(result.getUndeletedList());
- }
- refreshAffectedProjects(progress);
-
- postExecute(progress.newChild(1));
+ return co.getResult();
}
}
- private void closeProjectsMissingAfterCheckout(SubMonitor progress)
+ private void closeProjectsMissingAfterCheckout(Repository repo,
+ SubMonitor progress)
throws CoreException {
- IProject[] missing = getMissingProjects(target, ProjectUtil
- .getValidOpenProjects(repository));
+ IProject[] missing = getMissingProjects(repo, target);
progress.setTaskName(NLS.bind(
CoreText.BranchOperation_performingBranch, target));
@@ -161,14 +186,36 @@ public class BranchOperation extends BaseOperation {
private void refreshAffectedProjects(SubMonitor progress)
throws CoreException {
+
+ IProject[] refreshProjects = results.entrySet().stream() //
+ .map(this::getAffectedProjects) //
+ .flatMap(arr -> Stream.of(arr)) //
+ .distinct()
+ .toArray(IProject[]::new);
+
+ ProjectUtil.refreshValidProjects(refreshProjects, delete,
+ progress.newChild(1));
+ }
+
+ private IProject[] getAffectedProjects(
+ Entry<Repository, CheckoutResult> entry)
+ {
+ CheckoutResult result = entry.getValue();
+
+ if (result.getStatus() != Status.OK
+ && result.getStatus() != Status.NONDELETED) {
+ // the checkout did not succeed
+ return new IProject[0];
+ }
+
+ Repository repo = entry.getKey();
List<String> pathsToHandle = new ArrayList<>();
pathsToHandle.addAll(result.getModifiedList());
pathsToHandle.addAll(result.getRemovedList());
pathsToHandle.addAll(result.getConflictList());
IProject[] refreshProjects = ProjectUtil
- .getProjectsContaining(repository, pathsToHandle);
- ProjectUtil.refreshValidProjects(refreshProjects, delete,
- progress.newChild(1));
+ .getProjectsContaining(repo, pathsToHandle);
+ return refreshProjects;
}
};
// lock workspace to protect working tree changes
@@ -178,24 +225,32 @@ public class BranchOperation extends BaseOperation {
@Override
public ISchedulingRule getSchedulingRule() {
- return RuleUtil.getRule(repository);
+ return RuleUtil.getRuleForRepositories(Arrays.asList(repositories));
}
/**
* @return the result of the operation
*/
@NonNull
- public CheckoutResult getResult() {
- return result;
+ public Map<Repository, CheckoutResult> getResults() {
+ return results;
}
- void retryDelete(List<String> pathList) {
+ /**
+ * @param repo
+ * @return return the result specific to a repository
+ */
+ public CheckoutResult getResult(Repository repo) {
+ return results.get(repo);
+ }
+
+ void retryDelete(Repository repo, List<String> pathList) {
// try to delete, but for a short time only
long startTime = System.currentTimeMillis();
for (String path : pathList) {
if (System.currentTimeMillis() - startTime > 1000)
break;
- File fileToDelete = new File(repository.getWorkTree(), path);
+ File fileToDelete = new File(repo.getWorkTree(), path);
if (fileToDelete.exists())
try {
// Only files should be passed here, thus
@@ -213,13 +268,15 @@ public class BranchOperation extends BaseOperation {
* Compute the current projects that will be missing after the given branch
* is checked out
*
+ * @param repository
* @param branch
- * @param currentProjects
* @return non-null but possibly empty array of missing projects
+ * @throws CoreException
*/
- private IProject[] getMissingProjects(String branch,
- IProject[] currentProjects) {
- if (delete || currentProjects.length == 0)
+ private IProject[] getMissingProjects(Repository repository,
+ String branch) throws CoreException {
+ IProject[] openProjects = ProjectUtil.getValidOpenProjects(repository);
+ if (delete || openProjects.length == 0)
return new IProject[0];
ObjectId targetTreeId;
@@ -234,7 +291,7 @@ public class BranchOperation extends BaseOperation {
return new IProject[0];
Map<File, IProject> locations = new HashMap<>();
- for (IProject project : currentProjects) {
+ for (IProject project : openProjects) {
IPath location = project.getLocation();
if (location == null)
continue;
diff --git a/org.eclipse.egit.gitflow/src/org/eclipse/egit/gitflow/op/FeatureCheckoutOperation.java b/org.eclipse.egit.gitflow/src/org/eclipse/egit/gitflow/op/FeatureCheckoutOperation.java
index 765819d673..d8b6426637 100644
--- a/org.eclipse.egit.gitflow/src/org/eclipse/egit/gitflow/op/FeatureCheckoutOperation.java
+++ b/org.eclipse.egit.gitflow/src/org/eclipse/egit/gitflow/op/FeatureCheckoutOperation.java
@@ -15,6 +15,7 @@ import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.egit.core.op.BranchOperation;
import org.eclipse.egit.gitflow.GitFlowRepository;
import org.eclipse.jgit.api.CheckoutResult;
+import org.eclipse.jgit.lib.Repository;
/**
* git flow feature checkout
@@ -36,10 +37,11 @@ public final class FeatureCheckoutOperation extends AbstractFeatureOperation {
String branchName = repository.getConfig().getFeatureBranchName(featureName);
boolean dontCloseProjects = false;
+ Repository gitRepo = repository.getRepository();
BranchOperation branchOperation = new BranchOperation(
- repository.getRepository(), branchName, dontCloseProjects);
+ gitRepo, branchName, dontCloseProjects);
branchOperation.execute(monitor);
- result = branchOperation.getResult();
+ result = branchOperation.getResult(gitRepo);
}
/**
diff --git a/org.eclipse.egit.gitflow/src/org/eclipse/egit/gitflow/op/FeatureTrackOperation.java b/org.eclipse.egit.gitflow/src/org/eclipse/egit/gitflow/op/FeatureTrackOperation.java
index 72d2b0ba2a..5ad00894be 100644
--- a/org.eclipse.egit.gitflow/src/org/eclipse/egit/gitflow/op/FeatureTrackOperation.java
+++ b/org.eclipse.egit.gitflow/src/org/eclipse/egit/gitflow/op/FeatureTrackOperation.java
@@ -30,6 +30,7 @@ import org.eclipse.jgit.api.CheckoutResult.Status;
import org.eclipse.jgit.api.errors.GitAPIException;
import org.eclipse.jgit.lib.BranchConfig.BranchRebaseMode;
import org.eclipse.jgit.lib.Ref;
+import org.eclipse.jgit.lib.Repository;
import org.eclipse.jgit.transport.FetchResult;
import org.eclipse.osgi.util.NLS;
@@ -95,10 +96,11 @@ public final class FeatureTrackOperation extends AbstractFeatureOperation {
BranchRebaseMode.NONE);
createLocalBranchOperation.execute(progress.newChild(1));
- BranchOperation branchOperation = new BranchOperation(
- repository.getRepository(), newLocalBranch);
+ Repository gitRepo = repository.getRepository();
+ BranchOperation branchOperation = new BranchOperation(gitRepo,
+ newLocalBranch);
branchOperation.execute(progress.newChild(1));
- CheckoutResult result = branchOperation.getResult();
+ CheckoutResult result = branchOperation.getResult(gitRepo);
if (!Status.OK.equals(result.getStatus())) {
String errorMessage = NLS.bind(
CoreText.FeatureTrackOperation_checkoutReturned,
diff --git a/org.eclipse.egit.gitflow/src/org/eclipse/egit/gitflow/op/GitFlowOperation.java b/org.eclipse.egit.gitflow/src/org/eclipse/egit/gitflow/op/GitFlowOperation.java
index b3c3e7c1a8..b50fe8a0f2 100644
--- a/org.eclipse.egit.gitflow/src/org/eclipse/egit/gitflow/op/GitFlowOperation.java
+++ b/org.eclipse.egit.gitflow/src/org/eclipse/egit/gitflow/op/GitFlowOperation.java
@@ -36,6 +36,7 @@ import org.eclipse.jgit.api.CheckoutResult.Status;
import org.eclipse.jgit.api.MergeResult;
import org.eclipse.jgit.api.errors.GitAPIException;
import org.eclipse.jgit.lib.Ref;
+import org.eclipse.jgit.lib.Repository;
import org.eclipse.jgit.revwalk.RevCommit;
import org.eclipse.jgit.revwalk.RevWalk;
import org.eclipse.jgit.revwalk.RevWalkUtils;
@@ -179,18 +180,18 @@ abstract public class GitFlowOperation implements IEGitOperation {
}
SubMonitor progress = SubMonitor.convert(monitor, 2);
boolean dontCloseProjects = false;
- BranchOperation branchOperation = new BranchOperation(
- repository.getRepository(), targetBranchName,
- dontCloseProjects);
+ Repository gitRepo = repository.getRepository();
+ BranchOperation branchOperation = new BranchOperation(gitRepo,
+ targetBranchName, dontCloseProjects);
branchOperation.execute(progress.newChild(1));
- Status status = branchOperation.getResult().getStatus();
+ Status status = branchOperation.getResult(gitRepo).getStatus();
if (!CheckoutResult.Status.OK.equals(status)) {
throw new CoreException(error(NLS.bind(
CoreText.GitFlowOperation_unableToCheckout, branchName,
status.toString())));
}
- MergeOperation mergeOperation = new MergeOperation(
- repository.getRepository(), branchName);
+ MergeOperation mergeOperation = new MergeOperation(gitRepo,
+ branchName);
mergeOperation.setSquash(squash);
if (squash) {
mergeOperation.setCommit(true);
diff --git a/org.eclipse.egit.ui/src/org/eclipse/egit/ui/internal/UIText.java b/org.eclipse.egit.ui/src/org/eclipse/egit/ui/internal/UIText.java
index 4af01c8a24..061316b37f 100644
--- a/org.eclipse.egit.ui/src/org/eclipse/egit/ui/internal/UIText.java
+++ b/org.eclipse.egit.ui/src/org/eclipse/egit/ui/internal/UIText.java
@@ -3145,6 +3145,9 @@ public class UIText extends NLS {
public static String BranchAction_checkingOut;
/** */
+ public static String BranchAction_checkingOutMultiple;
+
+ /** */
public static String BranchAction_repositoryState;
/** */
@@ -3166,6 +3169,12 @@ public class UIText extends NLS {
public static String BranchConfigurationDialog_UpstreamBranchLabel;
/** */
+ public static String BranchOperationUI_CheckoutError_DialogMessage;
+
+ /** */
+ public static String BranchOperationUI_CheckoutError_DialogTitle;
+
+ /** */
public static String BranchOperationUI_CheckoutRemoteTrackingAsLocal;
/** */
@@ -4236,6 +4245,39 @@ public class UIText extends NLS {
public static String MultiPullResultDialog_WindowTitle;
/** */
+ public static String MultiBranchOperationResultDialog_WindowTitle;
+
+ /** */
+ public static String MultiBranchOperationResultDialog_RepositoryColumnHeader;
+
+ /** */
+ public static String MultiBranchOperationResultDialog_CheckoutStatusColumnHeader;
+
+ /** */
+ public static String MultiBranchOperationResultDialog_DialogTitle;
+
+ /** */
+ public static String MultiBranchOperationResultDialog_DialogErrorMessage;
+
+ /** */
+ public static String MultiBranchOperationResultDialog_CheckoutResultError;
+
+ /** */
+ public static String MultiBranchOperationResultDialog_CheckoutResultNonDeleted;
+
+ /** */
+ public static String MultiBranchOperationResultDialog_CheckoutResultConflicts;
+
+ /** */
+ public static String MultiBranchOperationResultDialog_CheckoutResultOK;
+
+ /** */
+ public static String MultiBranchOperationResultDialog_CheckoutResultNotTried;
+
+ /** */
+ public static String MultiBranchOperationResultDialog_OkStatus;
+
+ /** */
public static String UIIcons_errorDeterminingIconBase;
/** */
diff --git a/org.eclipse.egit.ui/src/org/eclipse/egit/ui/internal/actions/SwitchToMenu.java b/org.eclipse.egit.ui/src/org/eclipse/egit/ui/internal/actions/SwitchToMenu.java
index c9b63c3828..414ccdd9f2 100644
--- a/org.eclipse.egit.ui/src/org/eclipse/egit/ui/internal/actions/SwitchToMenu.java
+++ b/org.eclipse.egit.ui/src/org/eclipse/egit/ui/internal/actions/SwitchToMenu.java
@@ -20,9 +20,9 @@ import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
-import java.util.Map.Entry;
import java.util.Set;
import java.util.TreeSet;
+import java.util.stream.Stream;
import org.eclipse.egit.ui.Activator;
import org.eclipse.egit.ui.internal.CommonUtils;
@@ -266,16 +266,9 @@ public class SwitchToMenu extends ContributionItem implements
final MenuItem item = new MenuItem(menu, SWT.PUSH);
item.setText(shortName);
- boolean allRepositoriesCheckedOut = true;
- Map<Repository, String> repoToFullNameMap = new HashMap<>();
- for (Repository repository : repositories) {
- Map<String, Ref> refs = repository.getRefDatabase()
- .getRefs(Constants.R_HEADS);
- String fullName = refs.get(shortName).getName();
- repoToFullNameMap.put(repository, fullName);
- allRepositoriesCheckedOut &= fullName
- .equals(repository.getFullBranch());
- }
+ boolean allRepositoriesCheckedOut = Stream.of(repositories) //
+ .allMatch(r -> shortName.equals(getBranch(r)));
+
if (allRepositoriesCheckedOut)
item.setImage(checkedOutImage);
else
@@ -286,15 +279,19 @@ public class SwitchToMenu extends ContributionItem implements
@Override
public void widgetSelected(SelectionEvent e) {
- for (Entry<Repository, String> entry : repoToFullNameMap
- .entrySet()) {
- BranchOperationUI.checkout(entry.getKey(), entry.getValue())
- .start();
- }
+ BranchOperationUI.checkout(repositories, shortName).start();
}
});
}
+ private String getBranch(Repository repo) {
+ try {
+ return repo.getBranch();
+ } catch (IOException e) {
+ return ""; //$NON-NLS-1$
+ }
+ }
+
private Map<String, Ref> getMostActiveBranches(final Repository repository,
int maximumBranchCount) throws IOException {
Map<String, Ref> localBranches = repository.getRefDatabase()
diff --git a/org.eclipse.egit.ui/src/org/eclipse/egit/ui/internal/branch/BranchOperationUI.java b/org.eclipse.egit.ui/src/org/eclipse/egit/ui/internal/branch/BranchOperationUI.java
index a7755423cf..fa2bf093cd 100644
--- a/org.eclipse.egit.ui/src/org/eclipse/egit/ui/internal/branch/BranchOperationUI.java
+++ b/org.eclipse.egit.ui/src/org/eclipse/egit/ui/internal/branch/BranchOperationUI.java
@@ -14,7 +14,12 @@
package org.eclipse.egit.ui.internal.branch;
import java.io.File;
+import java.util.Arrays;
+import java.util.Collection;
import java.util.List;
+import java.util.Map;
+import java.util.Optional;
+import java.util.stream.Stream;
import org.eclipse.core.resources.IWorkspace;
import org.eclipse.core.resources.IWorkspaceRunnable;
@@ -34,7 +39,6 @@ import org.eclipse.egit.ui.Activator;
import org.eclipse.egit.ui.JobFamilies;
import org.eclipse.egit.ui.UIPreferences;
import org.eclipse.egit.ui.internal.UIText;
-import org.eclipse.egit.ui.internal.branch.BranchProjectTracker;
import org.eclipse.egit.ui.internal.decorators.GitLightweightDecorator;
import org.eclipse.egit.ui.internal.dialogs.NonDeletedFilesDialog;
import org.eclipse.egit.ui.internal.repository.CreateBranchWizard;
@@ -57,19 +61,36 @@ import org.eclipse.ui.PlatformUI;
*/
public class BranchOperationUI {
- private final Repository repository;
+ private final Repository[] repositories;
private String target;
+ private boolean isSingleRepositoryOperation;
+
/**
* In the case of checkout conflicts, a dialog is shown to let the user
* stash, reset or commit. After that, checkout is tried again. The second
* time we do checkout, we don't want to ask any questions we already asked
* the first time, so this will be false then.
+ *
+ * This behavior is disabled when checking out multiple repositories at once
*/
private final boolean showQuestionsBeforeCheckout;
/**
+ * Create an operation for checking out a branch on multiple repositories
+ *
+ * @param repositories
+ * @param target
+ * a valid {@link Ref} name or commit id
+ * @return the {@link BranchOperationUI}
+ */
+ public static BranchOperationUI checkout(Repository[] repositories,
+ String target) {
+ return new BranchOperationUI(repositories, target, true);
+ }
+
+ /**
* Create an operation for checking out a branch
*
* @param repository
@@ -79,7 +100,22 @@ public class BranchOperationUI {
*/
public static BranchOperationUI checkout(Repository repository,
String target) {
- return new BranchOperationUI(repository, target, true);
+ return checkout(repository, target, true);
+ }
+
+ /**
+ * Create an operation for checking out a branch
+ *
+ * @param repository
+ * @param target
+ * a valid {@link Ref} name or commit id
+ * @param showQuestionsBeforeCheckout
+ * @return the {@link BranchOperationUI}
+ */
+ public static BranchOperationUI checkout(Repository repository,
+ String target, boolean showQuestionsBeforeCheckout) {
+ return new BranchOperationUI(new Repository[] { repository }, target,
+ showQuestionsBeforeCheckout);
}
/**
@@ -93,54 +129,70 @@ public class BranchOperationUI {
}
/**
- * @param repository
+ * @param repositories
* @param target
* @param showQuestionsBeforeCheckout
*/
- private BranchOperationUI(Repository repository, String target,
+ private BranchOperationUI(Repository[] repositories, String target,
boolean showQuestionsBeforeCheckout) {
- this.repository = repository;
+ this.repositories = repositories;
this.target = target;
- this.showQuestionsBeforeCheckout = showQuestionsBeforeCheckout;
+ /*
+ * We do not have support for CreateBranchWizards when performing
+ * checkout on multiple repositories at once, thus, the
+ * showQuestionsBeforeCheckout is forced to false in this case
+ */
+ this.isSingleRepositoryOperation = repositories.length == 1;
+ this.showQuestionsBeforeCheckout = isSingleRepositoryOperation
+ ? showQuestionsBeforeCheckout
+ : false;
}
private String confirmTarget(IProgressMonitor monitor) {
- if (target != null) {
- if (!repository.getRepositoryState().canCheckout()) {
- PlatformUI.getWorkbench().getDisplay()
- .asyncExec(new Runnable() {
- @Override
- public void run() {
- MessageDialog.openError(getShell(),
- UIText.BranchAction_cannotCheckout,
- NLS.bind(
- UIText.BranchAction_repositoryState,
- repository.getRepositoryState()
- .getDescription()));
- }
- });
- return null;
- }
- if (LaunchFinder.shouldCancelBecauseOfRunningLaunches(repository,
- monitor)) {
- return null;
- }
+ if (target == null) {
+ return null;
+ }
+
+ Optional<Repository> invalidRepo = Stream.of(repositories) //
+ .filter(r -> !r.getRepositoryState().canCheckout()) //
+ .findFirst();
+
+ if (invalidRepo.isPresent()) {
+ PlatformUI.getWorkbench().getDisplay()
+ .asyncExec(() -> showRepositoryInInvalidStateForCheckout(
+ invalidRepo.get()));
+ return null;
+ }
- askForTargetIfNecessary();
+ Collection<Repository> repos = Arrays.asList(repositories);
+ if (LaunchFinder.shouldCancelBecauseOfRunningLaunches(repos, monitor)) {
+ return null;
}
+
+ askForTargetIfNecessary();
return target;
}
+ private void showRepositoryInInvalidStateForCheckout(Repository repo) {
+ String repoName = Activator.getDefault().getRepositoryUtil()
+ .getRepositoryName(repo);
+ String description = repo.getRepositoryState().getDescription();
+ String message = NLS.bind(UIText.BranchAction_repositoryState, repoName,
+ description);
+
+ MessageDialog.openError(getShell(), UIText.BranchAction_cannotCheckout,
+ message);
+ }
+
private void doCheckout(BranchOperation bop, boolean restore,
- IProgressMonitor monitor)
- throws CoreException {
+ IProgressMonitor monitor) throws CoreException {
SubMonitor progress = SubMonitor.convert(monitor, restore ? 10 : 1);
if (!restore) {
bop.execute(progress.newChild(1));
} else {
final BranchProjectTracker tracker = new BranchProjectTracker(
- repository);
+ repositories);
ProjectTrackerMemento snapshot = tracker.snapshot();
bop.execute(progress.newChild(7));
tracker.save(snapshot);
@@ -162,14 +214,16 @@ public class BranchOperationUI {
* Starts the operation asynchronously
*/
public void start() {
+
+ if (repositories == null || repositories.length == 0) {
+ return;
+ }
+
target = confirmTarget(new NullProgressMonitor());
if (target == null) {
return;
}
- String repoName = Activator.getDefault().getRepositoryUtil()
- .getRepositoryName(repository);
- String jobname = NLS.bind(UIText.BranchAction_checkingOut, repoName,
- target);
+ String jobname = getJobName(repositories, target);
boolean restore = Activator.getDefault().getPreferenceStore()
.getBoolean(UIPreferences.CHECKOUT_PROJECT_RESTORE);
final CheckoutJob job = new CheckoutJob(jobname, restore);
@@ -183,6 +237,17 @@ public class BranchOperationUI {
job.schedule();
}
+ private static String getJobName(Repository[] repos, String target) {
+
+ if (repos.length > 1) {
+ return NLS.bind(UIText.BranchAction_checkingOutMultiple, target);
+ }
+
+ String repoName = Activator.getDefault().getRepositoryUtil()
+ .getRepositoryName(repos[0]);
+ return NLS.bind(UIText.BranchAction_checkingOut, repoName, target);
+ }
+
private class CheckoutJob extends Job {
private BranchOperation bop;
@@ -196,18 +261,29 @@ public class BranchOperationUI {
@Override
public IStatus run(IProgressMonitor monitor) {
- bop = new BranchOperation(repository, target, !restore);
+ bop = new BranchOperation(repositories, target, !restore);
try {
doCheckout(bop, restore, monitor);
} catch (CoreException e) {
- switch (bop.getResult().getStatus()) {
- case CONFLICTS:
- case NONDELETED:
- break;
- default:
- return Activator.createErrorStatus(
- UIText.BranchAction_branchFailed, e);
+
+ /*
+ * For a checkout operation with multiple repositories we can
+ * handle any error status by displaying all of them in a table.
+ * For a single repository, though, we will stick to using a
+ * simple message in case of an unexpected exception.
+ */
+ if (!isSingleRepositoryOperation)
+ return Status.OK_STATUS;
+
+ CheckoutResult result = bop.getResult(repositories[0]);
+
+ if (result.getStatus() == CheckoutResult.Status.CONFLICTS || //
+ result.getStatus() == CheckoutResult.Status.NONDELETED) {
+ return Status.OK_STATUS;
}
+
+ return Activator
+ .createErrorStatus(UIText.BranchAction_branchFailed, e);
} finally {
GitLightweightDecorator.refresh();
monitor.done();
@@ -223,8 +299,8 @@ public class BranchOperationUI {
}
@NonNull
- public CheckoutResult getCheckoutResult() {
- return bop.getResult();
+ public Map<Repository, CheckoutResult> getCheckoutResult() {
+ return bop.getResults();
}
}
@@ -243,19 +319,25 @@ public class BranchOperationUI {
}
final boolean restore = Activator.getDefault().getPreferenceStore()
.getBoolean(UIPreferences.CHECKOUT_PROJECT_RESTORE);
- BranchOperation bop = new BranchOperation(repository, target, !restore);
+ BranchOperation bop = new BranchOperation(repositories, target,
+ !restore);
doCheckout(bop, restore, progress.newChild(80));
- show(bop.getResult());
+ show(bop.getResults());
}
private void askForTargetIfNecessary() {
- if (target != null && showQuestionsBeforeCheckout) {
- if (shouldShowCheckoutRemoteTrackingDialog(target))
- target = getTargetWithCheckoutRemoteTrackingDialog();
+
+ if (target == null || //
+ !showQuestionsBeforeCheckout || //
+ !shouldShowCheckoutRemoteTrackingDialog(target)) {
+ return;
}
+
+ target = getTargetWithCheckoutRemoteTrackingDialog(repositories[0]);
}
- private static boolean shouldShowCheckoutRemoteTrackingDialog(String refName) {
+ private static boolean shouldShowCheckoutRemoteTrackingDialog(
+ String refName) {
boolean isRemoteTrackingBranch = refName != null
&& refName.startsWith(Constants.R_REMOTES);
if (isRemoteTrackingBranch) {
@@ -272,32 +354,29 @@ public class BranchOperationUI {
}
}
- private String getTargetWithCheckoutRemoteTrackingDialog() {
+ private String getTargetWithCheckoutRemoteTrackingDialog(Repository repo) {
final String[] dialogResult = new String[1];
- PlatformUI.getWorkbench().getDisplay().syncExec(new Runnable() {
- @Override
- public void run() {
- dialogResult[0] = getTargetWithCheckoutRemoteTrackingDialogInUI();
- }
- });
+ PlatformUI.getWorkbench().getDisplay().syncExec(
+ () -> dialogResult[0] = getTargetWithCheckoutRemoteTrackingDialogInUI(
+ repo));
+
return dialogResult[0];
}
- private String getTargetWithCheckoutRemoteTrackingDialogInUI() {
+ private String getTargetWithCheckoutRemoteTrackingDialogInUI(
+ Repository repo) {
String[] buttons = new String[] {
UIText.BranchOperationUI_CheckoutRemoteTrackingAsLocal,
UIText.BranchOperationUI_CheckoutRemoteTrackingCommit,
IDialogConstants.CANCEL_LABEL };
- MessageDialog questionDialog = new MessageDialog(
- getShell(),
- UIText.BranchOperationUI_CheckoutRemoteTrackingTitle,
- null,
+ MessageDialog questionDialog = new MessageDialog(getShell(),
+ UIText.BranchOperationUI_CheckoutRemoteTrackingTitle, null,
UIText.BranchOperationUI_CheckoutRemoteTrackingQuestion,
MessageDialog.QUESTION, buttons, 0);
int result = questionDialog.open();
if (result == 0) {
// Check out as new local branch
- CreateBranchWizard wizard = new CreateBranchWizard(repository,
+ CreateBranchWizard wizard = new CreateBranchWizard(repo,
target);
WizardDialog createBranchDialog = new WizardDialog(getShell(),
wizard);
@@ -317,30 +396,60 @@ public class BranchOperationUI {
}
/**
- * @param result
- * the result to show
+ * @param results
*/
- private void show(final @NonNull CheckoutResult result) {
+ private void show(final @NonNull Map<Repository, CheckoutResult> results) {
+
+ if (allBranchOperationsSucceeded(results)) {
+
+ if (anyRepositoryIsInDetachedHeadState(results)) {
+ showDetachedHeadWarning();
+ }
+ return;
+ }
+
+ if (this.isSingleRepositoryOperation) {
+ Repository repo = repositories[0];
+ CheckoutResult result = results.get(repo);
+ handleSingleRepositoryCheckoutOperationResult(repo,
+ result);
+ return;
+ }
+
+ handleMultipleRepositoryCheckoutError(results);
+ }
+
+ private boolean allBranchOperationsSucceeded(
+ final @NonNull Map<Repository, CheckoutResult> results)
+ {
+ return results.values().stream()
+ .allMatch(r -> r.getStatus() == CheckoutResult.Status.OK);
+ }
+
+ private boolean anyRepositoryIsInDetachedHeadState(
+ final @NonNull Map<Repository, CheckoutResult> results) {
+ return results.keySet().stream()
+ .anyMatch(RepositoryUtil::isDetachedHead);
+ }
+
+ private void handleSingleRepositoryCheckoutOperationResult(Repository repository,
+ CheckoutResult result) {
+
if (result.getStatus() == CheckoutResult.Status.CONFLICTS) {
- PlatformUI.getWorkbench().getDisplay().asyncExec(new Runnable() {
- @Override
- public void run() {
- Shell shell = PlatformUI.getWorkbench()
- .getActiveWorkbenchWindow().getShell();
+ PlatformUI.getWorkbench().getDisplay().asyncExec(() -> {
+ Shell shell = PlatformUI.getWorkbench()
+ .getActiveWorkbenchWindow().getShell();
CleanupUncomittedChangesDialog cleanupUncomittedChangesDialog = new CleanupUncomittedChangesDialog(
- shell,
- UIText.BranchResultDialog_CheckoutConflictsTitle,
- NLS.bind(
- UIText.BranchResultDialog_CheckoutConflictsMessage,
- Repository.shortenRefName(target)),
- repository, result.getConflictList());
- cleanupUncomittedChangesDialog.open();
- if (cleanupUncomittedChangesDialog.shouldContinue()) {
- BranchOperationUI op = new BranchOperationUI(repository,
- target, false);
- op.start();
+ shell, UIText.BranchResultDialog_CheckoutConflictsTitle,
+ NLS.bind(
+ UIText.BranchResultDialog_CheckoutConflictsMessage,
+ Repository.shortenRefName(target)),
+ repository, result.getConflictList());
+ cleanupUncomittedChangesDialog.open();
+ if (cleanupUncomittedChangesDialog.shouldContinue()) {
+ BranchOperationUI.checkout(repository, target, false)
+ .start();
}
- }
});
} else if (result.getStatus() == CheckoutResult.Status.NONDELETED) {
// double-check if the files are still there
@@ -351,31 +460,51 @@ public class BranchOperationUI {
show = true;
break;
}
+
if (!show)
return;
- PlatformUI.getWorkbench().getDisplay().asyncExec(new Runnable() {
- @Override
- public void run() {
+
+ PlatformUI.getWorkbench().getDisplay().asyncExec(() -> {
Shell shell = PlatformUI.getWorkbench()
.getActiveWorkbenchWindow().getShell();
- new NonDeletedFilesDialog(shell, repository, result
- .getUndeletedList()).open();
- }
+ new NonDeletedFilesDialog(shell, repository,
+ result.getUndeletedList()).open();
+ });
+ } else {
+
+ String repoName = Activator.getDefault().getRepositoryUtil()
+ .getRepositoryName(repository);
+ String message = NLS.bind(
+ UIText.BranchOperationUI_CheckoutError_DialogMessage,
+ repoName, target);
+ PlatformUI.getWorkbench().getDisplay().asyncExec(() -> {
+ Shell shell = PlatformUI.getWorkbench()
+ .getActiveWorkbenchWindow().getShell();
+ MessageDialog.openError(shell,
+ UIText.BranchOperationUI_CheckoutError_DialogTitle,
+ message);
});
- } else if (result.getStatus() == CheckoutResult.Status.OK) {
- if (RepositoryUtil.isDetachedHead(repository))
- showDetachedHeadWarning();
}
}
+ private void handleMultipleRepositoryCheckoutError(
+ Map<Repository, CheckoutResult> results) {
+
+ PlatformUI.getWorkbench().getDisplay().asyncExec(() -> {
+ Shell shell = PlatformUI.getWorkbench()
+ .getActiveWorkbenchWindow().getShell();
+ new MultiBranchOperationResultDialog(shell, results).open();
+ }
+ );
+ }
+
private void showDetachedHeadWarning() {
- PlatformUI.getWorkbench().getDisplay().asyncExec(new Runnable() {
- @Override
- public void run() {
+ PlatformUI.getWorkbench().getDisplay().asyncExec(() -> {
IPreferenceStore store = Activator.getDefault()
.getPreferenceStore();
- if (store.getBoolean(UIPreferences.SHOW_DETACHED_HEAD_WARNING)) {
+ if (store
+ .getBoolean(UIPreferences.SHOW_DETACHED_HEAD_WARNING)) {
String toggleMessage = UIText.BranchResultDialog_DetachedHeadWarningDontShowAgain;
MessageDialogWithToggle dialog = new MessageDialogWithToggle(
@@ -384,16 +513,14 @@ public class BranchOperationUI {
UIText.BranchOperationUI_DetachedHeadTitle, null,
UIText.BranchOperationUI_DetachedHeadMessage,
MessageDialog.INFORMATION,
- new String[] { IDialogConstants.CLOSE_LABEL },
- 0, toggleMessage, false);
+ new String[] { IDialogConstants.CLOSE_LABEL }, 0,
+ toggleMessage, false);
dialog.open();
if (dialog.getToggleState()) {
store.setValue(UIPreferences.SHOW_DETACHED_HEAD_WARNING,
false);
}
}
- }
});
}
-
}
diff --git a/org.eclipse.egit.ui/src/org/eclipse/egit/ui/internal/branch/BranchProjectTracker.java b/org.eclipse.egit.ui/src/org/eclipse/egit/ui/internal/branch/BranchProjectTracker.java
index 6487aca27f..800d70b744 100644
--- a/org.eclipse.egit.ui/src/org/eclipse/egit/ui/internal/branch/BranchProjectTracker.java
+++ b/org.eclipse.egit.ui/src/org/eclipse/egit/ui/internal/branch/BranchProjectTracker.java
@@ -19,7 +19,9 @@ import java.util.ArrayList;
import java.util.Collections;
import java.util.LinkedHashSet;
import java.util.List;
+import java.util.Objects;
import java.util.Set;
+import java.util.stream.Stream;
import org.eclipse.core.resources.IProject;
import org.eclipse.core.resources.IProjectDescription;
@@ -59,7 +61,16 @@ class BranchProjectTracker {
private static final String REPO_ROOT = "/"; //$NON-NLS-1$
- private final Repository repository;
+ private final Repository[] repositories;
+
+ /**
+ * Create tracker for repositories
+ *
+ * @param repositories
+ */
+ public BranchProjectTracker(final Repository[] repositories) {
+ this.repositories = repositories;
+ }
/**
* Create tracker for repository
@@ -67,12 +78,12 @@ class BranchProjectTracker {
* @param repository
*/
public BranchProjectTracker(final Repository repository) {
- this.repository = repository;
+ this.repositories = new Repository[] { repository };
}
- private String getBranch() {
+ private String getBranch(Repository repo) {
try {
- return repository.getBranch();
+ return repo.getBranch();
} catch (IOException e) {
return null;
}
@@ -91,40 +102,40 @@ class BranchProjectTracker {
ProjectTrackerMemento memento = new ProjectTrackerMemento();
- ProjectTrackerPreferenceSnapshot snapshot = takeSnapshot();
- if (snapshot != null) {
- memento.addSnapshot(snapshot);
- }
+ Stream.of(repositories) //
+ .map(this::takeSnapshot) //
+ .filter(Objects::nonNull) //
+ .forEach(x -> memento.addSnapshot(x));
return memento;
}
- private ProjectTrackerPreferenceSnapshot takeSnapshot() {
+ private ProjectTrackerPreferenceSnapshot takeSnapshot(Repository repo) {
- String branch = getBranch();
+ String branch = getBranch(repo);
if (StringUtils.isEmptyOrNull(branch))
return null;
- List<String> projectPaths = getAssociatedProjectsPaths();
+ List<String> projectPaths = getAssociatedProjectsPaths(repo);
if (projectPaths.isEmpty()) {
return null;
}
- return new ProjectTrackerPreferenceSnapshot(repository, branch,
+ return new ProjectTrackerPreferenceSnapshot(repo, branch,
projectPaths);
}
@NonNull
- private List<String> getAssociatedProjectsPaths() {
+ private List<String> getAssociatedProjectsPaths(Repository repo) {
- IProject[] projects = getValidOpenProjects();
+ IProject[] projects = getValidOpenProjects(repo);
if (projects == null) {
return Collections.emptyList();
}
List<String> projectPaths = new ArrayList<>();
- final String workDir = repository.getWorkTree().getAbsolutePath();
+ final String workDir = repo.getWorkTree().getAbsolutePath();
for (IProject project : projects) {
IPath path = project.getLocation();
if (path == null) {
@@ -146,9 +157,9 @@ class BranchProjectTracker {
return projectPaths;
}
- private IProject[] getValidOpenProjects() {
+ private IProject[] getValidOpenProjects(Repository repo) {
try {
- return ProjectUtil.getValidOpenProjects(repository);
+ return ProjectUtil.getValidOpenProjects(repo);
} catch (CoreException e) {
return null;
}
@@ -185,26 +196,31 @@ class BranchProjectTracker {
* @param monitor
*/
public void restore(final IProgressMonitor monitor) {
- String branch = getBranch();
- if (branch != null) {
- restore(branch, monitor);
+
+ for (Repository repo : repositories) {
+ String branch = getBranch(repo);
+ if (branch != null) {
+ restore(repo, branch, monitor);
+ }
}
}
/**
* Restore projects associated with the given branch to the workspace
*
+ * @param repo
* @param branch
* @param monitor
*/
- public void restore(final String branch, final IProgressMonitor monitor) {
+ public void restore(Repository repo, final String branch,
+ final IProgressMonitor monitor) {
List<String> paths = ProjectTrackerPreferenceHelper
- .restoreFromPreferences(repository, branch);
+ .restoreFromPreferences(repo, branch);
if (paths.size() == 0)
return;
Set<ProjectRecord> records = new LinkedHashSet<>();
- File parent = repository.getWorkTree();
+ File parent = repo.getWorkTree();
for (String path : paths) {
File root;
if (!REPO_ROOT.equals(path)) {
diff --git a/org.eclipse.egit.ui/src/org/eclipse/egit/ui/internal/branch/MultiBranchOperationResultDialog.java b/org.eclipse.egit.ui/src/org/eclipse/egit/ui/internal/branch/MultiBranchOperationResultDialog.java
new file mode 100644
index 0000000000..7bc3d516bf
--- /dev/null
+++ b/org.eclipse.egit.ui/src/org/eclipse/egit/ui/internal/branch/MultiBranchOperationResultDialog.java
@@ -0,0 +1,254 @@
+/*******************************************************************************
+ * Copyright (C) 2018, Luís Copetti <lhcopetti@gmail.com>
+ *
+ * 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.branch;
+
+import java.util.EnumMap;
+import java.util.LinkedHashMap;
+import java.util.Map;
+import java.util.Map.Entry;
+
+import org.eclipse.egit.core.RepositoryUtil;
+import org.eclipse.egit.ui.Activator;
+import org.eclipse.egit.ui.internal.UIText;
+import org.eclipse.jface.dialogs.IDialogConstants;
+import org.eclipse.jface.dialogs.TitleAreaDialog;
+import org.eclipse.jface.layout.GridDataFactory;
+import org.eclipse.jface.layout.TableColumnLayout;
+import org.eclipse.jface.viewers.ArrayContentProvider;
+import org.eclipse.jface.viewers.ColumnLabelProvider;
+import org.eclipse.jface.viewers.ColumnWeightData;
+import org.eclipse.jface.viewers.TableViewer;
+import org.eclipse.jface.viewers.TableViewerColumn;
+import org.eclipse.jface.viewers.Viewer;
+import org.eclipse.jface.viewers.ViewerComparator;
+import org.eclipse.jgit.api.CheckoutResult;
+import org.eclipse.jgit.api.CheckoutResult.Status;
+import org.eclipse.jgit.lib.Repository;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.events.SelectionAdapter;
+import org.eclipse.swt.events.SelectionEvent;
+import org.eclipse.swt.graphics.Image;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Control;
+import org.eclipse.swt.widgets.Shell;
+import org.eclipse.swt.widgets.Table;
+import org.eclipse.swt.widgets.TableColumn;
+import org.eclipse.ui.ISharedImages;
+import org.eclipse.ui.PlatformUI;
+
+/**
+ * Presents the result of a checkout operation for multiple repositories
+ * <p>
+ */
+public class MultiBranchOperationResultDialog extends TitleAreaDialog {
+
+ private final Map<Repository, CheckoutResult> results = new LinkedHashMap<>();
+
+ private TableViewer tv;
+
+ private final RepositoryUtil utils = Activator.getDefault()
+ .getRepositoryUtil();
+
+ private EnumMap<CheckoutResult.Status, String> resultMessages;
+ /**
+ * @param parentShell
+ * @param results
+ */
+ protected MultiBranchOperationResultDialog(Shell parentShell,
+ Map<Repository, CheckoutResult> results) {
+ super(parentShell);
+ setShellStyle(
+ getShellStyle() & ~SWT.APPLICATION_MODAL | SWT.SHELL_TRIM);
+ setBlockOnOpen(false);
+ this.results.putAll(results);
+
+ this.initializeResultMessages();
+ }
+
+ @Override
+ public void create() {
+ super.create();
+ }
+
+ @Override
+ protected Control createDialogArea(Composite parent) {
+ Composite main = new Composite(parent, SWT.NONE);
+ tv = new TableViewer(main, SWT.MULTI | SWT.FULL_SELECTION | SWT.BORDER);
+ tv.setContentProvider(ArrayContentProvider.getInstance());
+ TableColumnLayout layout = new TableColumnLayout();
+ main.setLayout(layout);
+
+
+ Table table = tv.getTable();
+ TableViewerColumn tc = new TableViewerColumn(tv, SWT.NONE);
+ TableColumn col = tc.getColumn();
+ tc.setLabelProvider(new ColumnLabelProvider() {
+ @Override
+ public String getText(Object element) {
+ @SuppressWarnings("unchecked")
+ Entry<Repository, CheckoutResult> item = (Entry<Repository, CheckoutResult>) element;
+ return utils.getRepositoryName(item.getKey());
+ }
+ });
+ col.setText(
+ UIText.MultiBranchOperationResultDialog_RepositoryColumnHeader);
+ layout.setColumnData(col, new ColumnWeightData(200, 200));
+ createComparator(col, 0);
+
+ // update status
+ tc = new TableViewerColumn(tv, SWT.NONE);
+ col = tc.getColumn();
+ tc.setLabelProvider(new ColumnLabelProvider() {
+
+ @Override
+ public Image getImage(Object element) {
+
+ @SuppressWarnings("unchecked")
+ Entry<Repository, CheckoutResult> item = (Entry<Repository, CheckoutResult>) element;
+
+ if (item.getValue().getStatus() == Status.OK) {
+ return null;
+ }
+ return PlatformUI.getWorkbench().getSharedImages()
+ .getImage(ISharedImages.IMG_ELCL_STOP);
+ }
+
+ @Override
+ public String getText(Object element) {
+ @SuppressWarnings("unchecked")
+ Entry<Repository, CheckoutResult> item = (Entry<Repository, CheckoutResult>) element;
+
+ CheckoutResult.Status status = item.getValue().getStatus();
+ return getMessageForStatus(status);
+ }
+ });
+ col.setText(
+ UIText.MultiBranchOperationResultDialog_CheckoutStatusColumnHeader);
+ layout.setColumnData(col, new ColumnWeightData(200, 450));
+ createComparator(col, 1);
+
+ table.setHeaderVisible(true);
+ table.setLinesVisible(true);
+ tv.setInput(results.entrySet());
+
+ int linesToShow = Math.min(Math.max(results.size(), 5), 15);
+ int height = table.getItemHeight() * linesToShow;
+
+ GridDataFactory.fillDefaults().grab(true, true)
+ .minSize(SWT.DEFAULT, height).applyTo(main);
+
+ setTitle(UIText.MultiBranchOperationResultDialog_DialogTitle);
+ setErrorMessage(
+ UIText.MultiBranchOperationResultDialog_DialogErrorMessage);
+ return main;
+ }
+
+ @Override
+ protected void createButtonsForButtonBar(Composite parent) {
+ createButton(parent, IDialogConstants.OK_ID, IDialogConstants.OK_LABEL,
+ true);
+ }
+
+ @Override
+ protected void configureShell(Shell newShell) {
+ super.configureShell(newShell);
+ newShell.setText(UIText.MultiBranchOperationResultDialog_WindowTitle);
+ }
+
+ private ColumnComparator createComparator(TableColumn column,
+ int columnIndex) {
+ return new ColumnComparator(column, columnIndex);
+ }
+
+ private class ColumnComparator extends ViewerComparator {
+
+ private static final int ASCENDING = SWT.DOWN;
+
+ private static final int NONE = SWT.NONE;
+
+ private static final int DESCENDING = SWT.UP;
+
+ private final TableColumn column;
+
+ private final int columnIndex;
+
+ private int direction;
+
+ public ColumnComparator(TableColumn column, int columnIndex) {
+ super(null);
+ this.column = column;
+ this.columnIndex = columnIndex;
+ column.addSelectionListener(new SelectionAdapter() {
+ @Override
+ public void widgetSelected(SelectionEvent e) {
+ if (tv.getComparator() == ColumnComparator.this) {
+ if (direction == ASCENDING) {
+ setDirection(DESCENDING);
+ } else {
+ setDirection(NONE);
+ }
+ } else {
+ setDirection(ASCENDING);
+ }
+ }
+ });
+ }
+
+ private void setDirection(int newDirection) {
+ direction = newDirection;
+ Table table = column.getParent();
+ table.setSortDirection(direction);
+ if (direction == NONE) {
+ table.setSortColumn(null);
+ tv.setComparator(null);
+ } else {
+ table.setSortColumn(column);
+ if (tv.getComparator() == this) {
+ tv.refresh();
+ } else {
+ tv.setComparator(this);
+ }
+ }
+ }
+
+ @Override
+ public int compare(Viewer viewer, Object e1, Object e2) {
+ ColumnLabelProvider labelProvider = (ColumnLabelProvider) tv
+ .getLabelProvider(columnIndex);
+ String label1 = labelProvider.getText(e1);
+ String label2 = labelProvider.getText(e2);
+ if (direction == ASCENDING) {
+ return label1.compareTo(label2);
+ } else {
+ return label2.compareTo(label1);
+ }
+ }
+ }
+
+ private void initializeResultMessages() {
+ this.resultMessages = new EnumMap<>(CheckoutResult.Status.class);
+
+ this.resultMessages.put(Status.OK,
+ UIText.MultiBranchOperationResultDialog_CheckoutResultOK);
+ this.resultMessages.put(Status.CONFLICTS,
+ UIText.MultiBranchOperationResultDialog_CheckoutResultConflicts);
+ this.resultMessages.put(Status.NOT_TRIED,
+ UIText.MultiBranchOperationResultDialog_CheckoutResultNotTried);
+ this.resultMessages.put(Status.NONDELETED,
+ UIText.MultiBranchOperationResultDialog_CheckoutResultNonDeleted);
+ this.resultMessages.put(Status.ERROR,
+ UIText.MultiBranchOperationResultDialog_CheckoutResultError);
+ }
+
+ private String getMessageForStatus(CheckoutResult.Status status) {
+ return this.resultMessages.getOrDefault(status, "");//$NON-NLS-1$
+ }
+} \ No newline at end of file
diff --git a/org.eclipse.egit.ui/src/org/eclipse/egit/ui/internal/uitext.properties b/org.eclipse.egit.ui/src/org/eclipse/egit/ui/internal/uitext.properties
index d551a5c06d..3f14fbbb51 100644
--- a/org.eclipse.egit.ui/src/org/eclipse/egit/ui/internal/uitext.properties
+++ b/org.eclipse.egit.ui/src/org/eclipse/egit/ui/internal/uitext.properties
@@ -1125,13 +1125,17 @@ BasicConfigurationDialog_WindowTitle=Identify Yourself
BranchAction_branchFailed=Branch failed
BranchAction_cannotCheckout=Cannot check out now
BranchAction_checkingOut=Checking out {0} - {1}
-BranchAction_repositoryState=Repository state: {0}
+BranchAction_checkingOutMultiple=Checking out multiple repositories to {0}
+BranchAction_repositoryState=The repository {0} is not on a valid state: {1}
BranchConfigurationDialog_BranchConfigurationTitle=Git Branch Configuration
BranchConfigurationDialog_EditBranchConfigMessage=Edit the upstream configuration for branch {0}
BranchConfigurationDialog_ExceptionGettingRefs=Exception getting Refs
BranchConfigurationDialog_RemoteLabel=Rem&ote:
BranchConfigurationDialog_SaveBranchConfigFailed=Could not save branch configuration
BranchConfigurationDialog_UpstreamBranchLabel=Upstream &Branch:
+
+BranchOperationUI_CheckoutError_DialogTitle=Problem checking out repository
+BranchOperationUI_CheckoutError_DialogMessage=Checking out {0} - {1} has failed.
BranchOperationUI_CheckoutRemoteTrackingAsLocal=Check out as New Local Branch
BranchOperationUI_CheckoutRemoteTrackingCommit=Check out Commit
BranchOperationUI_CheckoutRemoteTrackingQuestion=If you want to work on the branch, a new local branch has to be created and checked out.\n\nIf you just want to have a look at the state of the branch, the commit of the remote-tracking branch can be checked out.
@@ -1584,6 +1588,20 @@ MultiPullResultDialog_UpdatedOneMessage=1 ref was updated
MultiPullResultDialog_UpdateStatusColumnHeader=Update Status
MultiPullResultDialog_WindowTitle=Pull Result for Multiple Repositories
+MultiBranchOperationResultDialog_OkStatus=OK
+MultiBranchOperationResultDialog_RepositoryColumnHeader=Repository
+MultiBranchOperationResultDialog_WindowTitle=Branch Operation for Multiple Repositories
+MultiBranchOperationResultDialog_CheckoutStatusColumnHeader=Checkout Status
+
+MultiBranchOperationResultDialog_DialogTitle=Problems checking out multiple repositories
+MultiBranchOperationResultDialog_DialogErrorMessage=Some of the repositories were not checked out
+
+MultiBranchOperationResultDialog_CheckoutResultError=An exception occurred during checkout
+MultiBranchOperationResultDialog_CheckoutResultNonDeleted=Checkout succeeded but some files could not be deleted
+MultiBranchOperationResultDialog_CheckoutResultConflicts=Operation has not completed because of conflicts
+MultiBranchOperationResultDialog_CheckoutResultOK=Checkout completed normally
+MultiBranchOperationResultDialog_CheckoutResultNotTried=The operation was not executed
+
CommitFileDiffViewer_CanNotOpenCompareEditorTitle=Cannot Open Compare Editor
CommitFileDiffViewer_CompareMenuLabel=Compare with Previous &Version
CommitFileDiffViewer_CompareWorkingDirectoryMenuLabel=Compare with &Working Tree

Back to the top