Fix RecursiveMerger in case of multiple, independent base commits

When RecursiveMerger found that there are multiple base-commits for the
commits to be merged it tries to temporarily merge the base commits. But
if these base commits have no common predecessor there was a bug in JGit
leading to a NPE. This commit fixes this by enforcing that an empty tree
is used as base when merging two unrelated base commits.

This logic was already there when merging two commits which have no
common predecessor (ThreeWayMerger.mergeBase()). But the code which was
computing a new temporary base commit in case of criss-cross merges
didn't take care to pick an empty tree when no common predecessor can be
found.

Bug: 462671
Change-Id: Ibd96302f5f81383f36d3b1e3edcbf5822147b1a4
This commit is contained in:
Christian Halstrick 2015-03-25 10:48:03 +01:00
parent f1a15f7eb0
commit 2bd3556e7e
2 changed files with 71 additions and 4 deletions

View File

@ -175,6 +175,69 @@ public void crissCrossMerge(MergeStrategy strategy, IndexState indexState,
}
}
@Theory
/**
* Merging m2,s2 from the following topology. m1 and s1 are the two root
* commits of the repo. In master and side different files are touched.
* No need to do a real content merge.
*
* <pre>
* m1--m2
* \/
* /\
* s1--s2
* </pre>
*/
public void crissCrossMerge_twoRoots(MergeStrategy strategy,
IndexState indexState, WorktreeState worktreeState)
throws Exception {
if (!validateStates(indexState, worktreeState))
return;
// fill the repo
BranchBuilder master = db_t.branch("master");
BranchBuilder side = db_t.branch("side");
RevCommit m1 = master.commit().add("m", "m1").message("m1").create();
db_t.getRevWalk().parseCommit(m1);
RevCommit s1 = side.commit().add("s", "s1").message("s1").create();
RevCommit s2 = side.commit().parent(m1).add("m", "m1")
.message("s2(merge)").create();
RevCommit m2 = master.commit().parent(s1).add("s", "s1")
.message("m2(merge)").create();
Git git = Git.wrap(db);
git.checkout().setName("master").call();
modifyWorktree(worktreeState, "m", "side");
modifyWorktree(worktreeState, "s", "side");
modifyIndex(indexState, "m", "side");
modifyIndex(indexState, "s", "side");
ResolveMerger merger = (ResolveMerger) strategy.newMerger(db,
worktreeState == WorktreeState.Bare);
if (worktreeState != WorktreeState.Bare)
merger.setWorkingTreeIterator(new FileTreeIterator(db));
try {
boolean expectSuccess = true;
if (!(indexState == IndexState.Bare
|| indexState == IndexState.Missing
|| indexState == IndexState.SameAsHead || indexState == IndexState.SameAsOther))
// index is dirty
expectSuccess = false;
assertEquals(Boolean.valueOf(expectSuccess),
Boolean.valueOf(merger.merge(new RevCommit[] { m2, s2 })));
assertEquals(MergeStrategy.RECURSIVE, strategy);
assertEquals("m1",
contentAsString(db, merger.getResultTreeId(), "m"));
assertEquals("s1",
contentAsString(db, merger.getResultTreeId(), "s"));
} catch (NoMergeBaseException e) {
assertEquals(MergeStrategy.RESOLVE, strategy);
assertEquals(e.getReason(),
MergeBaseFailureReason.MULTIPLE_MERGE_BASES_NOT_SUPPORTED);
}
}
@Theory
/**
* Merging m2,s2 from the following topology. The same file is modified

View File

@ -68,6 +68,8 @@
import org.eclipse.jgit.lib.Repository;
import org.eclipse.jgit.revwalk.RevCommit;
import org.eclipse.jgit.revwalk.filter.RevFilter;
import org.eclipse.jgit.treewalk.AbstractTreeIterator;
import org.eclipse.jgit.treewalk.EmptyTreeIterator;
import org.eclipse.jgit.treewalk.TreeWalk;
import org.eclipse.jgit.treewalk.WorkingTreeIterator;
@ -194,10 +196,12 @@ protected RevCommit getBaseCommit(RevCommit a, RevCommit b, int callDepth)
Integer.valueOf(MAX_BASES), a.name(), b.name(),
Integer.valueOf(baseCommits.size())));
parents.add(nextBase);
if (mergeTrees(
openTree(getBaseCommit(currentBase, nextBase,
callDepth + 1).getTree()),
currentBase.getTree(), nextBase.getTree(), true))
RevCommit bc = getBaseCommit(currentBase, nextBase,
callDepth + 1);
AbstractTreeIterator bcTree = (bc == null) ? new EmptyTreeIterator()
: openTree(bc.getTree());
if (mergeTrees(bcTree, currentBase.getTree(),
nextBase.getTree(), true))
currentBase = createCommitForTree(resultTree, parents);
else
throw new NoMergeBaseException(