1
2
3
4
5
6
7
8
9
10
11
12
13 package org.eclipse.jgit.pgm;
14
15 import java.io.BufferedOutputStream;
16 import java.io.IOException;
17 import java.text.DateFormat;
18 import java.text.MessageFormat;
19 import java.text.SimpleDateFormat;
20 import java.util.Locale;
21 import java.util.TimeZone;
22
23 import org.eclipse.jgit.diff.DiffFormatter;
24 import org.eclipse.jgit.diff.RawTextComparator;
25 import org.eclipse.jgit.diff.RenameDetector;
26 import org.eclipse.jgit.errors.CorruptObjectException;
27 import org.eclipse.jgit.errors.IncorrectObjectTypeException;
28 import org.eclipse.jgit.errors.MissingObjectException;
29 import org.eclipse.jgit.errors.RevisionSyntaxException;
30 import org.eclipse.jgit.lib.Constants;
31 import org.eclipse.jgit.lib.FileMode;
32 import org.eclipse.jgit.lib.GpgConfig;
33 import org.eclipse.jgit.lib.GpgSignatureVerifier;
34 import org.eclipse.jgit.lib.GpgSignatureVerifierFactory;
35 import org.eclipse.jgit.lib.ObjectId;
36 import org.eclipse.jgit.lib.PersonIdent;
37 import org.eclipse.jgit.lib.Repository;
38 import org.eclipse.jgit.lib.GpgSignatureVerifier.SignatureVerification;
39 import org.eclipse.jgit.pgm.internal.CLIText;
40 import org.eclipse.jgit.pgm.internal.VerificationUtils;
41 import org.eclipse.jgit.pgm.opt.PathTreeFilterHandler;
42 import org.eclipse.jgit.revwalk.RevCommit;
43 import org.eclipse.jgit.revwalk.RevObject;
44 import org.eclipse.jgit.revwalk.RevTag;
45 import org.eclipse.jgit.revwalk.RevTree;
46 import org.eclipse.jgit.revwalk.RevWalk;
47 import org.eclipse.jgit.treewalk.TreeWalk;
48 import org.eclipse.jgit.treewalk.filter.TreeFilter;
49 import org.eclipse.jgit.util.RawParseUtils;
50 import org.kohsuke.args4j.Argument;
51 import org.kohsuke.args4j.Option;
52
53 @Command(common = true, usage = "usage_show")
54 class Show extends TextBuiltin {
55 private final TimeZone myTZ = TimeZone.getDefault();
56
57 private final DateFormat fmt;
58
59 private DiffFormatter diffFmt;
60
61 @Argument(index = 0, metaVar = "metaVar_object")
62 private String objectName;
63
64 @Option(name = "--", metaVar = "metaVar_path", handler = PathTreeFilterHandler.class)
65 protected TreeFilter pathFilter = TreeFilter.ALL;
66
67 @Option(name = "--show-signature", usage = "usage_showSignature")
68 private boolean showSignature;
69
70
71 @Option(name = "-p", usage = "usage_showPatch")
72 boolean showPatch;
73
74 @Option(name = "-M", usage = "usage_detectRenames")
75 private Boolean detectRenames;
76
77 @Option(name = "--no-renames", usage = "usage_noRenames")
78 void noRenames(@SuppressWarnings("unused") boolean on) {
79 detectRenames = Boolean.FALSE;
80 }
81
82 @Option(name = "-l", usage = "usage_renameLimit")
83 private Integer renameLimit;
84
85 @Option(name = "--name-status", usage = "usage_nameStatus")
86 private boolean showNameAndStatusOnly;
87
88 @Option(name = "--ignore-space-at-eol")
89 void ignoreSpaceAtEol(@SuppressWarnings("unused") boolean on) {
90 diffFmt.setDiffComparator(RawTextComparator.WS_IGNORE_TRAILING);
91 }
92
93 @Option(name = "--ignore-leading-space")
94 void ignoreLeadingSpace(@SuppressWarnings("unused") boolean on) {
95 diffFmt.setDiffComparator(RawTextComparator.WS_IGNORE_LEADING);
96 }
97
98 @Option(name = "-b", aliases = { "--ignore-space-change" })
99 void ignoreSpaceChange(@SuppressWarnings("unused") boolean on) {
100 diffFmt.setDiffComparator(RawTextComparator.WS_IGNORE_CHANGE);
101 }
102
103 @Option(name = "-w", aliases = { "--ignore-all-space" })
104 void ignoreAllSpace(@SuppressWarnings("unused") boolean on) {
105 diffFmt.setDiffComparator(RawTextComparator.WS_IGNORE_ALL);
106 }
107
108 @Option(name = "-U", aliases = { "--unified" }, metaVar = "metaVar_linesOfContext")
109 void unified(int lines) {
110 diffFmt.setContext(lines);
111 }
112
113 @Option(name = "--abbrev", metaVar = "metaVar_n")
114 void abbrev(int lines) {
115 diffFmt.setAbbreviationLength(lines);
116 }
117
118 @Option(name = "--full-index")
119 void abbrev(@SuppressWarnings("unused") boolean on) {
120 diffFmt.setAbbreviationLength(Constants.OBJECT_ID_STRING_LENGTH);
121 }
122
123 @Option(name = "--src-prefix", usage = "usage_srcPrefix")
124 void sourcePrefix(String path) {
125 diffFmt.setOldPrefix(path);
126 }
127
128 @Option(name = "--dst-prefix", usage = "usage_dstPrefix")
129 void dstPrefix(String path) {
130 diffFmt.setNewPrefix(path);
131 }
132
133 @Option(name = "--no-prefix", usage = "usage_noPrefix")
134 void noPrefix(@SuppressWarnings("unused") boolean on) {
135 diffFmt.setOldPrefix("");
136 diffFmt.setNewPrefix("");
137 }
138
139
140
141 Show() {
142 fmt = new SimpleDateFormat("EEE MMM dd HH:mm:ss yyyy ZZZZZ", Locale.US);
143 }
144
145
146 @Override
147 protected void init(Repository repository, String gitDir) {
148 super.init(repository, gitDir);
149 diffFmt = new DiffFormatter(new BufferedOutputStream(outs));
150 }
151
152
153 @SuppressWarnings("boxing")
154 @Override
155 protected void run() {
156 diffFmt.setRepository(db);
157 try {
158 diffFmt.setPathFilter(pathFilter);
159 if (detectRenames != null) {
160 diffFmt.setDetectRenames(detectRenames.booleanValue());
161 }
162 if (renameLimit != null && diffFmt.isDetectRenames()) {
163 RenameDetector rd = diffFmt.getRenameDetector();
164 rd.setRenameLimit(renameLimit.intValue());
165 }
166
167 ObjectId objectId;
168 if (objectName == null) {
169 objectId = db.resolve(Constants.HEAD);
170 } else {
171 objectId = db.resolve(objectName);
172 }
173
174 try (RevWalk rw = new RevWalk(db)) {
175 RevObject obj = rw.parseAny(objectId);
176 while (obj instanceof RevTag) {
177 show((RevTag) obj);
178 obj = ((RevTag) obj).getObject();
179 rw.parseBody(obj);
180 }
181
182 switch (obj.getType()) {
183 case Constants.OBJ_COMMIT:
184 show(rw, (RevCommit) obj);
185 break;
186
187 case Constants.OBJ_TREE:
188 outw.print("tree ");
189 outw.print(objectName);
190 outw.println();
191 outw.println();
192 show((RevTree) obj);
193 break;
194
195 case Constants.OBJ_BLOB:
196 db.open(obj, obj.getType()).copyTo(System.out);
197 outw.flush();
198 break;
199
200 default:
201 throw die(MessageFormat.format(
202 CLIText.get().cannotReadBecause, obj.name(),
203 obj.getType()));
204 }
205 }
206 } catch (RevisionSyntaxException | IOException e) {
207 throw die(e.getMessage(), e);
208 } finally {
209 diffFmt.close();
210 }
211 }
212
213 private void show(RevTag tag) throws IOException {
214 outw.print(CLIText.get().tagLabel);
215 outw.print(" ");
216 outw.print(tag.getTagName());
217 outw.println();
218
219 final PersonIdent tagger = tag.getTaggerIdent();
220 if (tagger != null) {
221 outw.println(MessageFormat.format(CLIText.get().taggerInfo,
222 tagger.getName(), tagger.getEmailAddress()));
223
224 final TimeZone taggerTZ = tagger.getTimeZone();
225 fmt.setTimeZone(taggerTZ != null ? taggerTZ : myTZ);
226 outw.println(MessageFormat.format(CLIText.get().dateInfo,
227 fmt.format(tagger.getWhen())));
228 }
229
230 outw.println();
231 String fullMessage = tag.getFullMessage();
232 if (!fullMessage.isEmpty()) {
233 String[] lines = tag.getFullMessage().split("\n");
234 for (String s : lines) {
235 outw.println(s);
236 }
237 }
238 byte[] rawSignature = tag.getRawGpgSignature();
239 if (rawSignature != null) {
240 String[] lines = RawParseUtils.decode(rawSignature).split("\n");
241 for (String s : lines) {
242 outw.println(s);
243 }
244 }
245 outw.println();
246 }
247
248 private void show(RevTree obj) throws MissingObjectException,
249 IncorrectObjectTypeException, CorruptObjectException, IOException {
250 try (TreeWalk walk = new TreeWalk(db)) {
251 walk.reset();
252 walk.addTree(obj);
253
254 while (walk.next()) {
255 outw.print(walk.getPathString());
256 final FileMode mode = walk.getFileMode(0);
257 if (mode == FileMode.TREE)
258 outw.print("/");
259 outw.println();
260 }
261 }
262 }
263
264 private void show(RevWalk rw, RevCommit c) throws IOException {
265 char[] outbuffer = new char[Constants.OBJECT_ID_LENGTH * 2];
266
267 outw.print(CLIText.get().commitLabel);
268 outw.print(" ");
269 c.getId().copyTo(outbuffer, outw);
270 outw.println();
271
272 if (showSignature) {
273 showSignature(c);
274 }
275
276 final PersonIdent author = c.getAuthorIdent();
277 outw.println(MessageFormat.format(CLIText.get().authorInfo,
278 author.getName(), author.getEmailAddress()));
279
280 final TimeZone authorTZ = author.getTimeZone();
281 fmt.setTimeZone(authorTZ != null ? authorTZ : myTZ);
282 outw.println(MessageFormat.format(CLIText.get().dateInfo,
283 fmt.format(author.getWhen())));
284
285 outw.println();
286 final String[] lines = c.getFullMessage().split("\n");
287 for (String s : lines) {
288 outw.print(" ");
289 outw.print(s);
290 outw.println();
291 }
292
293 outw.println();
294 if (c.getParentCount() == 1) {
295 rw.parseHeaders(c.getParent(0));
296 showDiff(c);
297 }
298 outw.flush();
299 }
300
301 private void showDiff(RevCommit c) throws IOException {
302 final RevTree a = c.getParent(0).getTree();
303 final RevTree b = c.getTree();
304
305 if (showNameAndStatusOnly)
306 Diff.nameStatus(outw, diffFmt.scan(a, b));
307 else {
308 outw.flush();
309 diffFmt.format(a, b);
310 diffFmt.flush();
311 }
312 outw.println();
313 }
314
315 private void showSignature(RevCommit c) throws IOException {
316 if (c.getRawGpgSignature() == null) {
317 return;
318 }
319 GpgSignatureVerifierFactory factory = GpgSignatureVerifierFactory
320 .getDefault();
321 if (factory == null) {
322 throw die(CLIText.get().logNoSignatureVerifier, null);
323 }
324 GpgSignatureVerifier verifier = factory.getVerifier();
325 GpgConfig config = new GpgConfig(db.getConfig());
326 try {
327 SignatureVerification verification = verifier.verifySignature(c,
328 config);
329 if (verification == null) {
330 return;
331 }
332 VerificationUtils.writeVerification(outw, verification,
333 verifier.getName(), c.getCommitterIdent());
334 } finally {
335 verifier.clear();
336 }
337 }
338 }