aboutsummaryrefslogtreecommitdiffstats
path: root/org.eclipse.jgit.test/tst/org
diff options
context:
space:
mode:
Diffstat (limited to 'org.eclipse.jgit.test/tst/org')
-rw-r--r--org.eclipse.jgit.test/tst/org/eclipse/jgit/api/ArchiveCommandTest.java2
-rw-r--r--org.eclipse.jgit.test/tst/org/eclipse/jgit/api/PushCommandTest.java4
-rw-r--r--org.eclipse.jgit.test/tst/org/eclipse/jgit/ignore/IgnoreNodeTest.java108
-rw-r--r--org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/revwalk/ObjectReachabilityTestCase.java1
-rw-r--r--org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/revwalk/ReachabilityCheckerTestCase.java1
-rw-r--r--org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/commitgraph/CommitGraphTest.java68
-rw-r--r--org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/commitgraph/CommitGraphWriterTest.java312
-rw-r--r--org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/dfs/DfsGarbageCollectorTest.java117
-rw-r--r--org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/dfs/DfsInserterTest.java72
-rw-r--r--org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/dfs/DfsPackFileTest.java132
-rw-r--r--org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/dfs/DfsPackParserTest.java69
-rw-r--r--org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/dfs/DfsReaderTest.java262
-rw-r--r--org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/FileRepositoryBuilderAfterOpenConfigTest.java1
-rw-r--r--org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/GcReverseIndexTest.java116
-rw-r--r--org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/PackReverseIndexComputedTest.java108
-rw-r--r--org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/PackReverseIndexTest.java136
-rw-r--r--org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/PackReverseIndexV1Test.java289
-rw-r--r--org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/PackReverseIndexV1WriteReadTest.java119
-rw-r--r--org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/PackTest.java2
-rw-r--r--org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/RefDirectoryAfterOpenConfigTest.java1
-rw-r--r--org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/SnapshottingRefDirectoryTest.java1
-rw-r--r--org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/CommitTemplateConfigTest.java10
-rw-r--r--org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/ConfigTest.java16
-rw-r--r--org.eclipse.jgit.test/tst/org/eclipse/jgit/merge/MergeAlgorithmTest.java144
-rw-r--r--org.eclipse.jgit.test/tst/org/eclipse/jgit/revwalk/FooterLineTest.java191
-rw-r--r--org.eclipse.jgit.test/tst/org/eclipse/jgit/revwalk/RevWalkCommitGraphTest.java109
-rw-r--r--org.eclipse.jgit.test/tst/org/eclipse/jgit/revwalk/RevWalkFollowFilterTest.java24
-rw-r--r--org.eclipse.jgit.test/tst/org/eclipse/jgit/storage/file/UserConfigFileTest.java301
-rw-r--r--org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/HttpAuthTest.java2
-rw-r--r--org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/InMemoryPack.java88
-rw-r--r--org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/PackParserTest.java169
-rw-r--r--org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/UploadPackTest.java8
-rw-r--r--org.eclipse.jgit.test/tst/org/eclipse/jgit/treewalk/filter/PathSuffixFilterTest.java4
33 files changed, 2634 insertions, 353 deletions
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/ArchiveCommandTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/ArchiveCommandTest.java
index 411eab3e7e..3a4ea8e733 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/ArchiveCommandTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/ArchiveCommandTest.java
@@ -254,7 +254,7 @@ public class ArchiveCommandTest extends RepositoryTestCase {
}
}
- @SuppressWarnings({ "serial", "boxing" })
+ @SuppressWarnings({ "boxing" })
private void archiveHeadAllFilesWithCompression(String fmt) throws Exception {
try (Git git = new Git(db)) {
createLargeTestContent(git);
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/PushCommandTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/PushCommandTest.java
index ff5f8b76cc..70e990dedf 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/PushCommandTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/PushCommandTest.java
@@ -941,7 +941,7 @@ public class PushCommandTest extends RepositoryTestCase {
}
/**
- * Check that branch.<name>.pushRemote overrides anything else.
+ * Check that branch.&lt;name&gt;.pushRemote overrides anything else.
*
* @throws Exception
*/
@@ -980,7 +980,7 @@ public class PushCommandTest extends RepositoryTestCase {
}
/**
- * Check that remote.pushDefault overrides branch.<name>.remote
+ * Check that remote.pushDefault overrides branch.&lt;name&gt;.remote
*
* @throws Exception
*/
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/ignore/IgnoreNodeTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/ignore/IgnoreNodeTest.java
index 05a953e081..ab08c99796 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/ignore/IgnoreNodeTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/ignore/IgnoreNodeTest.java
@@ -20,14 +20,18 @@ import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
+import java.nio.file.Files;
+import java.nio.file.Path;
import java.util.ArrayList;
import java.util.Arrays;
+import org.eclipse.jgit.junit.MockSystemReader;
import org.eclipse.jgit.junit.RepositoryTestCase;
import org.eclipse.jgit.lib.FileMode;
import org.eclipse.jgit.treewalk.FileTreeIterator;
import org.eclipse.jgit.treewalk.TreeWalk;
import org.eclipse.jgit.treewalk.WorkingTreeIterator;
+import org.eclipse.jgit.util.FS;
import org.eclipse.jgit.util.FileUtils;
import org.eclipse.jgit.util.SystemReader;
import org.junit.After;
@@ -698,6 +702,110 @@ public class IgnoreNodeTest extends RepositoryTestCase {
}
@Test
+ public void testUserGitIgnoreFound() throws IOException {
+ File homeDir = FS.DETECTED.userHome();
+ Path userIgnore = homeDir.toPath().resolve(".config").resolve("git")
+ .resolve("ignore");
+ Files.createDirectories(userIgnore.getParent());
+ Files.writeString(userIgnore, "x");
+ try {
+ writeTrashFile(".foo", "");
+ writeTrashFile("a/x/file", "");
+ writeTrashFile("b/x", "");
+ writeTrashFile("x/file", "");
+
+ beginWalk();
+ assertEntry(F, tracked, ".foo");
+ assertEntry(D, tracked, "a");
+ assertEntry(D, ignored, "a/x");
+ assertEntry(F, ignored, "a/x/file");
+ assertEntry(D, tracked, "b");
+ assertEntry(F, ignored, "b/x");
+ assertEntry(D, ignored, "x");
+ assertEntry(F, ignored, "x/file");
+ endWalk();
+ } finally {
+ Files.deleteIfExists(userIgnore);
+ }
+ }
+
+ @Test
+ public void testXdgIgnoreFound() throws IOException {
+ File tmp = getTemporaryDirectory();
+ Path xdg = tmp.toPath().resolve("xdg");
+ Path userIgnore = xdg.resolve("git").resolve("ignore");
+ Files.createDirectories(userIgnore.getParent());
+ Files.writeString(userIgnore, "x");
+ SystemReader system = SystemReader.getInstance();
+ assertTrue(system instanceof MockSystemReader);
+ ((MockSystemReader) system).setProperty("XDG_CONFIG_HOME",
+ xdg.toAbsolutePath().toString());
+ // Also create the one in the home directory -- it should not be active
+ File homeDir = FS.DETECTED.userHome();
+ Path userIgnore2 = homeDir.toPath().resolve(".config").resolve("git")
+ .resolve("ignore");
+ Files.createDirectories(userIgnore2.getParent());
+ Files.writeString(userIgnore2, "a");
+ try {
+ writeTrashFile(".foo", "");
+ writeTrashFile("a/x/file", "");
+ writeTrashFile("b/x", "");
+ writeTrashFile("x/file", "");
+
+ beginWalk();
+ assertEntry(F, tracked, ".foo");
+ assertEntry(D, tracked, "a");
+ assertEntry(D, ignored, "a/x");
+ assertEntry(F, ignored, "a/x/file");
+ assertEntry(D, tracked, "b");
+ assertEntry(F, ignored, "b/x");
+ assertEntry(D, ignored, "x");
+ assertEntry(F, ignored, "x/file");
+ endWalk();
+ } finally {
+ ((MockSystemReader) system).setProperty("XDG_CONFIG_HOME", null);
+ Files.deleteIfExists(userIgnore2);
+ }
+ }
+
+ @Test
+ public void testXdgWrong() throws IOException {
+ File tmp = getTemporaryDirectory();
+ Path xdg = tmp.toPath().resolve("xdg");
+ SystemReader system = SystemReader.getInstance();
+ assertTrue(system instanceof MockSystemReader);
+ // Valid value, but the directory doesn't exist
+ ((MockSystemReader) system).setProperty("XDG_CONFIG_HOME",
+ xdg.toAbsolutePath().toString());
+ // Also create the one in the home directory -- it should not be active
+ File homeDir = FS.DETECTED.userHome();
+ Path userIgnore2 = homeDir.toPath().resolve(".config").resolve("git")
+ .resolve("ignore");
+ Files.createDirectories(userIgnore2.getParent());
+ Files.writeString(userIgnore2, "x");
+ try {
+ writeTrashFile(".foo", "");
+ writeTrashFile("a/x/file", "");
+ writeTrashFile("b/x", "");
+ writeTrashFile("x/file", "");
+
+ beginWalk();
+ assertEntry(F, tracked, ".foo");
+ assertEntry(D, tracked, "a");
+ assertEntry(D, tracked, "a/x");
+ assertEntry(F, tracked, "a/x/file");
+ assertEntry(D, tracked, "b");
+ assertEntry(F, tracked, "b/x");
+ assertEntry(D, tracked, "x");
+ assertEntry(F, tracked, "x/file");
+ endWalk();
+ } finally {
+ ((MockSystemReader) system).setProperty("XDG_CONFIG_HOME", null);
+ Files.deleteIfExists(userIgnore2);
+ }
+ }
+
+ @Test
public void testToString() throws Exception {
assertEquals(Arrays.asList("").toString(), new IgnoreNode().toString());
assertEquals(Arrays.asList("hello").toString(),
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/revwalk/ObjectReachabilityTestCase.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/revwalk/ObjectReachabilityTestCase.java
index 37ff40bdf7..0e73588c66 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/revwalk/ObjectReachabilityTestCase.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/revwalk/ObjectReachabilityTestCase.java
@@ -51,7 +51,6 @@ public abstract class ObjectReachabilityTestCase
}
}
- /** {@inheritDoc} */
@Override
@Before
public void setUp() throws Exception {
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/revwalk/ReachabilityCheckerTestCase.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/revwalk/ReachabilityCheckerTestCase.java
index 7679c11098..eeb13cc8b9 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/revwalk/ReachabilityCheckerTestCase.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/revwalk/ReachabilityCheckerTestCase.java
@@ -32,7 +32,6 @@ public abstract class ReachabilityCheckerTestCase
TestRepository<FileRepository> repo;
- /** {@inheritDoc} */
@Override
@Before
public void setUp() throws Exception {
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/commitgraph/CommitGraphTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/commitgraph/CommitGraphTest.java
index 97976564d8..4d05360252 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/commitgraph/CommitGraphTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/commitgraph/CommitGraphTest.java
@@ -10,24 +10,31 @@
package org.eclipse.jgit.internal.storage.commitgraph;
+import static java.nio.charset.StandardCharsets.UTF_8;
import static org.eclipse.jgit.lib.Constants.COMMIT_GENERATION_UNKNOWN;
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 java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
+import java.io.IOException;
import java.io.InputStream;
import java.util.Collections;
import java.util.HashSet;
import java.util.Set;
+import org.eclipse.jgit.errors.ConfigInvalidException;
import org.eclipse.jgit.internal.storage.file.FileRepository;
import org.eclipse.jgit.junit.RepositoryTestCase;
import org.eclipse.jgit.junit.TestRepository;
+import org.eclipse.jgit.lib.ConfigConstants;
import org.eclipse.jgit.lib.NullProgressMonitor;
import org.eclipse.jgit.lib.ObjectId;
import org.eclipse.jgit.revwalk.RevCommit;
import org.eclipse.jgit.revwalk.RevWalk;
+import org.eclipse.jgit.storage.file.FileBasedConfig;
import org.junit.Before;
import org.junit.Test;
@@ -45,6 +52,7 @@ public class CommitGraphTest extends RepositoryTestCase {
public void setUp() throws Exception {
super.setUp();
tr = new TestRepository<>(db, new RevWalk(db), mockSystemReader);
+ mockSystemReader.setJGitConfig(new MockConfig());
}
@Test
@@ -196,11 +204,32 @@ public class CommitGraphTest extends RepositoryTestCase {
assertEquals(getGenerationNumber(c8), 5);
}
+ @Test
+ public void testGraphComputeChangedPaths() throws Exception {
+ RevCommit a = tr.commit(tr.tree(tr.file("d/f", tr.blob("a"))));
+ RevCommit b = tr.commit(tr.tree(tr.file("d/f", tr.blob("a"))), a);
+ RevCommit c = tr.commit(tr.tree(tr.file("d/f", tr.blob("b"))), b);
+
+ writeAndReadCommitGraph(Collections.singleton(c));
+ ChangedPathFilter acpf = commitGraph
+ .getChangedPathFilter(commitGraph.findGraphPosition(a));
+ assertTrue(acpf.maybeContains("d".getBytes(UTF_8)));
+ assertTrue(acpf.maybeContains("d/f".getBytes(UTF_8)));
+ ChangedPathFilter bcpf = commitGraph
+ .getChangedPathFilter(commitGraph.findGraphPosition(b));
+ assertFalse(bcpf.maybeContains("d".getBytes(UTF_8)));
+ assertFalse(bcpf.maybeContains("d/f".getBytes(UTF_8)));
+ ChangedPathFilter ccpf = commitGraph
+ .getChangedPathFilter(commitGraph.findGraphPosition(c));
+ assertTrue(ccpf.maybeContains("d".getBytes(UTF_8)));
+ assertTrue(ccpf.maybeContains("d/f".getBytes(UTF_8)));
+ }
+
void writeAndReadCommitGraph(Set<ObjectId> wants) throws Exception {
NullProgressMonitor m = NullProgressMonitor.INSTANCE;
try (RevWalk walk = new RevWalk(db)) {
CommitGraphWriter writer = new CommitGraphWriter(
- GraphCommits.fromWalk(m, wants, walk));
+ GraphCommits.fromWalk(m, wants, walk), true);
ByteArrayOutputStream os = new ByteArrayOutputStream();
writer.write(m, os);
InputStream inputStream = new ByteArrayInputStream(
@@ -252,4 +281,41 @@ public class CommitGraphTest extends RepositoryTestCase {
RevCommit commit(RevCommit... parents) throws Exception {
return tr.commit(parents);
}
+
+ private static final class MockConfig extends FileBasedConfig {
+ private MockConfig() {
+ super(null, null);
+ }
+
+ @Override
+ public void load() throws IOException, ConfigInvalidException {
+ // Do nothing
+ }
+
+ @Override
+ public void save() throws IOException {
+ // Do nothing
+ }
+
+ @Override
+ public boolean isOutdated() {
+ return false;
+ }
+
+ @Override
+ public String toString() {
+ return "MockConfig";
+ }
+
+ @Override
+ public boolean getBoolean(final String section, final String name,
+ final boolean defaultValue) {
+ if (section.equals(ConfigConstants.CONFIG_COMMIT_GRAPH_SECTION)
+ && name.equals(
+ ConfigConstants.CONFIG_KEY_READ_CHANGED_PATHS)) {
+ return true;
+ }
+ return defaultValue;
+ }
+ }
}
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/commitgraph/CommitGraphWriterTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/commitgraph/CommitGraphWriterTest.java
index 6c5e5e5605..5040a3b6ad 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/commitgraph/CommitGraphWriterTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/commitgraph/CommitGraphWriterTest.java
@@ -10,21 +10,31 @@
package org.eclipse.jgit.internal.storage.commitgraph;
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.hamcrest.Matchers.containsInAnyOrder;
import static org.junit.Assert.assertArrayEquals;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
import java.io.ByteArrayOutputStream;
+import java.io.IOException;
import java.util.Collections;
+import java.util.HashSet;
import java.util.Set;
+import org.eclipse.jgit.dircache.DirCacheEntry;
+import org.eclipse.jgit.errors.ConfigInvalidException;
import org.eclipse.jgit.internal.storage.file.FileRepository;
+import org.eclipse.jgit.internal.storage.file.GC;
import org.eclipse.jgit.junit.RepositoryTestCase;
import org.eclipse.jgit.junit.TestRepository;
+import org.eclipse.jgit.lib.ConfigConstants;
import org.eclipse.jgit.lib.NullProgressMonitor;
import org.eclipse.jgit.lib.ObjectId;
+import org.eclipse.jgit.revwalk.RevBlob;
import org.eclipse.jgit.revwalk.RevCommit;
import org.eclipse.jgit.revwalk.RevWalk;
+import org.eclipse.jgit.storage.file.FileBasedConfig;
import org.eclipse.jgit.util.NB;
import org.junit.Before;
import org.junit.Test;
@@ -46,6 +56,7 @@ public class CommitGraphWriterTest extends RepositoryTestCase {
os = new ByteArrayOutputStream();
tr = new TestRepository<>(db, new RevWalk(db), mockSystemReader);
walk = new RevWalk(db);
+ mockSystemReader.setJGitConfig(new MockConfig());
}
@Test
@@ -68,7 +79,7 @@ public class CommitGraphWriterTest extends RepositoryTestCase {
Set<ObjectId> wants = Collections.singleton(tip);
NullProgressMonitor m = NullProgressMonitor.INSTANCE;
GraphCommits graphCommits = GraphCommits.fromWalk(m, wants, walk);
- writer = new CommitGraphWriter(graphCommits);
+ writer = new CommitGraphWriter(graphCommits, true);
writer.write(m, os);
assertEquals(5, graphCommits.size());
@@ -76,11 +87,20 @@ public class CommitGraphWriterTest extends RepositoryTestCase {
assertTrue(data.length > 0);
byte[] headers = new byte[8];
System.arraycopy(data, 0, headers, 0, 8);
- assertArrayEquals(new byte[] {'C', 'G', 'P', 'H', 1, 1, 4, 0}, headers);
- assertEquals(CommitGraphConstants.CHUNK_ID_OID_FANOUT, NB.decodeInt32(data, 8));
- assertEquals(CommitGraphConstants.CHUNK_ID_OID_LOOKUP, NB.decodeInt32(data, 20));
- assertEquals(CommitGraphConstants.CHUNK_ID_COMMIT_DATA, NB.decodeInt32(data, 32));
- assertEquals(CommitGraphConstants.CHUNK_ID_EXTRA_EDGE_LIST, NB.decodeInt32(data, 44));
+ assertArrayEquals(new byte[] { 'C', 'G', 'P', 'H', 1, 1, 6, 0 },
+ headers);
+ assertEquals(CommitGraphConstants.CHUNK_ID_OID_FANOUT,
+ NB.decodeInt32(data, 8));
+ assertEquals(CommitGraphConstants.CHUNK_ID_OID_LOOKUP,
+ NB.decodeInt32(data, 20));
+ assertEquals(CommitGraphConstants.CHUNK_ID_COMMIT_DATA,
+ NB.decodeInt32(data, 32));
+ assertEquals(CommitGraphConstants.CHUNK_ID_EXTRA_EDGE_LIST,
+ NB.decodeInt32(data, 44));
+ assertEquals(CommitGraphConstants.CHUNK_ID_BLOOM_FILTER_INDEX,
+ NB.decodeInt32(data, 56));
+ assertEquals(CommitGraphConstants.CHUNK_ID_BLOOM_FILTER_DATA,
+ NB.decodeInt32(data, 68));
}
@Test
@@ -93,7 +113,7 @@ public class CommitGraphWriterTest extends RepositoryTestCase {
Set<ObjectId> wants = Collections.singleton(tip);
NullProgressMonitor m = NullProgressMonitor.INSTANCE;
GraphCommits graphCommits = GraphCommits.fromWalk(m, wants, walk);
- writer = new CommitGraphWriter(graphCommits);
+ writer = new CommitGraphWriter(graphCommits, true);
writer.write(m, os);
assertEquals(4, graphCommits.size());
@@ -101,13 +121,281 @@ public class CommitGraphWriterTest extends RepositoryTestCase {
assertTrue(data.length > 0);
byte[] headers = new byte[8];
System.arraycopy(data, 0, headers, 0, 8);
- assertArrayEquals(new byte[] {'C', 'G', 'P', 'H', 1, 1, 3, 0}, headers);
- assertEquals(CommitGraphConstants.CHUNK_ID_OID_FANOUT, NB.decodeInt32(data, 8));
- assertEquals(CommitGraphConstants.CHUNK_ID_OID_LOOKUP, NB.decodeInt32(data, 20));
- assertEquals(CommitGraphConstants.CHUNK_ID_COMMIT_DATA, NB.decodeInt32(data, 32));
+ assertArrayEquals(new byte[] { 'C', 'G', 'P', 'H', 1, 1, 5, 0 },
+ headers);
+ assertEquals(CommitGraphConstants.CHUNK_ID_OID_FANOUT,
+ NB.decodeInt32(data, 8));
+ assertEquals(CommitGraphConstants.CHUNK_ID_OID_LOOKUP,
+ NB.decodeInt32(data, 20));
+ assertEquals(CommitGraphConstants.CHUNK_ID_COMMIT_DATA,
+ NB.decodeInt32(data, 32));
+ assertEquals(CommitGraphConstants.CHUNK_ID_BLOOM_FILTER_INDEX,
+ NB.decodeInt32(data, 44));
+ assertEquals(CommitGraphConstants.CHUNK_ID_BLOOM_FILTER_DATA,
+ NB.decodeInt32(data, 56));
+ }
+
+ static HashSet<String> changedPathStrings(byte[] data) {
+ int oidf_offset = -1;
+ int bidx_offset = -1;
+ int bdat_offset = -1;
+ for (int i = 8; i < data.length - 4; i += 12) {
+ switch (NB.decodeInt32(data, i)) {
+ case CommitGraphConstants.CHUNK_ID_OID_FANOUT:
+ oidf_offset = (int) NB.decodeInt64(data, i + 4);
+ break;
+ case CommitGraphConstants.CHUNK_ID_BLOOM_FILTER_INDEX:
+ bidx_offset = (int) NB.decodeInt64(data, i + 4);
+ break;
+ case CommitGraphConstants.CHUNK_ID_BLOOM_FILTER_DATA:
+ bdat_offset = (int) NB.decodeInt64(data, i + 4);
+ break;
+ }
+ }
+ assertTrue(oidf_offset > 0);
+ assertTrue(bidx_offset > 0);
+ assertTrue(bdat_offset > 0);
+ bdat_offset += 12; // skip version, hash count, bits per entry
+ int commit_count = NB.decodeInt32(data, oidf_offset + 255 * 4);
+ int[] changed_path_length_cumuls = new int[commit_count];
+ for (int i = 0; i < commit_count; i++) {
+ changed_path_length_cumuls[i] = NB.decodeInt32(data,
+ bidx_offset + i * 4);
+ }
+ HashSet<String> changed_paths = new HashSet<>();
+ for (int i = 0; i < commit_count; i++) {
+ int prior_cumul = i == 0 ? 0 : changed_path_length_cumuls[i - 1];
+ String changed_path = "";
+ for (int j = prior_cumul; j < changed_path_length_cumuls[i]; j++) {
+ changed_path += data[bdat_offset + j] + ",";
+ }
+ changed_paths.add(changed_path);
+ }
+ return changed_paths;
+ }
+
+ /**
+ * Expected value generated using the following:
+ *
+ * <pre>
+ * # apply into git-repo: https://lore.kernel.org/git/cover.1684790529.git.jonathantanmy@google.com/
+ * (cd git-repo; make)
+ * git-repo/bin-wrappers/git init tested
+ * (cd tested; touch foo.txt; mkdir -p onedir/twodir; touch onedir/twodir/bar.txt)
+ * git-repo/bin-wrappers/git -C tested add foo.txt onedir
+ * git-repo/bin-wrappers/git -C tested commit -m first_commit
+ * (cd tested; mv foo.txt foo-new.txt; mv onedir/twodir/bar.txt onedir/twodir/bar-new.txt)
+ * git-repo/bin-wrappers/git -C tested add foo-new.txt onedir
+ * git-repo/bin-wrappers/git -C tested commit -a -m second_commit
+ * git-repo/bin-wrappers/git -C tested maintenance run
+ * git-repo/bin-wrappers/git -C tested commit-graph write --changed-paths
+ * (cd tested; $JGIT debug-read-changed-path-filter .git/objects/info/commit-graph)
+ * </pre>
+ *
+ * @throws Exception
+ */
+ @Test
+ public void testChangedPathFilterRootAndNested() throws Exception {
+ RevBlob emptyBlob = tr.blob(new byte[] {});
+ RevCommit root = tr.commit(tr.tree(tr.file("foo.txt", emptyBlob),
+ tr.file("onedir/twodir/bar.txt", emptyBlob)));
+ RevCommit tip = tr.commit(tr.tree(tr.file("foo-new.txt", emptyBlob),
+ tr.file("onedir/twodir/bar-new.txt", emptyBlob)), root);
+
+ Set<ObjectId> wants = Collections.singleton(tip);
+ NullProgressMonitor m = NullProgressMonitor.INSTANCE;
+ GraphCommits graphCommits = GraphCommits.fromWalk(m, wants, walk);
+ writer = new CommitGraphWriter(graphCommits, true);
+ writer.write(m, os);
+
+ HashSet<String> changedPaths = changedPathStrings(os.toByteArray());
+ assertThat(changedPaths, containsInAnyOrder(
+ "109,-33,2,60,20,79,-11,116,",
+ "119,69,63,-8,0,"));
+ }
+
+ /**
+ * Expected value generated using the following:
+ *
+ * <pre>
+ * git -C git-repo checkout todo get version number when it is merged
+ * (cd git-repo; make)
+ * git-repo/bin-wrappers/git init tested
+ * (cd tested; mkdir -p onedir/twodir; touch onedir/twodir/a.txt; touch onedir/twodir/b.txt)
+ * git-repo/bin-wrappers/git -C tested add onedir
+ * git-repo/bin-wrappers/git -C tested commit -m first_commit
+ * (cd tested; mv onedir/twodir/a.txt onedir/twodir/c.txt; mv onedir/twodir/b.txt onedir/twodir/d.txt)
+ * git-repo/bin-wrappers/git -C tested add onedir
+ * git-repo/bin-wrappers/git -C tested commit -a -m second_commit
+ * git-repo/bin-wrappers/git -C tested maintenance run
+ * git-repo/bin-wrappers/git -C tested commit-graph write --changed-paths
+ * (cd tested; $JGIT debug-read-changed-path-filter .git/objects/info/commit-graph)
+ * </pre>
+ *
+ * @throws Exception
+ */
+ @Test
+ public void testChangedPathFilterOverlappingNested() throws Exception {
+ RevBlob emptyBlob = tr.blob(new byte[] {});
+ RevCommit root = tr
+ .commit(tr.tree(tr.file("onedir/twodir/a.txt", emptyBlob),
+ tr.file("onedir/twodir/b.txt", emptyBlob)));
+ RevCommit tip = tr
+ .commit(tr.tree(tr.file("onedir/twodir/c.txt", emptyBlob),
+ tr.file("onedir/twodir/d.txt", emptyBlob)), root);
+
+ Set<ObjectId> wants = Collections.singleton(tip);
+ NullProgressMonitor m = NullProgressMonitor.INSTANCE;
+ GraphCommits graphCommits = GraphCommits.fromWalk(m, wants, walk);
+ writer = new CommitGraphWriter(graphCommits, true);
+ writer.write(m, os);
+
+ HashSet<String> changedPaths = changedPathStrings(os.toByteArray());
+ assertThat(changedPaths, containsInAnyOrder("61,30,23,-24,1,",
+ "-58,-51,-46,60,29,-121,113,90,"));
+ }
+
+ /**
+ * Expected value generated using the following:
+ *
+ * <pre>
+ * git -C git-repo checkout todo get version number when it is merged
+ * (cd git-repo; make)
+ * git-repo/bin-wrappers/git init tested
+ * (cd tested; touch 你好)
+ * git-repo/bin-wrappers/git -C tested add 你好
+ * git-repo/bin-wrappers/git -C tested commit -m first_commit
+ * git-repo/bin-wrappers/git -C tested maintenance run
+ * git-repo/bin-wrappers/git -C tested commit-graph write --changed-paths
+ * (cd tested; $JGIT debug-read-changed-path-filter .git/objects/info/commit-graph)
+ * </pre>
+ *
+ * @throws Exception
+ */
+ @Test
+ public void testChangedPathFilterHighBit() throws Exception {
+ RevBlob emptyBlob = tr.blob(new byte[] {});
+ // tr.file encodes using UTF-8
+ RevCommit root = tr.commit(tr.tree(tr.file("你好", emptyBlob)));
+
+ Set<ObjectId> wants = Collections.singleton(root);
+ NullProgressMonitor m = NullProgressMonitor.INSTANCE;
+ GraphCommits graphCommits = GraphCommits.fromWalk(m, wants, walk);
+ writer = new CommitGraphWriter(graphCommits, true);
+ writer.write(m, os);
+
+ HashSet<String> changedPaths = changedPathStrings(os.toByteArray());
+ assertThat(changedPaths, containsInAnyOrder("16,16,"));
+ }
+
+ @Test
+ public void testChangedPathFilterEmptyChange() throws Exception {
+ RevCommit root = commit();
+
+ Set<ObjectId> wants = Collections.singleton(root);
+ NullProgressMonitor m = NullProgressMonitor.INSTANCE;
+ GraphCommits graphCommits = GraphCommits.fromWalk(m, wants, walk);
+ writer = new CommitGraphWriter(graphCommits, true);
+ writer.write(m, os);
+
+ HashSet<String> changedPaths = changedPathStrings(os.toByteArray());
+ assertThat(changedPaths, containsInAnyOrder("0,"));
+ }
+
+ @Test
+ public void testChangedPathFilterManyChanges() throws Exception {
+ RevBlob emptyBlob = tr.blob(new byte[] {});
+ DirCacheEntry[] entries = new DirCacheEntry[513];
+ for (int i = 0; i < entries.length; i++) {
+ entries[i] = tr.file(i + ".txt", emptyBlob);
+ }
+
+ RevCommit root = tr.commit(tr.tree(entries));
+
+ Set<ObjectId> wants = Collections.singleton(root);
+ NullProgressMonitor m = NullProgressMonitor.INSTANCE;
+ GraphCommits graphCommits = GraphCommits.fromWalk(m, wants, walk);
+ writer = new CommitGraphWriter(graphCommits, true);
+ writer.write(m, os);
+
+ HashSet<String> changedPaths = changedPathStrings(os.toByteArray());
+ assertThat(changedPaths, containsInAnyOrder("-1,"));
+ }
+
+ @Test
+ public void testReuseBloomFilters() throws Exception {
+ RevBlob emptyBlob = tr.blob(new byte[] {});
+ RevCommit root = tr.commit(tr.tree(tr.file("foo.txt", emptyBlob),
+ tr.file("onedir/twodir/bar.txt", emptyBlob)));
+ tr.branch("master").update(root);
+
+ db.getConfig().setBoolean(ConfigConstants.CONFIG_CORE_SECTION, null,
+ ConfigConstants.CONFIG_COMMIT_GRAPH, true);
+ db.getConfig().setBoolean(ConfigConstants.CONFIG_GC_SECTION, null,
+ ConfigConstants.CONFIG_KEY_WRITE_COMMIT_GRAPH, true);
+ db.getConfig().setBoolean(ConfigConstants.CONFIG_GC_SECTION, null,
+ ConfigConstants.CONFIG_KEY_WRITE_CHANGED_PATHS, true);
+ GC gc = new GC(db);
+ gc.gc().get();
+
+ RevCommit tip = tr.commit(tr.tree(tr.file("foo-new.txt", emptyBlob),
+ tr.file("onedir/twodir/bar-new.txt", emptyBlob)), root);
+
+ Set<ObjectId> wants = Collections.singleton(tip);
+ NullProgressMonitor m = NullProgressMonitor.INSTANCE;
+ GraphCommits graphCommits = GraphCommits.fromWalk(m, wants, walk);
+ writer = new CommitGraphWriter(graphCommits, true);
+ CommitGraphWriter.Stats stats = writer.write(m, os);
+
+ assertEquals(1, stats.getChangedPathFiltersReused());
+ assertEquals(1, stats.getChangedPathFiltersComputed());
+
+ // Expected strings are the same as in
+ // #testChangedPathFilterRootAndNested
+ HashSet<String> changedPaths = changedPathStrings(os.toByteArray());
+ assertThat(changedPaths, containsInAnyOrder(
+ "109,-33,2,60,20,79,-11,116,",
+ "119,69,63,-8,0,"));
}
RevCommit commit(RevCommit... parents) throws Exception {
return tr.commit(parents);
}
-}
+
+ private static final class MockConfig extends FileBasedConfig {
+ private MockConfig() {
+ super(null, null);
+ }
+
+ @Override
+ public void load() throws IOException, ConfigInvalidException {
+ // Do nothing
+ }
+
+ @Override
+ public void save() throws IOException {
+ // Do nothing
+ }
+
+ @Override
+ public boolean isOutdated() {
+ return false;
+ }
+
+ @Override
+ public String toString() {
+ return "MockConfig";
+ }
+
+ @Override
+ public boolean getBoolean(final String section, final String name,
+ final boolean defaultValue) {
+ if (section.equals(ConfigConstants.CONFIG_COMMIT_GRAPH_SECTION)
+ && name.equals(
+ ConfigConstants.CONFIG_KEY_READ_CHANGED_PATHS)) {
+ return true;
+ }
+ return defaultValue;
+ }
+ }
+} \ No newline at end of file
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 ab998951f3..05360dc052 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
@@ -15,10 +15,12 @@ import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
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.reftable.RefCursor;
import org.eclipse.jgit.internal.storage.reftable.ReftableConfig;
@@ -978,7 +980,7 @@ public class DfsGarbageCollectorTest {
}
@Test
- public void produceCommitGraphAllRefsIncludedFromDisk() throws Exception {
+ public void produceCommitGraphOnlyHeadsAndTags() throws Exception {
String tag = "refs/tags/tag1";
String head = "refs/heads/head1";
String nonHead = "refs/something/nonHead";
@@ -1000,19 +1002,20 @@ public class DfsGarbageCollectorTest {
CommitGraph cg = gcPack.getCommitGraph(reader);
assertNotNull(cg);
- assertTrue("all commits in commit graph", cg.getCommitCnt() == 3);
+ assertTrue("Only heads and tags reachable commits in commit graph",
+ cg.getCommitCnt() == 2);
// GC packed
assertTrue("tag referenced commit is in graph",
cg.findGraphPosition(rootCommitTagged) != -1);
assertTrue("head referenced commit is in graph",
cg.findGraphPosition(headTip) != -1);
- // GC_REST packed
- assertTrue("nonHead referenced commit is in graph",
- cg.findGraphPosition(nonHeadTip) != -1);
+ // GC_REST not in commit graph
+ assertEquals("nonHead referenced commit is NOT in graph",
+ -1, cg.findGraphPosition(nonHeadTip));
}
@Test
- public void produceCommitGraphAllRefsIncludedFromCache() throws Exception {
+ public void produceCommitGraphOnlyHeadsAndTagsIncludedFromCache() throws Exception {
String tag = "refs/tags/tag1";
String head = "refs/heads/head1";
String nonHead = "refs/something/nonHead";
@@ -1042,15 +1045,16 @@ public class DfsGarbageCollectorTest {
assertTrue("commit graph read time is recorded",
reader.stats.readCommitGraphMicros > 0);
- assertTrue("all commits in commit graph", cachedCG.getCommitCnt() == 3);
+ assertTrue("Only heads and tags reachable commits in commit graph",
+ cachedCG.getCommitCnt() == 2);
// GC packed
assertTrue("tag referenced commit is in graph",
cachedCG.findGraphPosition(rootCommitTagged) != -1);
assertTrue("head referenced commit is in graph",
cachedCG.findGraphPosition(headTip) != -1);
- // GC_REST packed
- assertTrue("nonHead referenced commit is in graph",
- cachedCG.findGraphPosition(nonHeadTip) != -1);
+ // GC_REST not in commit graph
+ assertEquals("nonHead referenced commit is not in graph",
+ -1, cachedCG.findGraphPosition(nonHeadTip));
}
@Test
@@ -1100,6 +1104,86 @@ public class DfsGarbageCollectorTest {
}
}
+ @Test
+ public void produceCommitGraphAndBloomFilter() throws Exception {
+ String head = "refs/heads/head1";
+
+ git.branch(head).commit().message("0").noParents().create();
+
+ gcWithCommitGraphAndBloomFilter();
+
+ assertEquals(1, odb.getPacks().length);
+ DfsPackFile pack = odb.getPacks()[0];
+ DfsPackDescription desc = pack.getPackDescription();
+ CommitGraphWriter.Stats stats = desc.getCommitGraphStats();
+ assertNotNull(stats);
+ assertEquals(1, stats.getChangedPathFiltersComputed());
+ }
+
+ @Test
+ public void objectSizeIdx_reachableBlob_bigEnough_indexed() throws Exception {
+ String master = "refs/heads/master";
+ RevCommit root = git.branch(master).commit().message("root").noParents()
+ .create();
+ RevBlob headsBlob = git.blob("twelve bytes");
+ git.branch(master).commit()
+ .message("commit on head")
+ .add("file.txt", headsBlob)
+ .parent(root)
+ .create();
+
+ gcWithObjectSizeIndex(10);
+
+ DfsReader reader = odb.newReader();
+ DfsPackFile gcPack = findFirstBySource(odb.getPacks(), GC);
+ assertTrue(gcPack.hasObjectSizeIndex(reader));
+ assertEquals(12, gcPack.getIndexedObjectSize(reader, headsBlob));
+ }
+
+ @Test
+ public void objectSizeIdx_reachableBlob_tooSmall_notIndexed() throws Exception {
+ String master = "refs/heads/master";
+ RevCommit root = git.branch(master).commit().message("root").noParents()
+ .create();
+ RevBlob tooSmallBlob = git.blob("small");
+ git.branch(master).commit()
+ .message("commit on head")
+ .add("small.txt", tooSmallBlob)
+ .parent(root)
+ .create();
+
+ gcWithObjectSizeIndex(10);
+
+ DfsReader reader = odb.newReader();
+ DfsPackFile gcPack = findFirstBySource(odb.getPacks(), GC);
+ assertTrue(gcPack.hasObjectSizeIndex(reader));
+ assertEquals(-1, gcPack.getIndexedObjectSize(reader, tooSmallBlob));
+ }
+
+ @Test
+ public void objectSizeIndex_unreachableGarbage_noIdx() throws Exception {
+ String master = "refs/heads/master";
+ RevCommit root = git.branch(master).commit().message("root").noParents()
+ .create();
+ git.branch(master).commit()
+ .message("commit on head")
+ .add("file.txt", git.blob("a blob"))
+ .parent(root)
+ .create();
+ git.update(master, root); // blob is unreachable
+ gcWithObjectSizeIndex(0);
+
+ DfsReader reader = odb.newReader();
+ DfsPackFile gcRestPack = findFirstBySource(odb.getPacks(), UNREACHABLE_GARBAGE);
+ assertFalse(gcRestPack.hasObjectSizeIndex(reader));
+ }
+
+ private static DfsPackFile findFirstBySource(DfsPackFile[] packs, PackSource source) {
+ return Arrays.stream(packs)
+ .filter(p -> p.getPackDescription().getPackSource() == source)
+ .findFirst().get();
+ }
+
private TestRepository<InMemoryRepository>.CommitBuilder commit() {
return git.commit();
}
@@ -1110,6 +1194,19 @@ public class DfsGarbageCollectorTest {
run(gc);
}
+ private void gcWithCommitGraphAndBloomFilter() throws IOException {
+ DfsGarbageCollector gc = new DfsGarbageCollector(repo);
+ gc.setWriteCommitGraph(true);
+ gc.setWriteBloomFilter(true);
+ run(gc);
+ }
+
+ private void gcWithObjectSizeIndex(int threshold) throws IOException {
+ DfsGarbageCollector gc = new DfsGarbageCollector(repo);
+ gc.getPackConfig().setMinBytesForObjSizeIndex(threshold);
+ run(gc);
+ }
+
private void gcNoTtl() throws IOException {
DfsGarbageCollector gc = new DfsGarbageCollector(repo);
gc.setGarbageTtl(0, TimeUnit.MILLISECONDS); // disable TTL
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/dfs/DfsInserterTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/dfs/DfsInserterTest.java
index adf577b0f7..b84a0b00ae 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/dfs/DfsInserterTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/dfs/DfsInserterTest.java
@@ -10,6 +10,8 @@
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.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertSame;
@@ -29,11 +31,16 @@ import org.eclipse.jgit.internal.storage.pack.PackExt;
import org.eclipse.jgit.junit.JGitTestUtil;
import org.eclipse.jgit.junit.TestRng;
import org.eclipse.jgit.lib.AbbreviatedObjectId;
+import org.eclipse.jgit.lib.CommitBuilder;
import org.eclipse.jgit.lib.Constants;
+import org.eclipse.jgit.lib.FileMode;
import org.eclipse.jgit.lib.ObjectId;
import org.eclipse.jgit.lib.ObjectInserter;
import org.eclipse.jgit.lib.ObjectLoader;
import org.eclipse.jgit.lib.ObjectReader;
+import org.eclipse.jgit.lib.PersonIdent;
+import org.eclipse.jgit.lib.TagBuilder;
+import org.eclipse.jgit.lib.TreeFormatter;
import org.eclipse.jgit.util.IO;
import org.eclipse.jgit.util.RawParseUtils;
import org.junit.Before;
@@ -240,6 +247,71 @@ public class DfsInserterTest {
}
}
+ @Test
+ public void testObjectSizePopulated() throws IOException {
+ // Blob
+ byte[] contents = Constants.encode("foo");
+
+ // Commit
+ PersonIdent person = new PersonIdent("Committer a", "jgit@eclipse.org");
+ CommitBuilder c = new CommitBuilder();
+ c.setAuthor(person);
+ c.setCommitter(person);
+ c.setTreeId(ObjectId
+ .fromString("45c4c6767a3945815371a7016532751dd558be40"));
+ c.setMessage("commit message");
+
+ // Tree
+ TreeFormatter treeBuilder = new TreeFormatter(2);
+ treeBuilder.append("filea", FileMode.REGULAR_FILE, ObjectId
+ .fromString("45c4c6767a3945815371a7016532751dd558be40"));
+ treeBuilder.append("fileb", FileMode.GITLINK, ObjectId
+ .fromString("1c458e25ca624bb8d4735bec1379a4a29ba786d0"));
+
+ // Tag
+ TagBuilder tagBuilder = new TagBuilder();
+ tagBuilder.setObjectId(
+ ObjectId.fromString("c97fe131649e80de55bd153e9a8d8629f7ca6932"),
+ Constants.OBJ_COMMIT);
+ tagBuilder.setTag("short name");
+
+ try (DfsInserter ins = (DfsInserter) db.newObjectInserter()) {
+ ObjectId aBlob = ins.insert(Constants.OBJ_BLOB, contents);
+ assertEquals(contents.length,
+ ins.objectMap.get(aBlob).getFullSize());
+
+ ObjectId aCommit = ins.insert(c);
+ assertEquals(174, ins.objectMap.get(aCommit).getFullSize());
+
+ ObjectId tree = ins.insert(treeBuilder);
+ assertEquals(66, ins.objectMap.get(tree).getFullSize());
+
+ ObjectId tag = ins.insert(tagBuilder);
+ assertEquals(76, ins.objectMap.get(tag).getFullSize());
+ }
+ }
+
+ @Test
+ public void testObjectSizeIndexOnInsert() throws IOException {
+ db.getConfig().setInt(CONFIG_PACK_SECTION, null,
+ CONFIG_KEY_MIN_BYTES_OBJ_SIZE_INDEX, 0);
+
+ byte[] contents = Constants.encode("foo");
+ ObjectId fooId;
+ try (ObjectInserter ins = db.newObjectInserter()) {
+ fooId = ins.insert(Constants.OBJ_BLOB, contents);
+ ins.flush();
+ }
+
+ DfsReader reader = db.getObjectDatabase().newReader();
+ assertEquals(1, db.getObjectDatabase().listPacks().size());
+ DfsPackFile insertPack = db.getObjectDatabase().getPacks()[0];
+ assertEquals(PackSource.INSERT,
+ insertPack.getPackDescription().getPackSource());
+ assertTrue(insertPack.hasObjectSizeIndex(reader));
+ assertEquals(contents.length, insertPack.getIndexedObjectSize(reader, fooId));
+ }
+
private static String readString(ObjectLoader loader) throws IOException {
return RawParseUtils.decode(readStream(loader));
}
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 ea5787309b..77e5b7cb14 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,12 +10,20 @@
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.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
+import java.util.HashMap;
+import java.util.Map;
import java.util.zip.Deflater;
+import org.eclipse.jgit.internal.storage.dfs.DfsObjDatabase.PackSource;
+import org.eclipse.jgit.internal.storage.dfs.DfsReader.PackLoadListener;
import org.eclipse.jgit.internal.storage.pack.PackExt;
import org.eclipse.jgit.internal.storage.pack.PackOutputStream;
import org.eclipse.jgit.internal.storage.pack.PackWriter;
@@ -23,6 +31,7 @@ import org.eclipse.jgit.junit.JGitTestUtil;
import org.eclipse.jgit.junit.TestRng;
import org.eclipse.jgit.lib.Constants;
import org.eclipse.jgit.lib.NullProgressMonitor;
+import org.eclipse.jgit.lib.ObjectId;
import org.junit.Before;
import org.junit.Test;
@@ -100,7 +109,120 @@ public class DfsPackFileTest {
assertPackSize();
}
- private void setupPack(int bs, int ps) throws IOException {
+ @Test
+ public void testLoadObjectSizeIndex() throws IOException {
+ bypassCache = false;
+ clearCache = true;
+ setObjectSizeIndexMinBytes(0);
+ ObjectId blobId = setupPack(512, 800);
+
+ DfsReader reader = db.getObjectDatabase().newReader();
+ DfsPackFile pack = db.getObjectDatabase().getPacks()[0];
+ assertTrue(pack.hasObjectSizeIndex(reader));
+ assertEquals(800, pack.getIndexedObjectSize(reader, blobId));
+ }
+
+ @Test
+ public void testLoadObjectSizeIndex_noIndex() throws IOException {
+ bypassCache = false;
+ clearCache = true;
+ setObjectSizeIndexMinBytes(-1);
+ setupPack(512, 800);
+
+ DfsReader reader = db.getObjectDatabase().newReader();
+ DfsPackFile pack = db.getObjectDatabase().getPacks()[0];
+ assertFalse(pack.hasObjectSizeIndex(reader));
+ }
+
+ private static class TestPackLoadListener implements PackLoadListener {
+ final Map<PackExt, Integer> indexLoadCount = new HashMap<>();
+
+ int blockLoadCount;
+
+ @SuppressWarnings("boxing")
+ @Override
+ public void onIndexLoad(String packName, PackSource src, PackExt ext,
+ long size, Object loadedIdx) {
+ indexLoadCount.merge(ext, 1, Integer::sum);
+ }
+
+ @Override
+ public void onBlockLoad(String packName, PackSource src, PackExt ext, long position,
+ DfsBlockData dfsBlockData) {
+ blockLoadCount += 1;
+ }
+ }
+
+ @Test
+ public void testIndexLoadCallback_indexNotInCache() throws IOException {
+ bypassCache = false;
+ clearCache = true;
+ setObjectSizeIndexMinBytes(-1);
+ setupPack(512, 800);
+
+ TestPackLoadListener tal = new TestPackLoadListener();
+ DfsReader reader = db.getObjectDatabase().newReader();
+ reader.addPackLoadListener(tal);
+ DfsPackFile pack = db.getObjectDatabase().getPacks()[0];
+ pack.getPackIndex(reader);
+
+ assertEquals(1, tal.indexLoadCount.get(PackExt.INDEX).intValue());
+ }
+
+ @Test
+ public void testIndexLoadCallback_indexInCache() throws IOException {
+ bypassCache = false;
+ clearCache = false;
+ setObjectSizeIndexMinBytes(-1);
+ setupPack(512, 800);
+
+ TestPackLoadListener tal = new TestPackLoadListener();
+ DfsReader reader = db.getObjectDatabase().newReader();
+ reader.addPackLoadListener(tal);
+ DfsPackFile pack = db.getObjectDatabase().getPacks()[0];
+ pack.getPackIndex(reader);
+ pack.getPackIndex(reader);
+ pack.getPackIndex(reader);
+
+ assertEquals(1, tal.indexLoadCount.get(PackExt.INDEX).intValue());
+ }
+
+ @Test
+ public void testIndexLoadCallback_multipleReads() throws IOException {
+ bypassCache = false;
+ clearCache = true;
+ setObjectSizeIndexMinBytes(-1);
+ setupPack(512, 800);
+
+ TestPackLoadListener tal = new TestPackLoadListener();
+ DfsReader reader = db.getObjectDatabase().newReader();
+ reader.addPackLoadListener(tal);
+ DfsPackFile pack = db.getObjectDatabase().getPacks()[0];
+ pack.getPackIndex(reader);
+ pack.getPackIndex(reader);
+ pack.getPackIndex(reader);
+
+ assertEquals(1, tal.indexLoadCount.get(PackExt.INDEX).intValue());
+ }
+
+
+ @Test
+ public void testBlockLoadCallback_loadInCache() throws IOException {
+ bypassCache = false;
+ clearCache = true;
+ setObjectSizeIndexMinBytes(-1);
+ setupPack(512, 800);
+
+ TestPackLoadListener tal = new TestPackLoadListener();
+ DfsReader reader = db.getObjectDatabase().newReader();
+ reader.addPackLoadListener(tal);
+ DfsPackFile pack = db.getObjectDatabase().getPacks()[0];
+ ObjectId anObject = pack.getPackIndex(reader).getObjectId(0);
+ pack.get(reader, anObject).getBytes();
+ assertEquals(2, tal.blockLoadCount);
+ }
+
+ private ObjectId setupPack(int bs, int ps) throws IOException {
DfsBlockCacheConfig cfg = new DfsBlockCacheConfig().setBlockSize(bs)
.setBlockLimit(bs * 100).setStreamRatio(bypassCache ? 0F : 1F);
DfsBlockCache.reconfigure(cfg);
@@ -108,13 +230,14 @@ public class DfsPackFileTest {
byte[] data = new TestRng(JGitTestUtil.getName()).nextBytes(ps);
DfsInserter ins = (DfsInserter) db.newObjectInserter();
ins.setCompressionLevel(Deflater.NO_COMPRESSION);
- ins.insert(Constants.OBJ_BLOB, data);
+ ObjectId blobId = ins.insert(Constants.OBJ_BLOB, data);
ins.flush();
if (clearCache) {
DfsBlockCache.reconfigure(cfg);
db.getObjectDatabase().clearCache();
}
+ return blobId;
}
private void assertPackSize() throws IOException {
@@ -129,4 +252,9 @@ public class DfsPackFileTest {
assertEquals(packSize - (12 + 20), os.size());
}
}
+
+ private void setObjectSizeIndexMinBytes(int threshold) {
+ db.getConfig().setInt(CONFIG_PACK_SECTION, null,
+ CONFIG_KEY_MIN_BYTES_OBJ_SIZE_INDEX, threshold);
+ }
}
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/dfs/DfsPackParserTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/dfs/DfsPackParserTest.java
new file mode 100644
index 0000000000..845d5fcca1
--- /dev/null
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/dfs/DfsPackParserTest.java
@@ -0,0 +1,69 @@
+/*
+ * 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.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.junit.Assert.assertEquals;
+
+import java.io.IOException;
+
+import org.eclipse.jgit.internal.storage.dfs.DfsObjDatabase.PackList;
+import org.eclipse.jgit.lib.Constants;
+import org.eclipse.jgit.lib.NullProgressMonitor;
+import org.eclipse.jgit.lib.ObjectId;
+import org.eclipse.jgit.lib.ObjectInserter;
+import org.eclipse.jgit.transport.InMemoryPack;
+import org.eclipse.jgit.transport.PackParser;
+import org.junit.Before;
+import org.junit.Test;
+
+public class DfsPackParserTest {
+ private InMemoryRepository repo;
+
+
+ @Before
+ public void setUp() throws Exception {
+ DfsRepositoryDescription desc = new DfsRepositoryDescription("test");
+ repo = new InMemoryRepository(desc);
+ repo.getConfig().setInt(CONFIG_PACK_SECTION, null,
+ CONFIG_KEY_MIN_BYTES_OBJ_SIZE_INDEX, 0);
+ }
+
+ @Test
+ public void parse_writeObjSizeIdx() throws IOException {
+ InMemoryPack pack = new InMemoryPack();
+
+ // Sha1 of the blob "a"
+ ObjectId blobA = ObjectId
+ .fromString("2e65efe2a145dda7ee51d1741299f848e5bf752e");
+
+ pack.header(2);
+ pack.write((Constants.OBJ_BLOB) << 4 | 1);
+ pack.deflate(new byte[] { 'a' });
+
+ pack.write((Constants.OBJ_REF_DELTA) << 4 | 4);
+ pack.copyRaw(blobA);
+ pack.deflate(new byte[] { 0x1, 0x1, 0x1, 'b' });
+ pack.digest();
+
+ try (ObjectInserter ins = repo.newObjectInserter()) {
+ PackParser parser = ins.newPackParser(pack.toInputStream());
+ parser.parse(NullProgressMonitor.INSTANCE,
+ NullProgressMonitor.INSTANCE);
+ ins.flush();
+ }
+
+ DfsReader reader = repo.getObjectDatabase().newReader();
+ PackList packList = repo.getObjectDatabase().getPackList();
+ assertEquals(1, packList.packs.length);
+ assertEquals(1, packList.packs[0].getIndexedObjectSize(reader, blobA));
+ }
+}
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
new file mode 100644
index 0000000000..eb8ceecd81
--- /dev/null
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/dfs/DfsReaderTest.java
@@ -0,0 +1,262 @@
+/*
+ * 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.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.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+import java.io.IOException;
+import java.util.HashMap;
+import java.util.Map;
+
+import org.eclipse.jgit.internal.storage.dfs.DfsObjDatabase.PackSource;
+import org.eclipse.jgit.internal.storage.dfs.DfsReader.PackLoadListener;
+import org.eclipse.jgit.internal.storage.pack.PackExt;
+import org.eclipse.jgit.junit.JGitTestUtil;
+import org.eclipse.jgit.junit.TestRng;
+import org.eclipse.jgit.lib.ObjectId;
+import org.eclipse.jgit.lib.ObjectInserter;
+import org.junit.Before;
+import org.junit.Test;
+
+public class DfsReaderTest {
+ InMemoryRepository db;
+
+ @Before
+ public void setUp() {
+ db = new InMemoryRepository(new DfsRepositoryDescription("test"));
+ }
+
+ @Test
+ public void isNotLargerThan_objAboveThreshold()
+ throws IOException {
+ setObjectSizeIndexMinBytes(100);
+ ObjectId obj = insertBlobWithSize(200);
+ try (DfsReader ctx = db.getObjectDatabase().newReader()) {
+ assertFalse("limit < threshold < obj",
+ ctx.isNotLargerThan(obj, OBJ_BLOB, 50));
+ assertEquals(1, ctx.stats.isNotLargerThanCallCount);
+ assertEquals(1, ctx.stats.objectSizeIndexHit);
+ assertEquals(0, ctx.stats.objectSizeIndexMiss);
+
+ assertFalse("limit = threshold < obj",
+ ctx.isNotLargerThan(obj, OBJ_BLOB, 100));
+ assertEquals(2, ctx.stats.isNotLargerThanCallCount);
+ assertEquals(2, ctx.stats.objectSizeIndexHit);
+ assertEquals(0, ctx.stats.objectSizeIndexMiss);
+
+ assertFalse("threshold < limit < obj",
+ ctx.isNotLargerThan(obj, OBJ_BLOB, 150));
+ assertEquals(3, ctx.stats.isNotLargerThanCallCount);
+ assertEquals(3, ctx.stats.objectSizeIndexHit);
+ assertEquals(0, ctx.stats.objectSizeIndexMiss);
+
+ assertTrue("threshold < limit = obj",
+ ctx.isNotLargerThan(obj, OBJ_BLOB, 200));
+ assertEquals(4, ctx.stats.isNotLargerThanCallCount);
+ assertEquals(4, ctx.stats.objectSizeIndexHit);
+ assertEquals(0, ctx.stats.objectSizeIndexMiss);
+
+ assertTrue("threshold < obj < limit",
+ ctx.isNotLargerThan(obj, OBJ_BLOB, 250));
+ assertEquals(5, ctx.stats.isNotLargerThanCallCount);
+ assertEquals(5, ctx.stats.objectSizeIndexHit);
+ assertEquals(0, ctx.stats.objectSizeIndexMiss);
+ }
+ }
+
+
+ @Test
+ public void isNotLargerThan_objBelowThreshold()
+ throws IOException {
+ setObjectSizeIndexMinBytes(100);
+ insertBlobWithSize(1000); // index not empty
+ ObjectId obj = insertBlobWithSize(50);
+ try (DfsReader ctx = db.getObjectDatabase().newReader()) {
+ assertFalse("limit < obj < threshold",
+ ctx.isNotLargerThan(obj, OBJ_BLOB, 10));
+ assertEquals(1, ctx.stats.isNotLargerThanCallCount);
+ assertEquals(0, ctx.stats.objectSizeIndexHit);
+ assertEquals(1, ctx.stats.objectSizeIndexMiss);
+
+ assertTrue("limit = obj < threshold",
+ ctx.isNotLargerThan(obj, OBJ_BLOB, 50));
+ assertEquals(2, ctx.stats.isNotLargerThanCallCount);
+ assertEquals(0, ctx.stats.objectSizeIndexHit);
+ assertEquals(2, ctx.stats.objectSizeIndexMiss);
+
+ assertTrue("obj < limit < threshold",
+ ctx.isNotLargerThan(obj, OBJ_BLOB, 80));
+ assertEquals(3, ctx.stats.isNotLargerThanCallCount);
+ assertEquals(0, ctx.stats.objectSizeIndexHit);
+ assertEquals(3, ctx.stats.objectSizeIndexMiss);
+
+ assertTrue("obj < limit = threshold",
+ ctx.isNotLargerThan(obj, OBJ_BLOB, 100));
+ assertEquals(4, ctx.stats.isNotLargerThanCallCount);
+ assertEquals(0, ctx.stats.objectSizeIndexHit);
+ assertEquals(4, ctx.stats.objectSizeIndexMiss);
+
+ assertTrue("obj < threshold < limit",
+ ctx.isNotLargerThan(obj, OBJ_BLOB, 120));
+ assertEquals(5, ctx.stats.isNotLargerThanCallCount);
+ assertEquals(0, ctx.stats.objectSizeIndexHit);
+ assertEquals(5, ctx.stats.objectSizeIndexMiss);
+ }
+ }
+
+ @Test
+ public void isNotLargerThan_emptyIdx() throws IOException {
+ setObjectSizeIndexMinBytes(100);
+ ObjectId obj = insertBlobWithSize(10);
+ try (DfsReader ctx = db.getObjectDatabase().newReader()) {
+ assertFalse(ctx.isNotLargerThan(obj, OBJ_BLOB, 0));
+ assertTrue(ctx.isNotLargerThan(obj, OBJ_BLOB, 10));
+ assertTrue(ctx.isNotLargerThan(obj, OBJ_BLOB, 40));
+ assertTrue(ctx.isNotLargerThan(obj, OBJ_BLOB, 50));
+ assertTrue(ctx.isNotLargerThan(obj, OBJ_BLOB, 100));
+
+ assertEquals(5, ctx.stats.isNotLargerThanCallCount);
+ assertEquals(5, ctx.stats.objectSizeIndexMiss);
+ assertEquals(0, ctx.stats.objectSizeIndexHit);
+ }
+ }
+
+ @Test
+ public void isNotLargerThan_noObjectSizeIndex() throws IOException {
+ setObjectSizeIndexMinBytes(-1);
+ ObjectId obj = insertBlobWithSize(10);
+ try (DfsReader ctx = db.getObjectDatabase().newReader()) {
+ assertFalse(ctx.isNotLargerThan(obj, OBJ_BLOB, 0));
+ assertTrue(ctx.isNotLargerThan(obj, OBJ_BLOB, 10));
+ assertTrue(ctx.isNotLargerThan(obj, OBJ_BLOB, 40));
+ assertTrue(ctx.isNotLargerThan(obj, OBJ_BLOB, 50));
+ assertTrue(ctx.isNotLargerThan(obj, OBJ_BLOB, 100));
+
+ assertEquals(5, ctx.stats.isNotLargerThanCallCount);
+ assertEquals(0, ctx.stats.objectSizeIndexMiss);
+ assertEquals(0, ctx.stats.objectSizeIndexHit);
+ }
+ }
+
+ @Test
+ public void packLoadListener_noInvocations() throws IOException {
+ insertBlobWithSize(100);
+ try (DfsReader ctx = db.getObjectDatabase().newReader()) {
+ CounterPackLoadListener listener = new CounterPackLoadListener();
+ ctx.addPackLoadListener(listener);
+ assertEquals(null, listener.callsPerExt.get(PackExt.INDEX));
+ }
+ }
+
+ @Test
+ public void packLoadListener_has_openIdx() throws IOException {
+ ObjectId obj = insertBlobWithSize(100);
+ try (DfsReader ctx = db.getObjectDatabase().newReader()) {
+ CounterPackLoadListener listener = new CounterPackLoadListener();
+ ctx.addPackLoadListener(listener);
+ boolean has = ctx.has(obj);
+ assertTrue(has);
+ 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));
+ }
+ }
+
+ @Test
+ public void packLoadListener_has_openMultipleIndices() throws IOException {
+ setObjectSizeIndexMinBytes(100);
+ insertBlobWithSize(200);
+ insertBlobWithSize(230);
+ insertBlobWithSize(100);
+ try (DfsReader ctx = db.getObjectDatabase().newReader()) {
+ CounterPackLoadListener listener = new CounterPackLoadListener();
+ ctx.addPackLoadListener(listener);
+ 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));
+ }
+ }
+
+
+ @Test
+ public void packLoadListener_has_repeatedCalls_openMultipleIndices() throws IOException {
+ // Two objects NOT in the repo
+ ObjectId oid = ObjectId.fromString("aa48de2aa61d9dffa8a05439dc115fe82f10f129");
+ ObjectId oid2 = ObjectId.fromString("aa48de2aa61d9dffa8a05439dc115fe82f10f130");
+
+ setObjectSizeIndexMinBytes(100);
+ insertBlobWithSize(200);
+ insertBlobWithSize(230);
+ insertBlobWithSize(100);
+ CounterPackLoadListener listener = new CounterPackLoadListener();
+ try (DfsReader ctx = db.getObjectDatabase().newReader()) {
+ ctx.addPackLoadListener(listener);
+ boolean has = ctx.has(oid);
+ ctx.has(oid);
+ ctx.has(oid2);
+ assertFalse(has);
+ // The 3 indices were loaded only once each
+ assertEquals(Integer.valueOf(3), listener.callsPerExt.get(PackExt.INDEX));
+ }
+ }
+
+ private static class CounterPackLoadListener implements PackLoadListener {
+ final Map<PackExt, Integer> callsPerExt = new HashMap<>();
+
+ @SuppressWarnings("boxing")
+ @Override
+ public void onIndexLoad(String packName, PackSource src, PackExt ext, long size,
+ Object loadedIdx) {
+ callsPerExt.merge(ext, 1, Integer::sum);
+ }
+
+ @Override
+ public void onBlockLoad(String packName, PackSource src, PackExt ext,
+ long size, DfsBlockData dfsBlockData) {
+ // empty
+ }
+ }
+
+ private ObjectId insertBlobWithSize(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));
+ ins.flush();
+ }
+ return oid;
+ }
+
+ private void setObjectSizeIndexMinBytes(int threshold) {
+ db.getConfig().setInt(CONFIG_PACK_SECTION, null,
+ CONFIG_KEY_MIN_BYTES_OBJ_SIZE_INDEX, threshold);
+ }
+}
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/FileRepositoryBuilderAfterOpenConfigTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/FileRepositoryBuilderAfterOpenConfigTest.java
index 100bd32ad8..ed5a6990ac 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/FileRepositoryBuilderAfterOpenConfigTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/FileRepositoryBuilderAfterOpenConfigTest.java
@@ -18,7 +18,6 @@ import org.eclipse.jgit.util.SystemReader;
import org.junit.Before;
public class FileRepositoryBuilderAfterOpenConfigTest extends FileRepositoryBuilderTest {
- /** {@inheritDoc} */
@Before
@Override
public void setUp() throws Exception {
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/GcReverseIndexTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/GcReverseIndexTest.java
new file mode 100644
index 0000000000..cbb0943426
--- /dev/null
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/GcReverseIndexTest.java
@@ -0,0 +1,116 @@
+/*
+ * 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.internal.storage.file;
+
+import static org.eclipse.jgit.internal.storage.pack.PackExt.REVERSE_INDEX;
+import static org.junit.Assert.assertArrayEquals;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.InputStream;
+import java.util.Collections;
+
+import org.eclipse.jgit.junit.TestRepository;
+import org.eclipse.jgit.revwalk.RevCommit;
+import org.eclipse.jgit.storage.pack.PackConfig;
+import org.eclipse.jgit.util.IO;
+import org.junit.Test;
+
+public class GcReverseIndexTest extends GcTestCase {
+
+ @Test
+ public void testWriteDefault() throws Exception {
+ PackConfig config = new PackConfig(repo);
+ gc.setPackConfig(config);
+
+ RevCommit tip = commitChain(10);
+ TestRepository.BranchBuilder bb = tr.branch("refs/heads/main");
+ bb.update(tip);
+
+ gc.gc().get();
+ assertRidxDoesNotExist(repo);
+ }
+
+ @Test
+ public void testWriteDisabled() throws Exception {
+ PackConfig config = new PackConfig(repo);
+ config.setWriteReverseIndex(false);
+ gc.setPackConfig(config);
+
+ RevCommit tip = commitChain(10);
+ TestRepository.BranchBuilder bb = tr.branch("refs/heads/main");
+ bb.update(tip);
+
+ gc.gc().get();
+ assertRidxDoesNotExist(repo);
+ }
+
+ @Test
+ public void testWriteEmptyRepo() throws Exception {
+ PackConfig config = new PackConfig(repo);
+ config.setWriteReverseIndex(true);
+ gc.setPackConfig(config);
+
+ gc.gc().get();
+ assertRidxDoesNotExist(repo);
+ }
+
+ @Test
+ public void testWriteShallowRepo() throws Exception {
+ PackConfig config = new PackConfig(repo);
+ config.setWriteReverseIndex(true);
+ gc.setPackConfig(config);
+
+ RevCommit tip = commitChain(2);
+ TestRepository.BranchBuilder bb = tr.branch("refs/heads/main");
+ bb.update(tip);
+ repo.getObjectDatabase().setShallowCommits(Collections.singleton(tip));
+
+ gc.gc().get();
+ assertValidRidxExists(repo);
+ }
+
+ @Test
+ public void testWriteEnabled() throws Exception {
+ PackConfig config = new PackConfig(repo);
+ config.setWriteReverseIndex(true);
+ gc.setPackConfig(config);
+
+ RevCommit tip = commitChain(10);
+ TestRepository.BranchBuilder bb = tr.branch("refs/heads/main");
+ bb.update(tip);
+
+ gc.gc().get();
+ assertValidRidxExists(repo);
+ }
+
+ private static void assertValidRidxExists(FileRepository repo)
+ throws Exception {
+ PackFile packFile = repo.getObjectDatabase().getPacks().iterator()
+ .next().getPackFile();
+ File file = packFile.create(REVERSE_INDEX);
+ assertTrue(file.exists());
+ try (InputStream os = new FileInputStream(file)) {
+ byte[] magic = new byte[4];
+ IO.readFully(os, magic, 0, 4);
+ assertArrayEquals(new byte[] { 'R', 'I', 'D', 'X' }, magic);
+ }
+ }
+
+ private static void assertRidxDoesNotExist(FileRepository repo) {
+ File packDir = repo.getObjectDatabase().getPackDirectory();
+ String[] reverseIndexFilenames = packDir.list(
+ (dir, name) -> name.endsWith(REVERSE_INDEX.getExtension()));
+ assertEquals(0, reverseIndexFilenames.length);
+ }
+}
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/PackReverseIndexComputedTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/PackReverseIndexComputedTest.java
new file mode 100644
index 0000000000..ea5aaf5dd4
--- /dev/null
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/PackReverseIndexComputedTest.java
@@ -0,0 +1,108 @@
+/*
+ * Copyright (C) 2008, Imran M Yousuf <imyousuf@smartitengineering.com>
+ * Copyright (C) 2008, Marek Zawirski <marek.zawirski@gmail.com> 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.internal.storage.file;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+
+import org.eclipse.jgit.errors.CorruptObjectException;
+import org.eclipse.jgit.errors.PackMismatchException;
+import org.eclipse.jgit.internal.storage.file.PackIndex.MutableEntry;
+import org.eclipse.jgit.junit.JGitTestUtil;
+import org.eclipse.jgit.junit.RepositoryTestCase;
+import org.junit.Before;
+import org.junit.Test;
+
+public class PackReverseIndexComputedTest extends RepositoryTestCase {
+
+ private PackIndex idx;
+
+ private PackReverseIndex reverseIdx;
+
+ /**
+ * Set up tested class instance, test constructor by the way.
+ */
+ @Override
+ @Before
+ public void setUp() throws Exception {
+ super.setUp();
+ // index with both small (< 2^31) and big offsets
+ idx = PackIndex.open(JGitTestUtil.getTestResourceFile("pack-huge.idx"));
+ reverseIdx = PackReverseIndexFactory.computeFromIndex(idx);
+ }
+
+ /**
+ * Test findObject() for all index entries.
+ */
+ @Test
+ public void testFindObject() {
+ for (MutableEntry me : idx)
+ assertEquals(me.toObjectId(), reverseIdx.findObject(me.getOffset()));
+ }
+
+ /**
+ * Test findObject() with illegal argument.
+ */
+ @Test
+ public void testFindObjectWrongOffset() {
+ assertNull(reverseIdx.findObject(0));
+ }
+
+ /**
+ * Test findNextOffset() for all index entries.
+ *
+ * @throws CorruptObjectException
+ */
+ @Test
+ public void testFindNextOffset() throws CorruptObjectException {
+ long offset = findFirstOffset();
+ assertTrue(offset > 0);
+ for (int i = 0; i < idx.getObjectCount(); i++) {
+ long newOffset = reverseIdx.findNextOffset(offset, Long.MAX_VALUE);
+ assertTrue(newOffset > offset);
+ if (i == idx.getObjectCount() - 1)
+ assertEquals(newOffset, Long.MAX_VALUE);
+ else
+ assertEquals(newOffset, idx.findOffset(reverseIdx
+ .findObject(newOffset)));
+ offset = newOffset;
+ }
+ }
+
+ /**
+ * Test findNextOffset() with wrong illegal argument as offset.
+ */
+ @Test
+ public void testFindNextOffsetWrongOffset() {
+ try {
+ reverseIdx.findNextOffset(0, Long.MAX_VALUE);
+ fail("findNextOffset() should throw exception");
+ } catch (CorruptObjectException x) {
+ // expected
+ }
+ }
+
+ @Test
+ public void testVerifyChecksum() throws PackMismatchException {
+ // ComputedReverseIndex doesn't have a file containing a checksum.
+ reverseIdx.verifyPackChecksum(null);
+ }
+
+ private long findFirstOffset() {
+ long min = Long.MAX_VALUE;
+ for (MutableEntry me : idx)
+ min = Math.min(min, me.getOffset());
+ return min;
+ }
+}
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/PackReverseIndexTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/PackReverseIndexTest.java
index 292e3e758a..f8fb4c15e7 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/PackReverseIndexTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/PackReverseIndexTest.java
@@ -1,6 +1,5 @@
/*
- * Copyright (C) 2008, Imran M Yousuf <imyousuf@smartitengineering.com>
- * Copyright (C) 2008, Marek Zawirski <marek.zawirski@gmail.com> and others
+ * Copyright (C) 2022, 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
@@ -8,95 +7,94 @@
*
* SPDX-License-Identifier: BSD-3-Clause
*/
-
package org.eclipse.jgit.internal.storage.file;
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertThrows;
import static org.junit.Assert.assertTrue;
-import static org.junit.Assert.fail;
-import org.eclipse.jgit.errors.CorruptObjectException;
-import org.eclipse.jgit.internal.storage.file.PackIndex.MutableEntry;
+import java.io.ByteArrayInputStream;
+import java.io.File;
+import java.io.IOException;
+
+import org.eclipse.jgit.internal.storage.pack.PackExt;
import org.eclipse.jgit.junit.JGitTestUtil;
-import org.eclipse.jgit.junit.RepositoryTestCase;
-import org.junit.Before;
import org.junit.Test;
-public class PackReverseIndexTest extends RepositoryTestCase {
+public class PackReverseIndexTest {
- private PackIndex idx;
-
- private PackReverseIndex reverseIdx;
+ @Test
+ public void open_fallbackToComputed() throws IOException {
+ String noRevFilePrefix = "pack-3280af9c07ee18a87705ef50b0cc4cd20266cf12.";
+ PackReverseIndex computed = PackReverseIndexFactory.openOrCompute(
+ getResourceFileFor(noRevFilePrefix, PackExt.REVERSE_INDEX), 7,
+ () -> PackIndex.open(
+ getResourceFileFor(noRevFilePrefix, PackExt.INDEX)));
- /**
- * Set up tested class instance, test constructor by the way.
- */
- @Override
- @Before
- public void setUp() throws Exception {
- super.setUp();
- // index with both small (< 2^31) and big offsets
- idx = PackIndex.open(JGitTestUtil.getTestResourceFile(
- "pack-huge.idx"));
- reverseIdx = new PackReverseIndex(idx);
+ assertTrue(computed instanceof PackReverseIndexComputed);
}
- /**
- * Test findObject() for all index entries.
- */
@Test
- public void testFindObject() {
- for (MutableEntry me : idx)
- assertEquals(me.toObjectId(), reverseIdx.findObject(me.getOffset()));
+ public void open_readGoodFile() throws IOException {
+ String hasRevFilePrefix = "pack-cbdeda40019ae0e6e789088ea0f51f164f489d14.";
+ PackReverseIndex version1 = PackReverseIndexFactory.openOrCompute(
+ getResourceFileFor(hasRevFilePrefix, PackExt.REVERSE_INDEX), 6,
+ () -> PackIndex.open(
+ getResourceFileFor(hasRevFilePrefix, PackExt.INDEX)));
+
+ assertTrue(version1 instanceof PackReverseIndexV1);
}
- /**
- * Test findObject() with illegal argument.
- */
@Test
- public void testFindObjectWrongOffset() {
- assertNull(reverseIdx.findObject(0));
+ public void open_readCorruptFile() {
+ String hasRevFilePrefix = "pack-cbdeda40019ae0e6e789088ea0f51f164f489d14.";
+
+ assertThrows(IOException.class,
+ () -> PackReverseIndexFactory.openOrCompute(
+ getResourceFileFor(hasRevFilePrefix + "corrupt.",
+ PackExt.REVERSE_INDEX),
+ 6, () -> PackIndex.open(getResourceFileFor(
+ hasRevFilePrefix, PackExt.INDEX))));
}
- /**
- * Test findNextOffset() for all index entries.
- *
- * @throws CorruptObjectException
- */
@Test
- public void testFindNextOffset() throws CorruptObjectException {
- long offset = findFirstOffset();
- assertTrue(offset > 0);
- for (int i = 0; i < idx.getObjectCount(); i++) {
- long newOffset = reverseIdx.findNextOffset(offset, Long.MAX_VALUE);
- assertTrue(newOffset > offset);
- if (i == idx.getObjectCount() - 1)
- assertEquals(newOffset, Long.MAX_VALUE);
- else
- assertEquals(newOffset, idx.findOffset(reverseIdx
- .findObject(newOffset)));
- offset = newOffset;
- }
+ public void read_badMagic() {
+ byte[] badMagic = new byte[] { 'R', 'B', 'A', 'D', // magic
+ 0x00, 0x00, 0x00, 0x01, // file version
+ 0x00, 0x00, 0x00, 0x01, // oid version
+ // pack checksum
+ 'P', 'A', 'C', 'K', 'C', 'H', 'E', 'C', 'K', 'S', 'U', 'M', '3',
+ '4', '5', '6', '7', '8', '9', '0',
+ // checksum
+ 0x66, 0x01, (byte) 0xbc, (byte) 0xe8, 0x51, 0x4b, 0x2f,
+ (byte) 0xa1, (byte) 0xa9, (byte) 0xcd, (byte) 0xbe, (byte) 0xd6,
+ 0x4f, (byte) 0xa8, 0x7d, (byte) 0xab, 0x50, (byte) 0xa3,
+ (byte) 0xf7, (byte) 0xcc, };
+ ByteArrayInputStream in = new ByteArrayInputStream(badMagic);
+
+ assertThrows(IOException.class,
+ () -> PackReverseIndexFactory.readFromFile(in, 0, () -> null));
}
- /**
- * Test findNextOffset() with wrong illegal argument as offset.
- */
@Test
- public void testFindNextOffsetWrongOffset() {
- try {
- reverseIdx.findNextOffset(0, Long.MAX_VALUE);
- fail("findNextOffset() should throw exception");
- } catch (CorruptObjectException x) {
- // expected
- }
+ public void read_unsupportedVersion2() {
+ byte[] version2 = new byte[] { 'R', 'I', 'D', 'X', // magic
+ 0x00, 0x00, 0x00, 0x02, // file version
+ 0x00, 0x00, 0x00, 0x01, // oid version
+ // pack checksum
+ 'P', 'A', 'C', 'K', 'C', 'H', 'E', 'C', 'K', 'S', 'U', 'M', '3',
+ '4', '5', '6', '7', '8', '9', '0',
+ // checksum
+ 0x70, 0x17, 0x10, 0x51, (byte) 0xfe, (byte) 0xab, (byte) 0x9b,
+ 0x68, (byte) 0xed, 0x3a, 0x3f, 0x27, 0x1d, (byte) 0xce,
+ (byte) 0xff, 0x38, 0x09, (byte) 0x9b, 0x29, 0x58, };
+ ByteArrayInputStream in = new ByteArrayInputStream(version2);
+
+ assertThrows(IOException.class,
+ () -> PackReverseIndexFactory.readFromFile(in, 0, () -> null));
}
- private long findFirstOffset() {
- long min = Long.MAX_VALUE;
- for (MutableEntry me : idx)
- min = Math.min(min, me.getOffset());
- return min;
+ private File getResourceFileFor(String packFilePrefix, PackExt ext) {
+ return JGitTestUtil
+ .getTestResourceFile(packFilePrefix + ext.getExtension());
}
}
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/PackReverseIndexV1Test.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/PackReverseIndexV1Test.java
new file mode 100644
index 0000000000..38b28b501b
--- /dev/null
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/PackReverseIndexV1Test.java
@@ -0,0 +1,289 @@
+/*
+ * Copyright (C) 2022, 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.internal.storage.file;
+
+import static org.eclipse.jgit.lib.Constants.OBJ_BLOB;
+import static org.eclipse.jgit.lib.Constants.OBJ_COMMIT;
+import static org.eclipse.jgit.lib.Constants.OBJ_TREE;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertThrows;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+import java.io.ByteArrayInputStream;
+import java.io.IOException;
+
+import org.eclipse.jgit.errors.CorruptObjectException;
+import org.eclipse.jgit.errors.PackMismatchException;
+import org.eclipse.jgit.junit.JGitTestUtil;
+import org.eclipse.jgit.lib.ObjectId;
+import org.eclipse.jgit.transport.PackedObjectInfo;
+import org.junit.Before;
+import org.junit.Test;
+
+public class PackReverseIndexV1Test {
+ private static final byte[] FAKE_PACK_CHECKSUM = new byte[] { 'P', 'A', 'C',
+ 'K', 'C', 'H', 'E', 'C', 'K', 'S', 'U', 'M', '3', '4', '5', '6',
+ '7', '8', '9', '0', };
+
+ private static final byte[] NO_OBJECTS = new byte[] { 'R', 'I', 'D', 'X', // magic
+ 0x00, 0x00, 0x00, 0x01, // file version
+ 0x00, 0x00, 0x00, 0x01, // oid version
+ // pack checksum to copy into at byte 12
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ // checksum
+ (byte) 0xd1, 0x1d, 0x17, (byte) 0xd5, (byte) 0xa1, 0x5c,
+ (byte) 0x8f, 0x45, 0x7e, 0x06, (byte) 0x91, (byte) 0xf2, 0x7e, 0x20,
+ 0x35, 0x2c, (byte) 0xdc, 0x4c, 0x46, (byte) 0xe4, };
+
+ private static final byte[] SMALL_PACK_CHECKSUM = new byte[] { (byte) 0xbb,
+ 0x1d, 0x25, 0x3d, (byte) 0xd3, (byte) 0xf0, 0x08, 0x75, (byte) 0xc8,
+ 0x04, (byte) 0xd0, 0x6f, 0x73, (byte) 0xe9, 0x00, (byte) 0x82,
+ (byte) 0xdb, 0x09, (byte) 0xc8, 0x13, };
+
+ private static final byte[] SMALL_CONTENTS = new byte[] { 'R', 'I', 'D',
+ 'X', // magic
+ 0x00, 0x00, 0x00, 0x01, // file version
+ 0x00, 0x00, 0x00, 0x01, // oid version
+ 0x00, 0x00, 0x00, 0x04, // offset 12: "68" -> index @ 4
+ 0x00, 0x00, 0x00, 0x02, // offset 165: "5c" -> index @ 2
+ 0x00, 0x00, 0x00, 0x03, // offset 257: "62" -> index @ 3
+ 0x00, 0x00, 0x00, 0x01, // offset 450: "58" -> index @ 1
+ 0x00, 0x00, 0x00, 0x05, // offset 556: "c5" -> index @ 5
+ 0x00, 0x00, 0x00, 0x00, // offset 614: "2d" -> index @ 0
+ // pack checksum to copy into at byte 36
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ // checksum
+ (byte) 0xf0, 0x6d, 0x03, (byte) 0xd7, 0x6f, (byte) 0x9f,
+ (byte) 0xc1, 0x36, 0x26, (byte) 0xbc, (byte) 0xcb, 0x75, 0x36,
+ (byte) 0xa1, 0x26, 0x6a, 0x2b, (byte) 0x84, 0x16, (byte) 0x83, };
+
+ private PackReverseIndex emptyReverseIndex;
+
+ /**
+ * Reverse index for the pack-cbdeda40019ae0e6e789088ea0f51f164f489d14.idx
+ * with contents `SHA-1 type size size-in-packfile offset-in-packfile` as
+ * shown by `verify-pack`:
+ * 2d04ee74dba30078c2dcdb713ddb8be4bc084d76 blob 8 17 614
+ * 58728c938a9a8b9970cc09236caf94ada4689923 blob 140 106 450
+ * 5ce00008cf3fb8f194f52742020bd40d78f3f1b3 commit 81 92 165 1 68cb1f232964f3cd698afc1dafe583937203c587
+ * 62299a7ae290d685196e948a2fcb7d8c07f95c7d tree 198 193 257
+ * 68cb1f232964f3cd698afc1dafe583937203c587 commit 220 153 12
+ * c5ab27309491cf641eb11bb4b7a78641f280b482 tree 46 58 556 1 62299a7ae290d685196e948a2fcb7d8c07f95c7d
+ */
+ private PackReverseIndex smallReverseIndex;
+
+ private final PackedObjectInfo object614 = objectInfo(
+ "2d04ee74dba30078c2dcdb713ddb8be4bc084d76", OBJ_BLOB, 614);
+
+ private final PackedObjectInfo object450 = objectInfo(
+ "58728c938a9a8b9970cc09236caf94ada4689923", OBJ_BLOB, 450);
+
+ private final PackedObjectInfo object165 = objectInfo(
+ "5ce00008cf3fb8f194f52742020bd40d78f3f1b3", OBJ_COMMIT, 165);
+
+ private final PackedObjectInfo object257 = objectInfo(
+ "62299a7ae290d685196e948a2fcb7d8c07f95c7d", OBJ_TREE, 257);
+
+ private final PackedObjectInfo object12 = objectInfo(
+ "68cb1f232964f3cd698afc1dafe583937203c587", OBJ_COMMIT, 12);
+
+ private final PackedObjectInfo object556 = objectInfo(
+ "c5ab27309491cf641eb11bb4b7a78641f280b482", OBJ_TREE, 556);
+
+ // last object's offset + last object's length
+ private final long smallMaxOffset = 631;
+
+ @Before
+ public void setUp() throws Exception {
+ System.arraycopy(SMALL_PACK_CHECKSUM, 0, SMALL_CONTENTS, 36,
+ SMALL_PACK_CHECKSUM.length);
+ ByteArrayInputStream smallIn = new ByteArrayInputStream(SMALL_CONTENTS);
+ smallReverseIndex = PackReverseIndexFactory.readFromFile(smallIn, 6,
+ () -> PackIndex.open(JGitTestUtil.getTestResourceFile(
+ "pack-cbdeda40019ae0e6e789088ea0f51f164f489d14.idx")));
+
+ System.arraycopy(FAKE_PACK_CHECKSUM, 0, NO_OBJECTS, 12,
+ FAKE_PACK_CHECKSUM.length);
+ ByteArrayInputStream emptyIn = new ByteArrayInputStream(NO_OBJECTS);
+ emptyReverseIndex = PackReverseIndexFactory.readFromFile(emptyIn, 0,
+ () -> null);
+ }
+
+ @Test
+ public void read_unsupportedOidSHA256() {
+ byte[] version2 = new byte[] { 'R', 'I', 'D', 'X', // magic
+ 0x00, 0x00, 0x00, 0x01, // file version
+ 0x00, 0x00, 0x00, 0x02, // oid version
+ // pack checksum to copy into at byte 12
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ // checksum
+ 0x6e, 0x78, 0x75, 0x67, (byte) 0x84, (byte) 0x89, (byte) 0xde,
+ (byte) 0xe3, (byte) 0x86, 0x6a, 0x3b, (byte) 0x98, 0x51,
+ (byte) 0xd8, (byte) 0x8c, (byte) 0xec, 0x50, (byte) 0xe7,
+ (byte) 0xfb, 0x22, };
+ System.arraycopy(FAKE_PACK_CHECKSUM, 0, version2, 12,
+ FAKE_PACK_CHECKSUM.length);
+ ByteArrayInputStream in = new ByteArrayInputStream(version2);
+
+ assertThrows(IOException.class,
+ () -> PackReverseIndexFactory.readFromFile(in, 0, () -> null));
+ }
+
+ @Test
+ public void read_objectCountTooLarge() {
+ ByteArrayInputStream dummyInput = new ByteArrayInputStream(NO_OBJECTS);
+ long biggerThanInt = ((long) Integer.MAX_VALUE) + 1;
+
+ assertThrows(IllegalArgumentException.class,
+ () -> PackReverseIndexFactory.readFromFile(dummyInput,
+ biggerThanInt,
+ () -> null));
+ }
+
+ @Test
+ public void read_incorrectChecksum() {
+ byte[] badChecksum = new byte[] { 'R', 'I', 'D', 'X', // magic
+ 0x00, 0x00, 0x00, 0x01, // file version
+ 0x00, 0x00, 0x00, 0x01, // oid version
+ // pack checksum to copy into at byte 12
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ // checksum
+ (byte) 0xf2, 0x1a, 0x1a, (byte) 0xaa, 0x32, 0x2d, (byte) 0xb9,
+ (byte) 0xfd, 0x0f, (byte) 0xa5, 0x4c, (byte) 0xea, (byte) 0xcf,
+ (byte) 0xbb, (byte) 0x99, (byte) 0xde, (byte) 0xd3, 0x4e,
+ (byte) 0xb1, (byte) 0xee, // would be 0x74 if correct
+ };
+ System.arraycopy(FAKE_PACK_CHECKSUM, 0, badChecksum, 12,
+ FAKE_PACK_CHECKSUM.length);
+ ByteArrayInputStream in = new ByteArrayInputStream(badChecksum);
+ assertThrows(CorruptObjectException.class,
+ () -> PackReverseIndexFactory.readFromFile(in, 0, () -> null));
+ }
+
+ @Test
+ public void findObject_noObjects() {
+ assertNull(emptyReverseIndex.findObject(0));
+ }
+
+ @Test
+ public void findObject_multipleObjects() {
+ assertEquals(object614, smallReverseIndex.findObject(614));
+ assertEquals(object450, smallReverseIndex.findObject(450));
+ assertEquals(object165, smallReverseIndex.findObject(165));
+ assertEquals(object257, smallReverseIndex.findObject(257));
+ assertEquals(object12, smallReverseIndex.findObject(12));
+ assertEquals(object556, smallReverseIndex.findObject(556));
+ }
+
+ @Test
+ public void findObject_badOffset() {
+ assertNull(smallReverseIndex.findObject(0));
+ }
+
+ @Test
+ public void findNextOffset_noObjects() {
+ assertThrows(IOException.class,
+ () -> emptyReverseIndex.findNextOffset(0, Long.MAX_VALUE));
+ }
+
+ @Test
+ public void findNextOffset_multipleObjects() throws CorruptObjectException {
+ assertEquals(smallMaxOffset,
+ smallReverseIndex.findNextOffset(614, smallMaxOffset));
+ assertEquals(614,
+ smallReverseIndex.findNextOffset(556, smallMaxOffset));
+ assertEquals(556,
+ smallReverseIndex.findNextOffset(450, smallMaxOffset));
+ assertEquals(450,
+ smallReverseIndex.findNextOffset(257, smallMaxOffset));
+ assertEquals(257,
+ smallReverseIndex.findNextOffset(165, smallMaxOffset));
+ assertEquals(165, smallReverseIndex.findNextOffset(12, smallMaxOffset));
+ }
+
+ @Test
+ public void findNextOffset_badOffset() {
+ assertThrows(IOException.class,
+ () -> smallReverseIndex.findNextOffset(0, Long.MAX_VALUE));
+ }
+
+ @Test
+ public void findPosition_noObjects() {
+ assertEquals(-1, emptyReverseIndex.findPosition(0));
+ }
+
+ @Test
+ public void findPosition_multipleObjects() {
+ assertEquals(0, smallReverseIndex.findPosition(12));
+ assertEquals(1, smallReverseIndex.findPosition(165));
+ assertEquals(2, smallReverseIndex.findPosition(257));
+ assertEquals(3, smallReverseIndex.findPosition(450));
+ assertEquals(4, smallReverseIndex.findPosition(556));
+ assertEquals(5, smallReverseIndex.findPosition(614));
+ }
+
+ @Test
+ public void findPosition_badOffset() {
+ assertEquals(-1, smallReverseIndex.findPosition(10));
+ }
+
+ @Test
+ public void findObjectByPosition_noObjects() {
+ assertThrows(AssertionError.class,
+ () -> emptyReverseIndex.findObjectByPosition(0));
+ }
+
+ @Test
+ public void findObjectByPosition_multipleObjects() {
+ assertEquals(object12, smallReverseIndex.findObjectByPosition(0));
+ assertEquals(object165, smallReverseIndex.findObjectByPosition(1));
+ assertEquals(object257, smallReverseIndex.findObjectByPosition(2));
+ assertEquals(object450, smallReverseIndex.findObjectByPosition(3));
+ assertEquals(object556, smallReverseIndex.findObjectByPosition(4));
+ assertEquals(object614, smallReverseIndex.findObjectByPosition(5));
+ }
+
+ @Test
+ public void findObjectByPosition_badOffset() {
+ assertThrows(AssertionError.class,
+ () -> smallReverseIndex.findObjectByPosition(10));
+ }
+
+ @Test
+ public void verifyChecksum_match() throws IOException {
+ smallReverseIndex.verifyPackChecksum("smallPackFilePath");
+ }
+
+ @Test
+ public void verifyChecksum_mismatch() throws IOException {
+ ByteArrayInputStream in = new ByteArrayInputStream(NO_OBJECTS);
+ PackIndex mockForwardIndex = mock(PackIndex.class);
+ when(mockForwardIndex.getChecksum()).thenReturn(
+ new byte[] { 'D', 'I', 'F', 'F', 'P', 'A', 'C', 'K', 'C', 'H',
+ 'E', 'C', 'K', 'S', 'U', 'M', '7', '8', '9', '0', });
+ PackReverseIndex reverseIndex = PackReverseIndexFactory.readFromFile(in,
+ 0,
+ () -> mockForwardIndex);
+
+ assertThrows(PackMismatchException.class,
+ () -> reverseIndex.verifyPackChecksum("packFilePath"));
+ }
+
+ private static PackedObjectInfo objectInfo(String objectId, int type,
+ long offset) {
+ PackedObjectInfo objectInfo = new PackedObjectInfo(
+ ObjectId.fromString(objectId));
+ objectInfo.setType(type);
+ objectInfo.setOffset(offset);
+ return objectInfo;
+ }
+}
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/PackReverseIndexV1WriteReadTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/PackReverseIndexV1WriteReadTest.java
new file mode 100644
index 0000000000..372a4c7cba
--- /dev/null
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/PackReverseIndexV1WriteReadTest.java
@@ -0,0 +1,119 @@
+/*
+ * Copyright (C) 2022, 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.internal.storage.file;
+
+import static org.eclipse.jgit.lib.Constants.OBJECT_ID_STRING_LENGTH;
+import static org.eclipse.jgit.lib.Constants.OBJ_BLOB;
+import static org.eclipse.jgit.lib.Constants.OBJ_COMMIT;
+import static org.eclipse.jgit.lib.Constants.OBJ_TREE;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertThrows;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
+
+import org.eclipse.jgit.lib.ObjectId;
+import org.eclipse.jgit.transport.PackedObjectInfo;
+import org.junit.Test;
+
+public class PackReverseIndexV1WriteReadTest {
+
+ private static byte[] PACK_CHECKSUM = new byte[] { 'P', 'A', 'C', 'K', 'C',
+ 'H', 'E', 'C', 'K', 'S', 'U', 'M', '3', '4', '5', '6', '7', '8',
+ '9', '0', };
+
+ @Test
+ public void writeThenRead_noObjects() throws IOException {
+ ByteArrayOutputStream out = new ByteArrayOutputStream();
+ PackReverseIndexWriter writer = PackReverseIndexWriter.createWriter(out,
+ 1);
+ List<PackedObjectInfo> objectsSortedByName = new ArrayList<>();
+
+ // write
+ writer.write(objectsSortedByName, PACK_CHECKSUM);
+
+ ByteArrayInputStream in = new ByteArrayInputStream(out.toByteArray());
+
+ // read
+ PackReverseIndex noObjectsReverseIndex = PackReverseIndexFactory
+ .readFromFile(in, 0, () -> null);
+
+ // use
+ assertThrows(AssertionError.class,
+ () -> noObjectsReverseIndex.findObjectByPosition(0));
+ }
+
+ @Test
+ public void writeThenRead_oneObject() throws IOException {
+ ByteArrayOutputStream out = new ByteArrayOutputStream();
+ PackReverseIndexWriter writer = PackReverseIndexWriter.createWriter(out,
+ 1);
+ PackedObjectInfo a = objectInfo("a", OBJ_COMMIT, 0);
+ List<PackedObjectInfo> objectsSortedByName = List.of(a);
+
+ // write
+ writer.write(objectsSortedByName, PACK_CHECKSUM);
+
+ ByteArrayInputStream in = new ByteArrayInputStream(out.toByteArray());
+ PackIndex mockForwardIndex = mock(PackIndex.class);
+ when(mockForwardIndex.getObjectId(0)).thenReturn(a);
+
+ // read
+ PackReverseIndex oneObjectReverseIndex = PackReverseIndexFactory
+ .readFromFile(in, 1, () -> mockForwardIndex);
+
+ // use
+ assertEquals(a, oneObjectReverseIndex.findObjectByPosition(0));
+ }
+
+ @Test
+ public void writeThenRead_multipleObjectsLargeOffsets() throws IOException {
+ ByteArrayOutputStream out = new ByteArrayOutputStream();
+ PackReverseIndexWriter writer = PackReverseIndexWriter.createWriter(out,
+ 1);
+ PackedObjectInfo a = objectInfo("a", OBJ_BLOB, 200000000);
+ PackedObjectInfo b = objectInfo("b", OBJ_COMMIT, 0);
+ PackedObjectInfo c = objectInfo("c", OBJ_COMMIT, 52000000000L);
+ PackedObjectInfo d = objectInfo("d", OBJ_TREE, 7);
+ PackedObjectInfo e = objectInfo("e", OBJ_COMMIT, 38000000000L);
+ List<PackedObjectInfo> objectsSortedByName = List.of(a, b, c, d, e);
+
+ writer.write(objectsSortedByName, PACK_CHECKSUM);
+
+ // write
+ writer.write(objectsSortedByName, PACK_CHECKSUM);
+
+ ByteArrayInputStream in = new ByteArrayInputStream(out.toByteArray());
+ PackIndex mockForwardIndex = mock(PackIndex.class);
+ when(mockForwardIndex.getObjectId(4)).thenReturn(e);
+
+ // read
+ PackReverseIndex multipleObjectsReverseIndex = PackReverseIndexFactory
+ .readFromFile(in, 5, () -> mockForwardIndex);
+
+ // use with minimal mocked forward index use
+ assertEquals(e, multipleObjectsReverseIndex.findObjectByPosition(3));
+ }
+
+ private static PackedObjectInfo objectInfo(String objectId, int type,
+ long offset) {
+ assert (objectId.length() == 1);
+ PackedObjectInfo objectInfo = new PackedObjectInfo(
+ ObjectId.fromString(objectId.repeat(OBJECT_ID_STRING_LENGTH)));
+ objectInfo.setType(type);
+ objectInfo.setOffset(offset);
+ return objectInfo;
+ }
+}
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/PackTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/PackTest.java
index a3596541fe..e1509456e5 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/PackTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/PackTest.java
@@ -261,7 +261,7 @@ public class PackTest extends LocalDiskRepositoryTestCase {
new PackIndexWriterV1(f).write(list, footer);
}
- Pack pack = new Pack(packName, null);
+ Pack pack = new Pack(repo.getConfig(), packName, null);
try {
pack.get(wc, b);
fail("expected LargeObjectException.ExceedsByteArrayLimit");
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/RefDirectoryAfterOpenConfigTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/RefDirectoryAfterOpenConfigTest.java
index 42304e2253..3ea4a167cb 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/RefDirectoryAfterOpenConfigTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/RefDirectoryAfterOpenConfigTest.java
@@ -17,7 +17,6 @@ import org.eclipse.jgit.lib.StoredConfig;
import org.eclipse.jgit.util.SystemReader;
public class RefDirectoryAfterOpenConfigTest extends RefDirectoryTest {
- /** {@inheritDoc} */
@Override
public void refDirectorySetup() throws Exception {
StoredConfig userConfig = SystemReader.getInstance().getUserConfig();
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/SnapshottingRefDirectoryTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/SnapshottingRefDirectoryTest.java
index c3dafe4aa2..90a2aa601e 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/SnapshottingRefDirectoryTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/SnapshottingRefDirectoryTest.java
@@ -23,7 +23,6 @@ import static org.junit.Assert.assertEquals;
public class SnapshottingRefDirectoryTest extends RefDirectoryTest {
private RefDirectory originalRefDirectory;
- /** {@inheritDoc} */
@Before
@Override
public void setUp() throws Exception {
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/CommitTemplateConfigTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/CommitTemplateConfigTest.java
index 42bafb60ca..3ccd0ef021 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/CommitTemplateConfigTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/CommitTemplateConfigTest.java
@@ -17,7 +17,9 @@ import java.io.IOException;
import org.eclipse.jgit.errors.ConfigInvalidException;
import org.eclipse.jgit.junit.JGitTestUtil;
+import org.eclipse.jgit.junit.LocalDiskRepositoryTestCase;
import org.eclipse.jgit.storage.file.FileRepositoryBuilder;
+import org.eclipse.jgit.util.FS;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.TemporaryFolder;
@@ -27,7 +29,7 @@ import org.junit.rules.TemporaryFolder;
* test using bazel which doesn't allow tests to create files in the home
* directory
*/
-public class CommitTemplateConfigTest {
+public class CommitTemplateConfigTest extends LocalDiskRepositoryTestCase {
@Rule
public TemporaryFolder tmp = new TemporaryFolder();
@@ -42,9 +44,11 @@ public class CommitTemplateConfigTest {
String templateContent = "content of the template";
JGitTestUtil.write(tempFile, templateContent);
// proper evaluation of the ~/ directory
- String homeDir = System.getProperty("user.home");
+ File homeDir = FS.DETECTED.userHome();
File tempFileInHomeDirectory = File.createTempFile("fileInHomeFolder",
- ".tmp", new File(homeDir));
+ ".tmp", homeDir);
+ // The home directory should be a mocked temporary directory, but
+ // still...
tempFileInHomeDirectory.deleteOnExit();
JGitTestUtil.write(tempFileInHomeDirectory, templateContent);
String expectedTemplatePath = "~/" + tempFileInHomeDirectory.getName();
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/ConfigTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/ConfigTest.java
index 8f9d105319..36cf77bb01 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/ConfigTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/ConfigTest.java
@@ -1482,7 +1482,9 @@ public class ConfigTest {
File workTree = tmp.newFolder("dummy-worktree");
File tempFile = tmp.newFile("testCommitTemplate-");
- Repository repo = FileRepositoryBuilder.create(workTree);
+ Repository repo = FileRepositoryBuilder
+ .create(new File(workTree, ".git"));
+ repo.create();
String templateContent = "content of the template";
JGitTestUtil.write(tempFile, templateContent);
String expectedTemplatePath = tempFile.getPath();
@@ -1532,7 +1534,9 @@ public class ConfigTest {
throws ConfigInvalidException, IOException {
Config config = new Config(null);
File workTree = tmp.newFolder("dummy-worktree");
- Repository repo = FileRepositoryBuilder.create(workTree);
+ Repository repo = FileRepositoryBuilder
+ .create(new File(workTree, ".git"));
+ repo.create();
File tempFile = tmp.newFile("testCommitTemplate-");
String templateContent = "content of the template";
JGitTestUtil.write(tempFile, templateContent);
@@ -1554,7 +1558,9 @@ public class ConfigTest {
Config config = new Config(null);
File workTree = tmp.newFolder("dummy-worktree");
File tempFile = tmp.newFile("testCommitTemplate-");
- Repository repo = FileRepositoryBuilder.create(workTree);
+ Repository repo = FileRepositoryBuilder
+ .create(new File(workTree, ".git"));
+ repo.create();
String templateContent = "content of the template";
JGitTestUtil.write(tempFile, templateContent);
config = parse("[i18n]\n\tcommitEncoding = invalidEcoding\n"
@@ -1569,7 +1575,9 @@ public class ConfigTest {
Config config = new Config(null);
File workTree = tmp.newFolder("dummy-worktree");
File tempFile = tmp.newFile("testCommitTemplate-");
- Repository repo = FileRepositoryBuilder.create(workTree);
+ Repository repo = FileRepositoryBuilder
+ .create(new File(workTree, ".git"));
+ repo.create();
String templateContent = "content of the template";
JGitTestUtil.write(tempFile, templateContent);
// commit message encoding
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 5f4331b04d..680a2d5d72 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
@@ -47,7 +47,10 @@ public class MergeAlgorithmTest {
@Test
public void testTwoConflictingModifications() throws IOException {
assertEquals(t("a<b=Z>Zdefghij"),
- merge("abcdefghij", "abZdefghij", "aZZdefghij"));
+ merge("abcdefghij", "abZdefghij", "aZZdefghij", false));
+
+ assertEquals(t("a<b|b=Z>Zdefghij"),
+ merge("abcdefghij", "abZdefghij", "aZZdefghij", true));
}
/**
@@ -60,7 +63,10 @@ public class MergeAlgorithmTest {
@Test
public void testOneAgainstTwoConflictingModifications() throws IOException {
assertEquals(t("aZ<Z=c>Zefghij"),
- merge("abcdefghij", "aZZZefghij", "aZcZefghij"));
+ merge("abcdefghij", "aZZZefghij", "aZcZefghij", false));
+
+ assertEquals(t("aZ<Z|c=c>Zefghij"),
+ merge("abcdefghij", "aZZZefghij", "aZcZefghij", true));
}
/**
@@ -72,7 +78,10 @@ public class MergeAlgorithmTest {
@Test
public void testNoAgainstOneModification() throws IOException {
assertEquals(t("aZcZefghij"),
- merge("abcdefghij", "abcdefghij", "aZcZefghij"));
+ merge("abcdefghij", "abcdefghij", "aZcZefghij", false));
+
+ assertEquals(t("aZcZefghij"),
+ merge("abcdefghij", "abcdefghij", "aZcZefghij", true));
}
/**
@@ -84,7 +93,10 @@ public class MergeAlgorithmTest {
@Test
public void testTwoNonConflictingModifications() throws IOException {
assertEquals(t("YbZdefghij"),
- merge("abcdefghij", "abZdefghij", "Ybcdefghij"));
+ merge("abcdefghij", "abZdefghij", "Ybcdefghij", false));
+
+ assertEquals(t("YbZdefghij"),
+ merge("abcdefghij", "abZdefghij", "Ybcdefghij", true));
}
/**
@@ -96,7 +108,10 @@ public class MergeAlgorithmTest {
@Test
public void testTwoComplicatedModifications() throws IOException {
assertEquals(t("a<ZZZZfZhZj=bYdYYYYiY>"),
- merge("abcdefghij", "aZZZZfZhZj", "abYdYYYYiY"));
+ merge("abcdefghij", "aZZZZfZhZj", "abYdYYYYiY", false));
+
+ assertEquals(t("a<ZZZZfZhZj|bcdefghij=bYdYYYYiY>"),
+ merge("abcdefghij", "aZZZZfZhZj", "abYdYYYYiY", true));
}
/**
@@ -109,7 +124,9 @@ public class MergeAlgorithmTest {
@Test
public void testTwoModificationsWithSharedDelete() throws IOException {
assertEquals(t("Cb}n}"),
- merge("ab}n}n}", "ab}n}", "Cb}n}"));
+ merge("ab}n}n}", "ab}n}", "Cb}n}", false));
+
+ assertEquals(t("Cb}n}"), merge("ab}n}n}", "ab}n}", "Cb}n}", true));
}
/**
@@ -122,7 +139,11 @@ public class MergeAlgorithmTest {
@Test
public void testModificationsWithMiddleInsert() throws IOException {
assertEquals(t("aBcd123123uvwxPq"),
- merge("abcd123uvwxpq", "aBcd123123uvwxPq", "abcd123123uvwxpq"));
+ merge("abcd123uvwxpq", "aBcd123123uvwxPq", "abcd123123uvwxpq",
+ false));
+
+ assertEquals(t("aBcd123123uvwxPq"), merge("abcd123uvwxpq",
+ "aBcd123123uvwxPq", "abcd123123uvwxpq", true));
}
/**
@@ -135,7 +156,10 @@ public class MergeAlgorithmTest {
@Test
public void testModificationsWithMiddleDelete() throws IOException {
assertEquals(t("Abz}z123Q"),
- merge("abz}z}z123q", "Abz}z123Q", "abz}z123q"));
+ merge("abz}z}z123q", "Abz}z123Q", "abz}z123q", false));
+
+ assertEquals(t("Abz}z123Q"),
+ merge("abz}z}z123q", "Abz}z123Q", "abz}z123q", true));
}
/**
@@ -146,7 +170,10 @@ public class MergeAlgorithmTest {
@Test
public void testConflictAtStart() throws IOException {
assertEquals(t("<Z=Y>bcdefghij"),
- merge("abcdefghij", "Zbcdefghij", "Ybcdefghij"));
+ merge("abcdefghij", "Zbcdefghij", "Ybcdefghij", false));
+
+ assertEquals(t("<Z|a=Y>bcdefghij"),
+ merge("abcdefghij", "Zbcdefghij", "Ybcdefghij", true));
}
/**
@@ -157,7 +184,10 @@ public class MergeAlgorithmTest {
@Test
public void testConflictAtEnd() throws IOException {
assertEquals(t("abcdefghi<Z=Y>"),
- merge("abcdefghij", "abcdefghiZ", "abcdefghiY"));
+ merge("abcdefghij", "abcdefghiZ", "abcdefghiY", false));
+
+ assertEquals(t("abcdefghi<Z|j=Y>"),
+ merge("abcdefghij", "abcdefghiZ", "abcdefghiY", true));
}
/**
@@ -169,7 +199,10 @@ public class MergeAlgorithmTest {
@Test
public void testSameModification() throws IOException {
assertEquals(t("abZdefghij"),
- merge("abcdefghij", "abZdefghij", "abZdefghij"));
+ merge("abcdefghij", "abZdefghij", "abZdefghij", false));
+
+ assertEquals(t("abZdefghij"),
+ merge("abcdefghij", "abZdefghij", "abZdefghij", true));
}
/**
@@ -181,27 +214,36 @@ public class MergeAlgorithmTest {
@Test
public void testDeleteVsModify() throws IOException {
assertEquals(t("ab<=Z>defghij"),
- merge("abcdefghij", "abdefghij", "abZdefghij"));
+ merge("abcdefghij", "abdefghij", "abZdefghij", false));
+
+ assertEquals(t("ab<|c=Z>defghij"),
+ merge("abcdefghij", "abdefghij", "abZdefghij", true));
}
@Test
public void testInsertVsModify() throws IOException {
- assertEquals(t("a<bZ=XY>"), merge("ab", "abZ", "aXY"));
+ assertEquals(t("a<bZ=XY>"), merge("ab", "abZ", "aXY", false));
+ assertEquals(t("a<bZ|b=XY>"), merge("ab", "abZ", "aXY", true));
}
@Test
public void testAdjacentModifications() throws IOException {
- assertEquals(t("a<Zc=bY>d"), merge("abcd", "aZcd", "abYd"));
+ assertEquals(t("a<Zc=bY>d"), merge("abcd", "aZcd", "abYd", false));
+ assertEquals(t("a<Zc|bc=bY>d"), merge("abcd", "aZcd", "abYd", true));
}
@Test
public void testSeparateModifications() throws IOException {
- assertEquals(t("aZcYe"), merge("abcde", "aZcde", "abcYe"));
+ assertEquals(t("aZcYe"), merge("abcde", "aZcde", "abcYe", false));
+ assertEquals(t("aZcYe"), merge("abcde", "aZcde", "abcYe", true));
}
@Test
public void testBlankLines() throws IOException {
- assertEquals(t("aZc\nYe"), merge("abc\nde", "aZc\nde", "abc\nYe"));
+ assertEquals(t("aZc\nYe"),
+ merge("abc\nde", "aZc\nde", "abc\nYe", false));
+ assertEquals(t("aZc\nYe"),
+ merge("abc\nde", "aZc\nde", "abc\nYe", true));
}
/**
@@ -214,11 +256,22 @@ public class MergeAlgorithmTest {
*/
@Test
public void testTwoSimilarModsAndOneInsert() throws IOException {
- assertEquals(t("aBcDde"), merge("abcde", "aBcde", "aBcDde"));
- assertEquals(t("IAAAJCAB"), merge("iACAB", "IACAB", "IAAAJCAB"));
- assertEquals(t("HIAAAJCAB"), merge("HiACAB", "HIACAB", "HIAAAJCAB"));
+ assertEquals(t("aBcDde"), merge("abcde", "aBcde", "aBcDde", false));
+ assertEquals(t("aBcDde"), merge("abcde", "aBcde", "aBcDde", true));
+
+ assertEquals(t("IAAAJCAB"), merge("iACAB", "IACAB", "IAAAJCAB", false));
+ assertEquals(t("IAAAJCAB"), merge("iACAB", "IACAB", "IAAAJCAB", true));
+
+ assertEquals(t("HIAAAJCAB"),
+ merge("HiACAB", "HIACAB", "HIAAAJCAB", false));
+ assertEquals(t("HIAAAJCAB"),
+ merge("HiACAB", "HIACAB", "HIAAAJCAB", true));
+
+ assertEquals(t("AGADEFHIAAAJCAB"),
+ merge("AGADEFHiACAB", "AGADEFHIACAB", "AGADEFHIAAAJCAB",
+ false));
assertEquals(t("AGADEFHIAAAJCAB"),
- merge("AGADEFHiACAB", "AGADEFHIACAB", "AGADEFHIAAAJCAB"));
+ merge("AGADEFHiACAB", "AGADEFHIACAB", "AGADEFHIAAAJCAB", true));
}
/**
@@ -232,18 +285,28 @@ public class MergeAlgorithmTest {
@Test
public void testTwoSimilarModsAndOneInsertAtEnd() throws IOException {
Assume.assumeTrue(newlineAtEnd);
- assertEquals(t("IAAJ"), merge("iA", "IA", "IAAJ"));
- assertEquals(t("IAJ"), merge("iA", "IA", "IAJ"));
- assertEquals(t("IAAAJ"), merge("iA", "IA", "IAAAJ"));
+ assertEquals(t("IAAJ"), merge("iA", "IA", "IAAJ", false));
+ assertEquals(t("IAAJ"), merge("iA", "IA", "IAAJ", true));
+
+ assertEquals(t("IAJ"), merge("iA", "IA", "IAJ", false));
+ assertEquals(t("IAJ"), merge("iA", "IA", "IAJ", true));
+
+ assertEquals(t("IAAAJ"), merge("iA", "IA", "IAAAJ", false));
+ assertEquals(t("IAAAJ"), merge("iA", "IA", "IAAAJ", true));
}
@Test
public void testTwoSimilarModsAndOneInsertAtEndNoNewlineAtEnd()
throws IOException {
Assume.assumeFalse(newlineAtEnd);
- assertEquals(t("I<A=AAJ>"), merge("iA", "IA", "IAAJ"));
- assertEquals(t("I<A=AJ>"), merge("iA", "IA", "IAJ"));
- assertEquals(t("I<A=AAAJ>"), merge("iA", "IA", "IAAAJ"));
+ assertEquals(t("I<A=AAJ>"), merge("iA", "IA", "IAAJ", false));
+ assertEquals(t("I<A|A=AAJ>"), merge("iA", "IA", "IAAJ", true));
+
+ assertEquals(t("I<A=AJ>"), merge("iA", "IA", "IAJ", false));
+ assertEquals(t("I<A|A=AJ>"), merge("iA", "IA", "IAJ", true));
+
+ assertEquals(t("I<A=AAAJ>"), merge("iA", "IA", "IAAAJ", false));
+ assertEquals(t("I<A|A=AAAJ>"), merge("iA", "IA", "IAAAJ", true));
}
/**
@@ -254,22 +317,34 @@ public class MergeAlgorithmTest {
@Test
public void testEmptyTexts() throws IOException {
// test modification against deletion
- assertEquals(t("<AB=>"), merge("A", "AB", ""));
- assertEquals(t("<=AB>"), merge("A", "", "AB"));
+ assertEquals(t("<AB=>"), merge("A", "AB", "", false));
+ assertEquals(t("<AB|A=>"), merge("A", "AB", "", true));
+
+ assertEquals(t("<=AB>"), merge("A", "", "AB", false));
+ assertEquals(t("<|A=AB>"), merge("A", "", "AB", true));
// test unmodified against deletion
- assertEquals(t(""), merge("AB", "AB", ""));
- assertEquals(t(""), merge("AB", "", "AB"));
+ assertEquals(t(""), merge("AB", "AB", "", false));
+ assertEquals(t(""), merge("AB", "AB", "", true));
+
+ assertEquals(t(""), merge("AB", "", "AB", false));
+ assertEquals(t(""), merge("AB", "", "AB", true));
// test deletion against deletion
- assertEquals(t(""), merge("AB", "", ""));
+ assertEquals(t(""), merge("AB", "", "", false));
+ assertEquals(t(""), merge("AB", "", "", true));
}
- private String merge(String commonBase, String ours, String theirs) throws IOException {
+ private String merge(String commonBase, String ours, String theirs,
+ boolean diff3) throws IOException {
MergeResult r = new MergeAlgorithm().merge(RawTextComparator.DEFAULT,
T(commonBase), T(ours), T(theirs));
ByteArrayOutputStream bo=new ByteArrayOutputStream(50);
- fmt.formatMerge(bo, r, "B", "O", "T", UTF_8);
+ if (diff3) {
+ fmt.formatMergeDiff3(bo, r, "B", "O", "T", UTF_8);
+ } else {
+ fmt.formatMerge(bo, r, "B", "O", "T", UTF_8);
+ }
return new String(bo.toByteArray(), UTF_8);
}
@@ -284,6 +359,9 @@ public class MergeAlgorithmTest {
case '=':
r.append("=======\n");
break;
+ case '|':
+ r.append("||||||| B\n");
+ break;
case '>':
r.append(">>>>>>> T\n");
break;
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/revwalk/FooterLineTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/revwalk/FooterLineTest.java
index 113f3bee82..01f6a3a0a0 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/revwalk/FooterLineTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/revwalk/FooterLineTest.java
@@ -16,76 +16,74 @@ import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
-import java.io.IOException;
import java.util.List;
import org.eclipse.jgit.junit.RepositoryTestCase;
-import org.eclipse.jgit.lib.Constants;
import org.eclipse.jgit.lib.ObjectId;
import org.junit.Test;
public class FooterLineTest extends RepositoryTestCase {
@Test
- public void testNoFooters_EmptyBody() throws IOException {
- final RevCommit commit = parse("");
- final List<FooterLine> footers = commit.getFooterLines();
+ public void testNoFooters_EmptyBody() {
+ String msg = buildMessage("");
+ List<FooterLine> footers = FooterLine.fromMessage(msg);
assertNotNull(footers);
assertEquals(0, footers.size());
}
@Test
- public void testNoFooters_NewlineOnlyBody1() throws IOException {
- final RevCommit commit = parse("\n");
- final List<FooterLine> footers = commit.getFooterLines();
+ public void testNoFooters_NewlineOnlyBody1() {
+ String msg = buildMessage("\n");
+ List<FooterLine> footers = FooterLine.fromMessage(msg);
assertNotNull(footers);
assertEquals(0, footers.size());
}
@Test
- public void testNoFooters_NewlineOnlyBody5() throws IOException {
- final RevCommit commit = parse("\n\n\n\n\n");
- final List<FooterLine> footers = commit.getFooterLines();
+ public void testNoFooters_NewlineOnlyBody5() {
+ String msg = buildMessage("\n\n\n\n\n");
+ List<FooterLine> footers = FooterLine.fromMessage(msg);
assertNotNull(footers);
assertEquals(0, footers.size());
}
@Test
- public void testNoFooters_OneLineBodyNoLF() throws IOException {
- final RevCommit commit = parse("this is a commit");
- final List<FooterLine> footers = commit.getFooterLines();
+ public void testNoFooters_OneLineBodyNoLF() {
+ String msg = buildMessage("this is a commit");
+ List<FooterLine> footers = FooterLine.fromMessage(msg);
assertNotNull(footers);
assertEquals(0, footers.size());
}
@Test
- public void testNoFooters_OneLineBodyWithLF() throws IOException {
- final RevCommit commit = parse("this is a commit\n");
- final List<FooterLine> footers = commit.getFooterLines();
+ public void testNoFooters_OneLineBodyWithLF() {
+ String msg = buildMessage("this is a commit\n");
+ List<FooterLine> footers = FooterLine.fromMessage(msg);
assertNotNull(footers);
assertEquals(0, footers.size());
}
@Test
- public void testNoFooters_ShortBodyNoLF() throws IOException {
- final RevCommit commit = parse("subject\n\nbody of commit");
- final List<FooterLine> footers = commit.getFooterLines();
+ public void testNoFooters_ShortBodyNoLF() {
+ String msg = buildMessage("subject\n\nbody of commit");
+ List<FooterLine> footers = FooterLine.fromMessage(msg);
assertNotNull(footers);
assertEquals(0, footers.size());
}
@Test
- public void testNoFooters_ShortBodyWithLF() throws IOException {
- final RevCommit commit = parse("subject\n\nbody of commit\n");
- final List<FooterLine> footers = commit.getFooterLines();
+ public void testNoFooters_ShortBodyWithLF() {
+ String msg = buildMessage("subject\n\nbody of commit\n");
+ List<FooterLine> footers = FooterLine.fromMessage(msg);
assertNotNull(footers);
assertEquals(0, footers.size());
}
@Test
- public void testSignedOffBy_OneUserNoLF() throws IOException {
- final RevCommit commit = parse("subject\n\nbody of commit\n" + "\n"
- + "Signed-off-by: A. U. Thor <a@example.com>");
- final List<FooterLine> footers = commit.getFooterLines();
+ public void testSignedOffBy_OneUserNoLF() {
+ String msg = buildMessage("subject\n\nbody of commit\n" + "\n"
+ + "Signed-off-by: A. U. Thor <a@example.com>");
+ List<FooterLine> footers = FooterLine.fromMessage(msg);
FooterLine f;
assertNotNull(footers);
@@ -98,10 +96,10 @@ public class FooterLineTest extends RepositoryTestCase {
}
@Test
- public void testSignedOffBy_OneUserWithLF() throws IOException {
- final RevCommit commit = parse("subject\n\nbody of commit\n" + "\n"
- + "Signed-off-by: A. U. Thor <a@example.com>\n");
- final List<FooterLine> footers = commit.getFooterLines();
+ public void testSignedOffBy_OneUserWithLF() {
+ String msg = buildMessage("subject\n\nbody of commit\n" + "\n"
+ + "Signed-off-by: A. U. Thor <a@example.com>\n");
+ List<FooterLine> footers = FooterLine.fromMessage(msg);
FooterLine f;
assertNotNull(footers);
@@ -114,14 +112,13 @@ public class FooterLineTest extends RepositoryTestCase {
}
@Test
- public void testSignedOffBy_IgnoreWhitespace() throws IOException {
+ public void testSignedOffBy_IgnoreWhitespace() {
// We only ignore leading whitespace on the value, trailing
// is assumed part of the value.
//
- final RevCommit commit = parse("subject\n\nbody of commit\n" + "\n"
- + "Signed-off-by: A. U. Thor <a@example.com> \n");
- final List<FooterLine> footers = commit.getFooterLines();
- FooterLine f;
+ String msg = buildMessage("subject\n\nbody of commit\n" + "\n"
+ + "Signed-off-by: A. U. Thor <a@example.com> \n");
+ List<FooterLine> footers = FooterLine.fromMessage(msg); FooterLine f;
assertNotNull(footers);
assertEquals(1, footers.size());
@@ -133,10 +130,10 @@ public class FooterLineTest extends RepositoryTestCase {
}
@Test
- public void testEmptyValueNoLF() throws IOException {
- final RevCommit commit = parse("subject\n\nbody of commit\n" + "\n"
- + "Signed-off-by:");
- final List<FooterLine> footers = commit.getFooterLines();
+ public void testEmptyValueNoLF() {
+ String msg = buildMessage("subject\n\nbody of commit\n" + "\n"
+ + "Signed-off-by:");
+ List<FooterLine> footers = FooterLine.fromMessage(msg);
FooterLine f;
assertNotNull(footers);
@@ -149,10 +146,10 @@ public class FooterLineTest extends RepositoryTestCase {
}
@Test
- public void testEmptyValueWithLF() throws IOException {
- final RevCommit commit = parse("subject\n\nbody of commit\n" + "\n"
- + "Signed-off-by:\n");
- final List<FooterLine> footers = commit.getFooterLines();
+ public void testEmptyValueWithLF() {
+ String msg = buildMessage("subject\n\nbody of commit\n" + "\n"
+ + "Signed-off-by:\n");
+ List<FooterLine> footers = FooterLine.fromMessage(msg);
FooterLine f;
assertNotNull(footers);
@@ -165,10 +162,10 @@ public class FooterLineTest extends RepositoryTestCase {
}
@Test
- public void testShortKey() throws IOException {
- final RevCommit commit = parse("subject\n\nbody of commit\n" + "\n"
- + "K:V\n");
- final List<FooterLine> footers = commit.getFooterLines();
+ public void testShortKey() {
+ String msg = buildMessage("subject\n\nbody of commit\n" + "\n"
+ + "K:V\n");
+ List<FooterLine> footers = FooterLine.fromMessage(msg);
FooterLine f;
assertNotNull(footers);
@@ -181,10 +178,10 @@ public class FooterLineTest extends RepositoryTestCase {
}
@Test
- public void testNonDelimtedEmail() throws IOException {
- final RevCommit commit = parse("subject\n\nbody of commit\n" + "\n"
- + "Acked-by: re@example.com\n");
- final List<FooterLine> footers = commit.getFooterLines();
+ public void testNonDelimtedEmail() {
+ String msg = buildMessage("subject\n\nbody of commit\n" + "\n"
+ + "Acked-by: re@example.com\n");
+ List<FooterLine> footers = FooterLine.fromMessage(msg);
FooterLine f;
assertNotNull(footers);
@@ -197,10 +194,10 @@ public class FooterLineTest extends RepositoryTestCase {
}
@Test
- public void testNotEmail() throws IOException {
- final RevCommit commit = parse("subject\n\nbody of commit\n" + "\n"
- + "Acked-by: Main Tain Er\n");
- final List<FooterLine> footers = commit.getFooterLines();
+ public void testNotEmail() {
+ String msg = buildMessage("subject\n\nbody of commit\n" + "\n"
+ + "Acked-by: Main Tain Er\n");
+ List<FooterLine> footers = FooterLine.fromMessage(msg);
FooterLine f;
assertNotNull(footers);
@@ -213,15 +210,15 @@ public class FooterLineTest extends RepositoryTestCase {
}
@Test
- public void testSignedOffBy_ManyUsers() throws IOException {
- final RevCommit commit = parse("subject\n\nbody of commit\n"
- + "Not-A-Footer-Line: this line must not be read as a footer\n"
- + "\n" // paragraph break, now footers appear in final block
- + "Signed-off-by: A. U. Thor <a@example.com>\n"
- + "CC: <some.mailing.list@example.com>\n"
- + "Acked-by: Some Reviewer <sr@example.com>\n"
- + "Signed-off-by: Main Tain Er <mte@example.com>\n");
- final List<FooterLine> footers = commit.getFooterLines();
+ public void testSignedOffBy_ManyUsers() {
+ String msg = buildMessage("subject\n\nbody of commit\n"
+ + "Not-A-Footer-Line: this line must not be read as a footer\n"
+ + "\n" // paragraph break, now footers appear in final block
+ + "Signed-off-by: A. U. Thor <a@example.com>\n"
+ + "CC: <some.mailing.list@example.com>\n"
+ + "Acked-by: Some Reviewer <sr@example.com>\n"
+ + "Signed-off-by: Main Tain Er <mte@example.com>\n");
+ List<FooterLine> footers = FooterLine.fromMessage(msg);
FooterLine f;
assertNotNull(footers);
@@ -249,16 +246,16 @@ public class FooterLineTest extends RepositoryTestCase {
}
@Test
- public void testSignedOffBy_SkipNonFooter() throws IOException {
- final RevCommit commit = parse("subject\n\nbody of commit\n"
- + "Not-A-Footer-Line: this line must not be read as a footer\n"
- + "\n" // paragraph break, now footers appear in final block
- + "Signed-off-by: A. U. Thor <a@example.com>\n"
- + "CC: <some.mailing.list@example.com>\n"
- + "not really a footer line but we'll skip it anyway\n"
- + "Acked-by: Some Reviewer <sr@example.com>\n"
- + "Signed-off-by: Main Tain Er <mte@example.com>\n");
- final List<FooterLine> footers = commit.getFooterLines();
+ public void testSignedOffBy_SkipNonFooter() {
+ String msg = buildMessage("subject\n\nbody of commit\n"
+ + "Not-A-Footer-Line: this line must not be read as a footer\n"
+ + "\n" // paragraph break, now footers appear in final block
+ + "Signed-off-by: A. U. Thor <a@example.com>\n"
+ + "CC: <some.mailing.list@example.com>\n"
+ + "not really a footer line but we'll skip it anyway\n"
+ + "Acked-by: Some Reviewer <sr@example.com>\n"
+ + "Signed-off-by: Main Tain Er <mte@example.com>\n");
+ List<FooterLine> footers = FooterLine.fromMessage(msg);
FooterLine f;
assertNotNull(footers);
@@ -282,15 +279,16 @@ public class FooterLineTest extends RepositoryTestCase {
}
@Test
- public void testFilterFootersIgnoreCase() throws IOException {
- final RevCommit commit = parse("subject\n\nbody of commit\n"
- + "Not-A-Footer-Line: this line must not be read as a footer\n"
- + "\n" // paragraph break, now footers appear in final block
- + "Signed-Off-By: A. U. Thor <a@example.com>\n"
- + "CC: <some.mailing.list@example.com>\n"
- + "Acked-by: Some Reviewer <sr@example.com>\n"
- + "signed-off-by: Main Tain Er <mte@example.com>\n");
- final List<String> footers = commit.getFooterLines("signed-off-by");
+ public void testFilterFootersIgnoreCase() {
+ String msg = buildMessage("subject\n\nbody of commit\n"
+ + "Not-A-Footer-Line: this line must not be read as a footer\n"
+ + "\n" // paragraph break, now footers appear in final block
+ + "Signed-Off-By: A. U. Thor <a@example.com>\n"
+ + "CC: <some.mailing.list@example.com>\n"
+ + "Acked-by: Some Reviewer <sr@example.com>\n"
+ + "signed-off-by: Main Tain Er <mte@example.com>\n");
+ List<String> footers = FooterLine.getValues(
+ FooterLine.fromMessage(msg), "signed-off-by");
assertNotNull(footers);
assertEquals(2, footers.size());
@@ -300,38 +298,33 @@ public class FooterLineTest extends RepositoryTestCase {
}
@Test
- public void testMatchesBugId() throws IOException {
- final RevCommit commit = parse("this is a commit subject for test\n"
- + "\n" // paragraph break, now footers appear in final block
- + "Simple-Bug-Id: 42\n");
- final List<FooterLine> footers = commit.getFooterLines();
+ public void testMatchesBugId() {
+ String msg = buildMessage("this is a commit subject for test\n"
+ + "\n" // paragraph break, now footers appear in final block
+ + "Simple-Bug-Id: 42\n");
+ List<FooterLine> footers = FooterLine.fromMessage(msg);
assertNotNull(footers);
assertEquals(1, footers.size());
- final FooterLine line = footers.get(0);
+ FooterLine line = footers.get(0);
assertNotNull(line);
assertEquals("Simple-Bug-Id", line.getKey());
assertEquals("42", line.getValue());
- final FooterKey bugid = new FooterKey("Simple-Bug-Id");
+ FooterKey bugid = new FooterKey("Simple-Bug-Id");
assertTrue("matches Simple-Bug-Id", line.matches(bugid));
assertFalse("not Signed-off-by", line.matches(FooterKey.SIGNED_OFF_BY));
assertFalse("not CC", line.matches(FooterKey.CC));
}
- private RevCommit parse(String msg) throws IOException {
- final StringBuilder buf = new StringBuilder();
+ private String buildMessage(String msg) {
+ StringBuilder buf = new StringBuilder();
buf.append("tree " + ObjectId.zeroId().name() + "\n");
buf.append("author A. U. Thor <a@example.com> 1 +0000\n");
buf.append("committer A. U. Thor <a@example.com> 1 +0000\n");
buf.append("\n");
buf.append(msg);
-
- try (RevWalk walk = new RevWalk(db)) {
- RevCommit c = new RevCommit(ObjectId.zeroId());
- c.parseCanonical(walk, Constants.encode(buf.toString()));
- return c;
- }
+ return buf.toString();
}
}
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 3cc0368943..8215a795b2 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
@@ -26,6 +26,8 @@ import java.util.Comparator;
import java.util.List;
import org.eclipse.jgit.api.Git;
+import org.eclipse.jgit.diff.DiffConfig;
+import org.eclipse.jgit.errors.ConfigInvalidException;
import org.eclipse.jgit.internal.storage.file.GC;
import org.eclipse.jgit.lib.AnyObjectId;
import org.eclipse.jgit.lib.ConfigConstants;
@@ -33,6 +35,7 @@ import org.eclipse.jgit.lib.Constants;
import org.eclipse.jgit.lib.Ref;
import org.eclipse.jgit.revwalk.filter.MessageRevFilter;
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.TreeFilter;
@@ -46,6 +49,7 @@ public class RevWalkCommitGraphTest extends RevWalkTestCase {
public void setUp() throws Exception {
super.setUp();
rw = new RevWalk(db);
+ mockSystemReader.setJGitConfig(new MockConfig());
}
@Test
@@ -167,6 +171,72 @@ public class RevWalkCommitGraphTest extends RevWalkTestCase {
}
@Test
+ public void testChangedPathFilter() throws Exception {
+ RevCommit c1 = commitFile("file1", "1", "master");
+ commitFile("file2", "2", "master");
+ RevCommit c3 = commitFile("file1", "3", "master");
+ RevCommit c4 = commitFile("file2", "4", "master");
+
+ enableAndWriteCommitGraph();
+
+ TreeRevFilter trf = new TreeRevFilter(rw, PathFilter.create("file1"));
+ rw.markStart(rw.lookupCommit(c4));
+ rw.setRevFilter(trf);
+ assertEquals(c3, rw.next());
+ assertEquals(c1, rw.next());
+ assertNull(rw.next());
+
+ // 1 commit that has exactly one parent and matches path
+ assertEquals(1, trf.getChangedPathFilterTruePositive());
+
+ // No false positives
+ assertEquals(0, trf.getChangedPathFilterFalsePositive());
+
+ // 2 commits that have exactly one parent and don't match path
+ assertEquals(2, trf.getChangedPathFilterNegative());
+ }
+
+ @Test
+ public void testChangedPathFilterWithFollowFilter() throws Exception {
+ RevCommit c0 = commit(tree());
+ RevCommit c1 = commit(tree(file("file", blob("contents"))), c0);
+ RevCommit c2 = commit(tree(file("file", blob("contents")),
+ file("unrelated", blob("unrelated change"))), c1);
+ RevCommit c3 = commit(tree(file("renamed-file", blob("contents")),
+ file("unrelated", blob("unrelated change"))), c2);
+ RevCommit c4 = commit(
+ tree(file("renamed-file", blob("contents")),
+ file("unrelated", blob("another unrelated change"))),
+ c3);
+ branch(c4, "master");
+
+ enableAndWriteCommitGraph();
+
+ db.getConfig().setString(ConfigConstants.CONFIG_DIFF_SECTION, null,
+ ConfigConstants.CONFIG_KEY_RENAMES, "true");
+
+ TreeRevFilter trf = new TreeRevFilter(rw,
+ new FollowFilter(PathFilter.create("renamed-file"),
+ db.getConfig().get(DiffConfig.KEY)));
+ rw.markStart(rw.lookupCommit(c4));
+ rw.setRevFilter(trf);
+ assertEquals(c3, rw.next());
+ assertEquals(c1, rw.next());
+ assertNull(rw.next());
+
+ // Path "renamed-file" is in c3's bloom filter, and another path "file"
+ // is in c1's bloom filter (we know of "file" because the rev walk
+ // detected that "renamed-file" is a renaming of "file")
+ assertEquals(2, trf.getChangedPathFilterTruePositive());
+
+ // No false positives
+ assertEquals(0, trf.getChangedPathFilterFalsePositive());
+
+ // 2 commits that have exactly one parent and don't match path
+ assertEquals(2, trf.getChangedPathFilterNegative());
+ }
+
+ @Test
public void testWalkWithCommitMessageFilter() throws Exception {
RevCommit a = commit();
RevCommit b = commitBuilder().parent(a)
@@ -437,6 +507,8 @@ public class RevWalkCommitGraphTest extends RevWalkTestCase {
ConfigConstants.CONFIG_COMMIT_GRAPH, true);
db.getConfig().setBoolean(ConfigConstants.CONFIG_GC_SECTION, null,
ConfigConstants.CONFIG_KEY_WRITE_COMMIT_GRAPH, true);
+ db.getConfig().setBoolean(ConfigConstants.CONFIG_GC_SECTION, null,
+ ConfigConstants.CONFIG_KEY_WRITE_CHANGED_PATHS, true);
GC gc = new GC(db);
gc.gc().get();
}
@@ -445,4 +517,41 @@ public class RevWalkCommitGraphTest extends RevWalkTestCase {
rw.close();
rw = new RevWalk(db);
}
+
+ private static final class MockConfig extends FileBasedConfig {
+ private MockConfig() {
+ super(null, null);
+ }
+
+ @Override
+ public void load() throws IOException, ConfigInvalidException {
+ // Do nothing
+ }
+
+ @Override
+ public void save() throws IOException {
+ // Do nothing
+ }
+
+ @Override
+ public boolean isOutdated() {
+ return false;
+ }
+
+ @Override
+ public String toString() {
+ return "MockConfig";
+ }
+
+ @Override
+ public boolean getBoolean(final String section, final String name,
+ final boolean defaultValue) {
+ if (section.equals(ConfigConstants.CONFIG_COMMIT_GRAPH_SECTION)
+ && name.equals(
+ ConfigConstants.CONFIG_KEY_READ_CHANGED_PATHS)) {
+ return true;
+ }
+ return defaultValue;
+ }
+ }
}
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/revwalk/RevWalkFollowFilterTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/revwalk/RevWalkFollowFilterTest.java
index c62136e64d..5203e3fbea 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/revwalk/RevWalkFollowFilterTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/revwalk/RevWalkFollowFilterTest.java
@@ -27,9 +27,17 @@ public class RevWalkFollowFilterTest extends RevWalkTestCase {
private static class DiffCollector extends RenameCallback {
List<DiffEntry> diffs = new ArrayList<>();
+ List<RevCommit> commits = new ArrayList<>();
+
@Override
public void renamed(DiffEntry diff) {
+ throw new UnsupportedOperationException("unimplemented");
+ }
+
+ @Override
+ public void renamed(DiffEntry diff, RevCommit commit) {
diffs.add(diff);
+ commits.add(commit);
}
}
@@ -77,6 +85,7 @@ public class RevWalkFollowFilterTest extends RevWalkTestCase {
assertNull(rw.next());
assertRenames("a->b");
+ assertRenameCommits(renameCommit);
}
@Test
@@ -108,6 +117,7 @@ public class RevWalkFollowFilterTest extends RevWalkTestCase {
assertNull(rw.next());
assertRenames("c->a", "b->c", "a->b");
+ assertRenameCommits(renameCommit3, renameCommit2, renameCommit1);
}
/**
@@ -136,6 +146,20 @@ public class RevWalkFollowFilterTest extends RevWalkTestCase {
}
}
+ protected void assertRenameCommits(RevCommit... expectedCommits) {
+ Assert.assertEquals(
+ "Unexpected number of rename commits. Expected: "
+ + expectedCommits.length + ", actual: "
+ + diffCollector.diffs.size(),
+ expectedCommits.length, diffCollector.diffs.size());
+
+ for (int i = 0; i < expectedCommits.length; i++) {
+ RevCommit renameCommit = diffCollector.commits.get(i);
+ Assert.assertNotNull(renameCommit);
+ Assert.assertEquals(expectedCommits[i], renameCommit);
+ }
+ }
+
protected void assertNoRenames() {
Assert.assertEquals("Found unexpected rename/copy diff", 0,
diffCollector.diffs.size());
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/storage/file/UserConfigFileTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/storage/file/UserConfigFileTest.java
new file mode 100644
index 0000000000..7d212d540f
--- /dev/null
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/storage/file/UserConfigFileTest.java
@@ -0,0 +1,301 @@
+/*
+ * Copyright (C) 2023, Thomas Wolf <twolf@apache.org> 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.storage.file;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
+
+import java.nio.file.Files;
+import java.nio.file.Path;
+
+import org.eclipse.jgit.util.FS;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.TemporaryFolder;
+
+public class UserConfigFileTest {
+
+ @Rule
+ public TemporaryFolder tmp = new TemporaryFolder();
+
+ @Test
+ public void testParentOnlyLoad() throws Exception {
+ Path xdg = tmp.getRoot().toPath().resolve("xdg.cfg");
+ Files.writeString(xdg, "[user]\n\tname = Archibald Ulysses Thor");
+ Path user = tmp.getRoot().toPath().resolve("user.cfg");
+ UserConfigFile config = new UserConfigFile(null, user.toFile(),
+ xdg.toFile(), FS.DETECTED);
+ config.load();
+ assertEquals("Archibald Ulysses Thor",
+ config.getString("user", null, "name"));
+ }
+
+ @Test
+ public void testLoadBoth() throws Exception {
+ Path xdg = tmp.getRoot().toPath().resolve("xdg.cfg");
+ Files.writeString(xdg, "[user]\n\tname = Archibald Ulysses Thor");
+ Path user = tmp.getRoot().toPath().resolve("user.cfg");
+ Files.writeString(user, "[user]\n\temail = a.u.thor@example.com");
+ UserConfigFile config = new UserConfigFile(null, user.toFile(),
+ xdg.toFile(), FS.DETECTED);
+ config.load();
+ assertEquals("Archibald Ulysses Thor",
+ config.getString("user", null, "name"));
+ assertEquals("a.u.thor@example.com",
+ config.getString("user", null, "email"));
+ }
+
+ @Test
+ public void testOverwriteChild() throws Exception {
+ Path xdg = tmp.getRoot().toPath().resolve("xdg.cfg");
+ Files.writeString(xdg, "[user]\n\tname = Archibald Ulysses Thor");
+ Path user = tmp.getRoot().toPath().resolve("user.cfg");
+ Files.writeString(user, "[user]\n\temail = a.u.thor@example.com");
+ UserConfigFile config = new UserConfigFile(null, user.toFile(),
+ xdg.toFile(), FS.DETECTED);
+ config.load();
+ assertEquals("Archibald Ulysses Thor",
+ config.getString("user", null, "name"));
+ assertEquals("a.u.thor@example.com",
+ config.getString("user", null, "email"));
+ config.setString("user", null, "name", "A U Thor");
+ assertEquals("A U Thor", config.getString("user", null, "name"));
+ config.save();
+ UserConfigFile config2 = new UserConfigFile(null, user.toFile(),
+ xdg.toFile(), FS.DETECTED);
+ config2.load();
+ assertEquals("A U Thor", config2.getString("user", null, "name"));
+ assertEquals("a.u.thor@example.com",
+ config.getString("user", null, "email"));
+ FileBasedConfig cfg = new FileBasedConfig(null, xdg.toFile(),
+ FS.DETECTED);
+ cfg.load();
+ assertEquals("Archibald Ulysses Thor",
+ cfg.getString("user", null, "name"));
+ assertNull(cfg.getString("user", null, "email"));
+ }
+
+ @Test
+ public void testUnset() throws Exception {
+ Path xdg = tmp.getRoot().toPath().resolve("xdg.cfg");
+ Files.writeString(xdg, "[user]\n\tname = Archibald Ulysses Thor");
+ Path user = tmp.getRoot().toPath().resolve("user.cfg");
+ Files.writeString(user, "[user]\n\temail = a.u.thor@example.com");
+ UserConfigFile config = new UserConfigFile(null, user.toFile(),
+ xdg.toFile(), FS.DETECTED);
+ config.load();
+ assertEquals("Archibald Ulysses Thor",
+ config.getString("user", null, "name"));
+ assertEquals("a.u.thor@example.com",
+ config.getString("user", null, "email"));
+ config.setString("user", null, "name", "A U Thor");
+ assertEquals("A U Thor", config.getString("user", null, "name"));
+ config.unset("user", null, "name");
+ assertEquals("Archibald Ulysses Thor",
+ config.getString("user", null, "name"));
+ assertEquals("a.u.thor@example.com",
+ config.getString("user", null, "email"));
+ config.save();
+ UserConfigFile config2 = new UserConfigFile(null, user.toFile(),
+ xdg.toFile(), FS.DETECTED);
+ config2.load();
+ assertEquals("Archibald Ulysses Thor",
+ config2.getString("user", null, "name"));
+ assertEquals("a.u.thor@example.com",
+ config.getString("user", null, "email"));
+ FileBasedConfig cfg = new FileBasedConfig(null, user.toFile(),
+ FS.DETECTED);
+ cfg.load();
+ assertNull(cfg.getString("user", null, "name"));
+ assertEquals("a.u.thor@example.com",
+ cfg.getString("user", null, "email"));
+ }
+
+ @Test
+ public void testUnsetSection() throws Exception {
+ Path xdg = tmp.getRoot().toPath().resolve("xdg.cfg");
+ Files.writeString(xdg, "[user]\n\tname = Archibald Ulysses Thor");
+ Path user = tmp.getRoot().toPath().resolve("user.cfg");
+ Files.writeString(user, "[user]\n\temail = a.u.thor@example.com");
+ UserConfigFile config = new UserConfigFile(null, user.toFile(),
+ xdg.toFile(), FS.DETECTED);
+ config.load();
+ assertEquals("Archibald Ulysses Thor",
+ config.getString("user", null, "name"));
+ assertEquals("a.u.thor@example.com",
+ config.getString("user", null, "email"));
+ config.unsetSection("user", null);
+ assertEquals("Archibald Ulysses Thor",
+ config.getString("user", null, "name"));
+ config.save();
+ assertTrue(Files.readString(user).strip().isEmpty());
+ }
+
+ @Test
+ public void testNoChild() throws Exception {
+ Path xdg = tmp.getRoot().toPath().resolve("xdg.cfg");
+ Files.writeString(xdg, "[user]\n\tname = Archibald Ulysses Thor");
+ Path user = tmp.getRoot().toPath().resolve("user.cfg");
+ UserConfigFile config = new UserConfigFile(null, user.toFile(),
+ xdg.toFile(), FS.DETECTED);
+ config.load();
+ assertEquals("Archibald Ulysses Thor",
+ config.getString("user", null, "name"));
+ assertNull(config.getString("user", null, "email"));
+ config.setString("user", null, "email", "a.u.thor@example.com");
+ assertEquals("a.u.thor@example.com",
+ config.getString("user", null, "email"));
+ config.save();
+ assertFalse(Files.exists(user));
+ UserConfigFile config2 = new UserConfigFile(null, user.toFile(),
+ xdg.toFile(), FS.DETECTED);
+ config2.load();
+ assertEquals("Archibald Ulysses Thor",
+ config2.getString("user", null, "name"));
+ assertEquals("a.u.thor@example.com",
+ config2.getString("user", null, "email"));
+ }
+
+ @Test
+ public void testNoFiles() throws Exception {
+ Path xdg = tmp.getRoot().toPath().resolve("xdg.cfg");
+ Path user = tmp.getRoot().toPath().resolve("user.cfg");
+ UserConfigFile config = new UserConfigFile(null, user.toFile(),
+ xdg.toFile(), FS.DETECTED);
+ config.load();
+ assertNull(config.getString("user", null, "name"));
+ assertNull(config.getString("user", null, "email"));
+ config.setString("user", null, "name", "Archibald Ulysses Thor");
+ config.setString("user", null, "email", "a.u.thor@example.com");
+ assertEquals("Archibald Ulysses Thor",
+ config.getString("user", null, "name"));
+ assertEquals("a.u.thor@example.com",
+ config.getString("user", null, "email"));
+ config.save();
+ assertTrue(Files.exists(user));
+ assertFalse(Files.exists(xdg));
+ UserConfigFile config2 = new UserConfigFile(null, user.toFile(),
+ xdg.toFile(), FS.DETECTED);
+ config2.load();
+ assertEquals("Archibald Ulysses Thor",
+ config2.getString("user", null, "name"));
+ assertEquals("a.u.thor@example.com",
+ config2.getString("user", null, "email"));
+ }
+
+ @Test
+ public void testSetInXdg() throws Exception {
+ Path xdg = tmp.getRoot().toPath().resolve("xdg.cfg");
+ Files.writeString(xdg, "[user]\n\tname = Archibald Ulysses Thor");
+ Path user = tmp.getRoot().toPath().resolve("user.cfg");
+ UserConfigFile config = new UserConfigFile(null, user.toFile(),
+ xdg.toFile(), FS.DETECTED);
+ config.load();
+ assertEquals("Archibald Ulysses Thor",
+ config.getString("user", null, "name"));
+ config.setString("user", null, "email", "a.u.thor@example.com");
+ config.save();
+ assertFalse(Files.exists(user));
+ FileBasedConfig cfg = new FileBasedConfig(null, xdg.toFile(),
+ FS.DETECTED);
+ cfg.load();
+ assertEquals("Archibald Ulysses Thor",
+ cfg.getString("user", null, "name"));
+ assertEquals("a.u.thor@example.com",
+ cfg.getString("user", null, "email"));
+ }
+
+ @Test
+ public void testUserConfigCreated() throws Exception {
+ Path xdg = tmp.getRoot().toPath().resolve("xdg.cfg");
+ Files.writeString(xdg, "[user]\n\tname = Archibald Ulysses Thor");
+ Path user = tmp.getRoot().toPath().resolve("user.cfg");
+ Thread.sleep(3000); // Avoid racily clean isOutdated() below.
+ UserConfigFile config = new UserConfigFile(null, user.toFile(),
+ xdg.toFile(), FS.DETECTED);
+ config.load();
+ assertEquals("Archibald Ulysses Thor",
+ config.getString("user", null, "name"));
+ Files.writeString(user,
+ "[user]\n\temail = a.u.thor@example.com\n\tname = A U Thor");
+ assertEquals("Archibald Ulysses Thor",
+ config.getString("user", null, "name"));
+ assertTrue(config.isOutdated());
+ config.load();
+ assertEquals("A U Thor", config.getString("user", null, "name"));
+ assertEquals("a.u.thor@example.com",
+ config.getString("user", null, "email"));
+ }
+
+ @Test
+ public void testUserConfigDeleted() throws Exception {
+ Path xdg = tmp.getRoot().toPath().resolve("xdg.cfg");
+ Files.writeString(xdg, "[user]\n\tname = Archibald Ulysses Thor");
+ Path user = tmp.getRoot().toPath().resolve("user.cfg");
+ Files.writeString(user,
+ "[user]\n\temail = a.u.thor@example.com\n\tname = A U Thor");
+ Thread.sleep(3000); // Avoid racily clean isOutdated() below.
+ UserConfigFile config = new UserConfigFile(null, user.toFile(),
+ xdg.toFile(), FS.DETECTED);
+ config.load();
+ assertEquals("A U Thor", config.getString("user", null, "name"));
+ assertEquals("a.u.thor@example.com",
+ config.getString("user", null, "email"));
+ Files.delete(user);
+ assertEquals("A U Thor", config.getString("user", null, "name"));
+ assertEquals("a.u.thor@example.com",
+ config.getString("user", null, "email"));
+ assertTrue(config.isOutdated());
+ config.load();
+ assertEquals("Archibald Ulysses Thor",
+ config.getString("user", null, "name"));
+ assertNull(config.getString("user", null, "email"));
+ }
+
+ @Test
+ public void testXdgConfigDeleted() throws Exception {
+ Path xdg = tmp.getRoot().toPath().resolve("xdg.cfg");
+ Files.writeString(xdg, "[user]\n\tname = Archibald Ulysses Thor");
+ Path user = tmp.getRoot().toPath().resolve("user.cfg");
+ Thread.sleep(3000); // Avoid racily clean isOutdated() below.
+ UserConfigFile config = new UserConfigFile(null, user.toFile(),
+ xdg.toFile(), FS.DETECTED);
+ config.load();
+ assertEquals("Archibald Ulysses Thor",
+ config.getString("user", null, "name"));
+ Files.delete(xdg);
+ assertEquals("Archibald Ulysses Thor",
+ config.getString("user", null, "name"));
+ assertTrue(config.isOutdated());
+ config.load();
+ assertNull(config.getString("user", null, "name"));
+ }
+
+ @Test
+ public void testXdgConfigDeletedUserConfigExists() throws Exception {
+ Path xdg = tmp.getRoot().toPath().resolve("xdg.cfg");
+ Files.writeString(xdg, "[user]\n\tname = Archibald Ulysses Thor");
+ Path user = tmp.getRoot().toPath().resolve("user.cfg");
+ Files.writeString(user,
+ "[user]\n\temail = a.u.thor@example.com\n\tname = A U Thor");
+ Thread.sleep(3000); // Avoid racily clean isOutdated() below.
+ UserConfigFile config = new UserConfigFile(null, user.toFile(),
+ xdg.toFile(), FS.DETECTED);
+ config.load();
+ assertEquals("A U Thor", config.getString("user", null, "name"));
+ Files.delete(xdg);
+ assertTrue(config.isOutdated());
+ config.load();
+ assertEquals("A U Thor", config.getString("user", null, "name"));
+ }
+
+}
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/HttpAuthTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/HttpAuthTest.java
index 947ca97615..c1ab43eadf 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/HttpAuthTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/HttpAuthTest.java
@@ -160,7 +160,7 @@ public class HttpAuthTest {
String value = header.substring(i + 1).trim();
if (!headerFields.containsKey(key))
- headerFields.put(key, new ArrayList<String>());
+ headerFields.put(key, new ArrayList<>());
List<String> values = headerFields.get(key);
values.add(value);
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/InMemoryPack.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/InMemoryPack.java
new file mode 100644
index 0000000000..cad80ef707
--- /dev/null
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/InMemoryPack.java
@@ -0,0 +1,88 @@
+/*
+ * 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.transport;
+
+import java.io.ByteArrayInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.security.MessageDigest;
+import java.util.zip.Deflater;
+
+import org.eclipse.jgit.lib.AnyObjectId;
+import org.eclipse.jgit.lib.Constants;
+import org.eclipse.jgit.util.NB;
+import org.eclipse.jgit.util.TemporaryBuffer;
+import org.eclipse.jgit.util.TemporaryBuffer.Heap;
+
+/**
+ * Helper class to create packs for tests.
+ */
+public class InMemoryPack {
+
+ private final Heap tinyPack;
+
+ public InMemoryPack() {
+ this(1024);
+ }
+
+ public InMemoryPack(int size) {
+ this.tinyPack = new TemporaryBuffer.Heap(size);
+ }
+
+ public InMemoryPack header(int cnt)
+ throws IOException {
+ final byte[] hdr = new byte[8];
+ NB.encodeInt32(hdr, 0, 2);
+ NB.encodeInt32(hdr, 4, cnt);
+
+ tinyPack.write(Constants.PACK_SIGNATURE);
+ tinyPack.write(hdr, 0, 8);
+ return this;
+ }
+
+ public InMemoryPack write(int i) throws IOException {
+ tinyPack.write(i);
+ return this;
+ }
+
+ public InMemoryPack deflate(byte[] content)
+ throws IOException {
+ Deflater deflater = new Deflater();
+ byte[] buf = new byte[128];
+ deflater.setInput(content, 0, content.length);
+ deflater.finish();
+ do {
+ final int n = deflater.deflate(buf, 0, buf.length);
+ if (n > 0)
+ tinyPack.write(buf, 0, n);
+ } while (!deflater.finished());
+ return this;
+ }
+
+ public InMemoryPack copyRaw(AnyObjectId o) throws IOException {
+ o.copyRawTo(tinyPack);
+ return this;
+ }
+
+ public InMemoryPack digest() throws IOException {
+ MessageDigest md = Constants.newMessageDigest();
+ md.update(tinyPack.toByteArray());
+ tinyPack.write(md.digest());
+ return this;
+ }
+
+ public InputStream toInputStream() throws IOException {
+ return new ByteArrayInputStream(tinyPack.toByteArray());
+ }
+
+ public byte[] toByteArray() throws IOException {
+ return tinyPack.toByteArray();
+ }
+}
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/PackParserTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/PackParserTest.java
index f02428efc9..2fd82e7bdb 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/PackParserTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/PackParserTest.java
@@ -22,12 +22,10 @@ import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
-import java.security.MessageDigest;
import java.text.MessageFormat;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
-import java.util.zip.Deflater;
import org.eclipse.jgit.errors.TooLargeObjectInPackException;
import org.eclipse.jgit.internal.JGitText;
@@ -42,8 +40,6 @@ import org.eclipse.jgit.lib.ObjectId;
import org.eclipse.jgit.lib.ObjectInserter;
import org.eclipse.jgit.lib.Repository;
import org.eclipse.jgit.revwalk.RevBlob;
-import org.eclipse.jgit.util.NB;
-import org.eclipse.jgit.util.TemporaryBuffer;
import org.eclipse.jgit.util.io.UnionInputStream;
import org.junit.After;
import org.junit.Test;
@@ -193,17 +189,14 @@ public class PackParserTest extends RepositoryTestCase {
a = d.blob("a");
}
- TemporaryBuffer.Heap pack = new TemporaryBuffer.Heap(1024);
-
- packHeader(pack, 1);
-
+ InMemoryPack pack = new InMemoryPack();
+ pack.header(1);
pack.write((Constants.OBJ_REF_DELTA) << 4 | 4);
- a.copyRawTo(pack);
- deflate(pack, new byte[] { 0x1, 0x1, 0x1, 'b' });
+ pack.copyRaw(a);
+ pack.deflate(new byte[] { 0x1, 0x1, 0x1, 'b' });
+ pack.digest();
- digest(pack);
-
- PackParser p = index(new ByteArrayInputStream(pack.toByteArray()));
+ PackParser p = index(pack.toInputStream());
p.setAllowThin(true);
p.parse(NullProgressMonitor.INSTANCE);
}
@@ -216,14 +209,14 @@ public class PackParserTest extends RepositoryTestCase {
assertTrue(db.getObjectDatabase().has(d.blob(data)));
}
- TemporaryBuffer.Heap pack = new TemporaryBuffer.Heap(1024);
- packHeader(pack, 1);
+ InMemoryPack pack = new InMemoryPack();
+ pack.header(1);
pack.write((Constants.OBJ_BLOB) << 4 | 0x80 | 1);
pack.write(1);
- deflate(pack, data);
- digest(pack);
+ pack.deflate(data);
+ pack.digest();
- PackParser p = index(new ByteArrayInputStream(pack.toByteArray()));
+ PackParser p = index(pack.toInputStream());
p.setAllowThin(false);
p.parse(NullProgressMonitor.INSTANCE);
}
@@ -236,16 +229,16 @@ public class PackParserTest extends RepositoryTestCase {
assertTrue(db.getObjectDatabase().has(d.blob(data)));
}
- TemporaryBuffer.Heap pack = new TemporaryBuffer.Heap(1024);
- packHeader(pack, 2);
+ InMemoryPack pack = new InMemoryPack();
+ pack.header(2);
pack.write((Constants.OBJ_BLOB) << 4 | 10); // offset 12
- deflate(pack, data);
+ pack.deflate(data);
pack.write((Constants.OBJ_OFS_DELTA) << 4 | 4); // offset 31
pack.write(19);
- deflate(pack, new byte[] { 0xA, 0xB, 0x1, 'b' });
- digest(pack);
+ pack.deflate(new byte[] { 0xA, 0xB, 0x1, 'b' });
+ pack.digest();
- PackParser p = index(new ByteArrayInputStream(pack.toByteArray()));
+ PackParser p = index(pack.toInputStream());
p.parse(NullProgressMonitor.INSTANCE);
List<PackedObjectInfo> sortedObjectList = p.getSortedObjectList(null);
@@ -275,15 +268,15 @@ public class PackParserTest extends RepositoryTestCase {
a = d.blob("a");
}
- TemporaryBuffer.Heap pack = new TemporaryBuffer.Heap(1024);
- packHeader(pack, 1);
+ InMemoryPack pack = new InMemoryPack();
+ pack.header(1);
pack.write((Constants.OBJ_REF_DELTA) << 4 | 4);
- a.copyRawTo(pack);
- deflate(pack, new byte[] { 0x1, 0x1, 0x1, 'b' });
- digest(pack);
+ pack.copyRaw(a);
+ pack.deflate(new byte[] { 0x1, 0x1, 0x1, 'b' });
+ pack.digest();
PackParser p = index(new UnionInputStream(
- new ByteArrayInputStream(pack.toByteArray()),
+ pack.toInputStream(),
new ByteArrayInputStream(new byte[] { 0x7e })));
p.setAllowThin(true);
p.setCheckEofAfterPackFooter(true);
@@ -305,22 +298,21 @@ public class PackParserTest extends RepositoryTestCase {
d.blob(data);
}
- TemporaryBuffer.Heap pack = new TemporaryBuffer.Heap(1024);
-
- packHeader(pack, 1);
+ InMemoryPack pack = new InMemoryPack();
+ pack.header(1);
pack.write((Constants.OBJ_BLOB) << 4 | 10);
- deflate(pack, data);
- digest(pack);
+ pack.deflate(data);
+ pack.digest();
- PackParser p = index(new ByteArrayInputStream(pack.toByteArray()));
+ PackParser p = index(pack.toInputStream());
p.setMaxObjectSizeLimit(11);
p.parse(NullProgressMonitor.INSTANCE);
- p = index(new ByteArrayInputStream(pack.toByteArray()));
+ p = index(pack.toInputStream());
p.setMaxObjectSizeLimit(10);
p.parse(NullProgressMonitor.INSTANCE);
- p = index(new ByteArrayInputStream(pack.toByteArray()));
+ p = index(pack.toInputStream());
p.setMaxObjectSizeLimit(9);
try {
p.parse(NullProgressMonitor.INSTANCE);
@@ -339,21 +331,20 @@ public class PackParserTest extends RepositoryTestCase {
a = d.blob("a");
}
- TemporaryBuffer.Heap pack = new TemporaryBuffer.Heap(1024);
-
- packHeader(pack, 1);
+ InMemoryPack pack = new InMemoryPack();
+ pack.header(1);
pack.write((Constants.OBJ_REF_DELTA) << 4 | 14);
- a.copyRawTo(pack);
- deflate(pack, new byte[] { 1, 11, 11, 'a', '0', '1', '2', '3', '4',
+ pack.copyRaw(a);
+ pack.deflate(new byte[] { 1, 11, 11, 'a', '0', '1', '2', '3', '4',
'5', '6', '7', '8', '9' });
- digest(pack);
+ pack.digest();
- PackParser p = index(new ByteArrayInputStream(pack.toByteArray()));
+ PackParser p = index(pack.toInputStream());
p.setAllowThin(true);
p.setMaxObjectSizeLimit(14);
p.parse(NullProgressMonitor.INSTANCE);
- p = index(new ByteArrayInputStream(pack.toByteArray()));
+ p = index(pack.toInputStream());
p.setAllowThin(true);
p.setMaxObjectSizeLimit(13);
try {
@@ -373,20 +364,19 @@ public class PackParserTest extends RepositoryTestCase {
a = d.blob("0123456789");
}
- TemporaryBuffer.Heap pack = new TemporaryBuffer.Heap(1024);
-
- packHeader(pack, 1);
+ InMemoryPack pack = new InMemoryPack();
+ pack.header(1);
pack.write((Constants.OBJ_REF_DELTA) << 4 | 4);
- a.copyRawTo(pack);
- deflate(pack, new byte[] { 10, 11, 1, 'a' });
- digest(pack);
+ pack.copyRaw(a);
+ pack.deflate(new byte[] { 10, 11, 1, 'a' });
+ pack.digest();
- PackParser p = index(new ByteArrayInputStream(pack.toByteArray()));
+ PackParser p = index(pack.toInputStream());
p.setAllowThin(true);
p.setMaxObjectSizeLimit(11);
p.parse(NullProgressMonitor.INSTANCE);
- p = index(new ByteArrayInputStream(pack.toByteArray()));
+ p = index(pack.toInputStream());
p.setAllowThin(true);
p.setMaxObjectSizeLimit(10);
try {
@@ -406,12 +396,12 @@ public class PackParserTest extends RepositoryTestCase {
a = d.blob("a");
}
- TemporaryBuffer.Heap pack = new TemporaryBuffer.Heap(1024);
- packHeader(pack, 1);
+ InMemoryPack pack = new InMemoryPack();
+ pack.header(1);
pack.write((Constants.OBJ_REF_DELTA) << 4 | 4);
- a.copyRawTo(pack);
- deflate(pack, new byte[] { 0x1, 0x1, 0x1, 'b' });
- digest(pack);
+ pack.copyRaw(a);
+ pack.deflate(new byte[] { 0x1, 0x1, 0x1, 'b' });
+ pack.digest();
InputStream in = new ByteArrayInputStream(pack.toByteArray()) {
@Override
@@ -447,12 +437,12 @@ public class PackParserTest extends RepositoryTestCase {
a = d.blob("a");
}
- TemporaryBuffer.Heap pack = new TemporaryBuffer.Heap(32*1024);
- packHeader(pack, 1);
+ InMemoryPack pack = new InMemoryPack();
+ pack.header(1);
pack.write((Constants.OBJ_REF_DELTA) << 4 | 4);
- a.copyRawTo(pack);
- deflate(pack, new byte[] { 0x1, 0x1, 0x1, 'b' });
- digest(pack);
+ pack.copyRaw(a);
+ pack.deflate(new byte[] { 0x1, 0x1, 0x1, 'b' });
+ pack.digest();
byte packData[] = pack.toByteArray();
byte streamData[] = new byte[packData.length + 1];
@@ -476,14 +466,14 @@ public class PackParserTest extends RepositoryTestCase {
// Build a pack ~17k
int objects = 900;
- TemporaryBuffer.Heap pack = new TemporaryBuffer.Heap(32 * 1024);
- packHeader(pack, objects);
+ InMemoryPack pack = new InMemoryPack(32 * 1024);
+ pack.header(objects);
for (int i = 0; i < objects; i++) {
pack.write((Constants.OBJ_BLOB) << 4 | 10);
- deflate(pack, data);
+ pack.deflate(data);
}
- digest(pack);
+ pack.digest();
byte packData[] = pack.toByteArray();
byte streamData[] = new byte[packData.length + 1];
@@ -510,8 +500,9 @@ public class PackParserTest extends RepositoryTestCase {
}
int objects = 248;
- TemporaryBuffer.Heap pack = new TemporaryBuffer.Heap(32 * 1024);
- packHeader(pack, objects + 1);
+ InMemoryPack pack = new InMemoryPack(32 * 1024);
+ pack.header(objects + 1);
+
int offset = 13;
StringBuilder sb = new StringBuilder();
for (int i = 0; i < offset; i++)
@@ -529,16 +520,16 @@ public class PackParserTest extends RepositoryTestCase {
lenByte |= 1 << 7;
pack.write(lenByte);
}
- deflate(pack, Constants.encode(sb.toString()));
+ pack.deflate(Constants.encode(sb.toString()));
for (int i = 0; i < objects; i++) {
// The last pack header written falls across the 8192 byte boundary
// between [8189:8210]
pack.write((Constants.OBJ_REF_DELTA) << 4 | 4);
- b.copyRawTo(pack);
- deflate(pack, new byte[] { 0x1, 0x1, 0x1, 'b' });
+ pack.copyRaw(b);
+ pack.deflate(new byte[] { 0x1, 0x1, 0x1, 'b' });
}
- digest(pack);
+ pack.digest();
byte packData[] = pack.toByteArray();
byte streamData[] = new byte[packData.length + 1];
@@ -555,36 +546,6 @@ public class PackParserTest extends RepositoryTestCase {
assertEquals(0x7e, in.read());
}
- private static void packHeader(TemporaryBuffer.Heap tinyPack, int cnt)
- throws IOException {
- final byte[] hdr = new byte[8];
- NB.encodeInt32(hdr, 0, 2);
- NB.encodeInt32(hdr, 4, cnt);
-
- tinyPack.write(Constants.PACK_SIGNATURE);
- tinyPack.write(hdr, 0, 8);
- }
-
- private static void deflate(TemporaryBuffer.Heap tinyPack,
- final byte[] content)
- throws IOException {
- final Deflater deflater = new Deflater();
- final byte[] buf = new byte[128];
- deflater.setInput(content, 0, content.length);
- deflater.finish();
- do {
- final int n = deflater.deflate(buf, 0, buf.length);
- if (n > 0)
- tinyPack.write(buf, 0, n);
- } while (!deflater.finished());
- }
-
- private static void digest(TemporaryBuffer.Heap buf) throws IOException {
- MessageDigest md = Constants.newMessageDigest();
- md.update(buf.toByteArray());
- buf.write(md.digest());
- }
-
private ObjectInserter inserter;
@After
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/UploadPackTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/UploadPackTest.java
index 9755ed1b69..f9687f9f82 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/UploadPackTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/UploadPackTest.java
@@ -2168,12 +2168,12 @@ public class UploadPackTest {
/**
* <pre>
* remote:
- * foo <- foofoo <-- branchFoo
- * bar <- barbar <-- branchBar
+ * foo &lt;- foofoo &lt;-- branchFoo
+ * bar &lt;- barbar &lt;-- branchBar
*
* client:
- * foo <-- branchFoo
- * bar <-- branchBar
+ * foo &lt;-- branchFoo
+ * bar &lt;-- branchBar
*
* fetch(branchFoo) should send exactly 1 have (i.e. foo) from branchFoo
* </pre>
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/treewalk/filter/PathSuffixFilterTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/treewalk/filter/PathSuffixFilterTest.java
index 0b5a7356c8..d6f4f18f30 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/treewalk/filter/PathSuffixFilterTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/treewalk/filter/PathSuffixFilterTest.java
@@ -53,8 +53,8 @@ public class PathSuffixFilterTest extends RepositoryTestCase {
@Test
public void testEdgeCases() throws IOException {
ObjectId treeId = createTree("abc", "abcd", "bcd", "c");
- assertEquals(new ArrayList<String>(), getMatchingPaths("xbcd", treeId));
- assertEquals(new ArrayList<String>(), getMatchingPaths("abcx", treeId));
+ assertEquals(new ArrayList<>(), getMatchingPaths("xbcd", treeId));
+ assertEquals(new ArrayList<>(), getMatchingPaths("abcx", treeId));
assertEquals(Arrays.asList("abcd"), getMatchingPaths("abcd", treeId));
assertEquals(Arrays.asList("abcd", "bcd"), getMatchingPaths("bcd", treeId));
assertEquals(Arrays.asList("abc", "c"), getMatchingPaths("c", treeId));