Don't crash the submit queue when a change is a criss-cross merge
A criss-cross merge occurs when two branches have merged in both
directions and there are now multiple candidate merge bases for
the merge between the change and the branch head. (For more on
this see http://revctrl.org/CrissCrossMerge.)
If JGit finds multiple merge bases, it throws an IOException. If
we catch this and mark a special status code, we can unsubmit the
change and let the user know about the problem by reporting it as
a message on the change, just like we do with path conflicts.
Bug: GERRIT-171
Signed-off-by: Shawn O. Pearce <sop@google.com>
diff --git a/src/main/java/com/google/gerrit/git/CommitMergeStatus.java b/src/main/java/com/google/gerrit/git/CommitMergeStatus.java
index e1ca173..4fa71de 100644
--- a/src/main/java/com/google/gerrit/git/CommitMergeStatus.java
+++ b/src/main/java/com/google/gerrit/git/CommitMergeStatus.java
@@ -34,5 +34,8 @@
NO_PATCH_SET,
/** */
- REVISION_GONE;
+ REVISION_GONE,
+
+ /** */
+ CRISS_CROSS_MERGE;
}
diff --git a/src/main/java/com/google/gerrit/git/MergeOp.java b/src/main/java/com/google/gerrit/git/MergeOp.java
index 53694d8..81869b9 100644
--- a/src/main/java/com/google/gerrit/git/MergeOp.java
+++ b/src/main/java/com/google/gerrit/git/MergeOp.java
@@ -342,25 +342,40 @@
writeMergeCommit(m, n);
} else {
- rw.reset();
- rw.markStart(n);
- rw.markUninteresting(mergeTip);
- CodeReviewCommit failed;
- while ((failed = (CodeReviewCommit) rw.next()) != null) {
- if (failed.patchsetId == null) {
- continue;
- }
-
- failed.statusCode = CommitMergeStatus.PATH_CONFLICT;
- status.put(failed.patchsetId.getParentKey(), failed.statusCode);
- }
+ failed(n, CommitMergeStatus.PATH_CONFLICT);
}
} catch (IOException e) {
- throw new MergeException("Cannot merge " + n.name(), e);
+ if (e.getMessage().startsWith("Multiple merge bases for")) {
+ try {
+ failed(n, CommitMergeStatus.CRISS_CROSS_MERGE);
+ } catch (IOException e2) {
+ throw new MergeException("Cannot merge " + n.name(), e);
+ }
+ } else {
+ throw new MergeException("Cannot merge " + n.name(), e);
+ }
}
}
}
+ private CodeReviewCommit failed(final CodeReviewCommit n,
+ final CommitMergeStatus failure) throws MissingObjectException,
+ IncorrectObjectTypeException, IOException {
+ rw.reset();
+ rw.markStart(n);
+ rw.markUninteresting(mergeTip);
+ CodeReviewCommit failed;
+ while ((failed = (CodeReviewCommit) rw.next()) != null) {
+ if (failed.patchsetId == null) {
+ continue;
+ }
+
+ failed.statusCode = failure;
+ status.put(failed.patchsetId.getParentKey(), failed.statusCode);
+ }
+ return failed;
+ }
+
private void writeMergeCommit(final Merger m, final CodeReviewCommit n)
throws IOException, MissingObjectException, IncorrectObjectTypeException {
final List<CodeReviewCommit> merged = new ArrayList<CodeReviewCommit>();
@@ -749,6 +764,15 @@
break;
}
+ case CRISS_CROSS_MERGE: {
+ final String txt =
+ "Your change requires a recursive merge to resolve.\n"
+ + "\n"
+ + "Please merge (or rebase) the change locally and upload the resolution for review.";
+ setNew(c, message(c, txt));
+ break;
+ }
+
case MISSING_DEPENDENCY: {
ChangeMessage msg = null;
try {