Browse Source

Fix ResolveMerger when files should be replaced by folders

When during Merge for a certain path OURS & BASE contains a file and
THEIRS contains a folder there was a bug in JGit leading to unnecessary
conflicts. This commit fixes it and adds a test for this situation.
 
Bug: 472693
Change-Id: I71fac5a6a2ef926c01adc266c6f9b3275e870129
Also-by: Clemens Buchacher <drizzd@aon.at>
Signed-off-by: Matthias Sohn <matthias.sohn@sap.com>
stable-4.1
Christian Halstrick 9 years ago committed by Matthias Sohn
parent
commit
1196dd0643
  1. 74
      org.eclipse.jgit.test/tst/org/eclipse/jgit/merge/ResolveMergerTest.java
  2. 14
      org.eclipse.jgit/src/org/eclipse/jgit/merge/ResolveMerger.java

74
org.eclipse.jgit.test/tst/org/eclipse/jgit/merge/ResolveMergerTest.java

@ -226,6 +226,80 @@ public class ResolveMergerTest extends RepositoryTestCase {
indexState(CONTENT)); 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 * An existing directory without tracked content should not prevent merging
* a file with that name. * a file with that name.

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

@ -525,9 +525,10 @@ 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 // THEIRS was not changed compared to BASE. All changes must be in
// OURS. OURS is chosen. We can keep the existing entry. // OURS. OURS is chosen. We can keep the existing entry.
if (ourDce != null)
keep(ourDce); keep(ourDce);
// no checkout needed! // no checkout needed!
return true; return true;
@ -549,11 +550,12 @@ public class ResolveMerger extends ThreeWayMerger {
if (e != null) if (e != null)
toBeCheckedOut.put(tw.getPathString(), e); toBeCheckedOut.put(tw.getPathString(), e);
return true; return true;
} else if (modeT == 0 && modeB != 0) { } else {
// we want THEIRS ... but THEIRS contains the deletion of the // we want THEIRS ... but THEIRS contains a folder or the
// file. Also, do not complain if the file is already deleted // deletion of the path. Delete what's in the workingtree (the
// locally. This complements the test in isWorktreeDirty() for // workingtree is clean) but do not complain if the file is
// the same case. // already deleted locally. This complements the test in
// isWorktreeDirty() for the same case.
if (tw.getTreeCount() > T_FILE && tw.getRawMode(T_FILE) == 0) if (tw.getTreeCount() > T_FILE && tw.getRawMode(T_FILE) == 0)
return true; return true;
toBeDeleted.add(tw.getPathString()); toBeDeleted.add(tw.getPathString());

Loading…
Cancel
Save