unsupportedOperation=Unsupported operation: {0}
untrackedFiles=Untracked files:
updating=Updating {0}..{1}
+usage_Aggressive=This option will cause gc to more aggressively optimize the repository at the expense of taking much more time
usage_Blame=Show what revision and author last modified each line
usage_CommandLineClientForamazonsS3Service=Command line client for Amazon's S3 service
usage_CommitAll=commit all modified and deleted files
package org.eclipse.jgit.pgm;
-import org.eclipse.jgit.internal.storage.file.FileRepository;
-import org.eclipse.jgit.internal.storage.file.GC;
+import org.eclipse.jgit.api.Git;
import org.eclipse.jgit.lib.TextProgressMonitor;
+import org.kohsuke.args4j.Option;
@Command(common = true, usage = "usage_Gc")
class Gc extends TextBuiltin {
+ @Option(name = "--aggressive", usage = "usage_Aggressive")
+ private boolean aggressive;
+
@Override
protected void run() throws Exception {
- GC gc = new GC((FileRepository) db);
- gc.setProgressMonitor(new TextProgressMonitor());
- gc.gc();
+ Git git = Git.wrap(db);
+ git.gc().setAggressive(aggressive)
+ .setProgressMonitor(new TextProgressMonitor()).call();
}
}
import org.eclipse.jgit.junit.TestRepository.BranchBuilder;
import org.eclipse.jgit.revwalk.RevCommit;
-import org.junit.Test;
+import org.eclipse.jgit.storage.pack.PackConfig;
+import org.junit.experimental.theories.DataPoints;
+import org.junit.experimental.theories.Theories;
+import org.junit.experimental.theories.Theory;
+import org.junit.runner.RunWith;
+@RunWith(Theories.class)
public class GcBasicPackingTest extends GcTestCase {
- @Test
- public void repackEmptyRepo_noPackCreated() throws IOException {
+ @DataPoints
+ public static boolean[] aggressiveValues = { true, false };
+
+ @Theory
+ public void repackEmptyRepo_noPackCreated(boolean aggressive)
+ throws IOException {
+ configureGc(gc, aggressive);
gc.repack();
assertEquals(0, repo.getObjectDatabase().getPacks().size());
}
- @Test
- public void testPackRepoWithNoRefs() throws Exception {
+ @Theory
+ public void testPackRepoWithNoRefs(boolean aggressive) throws Exception {
tr.commit().add("A", "A").add("B", "B").create();
stats = gc.getStatistics();
assertEquals(4, stats.numberOfLooseObjects);
assertEquals(0, stats.numberOfPackedObjects);
+ configureGc(gc, aggressive);
gc.gc();
stats = gc.getStatistics();
assertEquals(4, stats.numberOfLooseObjects);
assertEquals(0, stats.numberOfPackFiles);
}
- @Test
- public void testPack2Commits() throws Exception {
+ @Theory
+ public void testPack2Commits(boolean aggressive) throws Exception {
BranchBuilder bb = tr.branch("refs/heads/master");
bb.commit().add("A", "A").add("B", "B").create();
bb.commit().add("A", "A2").add("B", "B2").create();
stats = gc.getStatistics();
assertEquals(8, stats.numberOfLooseObjects);
assertEquals(0, stats.numberOfPackedObjects);
+ configureGc(gc, aggressive);
gc.gc();
stats = gc.getStatistics();
assertEquals(0, stats.numberOfLooseObjects);
assertEquals(1, stats.numberOfPackFiles);
}
- @Test
- public void testPackAllObjectsInOnePack() throws Exception {
+ @Theory
+ public void testPackAllObjectsInOnePack(boolean aggressive)
+ throws Exception {
tr.branch("refs/heads/master").commit().add("A", "A").add("B", "B")
.create();
stats = gc.getStatistics();
assertEquals(4, stats.numberOfLooseObjects);
assertEquals(0, stats.numberOfPackedObjects);
+ configureGc(gc, aggressive);
gc.gc();
stats = gc.getStatistics();
assertEquals(0, stats.numberOfLooseObjects);
assertEquals(1, stats.numberOfPackFiles);
}
- @Test
- public void testPackCommitsAndLooseOne() throws Exception {
+ @Theory
+ 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();
stats = gc.getStatistics();
assertEquals(8, stats.numberOfLooseObjects);
assertEquals(0, stats.numberOfPackedObjects);
+ configureGc(gc, aggressive);
gc.gc();
stats = gc.getStatistics();
assertEquals(0, stats.numberOfLooseObjects);
assertEquals(2, stats.numberOfPackFiles);
}
- @Test
- public void testNotPackTwice() throws Exception {
+ @Theory
+ public void testNotPackTwice(boolean aggressive) throws Exception {
BranchBuilder bb = tr.branch("refs/heads/master");
RevCommit first = bb.commit().message("M").add("M", "M").create();
bb.commit().message("B").add("B", "Q").create();
gc.setExpireAgeMillis(0);
fsTick();
+ configureGc(gc, aggressive);
gc.gc();
stats = gc.getStatistics();
assertEquals(0, stats.numberOfLooseObjects);
assertEquals(9, pIt.next().getObjectCount());
}
}
+
+ private void configureGc(GC myGc, boolean aggressive) {
+ PackConfig pconfig = new PackConfig(repo);
+ if (aggressive) {
+ pconfig.setDeltaSearchWindowSize(250);
+ pconfig.setMaxDeltaDepth(250);
+ pconfig.setReuseObjects(false);
+ } else
+ pconfig = new PackConfig(repo);
+ myGc.setPackConfig(pconfig);
+ }
}
import org.eclipse.jgit.internal.storage.file.FileRepository;
import org.eclipse.jgit.internal.storage.file.GC;
import org.eclipse.jgit.internal.storage.file.GC.RepoStatistics;
+import org.eclipse.jgit.lib.ConfigConstants;
import org.eclipse.jgit.lib.ProgressMonitor;
import org.eclipse.jgit.lib.Repository;
+import org.eclipse.jgit.lib.StoredConfig;
+import org.eclipse.jgit.storage.pack.PackConfig;
import org.eclipse.jgit.util.GitDateParser;
/**
* supported options and arguments of this command and a {@link #call()} method
* to finally execute the command. Each instance of this class should only be
* used for one invocation of the command (means: one call to {@link #call()})
- *
+ *
* @since 2.2
* @see <a href="http://www.kernel.org/pub/software/scm/git/docs/git-gc.html"
* >Git documentation about gc</a>
*/
public class GarbageCollectCommand extends GitCommand<Properties> {
+ /**
+ * Default value of maximum delta chain depth during aggressive garbage
+ * collection: {@value}
+ *
+ * @since 3.6
+ */
+ public static final int DEFAULT_GC_AGGRESSIVE_DEPTH = 250;
+
+ /**
+ * Default window size during packing during aggressive garbage collection:
+ * * {@value}
+ *
+ * @since 3.6
+ */
+ public static final int DEFAULT_GC_AGGRESSIVE_WINDOW = 250;
private ProgressMonitor monitor;
private Date expire;
+ private PackConfig pconfig;
+
/**
* @param repo
*/
if (!(repo instanceof FileRepository))
throw new UnsupportedOperationException(MessageFormat.format(
JGitText.get().unsupportedGC, repo.getClass().toString()));
+ pconfig = new PackConfig(repo);
}
/**
return this;
}
+ /**
+ * Whether to use aggressive mode or not. If set to true JGit behaves more
+ * similar to native git's "git gc --aggressive". If set to
+ * <code>true</code> compressed objects found in old packs are not reused
+ * but every object is compressed again. Configuration variables
+ * pack.window and pack.depth are set to 250 for this GC.
+ *
+ * @since 3.6
+ * @param aggressive
+ * whether to turn on or off aggressive mode
+ * @return this instance
+ */
+ public GarbageCollectCommand setAggressive(boolean aggressive) {
+ if (aggressive) {
+ StoredConfig repoConfig = repo.getConfig();
+ pconfig.setDeltaSearchWindowSize(repoConfig.getInt(
+ ConfigConstants.CONFIG_GC_SECTION,
+ ConfigConstants.CONFIG_KEY_AGGRESSIVE_WINDOW,
+ DEFAULT_GC_AGGRESSIVE_WINDOW));
+ pconfig.setMaxDeltaDepth(repoConfig.getInt(
+ ConfigConstants.CONFIG_GC_SECTION,
+ ConfigConstants.CONFIG_KEY_AGGRESSIVE_DEPTH,
+ DEFAULT_GC_AGGRESSIVE_DEPTH));
+ pconfig.setReuseObjects(false);
+ } else
+ pconfig = new PackConfig(repo);
+ return this;
+ }
+
@Override
public Properties call() throws GitAPIException {
checkCallable();
GC gc = new GC((FileRepository) repo);
+ gc.setPackConfig(pconfig);
gc.setProgressMonitor(monitor);
if (this.expire != null)
gc.setExpire(expire);
import org.eclipse.jgit.revwalk.ObjectWalk;
import org.eclipse.jgit.revwalk.RevObject;
import org.eclipse.jgit.revwalk.RevWalk;
+import org.eclipse.jgit.storage.pack.PackConfig;
import org.eclipse.jgit.treewalk.TreeWalk;
import org.eclipse.jgit.treewalk.filter.TreeFilter;
import org.eclipse.jgit.util.FileUtils;
private Date expire;
+ private PackConfig pconfig = null;
+
/**
* the refs which existed during the last call to {@link #repack()}. This is
* needed during {@link #prune(Set)} where we can optimize by looking at the
}
});
- PackWriter pw = new PackWriter(repo);
+ PackWriter pw = new PackWriter((pconfig == null) ? new PackConfig(repo) : pconfig, repo.newObjectReader());
try {
// prepare the PackWriter
pw.setDeltaBaseAsOffset(true);
expire = null;
}
+ /**
+ * Set the PackConfig used when (re-)writing packfiles. This allows to
+ * influence how packs are written and to implement something similar to
+ * "git gc --aggressive"
+ *
+ * @since 3.6
+ * @param pconfig
+ * the {@link PackConfig} used when writing packs
+ */
+ public void setPackConfig(PackConfig pconfig) {
+ this.pconfig = pconfig;
+ }
+
/**
* During gc() or prune() each unreferenced, loose object which has been
* created or modified after or at <code>expire</code> will not be pruned.
/** The "pruneexpire" key */
public static final String CONFIG_KEY_PRUNEEXPIRE = "pruneexpire";
+ /** The "aggressiveDepth" key */
+ public static final String CONFIG_KEY_AGGRESSIVE_DEPTH = "aggressiveDepth";
+
+ /** The "aggressiveWindow" key */
+ public static final String CONFIG_KEY_AGGRESSIVE_WINDOW = "aggressiveWindow";
+
/** The "mergeoptions" key */
public static final String CONFIG_KEY_MERGEOPTIONS = "mergeoptions";