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", multiValued = true, 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 @Override
172 protected void init(final Repository repository, final String gitDir) {
173 super.init(repository, gitDir);
174 diffFmt = new DiffFormatter(new BufferedOutputStream(outs));
175 }
176
177 @Override
178 protected void run() throws Exception {
179 diffFmt.setRepository(db);
180 try {
181 if (cached) {
182 if (oldTree == null) {
183 ObjectId head = db.resolve(HEAD + "^{tree}");
184 if (head == null)
185 die(MessageFormat.format(CLIText.get().notATree, HEAD));
186 CanonicalTreeParser p = new CanonicalTreeParser();
187 try (ObjectReader reader = db.newObjectReader()) {
188 p.reset(reader, head);
189 }
190 oldTree = p;
191 }
192 newTree = new DirCacheIterator(db.readDirCache());
193 } else if (oldTree == null) {
194 oldTree = new DirCacheIterator(db.readDirCache());
195 newTree = new FileTreeIterator(db);
196 } else if (newTree == null)
197 newTree = new FileTreeIterator(db);
198
199 TextProgressMonitor pm = new TextProgressMonitor(errw);
200 pm.setDelayStart(2, TimeUnit.SECONDS);
201 diffFmt.setProgressMonitor(pm);
202 diffFmt.setPathFilter(pathFilter);
203 if (detectRenames != null)
204 diffFmt.setDetectRenames(detectRenames.booleanValue());
205 if (renameLimit != null && diffFmt.isDetectRenames()) {
206 RenameDetector rd = diffFmt.getRenameDetector();
207 rd.setRenameLimit(renameLimit.intValue());
208 }
209
210 if (showNameAndStatusOnly) {
211 nameStatus(outw, diffFmt.scan(oldTree, newTree));
212 outw.flush();
213
214 } else {
215 diffFmt.format(oldTree, newTree);
216 diffFmt.flush();
217 }
218 } finally {
219 diffFmt.close();
220 }
221 }
222
223 static void nameStatus(ThrowingPrintWriter out, List<DiffEntry> files)
224 throws IOException {
225 for (DiffEntry ent : files) {
226 switch (ent.getChangeType()) {
227 case ADD:
228 out.println("A\t" + ent.getNewPath());
229 break;
230 case DELETE:
231 out.println("D\t" + ent.getOldPath());
232 break;
233 case MODIFY:
234 out.println("M\t" + ent.getNewPath());
235 break;
236 case COPY:
237 out.format("C%1$03d\t%2$s\t%3$s", valueOf(ent.getScore()),
238 ent.getOldPath(), ent.getNewPath());
239 out.println();
240 break;
241 case RENAME:
242 out.format("R%1$03d\t%2$s\t%3$s", valueOf(ent.getScore()),
243 ent.getOldPath(), ent.getNewPath());
244 out.println();
245 break;
246 }
247 }
248 }
249 }