Skip to main content
aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorLuís Copetti2018-08-25 14:12:57 +0000
committerLuís Copetti2018-09-27 02:37:30 +0000
commite4dc3eaf0900a469e605b0307a8387c464bb60e9 (patch)
tree43e4ef98e0ec618555ffdf2c1bf40f5de37dc95d /org.eclipse.egit.ui/src/org
parentc0f622a5a3e23bbcf43185f045de616839aa37c3 (diff)
downloadegit-e4dc3eaf0900a469e605b0307a8387c464bb60e9.tar.gz
egit-e4dc3eaf0900a469e605b0307a8387c464bb60e9.tar.xz
egit-e4dc3eaf0900a469e605b0307a8387c464bb60e9.zip
Add support for multi repository selection for switching branches
When multiple repositories are selected the Switch To... option will display only the branches that are present in all of the repositories. By clicking in any of them, the user will concurrently switch branches for all the repositories. If the list of branches is to be empty, a disabled menu item will be added to show that no matching branches were found. Change-Id: I0f850baeb94cb8cb4169d8684bb37756b5c0c372 Signed-off-by: Luís Copetti <lhcopetti@gmail.com>
Diffstat (limited to 'org.eclipse.egit.ui/src/org')
-rw-r--r--org.eclipse.egit.ui/src/org/eclipse/egit/ui/internal/UIText.java3
-rw-r--r--org.eclipse.egit.ui/src/org/eclipse/egit/ui/internal/actions/SwitchToMenu.java260
-rw-r--r--org.eclipse.egit.ui/src/org/eclipse/egit/ui/internal/selection/SelectionPropertyTester.java37
-rw-r--r--org.eclipse.egit.ui/src/org/eclipse/egit/ui/internal/uitext.properties1
4 files changed, 217 insertions, 84 deletions
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 72b824df42..4af01c8a24 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
@@ -5069,6 +5069,9 @@ public class UIText extends NLS {
public static String SwitchToMenu_NewBranchMenuLabel;
/** */
+ public static String SwitchToMenu_NoCommonBranchesFound;
+
+ /** */
public static String SwitchToMenu_OtherMenuLabel;
/** */
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 e8fccc37d8..c9b63c3828 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
@@ -14,11 +14,15 @@
package org.eclipse.egit.ui.internal.actions;
import java.io.IOException;
+import java.util.ArrayList;
import java.util.Collections;
+import java.util.HashMap;
+import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
-import java.util.TreeMap;
+import java.util.Set;
+import java.util.TreeSet;
import org.eclipse.egit.ui.Activator;
import org.eclipse.egit.ui.internal.CommonUtils;
@@ -90,13 +94,39 @@ public class SwitchToMenu extends ContributionItem implements
if (handlerService == null)
return;
- Repository repository = SelectionUtils
- .getRepository(handlerService.getCurrentState());
- if (repository != null)
- createDynamicMenu(menu, repository);
+ Repository[] repositories = SelectionUtils
+ .getRepositories(handlerService.getCurrentState());
+
+ if (repositories.length > 0) {
+ createDynamicMenu(menu, repositories);
+ }
}
- private void createDynamicMenu(Menu menu, final Repository repository) {
+ private void createDynamicMenu(Menu menu, final Repository[] repositories) {
+
+ if (!isMultipleSelection(repositories))
+ {
+ createNewBranchMenuItem(menu, repositories[0]);
+ createSeparator(menu);
+ }
+
+ int itemCount = createMostActiveBranchesMenuItems(menu, repositories);
+
+ if (!isMultipleSelection(repositories) && itemCount > 0) {
+ createSeparator(menu);
+ createOtherMenuItem(menu, repositories[0]);
+ }
+
+ if (itemCount == 0 && isMultipleSelection(repositories)) {
+ /*
+ * If the menu would be empty, add a disabled menuItem to inform the
+ * user that no common branches among the selection were found
+ */
+ createDisabledMenu(menu, UIText.SwitchToMenu_NoCommonBranchesFound);
+ }
+ }
+
+ private void createNewBranchMenuItem(Menu menu, Repository repository) {
MenuItem newBranch = new MenuItem(menu, SWT.PUSH);
newBranch.setText(UIText.SwitchToMenu_NewBranchMenuLabel);
newBranch.setImage(newBranchImage);
@@ -130,114 +160,176 @@ public class SwitchToMenu extends ContributionItem implements
dlg.open();
}
});
- createSeparator(menu);
+
+ }
+
+ private int createMostActiveBranchesMenuItems(Menu menu, Repository[] repositories)
+ {
+ int itemCount = 0;
try {
- String currentBranch = repository.getFullBranch();
- Map<String, Ref> localBranches = repository.getRefDatabase().getRefs(
- Constants.R_HEADS);
- TreeMap<String, Ref> sortedRefs = new TreeMap<>(
- CommonUtils.STRING_ASCENDING_COMPARATOR);
-
- // Add the MAX_NUM_MENU_ENTRIES most recently used branches first
- ReflogReader reflogReader = repository.getReflogReader(
- Constants.HEAD);
- List<ReflogEntry> reflogEntries;
- if (reflogReader == null) {
- reflogEntries = Collections.emptyList();
- } else {
- reflogEntries = reflogReader.getReverseEntries();
- }
- for (ReflogEntry entry : reflogEntries) {
- CheckoutEntry checkout = entry.parseCheckout();
- if (checkout != null) {
- Ref ref = localBranches.get(checkout.getFromBranch());
- if (ref != null)
- if (sortedRefs.size() < MAX_NUM_MENU_ENTRIES)
- sortedRefs.put(checkout.getFromBranch(), ref);
- ref = localBranches.get(checkout.getToBranch());
- if (ref != null)
- if (sortedRefs.size() < MAX_NUM_MENU_ENTRIES)
- sortedRefs.put(checkout.getToBranch(), ref);
- }
+ List<Map<String, Ref>> activeBranches = new ArrayList<>();
+
+ for (Repository repository : repositories) {
+ Map<String, Ref> branchRefMapping = getMostActiveBranches(
+ repository, MAX_NUM_MENU_ENTRIES);
+ activeBranches.add(branchRefMapping);
}
- // Add the recently used branches to the menu, in alphabetical order
- int itemCount = 0;
- for (final Entry<String, Ref> entry : sortedRefs.entrySet()) {
+ Set<String> activeBranchIntersection = getBranchNameIntersection(activeBranches);
+ for (String branchName : activeBranchIntersection) {
itemCount++;
- final String shortName = entry.getKey();
- final String fullName = entry.getValue().getName();
- createMenuItem(menu, repository, currentBranch, fullName, shortName);
- // Do not duplicate branch names
- localBranches.remove(shortName);
+ createMenuItemMultiple(menu, repositories,
+ branchName);
}
- if (itemCount < MAX_NUM_MENU_ENTRIES) {
- // A separator between recently used branches and local branches is
- // nice but only if we have both recently used branches and other
- // local branches
- if (itemCount > 0 && localBranches.size() > 0)
- createSeparator(menu);
-
- // Now add more other branches if we have only a few branch switches
- // Sort the remaining local branches
- sortedRefs.clear();
- sortedRefs.putAll(localBranches);
- for (final Entry<String, Ref> entry : sortedRefs.entrySet()) {
- itemCount++;
- // protect ourselves against a huge sub-menu
- if (itemCount > MAX_NUM_MENU_ENTRIES)
- break;
- final String fullName = entry.getValue().getName();
- final String shortName = entry.getKey();
- createMenuItem(menu, repository, currentBranch, fullName, shortName);
- }
+ if (itemCount >= MAX_NUM_MENU_ENTRIES) {
+ return itemCount;
+ }
+
+ List<Map<String, Ref>> localBranchMapping = new ArrayList<>();
+ for (Repository repository : repositories) {
+ Map<String, Ref> localBranches = repository.getRefDatabase()
+ .getRefs(Constants.R_HEADS);
+ localBranchMapping.add(localBranches);
}
- if (itemCount > 0)
+
+ // A separator between recently used branches and local branches is
+ // nice but only if we have both recently used branches and other
+ // local branches
+ Set<String> localBranchNameIntersection = getBranchNameIntersection(
+ localBranchMapping);
+ localBranchNameIntersection.removeAll(activeBranchIntersection);
+ if (itemCount > 0 && !localBranchNameIntersection.isEmpty()) {
createSeparator(menu);
- MenuItem others = new MenuItem(menu, SWT.PUSH);
- others.setText(UIText.SwitchToMenu_OtherMenuLabel);
- others.addSelectionListener(new SelectionAdapter() {
- @Override
- public void widgetSelected(SelectionEvent e) {
- CheckoutDialog dialog = new CheckoutDialog(
- e.display.getActiveShell(), repository);
- if (dialog.open() == Window.OK) {
- BranchOperationUI
- .checkout(repository, dialog.getRefName())
- .start();
- }
+ }
+ for (String localBranchName : localBranchNameIntersection) {
+ itemCount++;
+ if (itemCount > MAX_NUM_MENU_ENTRIES) {
+ break;
}
- });
- } catch (IOException e) {
+
+ createMenuItemMultiple(menu, repositories, localBranchName);
+ }
+ }
+ catch (IOException e) {
Activator.handleError(e.getMessage(), e, true);
}
+
+ return itemCount;
+ }
+
+ private Set<String> getBranchNameIntersection(
+ List<Map<String, Ref>> refMapping) {
+ Iterator<Map<String, Ref>> iterator = refMapping.iterator();
+ if (!iterator.hasNext()) {
+ return Collections.emptySet();
+ }
+
+ Set<String> intersection = new TreeSet<>(
+ CommonUtils.STRING_ASCENDING_COMPARATOR);
+ intersection.addAll(iterator.next().keySet());
+ iterator.forEachRemaining(map -> intersection.retainAll(map.keySet()));
+ return intersection;
+ }
+
+ private void createOtherMenuItem(Menu menu, Repository repository) {
+ MenuItem others = new MenuItem(menu, SWT.PUSH);
+ others.setText(UIText.SwitchToMenu_OtherMenuLabel);
+ others.addSelectionListener(new SelectionAdapter() {
+ @Override
+ public void widgetSelected(SelectionEvent e) {
+ CheckoutDialog dialog = new CheckoutDialog(
+ e.display.getActiveShell(), repository);
+ if (dialog.open() == Window.OK) {
+ BranchOperationUI.checkout(repository, dialog.getRefName())
+ .start();
+ }
+
+ }
+ });
+ }
+
+ private void createDisabledMenu(Menu menu, String text) {
+ MenuItem disabled = new MenuItem(menu, SWT.PUSH);
+ disabled.setText(text);
+ disabled.setImage(branchImage);
+ disabled.setEnabled(false);
}
private static MenuItem createSeparator(Menu menu) {
return new MenuItem(menu, SWT.SEPARATOR);
}
- private void createMenuItem(Menu menu, final Repository repository,
- String currentBranch, final String fullName, String shortName) {
+ private void createMenuItemMultiple(Menu menu, final Repository[] repositories, String shortName) throws IOException {
+
final MenuItem item = new MenuItem(menu, SWT.PUSH);
item.setText(shortName);
- boolean checkedOut = currentBranch.equals(fullName);
- if (checkedOut)
+
+ 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());
+ }
+ if (allRepositoriesCheckedOut)
item.setImage(checkedOutImage);
else
item.setImage(branchImage);
- item.setEnabled(!checkedOut);
+ item.setEnabled(!allRepositoriesCheckedOut);
+
item.addSelectionListener(new SelectionAdapter() {
@Override
public void widgetSelected(SelectionEvent e) {
- BranchOperationUI.checkout(repository, fullName)
- .start();
+
+ for (Entry<Repository, String> entry : repoToFullNameMap
+ .entrySet()) {
+ BranchOperationUI.checkout(entry.getKey(), entry.getValue())
+ .start();
+ }
}
});
}
+ private Map<String, Ref> getMostActiveBranches(final Repository repository,
+ int maximumBranchCount) throws IOException {
+ Map<String, Ref> localBranches = repository.getRefDatabase()
+ .getRefs(Constants.R_HEADS);
+ Map<String, Ref> activeRefs = new HashMap<>();
+
+ ReflogReader reflogReader = repository.getReflogReader(Constants.HEAD);
+ List<ReflogEntry> reflogEntries;
+ if (reflogReader == null) {
+ return Collections.emptyMap();
+ }
+
+ reflogEntries = reflogReader.getReverseEntries();
+
+ for (ReflogEntry entry : reflogEntries) {
+ CheckoutEntry checkout = entry.parseCheckout();
+ if (checkout != null) {
+ Ref ref = localBranches.get(checkout.getFromBranch());
+ if (ref != null)
+ if (activeRefs.size() < maximumBranchCount)
+ activeRefs.put(checkout.getFromBranch(), ref);
+ ref = localBranches.get(checkout.getToBranch());
+ if (ref != null)
+ if (activeRefs.size() < maximumBranchCount)
+ activeRefs.put(checkout.getToBranch(), ref);
+ }
+ }
+
+ return activeRefs;
+ }
+
+ private boolean isMultipleSelection(Repository[] repositories) {
+ return repositories.length > 1;
+ }
+
@Override
public boolean isDynamic() {
return true;
diff --git a/org.eclipse.egit.ui/src/org/eclipse/egit/ui/internal/selection/SelectionPropertyTester.java b/org.eclipse.egit.ui/src/org/eclipse/egit/ui/internal/selection/SelectionPropertyTester.java
index f06310aa3f..f580c3fbce 100644
--- a/org.eclipse.egit.ui/src/org/eclipse/egit/ui/internal/selection/SelectionPropertyTester.java
+++ b/org.eclipse.egit.ui/src/org/eclipse/egit/ui/internal/selection/SelectionPropertyTester.java
@@ -14,10 +14,13 @@
package org.eclipse.egit.ui.internal.selection;
import java.util.ArrayList;
+import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.HashSet;
import java.util.Set;
+import java.util.stream.Collectors;
+import java.util.stream.Stream;
import org.eclipse.core.expressions.PropertyTester;
import org.eclipse.core.resources.IContainer;
@@ -60,6 +63,14 @@ public class SelectionPropertyTester extends PropertyTester {
return SelectionUtils
.getRepository(getStructuredSelection(collection)) != null;
+ } else if ("resourcesMultipleRepositories".equals(property)) { //$NON-NLS-1$
+ return resourceSelectionContainsMoreThanOneRepository(collection,
+ args);
+
+ } else if ("selectionMultipleRepositories".equals(property)) { //$NON-NLS-1$
+ return selectionContainsMoreThanOneRepository(collection,
+ args);
+
} else if ("resourcesSingleRepository".equals(property)) { //$NON-NLS-1$
IStructuredSelection selection = getStructuredSelection(collection);
@@ -116,6 +127,32 @@ public class SelectionPropertyTester extends PropertyTester {
return false;
}
+ private boolean resourceSelectionContainsMoreThanOneRepository(
+ Collection<?> collection, Object[] args) {
+ IStructuredSelection selection = getStructuredSelection(collection);
+ IResource[] resources = SelectionUtils.getSelectedResources(selection);
+ Set<Repository> repos = Stream.of(resources) //
+ .map(SelectionPropertyTester::getRepositoryOfMapping) //
+ .collect(Collectors.toSet());
+ return testMultipleRepositoryProperties(repos, args);
+ }
+
+ private boolean selectionContainsMoreThanOneRepository(
+ Collection<?> collection, Object[] args) {
+ IStructuredSelection selection = getStructuredSelection(collection);
+ Repository[] repos = SelectionUtils.getRepositories(selection);
+ return testMultipleRepositoryProperties(Arrays.asList(repos), args);
+ }
+
+ private boolean testMultipleRepositoryProperties(
+ Collection<Repository> repos, Object[] args) {
+ if (repos.size() < 2)
+ return false;
+
+ return repos.stream().allMatch(
+ r -> SelectionPropertyTester.testRepositoryProperties(r, args));
+ }
+
private static @NonNull IStructuredSelection getStructuredSelection(
Collection<?> collection) {
Object firstElement = collection.iterator().next();
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 0cce452f66..d551a5c06d 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
@@ -1826,6 +1826,7 @@ SquashHandler_InternalError=An internal error occurred
SquashHandler_JobName=Squashing {0} Commits
SwitchToMenu_NewBranchMenuLabel=&New Branch...
SwitchToMenu_OtherMenuLabel=&Other...
+SwitchToMenu_NoCommonBranchesFound=No common branches found
GitActionContributor_ExpandAll=Expand All
GitActionContributor_Push=Push
GitActionContributor_Pull=Pull

Back to the top