From 557ea857e3b5229379476f1ee4160fcf5b695bbd Mon Sep 17 00:00:00 2001 From: Chris Aniszczyk Date: Wed, 18 May 2011 11:39:33 -0500 Subject: [PATCH] Implement rebase ff for upstream branches with merge commits 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 --- .../org/eclipse/jgit/api/RebaseCommand.java | 111 ++++++++++-------- .../org/eclipse/jgit/api/RebaseResult.java | 6 +- 2 files changed, 64 insertions(+), 53 deletions(-) diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/api/RebaseCommand.java b/org.eclipse.jgit/src/org/eclipse/jgit/api/RebaseCommand.java index 7e2e677993..114ef03a3b 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/api/RebaseCommand.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/api/RebaseCommand.java @@ -199,7 +199,7 @@ public class RebaseCommand extends GitCommand { 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 { } 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 { } } 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 { // 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 cherryPickList = new ArrayList(); - 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 { 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 commitsToUse = cmd.call(); + + List cherryPickList = new ArrayList(); 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 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 { } 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; } diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/api/RebaseResult.java b/org.eclipse.jgit/src/org/eclipse/jgit/api/RebaseResult.java index c0e339017e..af070d6535 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/api/RebaseResult.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/api/RebaseResult.java @@ -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 failingPaths; - RebaseResult(Status status) { + private RebaseResult(Status status) { this.mySatus = status; currentCommit = null; } -- 2.39.5