Browse Source

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>
stable-1.0
Chris Aniszczyk 14 years ago
parent
commit
557ea857e3
  1. 111
      org.eclipse.jgit/src/org/eclipse/jgit/api/RebaseCommand.java
  2. 6
      org.eclipse.jgit/src/org/eclipse/jgit/api/RebaseResult.java

111
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;
}

6
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;
}

Loading…
Cancel
Save