From db776102566a472e76073997cddf5417cc02389b Mon Sep 17 00:00:00 2001 From: Shawn Pearce Date: Tue, 3 Jan 2017 14:23:57 -0800 Subject: [PATCH] Pack refs/tags/ with refs/heads/ This fixes a nasty performance issue for repositories that have many objects referenced through refs/tags/, but not in refs/heads/. Situations like this can arise when a project has made releases like refs/tags/v1.0, and then decides to orphan history and start over for version 2. The v1.0 objects are not reachable from master anymore, but are still live due to the v1.0 tag. When tags are packed in the GC_OTHER pack, bitmaps are not able to cover the repository's contents. This may cause very slow counting times during git clone, as the server must enumerate the ancient history under refs/tags/ to respond to the client. Clients by default always ask for all tags when asking for all heads during clone. This has been true since git-core commit 8434c2f1afedb (Apr 27 2008), when clone was converted to a builtin. Including tags in the main GC pack should still allow servers to benefit from the fast full pack reuse path when serving a clone to a client. Change-Id: I22e29517b5bc6fa3d6b19a19f13bef0c68afdca3 --- .../internal/storage/file/GcBasicPackingTest.java | 14 +++++--------- .../internal/storage/dfs/DfsGarbageCollector.java | 6 +++++- .../org/eclipse/jgit/internal/storage/file/GC.java | 10 +++++++++- 3 files changed, 19 insertions(+), 11 deletions(-) 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 41a1a5d3f5..762feed3c2 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 @@ -47,9 +47,10 @@ import static org.junit.Assert.assertEquals; import java.io.File; import java.io.IOException; +import java.util.ArrayList; 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; @@ -175,14 +176,9 @@ public class GcBasicPackingTest extends GcTestCase { stats = gc.getStatistics(); assertEquals(0, stats.numberOfLooseObjects); - Iterator pIt = repo.getObjectDatabase().getPacks().iterator(); - long c = pIt.next().getObjectCount(); - if (c == 9) - assertEquals(2, pIt.next().getObjectCount()); - else { - assertEquals(2, c); - assertEquals(9, pIt.next().getObjectCount()); - } + List packs = new ArrayList<>( + repo.getObjectDatabase().getPacks()); + assertEquals(11, packs.get(0).getObjectCount()); } @Test diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/DfsGarbageCollector.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/DfsGarbageCollector.java index 6f760caea1..f7d078fa3c 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/DfsGarbageCollector.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/DfsGarbageCollector.java @@ -253,7 +253,7 @@ public class DfsGarbageCollector { for (Ref ref : refsBefore) { if (ref.isSymbolic() || ref.getObjectId() == null) continue; - if (isHead(ref)) + if (isHead(ref) || isTag(ref)) allHeads.add(ref.getObjectId()); else if (RefTreeNames.isRefTree(refdb, ref.getName())) txnHeads.add(ref.getObjectId()); @@ -461,6 +461,10 @@ public class DfsGarbageCollector { return ref.getName().startsWith(Constants.R_HEADS); } + private static boolean isTag(Ref ref) { + return ref.getName().startsWith(Constants.R_TAGS); + } + private int objectsBefore() { int cnt = 0; for (DfsPackFile p : packsBefore) diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/GC.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/GC.java index 560db92401..d67b9fa299 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/GC.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/GC.java @@ -607,7 +607,7 @@ public class GC { nonHeads.addAll(listRefLogObjects(ref, 0)); if (ref.isSymbolic() || ref.getObjectId() == null) continue; - if (ref.getName().startsWith(Constants.R_HEADS)) + if (isHead(ref) || isTag(ref)) allHeads.add(ref.getObjectId()); else if (RefTreeNames.isRefTree(refdb, ref.getName())) txnHeads.add(ref.getObjectId()); @@ -660,6 +660,14 @@ public class GC { return ret; } + private static boolean isHead(Ref ref) { + return ref.getName().startsWith(Constants.R_HEADS); + } + + private static boolean isTag(Ref ref) { + return ref.getName().startsWith(Constants.R_TAGS); + } + /** * @param ref * the ref which log should be inspected -- 2.39.5