diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/merge/ResolveMergerTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/merge/ResolveMergerTest.java index 478a93b77..cd6a4bea2 100644 --- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/merge/ResolveMergerTest.java +++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/merge/ResolveMergerTest.java @@ -226,6 +226,80 @@ public class ResolveMergerTest extends RepositoryTestCase { indexState(CONTENT)); } + /** + * A tracked file is replaced by a folder in THEIRS. + * + * @param strategy + * @throws Exception + */ + @Theory + public void checkFileReplacedByFolderInTheirs(MergeStrategy strategy) + throws Exception { + Git git = Git.wrap(db); + + writeTrashFile("sub", "file"); + git.add().addFilepattern("sub").call(); + RevCommit first = git.commit().setMessage("initial").call(); + + git.checkout().setCreateBranch(true).setStartPoint(first) + .setName("side").call(); + + git.rm().addFilepattern("sub").call(); + writeTrashFile("sub/file", "subfile"); + git.add().addFilepattern("sub/file").call(); + RevCommit masterCommit = git.commit().setMessage("file -> folder") + .call(); + + git.checkout().setName("master").call(); + writeTrashFile("noop", "other"); + git.add().addFilepattern("noop").call(); + git.commit().setAll(true).setMessage("noop").call(); + + MergeResult mergeRes = git.merge().setStrategy(strategy) + .include(masterCommit).call(); + assertEquals(MergeStatus.MERGED, mergeRes.getMergeStatus()); + assertEquals( + "[noop, mode:100644, content:other][sub/file, mode:100644, content:subfile]", + indexState(CONTENT)); + } + + /** + * A tracked file is replaced by a folder in OURS. + * + * @param strategy + * @throws Exception + */ + @Theory + public void checkFileReplacedByFolderInOurs(MergeStrategy strategy) + throws Exception { + Git git = Git.wrap(db); + + writeTrashFile("sub", "file"); + git.add().addFilepattern("sub").call(); + RevCommit first = git.commit().setMessage("initial").call(); + + git.checkout().setCreateBranch(true).setStartPoint(first) + .setName("side").call(); + writeTrashFile("noop", "other"); + git.add().addFilepattern("noop").call(); + RevCommit sideCommit = git.commit().setAll(true).setMessage("noop") + .call(); + + git.checkout().setName("master").call(); + git.rm().addFilepattern("sub").call(); + writeTrashFile("sub/file", "subfile"); + git.add().addFilepattern("sub/file").call(); + git.commit().setMessage("file -> folder") + .call(); + + MergeResult mergeRes = git.merge().setStrategy(strategy) + .include(sideCommit).call(); + assertEquals(MergeStatus.MERGED, mergeRes.getMergeStatus()); + assertEquals( + "[noop, mode:100644, content:other][sub/file, mode:100644, content:subfile]", + indexState(CONTENT)); + } + /** * An existing directory without tracked content should not prevent merging * a file with that name. 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 3654ffd1e..8a6343c3c 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/merge/ResolveMerger.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/merge/ResolveMerger.java @@ -525,10 +525,11 @@ public class ResolveMerger extends ThreeWayMerger { } } - if (nonTree(modeO) && modeB == modeT && tw.idEqual(T_BASE, T_THEIRS)) { + if (modeB == modeT && tw.idEqual(T_BASE, T_THEIRS)) { // THEIRS was not changed compared to BASE. All changes must be in // OURS. OURS is chosen. We can keep the existing entry. - keep(ourDce); + if (ourDce != null) + keep(ourDce); // no checkout needed! return true; } @@ -549,11 +550,12 @@ public class ResolveMerger extends ThreeWayMerger { if (e != null) toBeCheckedOut.put(tw.getPathString(), e); return true; - } else if (modeT == 0 && modeB != 0) { - // we want THEIRS ... but THEIRS contains the deletion of the - // file. Also, do not complain if the file is already deleted - // locally. This complements the test in isWorktreeDirty() for - // the same case. + } else { + // we want THEIRS ... but THEIRS contains a folder or the + // deletion of the path. Delete what's in the workingtree (the + // workingtree is clean) but do not complain if the file is + // already deleted locally. This complements the test in + // isWorktreeDirty() for the same case. if (tw.getTreeCount() > T_FILE && tw.getRawMode(T_FILE) == 0) return true; toBeDeleted.add(tw.getPathString());