Fixed ResolveMerger regarding handling of deletions
There was a bug in ResolveMerger which is one reason for bug 328841. If a merge was failing because of conflicts deletions where not handled correctly. Files which have to be deleted (because there was a non-conflicting deletion coming in from THEIRS) are not deleted. In the non-conflicting case we also forgot to delete the file but in this case we explicitly checkout in the end these files get deleted during that checkout. This is fixed by handling incoming deletions explicitly. Bug: 328841 Change-Id: I7f4c94ab54138e1b2f3fcdf34fb803d68e209ad0 Signed-off-by: Christian Halstrick <christian.halstrick@sap.com>
This commit is contained in:
parent
d1e8e97316
commit
0e815fe8c5
|
@ -365,6 +365,48 @@ public void testSingleDeletion() throws Exception {
|
|||
read(new File(db.getWorkTree(), "c/c/c")));
|
||||
}
|
||||
|
||||
public void testDeletionAndConflict() throws Exception {
|
||||
Git git = new Git(db);
|
||||
|
||||
writeTrashFile("a", "1\na\n3\n");
|
||||
writeTrashFile("b", "1\nb\n3\n");
|
||||
writeTrashFile("d", "1\nd\n3\n");
|
||||
writeTrashFile("c/c/c", "1\nc\n3\n");
|
||||
git.add().addFilepattern("a").addFilepattern("b")
|
||||
.addFilepattern("c/c/c").addFilepattern("d").call();
|
||||
RevCommit initialCommit = git.commit().setMessage("initial").call();
|
||||
|
||||
createBranch(initialCommit, "refs/heads/side");
|
||||
checkoutBranch("refs/heads/side");
|
||||
|
||||
assertTrue(new File(db.getWorkTree(), "b").delete());
|
||||
writeTrashFile("a", "1\na\n3(side)\n");
|
||||
git.add().addFilepattern("b").setUpdate(true).call();
|
||||
git.add().addFilepattern("a").setUpdate(true).call();
|
||||
RevCommit secondCommit = git.commit().setMessage("side").call();
|
||||
|
||||
assertFalse(new File(db.getWorkTree(), "b").exists());
|
||||
checkoutBranch("refs/heads/master");
|
||||
assertTrue(new File(db.getWorkTree(), "b").exists());
|
||||
|
||||
writeTrashFile("a", "1\na\n3(main)\n");
|
||||
writeTrashFile("c/c/c", "1\nc(main)\n3\n");
|
||||
git.add().addFilepattern("a").addFilepattern("c/c/c").call();
|
||||
git.commit().setMessage("main").call();
|
||||
|
||||
// We are merging a deletion into our branch
|
||||
MergeResult result = git.merge().include(secondCommit.getId())
|
||||
.setStrategy(MergeStrategy.RESOLVE).call();
|
||||
assertEquals(MergeStatus.CONFLICTING, result.getMergeStatus());
|
||||
|
||||
assertEquals(
|
||||
"1\na\n<<<<<<< HEAD\n3(main)\n=======\n3(side)\n>>>>>>> 54ffed45d62d252715fc20e41da92d44c48fb0ff\n",
|
||||
read(new File(db.getWorkTree(), "a")));
|
||||
assertFalse(new File(db.getWorkTree(), "b").exists());
|
||||
assertEquals("1\nc(main)\n3\n",
|
||||
read(new File(db.getWorkTree(), "c/c/c")));
|
||||
}
|
||||
|
||||
public void testMergeFailingWithDirtyWorkingTree() throws Exception {
|
||||
Git git = new Git(db);
|
||||
|
||||
|
|
|
@ -93,7 +93,9 @@ public enum MergeFailureReason {
|
|||
/** the merge failed because of a dirty index */
|
||||
DIRTY_INDEX,
|
||||
/** the merge failed because of a dirty workingtree */
|
||||
DIRTY_WORKTREE
|
||||
DIRTY_WORKTREE,
|
||||
/** the merge failed because of a file could not be deleted */
|
||||
COULD_NOT_DELETE
|
||||
}
|
||||
|
||||
private NameConflictTreeWalk tw;
|
||||
|
@ -229,10 +231,16 @@ protected boolean mergeImpl() throws IOException {
|
|||
private void checkout() throws NoWorkTreeException, IOException {
|
||||
for (Map.Entry<String, DirCacheEntry> entry : toBeCheckedOut.entrySet()) {
|
||||
File f = new File(db.getWorkTree(), entry.getKey());
|
||||
createDir(f.getParentFile());
|
||||
DirCacheCheckout.checkoutEntry(db,
|
||||
f,
|
||||
entry.getValue(), true);
|
||||
if (entry.getValue() != null) {
|
||||
createDir(f.getParentFile());
|
||||
DirCacheCheckout.checkoutEntry(db,
|
||||
f,
|
||||
entry.getValue(), true);
|
||||
} else {
|
||||
if (!f.delete())
|
||||
failingPathes.put(entry.getKey(),
|
||||
MergeFailureReason.COULD_NOT_DELETE);
|
||||
}
|
||||
modifiedFiles.add(entry.getKey());
|
||||
}
|
||||
}
|
||||
|
@ -373,13 +381,21 @@ private boolean processEntry(CanonicalTreeParser base,
|
|||
return true;
|
||||
}
|
||||
|
||||
if (nonTree(modeT) && modeB == modeO && tw.idEqual(T_BASE, T_OURS)) {
|
||||
if (modeB == modeO && tw.idEqual(T_BASE, T_OURS)) {
|
||||
// OURS was not changed compared to base. All changes must be in
|
||||
// THEIRS. Choose THEIRS.
|
||||
DirCacheEntry e=add(tw.getRawPath(), theirs, DirCacheEntry.STAGE_0);
|
||||
if (e!=null)
|
||||
toBeCheckedOut.put(tw.getPathString(), e);
|
||||
return true;
|
||||
if (nonTree(modeT)) {
|
||||
DirCacheEntry e = add(tw.getRawPath(), theirs,
|
||||
DirCacheEntry.STAGE_0);
|
||||
if (e != null)
|
||||
toBeCheckedOut.put(tw.getPathString(), e);
|
||||
return true;
|
||||
} else if (modeT == 0) {
|
||||
// we want THEIRS ... but THEIRS contains the deletion of the
|
||||
// file
|
||||
toBeCheckedOut.put(tw.getPathString(), null);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
if (tw.isSubtree()) {
|
||||
|
|
Loading…
Reference in New Issue