summaryrefslogtreecommitdiff
path: root/plugins/git4idea/src/git4idea/branch
diff options
context:
space:
mode:
Diffstat (limited to 'plugins/git4idea/src/git4idea/branch')
-rw-r--r--plugins/git4idea/src/git4idea/branch/GitBranchOperation.java67
-rw-r--r--plugins/git4idea/src/git4idea/branch/GitBranchUiHandler.java12
-rw-r--r--plugins/git4idea/src/git4idea/branch/GitBranchUiHandlerImpl.java4
-rw-r--r--plugins/git4idea/src/git4idea/branch/GitCheckoutNewBranchOperation.java6
-rw-r--r--plugins/git4idea/src/git4idea/branch/GitCheckoutOperation.java15
-rw-r--r--plugins/git4idea/src/git4idea/branch/GitDeleteBranchOperation.java2
-rw-r--r--plugins/git4idea/src/git4idea/branch/GitMergeOperation.java11
-rw-r--r--plugins/git4idea/src/git4idea/branch/GitSmartOperationDialog.java19
8 files changed, 101 insertions, 35 deletions
diff --git a/plugins/git4idea/src/git4idea/branch/GitBranchOperation.java b/plugins/git4idea/src/git4idea/branch/GitBranchOperation.java
index 7c2f77eeebd2..9147054ba7b9 100644
--- a/plugins/git4idea/src/git4idea/branch/GitBranchOperation.java
+++ b/plugins/git4idea/src/git4idea/branch/GitBranchOperation.java
@@ -15,6 +15,7 @@
*/
package git4idea.branch;
+import com.intellij.dvcs.repo.RepositoryUtil;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.progress.ProgressIndicator;
import com.intellij.openapi.project.Project;
@@ -25,6 +26,9 @@ import com.intellij.openapi.vcs.VcsNotifier;
import com.intellij.openapi.vcs.changes.Change;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.util.Function;
+import com.intellij.util.containers.ContainerUtil;
+import com.intellij.util.containers.MultiMap;
+import git4idea.GitLocalBranch;
import git4idea.GitPlatformFacade;
import git4idea.GitUtil;
import git4idea.commands.Git;
@@ -32,6 +36,7 @@ import git4idea.commands.GitMessageWithFilesDetector;
import git4idea.config.GitVcsSettings;
import git4idea.repo.GitRepository;
import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
import java.util.*;
@@ -50,7 +55,7 @@ abstract class GitBranchOperation {
@NotNull protected final Git myGit;
@NotNull protected final GitBranchUiHandler myUiHandler;
@NotNull private final Collection<GitRepository> myRepositories;
- @NotNull protected final String myCurrentBranchOrRev;
+ @NotNull protected final Map<GitRepository, String> myCurrentHeads;
private final GitVcsSettings mySettings;
@NotNull private final Collection<GitRepository> mySuccessfulRepositories;
@@ -63,7 +68,13 @@ abstract class GitBranchOperation {
myGit = git;
myUiHandler = uiHandler;
myRepositories = repositories;
- myCurrentBranchOrRev = GitBranchUtil.getCurrentBranchOrRev(repositories);
+ myCurrentHeads = ContainerUtil.map2Map(repositories, new Function<GitRepository, Pair<GitRepository, String>>() {
+ @Override
+ public Pair<GitRepository, String> fun(GitRepository repository) {
+ GitLocalBranch currentBranch = repository.getCurrentBranch();
+ return Pair.create(repository, currentBranch == null ? repository.getCurrentRevision() : currentBranch.getName());
+ }
+ });
mySuccessfulRepositories = new ArrayList<GitRepository>();
myRemainingRepositories = new ArrayList<GitRepository>(myRepositories);
mySettings = myFacade.getSettings(myProject);
@@ -220,11 +231,28 @@ abstract class GitBranchOperation {
protected void updateRecentBranch() {
if (getRepositories().size() == 1) {
GitRepository repository = myRepositories.iterator().next();
- mySettings.setRecentBranchOfRepository(repository.getRoot().getPath(), myCurrentBranchOrRev);
+ mySettings.setRecentBranchOfRepository(repository.getRoot().getPath(), myCurrentHeads.get(repository));
}
else {
- mySettings.setRecentCommonBranch(myCurrentBranchOrRev);
+ String recentCommonBranch = getRecentCommonBranch();
+ if (recentCommonBranch != null) {
+ mySettings.setRecentCommonBranch(recentCommonBranch);
+ }
+ }
+ }
+
+ @Nullable
+ private String getRecentCommonBranch() {
+ String recentCommonBranch = null;
+ for (String branch : myCurrentHeads.values()) {
+ if (recentCommonBranch == null) {
+ recentCommonBranch = branch;
+ }
+ else if (!recentCommonBranch.equals(branch)) {
+ return null;
+ }
}
+ return recentCommonBranch;
}
private void showUnmergedFilesDialogWithRollback() {
@@ -340,4 +368,35 @@ abstract class GitBranchOperation {
return Pair.create(allConflictingRepositories, affectedChanges);
}
+
+ @NotNull
+ protected static String stringifyBranchesByRepos(@NotNull Map<GitRepository, String> heads) {
+ MultiMap<String, VirtualFile> grouped = groupByBranches(heads);
+ if (grouped.size() == 1) {
+ return grouped.keySet().iterator().next();
+ }
+ return StringUtil.join(grouped.entrySet(), new Function<Map.Entry<String, Collection<VirtualFile>>, String>() {
+ @Override
+ public String fun(Map.Entry<String, Collection<VirtualFile>> entry) {
+ String roots = StringUtil.join(entry.getValue(), new Function<VirtualFile, String>() {
+ @Override
+ public String fun(VirtualFile file) {
+ return file.getName();
+ }
+ }, ", ");
+ return entry.getKey() + " (in " + roots + ")";
+ }
+ }, "<br/>");
+ }
+
+ @NotNull
+ private static MultiMap<String, VirtualFile> groupByBranches(@NotNull Map<GitRepository, String> heads) {
+ MultiMap<String, VirtualFile> result = MultiMap.createLinked();
+ List<GitRepository> sortedRepos = RepositoryUtil.sortRepositories(heads.keySet());
+ for (GitRepository repo : sortedRepos) {
+ result.putValue(heads.get(repo), repo.getRoot());
+ }
+ return result;
+ }
+
}
diff --git a/plugins/git4idea/src/git4idea/branch/GitBranchUiHandler.java b/plugins/git4idea/src/git4idea/branch/GitBranchUiHandler.java
index 3b694c455c7b..95546f0547e8 100644
--- a/plugins/git4idea/src/git4idea/branch/GitBranchUiHandler.java
+++ b/plugins/git4idea/src/git4idea/branch/GitBranchUiHandler.java
@@ -22,6 +22,7 @@ import com.intellij.openapi.vfs.VirtualFile;
import git4idea.GitCommit;
import git4idea.repo.GitRepository;
import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
import java.util.Collection;
import java.util.List;
@@ -68,14 +69,15 @@ public interface GitBranchUiHandler {
* Shows the dialog proposing to execute the operation (checkout or merge) smartly, i.e. stash-execute-unstash.
*
* @param project
- * @param changes local changes that would be overwritten by checkout or merge.
- * @param paths paths reported by Git (in most cases this is covered by {@code changes}.
- * @param operation operation name: checkout or merge
- * @param isForcePossible can the operation be executed force (force checkout is possible, force merge - not).
+ * @param changes local changes that would be overwritten by checkout or merge.
+ * @param paths paths reported by Git (in most cases this is covered by {@code changes}.
+ * @param operation operation name: checkout or merge
+ * @param forceButtonTitle if the operation can be executed force (force checkout is possible),
+ * specify the title of the force button; otherwise (force merge is not possible) pass null.
* @return the code of the decision.
*/
int showSmartOperationDialog(@NotNull Project project, @NotNull List<Change> changes, @NotNull Collection<String> paths,
- @NotNull String operation, boolean isForcePossible);
+ @NotNull String operation, @Nullable String forceButtonTitle);
boolean showBranchIsNotFullyMergedDialog(@NotNull Project project, @NotNull Map<GitRepository, List<GitCommit>> history,
@NotNull String unmergedBranch, @NotNull List<String> mergedToBranches,
diff --git a/plugins/git4idea/src/git4idea/branch/GitBranchUiHandlerImpl.java b/plugins/git4idea/src/git4idea/branch/GitBranchUiHandlerImpl.java
index cea2b55596e0..20e207d14ba1 100644
--- a/plugins/git4idea/src/git4idea/branch/GitBranchUiHandlerImpl.java
+++ b/plugins/git4idea/src/git4idea/branch/GitBranchUiHandlerImpl.java
@@ -172,7 +172,7 @@ public class GitBranchUiHandlerImpl implements GitBranchUiHandler {
@Override
public int showSmartOperationDialog(@NotNull Project project, @NotNull List<Change> changes, @NotNull Collection<String> paths,
- @NotNull String operation, boolean isForcePossible) {
+ @NotNull String operation, @Nullable String forceButtonTitle) {
JComponent fileBrowser;
if (!changes.isEmpty()) {
fileBrowser = new ChangesBrowserWithRollback(project, changes);
@@ -180,7 +180,7 @@ public class GitBranchUiHandlerImpl implements GitBranchUiHandler {
else {
fileBrowser = new GitSimplePathsBrowser(project, paths);
}
- return GitSmartOperationDialog.showAndGetAnswer(myProject, fileBrowser, operation, isForcePossible);
+ return GitSmartOperationDialog.showAndGetAnswer(myProject, fileBrowser, operation, forceButtonTitle);
}
@Override
diff --git a/plugins/git4idea/src/git4idea/branch/GitCheckoutNewBranchOperation.java b/plugins/git4idea/src/git4idea/branch/GitCheckoutNewBranchOperation.java
index 22e486e6eab0..cdc4f7711801 100644
--- a/plugins/git4idea/src/git4idea/branch/GitCheckoutNewBranchOperation.java
+++ b/plugins/git4idea/src/git4idea/branch/GitCheckoutNewBranchOperation.java
@@ -89,7 +89,7 @@ class GitCheckoutNewBranchOperation extends GitBranchOperation {
protected String getRollbackProposal() {
return "However checkout has succeeded for the following " + repositories() + ":<br/>" +
successfulRepositoriesJoined() +
- "<br/>You may rollback (checkout back to " + myCurrentBranchOrRev + " and delete " + myNewBranchName + ") not to let branches diverge.";
+ "<br/>You may rollback (checkout previous branch back, and delete " + myNewBranchName + ") not to let branches diverge.";
}
@NotNull
@@ -104,7 +104,7 @@ class GitCheckoutNewBranchOperation extends GitBranchOperation {
GitCompoundResult deleteResult = new GitCompoundResult(myProject);
Collection<GitRepository> repositories = getSuccessfulRepositories();
for (GitRepository repository : repositories) {
- GitCommandResult result = myGit.checkout(repository, myCurrentBranchOrRev, null, true);
+ GitCommandResult result = myGit.checkout(repository, myCurrentHeads.get(repository), null, true);
checkoutResult.append(repository, result);
if (result.success()) {
deleteResult.append(repository, myGit.branchDelete(repository, myNewBranchName, false));
@@ -113,7 +113,7 @@ class GitCheckoutNewBranchOperation extends GitBranchOperation {
}
if (checkoutResult.totalSuccess() && deleteResult.totalSuccess()) {
VcsNotifier.getInstance(myProject).notifySuccess("Rollback successful", String
- .format("Checked out %s and deleted %s on %s %s", code(myCurrentBranchOrRev), code(myNewBranchName),
+ .format("Checked out %s and deleted %s on %s %s", stringifyBranchesByRepos(myCurrentHeads), code(myNewBranchName),
StringUtil.pluralize("root", repositories.size()), successfulRepositoriesJoined()));
}
else {
diff --git a/plugins/git4idea/src/git4idea/branch/GitCheckoutOperation.java b/plugins/git4idea/src/git4idea/branch/GitCheckoutOperation.java
index 2441bf7d46cf..b772434959e8 100644
--- a/plugins/git4idea/src/git4idea/branch/GitCheckoutOperation.java
+++ b/plugins/git4idea/src/git4idea/branch/GitCheckoutOperation.java
@@ -47,7 +47,7 @@ import static git4idea.util.GitUIUtil.code;
*/
class GitCheckoutOperation extends GitBranchOperation {
- public static final String ROLLBACK_PROPOSAL_FORMAT = "You may rollback (checkout back to %s) not to let branches diverge.";
+ public static final String ROLLBACK_PROPOSAL_FORMAT = "You may rollback (checkout back to previous branch) not to let branches diverge.";
@NotNull private final String myStartPointReference;
@Nullable private final String myNewBranch;
@@ -115,12 +115,14 @@ class GitCheckoutOperation extends GitBranchOperation {
private boolean smartCheckoutOrNotify(@NotNull GitRepository repository,
@NotNull GitMessageWithFilesDetector localChangesOverwrittenByCheckout) {
Pair<List<GitRepository>, List<Change>> conflictingRepositoriesAndAffectedChanges =
- getConflictingRepositoriesAndAffectedChanges(repository, localChangesOverwrittenByCheckout, myCurrentBranchOrRev, myStartPointReference);
+ getConflictingRepositoriesAndAffectedChanges(repository, localChangesOverwrittenByCheckout, myCurrentHeads.get(repository),
+ myStartPointReference);
List<GitRepository> allConflictingRepositories = conflictingRepositoriesAndAffectedChanges.getFirst();
List<Change> affectedChanges = conflictingRepositoriesAndAffectedChanges.getSecond();
Collection<String> absolutePaths = GitUtil.toAbsolute(repository.getRoot(), localChangesOverwrittenByCheckout.getRelativeFilePaths());
- int smartCheckoutDecision = myUiHandler.showSmartOperationDialog(myProject, affectedChanges, absolutePaths, "checkout", true);
+ int smartCheckoutDecision = myUiHandler.showSmartOperationDialog(myProject, affectedChanges, absolutePaths, "checkout",
+ "&Force Checkout");
if (smartCheckoutDecision == GitSmartOperationDialog.SMART_EXIT_CODE) {
boolean smartCheckedOutSuccessfully = smartCheckout(allConflictingRepositories, myStartPointReference, myNewBranch, getIndicator());
if (smartCheckedOutSuccessfully) {
@@ -153,8 +155,7 @@ class GitCheckoutOperation extends GitBranchOperation {
@Override
protected String getRollbackProposal() {
return "However checkout has succeeded for the following " + repositories() + ":<br/>" +
- successfulRepositoriesJoined() +
- "<br/>" + String.format(ROLLBACK_PROPOSAL_FORMAT, myCurrentBranchOrRev);
+ successfulRepositoriesJoined() + "<br/>" + ROLLBACK_PROPOSAL_FORMAT;
}
@NotNull
@@ -168,7 +169,7 @@ class GitCheckoutOperation extends GitBranchOperation {
GitCompoundResult checkoutResult = new GitCompoundResult(myProject);
GitCompoundResult deleteResult = new GitCompoundResult(myProject);
for (GitRepository repository : getSuccessfulRepositories()) {
- GitCommandResult result = myGit.checkout(repository, myCurrentBranchOrRev, null, true);
+ GitCommandResult result = myGit.checkout(repository, myCurrentHeads.get(repository), null, true);
checkoutResult.append(repository, result);
if (result.success() && myNewBranch != null) {
/*
@@ -183,7 +184,7 @@ class GitCheckoutOperation extends GitBranchOperation {
if (!checkoutResult.totalSuccess() || !deleteResult.totalSuccess()) {
StringBuilder message = new StringBuilder();
if (!checkoutResult.totalSuccess()) {
- message.append("Errors during checking out ").append(myCurrentBranchOrRev).append(": ");
+ message.append("Errors during checkout: ");
message.append(checkoutResult.getErrorOutputWithReposIndication());
}
if (!deleteResult.totalSuccess()) {
diff --git a/plugins/git4idea/src/git4idea/branch/GitDeleteBranchOperation.java b/plugins/git4idea/src/git4idea/branch/GitDeleteBranchOperation.java
index d3501caf7089..1dba1e07f6ac 100644
--- a/plugins/git4idea/src/git4idea/branch/GitDeleteBranchOperation.java
+++ b/plugins/git4idea/src/git4idea/branch/GitDeleteBranchOperation.java
@@ -68,7 +68,7 @@ class GitDeleteBranchOperation extends GitBranchOperation {
else if (notFullyMergedDetector.hasHappened()) {
String baseBranch = notMergedToUpstreamDetector.getBaseBranch();
if (baseBranch == null) { // GitBranchNotMergedToUpstreamDetector didn't happen
- baseBranch = myCurrentBranchOrRev;
+ baseBranch = myCurrentHeads.get(repository);
}
Collection<GitRepository> remainingRepositories = getRemainingRepositories();
diff --git a/plugins/git4idea/src/git4idea/branch/GitMergeOperation.java b/plugins/git4idea/src/git4idea/branch/GitMergeOperation.java
index 89a50cfd6054..1b8f382d1dd8 100644
--- a/plugins/git4idea/src/git4idea/branch/GitMergeOperation.java
+++ b/plugins/git4idea/src/git4idea/branch/GitMergeOperation.java
@@ -34,6 +34,7 @@ import git4idea.commands.*;
import git4idea.merge.GitMergeCommittingConflictResolver;
import git4idea.merge.GitMerger;
import git4idea.repo.GitRepository;
+import git4idea.reset.GitResetMode;
import git4idea.util.GitPreservingProcess;
import org.jetbrains.annotations.NotNull;
@@ -189,12 +190,13 @@ class GitMergeOperation extends GitBranchOperation {
private boolean proposeSmartMergePerformAndNotify(@NotNull GitRepository repository,
@NotNull GitMessageWithFilesDetector localChangesOverwrittenByMerge) {
Pair<List<GitRepository>, List<Change>> conflictingRepositoriesAndAffectedChanges =
- getConflictingRepositoriesAndAffectedChanges(repository, localChangesOverwrittenByMerge, myCurrentBranchOrRev, myBranchToMerge);
+ getConflictingRepositoriesAndAffectedChanges(repository, localChangesOverwrittenByMerge, myCurrentHeads.get(repository),
+ myBranchToMerge);
List<GitRepository> allConflictingRepositories = conflictingRepositoriesAndAffectedChanges.getFirst();
List<Change> affectedChanges = conflictingRepositoriesAndAffectedChanges.getSecond();
Collection<String> absolutePaths = GitUtil.toAbsolute(repository.getRoot(), localChangesOverwrittenByMerge.getRelativeFilePaths());
- int smartCheckoutDecision = myUiHandler.showSmartOperationDialog(myProject, affectedChanges, absolutePaths, "merge", false);
+ int smartCheckoutDecision = myUiHandler.showSmartOperationDialog(myProject, affectedChanges, absolutePaths, "merge", null);
if (smartCheckoutDecision == GitSmartOperationDialog.SMART_EXIT_CODE) {
return doSmartMerge(allConflictingRepositories);
}
@@ -322,7 +324,7 @@ class GitMergeOperation extends GitBranchOperation {
@NotNull
private GitCommandResult rollback(@NotNull GitRepository repository) {
- return myGit.resetHard(repository, myCurrentRevisionsBeforeMerge.get(repository));
+ return myGit.reset(repository, GitResetMode.HARD, myCurrentRevisionsBeforeMerge.get(repository));
}
@NotNull
@@ -339,7 +341,8 @@ class GitMergeOperation extends GitBranchOperation {
@NotNull
@Override
public String getSuccessMessage() {
- return String.format("Merged <b><code>%s</code></b> to <b><code>%s</code></b>", myBranchToMerge, myCurrentBranchOrRev);
+ return String.format("Merged <b><code>%s</code></b> to <b><code>%s</code></b>",
+ myBranchToMerge, stringifyBranchesByRepos(myCurrentHeads));
}
@NotNull
diff --git a/plugins/git4idea/src/git4idea/branch/GitSmartOperationDialog.java b/plugins/git4idea/src/git4idea/branch/GitSmartOperationDialog.java
index 9c2ef4a8d107..4f5073027624 100644
--- a/plugins/git4idea/src/git4idea/branch/GitSmartOperationDialog.java
+++ b/plugins/git4idea/src/git4idea/branch/GitSmartOperationDialog.java
@@ -24,6 +24,7 @@ import com.intellij.ui.components.JBLabel;
import com.intellij.util.ui.UIUtil;
import git4idea.GitPlatformFacade;
import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
import javax.swing.*;
import java.awt.event.ActionEvent;
@@ -44,18 +45,18 @@ public class GitSmartOperationDialog extends DialogWrapper {
@NotNull private final JComponent myFileBrowser;
@NotNull private final String myOperationTitle;
- private final boolean myShowForceButton;
+ @Nullable private final String myForceButton;
/**
* Shows the dialog with the list of local changes preventing merge/checkout and returns the dialog exit code.
*/
static int showAndGetAnswer(@NotNull final Project project, @NotNull final JComponent fileBrowser,
- @NotNull final String operationTitle, final boolean showForceButton) {
+ @NotNull final String operationTitle, @Nullable final String forceButtonTitle) {
final AtomicInteger exitCode = new AtomicInteger();
UIUtil.invokeAndWaitIfNeeded(new Runnable() {
@Override
public void run() {
- GitSmartOperationDialog dialog = new GitSmartOperationDialog(project, fileBrowser, operationTitle, showForceButton);
+ GitSmartOperationDialog dialog = new GitSmartOperationDialog(project, fileBrowser, operationTitle, forceButtonTitle);
ServiceManager.getService(project, GitPlatformFacade.class).showDialog(dialog);
exitCode.set(dialog.getExitCode());
}
@@ -64,11 +65,11 @@ public class GitSmartOperationDialog extends DialogWrapper {
}
private GitSmartOperationDialog(@NotNull Project project, @NotNull JComponent fileBrowser, @NotNull String operationTitle,
- boolean showForceButton) {
+ @Nullable String forceButton) {
super(project);
myFileBrowser = fileBrowser;
myOperationTitle = operationTitle;
- myShowForceButton = showForceButton;
+ myForceButton = forceButton;
String capitalizedOperation = capitalize(myOperationTitle);
setTitle("Git " + capitalizedOperation + " Problem");
@@ -82,8 +83,8 @@ public class GitSmartOperationDialog extends DialogWrapper {
@NotNull
@Override
protected Action[] createLeftSideActions() {
- if (myShowForceButton) {
- return new Action[] {new ForceCheckoutAction(myOperationTitle) };
+ if (myForceButton != null) {
+ return new Action[]{new ForceCheckoutAction(myForceButton, myOperationTitle)};
}
return new Action[0];
}
@@ -110,8 +111,8 @@ public class GitSmartOperationDialog extends DialogWrapper {
private class ForceCheckoutAction extends AbstractAction {
- ForceCheckoutAction(@NotNull String operationTitle) {
- super("&Force " + capitalize(operationTitle));
+ ForceCheckoutAction(@NotNull String buttonTitle, @NotNull String operationTitle) {
+ super(buttonTitle);
putValue(Action.SHORT_DESCRIPTION, capitalize(operationTitle) + " and overwrite local changes");
}