diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/MergeCommandTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/MergeCommandTest.java index 7efe7c948..dbf3ba015 100644 --- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/MergeCommandTest.java +++ b/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); diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/merge/ResolveMerger.java b/org.eclipse.jgit/src/org/eclipse/jgit/merge/ResolveMerger.java index a8ab433fb..1b6ec23ba 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/merge/ResolveMerger.java +++ b/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 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 @@ 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 (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()) {