diff options
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; + } + } + } |