]> source.dussan.org Git - jgit.git/commitdiff
Implement rebase ff for upstream branches with merge commits 36/3336/8
authorChris Aniszczyk <caniszczyk@gmail.com>
Wed, 18 May 2011 16:39:33 +0000 (11:39 -0500)
committerChris Aniszczyk <caniszczyk@gmail.com>
Wed, 18 May 2011 16:40:21 +0000 (11:40 -0500)
Change Ib9898fe0f982fa08e41f1dca9452c43de715fdb6 added support for
the 'cherry-pick' fast forward case where the upstream commit history
does not include any merge commits.  This change adds support for the
case where merge commits exist and the local branch has no changes.

Bug: 344779
Change-Id: If203ce5aa1b4e5d4d7982deb621b710e71f4ee10
Signed-off-by: Chris Aniszczyk <caniszczyk@gmail.com>
org.eclipse.jgit/src/org/eclipse/jgit/api/RebaseCommand.java
org.eclipse.jgit/src/org/eclipse/jgit/api/RebaseResult.java

index 7e2e67799363d6db0501395de08f691d8ccd6bb8..114ef03a3b09b95a4b40a89910963b4ea4064ae7 100644 (file)
@@ -199,7 +199,7 @@ public class RebaseCommand extends GitCommand<RebaseResult> {
                        switch (operation) {
                        case ABORT:
                                try {
-                                       return abort(new RebaseResult(Status.ABORTED));
+                                       return abort(RebaseResult.ABORTED_RESULT);
                                } catch (IOException ioe) {
                                        throw new JGitInternalException(ioe.getMessage(), ioe);
                                }
@@ -217,7 +217,7 @@ public class RebaseCommand extends GitCommand<RebaseResult> {
                        }
 
                        if (monitor.isCancelled())
-                               return abort(new RebaseResult(Status.ABORTED));
+                               return abort(RebaseResult.ABORTED_RESULT);
 
                        if (operation == Operation.CONTINUE)
                                newHead = continueRebase();
@@ -272,42 +272,48 @@ public class RebaseCommand extends GitCommand<RebaseResult> {
                                }
                        }
                        if (newHead != null) {
-                               // point the previous head (if any) to the new commit
                                String headName = readFile(rebaseDir, HEAD_NAME);
-                               if (headName.startsWith(Constants.R_REFS)) {
-                                       RefUpdate rup = repo.updateRef(headName);
-                                       rup.setNewObjectId(newHead);
-                                       Result res = rup.forceUpdate();
-                                       switch (res) {
-                                       case FAST_FORWARD:
-                                       case FORCED:
-                                       case NO_CHANGE:
-                                               break;
-                                       default:
-                                               throw new JGitInternalException("Updating HEAD failed");
-                                       }
-                                       rup = repo.updateRef(Constants.HEAD);
-                                       res = rup.link(headName);
-                                       switch (res) {
-                                       case FAST_FORWARD:
-                                       case FORCED:
-                                       case NO_CHANGE:
-                                               break;
-                                       default:
-                                               throw new JGitInternalException("Updating HEAD failed");
-                                       }
-                               }
+                               updateHead(headName, newHead);
                                FileUtils.delete(rebaseDir, FileUtils.RECURSIVE);
                                if (lastStepWasForward)
-                                       return new RebaseResult(Status.FAST_FORWARD);
-                               return new RebaseResult(Status.OK);
+                                       return RebaseResult.FAST_FORWARD_RESULT;
+                               return RebaseResult.OK_RESULT;
                        }
-                       return new RebaseResult(Status.UP_TO_DATE);
+                       return RebaseResult.FAST_FORWARD_RESULT;
                } catch (IOException ioe) {
                        throw new JGitInternalException(ioe.getMessage(), ioe);
                }
        }
 
