View Javadoc
1   /*
2    * Copyright (C) 2012, Matthias Sohn <matthias.sohn@sap.com> and others
3    *
4    * This program and the accompanying materials are made available under the
5    * terms of the Eclipse Distribution License v. 1.0 which is available at
6    * https://www.eclipse.org/org/documents/edl-v10.php.
7    *
8    * SPDX-License-Identifier: BSD-3-Clause
9    */
10  package org.eclipse.jgit.api;
11  
12  import java.io.IOException;
13  import java.text.MessageFormat;
14  import java.text.ParseException;
15  import java.util.Date;
16  import java.util.Properties;
17  import java.util.concurrent.ExecutionException;
18  
19  import org.eclipse.jgit.api.errors.GitAPIException;
20  import org.eclipse.jgit.api.errors.JGitInternalException;
21  import org.eclipse.jgit.internal.JGitText;
22  import org.eclipse.jgit.internal.storage.dfs.DfsGarbageCollector;
23  import org.eclipse.jgit.internal.storage.dfs.DfsRepository;
24  import org.eclipse.jgit.internal.storage.file.FileRepository;
25  import org.eclipse.jgit.internal.storage.file.GC;
26  import org.eclipse.jgit.internal.storage.file.GC.RepoStatistics;
27  import org.eclipse.jgit.lib.ConfigConstants;
28  import org.eclipse.jgit.lib.ProgressMonitor;
29  import org.eclipse.jgit.lib.Repository;
30  import org.eclipse.jgit.lib.StoredConfig;
31  import org.eclipse.jgit.storage.pack.PackConfig;
32  
33  /**
34   * A class used to execute a {@code gc} command. It has setters for all
35   * supported options and arguments of this command and a {@link #call()} method
36   * to finally execute the command. Each instance of this class should only be
37   * used for one invocation of the command (means: one call to {@link #call()})
38   *
39   * @since 2.2
40   * @see <a href="http://www.kernel.org/pub/software/scm/git/docs/git-gc.html"
41   *      >Git documentation about gc</a>
42   */
43  public class GarbageCollectCommand extends GitCommand<Properties> {
44  	/**
45  	 * Default value of maximum delta chain depth during aggressive garbage
46  	 * collection: {@value}
47  	 *
48  	 * @since 3.6
49  	 */
50  	public static final int DEFAULT_GC_AGGRESSIVE_DEPTH = 250;
51  
52  	/**
53  	 * Default window size during packing during aggressive garbage collection:
54  	 * * {@value}
55  	 *
56  	 * @since 3.6
57  	 */
58  	public static final int DEFAULT_GC_AGGRESSIVE_WINDOW = 250;
59  
60  	private ProgressMonitor monitor;
61  
62  	private Date expire;
63  
64  	private PackConfig pconfig;
65  
66  	/**
67  	 * Constructor for GarbageCollectCommand.
68  	 *
69  	 * @param repo
70  	 *            a {@link org.eclipse.jgit.lib.Repository} object.
71  	 */
72  	protected GarbageCollectCommand(Repository repo) {
73  		super(repo);
74  		pconfig = new PackConfig(repo);
75  	}
76  
77  	/**
78  	 * Set progress monitor
79  	 *
80  	 * @param monitor
81  	 *            a progress monitor
82  	 * @return this instance
83  	 */
84  	public GarbageCollectCommand setProgressMonitor(ProgressMonitor monitor) {
85  		this.monitor = monitor;
86  		return this;
87  	}
88  
89  	/**
90  	 * During gc() or prune() each unreferenced, loose object which has been
91  	 * created or modified after <code>expire</code> will not be pruned. Only
92  	 * older objects may be pruned. If set to null then every object is a
93  	 * candidate for pruning. Use {@link org.eclipse.jgit.util.GitDateParser} to
94  	 * parse time formats used by git gc.
95  	 *
96  	 * @param expire
97  	 *            minimal age of objects to be pruned.
98  	 * @return this instance
99  	 */
100 	public GarbageCollectCommand setExpire(Date expire) {
101 		this.expire = expire;
102 		return this;
103 	}
104 
105 	/**
106 	 * Whether to use aggressive mode or not. If set to true JGit behaves more
107 	 * similar to native git's "git gc --aggressive". If set to
108 	 * <code>true</code> compressed objects found in old packs are not reused
109 	 * but every object is compressed again. Configuration variables
110 	 * pack.window and pack.depth are set to 250 for this GC.
111 	 *
112 	 * @since 3.6
113 	 * @param aggressive
114 	 *            whether to turn on or off aggressive mode
115 	 * @return this instance
116 	 */
117 	public GarbageCollectCommand setAggressive(boolean aggressive) {
118 		if (aggressive) {
119 			StoredConfig repoConfig = repo.getConfig();
120 			pconfig.setDeltaSearchWindowSize(repoConfig.getInt(
121 					ConfigConstants.CONFIG_GC_SECTION,
122 					ConfigConstants.CONFIG_KEY_AGGRESSIVE_WINDOW,
123 					DEFAULT_GC_AGGRESSIVE_WINDOW));
124 			pconfig.setMaxDeltaDepth(repoConfig.getInt(
125 					ConfigConstants.CONFIG_GC_SECTION,
126 					ConfigConstants.CONFIG_KEY_AGGRESSIVE_DEPTH,
127 					DEFAULT_GC_AGGRESSIVE_DEPTH));
128 			pconfig.setReuseObjects(false);
129 		} else
130 			pconfig = new PackConfig(repo);
131 		return this;
132 	}
133 
134 	/**
135 	 * Whether to preserve old pack files instead of deleting them.
136 	 *
137 	 * @since 4.7
138 	 * @param preserveOldPacks
139 	 *            whether to preserve old pack files
140 	 * @return this instance
141 	 */
142 	public GarbageCollectCommand setPreserveOldPacks(boolean preserveOldPacks) {
143 		if (pconfig == null)
144 			pconfig = new PackConfig(repo);
145 
146 		pconfig.setPreserveOldPacks(preserveOldPacks);
147 		return this;
148 	}
149 
150 	/**
151 	 * Whether to prune preserved pack files in the preserved directory.
152 	 *
153 	 * @since 4.7
154 	 * @param prunePreserved
155 	 *            whether to prune preserved pack files
156 	 * @return this instance
157 	 */
158 	public GarbageCollectCommand setPrunePreserved(boolean prunePreserved) {
159 		if (pconfig == null)
160 			pconfig = new PackConfig(repo);
161 
162 		pconfig.setPrunePreserved(prunePreserved);
163 		return this;
164 	}
165 
166 	/** {@inheritDoc} */
167 	@Override
168 	public Properties call() throws GitAPIException {
169 		checkCallable();
170 
171 		try {
172 			if (repo instanceof FileRepository) {
173 				GC gc = new GC((FileRepository) repo);
174 				gc.setPackConfig(pconfig);
175 				gc.setProgressMonitor(monitor);
176 				if (this.expire != null)
177 					gc.setExpire(expire);
178 
179 				try {
180 					gc.gc().get();
181 					return toProperties(gc.getStatistics());
182 				} catch (ParseException | InterruptedException
183 						| ExecutionException e) {
184 					throw new JGitInternalException(JGitText.get().gcFailed, e);
185 				}
186 			} else if (repo instanceof DfsRepository) {
187 				DfsGarbageCollector gc =
188 					new DfsGarbageCollector((DfsRepository) repo);
189 				gc.setPackConfig(pconfig);
190 				gc.pack(monitor);
191 				return new Properties();
192 			} else {
193 				throw new UnsupportedOperationException(MessageFormat.format(
194 						JGitText.get().unsupportedGC,
195 						repo.getClass().toString()));
196 			}
197 		} catch (IOException e) {
198 			throw new JGitInternalException(JGitText.get().gcFailed, e);
199 		}
200 	}
201 
202 	/**
203 	 * Computes and returns the repository statistics.
204 	 *
205 	 * @return the repository statistics
206 	 * @throws org.eclipse.jgit.api.errors.GitAPIException
207 	 *             thrown if the repository statistics cannot be computed
208 	 * @since 3.0
209 	 */
210 	public Properties getStatistics() throws GitAPIException {
211 		try {
212 			if (repo instanceof FileRepository) {
213 				GC gc = new GC((FileRepository) repo);
214 				return toProperties(gc.getStatistics());
215 			}
216 			return new Properties();
217 		} catch (IOException e) {
218 			throw new JGitInternalException(
219 					JGitText.get().couldNotGetRepoStatistics, e);
220 		}
221 	}
222 
223 	@SuppressWarnings("boxing")
224 	private static Properties toProperties(RepoStatistics stats) {
225 		Properties p = new Properties();
226 		p.put("numberOfBitmaps", stats.numberOfBitmaps); //$NON-NLS-1$
227 		p.put("numberOfLooseObjects", stats.numberOfLooseObjects); //$NON-NLS-1$
228 		p.put("numberOfLooseRefs", stats.numberOfLooseRefs); //$NON-NLS-1$
229 		p.put("numberOfPackedObjects", stats.numberOfPackedObjects); //$NON-NLS-1$
230 		p.put("numberOfPackedRefs", stats.numberOfPackedRefs); //$NON-NLS-1$
231 		p.put("numberOfPackFiles", stats.numberOfPackFiles); //$NON-NLS-1$
232 		p.put("sizeOfLooseObjects", stats.sizeOfLooseObjects); //$NON-NLS-1$
233 		p.put("sizeOfPackedObjects", stats.sizeOfPackedObjects); //$NON-NLS-1$
234 		return p;
235 	}
236 }