CherryPick: Keep going in the face of a single merge conflict
In addition to verifying this behavior in a test, this also fixes an issue caused by setting mergeTip to null. On the next iteration of the loop we were incorrectly treating the null merge tip as a cherry-pick onto an unborn root. Setting the commit as CLEAN_MERGE resulted in rejecting the later ref update in MergeOp. Change-Id: I82299ea96725a21430a0871199cde3e2f3af267b
This commit is contained in:
@@ -268,4 +268,36 @@ public class SubmitByCherryPickIT extends AbstractSubmit {
|
|||||||
assertThat(log.get(2).getShortMessage())
|
assertThat(log.get(2).getShortMessage())
|
||||||
.isEqualTo(initialHead.getShortMessage());
|
.isEqualTo(initialHead.getShortMessage());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void submitChangeAfterParentFailsDueToConflict() throws Exception {
|
||||||
|
Git git = createProject();
|
||||||
|
RevCommit initialHead = getRemoteHead();
|
||||||
|
|
||||||
|
checkout(git, initialHead.getId().getName());
|
||||||
|
PushOneCommit.Result change2 = createChange(git, "Change 2", "b", "b1");
|
||||||
|
submit(change2.getChangeId());
|
||||||
|
|
||||||
|
checkout(git, initialHead.getId().getName());
|
||||||
|
PushOneCommit.Result change3 = createChange(git, "Change 3", "b", "b2");
|
||||||
|
assertThat(change3.getCommit().getParent(0)).isEqualTo(initialHead);
|
||||||
|
PushOneCommit.Result change4 = createChange(git, "Change 3", "c", "c3");
|
||||||
|
|
||||||
|
submitStatusOnly(change3.getChangeId());
|
||||||
|
submitStatusOnly(change4.getChangeId());
|
||||||
|
|
||||||
|
// Merge fails; change3 contains the delta "b1" -> "b2", which cannot be
|
||||||
|
// applied against tip.
|
||||||
|
submitWithConflict(change3.getChangeId());
|
||||||
|
|
||||||
|
// change4 is a clean merge, so should succeed in the same run where change3
|
||||||
|
// failed.
|
||||||
|
ChangeInfo info4 = get(change4.getChangeId());
|
||||||
|
assertThat(info4.status).isEqualTo(ChangeStatus.MERGED);
|
||||||
|
List<RevCommit> log = getRemoteLog();
|
||||||
|
assertThat(log.get(0).getShortMessage())
|
||||||
|
.isEqualTo(change4.getCommit().getShortMessage());
|
||||||
|
assertThat(log.get(1).getShortMessage())
|
||||||
|
.isEqualTo(change2.getCommit().getShortMessage());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -72,11 +72,11 @@ public class CherryPick extends SubmitStrategy {
|
|||||||
CodeReviewCommit n = sorted.remove(0);
|
CodeReviewCommit n = sorted.remove(0);
|
||||||
try {
|
try {
|
||||||
if (mergeTip.getCurrentTip() == null) {
|
if (mergeTip.getCurrentTip() == null) {
|
||||||
mergeTip = cherryPickUnbornRoot(n);
|
cherryPickUnbornRoot(n, mergeTip);
|
||||||
} else if (n.getParentCount() == 0) {
|
} else if (n.getParentCount() == 0) {
|
||||||
cherryPickRootOntoBranch(n);
|
cherryPickRootOntoBranch(n);
|
||||||
} else if (n.getParentCount() == 1) {
|
} else if (n.getParentCount() == 1) {
|
||||||
mergeTip = cherryPickOne(n, mergeTip);
|
cherryPickOne(n, mergeTip);
|
||||||
} else {
|
} else {
|
||||||
cherryPickMultipleParents(n, mergeTip);
|
cherryPickMultipleParents(n, mergeTip);
|
||||||
}
|
}
|
||||||
@@ -87,11 +87,10 @@ public class CherryPick extends SubmitStrategy {
|
|||||||
return mergeTip;
|
return mergeTip;
|
||||||
}
|
}
|
||||||
|
|
||||||
private MergeTip cherryPickUnbornRoot(CodeReviewCommit n) {
|
private void cherryPickUnbornRoot(CodeReviewCommit n, MergeTip mergeTip) {
|
||||||
// The branch is unborn. Take fast-forward resolution to create the branch.
|
// The branch is unborn. Take fast-forward resolution to create the branch.
|
||||||
MergeTip mergeTip = new MergeTip(n, Lists.newArrayList(n));
|
mergeTip.moveTipTo(n, n);
|
||||||
n.setStatusCode(CommitMergeStatus.CLEAN_MERGE);
|
n.setStatusCode(CommitMergeStatus.CLEAN_MERGE);
|
||||||
return mergeTip;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void cherryPickRootOntoBranch(CodeReviewCommit n) {
|
private void cherryPickRootOntoBranch(CodeReviewCommit n) {
|
||||||
@@ -100,24 +99,24 @@ public class CherryPick extends SubmitStrategy {
|
|||||||
n.setStatusCode(CommitMergeStatus.CANNOT_CHERRY_PICK_ROOT);
|
n.setStatusCode(CommitMergeStatus.CANNOT_CHERRY_PICK_ROOT);
|
||||||
}
|
}
|
||||||
|
|
||||||
private MergeTip cherryPickOne(CodeReviewCommit n, MergeTip mergeTip)
|
private void cherryPickOne(CodeReviewCommit n, MergeTip mergeTip)
|
||||||
throws NoSuchChangeException, OrmException, IOException {
|
throws NoSuchChangeException, OrmException, IOException {
|
||||||
// If there is only one parent, a cherry-pick can be done by
|
// If there is only one parent, a cherry-pick can be done by taking the
|
||||||
// taking the delta relative to that one parent and redoing
|
// delta relative to that one parent and redoing that on the current merge
|
||||||
// that on the current merge tip.
|
// tip.
|
||||||
|
//
|
||||||
|
// Keep going in the case of a single merge failure; the goal is to
|
||||||
|
// cherry-pick as many commits as possible.
|
||||||
try {
|
try {
|
||||||
CodeReviewCommit merge =
|
CodeReviewCommit merge =
|
||||||
writeCherryPickCommit(mergeTip.getCurrentTip(), n);
|
writeCherryPickCommit(mergeTip.getCurrentTip(), n);
|
||||||
mergeTip.moveTipTo(merge, merge);
|
mergeTip.moveTipTo(merge, merge);
|
||||||
newCommits.put(mergeTip.getCurrentTip().getPatchsetId()
|
newCommits.put(mergeTip.getCurrentTip().getPatchsetId()
|
||||||
.getParentKey(), mergeTip.getCurrentTip());
|
.getParentKey(), mergeTip.getCurrentTip());
|
||||||
return mergeTip;
|
|
||||||
} catch (MergeConflictException mce) {
|
} catch (MergeConflictException mce) {
|
||||||
n.setStatusCode(CommitMergeStatus.PATH_CONFLICT);
|
n.setStatusCode(CommitMergeStatus.PATH_CONFLICT);
|
||||||
return null;
|
|
||||||
} catch (MergeIdenticalTreeException mie) {
|
} catch (MergeIdenticalTreeException mie) {
|
||||||
n.setStatusCode(CommitMergeStatus.ALREADY_MERGED);
|
n.setStatusCode(CommitMergeStatus.ALREADY_MERGED);
|
||||||
return null;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user