GC: don't loosen doomed objects

If the pruneexpire config is set to "now", then any unreferenced loose
objects are immediately eligible for gc.  So there is no need to
actually write the loose objects.

Users who run hosting services which sometimes accept large, entirely
garbage packs might set the following configurations:

gc.pruneExpire = now
gc.prunePackExpire = 2.weeks

Then garbage objects will be kept around in packs, but after two weeks
the packs themselves will get deleted.

For client-side users of jgit, the default settings will loosen
garbage objects, and, after an hour, delete the old packs in which
they resided.

Change-Id: I8f686ac60b40181b1ee92ac6c313c3f33b55c44c
Signed-off-by: David Turner <dturner@twosigma.com>
This commit is contained in:
David Turner 2017-02-16 13:43:49 -05:00
parent e43db8ebf6
commit d3962fef6b
2 changed files with 49 additions and 4 deletions

View File

@ -55,8 +55,10 @@
import java.util.List;
import org.eclipse.jgit.junit.TestRepository.BranchBuilder;
import org.eclipse.jgit.lib.ConfigConstants;
import org.eclipse.jgit.lib.RefUpdate;
import org.eclipse.jgit.revwalk.RevCommit;
import org.eclipse.jgit.storage.file.FileBasedConfig;
import org.eclipse.jgit.storage.pack.PackConfig;
import org.junit.Test;
import org.junit.experimental.theories.DataPoints;
@ -247,7 +249,44 @@ public void testDonePruneTooYoungPacks() throws Exception {
// times
assertEquals(6, stats.numberOfPackedObjects);
assertEquals(1, stats.numberOfPackFiles);
}
@Test
public void testImmediatePruning() throws Exception {
BranchBuilder bb = tr.branch("refs/heads/master");
bb.commit().message("M").add("M", "M").create();
String tempRef = "refs/heads/soon-to-be-unreferenced";
BranchBuilder bb2 = tr.branch(tempRef);
bb2.commit().message("M").add("M", "M").create();
gc.setExpireAgeMillis(0);
gc.gc();
stats = gc.getStatistics();
fsTick();
// delete the temp ref, orphaning its commit
RefUpdate update = tr.getRepository().getRefDatabase().newUpdate(tempRef, false);
update.setForceUpdate(true);
update.delete();
bb.commit().message("B").add("B", "Q").create();
// We want to immediately prune deleted objects
FileBasedConfig config = repo.getConfig();
config.setString(ConfigConstants.CONFIG_GC_SECTION, null,
ConfigConstants.CONFIG_KEY_PRUNEEXPIRE, "now");
config.save();
//And we don't want to keep packs full of dead objects
gc.setPackExpireAgeMillis(0);
gc.gc();
stats = gc.getStatistics();
assertEquals(0, stats.numberOfLooseObjects);
assertEquals(6, stats.numberOfPackedObjects);
assertEquals(1, stats.numberOfPackFiles);
}
@Test

View File

@ -274,7 +274,8 @@ private void deleteOldPacks(Collection<PackFile> oldPacks,
ObjectReader reader = repo.newObjectReader();
ObjectDirectory dir = repo.getObjectDatabase();
ObjectDirectoryInserter inserter = dir.newInserter();
boolean shouldLoosen = getExpireDate() < Long.MAX_VALUE;
boolean shouldLoosen = !"now".equals(getPruneExpireStr()) && //$NON-NLS-1$
getExpireDate() < Long.MAX_VALUE;
prunePreserved();
long packExpireDate = getPackExpireDate();
@ -297,6 +298,7 @@ private void deleteOldPacks(Collection<PackFile> oldPacks,
prunePack(oldName);
}
}
// close the complete object database. That's my only chance to force
// rescanning and to detect that certain pack files are now deleted.
repo.getObjectDatabase().close();
@ -599,9 +601,7 @@ private long getExpireDate() throws ParseException {
long expireDate = Long.MAX_VALUE;
if (expire == null && expireAgeMillis == -1) {
String pruneExpireStr = repo.getConfig().getString(
ConfigConstants.CONFIG_GC_SECTION, null,
ConfigConstants.CONFIG_KEY_PRUNEEXPIRE);
String pruneExpireStr = getPruneExpireStr();
if (pruneExpireStr == null)
pruneExpireStr = PRUNE_EXPIRE_DEFAULT;
expire = GitDateParser.parse(pruneExpireStr, null, SystemReader
@ -615,6 +615,12 @@ private long getExpireDate() throws ParseException {
return expireDate;
}
private String getPruneExpireStr() {
return repo.getConfig().getString(
ConfigConstants.CONFIG_GC_SECTION, null,
ConfigConstants.CONFIG_KEY_PRUNEEXPIRE);
}
private long getPackExpireDate() throws ParseException {
long packExpireDate = Long.MAX_VALUE;