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