diff options
author | Matthias Sohn <matthias.sohn@sap.com> | 2024-04-26 21:51:27 +0200 |
---|---|---|
committer | Matthias Sohn <matthias.sohn@sap.com> | 2024-04-26 23:46:29 +0200 |
commit | c8844571b3a483e3cc5f77d18b4e972ad5fe2c5e (patch) | |
tree | e49ae068069a99fd2d3586eb194d5f95d6dfe2eb /org.eclipse.jgit.test | |
parent | 265550e63719c47ac9fc59fce107914acf0f8522 (diff) | |
parent | 567315af548017cc58eadb91bba493b74c391009 (diff) | |
download | jgit-c8844571b3a483e3cc5f77d18b4e972ad5fe2c5e.tar.gz jgit-c8844571b3a483e3cc5f77d18b4e972ad5fe2c5e.zip |
Merge branch 'master' into next
* master:
ResolveMerger: Fix the issue with binary modify-modify conflicts
Explain why RacyGitTests may be flaky in some environments
PackBitmapIndexBuilder.StoredEntry: add getter for objectId
PackBitmapIndex: clarify naming of getObject inputs
Revert "[releng] Bump Bouncy Castle to 1.78"
[releng] Bump Bouncy Castle to 1.78
MergeAlgorithm: Fix diff3 conflict hunk computation
[gpg] Remove obsolete import-package
[gpg] Correct finding public keys from pubring.gpg
[gpg] Fix reading ed25519 GPG keys
PackBitmapIndexBuilder: make StoredEntry constructor public
PackBitmapIndexBuilder: allow repeated call of getCompressedBitmaps()
DfsPackFile: Make the loader classes used to construct bitmaps public.
DfsPackFile: make public the constructor with bitmap loader
PackWriter: writeBitmapIndex takes bitmap index writer
Add more tests on rewriting parents in a RevWalk
PackBitmapIndex: convert from class to interface
Cache refreshed loose ref dirs in SnapshottingRefDirectory
DfsGarbageCollectorTest: add test for bitmap index creation
PackBitmapIndex: hide packChecksum behind getter
TreeRevFilter: correct changedPathFilter usage for multi-paths inclusion
PathFilterGroup: implement getPathsBestEffort()
CleanupService: preload JgitText if not running in OSGi
DfsPackFile: get commitGraph.readChangePaths from repo config
CommitGraphLoader: receive readChangedPaths as parameter
Add pull request template discouraging usage of pull requests
Update CONTRIBUTING.md to point to GitHub issues
FS_POSIX.runInShell(): on MacOS use a login shell
[ssh] Implement the "Ciphers" SSH config
ShutdownHook: run on bundle deactivation if in OSGi
[diffmergetool] Fix running command on Cygwin
[releng] Bump japicmp base version to 6.9.0.202403050737-r
DfsBlockCache: move cache table specific implementations to a new class
Prepare 6.10.0-SNAPSHOT builds
Prepare 6.9.1-SNAPSHOT builds
JGit v6.9.0.202403050737-r
JGit v6.9.0.202403050045-r
Prepare 6.9.0-SNAPSHOT builds
Introduce core.trustLooseRefStat config
JGit v6.9.0.202402281855-rc1
Update SECURITY.md
DfsObjDatabase: Let object database instantiate DfsPackFiles
DfsPackFile: Abstract the bitmap loading to support other backends
Remove unused API problem filters
Support public key in IdentityFile
Revert "StartGenerator: Fix parent rewrite with non-default RevFilter"
Prepare 6.9.0-SNAPSHOT builds
JGit v6.9.0.202402211805-m3
DfsReader#getObjectSize: use size index if possible
Change-Id: Ibdde5be8c5c2b3938aeaa4f278020cadcaad36a3
Diffstat (limited to 'org.eclipse.jgit.test')
11 files changed, 657 insertions, 159 deletions
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/attributes/merge/MergeGitAttributeTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/attributes/merge/MergeGitAttributeTest.java index 795029188d..6b23de3320 100644 --- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/attributes/merge/MergeGitAttributeTest.java +++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/attributes/merge/MergeGitAttributeTest.java @@ -10,7 +10,6 @@ package org.eclipse.jgit.attributes.merge; import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertNull; import static org.junit.Assert.assertTrue; @@ -42,7 +41,6 @@ import org.eclipse.jgit.revwalk.RevCommit; import org.eclipse.jgit.treewalk.FileTreeIterator; import org.eclipse.jgit.treewalk.TreeWalk; import org.eclipse.jgit.treewalk.filter.PathFilter; -import org.junit.Ignore; import org.junit.Test; public class MergeGitAttributeTest extends RepositoryTestCase { @@ -268,12 +266,7 @@ public class MergeGitAttributeTest extends RepositoryTestCase { } } - /* - * This test is commented because JGit add conflict markers in binary files. - * cf. https://www.eclipse.org/forums/index.php/t/1086511/ - */ @Test - @Ignore public void mergeBinaryFile_NoAttr_Conflict() throws IllegalStateException, IOException, NoHeadException, ConcurrentRefUpdateException, CheckoutConflictException, InvalidMergeHeadsException, @@ -433,7 +426,7 @@ public class MergeGitAttributeTest extends RepositoryTestCase { try (FileInputStream mergeResultFile = new FileInputStream( db.getWorkTree().toPath().resolve(ENABLED_CHECKED_GIF) .toFile())) { - assertFalse(contentEquals( + assertTrue(contentEquals( getClass().getResourceAsStream(ENABLED_CHECKED_GIF), mergeResultFile)); } diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/dfs/DfsGarbageCollectorTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/dfs/DfsGarbageCollectorTest.java index 05360dc052..e193de9764 100644 --- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/dfs/DfsGarbageCollectorTest.java +++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/dfs/DfsGarbageCollectorTest.java @@ -18,10 +18,11 @@ import java.io.IOException; import java.util.Arrays; import java.util.Collections; import java.util.concurrent.TimeUnit; - import org.eclipse.jgit.internal.storage.commitgraph.CommitGraph; import org.eclipse.jgit.internal.storage.commitgraph.CommitGraphWriter; import org.eclipse.jgit.internal.storage.dfs.DfsObjDatabase.PackSource; +import org.eclipse.jgit.internal.storage.file.PackBitmapIndex; +import org.eclipse.jgit.internal.storage.pack.PackExt; import org.eclipse.jgit.internal.storage.reftable.RefCursor; import org.eclipse.jgit.internal.storage.reftable.ReftableConfig; import org.eclipse.jgit.internal.storage.reftable.ReftableReader; @@ -30,6 +31,8 @@ import org.eclipse.jgit.junit.MockSystemReader; import org.eclipse.jgit.junit.TestRepository; import org.eclipse.jgit.lib.AnyObjectId; import org.eclipse.jgit.lib.BatchRefUpdate; +import org.eclipse.jgit.lib.Config; +import org.eclipse.jgit.lib.ConfigConstants; import org.eclipse.jgit.lib.NullProgressMonitor; import org.eclipse.jgit.lib.ObjectId; import org.eclipse.jgit.lib.ObjectIdRef; @@ -1121,6 +1124,40 @@ public class DfsGarbageCollectorTest { } @Test + public void testReadChangedPathConfigAsFalse() throws Exception { + String head = "refs/heads/head1"; + git.branch(head).commit().message("0").noParents().create(); + gcWithCommitGraphAndBloomFilter(); + + Config repoConfig = odb.getRepository().getConfig(); + repoConfig.setBoolean(ConfigConstants.CONFIG_COMMIT_GRAPH_SECTION, null, + ConfigConstants.CONFIG_KEY_READ_CHANGED_PATHS, false); + + DfsPackFile gcPack = odb.getPacks()[0]; + try (DfsReader reader = odb.newReader()) { + CommitGraph cg = gcPack.getCommitGraph(reader); + assertNull(cg.getChangedPathFilter(0)); + } + } + + @Test + public void testReadChangedPathConfigAsTrue() throws Exception { + String head = "refs/heads/head1"; + git.branch(head).commit().message("0").noParents().create(); + gcWithCommitGraphAndBloomFilter(); + + Config repoConfig = odb.getRepository().getConfig(); + repoConfig.setBoolean(ConfigConstants.CONFIG_COMMIT_GRAPH_SECTION, null, + ConfigConstants.CONFIG_KEY_READ_CHANGED_PATHS, true); + + DfsPackFile gcPack = odb.getPacks()[0]; + try (DfsReader reader = odb.newReader()) { + CommitGraph cg = gcPack.getCommitGraph(reader); + assertNotNull(cg.getChangedPathFilter(0)); + } + } + + @Test public void objectSizeIdx_reachableBlob_bigEnough_indexed() throws Exception { String master = "refs/heads/master"; RevCommit root = git.branch(master).commit().message("root").noParents() @@ -1178,6 +1215,71 @@ public class DfsGarbageCollectorTest { assertFalse(gcRestPack.hasObjectSizeIndex(reader)); } + @Test + public void bitmapIndexWrittenDuringGc() throws Exception { + int numBranches = 2; + int commitsPerBranch = 50; + + RevCommit commit0 = commit().message("0").create(); + git.update("branch0", commit0); + RevCommit branch1 = commitChain(commit0, commitsPerBranch); + git.update("branch1", branch1); + RevCommit branch2 = commitChain(commit0, commitsPerBranch); + git.update("branch2", branch2); + + int contiguousCommitCount = 5; + int recentCommitSpan = 2; + int recentCommitCount = 10; + int distantCommitSpan = 5; + + PackConfig packConfig = new PackConfig(); + packConfig.setBitmapContiguousCommitCount(contiguousCommitCount); + packConfig.setBitmapRecentCommitSpan(recentCommitSpan); + packConfig.setBitmapRecentCommitCount(recentCommitCount); + packConfig.setBitmapDistantCommitSpan(distantCommitSpan); + + DfsGarbageCollector gc = new DfsGarbageCollector(repo); + gc.setPackConfig(packConfig); + run(gc); + + DfsPackFile pack = odb.getPacks()[0]; + PackBitmapIndex bitmapIndex = pack.getBitmapIndex(odb.newReader()); + assertTrue("pack file has bitmap index extension", + pack.getPackDescription().hasFileExt(PackExt.BITMAP_INDEX)); + + int recentCommitsPerBranch = (recentCommitCount - contiguousCommitCount + - 1) / recentCommitSpan; + assertEquals("expected recent commits", 2, recentCommitsPerBranch); + + int distantCommitsPerBranch = (commitsPerBranch - 1 - recentCommitCount) + / distantCommitSpan; + assertEquals("expected distant commits", 7, distantCommitsPerBranch); + + int branchBitmapsCount = contiguousCommitCount + + numBranches + * (recentCommitsPerBranch + + distantCommitsPerBranch); + assertEquals("expected bitmaps count", 23, branchBitmapsCount); + assertEquals("bitmap index has expected number of bitmaps", + branchBitmapsCount, + bitmapIndex.getBitmapCount()); + + // The count is just a function of whether any bitmaps happen to + // compress efficiently against the others in the index. We expect for + // this test that this there will be at least one like this, but the + // actual count is situation-specific + assertTrue("bitmap index has xor-compressed bitmaps", + bitmapIndex.getXorBitmapCount() > 0); + } + + private RevCommit commitChain(RevCommit parent, int length) + throws Exception { + for (int i = 0; i < length; i++) { + parent = commit().message("" + i).parent(parent).create(); + } + return parent; + } + private static DfsPackFile findFirstBySource(DfsPackFile[] packs, PackSource source) { return Arrays.stream(packs) .filter(p -> p.getPackDescription().getPackSource() == source) diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/dfs/DfsPackFileTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/dfs/DfsPackFileTest.java index 77e5b7cb14..d21e51f276 100644 --- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/dfs/DfsPackFileTest.java +++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/dfs/DfsPackFileTest.java @@ -10,10 +10,14 @@ package org.eclipse.jgit.internal.storage.dfs; +import static org.eclipse.jgit.lib.ConfigConstants.CONFIG_COMMIT_GRAPH_SECTION; import static org.eclipse.jgit.lib.ConfigConstants.CONFIG_KEY_MIN_BYTES_OBJ_SIZE_INDEX; +import static org.eclipse.jgit.lib.ConfigConstants.CONFIG_KEY_READ_CHANGED_PATHS; import static org.eclipse.jgit.lib.ConfigConstants.CONFIG_PACK_SECTION; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertNull; import static org.junit.Assert.assertTrue; import java.io.ByteArrayOutputStream; @@ -22,16 +26,22 @@ import java.util.HashMap; import java.util.Map; import java.util.zip.Deflater; +import org.eclipse.jgit.internal.storage.commitgraph.CommitGraph; import org.eclipse.jgit.internal.storage.dfs.DfsObjDatabase.PackSource; import org.eclipse.jgit.internal.storage.dfs.DfsReader.PackLoadListener; +import org.eclipse.jgit.internal.storage.file.PackBitmapIndex; import org.eclipse.jgit.internal.storage.pack.PackExt; import org.eclipse.jgit.internal.storage.pack.PackOutputStream; import org.eclipse.jgit.internal.storage.pack.PackWriter; import org.eclipse.jgit.junit.JGitTestUtil; +import org.eclipse.jgit.junit.TestRepository; import org.eclipse.jgit.junit.TestRng; +import org.eclipse.jgit.lib.BatchRefUpdate; import org.eclipse.jgit.lib.Constants; import org.eclipse.jgit.lib.NullProgressMonitor; import org.eclipse.jgit.lib.ObjectId; +import org.eclipse.jgit.revwalk.RevWalk; +import org.eclipse.jgit.transport.ReceiveCommand; import org.junit.Before; import org.junit.Test; @@ -123,6 +133,41 @@ public class DfsPackFileTest { } @Test + public void testGetBitmapIndex() throws IOException { + bypassCache = false; + clearCache = true; + ObjectId objectId = setupPack(512, 800); + + // Add a ref for GC + BatchRefUpdate batchRefUpdate = db.getRefDatabase().newBatchUpdate(); + batchRefUpdate.addCommand(new ReceiveCommand(ObjectId.zeroId(), + objectId, "refs/heads/master")); + try (RevWalk rw = new RevWalk(db)) { + batchRefUpdate.execute(rw, NullProgressMonitor.INSTANCE); + } + DfsGarbageCollector gc = new DfsGarbageCollector(db); + gc.pack(NullProgressMonitor.INSTANCE); + + DfsReader reader = db.getObjectDatabase().newReader(); + PackBitmapIndex bitmapIndex = db.getObjectDatabase().getPacks()[0] + .getBitmapIndex(reader); + assertNotNull(bitmapIndex); + assertEquals(1, bitmapIndex.getObjectCount()); + } + + @Test + public void testGetBitmapIndex_noBitmaps() throws IOException { + bypassCache = false; + clearCache = true; + setupPack(512, 800); + + DfsReader reader = db.getObjectDatabase().newReader(); + PackBitmapIndex bitmapIndex = db.getObjectDatabase().getPacks()[0] + .getBitmapIndex(reader); + assertNull(bitmapIndex); + } + + @Test public void testLoadObjectSizeIndex_noIndex() throws IOException { bypassCache = false; clearCache = true; @@ -222,6 +267,27 @@ public class DfsPackFileTest { assertEquals(2, tal.blockLoadCount); } + @Test + public void testExistenceOfBloomFilterAlongWithCommitGraph() + throws Exception { + try (TestRepository<InMemoryRepository> repository = new TestRepository<>( + db)) { + repository.branch("/refs/heads/main").commit().add("blob1", "blob1") + .create(); + } + setReadChangedPaths(true); + DfsGarbageCollector gc = new DfsGarbageCollector(db); + gc.setWriteCommitGraph(true).setWriteBloomFilter(true) + .pack(NullProgressMonitor.INSTANCE); + + DfsReader reader = db.getObjectDatabase().newReader(); + CommitGraph cg = db.getObjectDatabase().getPacks()[0] + .getCommitGraph(reader); + assertNotNull(cg); + assertEquals(1, cg.getCommitCnt()); + assertNotNull(cg.getChangedPathFilter(0)); + } + private ObjectId setupPack(int bs, int ps) throws IOException { DfsBlockCacheConfig cfg = new DfsBlockCacheConfig().setBlockSize(bs) .setBlockLimit(bs * 100).setStreamRatio(bypassCache ? 0F : 1F); @@ -257,4 +323,9 @@ public class DfsPackFileTest { db.getConfig().setInt(CONFIG_PACK_SECTION, null, CONFIG_KEY_MIN_BYTES_OBJ_SIZE_INDEX, threshold); } + + private void setReadChangedPaths(boolean enable) { + db.getConfig().setBoolean(CONFIG_COMMIT_GRAPH_SECTION, null, + CONFIG_KEY_READ_CHANGED_PATHS, enable); + } } diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/dfs/DfsReaderTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/dfs/DfsReaderTest.java index eb8ceecd81..254184ee80 100644 --- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/dfs/DfsReaderTest.java +++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/dfs/DfsReaderTest.java @@ -12,6 +12,7 @@ package org.eclipse.jgit.internal.storage.dfs; import static org.eclipse.jgit.lib.ConfigConstants.CONFIG_KEY_MIN_BYTES_OBJ_SIZE_INDEX; import static org.eclipse.jgit.lib.ConfigConstants.CONFIG_PACK_SECTION; import static org.eclipse.jgit.lib.Constants.OBJ_BLOB; +import static org.eclipse.jgit.lib.Constants.OBJ_COMMIT; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; @@ -39,8 +40,56 @@ public class DfsReaderTest { } @Test - public void isNotLargerThan_objAboveThreshold() - throws IOException { + public void getObjectSize_noIndex_blob() throws IOException { + ObjectId obj = insertBlobWithSize(100); + try (DfsReader ctx = db.getObjectDatabase().newReader()) { + long size = ctx.getObjectSize(obj, OBJ_BLOB); + assertEquals(100, size); + } + } + + @Test + public void getObjectSize_noIndex_commit() throws IOException { + ObjectId obj = insertObjectWithSize(OBJ_COMMIT, 110); + try (DfsReader ctx = db.getObjectDatabase().newReader()) { + long size = ctx.getObjectSize(obj, OBJ_COMMIT); + assertEquals(110, size); + } + } + + @Test + public void getObjectSize_index_indexedBlob() throws IOException { + setObjectSizeIndexMinBytes(100); + ObjectId obj = insertBlobWithSize(200); + try (DfsReader ctx = db.getObjectDatabase().newReader()) { + long size = ctx.getObjectSize(obj, OBJ_BLOB); + assertEquals(200, size); + } + } + + @Test + public void getObjectSize_index_nonIndexedBlob() throws IOException { + setObjectSizeIndexMinBytes(100); + ObjectId obj = insertBlobWithSize(50); + try (DfsReader ctx = db.getObjectDatabase().newReader()) { + long size = ctx.getObjectSize(obj, OBJ_BLOB); + assertEquals(50, size); + } + } + + @Test + public void getObjectSize_index_commit() throws IOException { + setObjectSizeIndexMinBytes(100); + insertBlobWithSize(110); + ObjectId obj = insertObjectWithSize(OBJ_COMMIT, 120); + try (DfsReader ctx = db.getObjectDatabase().newReader()) { + long size = ctx.getObjectSize(obj, OBJ_COMMIT); + assertEquals(120, size); + } + } + + @Test + public void isNotLargerThan_objAboveThreshold() throws IOException { setObjectSizeIndexMinBytes(100); ObjectId obj = insertBlobWithSize(200); try (DfsReader ctx = db.getObjectDatabase().newReader()) { @@ -76,10 +125,8 @@ public class DfsReaderTest { } } - @Test - public void isNotLargerThan_objBelowThreshold() - throws IOException { + public void isNotLargerThan_objBelowThreshold() throws IOException { setObjectSizeIndexMinBytes(100); insertBlobWithSize(1000); // index not empty ObjectId obj = insertBlobWithSize(50); @@ -168,22 +215,26 @@ public class DfsReaderTest { ctx.addPackLoadListener(listener); boolean has = ctx.has(obj); assertTrue(has); - assertEquals(Integer.valueOf(1), listener.callsPerExt.get(PackExt.INDEX)); + assertEquals(Integer.valueOf(1), + listener.callsPerExt.get(PackExt.INDEX)); } } @Test - public void packLoadListener_notLargerThan_openMultipleIndices() throws IOException { - setObjectSizeIndexMinBytes(100); - ObjectId obj = insertBlobWithSize(200); - try (DfsReader ctx = db.getObjectDatabase().newReader()) { - CounterPackLoadListener listener = new CounterPackLoadListener(); - ctx.addPackLoadListener(listener); - boolean notLargerThan = ctx.isNotLargerThan(obj, OBJ_BLOB, 1000); - assertTrue(notLargerThan); - assertEquals(Integer.valueOf(1), listener.callsPerExt.get(PackExt.INDEX)); - assertEquals(Integer.valueOf(1), listener.callsPerExt.get(PackExt.OBJECT_SIZE_INDEX)); - } + public void packLoadListener_notLargerThan_openMultipleIndices() + throws IOException { + setObjectSizeIndexMinBytes(100); + ObjectId obj = insertBlobWithSize(200); + try (DfsReader ctx = db.getObjectDatabase().newReader()) { + CounterPackLoadListener listener = new CounterPackLoadListener(); + ctx.addPackLoadListener(listener); + boolean notLargerThan = ctx.isNotLargerThan(obj, OBJ_BLOB, 1000); + assertTrue(notLargerThan); + assertEquals(Integer.valueOf(1), + listener.callsPerExt.get(PackExt.INDEX)); + assertEquals(Integer.valueOf(1), + listener.callsPerExt.get(PackExt.OBJECT_SIZE_INDEX)); + } } @Test @@ -195,20 +246,24 @@ public class DfsReaderTest { try (DfsReader ctx = db.getObjectDatabase().newReader()) { CounterPackLoadListener listener = new CounterPackLoadListener(); ctx.addPackLoadListener(listener); - ObjectId oid = ObjectId.fromString("aa48de2aa61d9dffa8a05439dc115fe82f10f129"); + ObjectId oid = ObjectId + .fromString("aa48de2aa61d9dffa8a05439dc115fe82f10f129"); boolean has = ctx.has(oid); assertFalse(has); // Open 3 indices trying to find the pack - assertEquals(Integer.valueOf(3), listener.callsPerExt.get(PackExt.INDEX)); + assertEquals(Integer.valueOf(3), + listener.callsPerExt.get(PackExt.INDEX)); } } - @Test - public void packLoadListener_has_repeatedCalls_openMultipleIndices() throws IOException { + public void packLoadListener_has_repeatedCalls_openMultipleIndices() + throws IOException { // Two objects NOT in the repo - ObjectId oid = ObjectId.fromString("aa48de2aa61d9dffa8a05439dc115fe82f10f129"); - ObjectId oid2 = ObjectId.fromString("aa48de2aa61d9dffa8a05439dc115fe82f10f130"); + ObjectId oid = ObjectId + .fromString("aa48de2aa61d9dffa8a05439dc115fe82f10f129"); + ObjectId oid2 = ObjectId + .fromString("aa48de2aa61d9dffa8a05439dc115fe82f10f130"); setObjectSizeIndexMinBytes(100); insertBlobWithSize(200); @@ -222,7 +277,8 @@ public class DfsReaderTest { ctx.has(oid2); assertFalse(has); // The 3 indices were loaded only once each - assertEquals(Integer.valueOf(3), listener.callsPerExt.get(PackExt.INDEX)); + assertEquals(Integer.valueOf(3), + listener.callsPerExt.get(PackExt.INDEX)); } } @@ -231,8 +287,8 @@ public class DfsReaderTest { @SuppressWarnings("boxing") @Override - public void onIndexLoad(String packName, PackSource src, PackExt ext, long size, - Object loadedIdx) { + public void onIndexLoad(String packName, PackSource src, PackExt ext, + long size, Object loadedIdx) { callsPerExt.merge(ext, 1, Integer::sum); } @@ -243,13 +299,16 @@ public class DfsReaderTest { } } - private ObjectId insertBlobWithSize(int size) + private ObjectId insertBlobWithSize(int size) throws IOException { + return insertObjectWithSize(OBJ_BLOB, size); + } + + private ObjectId insertObjectWithSize(int object_type, int size) throws IOException { TestRng testRng = new TestRng(JGitTestUtil.getName()); ObjectId oid; try (ObjectInserter ins = db.newObjectInserter()) { - oid = ins.insert(OBJ_BLOB, - testRng.nextBytes(size)); + oid = ins.insert(object_type, testRng.nextBytes(size)); ins.flush(); } return oid; diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/RacyGitTests.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/RacyGitTests.java index b1d80c5c30..f25e5d10ff 100644 --- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/RacyGitTests.java +++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/RacyGitTests.java @@ -30,6 +30,22 @@ import org.junit.Test; public class RacyGitTests extends RepositoryTestCase { + /** + * This test is inherently flaky in nature since using clocks in a computer + * to determine file modifications in a filesystem from Java is difficult + * and depends on many factors and we can't test all combinations + * + * If this test fails on your computer, don't worry but let us know if you + * are willing to provide details which may help to further improve handling + * of the racy git problem in JGit. + * + * Despite not being completely reproducible this test is still useful to + * detect regressions when running this test repeatedly on the same + * OS/filesystem/Java version (which we do on the CI used to build JGit). + * + * @see "https://git-scm.com/docs/racy-git" + * @see "https://www.youtube.com/watch?v=m44cAozuLNI" + */ @Test public void testRacyGitDetection() throws Exception { // Reset to force creation of index file diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/merge/MergeAlgorithmTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/merge/MergeAlgorithmTest.java index 680a2d5d72..7a8a93e977 100644 --- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/merge/MergeAlgorithmTest.java +++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/merge/MergeAlgorithmTest.java @@ -162,6 +162,19 @@ public class MergeAlgorithmTest { merge("abz}z}z123q", "Abz}z123Q", "abz}z123q", true)); } + @Test + public void testInsertionAfterDeletion() throws IOException { + assertEquals(t("a<=bc>d"), merge("abd", "ad", "abcd", false)); + assertEquals(t("a<|b=bc>d"), + merge("abd", "ad", "abcd", true)); + } + + @Test + public void testInsertionBeforeDeletion() throws IOException { + assertEquals(t("a<=cb>d"), merge("abd", "ad", "acbd", false)); + assertEquals(t("a<|b=cb>d"), merge("abd", "ad", "acbd", true)); + } + /** * Test a conflicting region at the very start of the text. * diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/merge/MergerTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/merge/MergerTest.java index 022e8cd55e..3f99fe2b26 100644 --- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/merge/MergerTest.java +++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/merge/MergerTest.java @@ -22,9 +22,12 @@ import java.io.ByteArrayOutputStream; import java.io.File; import java.io.FileInputStream; import java.io.IOException; +import java.nio.file.Files; import java.time.Instant; import java.util.Arrays; +import java.util.HashSet; import java.util.Map; +import java.util.Set; import org.eclipse.jgit.api.Git; import org.eclipse.jgit.api.MergeResult; @@ -51,6 +54,7 @@ import org.eclipse.jgit.lib.ObjectInserter; import org.eclipse.jgit.lib.ObjectLoader; import org.eclipse.jgit.lib.ObjectReader; import org.eclipse.jgit.lib.ObjectStream; +import org.eclipse.jgit.lib.Repository; import org.eclipse.jgit.lib.StoredConfig; import org.eclipse.jgit.merge.ResolveMerger.MergeFailureReason; import org.eclipse.jgit.revwalk.RevCommit; @@ -1789,6 +1793,188 @@ public class MergerTest extends RepositoryTestCase { } + /** + * File is binary in ours, theirs and base with different content in each of + * them. Content of the file should not change after the merge conflict as + * no conflict markers are added to the binary files + */ + @Theory + public void oursBinaryTheirsBinaryBaseBinary(MergeStrategy strategy) + throws Exception { + Git git = Git.wrap(db); + String binaryFile = "file"; + + writeTrashFile(binaryFile, "\u0000\u0001"); + git.add().addFilepattern(binaryFile).call(); + RevCommit parent = git.commit().setMessage("BASE COMMIT").call(); + String fileHashInBase = getFileHashInWorkTree(git, binaryFile); + + writeTrashFile(binaryFile, "\u0001\u0002"); + git.add().addFilepattern(binaryFile).call(); + RevCommit child1 = git.commit().setMessage("THEIRS COMMIT").call(); + String fileHashInChild1 = getFileHashInWorkTree(git, binaryFile); + + git.checkout().setCreateBranch(true).setStartPoint(parent) + .setName("side").call(); + + writeTrashFile(binaryFile, "\u0002\u0000"); + git.add().addFilepattern(binaryFile).call(); + git.commit().setMessage("OURS COMMIT").call(); + String fileHashInChild2 = getFileHashInWorkTree(git, binaryFile); + + MergeResult mergeResult = git.merge().setStrategy(strategy) + .include(child1).call(); + + // check if the merge caused a conflict + assertTrue(mergeResult.getConflicts() != null + && !mergeResult.getConflicts().isEmpty()); + String fileHashInChild2AfterMerge = getFileHashInWorkTree(git, + binaryFile); + + // check if the file content changed during a conflicting merge + assertEquals(fileHashInChild2AfterMerge, fileHashInChild2); + + Set<String> hashesInIndexFile = new HashSet<>(); + DirCache indexContent = git.getRepository().readDirCache(); + for (int i = 0; i < indexContent.getEntryCount(); ++i) { + DirCacheEntry indexEntry = indexContent.getEntry(i); + if (binaryFile.equals(indexEntry.getPathString())) { + hashesInIndexFile.add(indexEntry.getObjectId().name()); + } + } + + // check if all the three stages are added to index file + assertTrue(hashesInIndexFile.contains(fileHashInBase)); + assertTrue(hashesInIndexFile.contains(fileHashInChild1)); + assertTrue(hashesInIndexFile.contains(fileHashInChild2)); + } + + /** + * File is text in ours and theirs with different content but binary in + * base. Even in this case, file will be treated as a binary and no conflict + * markers are added to it + */ + @Theory + public void oursAndTheirsDifferentTextBaseBinary(MergeStrategy strategy) + throws Exception { + Git git = Git.wrap(db); + String binaryFile = "file"; + + writeTrashFile(binaryFile, "\u0000\u0001"); + git.add().addFilepattern(binaryFile).call(); + RevCommit parent = git.commit().setMessage("BASE COMMIT").call(); + String fileHashInBase = getFileHashInWorkTree(git, binaryFile); + + writeTrashFile(binaryFile, "TEXT1"); + git.add().addFilepattern(binaryFile).call(); + RevCommit child1 = git.commit().setMessage("THEIRS COMMIT").call(); + String fileHashInChild1 = getFileHashInWorkTree(git, binaryFile); + + git.checkout().setCreateBranch(true).setStartPoint(parent) + .setName("side").call(); + + writeTrashFile(binaryFile, "TEXT2"); + git.add().addFilepattern(binaryFile).call(); + git.commit().setMessage("OURS COMMIT").call(); + String fileHashInChild2 = getFileHashInWorkTree(git, binaryFile); + + MergeResult mergeResult = git.merge().setStrategy(strategy) + .include(child1).call(); + + assertTrue(mergeResult.getConflicts() != null + && !mergeResult.getConflicts().isEmpty()); + String fileHashInChild2AfterMerge = getFileHashInWorkTree(git, + binaryFile); + + assertEquals(fileHashInChild2AfterMerge, fileHashInChild2); + + Set<String> hashesInIndexFile = new HashSet<>(); + DirCache indexContent = git.getRepository().readDirCache(); + for (int i = 0; i < indexContent.getEntryCount(); ++i) { + DirCacheEntry indexEntry = indexContent.getEntry(i); + if (binaryFile.equals(indexEntry.getPathString())) { + hashesInIndexFile.add(indexEntry.getObjectId().name()); + } + } + + assertTrue(hashesInIndexFile.contains(fileHashInBase)); + assertTrue(hashesInIndexFile.contains(fileHashInChild1)); + assertTrue(hashesInIndexFile.contains(fileHashInChild2)); + } + + /** + * Tests the scenario where a file is expected to be treated as binary + * according to Git attributes + */ + @Theory + public void fileInBinaryInAttribute(MergeStrategy strategy) + throws Exception { + Git git = Git.wrap(db); + String binaryFile = "file.bin"; + + writeTrashFile(".gitattributes", binaryFile + " binary"); + git.add().addFilepattern(".gitattributes").call(); + git.commit().setMessage("ADDING GITATTRIBUTES").call(); + + writeTrashFile(binaryFile, "\u0000\u0001"); + git.add().addFilepattern(binaryFile).call(); + RevCommit parent = git.commit().setMessage("BASE COMMIT").call(); + String fileHashInBase = getFileHashInWorkTree(git, binaryFile); + + writeTrashFile(binaryFile, "\u0001\u0002"); + git.add().addFilepattern(binaryFile).call(); + RevCommit child1 = git.commit().setMessage("THEIRS COMMIT").call(); + String fileHashInChild1 = getFileHashInWorkTree(git, binaryFile); + + git.checkout().setCreateBranch(true).setStartPoint(parent) + .setName("side").call(); + + writeTrashFile(binaryFile, "\u0002\u0000"); + git.add().addFilepattern(binaryFile).call(); + git.commit().setMessage("OURS COMMIT").call(); + String fileHashInChild2 = getFileHashInWorkTree(git, binaryFile); + + MergeResult mergeResult = git.merge().setStrategy(strategy) + .include(child1).call(); + + // check if the merge caused a conflict + assertTrue(mergeResult.getConflicts() != null + && !mergeResult.getConflicts().isEmpty()); + String fileHashInChild2AfterMerge = getFileHashInWorkTree(git, + binaryFile); + + // check if the file content changed during a conflicting merge + assertEquals(fileHashInChild2AfterMerge, fileHashInChild2); + + Set<String> hashesInIndexFile = new HashSet<>(); + DirCache indexContent = git.getRepository().readDirCache(); + for (int i = 0; i < indexContent.getEntryCount(); ++i) { + DirCacheEntry indexEntry = indexContent.getEntry(i); + if (binaryFile.equals(indexEntry.getPathString())) { + hashesInIndexFile.add(indexEntry.getObjectId().name()); + } + } + + // check if all the three stages are added to index file + assertTrue(hashesInIndexFile.contains(fileHashInBase)); + assertTrue(hashesInIndexFile.contains(fileHashInChild1)); + assertTrue(hashesInIndexFile.contains(fileHashInChild2)); + } + + private String getFileHashInWorkTree(Git git, String filePath) + throws IOException { + Repository repository = git.getRepository(); + ObjectInserter objectInserter = repository.newObjectInserter(); + + File conflictingFile = new File(repository.getWorkTree(), filePath); + byte[] fileContent = Files.readAllBytes(conflictingFile.toPath()); + ObjectId blobId = objectInserter.insert(Constants.OBJ_BLOB, + fileContent); + objectInserter.flush(); + + return blobId.name(); + } + private void writeSubmodule(String path, ObjectId commit) throws IOException, ConfigInvalidException { addSubmoduleToIndex(path, commit); diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/revwalk/RevWalkCommitGraphTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/revwalk/RevWalkCommitGraphTest.java index 8215a795b2..c2f8f10631 100644 --- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/revwalk/RevWalkCommitGraphTest.java +++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/revwalk/RevWalkCommitGraphTest.java @@ -38,6 +38,7 @@ import org.eclipse.jgit.revwalk.filter.RevFilter; import org.eclipse.jgit.storage.file.FileBasedConfig; import org.eclipse.jgit.treewalk.filter.AndTreeFilter; import org.eclipse.jgit.treewalk.filter.PathFilter; +import org.eclipse.jgit.treewalk.filter.PathFilterGroup; import org.eclipse.jgit.treewalk.filter.TreeFilter; import org.junit.Test; @@ -197,6 +198,35 @@ public class RevWalkCommitGraphTest extends RevWalkTestCase { } @Test + public void testChangedPathFilterWithMultiPaths() throws Exception { + RevCommit c1 = commitFile("file1", "1", "master"); + RevCommit c2 = commitFile("file1", "2", "master"); + RevCommit c3 = commitFile("file2", "3", "master"); + RevCommit c4 = commitFile("file3", "4", "master"); + + enableAndWriteCommitGraph(); + + TreeRevFilter trf = new TreeRevFilter(rw, + PathFilterGroup.createFromStrings(List.of("file1", "file2"))); + rw.markStart(rw.lookupCommit(c4)); + rw.setRevFilter(trf); + assertEquals(c3, rw.next()); + assertEquals(c2, rw.next()); + assertEquals(c1, rw.next()); + assertNull(rw.next()); + + // c2 and c3 has either file1 or file2, c1 did not use ChangedPathFilter + // since it has no parent + assertEquals(2, trf.getChangedPathFilterTruePositive()); + + // No false positives + assertEquals(0, trf.getChangedPathFilterFalsePositive()); + + // c4 does not match either file1 or file2 + assertEquals(1, trf.getChangedPathFilterNegative()); + } + + @Test public void testChangedPathFilterWithFollowFilter() throws Exception { RevCommit c0 = commit(tree()); RevCommit c1 = commit(tree(file("file", blob("contents"))), c0); diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/revwalk/TreeRevFilterTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/revwalk/TreeRevFilterTest.java index f67a623ff6..ddbb19cb8f 100644 --- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/revwalk/TreeRevFilterTest.java +++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/revwalk/TreeRevFilterTest.java @@ -20,23 +20,58 @@ import org.eclipse.jgit.treewalk.filter.TreeFilter; import org.junit.Test; public class TreeRevFilterTest extends RevWalkTestCase { - private RevFilter treeRevFilter() { + @Test + public void testStringOfPearls_FilePath1_treeRevFilter() + throws Exception { + RevCommit a = commit(tree(file("d/f", blob("a")))); + RevCommit b = commit(tree(file("d/f", blob("a"))), a); + RevCommit c = commit(tree(file("d/f", blob("b"))), b); + + rw.setRevFilter(new TreeRevFilter(rw, TreeFilter.ANY_DIFF)); + markStart(c); + + assertCommit(c, rw.next()); + assertEquals(1, c.getParentCount()); + assertCommit(b, c.getParent(0)); + + assertCommit(a, rw.next()); // b was skipped + assertEquals(0, a.getParentCount()); + assertNull(rw.next()); + } + @Test + public void testStringOfPearls_FilePath1_noRewriteParents() + throws Exception { + RevCommit a = commit(tree(file("d/f", blob("a")))); + RevCommit b = commit(tree(file("d/f", blob("a"))), a); + RevCommit c = commit(tree(file("d/f", blob("b"))), b); + rw.setRewriteParents(false); - return new TreeRevFilter(rw, TreeFilter.ANY_DIFF); + rw.setTreeFilter(TreeFilter.ANY_DIFF); + markStart(c); + + assertCommit(c, rw.next()); + assertEquals(1, c.getParentCount()); + assertCommit(b, c.getParent(0)); + + assertCommit(a, rw.next()); // b was skipped + assertEquals(0, a.getParentCount()); + assertNull(rw.next()); } @Test - public void testStringOfPearls_FilePath1() + public void testStringOfPearls_FilePath1_RewriteParents() throws Exception { RevCommit a = commit(tree(file("d/f", blob("a")))); RevCommit b = commit(tree(file("d/f", blob("a"))), a); RevCommit c = commit(tree(file("d/f", blob("b"))), b); - rw.setRevFilter(treeRevFilter()); + + rw.setRevFilter(new TreeRevFilter(rw, TreeFilter.ANY_DIFF)); + rw.setTreeFilter(TreeFilter.ANY_DIFF); markStart(c); assertCommit(c, rw.next()); assertEquals(1, c.getParentCount()); - assertCommit(b, c.getParent(0)); + assertCommit(a, c.getParent(0)); assertCommit(a, rw.next()); // b was skipped assertEquals(0, a.getParentCount()); @@ -49,7 +84,49 @@ public class TreeRevFilterTest extends RevWalkTestCase { RevCommit b = commit(tree(file("d/f", blob("a"))), a); RevCommit c = commit(tree(file("d/f", blob("b"))), b); RevCommit d = commit(tree(file("d/f", blob("b"))), c); - rw.setRevFilter(treeRevFilter()); + rw.setRevFilter(new TreeRevFilter(rw, TreeFilter.ANY_DIFF)); + markStart(d); + + // d was skipped + assertCommit(c, rw.next()); + assertEquals(1, c.getParentCount()); + assertCommit(b, c.getParent(0)); + + // b was skipped + assertCommit(a, rw.next()); + assertEquals(0, a.getParentCount()); + assertNull(rw.next()); + } + + @Test + public void testStringOfPearls_FilePath2_RewriteParents() throws Exception { + RevCommit a = commit(tree(file("d/f", blob("a")))); + RevCommit b = commit(tree(file("d/f", blob("a"))), a); + RevCommit c = commit(tree(file("d/f", blob("b"))), b); + RevCommit d = commit(tree(file("d/f", blob("b"))), c); + rw.setTreeFilter(TreeFilter.ANY_DIFF); + markStart(d); + + // d was skipped + assertCommit(c, rw.next()); + assertEquals(1, c.getParentCount()); + assertCommit(a, c.getParent(0)); + + // b was skipped + assertCommit(a, rw.next()); + assertEquals(0, a.getParentCount()); + assertNull(rw.next()); + } + + @Test + public void testStringOfPearls_FilePath2_RewriteParents_False() throws Exception { + RevCommit a = commit(tree(file("d/f", blob("a")))); + RevCommit b = commit(tree(file("d/f", blob("a"))), a); + RevCommit c = commit(tree(file("d/f", blob("b"))), b); + RevCommit d = commit(tree(file("d/f", blob("b"))), c); + rw.setRewriteParents(false); + rw.setRevFilter(new TreeRevFilter(rw, TreeFilter.ANY_DIFF)); + rw.setTreeFilter(TreeFilter.ANY_DIFF); markStart(d); // d was skipped @@ -69,7 +146,7 @@ public class TreeRevFilterTest extends RevWalkTestCase { RevCommit b = commit(tree(file("d/f", blob("a"))), a); RevCommit c = commit(tree(file("d/f", blob("b"))), b); RevCommit d = commit(tree(file("d/f", blob("b"))), c); - rw.setRevFilter(treeRevFilter()); + rw.setRevFilter(new TreeRevFilter(rw, TreeFilter.ANY_DIFF)); markStart(d); // d was skipped @@ -94,7 +171,9 @@ public class TreeRevFilterTest extends RevWalkTestCase { RevCommit g = commit(tree(file("d/f", blob("b"))), f); RevCommit h = commit(tree(file("d/f", blob("b"))), g); RevCommit i = commit(tree(file("d/f", blob("c"))), h); - rw.setRevFilter(treeRevFilter()); + + // Doesn't rewrite parents since no TreeFilter is set + rw.setRevFilter(new TreeRevFilter(rw, TreeFilter.ANY_DIFF)); markStart(i); assertCommit(i, rw.next()); @@ -113,8 +192,39 @@ public class TreeRevFilterTest extends RevWalkTestCase { } @Test + public void testStringOfPearls_FilePath3_RewriteParents() throws Exception { + RevCommit a = commit(tree(file("d/f", blob("a")))); + RevCommit b = commit(tree(file("d/f", blob("a"))), a); + RevCommit c = commit(tree(file("d/f", blob("b"))), b); + RevCommit d = commit(tree(file("d/f", blob("b"))), c); + RevCommit e = commit(tree(file("d/f", blob("b"))), d); + RevCommit f = commit(tree(file("d/f", blob("b"))), e); + RevCommit g = commit(tree(file("d/f", blob("b"))), f); + RevCommit h = commit(tree(file("d/f", blob("b"))), g); + RevCommit i = commit(tree(file("d/f", blob("c"))), h); + + rw.setRevFilter(new TreeRevFilter(rw, TreeFilter.ANY_DIFF)); + rw.setTreeFilter(TreeFilter.ANY_DIFF); + markStart(i); + + assertCommit(i, rw.next()); + assertEquals(1, i.getParentCount()); + assertCommit(c, i.getParent(0)); + + // h..d was skipped + assertCommit(c, rw.next()); + assertEquals(1, c.getParentCount()); + assertCommit(a, c.getParent(0)); + + // b was skipped + assertCommit(a, rw.next()); + assertEquals(0, a.getParentCount()); + assertNull(rw.next()); + } + + @Test public void testPathFilterOrOtherFilter() throws Exception { - RevFilter pathFilter = treeRevFilter(); + RevFilter pathFilter = new TreeRevFilter(rw, TreeFilter.ANY_DIFF); RevFilter skipFilter = SkipRevFilter.create(1); RevFilter orFilter = OrRevFilter.create(skipFilter, pathFilter); @@ -126,6 +236,9 @@ public class TreeRevFilterTest extends RevWalkTestCase { rw.setRevFilter(pathFilter); markStart(c); assertCommit(c, rw.next()); + assertEquals(1, c.getParentCount()); + assertCommit(b, c.getParent(0)); + assertCommit(a, rw.next()); // Skip filter matches b, a. diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/revwalk/TreeRevFilterWithRewriteParentsTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/revwalk/TreeRevFilterWithRewriteParentsTest.java deleted file mode 100644 index 100f2e4164..0000000000 --- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/revwalk/TreeRevFilterWithRewriteParentsTest.java +++ /dev/null @@ -1,112 +0,0 @@ -/* - * Copyright (C) 2023, 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 - * https://www.eclipse.org/org/documents/edl-v10.php. - * - * SPDX-License-Identifier: BSD-3-Clause - */ - -package org.eclipse.jgit.revwalk; - -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertNull; - -import org.eclipse.jgit.revwalk.filter.RevFilter; -import org.eclipse.jgit.treewalk.filter.TreeFilter; -import org.junit.Test; - -public class TreeRevFilterWithRewriteParentsTest extends RevWalkTestCase { - private RevFilter treeRevFilter() { - rw.setRewriteParents(true); - return new TreeRevFilter(rw, TreeFilter.ANY_DIFF); - } - - @Test - public void testStringOfPearls_FilePath1() - throws Exception { - RevCommit a = commit(tree(file("d/f", blob("a")))); - RevCommit b = commit(tree(file("d/f", blob("a"))), a); - RevCommit c = commit(tree(file("d/f", blob("b"))), b); - rw.setRevFilter(treeRevFilter()); - markStart(c); - - assertCommit(c, rw.next()); - assertEquals(1, c.getParentCount()); - assertCommit(a, c.getParent(0)); - - assertCommit(a, rw.next()); // b was skipped - assertEquals(0, a.getParentCount()); - assertNull(rw.next()); - } - - @Test - public void testStringOfPearls_FilePath2() throws Exception { - RevCommit a = commit(tree(file("d/f", blob("a")))); - RevCommit b = commit(tree(file("d/f", blob("a"))), a); - RevCommit c = commit(tree(file("d/f", blob("b"))), b); - RevCommit d = commit(tree(file("d/f", blob("b"))), c); - rw.setRevFilter(treeRevFilter()); - markStart(d); - - // d was skipped - assertCommit(c, rw.next()); - assertEquals(1, c.getParentCount()); - assertCommit(a, c.getParent(0)); - - // b was skipped - assertCommit(a, rw.next()); - assertEquals(0, a.getParentCount()); - assertNull(rw.next()); - } - - @Test - public void testStringOfPearls_DirPath2() throws Exception { - RevCommit a = commit(tree(file("d/f", blob("a")))); - RevCommit b = commit(tree(file("d/f", blob("a"))), a); - RevCommit c = commit(tree(file("d/f", blob("b"))), b); - RevCommit d = commit(tree(file("d/f", blob("b"))), c); - rw.setRevFilter(treeRevFilter()); - markStart(d); - - // d was skipped - assertCommit(c, rw.next()); - assertEquals(1, c.getParentCount()); - assertCommit(a, c.getParent(0)); - - // b was skipped - assertCommit(a, rw.next()); - assertEquals(0, a.getParentCount()); - assertNull(rw.next()); - } - - @Test - public void testStringOfPearls_FilePath3() throws Exception { - RevCommit a = commit(tree(file("d/f", blob("a")))); - RevCommit b = commit(tree(file("d/f", blob("a"))), a); - RevCommit c = commit(tree(file("d/f", blob("b"))), b); - RevCommit d = commit(tree(file("d/f", blob("b"))), c); - RevCommit e = commit(tree(file("d/f", blob("b"))), d); - RevCommit f = commit(tree(file("d/f", blob("b"))), e); - RevCommit g = commit(tree(file("d/f", blob("b"))), f); - RevCommit h = commit(tree(file("d/f", blob("b"))), g); - RevCommit i = commit(tree(file("d/f", blob("c"))), h); - rw.setRevFilter(treeRevFilter()); - markStart(i); - - assertCommit(i, rw.next()); - assertEquals(1, i.getParentCount()); - assertCommit(c, i.getParent(0)); - - // h..d was skipped - assertCommit(c, rw.next()); - assertEquals(1, c.getParentCount()); - assertCommit(a, c.getParent(0)); - - // b was skipped - assertCommit(a, rw.next()); - assertEquals(0, a.getParentCount()); - assertNull(rw.next()); - } -} diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/treewalk/filter/PathFilterGroupTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/treewalk/filter/PathFilterGroupTest.java index 32bd40312f..1bb4939c85 100644 --- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/treewalk/filter/PathFilterGroupTest.java +++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/treewalk/filter/PathFilterGroupTest.java @@ -11,6 +11,7 @@ package org.eclipse.jgit.treewalk.filter; import static org.junit.Assert.assertArrayEquals; +import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; @@ -21,7 +22,9 @@ import java.util.Arrays; import java.util.HashMap; import java.util.List; import java.util.Map; +import java.util.Optional; import java.util.Set; +import java.util.stream.Collectors; import org.eclipse.jgit.dircache.DirCache; import org.eclipse.jgit.dircache.DirCacheEditor; @@ -30,6 +33,7 @@ import org.eclipse.jgit.dircache.DirCacheIterator; import org.eclipse.jgit.errors.IncorrectObjectTypeException; import org.eclipse.jgit.errors.MissingObjectException; import org.eclipse.jgit.errors.StopWalkException; +import org.eclipse.jgit.lib.Constants; import org.eclipse.jgit.lib.FileMode; import org.eclipse.jgit.lib.ObjectReader; import org.eclipse.jgit.lib.Sets; @@ -143,6 +147,29 @@ public class PathFilterGroupTest { } @Test + public void testGetPathsBestEffort() { + String[] paths = { "path1", "path2", "path3" }; + Set<byte[]> expected = Arrays.stream(paths).map(Constants::encode) + .collect(Collectors.toSet()); + TreeFilter pathFilterGroup = PathFilterGroup.createFromStrings(paths); + Optional<Set<byte[]>> bestEffortPaths = pathFilterGroup + .getPathsBestEffort(); + assertTrue(bestEffortPaths.isPresent()); + Set<byte[]> actual = bestEffortPaths.get(); + assertEquals(expected.size(), actual.size()); + for (byte[] actualPath : actual) { + boolean findMatch = false; + for (byte[] expectedPath : expected) { + if (Arrays.equals(actualPath, expectedPath)) { + findMatch = true; + break; + } + } + assertTrue(findMatch); + } + } + + @Test public void testStopWalk() throws MissingObjectException, IncorrectObjectTypeException, IOException { // Obvious |