Permalink
Browse files

Fix merges involving clean subtrees with StrategySimpleTwoWayInCore

If a both sides modify files in the same subtree, but do so in a
non-conflicting way, we should still be able to merge them by an
automated merge strategy.  Recursing into the subtree permits us
to do this merge.

Signed-off-by: Shawn O. Pearce <spearce@spearce.org>
Signed-off-by: Robin Rosenberg <robin.rosenberg@dewire.com>
  • Loading branch information...
1 parent aa91718 commit 093920a17c12be35a2c29b8163166ef258f54be2 @spearce spearce committed with robinrosenberg Mar 6, 2009
View
204 org.spearce.jgit.test/tst/org/spearce/jgit/merge/SimpleMergeTest.java
@@ -146,6 +146,210 @@ public void testTrivialTwoWay_validSubtreeSort() throws Exception {
assertFalse(tw.next());
}
+ public void testTrivialTwoWay_concurrentSubtreeChange() throws Exception {
+ final DirCache treeB = DirCache.read(db);
+ final DirCache treeO = DirCache.read(db);
+ final DirCache treeT = DirCache.read(db);
+ {
+ final DirCacheBuilder b = treeB.builder();
+ final DirCacheBuilder o = treeO.builder();
+ final DirCacheBuilder t = treeT.builder();
+
+ b.add(makeEntry("d/o", FileMode.REGULAR_FILE));
+ b.add(makeEntry("d/t", FileMode.REGULAR_FILE));
+
+ o.add(makeEntry("d/o", FileMode.REGULAR_FILE, "o !"));
+ o.add(makeEntry("d/t", FileMode.REGULAR_FILE));
+
+ t.add(makeEntry("d/o", FileMode.REGULAR_FILE));
+ t.add(makeEntry("d/t", FileMode.REGULAR_FILE, "t !"));
+
+ b.finish();
+ o.finish();
+ t.finish();
+ }
+
+ final ObjectWriter ow = new ObjectWriter(db);
+ final ObjectId b = commit(ow, treeB, new ObjectId[] {});
+ final ObjectId o = commit(ow, treeO, new ObjectId[] { b });
+ final ObjectId t = commit(ow, treeT, new ObjectId[] { b });
+
+ Merger ourMerger = MergeStrategy.SIMPLE_TWO_WAY_IN_CORE.newMerger(db);
+ boolean merge = ourMerger.merge(new ObjectId[] { o, t });
+ assertTrue(merge);
+
+ final TreeWalk tw = new TreeWalk(db);
+ tw.setRecursive(true);
+ tw.reset(ourMerger.getResultTreeId());
+
+ assertTrue(tw.next());
+ assertEquals("d/o", tw.getPathString());
+ assertCorrectId(treeO, tw);
+
+ assertTrue(tw.next());
+ assertEquals("d/t", tw.getPathString());
+ assertCorrectId(treeT, tw);
+
+ assertFalse(tw.next());
+ }
+
+ public void testTrivialTwoWay_conflictSubtreeChange() throws Exception {
+ final DirCache treeB = DirCache.read(db);
+ final DirCache treeO = DirCache.read(db);
+ final DirCache treeT = DirCache.read(db);
+ {
+ final DirCacheBuilder b = treeB.builder();
+ final DirCacheBuilder o = treeO.builder();
+ final DirCacheBuilder t = treeT.builder();
+
+ b.add(makeEntry("d/o", FileMode.REGULAR_FILE));
+ b.add(makeEntry("d/t", FileMode.REGULAR_FILE));
+
+ o.add(makeEntry("d/o", FileMode.REGULAR_FILE));
+ o.add(makeEntry("d/t", FileMode.REGULAR_FILE, "o !"));
+
+ t.add(makeEntry("d/o", FileMode.REGULAR_FILE, "t !"));
+ t.add(makeEntry("d/t", FileMode.REGULAR_FILE, "t !"));
+
+ b.finish();
+ o.finish();
+ t.finish();
+ }
+
+ final ObjectWriter ow = new ObjectWriter(db);
+ final ObjectId b = commit(ow, treeB, new ObjectId[] {});
+ final ObjectId o = commit(ow, treeO, new ObjectId[] { b });
+ final ObjectId t = commit(ow, treeT, new ObjectId[] { b });
+
+ Merger ourMerger = MergeStrategy.SIMPLE_TWO_WAY_IN_CORE.newMerger(db);
+ boolean merge = ourMerger.merge(new ObjectId[] { o, t });
+ assertFalse(merge);
+ }
+
+ public void testTrivialTwoWay_leftDFconflict1() throws Exception {
+ final DirCache treeB = DirCache.read(db);
+ final DirCache treeO = DirCache.read(db);
+ final DirCache treeT = DirCache.read(db);
+ {
+ final DirCacheBuilder b = treeB.builder();
+ final DirCacheBuilder o = treeO.builder();
+ final DirCacheBuilder t = treeT.builder();
+
+ b.add(makeEntry("d/o", FileMode.REGULAR_FILE));
+ b.add(makeEntry("d/t", FileMode.REGULAR_FILE));
+
+ o.add(makeEntry("d", FileMode.REGULAR_FILE));
+
+ t.add(makeEntry("d/o", FileMode.REGULAR_FILE));
+ t.add(makeEntry("d/t", FileMode.REGULAR_FILE, "t !"));
+
+ b.finish();
+ o.finish();
+ t.finish();
+ }
+
+ final ObjectWriter ow = new ObjectWriter(db);
+ final ObjectId b = commit(ow, treeB, new ObjectId[] {});
+ final ObjectId o = commit(ow, treeO, new ObjectId[] { b });
+ final ObjectId t = commit(ow, treeT, new ObjectId[] { b });
+
+ Merger ourMerger = MergeStrategy.SIMPLE_TWO_WAY_IN_CORE.newMerger(db);
+ boolean merge = ourMerger.merge(new ObjectId[] { o, t });
+ assertFalse(merge);
+ }
+
+ public void testTrivialTwoWay_rightDFconflict1() throws Exception {
+ final DirCache treeB = DirCache.read(db);
+ final DirCache treeO = DirCache.read(db);
+ final DirCache treeT = DirCache.read(db);
+ {
+ final DirCacheBuilder b = treeB.builder();
+ final DirCacheBuilder o = treeO.builder();
+ final DirCacheBuilder t = treeT.builder();
+
+ b.add(makeEntry("d/o", FileMode.REGULAR_FILE));
+ b.add(makeEntry("d/t", FileMode.REGULAR_FILE));
+
+ o.add(makeEntry("d/o", FileMode.REGULAR_FILE));
+ o.add(makeEntry("d/t", FileMode.REGULAR_FILE, "o !"));
+
+ t.add(makeEntry("d", FileMode.REGULAR_FILE));
+
+ b.finish();
+ o.finish();
+ t.finish();
+ }
+
+ final ObjectWriter ow = new ObjectWriter(db);
+ final ObjectId b = commit(ow, treeB, new ObjectId[] {});
+ final ObjectId o = commit(ow, treeO, new ObjectId[] { b });
+ final ObjectId t = commit(ow, treeT, new ObjectId[] { b });
+
+ Merger ourMerger = MergeStrategy.SIMPLE_TWO_WAY_IN_CORE.newMerger(db);
+ boolean merge = ourMerger.merge(new ObjectId[] { o, t });
+ assertFalse(merge);
+ }
+
+ public void testTrivialTwoWay_leftDFconflict2() throws Exception {
+ final DirCache treeB = DirCache.read(db);
+ final DirCache treeO = DirCache.read(db);
+ final DirCache treeT = DirCache.read(db);
+ {
+ final DirCacheBuilder b = treeB.builder();
+ final DirCacheBuilder o = treeO.builder();
+ final DirCacheBuilder t = treeT.builder();
+
+ b.add(makeEntry("d", FileMode.REGULAR_FILE));
+
+ o.add(makeEntry("d", FileMode.REGULAR_FILE, "o !"));
+
+ t.add(makeEntry("d/o", FileMode.REGULAR_FILE));
+
+ b.finish();
+ o.finish();
+ t.finish();
+ }
+
+ final ObjectWriter ow = new ObjectWriter(db);
+ final ObjectId b = commit(ow, treeB, new ObjectId[] {});
+ final ObjectId o = commit(ow, treeO, new ObjectId[] { b });
+ final ObjectId t = commit(ow, treeT, new ObjectId[] { b });
+
+ Merger ourMerger = MergeStrategy.SIMPLE_TWO_WAY_IN_CORE.newMerger(db);
+ boolean merge = ourMerger.merge(new ObjectId[] { o, t });
+ assertFalse(merge);
+ }
+
+ public void testTrivialTwoWay_rightDFconflict2() throws Exception {
+ final DirCache treeB = DirCache.read(db);
+ final DirCache treeO = DirCache.read(db);
+ final DirCache treeT = DirCache.read(db);
+ {
+ final DirCacheBuilder b = treeB.builder();
+ final DirCacheBuilder o = treeO.builder();
+ final DirCacheBuilder t = treeT.builder();
+
+ b.add(makeEntry("d", FileMode.REGULAR_FILE));
+
+ o.add(makeEntry("d/o", FileMode.REGULAR_FILE));
+
+ t.add(makeEntry("d", FileMode.REGULAR_FILE, "t !"));
+
+ b.finish();
+ o.finish();
+ t.finish();
+ }
+
+ final ObjectWriter ow = new ObjectWriter(db);
+ final ObjectId b = commit(ow, treeB, new ObjectId[] {});
+ final ObjectId o = commit(ow, treeO, new ObjectId[] { b });
+ final ObjectId t = commit(ow, treeT, new ObjectId[] { b });
+
+ Merger ourMerger = MergeStrategy.SIMPLE_TWO_WAY_IN_CORE.newMerger(db);
+ boolean merge = ourMerger.merge(new ObjectId[] { o, t });
+ assertFalse(merge);
+ }
+
private void assertCorrectId(final DirCache treeT, final TreeWalk tw) {
assertEquals(treeT.getEntry(tw.getPathString()).getObjectId(), tw
.getObjectId(0));
View
32 org.spearce.jgit/src/org/spearce/jgit/merge/StrategySimpleTwoWayInCore.java
@@ -115,7 +115,7 @@ protected boolean mergeImpl() throws IOException {
final int modeO = tw.getRawMode(T_OURS);
final int modeT = tw.getRawMode(T_THEIRS);
if (modeO == modeT && tw.idEqual(T_OURS, T_THEIRS)) {
- same();
+ add(T_OURS, DirCacheEntry.STAGE_0);
continue;
}
@@ -124,8 +124,24 @@ protected boolean mergeImpl() throws IOException {
add(T_THEIRS, DirCacheEntry.STAGE_0);
else if (modeB == modeT && tw.idEqual(T_BASE, T_THEIRS))
add(T_OURS, DirCacheEntry.STAGE_0);
- else {
- conflict();
+ else if (tw.isSubtree()) {
+ if (nonTree(modeB)) {
+ add(T_BASE, DirCacheEntry.STAGE_1);
+ hasConflict = true;
+ }
+ if (nonTree(modeO)) {
+ add(T_OURS, DirCacheEntry.STAGE_2);
+ hasConflict = true;
+ }
+ if (nonTree(modeT)) {
+ add(T_THEIRS, DirCacheEntry.STAGE_3);
+ hasConflict = true;
+ }
+ tw.enterSubtree();
+ } else {
+ add(T_BASE, DirCacheEntry.STAGE_1);
+ add(T_OURS, DirCacheEntry.STAGE_2);
+ add(T_THEIRS, DirCacheEntry.STAGE_3);
hasConflict = true;
}
}
@@ -143,14 +159,8 @@ else if (modeB == modeT && tw.idEqual(T_BASE, T_THEIRS))
}
}
- private void same() throws IOException {
- add(T_OURS, DirCacheEntry.STAGE_0);
- }
-
- private void conflict() throws IOException {
- add(T_BASE, DirCacheEntry.STAGE_1);
- add(T_OURS, DirCacheEntry.STAGE_2);
- add(T_THEIRS, DirCacheEntry.STAGE_3);
+ private static boolean nonTree(final int mode) {
+ return mode != 0 && !FileMode.TREE.equals(mode);
}
private void add(final int tree, final int stage) throws IOException {

0 comments on commit 093920a

Please sign in to comment.