diff options
author | Luís Copetti | 2018-09-20 03:24:42 +0000 |
---|---|---|
committer | Luís Copetti | 2018-10-05 03:23:26 +0000 |
commit | 7ae6201ee08ac03749488509af1ccac0755e5c43 (patch) | |
tree | 3944f5fc64c1a3e1626082787897ee6055d9cb99 | |
parent | 096d9b869f5876379b6353241327a45f2230a195 (diff) | |
download | egit-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>
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 |