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:
parent
86aa6deff4
commit
e60ea7324f
|
@ -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
|
||||
*
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue