/* * Copyright (C) 2012, Matthias Sohn and others * * This program and the accompanying materials are made available under the * terms of the Eclipse Distribution License v. 1.0 which is available at * https://www.eclipse.org/org/documents/edl-v10.php. * * SPDX-License-Identifier: BSD-3-Clause */ package org.eclipse.jgit.api; import java.io.IOException; import java.text.MessageFormat; import java.text.ParseException; import java.util.Date; import java.util.Properties; import java.util.concurrent.ExecutionException; import org.eclipse.jgit.api.errors.GitAPIException; import org.eclipse.jgit.api.errors.JGitInternalException; import org.eclipse.jgit.internal.JGitText; import org.eclipse.jgit.internal.storage.dfs.DfsGarbageCollector; import org.eclipse.jgit.internal.storage.dfs.DfsRepository; 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; /** * A class used to execute a {@code gc} command. It has setters for all * 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 Git documentation about gc */ public class GarbageCollectCommand extends GitCommand { /** * 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; private Boolean packKeptObjects; /** * Constructor for GarbageCollectCommand. * * @param repo * a {@link org.eclipse.jgit.lib.Repository} object. */ protected GarbageCollectCommand(Repository repo) { super(repo); pconfig = new PackConfig(repo); } /** * Set progress monitor * * @param monitor * a progress monitor * @return this instance */ public GarbageCollectCommand setProgressMonitor(ProgressMonitor monitor) { this.monitor = monitor; return this; } /** * During gc() or prune() each unreferenced, loose object which has been * created or modified after expire will not be pruned. Only * older objects may be pruned. If set to null then every object is a * candidate for pruning. Use {@link org.eclipse.jgit.util.GitDateParser} to * parse time formats used by git gc. * * @param expire * minimal age of objects to be pruned. * @return this instance */ public GarbageCollectCommand setExpire(Date expire) { this.expire = expire; 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 * true 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; } /** * Whether to include objects in `.keep` packs when repacking. * * @param packKeptObjects * whether to include objects in `.keep` files when repacking. * @return this instance * @since 5.13.3 */ public GarbageCollectCommand setPackKeptObjects(boolean packKeptObjects) { this.packKeptObjects = Boolean.valueOf(packKeptObjects); return this; } /** * Whether to preserve old pack files instead of deleting them. * * @since 4.7 * @param preserveOldPacks * whether to preserve old pack files * @return this instance */ public GarbageCollectCommand setPreserveOldPacks(boolean preserveOldPacks) { if (pconfig == null) pconfig = new PackConfig(repo); pconfig.setPreserveOldPacks(preserveOldPacks); return this; } /** * Whether to prune preserved pack files in the preserved directory. * * @since 4.7 * @param prunePreserved * whether to prune preserved pack files * @return this instance */ public GarbageCollectCommand setPrunePreserved(boolean prunePreserved) { if (pconfig == null) pconfig = new PackConfig(repo); pconfig.setPrunePreserved(prunePreserved); return this; } @Override public Properties call() throws GitAPIException { checkCallable(); try { if (repo instanceof FileRepository) { GC gc = new GC((FileRepository) repo); gc.setPackConfig(pconfig); gc.setProgressMonitor(monitor); if (this.expire != null) gc.setExpire(expire); if (this.packKeptObjects != null) { gc.setPackKeptObjects(packKeptObjects.booleanValue()); } try { gc.gc().get(); return toProperties(gc.getStatistics()); } catch (ParseException | InterruptedException | ExecutionException e) { throw new JGitInternalException(JGitText.get().gcFailed, e); } } else if (repo instanceof DfsRepository) { DfsGarbageCollector gc = new DfsGarbageCollector((DfsRepository) repo); gc.setPackConfig(pconfig); gc.pack(monitor); return new Properties(); } else { throw new UnsupportedOperationException(MessageFormat.format( JGitText.get().unsupportedGC, repo.getClass().toString())); } } catch (IOException e) { throw new JGitInternalException(JGitText.get().gcFailed, e); } } /** * Computes and returns the repository statistics. * * @return the repository statistics * @throws org.eclipse.jgit.api.errors.GitAPIException * thrown if the repository statistics cannot be computed * @since 3.0 */ public Properties getStatistics() throws GitAPIException { try { if (repo instanceof FileRepository) { GC gc = new GC((FileRepository) repo); return toProperties(gc.getStatistics()); } return new Properties(); } catch (IOException e) { throw new JGitInternalException( JGitText.get().couldNotGetRepoStatistics, e); } } @SuppressWarnings("boxing") private static Properties toProperties(RepoStatistics stats) { Properties p = new Properties(); p.put("numberOfBitmaps", stats.numberOfBitmaps); //$NON-NLS-1$ p.put("numberOfLooseObjects", stats.numberOfLooseObjects); //$NON-NLS-1$ p.put("numberOfLooseRefs", stats.numberOfLooseRefs); //$NON-NLS-1$ p.put("numberOfPackedObjects", stats.numberOfPackedObjects); //$NON-NLS-1$ p.put("numberOfPackedRefs", stats.numberOfPackedRefs); //$NON-NLS-1$ p.put("numberOfPackFiles", stats.numberOfPackFiles); //$NON-NLS-1$ p.put("sizeOfLooseObjects", stats.sizeOfLooseObjects); //$NON-NLS-1$ p.put("sizeOfPackedObjects", stats.sizeOfPackedObjects); //$NON-NLS-1$ return p; } }