diff options
author | Chris Aniszczyk <caniszczyk@gmail.com> | 2011-05-18 11:39:33 -0500 |
---|---|---|
committer | Chris Aniszczyk <caniszczyk@gmail.com> | 2011-05-18 11:40:21 -0500 |
commit | 557ea857e3b5229379476f1ee4160fcf5b695bbd (patch) | |
tree | bdfe51118576afbc4cc1cf2cd129e1d89f6b034b | |
parent | 7449b9a297af193107560c42508c53d7adb00226 (diff) | |
download | jgit-557ea857e3b5229379476f1ee4160fcf5b695bbd.tar.gz jgit-557ea857e3b5229379476f1ee4160fcf5b695bbd.zip |
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 <caniszczyk@gmail.com>
-rw-r--r-- | org.eclipse.jgit/src/org/eclipse/jgit/api/RebaseCommand.java | 111 | ||||
-rw-r--r-- | org.eclipse.jgit/src/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<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; } 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<String, MergeFailureReason> failingPaths; - RebaseResult(Status status) { + private RebaseResult(Status status) { this.mySatus = status; currentCommit = null; } |