+       private void updateHead(String headName, RevCommit newHead)
+                       throws IOException {
+               // point the previous head (if any) to the new commit
+
+               if (headName.startsWith(Constants.R_REFS)) {
+                       RefUpdate rup = repo.updateRef(headName);
+                       rup.setNewObjectId(newHead);
+                       Result res = rup.forceUpdate();
+                       switch (res) {
+                       case FAST_FORWARD:
+                       case FORCED:
+                       case NO_CHANGE:
+                               break;
+                       default:
+                               throw new JGitInternalException("Updating HEAD failed");
+                       }
+                       rup = repo.updateRef(Constants.HEAD);
+                       res = rup.link(headName);
+                       switch (res) {
+                       case FAST_FORWARD:
+                       case FORCED:
+                       case NO_CHANGE:
+                               break;
+                       default:
+                               throw new JGitInternalException("Updating HEAD failed");
+                       }
+               }
+       }
+
        private RevCommit checkoutCurrentHead() throws IOException,
                        NoHeadException, JGitInternalException {
                ObjectId headTree = repo.resolve(Constants.HEAD + "^{tree}");
@@ -505,9 +511,6 @@ public class RebaseCommand extends GitCommand<RebaseResult> {
                // we need to store everything into files so that we can implement
                // --skip, --continue, and --abort
 
-               // first of all, we determine the commits to be applied
-               List<RevCommit> cherryPickList = new ArrayList<RevCommit>();
-
                Ref head = repo.getRef(Constants.HEAD);
                if (head == null || head.getObjectId() == null)
                        throw new RefNotFoundException(MessageFormat.format(
@@ -523,36 +526,38 @@ public class RebaseCommand extends GitCommand<RebaseResult> {
                        throw new RefNotFoundException(MessageFormat.format(
                                        JGitText.get().refNotResolved, Constants.HEAD));
                RevCommit headCommit = walk.lookupCommit(headId);
+               RevCommit upstream = walk.lookupCommit(upstreamCommit.getId());
+
+               if (walk.isMergedInto(upstream, headCommit))
+                       return RebaseResult.UP_TO_DATE_RESULT;
+               else if (walk.isMergedInto(headCommit, upstream)) {
+                       // head is already merged into upstream, fast-foward
+                       monitor.beginTask(MessageFormat.format(
+                                       JGitText.get().resettingHead,
+                                       upstreamCommit.getShortMessage()), ProgressMonitor.UNKNOWN);
+                       checkoutCommit(upstreamCommit);
+                       monitor.endTask();
+
+                       updateHead(headName, upstreamCommit);
+                       return RebaseResult.FAST_FORWARD_RESULT;
+               }
+
                monitor.beginTask(JGitText.get().obtainingCommitsForCherryPick,
                                ProgressMonitor.UNKNOWN);
 
+               // determine the commits to be applied
                LogCommand cmd = new Git(repo).log().addRange(upstreamCommit,
                                headCommit);
                Iterable<RevCommit> commitsToUse = cmd.call();
+
+               List<RevCommit> cherryPickList = new ArrayList<RevCommit>();
                for (RevCommit commit : commitsToUse) {
+                       if (commit.getParentCount() != 1)
+                               throw new JGitInternalException(
+                                               JGitText.get().canOnlyCherryPickCommitsWithOneParent);
                        cherryPickList.add(commit);
                }
 
-               // if the upstream commit is in a direct line to the current head,
-               // the log command will not report any commits; in this case,
-               // we create the cherry-pick list ourselves
-               if (cherryPickList.isEmpty()) {
-                       Iterable<RevCommit> parents = new Git(repo).log().add(
-                                       upstreamCommit).call();
-                       for (RevCommit parent : parents) {
-                               if (parent.equals(headCommit))
-                                       break;
-                               if (parent.getParentCount() != 1)
-                                       throw new JGitInternalException(
-                                                       JGitText.get().canOnlyCherryPickCommitsWithOneParent);
-                               cherryPickList.add(parent);
-                       }
-               }
-
-               // nothing to do: return with UP_TO_DATE_RESULT
-               if (cherryPickList.isEmpty())
-                       return RebaseResult.UP_TO_DATE_RESULT;
-
                Collections.reverse(cherryPickList);
                // create the folder for the meta information
                FileUtils.mkdir(rebaseDir);
@@ -586,11 +591,13 @@ public class RebaseCommand extends GitCommand<RebaseResult> {
                }
 
                monitor.endTask();
+
                // we rewind to the upstream commit
                monitor.beginTask(MessageFormat.format(JGitText.get().rewinding,
                                upstreamCommit.getShortMessage()), ProgressMonitor.UNKNOWN);
                checkoutCommit(upstreamCommit);
                monitor.endTask();
+
                return null;
        }
 
index c0e339017eab70cb6baa470b0f60882a9ea633ac..af070d6535e88555af075b22d1960662e0e65dba 100644 (file)
@@ -82,6 +82,10 @@ public class RebaseResult {
                FAST_FORWARD;
        }
 
+       static final RebaseResult OK_RESULT = new RebaseResult(Status.OK);
+
+       static final RebaseResult ABORTED_RESULT = new RebaseResult(Status.ABORTED);
+
        static final RebaseResult UP_TO_DATE_RESULT = new RebaseResult(
                        Status.UP_TO_DATE);
 
@@ -94,7 +98,7 @@ public class RebaseResult {
 
        private Map<String, MergeFailureReason> failingPaths;
 
-       RebaseResult(Status status) {
+       private RebaseResult(Status status) {
                this.mySatus = status;
                currentCommit = null;
        }