[git] IDEA-87609 When updating via Rebase, use merge instead of rebase, if it is fast-forward.

Before starting the normal update procedure (with possible stash-unstash), try to perform a fast-forward merge instead of rebase, because it is equal to fast-forward rebase, but is faster, because doesn't require stash-unstash (unless the uncommitted files would be overwritten by merge)
Any failure in 'git merge --ff-only' means, than this repository will then be updated in a usual manner.
No pre-checking if a fast-forward merge is possible is done, because it is fast enough to execute the command and get the error.
This commit is contained in:
Kirill Likhodedov
2012-06-21 17:41:09 +04:00
parent 02c9e05803
commit 85bf22894c
6 changed files with 53 additions and 8 deletions
@@ -93,7 +93,7 @@ class GitMergeOperation extends GitBranchOperation {
GitSimpleEventDetector mergeConflict = new GitSimpleEventDetector(GitSimpleEventDetector.Event.MERGE_CONFLICT);
GitSimpleEventDetector alreadyUpToDateDetector = new GitSimpleEventDetector(GitSimpleEventDetector.Event.ALREADY_UP_TO_DATE);
GitCommandResult result = myGit.merge(repository, myBranchToMerge,
GitCommandResult result = myGit.merge(repository, myBranchToMerge, Collections.<String>emptyList(),
localChangesOverwrittenByMerge, unmergedFiles, untrackedOverwrittenByMerge, mergeConflict,
alreadyUpToDateDetector);
if (result.success()) {
@@ -238,7 +238,7 @@ class GitMergeOperation extends GitBranchOperation {
private boolean doMerge(@NotNull Collection<GitRepository> repositories) {
for (GitRepository repository : repositories) {
GitSimpleEventDetector mergeConflict = new GitSimpleEventDetector(GitSimpleEventDetector.Event.MERGE_CONFLICT);
GitCommandResult result = myGit.merge(repository, myBranchToMerge, mergeConflict);
GitCommandResult result = myGit.merge(repository, myBranchToMerge, Collections.<String>emptyList(), mergeConflict);
if (!result.success()) {
if (mergeConflict.hasHappened()) {
myConflictedRepositories.put(repository, Boolean.TRUE);
@@ -49,7 +49,8 @@ public interface Git {
GitCommandResult clone(@NotNull Project project, @NotNull File parentDirectory, @NotNull String url, @NotNull String clonedDirectoryName);
@NotNull
GitCommandResult merge(@NotNull GitRepository repository, @NotNull String branchToMerge, @NotNull GitLineHandlerListener... listeners);
GitCommandResult merge(@NotNull GitRepository repository, @NotNull String branchToMerge, @Nullable List<String> additionalParams,
@NotNull GitLineHandlerListener... listeners);
@NotNull
GitCommandResult checkout(@NotNull GitRepository repository, @NotNull String reference, @Nullable String newBranch, boolean force,
@@ -139,10 +139,13 @@ public class GitImpl implements Git {
@Override
@NotNull
public GitCommandResult merge(@NotNull GitRepository repository, @NotNull String branchToMerge,
@NotNull GitLineHandlerListener... listeners) {
@Nullable List<String> additionalParams, @NotNull GitLineHandlerListener... listeners) {
final GitLineHandler mergeHandler = new GitLineHandler(repository.getProject(), repository.getRoot(), GitCommand.MERGE);
mergeHandler.setSilent(false);
mergeHandler.addParameters(branchToMerge);
if (additionalParams != null) {
mergeHandler.addParameters(additionalParams);
}
for (GitLineHandlerListener listener : listeners) {
mergeHandler.addLineListener(listener);
}
@@ -24,12 +24,15 @@ import com.intellij.openapi.vcs.VcsException;
import com.intellij.openapi.vcs.update.UpdatedFiles;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.util.ui.UIUtil;
import git4idea.GitBranch;
import git4idea.GitUtil;
import git4idea.PlatformFacade;
import git4idea.branch.GitBranchPair;
import git4idea.commands.*;
import git4idea.merge.GitConflictResolver;
import git4idea.rebase.GitRebaseProblemDetector;
import git4idea.rebase.GitRebaser;
import git4idea.repo.GitRepository;
import git4idea.util.GitUIUtil;
import git4idea.util.UntrackedFilesNotifier;
import org.jetbrains.annotations.NotNull;
@@ -60,8 +63,7 @@ public class GitRebaseUpdater extends GitUpdater {
protected GitUpdateResult doUpdate() {
LOG.info("doUpdate ");
GitBranchPair gitBranchPair = myTrackedBranches.get(myRoot);
String remoteBranch = gitBranchPair.getDest().getName();
String remoteBranch = getRemoteBranchToMerge();
final GitLineHandler rebaseHandler = new GitLineHandler(myProject, myRoot, GitCommand.REBASE);
rebaseHandler.addParameters(remoteBranch);
@@ -99,6 +101,15 @@ public class GitRebaseUpdater extends GitUpdater {
return updateResult.get();
}
@NotNull
private String getRemoteBranchToMerge() {
GitBranchPair gitBranchPair = myTrackedBranches.get(myRoot);
GitBranch dest = gitBranchPair.getDest();
LOG.assertTrue(dest != null, String.format("Destination branch is null for source branch %s in %s",
gitBranchPair.getBranch().getName(), myRoot));
return dest.getName();
}
private GitUpdateResult handleRebaseFailure(GitLineHandler pullHandler,
GitRebaseProblemDetector rebaseConflictDetector,
final GitMessageWithFilesDetector untrackedWouldBeOverwrittenDetector) {
@@ -159,6 +170,17 @@ public class GitRebaseUpdater extends GitUpdater {
return "Rebase updater";
}
public boolean fastForwardMerge() {
LOG.info("Trying fast-forward merge for " + myRoot);
GitRepository repository = GitUtil.getRepositoryManager(myProject).getRepositoryForRoot(myRoot);
if (repository == null) {
LOG.error("Repository is null for " + myRoot);
return false;
}
GitCommandResult result = myGit.merge(repository, getRemoteBranchToMerge(), Collections.singletonList("--ff-only"));
return result.success();
}
private static class MyConflictResolver extends GitConflictResolver {
private final GitRebaser myRebaser;
private final VirtualFile myRoot;
@@ -134,7 +134,7 @@ public class GitUpdateProcess {
@NotNull
private GitUpdateResult updateImpl(@NotNull UpdateMethod updateMethod, ContinuationContext context) {
final Map<VirtualFile, GitUpdater> updaters;
Map<VirtualFile, GitUpdater> updaters;
try {
updaters = defineUpdaters(updateMethod);
}
@@ -144,6 +144,8 @@ public class GitUpdateProcess {
return GitUpdateResult.ERROR;
}
updaters = tryFastForwardMergeForRebaseUpdaters(updaters);
if (updaters.isEmpty()) {
return GitUpdateResult.NOTHING_TO_UPDATE;
}
@@ -204,6 +206,22 @@ public class GitUpdateProcess {
return compoundResult;
}
@NotNull
private static Map<VirtualFile, GitUpdater> tryFastForwardMergeForRebaseUpdaters(@NotNull Map<VirtualFile, GitUpdater> updaters) {
Map<VirtualFile, GitUpdater> modifiedUpdaters = new HashMap<VirtualFile, GitUpdater>();
for (Map.Entry<VirtualFile, GitUpdater> updaterEntry : updaters.entrySet()) {
GitUpdater updater = updaterEntry.getValue();
if (updater instanceof GitRebaseUpdater) {
GitRebaseUpdater rebaseUpdater = (GitRebaseUpdater) updater;
if (rebaseUpdater.fastForwardMerge()) {
continue;
}
}
modifiedUpdaters.put(updaterEntry.getKey(), updaterEntry.getValue());
}
return modifiedUpdaters;
}
@NotNull
private Map<VirtualFile, GitUpdater> defineUpdaters(@NotNull UpdateMethod updateMethod) throws VcsException {
final Map<VirtualFile, GitUpdater> updaters = new HashMap<VirtualFile, GitUpdater>();
@@ -88,7 +88,8 @@ class MockGit implements Git {
@NotNull
@Override
GitCommandResult merge(@NotNull GitRepository repository, @NotNull String branchToMerge, @NotNull GitLineHandlerListener... listeners) {
GitCommandResult merge(@NotNull GitRepository repository, @NotNull String branchToMerge, @Nullable List<String> additionalParams,
@NotNull GitLineHandlerListener... listeners) {
throw new UnsupportedOperationException()
}