1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46 package org.eclipse.jgit.pgm;
47
48 import static java.lang.Integer.valueOf;
49 import static org.eclipse.jgit.lib.Constants.HEAD;
50 import static org.eclipse.jgit.lib.Constants.OBJECT_ID_STRING_LENGTH;
51
52 import java.io.BufferedOutputStream;
53 import java.io.IOException;
54 import java.text.MessageFormat;
55 import java.util.List;
56 import java.util.concurrent.TimeUnit;
57
58 import org.eclipse.jgit.diff.DiffAlgorithm;
59 import org.eclipse.jgit.diff.DiffAlgorithm.SupportedAlgorithm;
60 import org.eclipse.jgit.diff.DiffEntry;
61 import org.eclipse.jgit.diff.DiffFormatter;
62 import org.eclipse.jgit.diff.RawTextComparator;
63 import org.eclipse.jgit.diff.RenameDetector;
64 import org.eclipse.jgit.dircache.DirCacheIterator;
65 import org.eclipse.jgit.errors.RevisionSyntaxException;
66 import org.eclipse.jgit.lib.ObjectId;
67 import org.eclipse.jgit.lib.ObjectReader;
68 import org.eclipse.jgit.lib.Repository;
69 import org.eclipse.jgit.lib.TextProgressMonitor;
70 import org.eclipse.jgit.pgm.internal.CLIText;
71 import org.eclipse.jgit.pgm.opt.PathTreeFilterHandler;
72 import org.eclipse.jgit.treewalk.AbstractTreeIterator;
73 import org.eclipse.jgit.treewalk.CanonicalTreeParser;
74 import org.eclipse.jgit.treewalk.FileTreeIterator;
75 import org.eclipse.jgit.treewalk.filter.TreeFilter;
76 import org.eclipse.jgit.util.io.ThrowingPrintWriter;
77 import org.kohsuke.args4j.Argument;
78 import org.kohsuke.args4j.Option;
79
80 @Command(common = true, usage = "usage_ShowDiffs")
81 class Diff extends TextBuiltin {
82 private DiffFormatter diffFmt;
83
84 @Argument(index = 0, metaVar = "metaVar_treeish")
85 private AbstractTreeIterator oldTree;
86
87 @Argument(index = 1, metaVar = "metaVar_treeish")
88 private AbstractTreeIterator newTree;
89
90 @Option(name = "--cached", aliases = { "--staged" }, usage = "usage_cached")
91 private boolean cached;
92
93 @Option(name = "--", metaVar = "metaVar_paths", handler = PathTreeFilterHandler.class)
94 private TreeFilter pathFilter = TreeFilter.ALL;
95
96
97 @Option(name = "-p", usage = "usage_showPatch")
98 boolean showPatch;
99
100 @Option(name = "-M", usage = "usage_detectRenames")
101 private Boolean detectRenames;
102
103 @Option(name = "--no-renames", usage = "usage_noRenames")
104 void noRenames(@SuppressWarnings("unused") boolean on) {
105 detectRenames = Boolean.FALSE;
106 }
107
108 @Option(name = "--algorithm", metaVar = "metaVar_diffAlg", usage = "usage_diffAlgorithm")
109 void setAlgorithm(SupportedAlgorithm s) {
110 diffFmt.setDiffAlgorithm(DiffAlgorithm.getAlgorithm(s));
111 }
112
113 @Option(name = "-l", usage = "usage_renameLimit")
114 private Integer renameLimit;
115
116 @Option(name = "--name-status", usage = "usage_nameStatus")
117 private boolean showNameAndStatusOnly;
118
119 @Option(name = "--ignore-space-at-eol")
120 void ignoreSpaceAtEol(@SuppressWarnings("unused") boolean on) {
121 diffFmt.setDiffComparator(RawTextComparator.WS_IGNORE_TRAILING);
122 }
123
124 @Option(name = "--ignore-leading-space")
125 void ignoreLeadingSpace(@SuppressWarnings("unused") boolean on) {
126 diffFmt.setDiffComparator(RawTextComparator.WS_IGNORE_LEADING);
127 }
128
129 @Option(name = "-b", aliases = { "--ignore-space-change" })
130 void ignoreSpaceChange(@SuppressWarnings("unused") boolean on) {
131 diffFmt.setDiffComparator(RawTextComparator.WS_IGNORE_CHANGE);
132 }
133
134 @Option(name = "-w", aliases = { "--ignore-all-space" })
135 void ignoreAllSpace(@SuppressWarnings("unused") boolean on) {
136 diffFmt.setDiffComparator(RawTextComparator.WS_IGNORE_ALL);
137 }
138
139 @Option(name = "-U", aliases = { "--unified" }, metaVar = "metaVar_linesOfContext")
140 void unified(int lines) {
141 diffFmt.setContext(lines);
142 }
143
144 @Option(name = "--abbrev", metaVar = "metaVar_n")
145 void abbrev(int lines) {
146 diffFmt.setAbbreviationLength(lines);
147 }
148
149 @Option(name = "--full-index")
150 void abbrev(@SuppressWarnings("unused") boolean on) {
151 diffFmt.setAbbreviationLength(OBJECT_ID_STRING_LENGTH);
152 }
153
154 @Option(name = "--src-prefix", metaVar = "metaVar_prefix", usage = "usage_srcPrefix")
155 void sourcePrefix(String path) {
156 diffFmt.setOldPrefix(path);
157 }
158
159 @Option(name = "--dst-prefix", metaVar = "metaVar_prefix", usage = "usage_dstPrefix")
160 void dstPrefix(String path) {
161 diffFmt.setNewPrefix(path);
162 }
163
164 @Option(name = "--no-prefix", usage = "usage_noPrefix")
165 void noPrefix(@SuppressWarnings("unused") boolean on) {
166 diffFmt.setOldPrefix("");
167 diffFmt.setNewPrefix("");
168 }
169
170
171
172
173 @Override
174 protected void init(Repository repository, String gitDir) {
175 super.init(repository, gitDir);
176 diffFmt = new DiffFormatter(new BufferedOutputStream(outs));
177 }
178
179
180 @Override
181 protected void run() {
182 diffFmt.setRepository(db);
183 try {
184 if (cached) {
185 if (oldTree == null) {
186 ObjectId head = db.resolve(HEAD + "^{tree}");
187 if (head == null) {
188 die(MessageFormat.format(CLIText.get().notATree, HEAD));
189 }
190 CanonicalTreeParser p = new CanonicalTreeParser();
191 try (ObjectReader reader = db.newObjectReader()) {
192 p.reset(reader, head);
193 }
194 oldTree = p;
195 }
196 newTree = new DirCacheIterator(db.readDirCache());
197 } else if (oldTree == null) {
198 oldTree = new DirCacheIterator(db.readDirCache());
199 newTree = new FileTreeIterator(db);
200 } else if (newTree == null) {
201 newTree = new FileTreeIterator(db);
202 }
203
204 TextProgressMonitor pm = new TextProgressMonitor(errw);
205 pm.setDelayStart(2, TimeUnit.SECONDS);
206 diffFmt.setProgressMonitor(pm);
207 diffFmt.setPathFilter(pathFilter);
208 if (detectRenames != null) {
209 diffFmt.setDetectRenames(detectRenames.booleanValue());
210 }
211 if (renameLimit != null && diffFmt.isDetectRenames()) {
212 RenameDetector rd = diffFmt.getRenameDetector();
213 rd.setRenameLimit(renameLimit.intValue());
214 }
215
216 if (showNameAndStatusOnly) {
217 nameStatus(outw, diffFmt.scan(oldTree, newTree));
218 outw.flush();
219 } else {
220 diffFmt.format(oldTree, newTree);
221 diffFmt.flush();
222 }
223 } catch (RevisionSyntaxException | IOException e) {
224 throw die(e.getMessage(), e);
225 } finally {
226 diffFmt.close();
227 }
228 }
229
230 static void nameStatus(ThrowingPrintWriter out, List<DiffEntry> files)
231 throws IOException {
232 for (DiffEntry ent : files) {
233 switch (ent.getChangeType()) {
234 case ADD:
235 out.println("A\t" + ent.getNewPath());
236 break;
237 case DELETE:
238 out.println("D\t" + ent.getOldPath());
239 break;
240 case MODIFY:
241 out.println("M\t" + ent.getNewPath());
242 break;
243 case COPY:
244 out.format("C%1$03d\t%2$s\t%3$s", valueOf(ent.getScore()),
245 ent.getOldPath(), ent.getNewPath());
246 out.println();
247 break;
248 case RENAME:
249 out.format("R%1$03d\t%2$s\t%3$s", valueOf(ent.getScore()),
250 ent.getOldPath(), ent.getNewPath());
251 out.println();
252 break;
253 }
254 }
255 }
256 }