Browse Source

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>
stable-0.10
Christian Halstrick 14 years ago
parent
commit
0e815fe8c5
  1. 42
      org.eclipse.jgit.test/tst/org/eclipse/jgit/api/MergeCommandTest.java
  2. 22
      org.eclipse.jgit/src/org/eclipse/jgit/merge/ResolveMerger.java

42
org.eclipse.jgit.test/tst/org/eclipse/jgit/api/MergeCommandTest.java

@ -365,6 +365,48 @@ public class MergeCommandTest extends RepositoryTestCase {
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);

22
org.eclipse.jgit/src/org/eclipse/jgit/merge/ResolveMerger.java

@ -93,7 +93,9 @@ public class ResolveMerger extends ThreeWayMerger {
/** 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 @@ public class ResolveMerger extends ThreeWayMerger {
private void checkout() throws NoWorkTreeException, IOException {
for (Map.Entry<String, DirCacheEntry> entry : toBeCheckedOut.entrySet()) {
File f = new File(db.getWorkTree(), entry.getKey());
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 @@ public class ResolveMerger extends ThreeWayMerger {
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 (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…
Cancel
Save