Merge "Fix crashes on rare combination of file names"
This commit is contained in:
commit
1d5a6c77a6
|
@ -1035,6 +1035,32 @@ public void fileBecomesDir_noTree(MergeStrategy strategy)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This is a high-level test for https://bugs.eclipse.org/bugs/show_bug.cgi?id=535919
|
||||||
|
*
|
||||||
|
* The actual fix was made in {@link org.eclipse.jgit.treewalk.NameConflictTreeWalk}
|
||||||
|
* and tested in {@link org.eclipse.jgit.treewalk.NameConflictTreeWalkTest#tesdDF_LastItemsInTreeHasDFConflictAndSpecialNames}.
|
||||||
|
*/
|
||||||
|
@Theory
|
||||||
|
public void checkMergeDoesntCrashWithSpecialFileNames(
|
||||||
|
MergeStrategy strategy) throws Exception {
|
||||||
|
Git git = Git.wrap(db);
|
||||||
|
|
||||||
|
writeTrashFile("subtree", "");
|
||||||
|
writeTrashFile("subtree-0", "");
|
||||||
|
git.add().addFilepattern("subtree").call();
|
||||||
|
git.add().addFilepattern("subtree-0").call();
|
||||||
|
RevCommit toMerge = git.commit().setMessage("commit-1").call();
|
||||||
|
|
||||||
|
git.rm().addFilepattern("subtree").call();
|
||||||
|
writeTrashFile("subtree/file", "");
|
||||||
|
git.add().addFilepattern("subtree").call();
|
||||||
|
RevCommit mergeTip = git.commit().setMessage("commit2").call();
|
||||||
|
|
||||||
|
ResolveMerger merger = (ResolveMerger) strategy.newMerger(db, false);
|
||||||
|
assertTrue(merger.merge(mergeTip, toMerge));
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Merging after criss-cross merges. In this case we merge together two
|
* Merging after criss-cross merges. In this case we merge together two
|
||||||
* commits which have two equally good common ancestors
|
* commits which have two equally good common ancestors
|
||||||
|
|
|
@ -137,6 +137,177 @@ public void testDF_GapByOne() throws Exception {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The test reproduces https://bugs.eclipse.org/bugs/show_bug.cgi?id=535919.
|
||||||
|
*/
|
||||||
|
@Test
|
||||||
|
public void tesdDF_LastItemsInTreeHasDFConflictAndSpecialNames()
|
||||||
|
throws Exception {
|
||||||
|
|
||||||
|
final DirCache tree0 = db.readDirCache();
|
||||||
|
final DirCache tree1 = db.readDirCache();
|
||||||
|
|
||||||
|
final DirCacheBuilder b0 = tree0.builder();
|
||||||
|
final DirCacheBuilder b1 = tree1.builder();
|
||||||
|
// The tree0 has the following order in git:
|
||||||
|
// subtree, subtree-0
|
||||||
|
b0.add(createEntry("subtree", REGULAR_FILE));
|
||||||
|
b0.add(createEntry("subtree-0", REGULAR_FILE));
|
||||||
|
// The tree1 has the following order in git:
|
||||||
|
// subtree-0, subtree/file
|
||||||
|
b1.add(createEntry("subtree/file", REGULAR_FILE));
|
||||||
|
b1.add(createEntry("subtree-0", REGULAR_FILE));
|
||||||
|
|
||||||
|
b0.finish();
|
||||||
|
b1.finish();
|
||||||
|
|
||||||
|
try (NameConflictTreeWalk tw = new NameConflictTreeWalk(db)) {
|
||||||
|
tw.addTree(new DirCacheIterator(tree0));
|
||||||
|
tw.addTree(new DirCacheIterator(tree1));
|
||||||
|
|
||||||
|
assertModes("subtree", REGULAR_FILE, TREE, tw);
|
||||||
|
assertTrue(tw.isSubtree());
|
||||||
|
assertTrue(tw.isDirectoryFileConflict());
|
||||||
|
tw.enterSubtree();
|
||||||
|
assertModes("subtree/file", MISSING, REGULAR_FILE, tw);
|
||||||
|
assertFalse(tw.isSubtree());
|
||||||
|
// isDirectoryFileConflict is true, because the conflict is detected
|
||||||
|
// on parent.
|
||||||
|
assertTrue(tw.isDirectoryFileConflict());
|
||||||
|
assertModes("subtree-0", REGULAR_FILE, REGULAR_FILE, tw);
|
||||||
|
assertFalse(tw.isSubtree());
|
||||||
|
assertFalse(tw.isDirectoryFileConflict());
|
||||||
|
assertFalse(tw.next());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The test reproduces https://bugs.eclipse.org/bugs/show_bug.cgi?id=535919.
|
||||||
|
*/
|
||||||
|
@Test
|
||||||
|
public void tesdDF_LastItemsInTreeHasDFConflictAndSpecialNames2()
|
||||||
|
throws Exception {
|
||||||
|
|
||||||
|
final DirCache tree0 = db.readDirCache();
|
||||||
|
final DirCache tree1 = db.readDirCache();
|
||||||
|
|
||||||
|
final DirCacheBuilder b0 = tree0.builder();
|
||||||
|
final DirCacheBuilder b1 = tree1.builder();
|
||||||
|
// The tree0 has the following order in git:
|
||||||
|
// subtree-0, subtree/file
|
||||||
|
b0.add(createEntry("subtree/file", REGULAR_FILE));
|
||||||
|
b0.add(createEntry("subtree-0", REGULAR_FILE));
|
||||||
|
// The tree1 has the following order in git:
|
||||||
|
// subtree, subtree-0
|
||||||
|
b1.add(createEntry("subtree", REGULAR_FILE));
|
||||||
|
b1.add(createEntry("subtree-0", REGULAR_FILE));
|
||||||
|
|
||||||
|
b0.finish();
|
||||||
|
b1.finish();
|
||||||
|
|
||||||
|
try (NameConflictTreeWalk tw = new NameConflictTreeWalk(db)) {
|
||||||
|
tw.addTree(new DirCacheIterator(tree0));
|
||||||
|
tw.addTree(new DirCacheIterator(tree1));
|
||||||
|
|
||||||
|
assertModes("subtree", TREE, REGULAR_FILE, tw);
|
||||||
|
assertTrue(tw.isSubtree());
|
||||||
|
assertTrue(tw.isDirectoryFileConflict());
|
||||||
|
tw.enterSubtree();
|
||||||
|
assertModes("subtree/file", REGULAR_FILE, MISSING, tw);
|
||||||
|
assertFalse(tw.isSubtree());
|
||||||
|
// isDirectoryFileConflict is true, because the conflict is detected
|
||||||
|
// on parent.
|
||||||
|
assertTrue(tw.isDirectoryFileConflict());
|
||||||
|
assertModes("subtree-0", REGULAR_FILE, REGULAR_FILE, tw);
|
||||||
|
assertFalse(tw.isSubtree());
|
||||||
|
assertFalse(tw.isDirectoryFileConflict());
|
||||||
|
assertFalse(tw.next());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void tesdDF_NonLastItemsInTreeHasDFConflictAndSpecialNames()
|
||||||
|
throws Exception {
|
||||||
|
final DirCache tree0 = db.readDirCache();
|
||||||
|
final DirCache tree1 = db.readDirCache();
|
||||||
|
|
||||||
|
final DirCacheBuilder b0 = tree0.builder();
|
||||||
|
final DirCacheBuilder b1 = tree1.builder();
|
||||||
|
b0.add(createEntry("subtree", REGULAR_FILE));
|
||||||
|
b0.add(createEntry("subtree-0", REGULAR_FILE));
|
||||||
|
b0.add(createEntry("x", REGULAR_FILE));
|
||||||
|
|
||||||
|
b1.add(createEntry("subtree/file", REGULAR_FILE));
|
||||||
|
b1.add(createEntry("subtree-0", REGULAR_FILE));
|
||||||
|
b1.add(createEntry("x", REGULAR_FILE));
|
||||||
|
|
||||||
|
b0.finish();
|
||||||
|
b1.finish();
|
||||||
|
|
||||||
|
try (NameConflictTreeWalk tw = new NameConflictTreeWalk(db)) {
|
||||||
|
tw.addTree(new DirCacheIterator(tree0));
|
||||||
|
tw.addTree(new DirCacheIterator(tree1));
|
||||||
|
|
||||||
|
assertModes("subtree", REGULAR_FILE, TREE, tw);
|
||||||
|
assertTrue(tw.isSubtree());
|
||||||
|
assertTrue(tw.isDirectoryFileConflict());
|
||||||
|
tw.enterSubtree();
|
||||||
|
assertModes("subtree/file", MISSING, REGULAR_FILE, tw);
|
||||||
|
assertFalse(tw.isSubtree());
|
||||||
|
// isDirectoryFileConflict is true, because the conflict is detected
|
||||||
|
// on parent.
|
||||||
|
// see JavaDoc for isDirectoryFileConflict for details
|
||||||
|
assertTrue(tw.isDirectoryFileConflict());
|
||||||
|
assertModes("subtree-0", REGULAR_FILE, REGULAR_FILE, tw);
|
||||||
|
assertFalse(tw.isSubtree());
|
||||||
|
assertFalse(tw.isDirectoryFileConflict());
|
||||||
|
assertModes("x", REGULAR_FILE, REGULAR_FILE, tw);
|
||||||
|
assertFalse(tw.isSubtree());
|
||||||
|
assertFalse(tw.isDirectoryFileConflict());
|
||||||
|
assertFalse(tw.next());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void tesdDF_NoSpecialNames() throws Exception {
|
||||||
|
final DirCache tree0 = db.readDirCache();
|
||||||
|
final DirCache tree1 = db.readDirCache();
|
||||||
|
|
||||||
|
final DirCacheBuilder b0 = tree0.builder();
|
||||||
|
final DirCacheBuilder b1 = tree1.builder();
|
||||||
|
// In this test both trees (tree0 and tree1) have exactly the same order
|
||||||
|
// of entries:
|
||||||
|
// subtree, xubtree-0
|
||||||
|
b0.add(createEntry("subtree", REGULAR_FILE));
|
||||||
|
b0.add(createEntry("xubtree-0", REGULAR_FILE));
|
||||||
|
|
||||||
|
b1.add(createEntry("subtree/file", REGULAR_FILE));
|
||||||
|
b1.add(createEntry("xubtree-0", REGULAR_FILE));
|
||||||
|
|
||||||
|
b0.finish();
|
||||||
|
b1.finish();
|
||||||
|
|
||||||
|
try (NameConflictTreeWalk tw = new NameConflictTreeWalk(db)) {
|
||||||
|
tw.addTree(new DirCacheIterator(tree0));
|
||||||
|
tw.addTree(new DirCacheIterator(tree1));
|
||||||
|
|
||||||
|
assertModes("subtree", REGULAR_FILE, TREE, tw);
|
||||||
|
assertTrue(tw.isSubtree());
|
||||||
|
assertTrue(tw.isDirectoryFileConflict());
|
||||||
|
tw.enterSubtree();
|
||||||
|
assertModes("subtree/file", MISSING, REGULAR_FILE, tw);
|
||||||
|
assertFalse(tw.isSubtree());
|
||||||
|
// isDirectoryFileConflict is true, because the conflict is detected
|
||||||
|
// on parent.
|
||||||
|
// see JavaDoc for isDirectoryFileConflict for details
|
||||||
|
assertTrue(tw.isDirectoryFileConflict());
|
||||||
|
assertModes("xubtree-0", REGULAR_FILE, REGULAR_FILE, tw);
|
||||||
|
assertFalse(tw.isSubtree());
|
||||||
|
assertFalse(tw.isDirectoryFileConflict());
|
||||||
|
assertFalse(tw.next());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testDF_specialFileNames() throws Exception {
|
public void testDF_specialFileNames() throws Exception {
|
||||||
final DirCache tree0 = db.readDirCache();
|
final DirCache tree0 = db.readDirCache();
|
||||||
|
|
|
@ -56,6 +56,13 @@
|
||||||
public class NameConflictTreeWalk extends TreeWalk {
|
public class NameConflictTreeWalk extends TreeWalk {
|
||||||
private static final int TREE_MODE = FileMode.TREE.getBits();
|
private static final int TREE_MODE = FileMode.TREE.getBits();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* True if all {@link #trees} point to entries with equal names.
|
||||||
|
*
|
||||||
|
* If at least one tree iterator point to a different name or
|
||||||
|
* reached end of the tree, the value is false.
|
||||||
|
* Note: if all iterators reached end of trees, the value is true.
|
||||||
|
*/
|
||||||
private boolean allTreesNamesMatchFastMinRef;
|
private boolean allTreesNamesMatchFastMinRef;
|
||||||
|
|
||||||
private AbstractTreeIterator dfConflict;
|
private AbstractTreeIterator dfConflict;
|
||||||
|
@ -125,13 +132,20 @@ private AbstractTreeIterator fastMin() {
|
||||||
return trees[trees.length - 1];
|
return trees[trees.length - 1];
|
||||||
}
|
}
|
||||||
AbstractTreeIterator minRef = trees[i];
|
AbstractTreeIterator minRef = trees[i];
|
||||||
allTreesNamesMatchFastMinRef = true;
|
// if i > 0 then we already know that only some trees reached the end
|
||||||
|
// (but not all), so it is impossible that ALL trees points to the
|
||||||
|
// minRef entry.
|
||||||
|
// Only if i == 0 it is still possible that all trees points to the same
|
||||||
|
// minRef entry.
|
||||||
|
allTreesNamesMatchFastMinRef = i == 0;
|
||||||
boolean hasConflict = false;
|
boolean hasConflict = false;
|
||||||
minRef.matches = minRef;
|
minRef.matches = minRef;
|
||||||
while (++i < trees.length) {
|
while (++i < trees.length) {
|
||||||
final AbstractTreeIterator t = trees[i];
|
final AbstractTreeIterator t = trees[i];
|
||||||
if (t.eof())
|
if (t.eof()) {
|
||||||
|
allTreesNamesMatchFastMinRef = false;
|
||||||
continue;
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
final int cmp = t.pathCompare(minRef);
|
final int cmp = t.pathCompare(minRef);
|
||||||
if (cmp < 0) {
|
if (cmp < 0) {
|
||||||
|
@ -152,8 +166,9 @@ && nameEqual(minRef, t)) {
|
||||||
// Exact name/mode match is best.
|
// Exact name/mode match is best.
|
||||||
//
|
//
|
||||||
t.matches = minRef;
|
t.matches = minRef;
|
||||||
} else if (allTreesNamesMatchFastMinRef && isTree(t) && !isTree(minRef)
|
} else if (allTreesNamesMatchFastMinRef && isTree(t)
|
||||||
&& !isGitlink(minRef) && nameEqual(t, minRef)) {
|
&& !isTree(minRef) && !isGitlink(minRef)
|
||||||
|
&& nameEqual(t, minRef)) {
|
||||||
// The minimum is a file (non-tree) but the next entry
|
// The minimum is a file (non-tree) but the next entry
|
||||||
// of this iterator is a tree whose name matches our file.
|
// of this iterator is a tree whose name matches our file.
|
||||||
// This is a classic D/F conflict and commonly occurs like
|
// This is a classic D/F conflict and commonly occurs like
|
||||||
|
|
Loading…
Reference in New Issue