Browse Source

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
stable-4.7
Shawn Pearce 8 years ago
parent
commit
db77610256
  1. 14
      org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/GcBasicPackingTest.java
  2. 6
      org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/DfsGarbageCollector.java
  3. 10
      org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/GC.java

14
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.File;
import java.io.IOException; import java.io.IOException;
import java.util.ArrayList;
import java.util.Collection; import java.util.Collection;
import java.util.Date; import java.util.Date;
import java.util.Iterator; import java.util.List;
import org.eclipse.jgit.junit.TestRepository.BranchBuilder; import org.eclipse.jgit.junit.TestRepository.BranchBuilder;
import org.eclipse.jgit.revwalk.RevCommit; import org.eclipse.jgit.revwalk.RevCommit;
@ -175,14 +176,9 @@ public class GcBasicPackingTest extends GcTestCase {
stats = gc.getStatistics(); stats = gc.getStatistics();
assertEquals(0, stats.numberOfLooseObjects); assertEquals(0, stats.numberOfLooseObjects);
Iterator<PackFile> pIt = repo.getObjectDatabase().getPacks().iterator(); List<PackFile> packs = new ArrayList<>(
long c = pIt.next().getObjectCount(); repo.getObjectDatabase().getPacks());
if (c == 9) assertEquals(11, packs.get(0).getObjectCount());
assertEquals(2, pIt.next().getObjectCount());
else {
assertEquals(2, c);
assertEquals(9, pIt.next().getObjectCount());
}
} }
@Test @Test

6
org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/DfsGarbageCollector.java

@ -253,7 +253,7 @@ public class DfsGarbageCollector {
for (Ref ref : refsBefore) { for (Ref ref : refsBefore) {
if (ref.isSymbolic() || ref.getObjectId() == null) if (ref.isSymbolic() || ref.getObjectId() == null)
continue; continue;
if (isHead(ref)) if (isHead(ref) || isTag(ref))
allHeads.add(ref.getObjectId()); allHeads.add(ref.getObjectId());
else if (RefTreeNames.isRefTree(refdb, ref.getName())) else if (RefTreeNames.isRefTree(refdb, ref.getName()))
txnHeads.add(ref.getObjectId()); txnHeads.add(ref.getObjectId());
@ -461,6 +461,10 @@ public class DfsGarbageCollector {
return ref.getName().startsWith(Constants.R_HEADS); return ref.getName().startsWith(Constants.R_HEADS);
} }
private static boolean isTag(Ref ref) {
return ref.getName().startsWith(Constants.R_TAGS);
}
private int objectsBefore() { private int objectsBefore() {
int cnt = 0; int cnt = 0;
for (DfsPackFile p : packsBefore) for (DfsPackFile p : packsBefore)

10
org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/GC.java

@ -607,7 +607,7 @@ public class GC {
nonHeads.addAll(listRefLogObjects(ref, 0)); nonHeads.addAll(listRefLogObjects(ref, 0));
if (ref.isSymbolic() || ref.getObjectId() == null) if (ref.isSymbolic() || ref.getObjectId() == null)
continue; continue;
if (ref.getName().startsWith(Constants.R_HEADS)) if (isHead(ref) || isTag(ref))
allHeads.add(ref.getObjectId()); allHeads.add(ref.getObjectId());
else if (RefTreeNames.isRefTree(refdb, ref.getName())) else if (RefTreeNames.isRefTree(refdb, ref.getName()))
txnHeads.add(ref.getObjectId()); txnHeads.add(ref.getObjectId());
@ -660,6 +660,14 @@ public class GC {
return ret; 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 * @param ref
* the ref which log should be inspected * the ref which log should be inspected

Loading…
Cancel
Save