View Javadoc
1   /*
2    * Copyright (C) 2011, Tomasz Zarna <Tomasz.Zarna@pl.ibm.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 static org.eclipse.jgit.lib.Constants.HEAD;
13  
14  import java.io.BufferedOutputStream;
15  import java.io.IOException;
16  import java.io.OutputStream;
17  import java.util.List;
18  
19  import org.eclipse.jgit.api.errors.GitAPIException;
20  import org.eclipse.jgit.api.errors.JGitInternalException;
21  import org.eclipse.jgit.api.errors.NoHeadException;
22  import org.eclipse.jgit.diff.DiffEntry;
23  import org.eclipse.jgit.diff.DiffFormatter;
24  import org.eclipse.jgit.dircache.DirCacheIterator;
25  import org.eclipse.jgit.internal.JGitText;
26  import org.eclipse.jgit.lib.NullProgressMonitor;
27  import org.eclipse.jgit.lib.ObjectId;
28  import org.eclipse.jgit.lib.ObjectReader;
29  import org.eclipse.jgit.lib.ProgressMonitor;
30  import org.eclipse.jgit.lib.Repository;
31  import org.eclipse.jgit.treewalk.AbstractTreeIterator;
32  import org.eclipse.jgit.treewalk.CanonicalTreeParser;
33  import org.eclipse.jgit.treewalk.FileTreeIterator;
34  import org.eclipse.jgit.treewalk.filter.TreeFilter;
35  import org.eclipse.jgit.util.io.NullOutputStream;
36  
37  /**
38   * Show changes between commits, commit and working tree, etc.
39   *
40   * @see <a href="http://www.kernel.org/pub/software/scm/git/docs/git-diff.html"
41   *      >Git documentation about diff</a>
42   */
43  public class DiffCommand extends GitCommand<List<DiffEntry>> {
44  	private AbstractTreeIterator oldTree;
45  
46  	private AbstractTreeIterator newTree;
47  
48  	private boolean cached;
49  
50  	private TreeFilter pathFilter = TreeFilter.ALL;
51  
52  	private boolean showNameAndStatusOnly;
53  
54  	private boolean showNameOnly;
55  
56  	private OutputStream out;
57  
58  	private int contextLines = -1;
59  
60  	private String sourcePrefix;
61  
62  	private String destinationPrefix;
63  
64  	private ProgressMonitor monitor = NullProgressMonitor.INSTANCE;
65  
66  	/**
67  	 * Constructor for DiffCommand
68  	 *
69  	 * @param repo
70  	 *            a {@link org.eclipse.jgit.lib.Repository} object.
71  	 */
72  	protected DiffCommand(Repository repo) {
73  		super(repo);
74  	}
75  
76  	private DiffFormatter getDiffFormatter() {
77  		return out != null && !showNameAndStatusOnly && !showNameOnly
78  				? new DiffFormatter(new BufferedOutputStream(out))
79  				: new DiffFormatter(NullOutputStream.INSTANCE);
80  	}
81  
82  	/**
83  	 * {@inheritDoc}
84  	 * <p>
85  	 * Executes the {@code Diff} command with all the options and parameters
86  	 * collected by the setter methods (e.g. {@link #setCached(boolean)} of this
87  	 * class. Each instance of this class should only be used for one invocation
88  	 * of the command. Don't call this method twice on an instance.
89  	 */
90  	@Override
91  	public List<DiffEntry> call() throws GitAPIException {
92  		try (DiffFormatter diffFmt = getDiffFormatter()) {
93  			diffFmt.setRepository(repo);
94  			diffFmt.setProgressMonitor(monitor);
95  			if (cached) {
96  				if (oldTree == null) {
97  					ObjectId head = repo.resolve(HEAD + "^{tree}"); //$NON-NLS-1$
98  					if (head == null)
99  						throw new NoHeadException(JGitText.get().cannotReadTree);
100 					CanonicalTreeParser p = new CanonicalTreeParser();
101 					try (ObjectReader reader = repo.newObjectReader()) {
102 						p.reset(reader, head);
103 					}
104 					oldTree = p;
105 				}
106 				newTree = new DirCacheIterator(repo.readDirCache());
107 			} else {
108 				if (oldTree == null) {
109 					oldTree = new DirCacheIterator(repo.readDirCache());
110 				}
111 				if (newTree == null) {
112 					newTree = new FileTreeIterator(repo);
113 				}
114 			}
115 
116 			diffFmt.setPathFilter(pathFilter);
117 
118 			List<DiffEntry> result = diffFmt.scan(oldTree, newTree);
119 			if (showNameAndStatusOnly || showNameOnly) {
120 				return result;
121 			}
122 			if (contextLines >= 0) {
123 				diffFmt.setContext(contextLines);
124 			}
125 			if (destinationPrefix != null) {
126 				diffFmt.setNewPrefix(destinationPrefix);
127 			}
128 			if (sourcePrefix != null) {
129 				diffFmt.setOldPrefix(sourcePrefix);
130 			}
131 			diffFmt.format(result);
132 			diffFmt.flush();
133 			return result;
134 		} catch (IOException e) {
135 			throw new JGitInternalException(e.getMessage(), e);
136 		}
137 	}
138 
139 	/**
140 	 * Whether to view the changes staged for the next commit
141 	 *
142 	 * @param cached
143 	 *            whether to view the changes staged for the next commit
144 	 * @return this instance
145 	 */
146 	public DiffCommand setCached(boolean cached) {
147 		this.cached = cached;
148 		return this;
149 	}
150 
151 	/**
152 	 * Set path filter
153 	 *
154 	 * @param pathFilter
155 	 *            parameter, used to limit the diff to the named path
156 	 * @return this instance
157 	 */
158 	public DiffCommand setPathFilter(TreeFilter pathFilter) {
159 		this.pathFilter = pathFilter;
160 		return this;
161 	}
162 
163 	/**
164 	 * Set old tree
165 	 *
166 	 * @param oldTree
167 	 *            the previous state
168 	 * @return this instance
169 	 */
170 	public DiffCommand setOldTree(AbstractTreeIterator oldTree) {
171 		this.oldTree = oldTree;
172 		return this;
173 	}
174 
175 	/**
176 	 * Set new tree
177 	 *
178 	 * @param newTree
179 	 *            the updated state
180 	 * @return this instance
181 	 */
182 	public DiffCommand setNewTree(AbstractTreeIterator newTree) {
183 		this.newTree = newTree;
184 		return this;
185 	}
186 
187 	/**
188 	 * Set whether to return only names and status of changed files
189 	 *
190 	 * @param showNameAndStatusOnly
191 	 *            whether to return only names and status of changed files
192 	 * @return this instance
193 	 */
194 	public DiffCommand setShowNameAndStatusOnly(boolean showNameAndStatusOnly) {
195 		this.showNameAndStatusOnly = showNameAndStatusOnly;
196 		return this;
197 	}
198 
199 	/**
200 	 * Set whether to return only names of changed files
201 	 *
202 	 * @param showNameOnly
203 	 *            whether to return only names files
204 	 * @return this instance
205 	 * @since 6.4
206 	 */
207 	public DiffCommand setShowNameOnly(boolean showNameOnly) {
208 		this.showNameOnly = showNameOnly;
209 		return this;
210 	}
211 
212 	/**
213 	 * Set output stream
214 	 *
215 	 * @param out
216 	 *            the stream to write line data
217 	 * @return this instance
218 	 */
219 	public DiffCommand setOutputStream(OutputStream out) {
220 		this.out = out;
221 		return this;
222 	}
223 
224 	/**
225 	 * Set number of context lines instead of the usual three.
226 	 *
227 	 * @param contextLines
228 	 *            the number of context lines
229 	 * @return this instance
230 	 */
231 	public DiffCommand setContextLines(int contextLines) {
232 		this.contextLines = contextLines;
233 		return this;
234 	}
235 
236 	/**
237 	 * Set the given source prefix instead of "a/".
238 	 *
239 	 * @param sourcePrefix
240 	 *            the prefix
241 	 * @return this instance
242 	 */
243 	public DiffCommand setSourcePrefix(String sourcePrefix) {
244 		this.sourcePrefix = sourcePrefix;
245 		return this;
246 	}
247 
248 	/**
249 	 * Set the given destination prefix instead of "b/".
250 	 *
251 	 * @param destinationPrefix
252 	 *            the prefix
253 	 * @return this instance
254 	 */
255 	public DiffCommand setDestinationPrefix(String destinationPrefix) {
256 		this.destinationPrefix = destinationPrefix;
257 		return this;
258 	}
259 
260 	/**
261 	 * The progress monitor associated with the diff operation. By default, this
262 	 * is set to <code>NullProgressMonitor</code>
263 	 *
264 	 * @see NullProgressMonitor
265 	 * @param monitor
266 	 *            a progress monitor
267 	 * @return this instance
268 	 */
269 	public DiffCommand setProgressMonitor(ProgressMonitor monitor) {
270 		if (monitor == null) {
271 			monitor = NullProgressMonitor.INSTANCE;
272 		}
273 		this.monitor = monitor;
274 		return this;
275 	}
276 }