Browse Source

Fixed Merge Algorithm regarding concurrent file creations

When in OURS and THEIRS a new file is created we want a conflict
when the two contents differ. If on two branches the same file
with the same content is created this should not be a conflict.
But: the current merge algorithm is throwing NPEs in this case.
Fix this by choosing an empty RawText as common base if the
base is empty.

Change-Id: I21cb23f852965b82fb82ccd66ec961c7edb3ac3d
Signed-off-by: Christian Halstrick <christian.halstrick@sap.com>
stable-0.10
Christian Halstrick 14 years ago
parent
commit
deabacc420
  1. 79
      org.eclipse.jgit.test/tst/org/eclipse/jgit/api/MergeCommandTest.java
  2. 3
      org.eclipse.jgit/src/org/eclipse/jgit/diff/RawText.java
  3. 8
      org.eclipse.jgit/src/org/eclipse/jgit/merge/ResolveMerger.java

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

@ -247,6 +247,57 @@ public class MergeCommandTest extends RepositoryTestCase {
assertEquals(RepositoryState.MERGING, db.getRepositoryState()); assertEquals(RepositoryState.MERGING, db.getRepositoryState());
} }
public void testMultipleCreations() throws Exception {
Git git = new Git(db);
writeTrashFile("a", "1\na\n3\n");
git.add().addFilepattern("a").call();
RevCommit initialCommit = git.commit().setMessage("initial").call();
createBranch(initialCommit, "refs/heads/side");
checkoutBranch("refs/heads/side");
writeTrashFile("b", "1\nb(side)\n3\n");
git.add().addFilepattern("b").call();
RevCommit secondCommit = git.commit().setMessage("side").call();
checkoutBranch("refs/heads/master");
writeTrashFile("b", "1\nb(main)\n3\n");
git.add().addFilepattern("b").call();
git.commit().setMessage("main").call();
MergeResult result = git.merge().include(secondCommit.getId())
.setStrategy(MergeStrategy.RESOLVE).call();
assertEquals(MergeStatus.CONFLICTING, result.getMergeStatus());
}
public void testMultipleCreationsSameContent() throws Exception {
Git git = new Git(db);
writeTrashFile("a", "1\na\n3\n");
git.add().addFilepattern("a").call();
RevCommit initialCommit = git.commit().setMessage("initial").call();
createBranch(initialCommit, "refs/heads/side");
checkoutBranch("refs/heads/side");
writeTrashFile("b", "1\nb(1)\n3\n");
git.add().addFilepattern("b").call();
RevCommit secondCommit = git.commit().setMessage("side").call();
checkoutBranch("refs/heads/master");
writeTrashFile("b", "1\nb(1)\n3\n");
git.add().addFilepattern("b").call();
git.commit().setMessage("main").call();
MergeResult result = git.merge().include(secondCommit.getId())
.setStrategy(MergeStrategy.RESOLVE).call();
assertEquals(MergeStatus.MERGED, result.getMergeStatus());
assertEquals("1\nb(1)\n3\n", read(new File(db.getWorkTree(), "b")));
}
public void testSuccessfulContentMerge() throws Exception { public void testSuccessfulContentMerge() throws Exception {
Git git = new Git(db); Git git = new Git(db);
@ -415,6 +466,34 @@ public class MergeCommandTest extends RepositoryTestCase {
read(new File(db.getWorkTree(), "c/c/c"))); read(new File(db.getWorkTree(), "c/c/c")));
} }
public void testMultipleDeletions() throws Exception {
Git git = new Git(db);
writeTrashFile("a", "1\na\n3\n");
git.add().addFilepattern("a").call();
RevCommit initialCommit = git.commit().setMessage("initial").call();
createBranch(initialCommit, "refs/heads/side");
checkoutBranch("refs/heads/side");
assertTrue(new File(db.getWorkTree(), "a").delete());
git.add().addFilepattern("a").setUpdate(true).call();
RevCommit secondCommit = git.commit().setMessage("side").call();
assertFalse(new File(db.getWorkTree(), "a").exists());
checkoutBranch("refs/heads/master");
assertTrue(new File(db.getWorkTree(), "a").exists());
assertTrue(new File(db.getWorkTree(), "a").delete());
git.add().addFilepattern("a").setUpdate(true).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.MERGED, result.getMergeStatus());
}
public void testDeletionAndConflict() throws Exception { public void testDeletionAndConflict() throws Exception {
Git git = new Git(db); Git git = new Git(db);

3
org.eclipse.jgit/src/org/eclipse/jgit/diff/RawText.java

@ -66,6 +66,9 @@ import org.eclipse.jgit.util.RawParseUtils;
* they are converting from "line number" to "element index". * they are converting from "line number" to "element index".
*/ */
public class RawText extends Sequence { public class RawText extends Sequence {
/** A Rawtext of length 0 */
public static final RawText EMPTY_TEXT = new RawText(new byte[0]);
/** Number of bytes to check for heuristics in {@link #isBinary(byte[])} */ /** Number of bytes to check for heuristics in {@link #isBinary(byte[])} */
private static final int FIRST_FEW_BYTES = 8000; private static final int FIRST_FEW_BYTES = 8000;

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

@ -467,10 +467,12 @@ public class ResolveMerger extends ThreeWayMerger {
throws FileNotFoundException, IllegalStateException, IOException { throws FileNotFoundException, IllegalStateException, IOException {
MergeFormatter fmt = new MergeFormatter(); MergeFormatter fmt = new MergeFormatter();
RawText baseText = base == null ? RawText.EMPTY_TEXT : getRawText(
base.getEntryObjectId(), db);
// do the merge // do the merge
MergeResult<RawText> result = mergeAlgorithm.merge( MergeResult<RawText> result = MergeAlgorithm.merge(
RawTextComparator.DEFAULT, RawTextComparator.DEFAULT, baseText,
getRawText(base.getEntryObjectId(), db),
getRawText(ours.getEntryObjectId(), db), getRawText(ours.getEntryObjectId(), db),
getRawText(theirs.getEntryObjectId(), db)); getRawText(theirs.getEntryObjectId(), db));

Loading…
Cancel
Save