ResolveMerger: Adding test cases for GITLINK merge

Add test cases which cover content-merge resolve logic.

Git clients try to agressively merge blobs by content, but GITLINK types
of entries can't be merged with each other or with blobs. This change
ensures all possible permutations which can trigger blob and GITLINK
content merge are covered.

Signed-off-by: Demetr Starshov <dstarshov@google.com>
Change-Id: I7e83a28a14d4d2f9e0ba2b1cffbf3224fb7f3fef
This commit is contained in:
Demetr Starshov 2020-08-11 17:57:10 -07:00
parent 86aa6deff4
commit e60ea7324f
2 changed files with 307 additions and 0 deletions

View File

@ -34,6 +34,7 @@
import org.eclipse.jgit.dircache.DirCacheCheckout;
import org.eclipse.jgit.dircache.DirCacheEntry;
import org.eclipse.jgit.internal.storage.file.FileRepository;
import org.eclipse.jgit.lib.AnyObjectId;
import org.eclipse.jgit.lib.Constants;
import org.eclipse.jgit.lib.FileMode;
import org.eclipse.jgit.lib.ObjectId;
@ -512,6 +513,21 @@ protected DirCacheEntry createEntry(final String path, final FileMode mode,
return entry;
}
/**
* Create <code>DirCacheEntry</code>
*
* @param path
* @param objectId
* @return the DirCacheEntry
*/
protected DirCacheEntry createGitLink(String path, AnyObjectId objectId) {
final DirCacheEntry entry = new DirCacheEntry(path,
DirCacheEntry.STAGE_0);
entry.setFileMode(FileMode.GITLINK);
entry.setObjectId(objectId);
return entry;
}
/**
* Assert files are equal
*

View File

@ -0,0 +1,291 @@
/*
* Copyright (c) 2020, Google LLC and others
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Distribution License v. 1.0 which is available at
* http://www.eclipse.org/org/documents/edl-v10.php.
*
* SPDX-License-Identifier: BSD-3-Clause
*/
package org.eclipse.jgit.merge;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import java.io.IOException;
import org.eclipse.jgit.annotations.Nullable;
import org.eclipse.jgit.dircache.DirCache;
import org.eclipse.jgit.dircache.DirCacheBuilder;
import org.eclipse.jgit.dircache.DirCacheEntry;
import org.eclipse.jgit.lib.CommitBuilder;
import org.eclipse.jgit.lib.FileMode;
import org.eclipse.jgit.lib.ObjectId;
import org.eclipse.jgit.lib.ObjectInserter;
import org.eclipse.jgit.lib.PersonIdent;
import org.eclipse.jgit.test.resources.SampleDataRepositoryTestCase;
import org.eclipse.jgit.treewalk.TreeWalk;
import org.junit.Ignore;
import org.junit.Test;
public class GitlinkMergeTest extends SampleDataRepositoryTestCase {
private static final String LINK_ID1 = "DEADBEEFDEADBEEFBABEDEADBEEFDEADBEEFBABE";
private static final String LINK_ID2 = "DEADDEADDEADDEADDEADDEADDEADDEADDEADDEAD";
private static final String LINK_ID3 = "BEEFBEEFBEEFBEEFBEEFBEEFBEEFBEEFBEEFBEEF";
private static final String SUBMODULE_PATH = "submodule.link";
@Test
public void testGitLinkMerging_UpdateUpdate() throws Exception {
testGitLink(LINK_ID1, LINK_ID2, LINK_ID3, newResolveMerger(), false);
}
@Test
public void testGitLinkMerging_bothAddedSameLink() throws Exception {
assertGitLinkValue(
testGitLink(null, LINK_ID2, LINK_ID2, newResolveMerger(), true),
LINK_ID2);
}
@Test
public void testGitLinkMerging_bothAddedDifferentLink() throws Exception {
testGitLink(null, LINK_ID2, LINK_ID3, newResolveMerger(), false);
}
@Test
public void testGitLinkMerging_AddNew_ignoreConflicts() throws Exception {
assertGitLinkValue(
testGitLink(null, null, LINK_ID3, newIgnoreConflictMerger(),
true),
LINK_ID3);
}
@Test
@Ignore("broken - doesn't ignore conflicts")
public void testGitLinkMerging_UpdateUpdate_ignoreConflicts()
throws Exception {
assertGitLinkValue(testGitLink(LINK_ID1, LINK_ID2, LINK_ID3,
newIgnoreConflictMerger(), true), LINK_ID2);
}
@Test
@Ignore("broken - doesn't ignore conflicts")
public void testGitLinkMerging_bothAddedSameLink_ignoreConflicts()
throws Exception {
assertGitLinkValue(testGitLink(null, LINK_ID2, LINK_ID2,
newIgnoreConflictMerger(), true), LINK_ID2);
}
@Test
@Ignore("broken - doesn't ignore conflicts")
public void testGitLinkMerging_bothAddedDifferentLink_ignoreConflicts()
throws Exception {
assertGitLinkValue(testGitLink(null, LINK_ID2, LINK_ID3,
newIgnoreConflictMerger(), true), LINK_ID2);
}
protected Merger testGitLink(@Nullable String baseLink,
@Nullable String oursLink, @Nullable String theirsLink,
Merger merger, boolean shouldMerge)
throws Exception {
DirCache treeB = db.readDirCache();
DirCache treeO = db.readDirCache();
DirCache treeT = db.readDirCache();
DirCacheBuilder bTreeBuilder = treeB.builder();
DirCacheBuilder oTreeBuilder = treeO.builder();
DirCacheBuilder tTreeBuilder = treeT.builder();
maybeAddLink(bTreeBuilder, baseLink);
maybeAddLink(oTreeBuilder, oursLink);
maybeAddLink(tTreeBuilder, theirsLink);
bTreeBuilder.finish();
oTreeBuilder.finish();
tTreeBuilder.finish();
ObjectInserter ow = db.newObjectInserter();
ObjectId b = commit(ow, treeB, new ObjectId[] {});
ObjectId o = commit(ow, treeO, new ObjectId[] { b });
ObjectId t = commit(ow, treeT, new ObjectId[] { b });
boolean merge = merger.merge(new ObjectId[] { o, t });
assertEquals(shouldMerge, merge);
return merger;
}
private Merger newResolveMerger() {
return MergeStrategy.RESOLVE.newMerger(db, true);
}
private Merger newIgnoreConflictMerger() {
return new ResolveMerger(db, true) {
@Override
protected boolean mergeImpl() throws IOException {
// emulate call with ignore conflicts.
return mergeTrees(mergeBase(), sourceTrees[0], sourceTrees[1],
true);
}
};
}
@Test
public void testGitLinkMerging_blobWithLink() throws Exception {
DirCache treeB = db.readDirCache();
DirCache treeO = db.readDirCache();
DirCache treeT = db.readDirCache();
DirCacheBuilder bTreeBuilder = treeB.builder();
DirCacheBuilder oTreeBuilder = treeO.builder();
DirCacheBuilder tTreeBuilder = treeT.builder();
bTreeBuilder.add(
createEntry(SUBMODULE_PATH, FileMode.REGULAR_FILE, "blob"));
oTreeBuilder.add(
createEntry(SUBMODULE_PATH, FileMode.REGULAR_FILE, "blob 2"));
maybeAddLink(tTreeBuilder, LINK_ID3);
bTreeBuilder.finish();
oTreeBuilder.finish();
tTreeBuilder.finish();
ObjectInserter ow = db.newObjectInserter();
ObjectId b = commit(ow, treeB, new ObjectId[] {});
ObjectId o = commit(ow, treeO, new ObjectId[] { b });
ObjectId t = commit(ow, treeT, new ObjectId[] { b });
Merger resolveMerger = MergeStrategy.RESOLVE.newMerger(db);
boolean merge = resolveMerger.merge(new ObjectId[] { o, t });
assertFalse(merge);
}
@Test
public void testGitLinkMerging_linkWithBlob() throws Exception {
DirCache treeB = db.readDirCache();
DirCache treeO = db.readDirCache();
DirCache treeT = db.readDirCache();
DirCacheBuilder bTreeBuilder = treeB.builder();
DirCacheBuilder oTreeBuilder = treeO.builder();
DirCacheBuilder tTreeBuilder = treeT.builder();
maybeAddLink(bTreeBuilder, LINK_ID1);
maybeAddLink(oTreeBuilder, LINK_ID2);
tTreeBuilder.add(
createEntry(SUBMODULE_PATH, FileMode.REGULAR_FILE, "blob 3"));
bTreeBuilder.finish();
oTreeBuilder.finish();
tTreeBuilder.finish();
ObjectInserter ow = db.newObjectInserter();
ObjectId b = commit(ow, treeB, new ObjectId[] {});
ObjectId o = commit(ow, treeO, new ObjectId[] { b });
ObjectId t = commit(ow, treeT, new ObjectId[] { b });
Merger resolveMerger = MergeStrategy.RESOLVE.newMerger(db);
boolean merge = resolveMerger.merge(new ObjectId[] { o, t });
assertFalse(merge);
}
@Test
public void testGitLinkMerging_linkWithLink() throws Exception {
DirCache treeB = db.readDirCache();
DirCache treeO = db.readDirCache();
DirCache treeT = db.readDirCache();
DirCacheBuilder bTreeBuilder = treeB.builder();
DirCacheBuilder oTreeBuilder = treeO.builder();
DirCacheBuilder tTreeBuilder = treeT.builder();
bTreeBuilder.add(
createEntry(SUBMODULE_PATH, FileMode.REGULAR_FILE, "blob"));
maybeAddLink(oTreeBuilder, LINK_ID2);
maybeAddLink(tTreeBuilder, LINK_ID3);
bTreeBuilder.finish();
oTreeBuilder.finish();
tTreeBuilder.finish();
ObjectInserter ow = db.newObjectInserter();
ObjectId b = commit(ow, treeB, new ObjectId[] {});
ObjectId o = commit(ow, treeO, new ObjectId[] { b });
ObjectId t = commit(ow, treeT, new ObjectId[] { b });
Merger resolveMerger = MergeStrategy.RESOLVE.newMerger(db);
boolean merge = resolveMerger.merge(new ObjectId[] { o, t });
assertFalse(merge);
}
@Test
@Ignore("broken - try to do content-merge with GITLINK")
public void testGitLinkMerging_blobWithBlobFromLink() throws Exception {
DirCache treeB = db.readDirCache();
DirCache treeO = db.readDirCache();
DirCache treeT = db.readDirCache();
DirCacheBuilder bTreeBuilder = treeB.builder();
DirCacheBuilder oTreeBuilder = treeO.builder();
DirCacheBuilder tTreeBuilder = treeT.builder();
maybeAddLink(bTreeBuilder, LINK_ID1);
oTreeBuilder.add(
createEntry(SUBMODULE_PATH, FileMode.REGULAR_FILE, "blob 2"));
tTreeBuilder.add(
createEntry(SUBMODULE_PATH, FileMode.REGULAR_FILE, "blob 3"));
bTreeBuilder.finish();
oTreeBuilder.finish();
tTreeBuilder.finish();
ObjectInserter ow = db.newObjectInserter();
ObjectId b = commit(ow, treeB, new ObjectId[] {});
ObjectId o = commit(ow, treeO, new ObjectId[] { b });
ObjectId t = commit(ow, treeT, new ObjectId[] { b });
Merger resolveMerger = MergeStrategy.RESOLVE.newMerger(db);
boolean merge = resolveMerger.merge(new ObjectId[] { o, t });
assertFalse(merge);
}
private void maybeAddLink(DirCacheBuilder builder,
@Nullable String linkId) {
if (linkId == null) {
return;
}
DirCacheEntry newLink = createGitLink(SUBMODULE_PATH,
ObjectId.fromString(linkId));
builder.add(newLink);
}
private void assertGitLinkValue(Merger resolveMerger, String expectedValue)
throws Exception {
try (TreeWalk tw = new TreeWalk(db)) {
tw.setRecursive(true);
tw.reset(resolveMerger.getResultTreeId());
assertTrue(tw.next());
assertEquals(SUBMODULE_PATH, tw.getPathString());
assertEquals(ObjectId.fromString(expectedValue), tw.getObjectId(0));
assertFalse(tw.next());
}
}
private static ObjectId commit(ObjectInserter odi, DirCache treeB,
ObjectId[] parentIds) throws Exception {
CommitBuilder c = new CommitBuilder();
c.setTreeId(treeB.writeTree(odi));
c.setAuthor(new PersonIdent("A U Thor", "a.u.thor", 1L, 0));
c.setCommitter(c.getAuthor());
c.setParentIds(parentIds);
c.setMessage("Tree " + c.getTreeId().name());
ObjectId id = odi.insert(c);
odi.flush();
return id;
}
}