aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--org.eclipse.jgit.junit/src/org/eclipse/jgit/junit/TestRepository.java11
-rw-r--r--org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/GcBasicPackingTest.java194
-rw-r--r--org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/GcTestCase.java4
-rw-r--r--org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/pack/PackWriterBitmapPreparerTest.java136
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/pack/PackWriter.java7
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/pack/PackWriterBitmapPreparer.java375
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/storage/pack/PackConfig.java283
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/util/FileUtils.java25
8 files changed, 835 insertions, 200 deletions
diff --git a/org.eclipse.jgit.junit/src/org/eclipse/jgit/junit/TestRepository.java b/org.eclipse.jgit.junit/src/org/eclipse/jgit/junit/TestRepository.java
index 98e29d7083..251e65f553 100644
--- a/org.eclipse.jgit.junit/src/org/eclipse/jgit/junit/TestRepository.java
+++ b/org.eclipse.jgit.junit/src/org/eclipse/jgit/junit/TestRepository.java
@@ -208,6 +208,17 @@ public class TestRepository<R extends Repository> {
return new Date(mockSystemReader.getCurrentTime());
}
+ /**
+ * @return current date.
+ *
+ * @deprecated Use {@link #getDate()} instead.
+ */
+ @Deprecated
+ public Date getClock() {
+ // Remove once Gitiles and Gerrit are using the updated JGit.
+ return getDate();
+ }
+
/** @return timezone used for default identities. */
public TimeZone getTimeZone() {
return mockSystemReader.getTimeZone();
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/GcBasicPackingTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/GcBasicPackingTest.java
index 4f6d249c5f..5a91e173bf 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/GcBasicPackingTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/GcBasicPackingTest.java
@@ -44,12 +44,15 @@
package org.eclipse.jgit.internal.storage.file;
import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
import java.io.File;
import java.io.IOException;
+import java.util.Arrays;
import java.util.Collection;
import java.util.Date;
import java.util.Iterator;
+import java.util.List;
import org.eclipse.jgit.junit.TestRepository.BranchBuilder;
import org.eclipse.jgit.revwalk.RevCommit;
@@ -132,7 +135,8 @@ public class GcBasicPackingTest extends GcTestCase {
}
@Theory
- public void testPackCommitsAndLooseOne(boolean aggressive) throws Exception {
+ public void testPackCommitsAndLooseOne(boolean aggressive)
+ throws Exception {
BranchBuilder bb = tr.branch("refs/heads/master");
RevCommit first = bb.commit().add("A", "A").add("B", "B").create();
bb.commit().add("A", "A2").add("B", "B2").create();
@@ -226,71 +230,139 @@ public class GcBasicPackingTest extends GcTestCase {
}
@Test
- public void testCommitRangeForBitmaps() throws Exception {
- BranchBuilder bb1 = tr.branch("refs/heads/master");
- bb1.commit().message("A1").add("A1", "A1").create();
- bb1.commit().message("B1").add("B1", "B1").create();
- bb1.commit().message("C1").add("C1", "C1").create();
- BranchBuilder bb2 = tr.branch("refs/heads/working");
- bb2.commit().message("A2").add("A2", "A2").create();
- bb2.commit().message("B2").add("B2", "B2").create();
- bb2.commit().message("C2").add("C2", "C2").create();
-
- // Consider all commits. Since history isn't deep all commits are
- // selected.
- configureGcRange(gc, -1);
- gc.gc();
- assertEquals(6, gc.getStatistics().numberOfBitmaps);
-
- // Range==0 means don't examine commit history, create bitmaps only for
- // branch tips, C1 & C2.
- configureGcRange(gc, 0);
- gc.gc();
- assertEquals(2, gc.getStatistics().numberOfBitmaps);
-
- // Consider only the most recent commit (C2, which is also a branch
- // tip).
- configureGcRange(gc, 1);
- gc.gc();
- assertEquals(2, gc.getStatistics().numberOfBitmaps);
-
- // Consider only the two most recent commits, C2 & B2. C1 gets included
- // too since it is a branch tip.
- configureGcRange(gc, 2);
- gc.gc();
- assertEquals(3, gc.getStatistics().numberOfBitmaps);
-
- // Consider C2 & B2 & A2. C1 gets included too since it is a branch tip.
- configureGcRange(gc, 3);
- gc.gc();
- assertEquals(4, gc.getStatistics().numberOfBitmaps);
+ public void testBitmapSpansNoMerges() throws Exception {
+ /*
+ * Commit counts -> expected bitmap counts for history without merges.
+ * The top 100 contiguous commits should always have bitmaps, and the
+ * "recent" bitmaps beyond that are spaced out every 100-200 commits.
+ * (Starting at 100, the next 100 commits are searched for a merge
+ * commit. Since one is not found, the spacing between commits is 200.
+ */
+ int[][] bitmapCounts = { //
+ { 1, 1 }, { 50, 50 }, { 99, 99 }, { 100, 100 }, { 101, 100 },
+ { 200, 100 }, { 201, 100 }, { 299, 100 }, { 300, 101 },
+ { 301, 101 }, { 401, 101 }, { 499, 101 }, { 500, 102 }, };
+ int currentCommits = 0;
+ BranchBuilder bb = tr.branch("refs/heads/main");
+
+ for (int[] counts : bitmapCounts) {
+ int nextCommitCount = counts[0];
+ int expectedBitmapCount = counts[1];
+ assertTrue(nextCommitCount > currentCommits); // programming error
+ for (int i = currentCommits; i < nextCommitCount; i++) {
+ String str = "A" + i;
+ bb.commit().message(str).add(str, str).create();
+ }
+ currentCommits = nextCommitCount;
+
+ gc.setExpireAgeMillis(0); // immediately delete old packs
+ gc.gc();
+ assertEquals(currentCommits * 3, // commit/tree/object
+ gc.getStatistics().numberOfPackedObjects);
+ assertEquals(currentCommits + " commits: ", expectedBitmapCount,
+ gc.getStatistics().numberOfBitmaps);
+ }
+ }
- // Consider C2 & B2 & A2 & C1.
- configureGcRange(gc, 4);
- gc.gc();
- assertEquals(4, gc.getStatistics().numberOfBitmaps);
+ @Test
+ public void testBitmapSpansWithMerges() throws Exception {
+ /*
+ * Commits that are merged. Since 55 is in the oldest history it is
+ * never considered. Searching goes from oldest to newest so 115 is the
+ * first merge commit found. After that the range 116-216 is ignored so
+ * 175 is never considered.
+ */
+ List<Integer> merges = Arrays.asList(Integer.valueOf(55),
+ Integer.valueOf(115), Integer.valueOf(175),
+ Integer.valueOf(235));
+ /*
+ * Commit counts -> expected bitmap counts for history with merges. The
+ * top 100 contiguous commits should always have bitmaps, and the
+ * "recent" bitmaps beyond that are spaced out every 100-200 commits.
+ * Merges in the < 100 range have no effect and merges in the > 100
+ * range will only be considered for commit counts > 200.
+ */
+ int[][] bitmapCounts = { //
+ { 1, 1 }, { 55, 55 }, { 56, 57 }, // +1 bitmap from branch A55
+ { 99, 100 }, // still +1 branch @55
+ { 100, 100 }, // 101 commits, only 100 newest
+ { 116, 100 }, // @55 still in 100 newest bitmaps
+ { 176, 101 }, // @55 branch tip is not in 100 newest
+ { 213, 101 }, // 216 commits, @115&@175 in 100 newest
+ { 214, 102 }, // @55 branch tip, merge @115, @177 in newest
+ { 236, 102 }, // all 4 merge points in history
+ { 273, 102 }, // 277 commits, @175&@235 in newest
+ { 274, 103 }, // @55, @115, merge @175, @235 in newest
+ { 334, 103 }, // @55,@115,@175, @235 in newest
+ { 335, 104 }, // @55,@115,@175, merge @235
+ { 435, 104 }, // @55,@115,@175,@235 tips
+ { 436, 104 }, // force @236
+ };
+
+ int currentCommits = 0;
+ BranchBuilder bb = tr.branch("refs/heads/main");
+
+ for (int[] counts : bitmapCounts) {
+ int nextCommitCount = counts[0];
+ int expectedBitmapCount = counts[1];
+ assertTrue(nextCommitCount > currentCommits); // programming error
+ for (int i = currentCommits; i < nextCommitCount; i++) {
+ String str = "A" + i;
+ if (!merges.contains(Integer.valueOf(i))) {
+ bb.commit().message(str).add(str, str).create();
+ } else {
+ BranchBuilder bbN = tr.branch("refs/heads/A" + i);
+ bb.commit().message(str).add(str, str)
+ .parent(bbN.commit().create()).create();
+ }
+ }
+ currentCommits = nextCommitCount;
+
+ gc.setExpireAgeMillis(0); // immediately delete old packs
+ gc.gc();
+ assertEquals(currentCommits + " commits: ", expectedBitmapCount,
+ gc.getStatistics().numberOfBitmaps);
+ }
+ }
- // Consider C2 & B2 & A2 & C1 & B1.
- configureGcRange(gc, 5);
- gc.gc();
- assertEquals(5, gc.getStatistics().numberOfBitmaps);
+ @Test
+ public void testBitmapsForExcessiveBranches() throws Exception {
+ int oneDayInSeconds = 60 * 60 * 24;
+
+ // All of branch A is committed on day1
+ BranchBuilder bbA = tr.branch("refs/heads/A");
+ for (int i = 0; i < 1001; i++) {
+ String msg = "A" + i;
+ bbA.commit().message(msg).add(msg, msg).create();
+ }
+ // All of in branch B is committed on day91
+ tr.tick(oneDayInSeconds * 90);
+ BranchBuilder bbB = tr.branch("refs/heads/B");
+ for (int i = 0; i < 1001; i++) {
+ String msg = "B" + i;
+ bbB.commit().message(msg).add(msg, msg).create();
+ }
+ // Create 100 other branches with a single commit
+ for (int i = 0; i < 100; i++) {
+ BranchBuilder bb = tr.branch("refs/heads/N" + i);
+ String msg = "singlecommit" + i;
+ bb.commit().message(msg).add(msg, msg).create();
+ }
+ // now is day92
+ tr.tick(oneDayInSeconds);
- // Consider all six commits.
- configureGcRange(gc, 6);
- gc.gc();
- assertEquals(6, gc.getStatistics().numberOfBitmaps);
+ // Since there are no merges, commits in recent history are selected
+ // every 200 commits.
+ final int commitsForSparseBranch = 1 + (1001 / 200);
+ final int commitsForFullBranch = 100 + (901 / 200);
+ final int commitsForShallowBranches = 100;
- // Input is out of range but should be capped to the total number of
- // commits.
- configureGcRange(gc, 1000);
+ // Excessive branch history pruning, one old branch.
gc.gc();
- assertEquals(6, gc.getStatistics().numberOfBitmaps);
- }
-
- private void configureGcRange(GC myGc, int range) {
- PackConfig pconfig = new PackConfig(repo);
- pconfig.setBitmapCommitRange(range);
- myGc.setPackConfig(pconfig);
+ assertEquals(
+ commitsForSparseBranch + commitsForFullBranch
+ + commitsForShallowBranches,
+ gc.getStatistics().numberOfBitmaps);
}
private void configureGc(GC myGc, boolean aggressive) {
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/GcTestCase.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/GcTestCase.java
index a764f0fddd..5abf625489 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/GcTestCase.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/GcTestCase.java
@@ -52,6 +52,7 @@ import org.eclipse.jgit.junit.TestRepository;
import org.eclipse.jgit.junit.TestRepository.CommitBuilder;
import org.eclipse.jgit.lib.AnyObjectId;
import org.eclipse.jgit.revwalk.RevCommit;
+import org.eclipse.jgit.revwalk.RevWalk;
import org.junit.After;
import org.junit.Before;
@@ -65,7 +66,8 @@ public abstract class GcTestCase extends LocalDiskRepositoryTestCase {
public void setUp() throws Exception {
super.setUp();
repo = createWorkRepository();
- tr = new TestRepository<FileRepository>((repo));
+ tr = new TestRepository<FileRepository>(repo, new RevWalk(repo),
+ mockSystemReader);
gc = new GC(repo);
}
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/pack/PackWriterBitmapPreparerTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/pack/PackWriterBitmapPreparerTest.java
new file mode 100644
index 0000000000..b0f92ffa0c
--- /dev/null
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/pack/PackWriterBitmapPreparerTest.java
@@ -0,0 +1,136 @@
+package org.eclipse.jgit.internal.storage.pack;
+
+import static org.eclipse.jgit.storage.pack.PackConfig.DEFAULT_BITMAP_DISTANT_COMMIT_SPAN;
+import static org.eclipse.jgit.storage.pack.PackConfig.DEFAULT_BITMAP_RECENT_COMMIT_COUNT;
+import static org.eclipse.jgit.storage.pack.PackConfig.DEFAULT_BITMAP_RECENT_COMMIT_SPAN;
+import static org.junit.Assert.assertEquals;
+
+import java.io.IOException;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.List;
+import java.util.Set;
+
+import org.eclipse.jgit.errors.IncorrectObjectTypeException;
+import org.eclipse.jgit.errors.MissingObjectException;
+import org.eclipse.jgit.internal.storage.file.PackBitmapIndexBuilder;
+import org.eclipse.jgit.lib.AbbreviatedObjectId;
+import org.eclipse.jgit.lib.AnyObjectId;
+import org.eclipse.jgit.lib.ObjectId;
+import org.eclipse.jgit.lib.ObjectLoader;
+import org.eclipse.jgit.lib.ObjectReader;
+import org.eclipse.jgit.storage.pack.PackConfig;
+import org.junit.Test;
+
+/** Tests for the {@link PackWriterBitmapPreparer}. */
+public class PackWriterBitmapPreparerTest {
+ private static class StubObjectReader extends ObjectReader {
+ @Override
+ public ObjectReader newReader() {
+ return null;
+ }
+
+ @Override
+ public Collection<ObjectId> resolve(AbbreviatedObjectId id)
+ throws IOException {
+ return null;
+ }
+
+ @Override
+ public ObjectLoader open(AnyObjectId objectId, int typeHint)
+ throws MissingObjectException, IncorrectObjectTypeException,
+ IOException {
+ return null;
+ }
+
+ @Override
+ public Set<ObjectId> getShallowCommits() throws IOException {
+ return null;
+ }
+
+ @Override
+ public void close() {
+ // stub
+ }
+ }
+
+ @Test
+ public void testNextSelectionDistanceForActiveBranch() throws Exception {
+ PackWriterBitmapPreparer preparer = newPeparer(
+ DEFAULT_BITMAP_RECENT_COMMIT_COUNT, // 20000
+ DEFAULT_BITMAP_RECENT_COMMIT_SPAN, // 100
+ DEFAULT_BITMAP_DISTANT_COMMIT_SPAN); // 5000
+ int[][] distancesAndSpans = { { 0, 100 }, { 100, 100 }, { 10000, 100 },
+ { 20000, 100 }, { 20100, 100 }, { 20102, 102 }, { 20200, 200 },
+ { 22200, 2200 }, { 24999, 4999 }, { 25000, 5000 },
+ { 50000, 5000 }, { 1000000, 5000 }, };
+
+ for (int[] pair : distancesAndSpans) {
+ assertEquals(pair[1], preparer.nextSpan(pair[0]));
+ }
+ }
+
+ @Test
+ public void testNextSelectionDistanceWithFewerRecentCommits()
+ throws Exception {
+ PackWriterBitmapPreparer preparer = newPeparer(1000,
+ DEFAULT_BITMAP_RECENT_COMMIT_SPAN, // 100
+ DEFAULT_BITMAP_DISTANT_COMMIT_SPAN); // 5000
+ int[][] distancesAndSpans = { { 0, 100 }, { 100, 100 }, { 1000, 100 },
+ { 1100, 100 }, { 1111, 111 }, { 2000, 1000 }, { 5999, 4999 },
+ { 6000, 5000 }, { 10000, 5000 }, { 50000, 5000 },
+ { 1000000, 5000 } };
+
+ for (int[] pair : distancesAndSpans) {
+ assertEquals(pair[1], preparer.nextSpan(pair[0]));
+ }
+ }
+
+ @Test
+ public void testNextSelectionDistanceWithSmallerRecentSpan()
+ throws Exception {
+ PackWriterBitmapPreparer preparer = newPeparer(
+ DEFAULT_BITMAP_RECENT_COMMIT_COUNT, // 20000
+ 10, // recent span
+ DEFAULT_BITMAP_DISTANT_COMMIT_SPAN); // 5000
+ int[][] distancesAndSpans = { { 0, 10 }, { 100, 10 }, { 10000, 10 },
+ { 20000, 10 }, { 20010, 10 }, { 20012, 12 }, { 20050, 50 },
+ { 20200, 200 }, { 22200, 2200 }, { 24999, 4999 },
+ { 25000, 5000 }, { 50000, 5000 }, { 1000000, 5000 } };
+
+ for (int[] pair : distancesAndSpans) {
+ assertEquals(pair[1], preparer.nextSpan(pair[0]));
+ }
+ }
+
+ @Test
+ public void testNextSelectionDistanceWithSmallerDistantSpan()
+ throws Exception {
+ PackWriterBitmapPreparer preparer = newPeparer(
+ DEFAULT_BITMAP_RECENT_COMMIT_COUNT, // 20000
+ DEFAULT_BITMAP_RECENT_COMMIT_SPAN, // 100
+ 1000);
+ int[][] distancesAndSpans = { { 0, 100 }, { 100, 100 }, { 10000, 100 },
+ { 20000, 100 }, { 20100, 100 }, { 20102, 102 }, { 20200, 200 },
+ { 20999, 999 }, { 21000, 1000 }, { 22000, 1000 },
+ { 25000, 1000 }, { 50000, 1000 }, { 1000000, 1000 } };
+
+ for (int[] pair : distancesAndSpans) {
+ assertEquals(pair[1], preparer.nextSpan(pair[0]));
+ }
+ }
+
+ private PackWriterBitmapPreparer newPeparer(int recentCount, int recentSpan,
+ int distantSpan) throws IOException {
+ List<ObjectToPack> objects = Collections.emptyList();
+ Set<ObjectId> wants = Collections.emptySet();
+ PackConfig config = new PackConfig();
+ config.setBitmapRecentCommitCount(recentCount);
+ config.setBitmapRecentCommitSpan(recentSpan);
+ config.setBitmapDistantCommitSpan(distantSpan);
+ PackBitmapIndexBuilder indexBuilder = new PackBitmapIndexBuilder(
+ objects);
+ return new PackWriterBitmapPreparer(new StubObjectReader(),
+ indexBuilder, null, wants, config);
+ }
+}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/pack/PackWriter.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/pack/PackWriter.java
index cb69074539..1bff6ba4cb 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/pack/PackWriter.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/pack/PackWriter.java
@@ -2015,13 +2015,10 @@ public class PackWriter implements AutoCloseable {
byName = null;
PackWriterBitmapPreparer bitmapPreparer = new PackWriterBitmapPreparer(
- reader, writeBitmaps, pm, stats.interestingObjects);
+ reader, writeBitmaps, pm, stats.interestingObjects, config);
- int commitRange = config.getBitmapCommitRange();
- if (commitRange < 0)
- commitRange = numCommits; // select from all commits
Collection<PackWriterBitmapPreparer.BitmapCommit> selectedCommits =
- bitmapPreparer.doCommitSelection(commitRange);
+ bitmapPreparer.selectCommits(numCommits);
beginPhase(PackingPhase.BUILDING_BITMAPS, pm, selectedCommits.size());
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/pack/PackWriterBitmapPreparer.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/pack/PackWriterBitmapPreparer.java
index efde4792ef..1834fef816 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/pack/PackWriterBitmapPreparer.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/pack/PackWriterBitmapPreparer.java
@@ -55,8 +55,6 @@ import java.util.Iterator;
import java.util.List;
import java.util.Set;
-import com.googlecode.javaewah.EWAHCompressedBitmap;
-
import org.eclipse.jgit.errors.IncorrectObjectTypeException;
import org.eclipse.jgit.errors.MissingObjectException;
import org.eclipse.jgit.internal.JGitText;
@@ -74,15 +72,22 @@ import org.eclipse.jgit.revwalk.ObjectWalk;
import org.eclipse.jgit.revwalk.RevCommit;
import org.eclipse.jgit.revwalk.RevObject;
import org.eclipse.jgit.revwalk.RevWalk;
+import org.eclipse.jgit.storage.pack.PackConfig;
import org.eclipse.jgit.util.BlockList;
+import org.eclipse.jgit.util.SystemReader;
-/** Helper class for the PackWriter to select commits for pack index bitmaps. */
+/**
+ * Helper class for the {@link PackWriter} to select commits for which to build
+ * pack index bitmaps.
+ */
class PackWriterBitmapPreparer {
- private static final Comparator<BitmapBuilder> BUILDER_BY_CARDINALITY_DSC =
- new Comparator<BitmapBuilder>() {
- public int compare(BitmapBuilder a, BitmapBuilder b) {
- return Integer.signum(b.cardinality() - a.cardinality());
+ private static final int DAY_IN_SECONDS = 24 * 60 * 60;
+
+ private static final Comparator<BitmapBuilderEntry> ORDER_BY_DESCENDING_CARDINALITY = new Comparator<BitmapBuilderEntry>() {
+ public int compare(BitmapBuilderEntry a, BitmapBuilderEntry b) {
+ return Integer.signum(b.getBuilder().cardinality()
+ - a.getBuilder().cardinality());
}
};
@@ -93,12 +98,18 @@ class PackWriterBitmapPreparer {
private final BitmapIndexImpl commitBitmapIndex;
private final PackBitmapIndexRemapper bitmapRemapper;
private final BitmapIndexImpl bitmapIndex;
- private final int minCommits = 100;
- private final int maxCommits = 5000;
+
+ private final int contiguousCommitCount;
+ private final int recentCommitCount;
+ private final int recentCommitSpan;
+ private final int distantCommitSpan;
+ private final int excessiveBranchCount;
+ private final long inactiveBranchTimestamp;
PackWriterBitmapPreparer(ObjectReader reader,
PackBitmapIndexBuilder writeBitmaps, ProgressMonitor pm,
- Set<? extends ObjectId> want) throws IOException {
+ Set<? extends ObjectId> want, PackConfig config)
+ throws IOException {
this.reader = reader;
this.writeBitmaps = writeBitmaps;
this.pm = pm;
@@ -107,69 +118,139 @@ class PackWriterBitmapPreparer {
this.bitmapRemapper = PackBitmapIndexRemapper.newPackBitmapIndex(
reader.getBitmapIndex(), writeBitmaps);
this.bitmapIndex = new BitmapIndexImpl(bitmapRemapper);
+ this.contiguousCommitCount = config.getBitmapContiguousCommitCount();
+ this.recentCommitCount = config.getBitmapRecentCommitCount();
+ this.recentCommitSpan = config.getBitmapRecentCommitSpan();
+ this.distantCommitSpan = config.getBitmapDistantCommitSpan();
+ this.excessiveBranchCount = config.getBitmapExcessiveBranchCount();
+ long now = SystemReader.getInstance().getCurrentTime();
+ long ageInSeconds = config.getBitmapInactiveBranchAgeInDays()
+ * DAY_IN_SECONDS;
+ this.inactiveBranchTimestamp = (now / 1000) - ageInSeconds;
}
- Collection<BitmapCommit> doCommitSelection(int commitRange)
- throws MissingObjectException, IncorrectObjectTypeException,
- IOException {
+ /**
+ * Returns the commit objects for which bitmap indices should be built.
+ *
+ * @param expectedCommitCount
+ * count of commits in the pack
+ * @return commit objects for which bitmap indices should be built
+ * @throws IncorrectObjectTypeException
+ * if any of the processed objects is not a commit
+ * @throws IOException
+ * on errors reading pack or index files
+ * @throws MissingObjectException
+ * if an expected object is missing
+ */
+ Collection<BitmapCommit> selectCommits(int expectedCommitCount)
+ throws IncorrectObjectTypeException, IOException,
+ MissingObjectException {
+ /*
+ * Thinking of bitmap indices as a cache, if we find bitmaps at or at a
+ * close ancestor to 'old' and 'new' when calculating old..new, then all
+ * objects can be calculated with minimal graph walking. A distribution
+ * that favors creating bitmaps for the most recent commits maximizes
+ * the cache hits for clients that are close to HEAD, which is the
+ * majority of calculations performed.
+ */
pm.beginTask(JGitText.get().selectingCommits, ProgressMonitor.UNKNOWN);
RevWalk rw = new RevWalk(reader);
rw.setRetainBody(false);
- WalkResult result = findPaths(rw, commitRange);
+ CommitSelectionHelper selectionHelper = setupTipCommitBitmaps(rw,
+ expectedCommitCount);
pm.endTask();
- int totCommits = result.commitsByOldest.length - result.commitStartPos;
+ int totCommits = selectionHelper.getCommitCount();
BlockList<BitmapCommit> selections = new BlockList<BitmapCommit>(
- totCommits / minCommits + 1);
- for (BitmapCommit reuse : result.reuse)
+ totCommits / recentCommitSpan + 1);
+ for (BitmapCommit reuse : selectionHelper.reusedCommits) {
selections.add(reuse);
+ }
if (totCommits == 0) {
- for (AnyObjectId id : result.peeledWant)
+ for (AnyObjectId id : selectionHelper.peeledWants) {
selections.add(new BitmapCommit(id, false, 0));
+ }
return selections;
}
pm.beginTask(JGitText.get().selectingCommits, totCommits);
+ int totalWants = selectionHelper.peeledWants.size();
- for (BitmapBuilder bitmapableCommits : result.paths) {
- int cardinality = bitmapableCommits.cardinality();
+ for (BitmapBuilderEntry entry : selectionHelper.tipCommitBitmaps) {
+ BitmapBuilder bitmap = entry.getBuilder();
+ int cardinality = bitmap.cardinality();
List<List<BitmapCommit>> running = new ArrayList<
List<BitmapCommit>>();
+ // Mark the current branch as inactive if its tip commit isn't
+ // recent and there are an excessive number of branches, to
+ // prevent memory bloat of computing too many bitmaps for stale
+ // branches.
+ boolean isActiveBranch = true;
+ if (totalWants > excessiveBranchCount
+ && !isRecentCommit(entry.getCommit())) {
+ isActiveBranch = false;
+ }
+
// Insert bitmaps at the offsets suggested by the
// nextSelectionDistance() heuristic.
int index = -1;
- int nextIn = nextSelectionDistance(0, cardinality);
- int nextFlg = nextIn == maxCommits ? PackBitmapIndex.FLAG_REUSE : 0;
- boolean mustPick = nextIn == 0;
- for (RevCommit c : result) {
- if (!bitmapableCommits.contains(c))
+ int nextIn = nextSpan(cardinality);
+ int nextFlg = nextIn == distantCommitSpan
+ ? PackBitmapIndex.FLAG_REUSE : 0;
+
+ // For the current branch, iterate through all commits from oldest
+ // to newest.
+ for (RevCommit c : selectionHelper) {
+ // Optimization: if we have found all the commits for this
+ // branch, stop searching
+ int distanceFromTip = cardinality - index - 1;
+ if (distanceFromTip == 0) {
+ break;
+ }
+
+ // Ignore commits that are not in this branch
+ if (!bitmap.contains(c)) {
continue;
+ }
index++;
nextIn--;
pm.update(1);
- // Always pick the items in want and prefer merge commits.
- if (result.peeledWant.remove(c)) {
- if (nextIn > 0)
+ // Always pick the items in wants, prefer merge commits.
+ if (selectionHelper.peeledWants.remove(c)) {
+ if (nextIn > 0) {
nextFlg = 0;
- } else if (!mustPick && ((nextIn > 0)
- || (c.getParentCount() <= 1 && nextIn > -minCommits))) {
- continue;
+ }
+ } else {
+ boolean stillInSpan = nextIn >= 0;
+ boolean isMergeCommit = c.getParentCount() > 1;
+ // Force selection if:
+ // a) we have exhausted the window looking for merges
+ // b) we are in the top commits of an active branch
+ // c) we are at a branch tip
+ boolean mustPick = (nextIn <= -recentCommitSpan)
+ || (isActiveBranch
+ && (distanceFromTip <= contiguousCommitCount))
+ || (distanceFromTip == 1); // most recent commit
+ if (!mustPick && (stillInSpan || !isMergeCommit)) {
+ continue;
+ }
}
+ // This commit is selected, calculate the next one.
int flags = nextFlg;
- nextIn = nextSelectionDistance(index, cardinality);
- nextFlg = nextIn == maxCommits ? PackBitmapIndex.FLAG_REUSE : 0;
- mustPick = nextIn == 0;
+ nextIn = nextSpan(distanceFromTip);
+ nextFlg = nextIn == distantCommitSpan
+ ? PackBitmapIndex.FLAG_REUSE : 0;
BitmapBuilder fullBitmap = commitBitmapIndex.newBitmapBuilder();
rw.reset();
rw.markStart(c);
- for (AnyObjectId objectId : result.reuse)
+ for (AnyObjectId objectId : selectionHelper.reusedCommits)
rw.markUninteresting(rw.parseCommit(objectId));
rw.setRevFilter(
PackWriterBitmapWalker.newRevFilter(null, fullBitmap));
@@ -182,8 +263,9 @@ class PackWriterBitmapPreparer {
List<BitmapCommit>>();
for (List<BitmapCommit> list : running) {
BitmapCommit last = list.get(list.size() - 1);
- if (fullBitmap.contains(last))
+ if (fullBitmap.contains(last)) {
matches.add(list);
+ }
}
List<BitmapCommit> match;
@@ -194,121 +276,176 @@ class PackWriterBitmapPreparer {
match = matches.get(0);
// Append to longest
for (List<BitmapCommit> list : matches) {
- if (list.size() > match.size())
+ if (list.size() > match.size()) {
match = list;
+ }
}
}
match.add(new BitmapCommit(c, !match.isEmpty(), flags));
writeBitmaps.addBitmap(c, fullBitmap, 0);
}
- for (List<BitmapCommit> list : running)
+ for (List<BitmapCommit> list : running) {
selections.addAll(list);
+ }
}
writeBitmaps.clearBitmaps(); // Remove the temporary commit bitmaps.
// Add the remaining peeledWant
- for (AnyObjectId remainingWant : result.peeledWant)
+ for (AnyObjectId remainingWant : selectionHelper.peeledWants) {
selections.add(new BitmapCommit(remainingWant, false, 0));
+ }
pm.endTask();
return selections;
}
- private WalkResult findPaths(RevWalk rw, int commitRange)
- throws MissingObjectException, IOException {
- BitmapBuilder reuseBitmap = commitBitmapIndex.newBitmapBuilder();
- List<BitmapCommit> reuse = new ArrayList<BitmapCommit>();
+ private boolean isRecentCommit(RevCommit revCommit) {
+ return revCommit.getCommitTime() > inactiveBranchTimestamp;
+ }
+
+ /**
+ * For each of the {@code want}s, which represent the tip commit of each
+ * branch, set up an initial {@link BitmapBuilder}. Reuse previously built
+ * bitmaps if possible.
+ *
+ * @param rw
+ * a {@link RevWalk} to find reachable objects in this repository
+ * @param expectedCommitCount
+ * expected count of commits. The actual count may be less due to
+ * unreachable garbage.
+ * @return a {@link CommitSelectionHelper} containing bitmaps for the tip
+ * commits
+ * @throws IncorrectObjectTypeException
+ * if any of the processed objects is not a commit
+ * @throws IOException
+ * on errors reading pack or index files
+ * @throws MissingObjectException
+ * if an expected object is missing
+ */
+ private CommitSelectionHelper setupTipCommitBitmaps(RevWalk rw,
+ int expectedCommitCount) throws IncorrectObjectTypeException,
+ IOException, MissingObjectException {
+ BitmapBuilder reuse = commitBitmapIndex.newBitmapBuilder();
+ List<BitmapCommit> reuseCommits = new ArrayList<BitmapCommit>();
for (PackBitmapIndexRemapper.Entry entry : bitmapRemapper) {
- if ((entry.getFlags() & FLAG_REUSE) != FLAG_REUSE)
+ if ((entry.getFlags() & FLAG_REUSE) != FLAG_REUSE) {
continue;
+ }
RevObject ro = rw.peel(rw.parseAny(entry));
if (ro instanceof RevCommit) {
RevCommit rc = (RevCommit) ro;
- reuse.add(new BitmapCommit(rc, false, entry.getFlags()));
+ reuseCommits.add(new BitmapCommit(rc, false, entry.getFlags()));
rw.markUninteresting(rc);
-
- EWAHCompressedBitmap bitmap = bitmapRemapper.ofObjectType(
- bitmapRemapper.getBitmap(rc), Constants.OBJ_COMMIT);
- writeBitmaps.addBitmap(rc, bitmap, 0);
- reuseBitmap.add(rc, Constants.OBJ_COMMIT);
+ // PackBitmapIndexRemapper.ofObjectType() ties the underlying
+ // bitmap in the old pack into the new bitmap builder.
+ bitmapRemapper.ofObjectType(bitmapRemapper.getBitmap(rc),
+ Constants.OBJ_COMMIT).trim();
+ reuse.add(rc, Constants.OBJ_COMMIT);
}
}
- writeBitmaps.clearBitmaps(); // Remove temporary bitmaps
// Do a RevWalk by commit time descending. Keep track of all the paths
// from the wants.
- List<BitmapBuilder> paths = new ArrayList<BitmapBuilder>(want.size());
+ List<BitmapBuilderEntry> tipCommitBitmaps = new ArrayList<BitmapBuilderEntry>(
+ want.size());
Set<RevCommit> peeledWant = new HashSet<RevCommit>(want.size());
for (AnyObjectId objectId : want) {
RevObject ro = rw.peel(rw.parseAny(objectId));
- if (ro instanceof RevCommit && !reuseBitmap.contains(ro)) {
+ if (ro instanceof RevCommit && !reuse.contains(ro)) {
RevCommit rc = (RevCommit) ro;
peeledWant.add(rc);
rw.markStart(rc);
BitmapBuilder bitmap = commitBitmapIndex.newBitmapBuilder();
- bitmap.or(reuseBitmap);
+ bitmap.or(reuse);
bitmap.add(rc, Constants.OBJ_COMMIT);
- paths.add(bitmap);
+ tipCommitBitmaps.add(new BitmapBuilderEntry(rc, bitmap));
}
}
- // Update the paths from the wants and create a list of commits in
- // reverse iteration order for the desired commit range.
- RevCommit[] commits = new RevCommit[commitRange];
+ // Create a list of commits in reverse order (older to newer).
+ RevCommit[] commits = new RevCommit[expectedCommitCount];
int pos = commits.length;
RevCommit rc;
while ((rc = rw.next()) != null && pos > 0) {
commits[--pos] = rc;
- for (BitmapBuilder path : paths) {
- if (path.contains(rc)) {
- for (RevCommit c : rc.getParents())
- path.add(c, Constants.OBJ_COMMIT);
+ for (BitmapBuilderEntry entry : tipCommitBitmaps) {
+ BitmapBuilder bitmap = entry.getBuilder();
+ if (bitmap.contains(rc)) {
+ for (RevCommit c : rc.getParents()) {
+ bitmap.add(c, Constants.OBJ_COMMIT);
+ }
}
}
-
pm.update(1);
}
- // Remove the reused bitmaps from the paths
- if (!reuse.isEmpty())
- for (BitmapBuilder bitmap : paths)
- bitmap.andNot(reuseBitmap);
+ // Remove the reused bitmaps from the tip commit bitmaps
+ if (!reuseCommits.isEmpty()) {
+ for (BitmapBuilderEntry entry : tipCommitBitmaps) {
+ entry.getBuilder().andNot(reuse);
+ }
+ }
- // Sort the paths
- List<BitmapBuilder> distinctPaths = new ArrayList<BitmapBuilder>(paths.size());
- while (!paths.isEmpty()) {
- Collections.sort(paths, BUILDER_BY_CARDINALITY_DSC);
- BitmapBuilder largest = paths.remove(0);
- distinctPaths.add(largest);
+ // Sort the tip commit bitmaps. Find the one containing the most
+ // commits, remove those commits from the remaining bitmaps, resort and
+ // repeat.
+ List<BitmapBuilderEntry> orderedTipCommitBitmaps = new ArrayList<>(
+ tipCommitBitmaps.size());
+ while (!tipCommitBitmaps.isEmpty()) {
+ Collections.sort(tipCommitBitmaps, ORDER_BY_DESCENDING_CARDINALITY);
+ BitmapBuilderEntry largest = tipCommitBitmaps.remove(0);
+ orderedTipCommitBitmaps.add(largest);
// Update the remaining paths, by removing the objects from
// the path that was just added.
- for (int i = paths.size() - 1; i >= 0; i--)
- paths.get(i).andNot(largest);
+ for (int i = tipCommitBitmaps.size() - 1; i >= 0; i--) {
+ tipCommitBitmaps.get(i).getBuilder()
+ .andNot(largest.getBuilder());
+ }
}
- return new WalkResult(peeledWant, commits, pos, distinctPaths, reuse);
+ return new CommitSelectionHelper(peeledWant, commits, pos,
+ orderedTipCommitBitmaps, reuseCommits);
}
- private int nextSelectionDistance(int idx, int cardinality) {
- if (idx > cardinality)
+ /*-
+ * Returns the desired distance to the next bitmap based on the distance
+ * from the tip commit. Only differentiates recent from distant spans,
+ * selectCommits() handles the contiguous commits at the tip for active
+ * or inactive branches.
+ *
+ * A graph of this function looks like this, where
+ * the X axis is the distance from the tip commit and the Y axis is the
+ * bitmap selection distance.
+ *
+ * 5000 ____...
+ * /
+ * /
+ * /
+ * /
+ * 100 _____/
+ * 0 20100 25000
+ *
+ * Linear scaling between 20100 and 25000 prevents spans >100 for distances
+ * <20000 (otherwise, a span of 5000 would be returned for a distance of
+ * 21000, and the range 16000-20000 would have no selections).
+ */
+ int nextSpan(int distanceFromTip) {
+ if (distanceFromTip < 0) {
throw new IllegalArgumentException();
- int idxFromStart = cardinality - idx;
- int mustRegionEnd = 100;
- if (idxFromStart <= mustRegionEnd)
- return 0;
+ }
// Commits more toward the start will have more bitmaps.
- int minRegionEnd = 20000;
- if (idxFromStart <= minRegionEnd)
- return Math.min(idxFromStart - mustRegionEnd, minCommits);
+ if (distanceFromTip <= recentCommitCount) {
+ return recentCommitSpan;
+ }
- // Commits more toward the end will have fewer.
- int next = Math.min(idxFromStart - minRegionEnd, maxCommits);
- return Math.max(next, minCommits);
+ int next = Math.min(distanceFromTip - recentCommitCount,
+ distantCommitSpan);
+ return Math.max(next, recentCommitSpan);
}
PackWriterBitmapWalker newBitmapWalker() {
@@ -316,12 +453,14 @@ class PackWriterBitmapPreparer {
new ObjectWalk(reader), bitmapIndex, null);
}
+ /**
+ * A commit object for which a bitmap index should be built.
+ */
static final class BitmapCommit extends ObjectId {
private final boolean reuseWalker;
private final int flags;
- private BitmapCommit(
- AnyObjectId objectId, boolean reuseWalker, int flags) {
+ BitmapCommit(AnyObjectId objectId, boolean reuseWalker, int flags) {
super(objectId);
this.reuseWalker = reuseWalker;
this.flags = flags;
@@ -336,21 +475,55 @@ class PackWriterBitmapPreparer {
}
}
- private static final class WalkResult implements Iterable<RevCommit> {
- private final Set<? extends ObjectId> peeledWant;
+ /**
+ * A POJO representing a Pair<RevCommit, BitmapBuidler>.
+ */
+ private static final class BitmapBuilderEntry {
+ private final RevCommit commit;
+
+ private final BitmapBuilder builder;
+
+ BitmapBuilderEntry(RevCommit commit, BitmapBuilder builder) {
+ this.commit = commit;
+ this.builder = builder;
+ }
+
+ RevCommit getCommit() {
+ return commit;
+ }
+
+ BitmapBuilder getBuilder() {
+ return builder;
+ }
+ }
+
+ /**
+ * Container for state used in the first phase of selecting commits, which
+ * walks all of the reachable commits via the branch tips (
+ * {@code peeledWants}), stores them in {@code commitsByOldest}, and sets up
+ * bitmaps for each branch tip ({@code tipCommitBitmaps}).
+ * {@code commitsByOldest} is initialized with an expected size of all
+ * commits, but may be smaller if some commits are unreachable, in which
+ * case {@code commitStartPos} will contain a positive offset to the root
+ * commit.
+ */
+ private static final class CommitSelectionHelper implements Iterable<RevCommit> {
+ final Set<? extends ObjectId> peeledWants;
+
+ final List<BitmapBuilderEntry> tipCommitBitmaps;
+ final Iterable<BitmapCommit> reusedCommits;
private final RevCommit[] commitsByOldest;
private final int commitStartPos;
- private final List<BitmapBuilder> paths;
- private final Iterable<BitmapCommit> reuse;
- private WalkResult(Set<? extends ObjectId> peeledWant,
+ CommitSelectionHelper(Set<? extends ObjectId> peeledWant,
RevCommit[] commitsByOldest, int commitStartPos,
- List<BitmapBuilder> paths, Iterable<BitmapCommit> reuse) {
- this.peeledWant = peeledWant;
+ List<BitmapBuilderEntry> bitmapEntries,
+ Iterable<BitmapCommit> reuse) {
+ this.peeledWants = peeledWant;
this.commitsByOldest = commitsByOldest;
this.commitStartPos = commitStartPos;
- this.paths = paths;
- this.reuse = reuse;
+ this.tipCommitBitmaps = bitmapEntries;
+ this.reusedCommits = reuse;
}
public Iterator<RevCommit> iterator() {
@@ -370,5 +543,9 @@ class PackWriterBitmapPreparer {
}
};
}
+
+ int getCommitCount() {
+ return commitsByOldest.length - commitStartPos;
+ }
}
}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/storage/pack/PackConfig.java b/org.eclipse.jgit/src/org/eclipse/jgit/storage/pack/PackConfig.java
index 40309962c7..d594e97671 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/storage/pack/PackConfig.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/storage/pack/PackConfig.java
@@ -139,13 +139,64 @@ public class PackConfig {
public static final boolean DEFAULT_BUILD_BITMAPS = true;
/**
- * Default range of commits for which to create bitmaps: {@value}
+ * Default count of most recent commits to select for bitmaps. Only applies
+ * when bitmaps are enabled: {@value}
*
- * @see #setBitmapCommitRange(int)
+ * @see #setBitmapContiguousCommitCount(int)
* @since 4.2
*/
- public static final int DEFAULT_BITMAP_COMMIT_RANGE = -1;
+ public static final int DEFAULT_BITMAP_CONTIGUOUS_COMMIT_COUNT = 100;
+ /**
+ * Count at which the span between selected commits changes from
+ * "bitmapRecentCommitSpan" to "bitmapDistantCommitSpan". Only applies when
+ * bitmaps are enabled: {@value}
+ *
+ * @see #setBitmapRecentCommitCount(int)
+ * @since 4.2
+ */
+ public static final int DEFAULT_BITMAP_RECENT_COMMIT_COUNT = 20000;
+
+ /**
+ * Default spacing between commits in recent history when selecting commits
+ * for bitmaps. Only applies when bitmaps are enabled: {@value}
+ *
+ * @see #setBitmapRecentCommitSpan(int)
+ * @since 4.2
+ */
+ public static final int DEFAULT_BITMAP_RECENT_COMMIT_SPAN = 100;
+
+ /**
+ * Default spacing between commits in distant history when selecting commits
+ * for bitmaps. Only applies when bitmaps are enabled: {@value}
+ *
+ * @see #setBitmapDistantCommitSpan(int)
+ * @since 4.2
+ */
+ public static final int DEFAULT_BITMAP_DISTANT_COMMIT_SPAN = 5000;
+
+ /**
+ * Default count of branches required to activate inactive branch commit
+ * selection. If the number of branches is less than this then bitmaps for
+ * the entire commit history of all branches will be created, otherwise
+ * branches marked as "inactive" will have coverage for only partial
+ * history: {@value}
+ *
+ * @see #setBitmapExcessiveBranchCount(int)
+ * @since 4.2
+ */
+ public static final int DEFAULT_BITMAP_EXCESSIVE_BRANCH_COUNT = 100;
+
+ /**
+ * Default age at which a branch is considered inactive. Age is taken as the
+ * number of days ago that the most recent commit was made to a branch. Only
+ * affects bitmap processing if bitmaps are enabled and the
+ * "excessive branch count" has been exceeded: {@value}
+ *
+ * @see #setBitmapInactiveBranchAgeInDays(int)
+ * @since 4.2
+ */
+ public static final int DEFAULT_BITMAP_INACTIVE_BRANCH_AGE_IN_DAYS = 90;
private int compressionLevel = Deflater.DEFAULT_COMPRESSION;
@@ -177,7 +228,17 @@ public class PackConfig {
private boolean buildBitmaps = DEFAULT_BUILD_BITMAPS;
- private int bitmapCommitRange = DEFAULT_BITMAP_COMMIT_RANGE;
+ private int bitmapContiguousCommitCount = DEFAULT_BITMAP_CONTIGUOUS_COMMIT_COUNT;
+
+ private int bitmapRecentCommitCount = DEFAULT_BITMAP_RECENT_COMMIT_COUNT;
+
+ private int bitmapRecentCommitSpan = DEFAULT_BITMAP_RECENT_COMMIT_SPAN;
+
+ private int bitmapDistantCommitSpan = DEFAULT_BITMAP_DISTANT_COMMIT_SPAN;
+
+ private int bitmapExcessiveBranchCount = DEFAULT_BITMAP_EXCESSIVE_BRANCH_COUNT;
+
+ private int bitmapInactiveBranchAgeInDays = DEFAULT_BITMAP_INACTIVE_BRANCH_AGE_IN_DAYS;
private boolean cutDeltaChains;
@@ -232,7 +293,12 @@ public class PackConfig {
this.executor = cfg.executor;
this.indexVersion = cfg.indexVersion;
this.buildBitmaps = cfg.buildBitmaps;
- this.bitmapCommitRange = cfg.bitmapCommitRange;
+ this.bitmapContiguousCommitCount = cfg.bitmapContiguousCommitCount;
+ this.bitmapRecentCommitCount = cfg.bitmapRecentCommitCount;
+ this.bitmapRecentCommitSpan = cfg.bitmapRecentCommitSpan;
+ this.bitmapDistantCommitSpan = cfg.bitmapDistantCommitSpan;
+ this.bitmapExcessiveBranchCount = cfg.bitmapExcessiveBranchCount;
+ this.bitmapInactiveBranchAgeInDays = cfg.bitmapInactiveBranchAgeInDays;
this.cutDeltaChains = cfg.cutDeltaChains;
}
@@ -661,7 +727,7 @@ public class PackConfig {
* oldest (most compatible) format available for the objects.
* @see PackIndexWriter
*/
- public void setIndexVersion(final int version) {
+ public void setIndexVersion(int version) {
indexVersion = version;
}
@@ -695,36 +761,159 @@ public class PackConfig {
}
/**
- * Get the range of commits for which to build bitmaps. The range starts
- * from the most recent commit.
+ * Get the count of most recent commits for which to build bitmaps.
*
- * A value of 0 creates bitmaps for only branch tips. A value of -1 creates
- * bitmaps spaced through the entire history of commits.
+ * Default setting: {@value #DEFAULT_BITMAP_CONTIGUOUS_COMMIT_COUNT}
*
- * Default setting: {@value #DEFAULT_BITMAP_COMMIT_RANGE}
+ * @return the count of most recent commits for which to build bitmaps
+ * @since 4.2
+ */
+ public int getBitmapContiguousCommitCount() {
+ return bitmapContiguousCommitCount;
+ }
+
+ /**
+ * Set the count of most recent commits for which to build bitmaps.
*
- * @return the range of commits for which to create bitmaps, starting with
- * the most recent commit
- * @see PackIndexWriter
+ * Default setting: {@value #DEFAULT_BITMAP_CONTIGUOUS_COMMIT_COUNT}
+ *
+ * @param count
+ * the count of most recent commits for which to build bitmaps
* @since 4.2
*/
- public int getBitmapCommitRange() {
- return bitmapCommitRange;
+ public void setBitmapContiguousCommitCount(int count) {
+ bitmapContiguousCommitCount = count;
}
/**
- * Set the range of commits for which to build bitmaps.
+ * Get the count at which to switch from "bitmapRecentCommitSpan" to
+ * "bitmapDistantCommitSpan".
*
- * Default setting: {@value #DEFAULT_BITMAP_COMMIT_RANGE}
+ * Default setting: {@value #DEFAULT_BITMAP_RECENT_COMMIT_COUNT}
*
- * @param range
- * the range of commits for which to create bitmaps, starting
- * with the most recent commit
- * @see PackIndexWriter
+ * @return the count for switching between recent and distant spans
+ * @since 4.2
+ */
+ public int getBitmapRecentCommitCount() {
+ return bitmapRecentCommitCount;
+ }
+
+ /**
+ * Set the count at which to switch from "bitmapRecentCommitSpan" to
+ * "bitmapDistantCommitSpan".
+ *
+ * Default setting: {@value #DEFAULT_BITMAP_RECENT_COMMIT_COUNT}
+ *
+ * @param count
+ * the count for switching between recent and distant spans
+ * @since 4.2
+ */
+ public void setBitmapRecentCommitCount(int count) {
+ bitmapRecentCommitCount = count;
+ }
+
+ /**
+ * Get the span of commits when building bitmaps for recent history.
+ *
+ * Default setting: {@value #DEFAULT_BITMAP_RECENT_COMMIT_SPAN}
+ *
+ * @return the span of commits when building bitmaps for recent history
+ * @since 4.2
+ */
+ public int getBitmapRecentCommitSpan() {
+ return bitmapRecentCommitSpan;
+ }
+
+ /**
+ * Set the span of commits when building bitmaps for recent history.
+ *
+ * Default setting: {@value #DEFAULT_BITMAP_RECENT_COMMIT_SPAN}
+ *
+ * @param span
+ * the span of commits when building bitmaps for recent history
+ * @since 4.2
+ */
+ public void setBitmapRecentCommitSpan(int span) {
+ bitmapRecentCommitSpan = span;
+ }
+
+ /**
+ * Get the span of commits when building bitmaps for distant history.
+ *
+ * Default setting: {@value #DEFAULT_BITMAP_DISTANT_COMMIT_SPAN}
+ *
+ * @return the span of commits when building bitmaps for distant history
+ * @since 4.2
+ */
+ public int getBitmapDistantCommitSpan() {
+ return bitmapDistantCommitSpan;
+ }
+
+ /**
+ * Set the span of commits when building bitmaps for distant history.
+ *
+ * Default setting: {@value #DEFAULT_BITMAP_DISTANT_COMMIT_SPAN}
+ *
+ * @param span
+ * the span of commits when building bitmaps for distant history
+ * @since 4.2
+ */
+ public void setBitmapDistantCommitSpan(int span) {
+ bitmapDistantCommitSpan = span;
+ }
+
+ /**
+ * Get the count of branches deemed "excessive". If the count of branches in
+ * a repository exceeds this number and bitmaps are enabled, "inactive"
+ * branches will have fewer bitmaps than "active" branches.
+ *
+ * Default setting: {@value #DEFAULT_BITMAP_EXCESSIVE_BRANCH_COUNT}
+ *
+ * @return the count of branches deemed "excessive"
+ * @since 4.2
+ */
+ public int getBitmapExcessiveBranchCount() {
+ return bitmapExcessiveBranchCount;
+ }
+
+ /**
+ * Set the count of branches deemed "excessive". If the count of branches in
+ * a repository exceeds this number and bitmaps are enabled, "inactive"
+ * branches will have fewer bitmaps than "active" branches.
+ *
+ * Default setting: {@value #DEFAULT_BITMAP_EXCESSIVE_BRANCH_COUNT}
+ *
+ * @param count
+ * the count of branches deemed "excessive"
+ * @since 4.2
+ */
+ public void setBitmapExcessiveBranchCount(int count) {
+ bitmapExcessiveBranchCount = count;
+ }
+
+ /**
+ * Get the the age in days that marks a branch as "inactive".
+ *
+ * Default setting: {@value #DEFAULT_BITMAP_INACTIVE_BRANCH_AGE_IN_DAYS}
+ *
+ * @return the age in days that marks a branch as "inactive"
+ * @since 4.2
+ */
+ public int getBitmapInactiveBranchAgeInDays() {
+ return bitmapInactiveBranchAgeInDays;
+ }
+
+ /**
+ * Set the the age in days that marks a branch as "inactive".
+ *
+ * Default setting: {@value #DEFAULT_BITMAP_INACTIVE_BRANCH_AGE_IN_DAYS}
+ *
+ * @param ageInDays
+ * the age in days that marks a branch as "inactive"
* @since 4.2
*/
- public void setBitmapCommitRange(final int range) {
- bitmapCommitRange = range;
+ public void setBitmapInactiveBranchAgeInDays(int ageInDays) {
+ bitmapInactiveBranchAgeInDays = ageInDays;
}
/**
@@ -756,21 +945,36 @@ public class PackConfig {
// These variables aren't standardized
//
setReuseDeltas(rc.getBoolean("pack", "reusedeltas", isReuseDeltas())); //$NON-NLS-1$ //$NON-NLS-2$
- setReuseObjects(rc.getBoolean("pack", "reuseobjects", isReuseObjects())); //$NON-NLS-1$ //$NON-NLS-2$
- setDeltaCompress(rc.getBoolean(
- "pack", "deltacompression", isDeltaCompress())); //$NON-NLS-1$ //$NON-NLS-2$
- setCutDeltaChains(rc.getBoolean(
- "pack", "cutdeltachains", getCutDeltaChains())); //$NON-NLS-1$ //$NON-NLS-2$
- setBuildBitmaps(rc.getBoolean("pack", "buildbitmaps", isBuildBitmaps())); //$NON-NLS-1$ //$NON-NLS-2$
- setBitmapCommitRange(
- rc.getInt("pack", "bitmapcommitrange", getBitmapCommitRange())); //$NON-NLS-1$ //$NON-NLS-2$
+ setReuseObjects(
+ rc.getBoolean("pack", "reuseobjects", isReuseObjects())); //$NON-NLS-1$ //$NON-NLS-2$
+ setDeltaCompress(
+ rc.getBoolean("pack", "deltacompression", isDeltaCompress())); //$NON-NLS-1$ //$NON-NLS-2$
+ setCutDeltaChains(
+ rc.getBoolean("pack", "cutdeltachains", getCutDeltaChains())); //$NON-NLS-1$ //$NON-NLS-2$
+ setBuildBitmaps(
+ rc.getBoolean("pack", "buildbitmaps", isBuildBitmaps())); //$NON-NLS-1$ //$NON-NLS-2$
+ setBitmapContiguousCommitCount(
+ rc.getInt("pack", "bitmapcontiguouscommitcount", //$NON-NLS-1$ //$NON-NLS-2$
+ getBitmapContiguousCommitCount()));
+ setBitmapRecentCommitCount(rc.getInt("pack", "bitmaprecentcommitcount", //$NON-NLS-1$ //$NON-NLS-2$
+ getBitmapRecentCommitCount()));
+ setBitmapRecentCommitSpan(rc.getInt("pack", "bitmaprecentcommitspan", //$NON-NLS-1$ //$NON-NLS-2$
+ getBitmapRecentCommitSpan()));
+ setBitmapDistantCommitSpan(rc.getInt("pack", "bitmapdistantcommitspan", //$NON-NLS-1$ //$NON-NLS-2$
+ getBitmapDistantCommitSpan()));
+ setBitmapExcessiveBranchCount(rc.getInt("pack", //$NON-NLS-1$
+ "bitmapexcessivebranchcount", getBitmapExcessiveBranchCount())); //$NON-NLS-1$
+ setBitmapInactiveBranchAgeInDays(
+ rc.getInt("pack", "bitmapinactivebranchageindays", //$NON-NLS-1$ //$NON-NLS-2$
+ getBitmapInactiveBranchAgeInDays()));
}
public String toString() {
final StringBuilder b = new StringBuilder();
b.append("maxDeltaDepth=").append(getMaxDeltaDepth()); //$NON-NLS-1$
b.append(", deltaSearchWindowSize=").append(getDeltaSearchWindowSize()); //$NON-NLS-1$
- b.append(", deltaSearchMemoryLimit=").append(getDeltaSearchMemoryLimit()); //$NON-NLS-1$
+ b.append(", deltaSearchMemoryLimit=") //$NON-NLS-1$
+ .append(getDeltaSearchMemoryLimit());
b.append(", deltaCacheSize=").append(getDeltaCacheSize()); //$NON-NLS-1$
b.append(", deltaCacheLimit=").append(getDeltaCacheLimit()); //$NON-NLS-1$
b.append(", compressionLevel=").append(getCompressionLevel()); //$NON-NLS-1$
@@ -781,7 +985,18 @@ public class PackConfig {
b.append(", reuseObjects=").append(isReuseObjects()); //$NON-NLS-1$
b.append(", deltaCompress=").append(isDeltaCompress()); //$NON-NLS-1$
b.append(", buildBitmaps=").append(isBuildBitmaps()); //$NON-NLS-1$
- b.append(", bitmapCommitRange=").append(getBitmapCommitRange()); //$NON-NLS-1$
+ b.append(", bitmapContiguousCommitCount=") //$NON-NLS-1$
+ .append(getBitmapContiguousCommitCount());
+ b.append(", bitmapRecentCommitCount=") //$NON-NLS-1$
+ .append(getBitmapRecentCommitCount());
+ b.append(", bitmapRecentCommitSpan=") //$NON-NLS-1$
+ .append(getBitmapRecentCommitSpan());
+ b.append(", bitmapDistantCommitSpan=") //$NON-NLS-1$
+ .append(getBitmapDistantCommitSpan());
+ b.append(", bitmapExcessiveBranchCount=") //$NON-NLS-1$
+ .append(getBitmapExcessiveBranchCount());
+ b.append(", bitmapInactiveBranchAge=") //$NON-NLS-1$
+ .append(getBitmapInactiveBranchAgeInDays());
return b.toString();
}
}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/util/FileUtils.java b/org.eclipse.jgit/src/org/eclipse/jgit/util/FileUtils.java
index 720fdedefb..6d0318c029 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/util/FileUtils.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/util/FileUtils.java
@@ -735,4 +735,29 @@ public class FileUtils {
}
return name;
}
+
+ /**
+ * Best-effort variation of {@link File#getCanonicalFile()} returning the
+ * input file if the file cannot be canonicalized instead of throwing
+ * {@link IOException}.
+ *
+ * @param file
+ * to be canonicalized; may be {@code null}
+ * @return canonicalized file, or the unchanged input file if
+ * canonicalization failed or if {@code file == null}
+ * @throws SecurityException
+ * if {@link File#getCanonicalFile()} throws one
+ * @since 4.2
+ */
+ public static File canonicalize(File file) {
+ if (file == null) {
+ return null;
+ }
+ try {
+ return file.getCanonicalFile();
+ } catch (IOException e) {
+ return file;
+ }
+ }
+
}