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 java.io.BufferedOutputStream;
49 import java.io.IOException;
50 import java.text.MessageFormat;
51 import java.util.ArrayList;
52 import java.util.Collection;
53 import java.util.Iterator;
54 import java.util.LinkedHashMap;
55 import java.util.List;
56 import java.util.Locale;
57 import java.util.Map;
58 import java.util.Set;
59
60 import org.eclipse.jgit.diff.DiffFormatter;
61 import org.eclipse.jgit.diff.RawText;
62 import org.eclipse.jgit.diff.RawTextComparator;
63 import org.eclipse.jgit.diff.RenameDetector;
64 import org.eclipse.jgit.errors.LargeObjectException;
65 import org.eclipse.jgit.lib.AnyObjectId;
66 import org.eclipse.jgit.lib.Constants;
67 import org.eclipse.jgit.lib.ObjectId;
68 import org.eclipse.jgit.lib.PersonIdent;
69 import org.eclipse.jgit.lib.Ref;
70 import org.eclipse.jgit.lib.Repository;
71 import org.eclipse.jgit.notes.NoteMap;
72 import org.eclipse.jgit.pgm.internal.CLIText;
73 import org.eclipse.jgit.revwalk.RevCommit;
74 import org.eclipse.jgit.revwalk.RevTree;
75 import org.eclipse.jgit.util.GitDateFormatter;
76 import org.eclipse.jgit.util.GitDateFormatter.Format;
77 import org.kohsuke.args4j.Option;
78
79 @Command(common = true, usage = "usage_viewCommitHistory")
80 class Log extends RevWalkTextBuiltin {
81
82 private GitDateFormatteritDateFormatter">GitDateFormatter dateFormatter = new GitDateFormatter(
83 Format.DEFAULT);
84
85 private DiffFormatter diffFmt;
86
87 private Map<AnyObjectId, Set<Ref>> allRefsByPeeledObjectId;
88
89 private Map<String, NoteMap> noteMaps;
90
91 @Option(name="--decorate", usage="usage_showRefNamesMatchingCommits")
92 private boolean decorate;
93
94 @Option(name = "--no-standard-notes", usage = "usage_noShowStandardNotes")
95 private boolean noStandardNotes;
96
97 private List<String> additionalNoteRefs = new ArrayList<>();
98
99 @Option(name = "--show-notes", usage = "usage_showNotes", metaVar = "metaVar_ref")
100 void addAdditionalNoteRef(String notesRef) {
101 additionalNoteRefs.add(notesRef);
102 }
103
104 @Option(name = "--date", usage = "usage_date")
105 void dateFormat(String date) {
106 if (date.toLowerCase(Locale.ROOT).equals(date))
107 date = date.toUpperCase(Locale.ROOT);
108 dateFormatter = new GitDateFormatter(Format.valueOf(date));
109 }
110
111
112 @Option(name = "-p", usage = "usage_showPatch")
113 boolean showPatch;
114
115 @Option(name = "-M", usage = "usage_detectRenames")
116 private Boolean detectRenames;
117
118 @Option(name = "--no-renames", usage = "usage_noRenames")
119 void noRenames(@SuppressWarnings("unused") boolean on) {
120 detectRenames = Boolean.FALSE;
121 }
122
123 @Option(name = "-l", usage = "usage_renameLimit")
124 private Integer renameLimit;
125
126 @Option(name = "--name-status", usage = "usage_nameStatus")
127 private boolean showNameAndStatusOnly;
128
129 @Option(name = "--ignore-space-at-eol")
130 void ignoreSpaceAtEol(@SuppressWarnings("unused") boolean on) {
131 diffFmt.setDiffComparator(RawTextComparator.WS_IGNORE_TRAILING);
132 }
133
134 @Option(name = "--ignore-leading-space")
135 void ignoreLeadingSpace(@SuppressWarnings("unused") boolean on) {
136 diffFmt.setDiffComparator(RawTextComparator.WS_IGNORE_LEADING);
137 }
138
139 @Option(name = "-b", aliases = { "--ignore-space-change" })
140 void ignoreSpaceChange(@SuppressWarnings("unused") boolean on) {
141 diffFmt.setDiffComparator(RawTextComparator.WS_IGNORE_CHANGE);
142 }
143
144 @Option(name = "-w", aliases = { "--ignore-all-space" })
145 void ignoreAllSpace(@SuppressWarnings("unused") boolean on) {
146 diffFmt.setDiffComparator(RawTextComparator.WS_IGNORE_ALL);
147 }
148
149 @Option(name = "-U", aliases = { "--unified" }, metaVar = "metaVar_linesOfContext")
150 void unified(int lines) {
151 diffFmt.setContext(lines);
152 }
153
154 @Option(name = "--abbrev", metaVar = "metaVar_n")
155 void abbrev(int lines) {
156 diffFmt.setAbbreviationLength(lines);
157 }
158
159 @Option(name = "--full-index")
160 void abbrev(@SuppressWarnings("unused") boolean on) {
161 diffFmt.setAbbreviationLength(Constants.OBJECT_ID_STRING_LENGTH);
162 }
163
164 @Option(name = "--src-prefix", usage = "usage_srcPrefix")
165 void sourcePrefix(String path) {
166 diffFmt.setOldPrefix(path);
167 }
168
169 @Option(name = "--dst-prefix", usage = "usage_dstPrefix")
170 void dstPrefix(String path) {
171 diffFmt.setNewPrefix(path);
172 }
173
174 @Option(name = "--no-prefix", usage = "usage_noPrefix")
175 void noPrefix(@SuppressWarnings("unused") boolean on) {
176 diffFmt.setOldPrefix("");
177 diffFmt.setNewPrefix("");
178 }
179
180
181
182
183 Log() {
184 dateFormatter = new GitDateFormatter(Format.DEFAULT);
185 }
186
187
188 @Override
189 protected void init(Repository repository, String gitDir) {
190 super.init(repository, gitDir);
191 diffFmt = new DiffFormatter(new BufferedOutputStream(outs));
192 }
193
194
195 @Override
196 protected void run() {
197 diffFmt.setRepository(db);
198 try {
199 diffFmt.setPathFilter(pathFilter);
200 if (detectRenames != null) {
201 diffFmt.setDetectRenames(detectRenames.booleanValue());
202 }
203 if (renameLimit != null && diffFmt.isDetectRenames()) {
204 RenameDetector rd = diffFmt.getRenameDetector();
205 rd.setRenameLimit(renameLimit.intValue());
206 }
207
208 if (!noStandardNotes || !additionalNoteRefs.isEmpty()) {
209 createWalk();
210 noteMaps = new LinkedHashMap<>();
211 if (!noStandardNotes) {
212 addNoteMap(Constants.R_NOTES_COMMITS);
213 }
214 if (!additionalNoteRefs.isEmpty()) {
215 for (String notesRef : additionalNoteRefs) {
216 if (!notesRef.startsWith(Constants.R_NOTES)) {
217 notesRef = Constants.R_NOTES + notesRef;
218 }
219 addNoteMap(notesRef);
220 }
221 }
222 }
223
224 if (decorate) {
225 allRefsByPeeledObjectId = getRepository()
226 .getAllRefsByPeeledObjectId();
227 }
228 super.run();
229 } catch (Exception e) {
230 throw die(e.getMessage(), e);
231 } finally {
232 diffFmt.close();
233 }
234 }
235
236 private void addNoteMap(String notesRef) throws IOException {
237 Ref notes = db.exactRef(notesRef);
238 if (notes == null)
239 return;
240 RevCommit notesCommit = argWalk.parseCommit(notes.getObjectId());
241 noteMaps.put(notesRef,
242 NoteMap.read(argWalk.getObjectReader(), notesCommit));
243 }
244
245
246 @Override
247 protected void show(RevCommit c) throws Exception {
248 outw.print(CLIText.get().commitLabel);
249 outw.print(" ");
250 c.getId().copyTo(outbuffer, outw);
251 if (decorate) {
252 Collection<Ref> list = allRefsByPeeledObjectId.get(c);
253 if (list != null) {
254 outw.print(" (");
255 for (Iterator<Ref> i = list.iterator(); i.hasNext(); ) {
256 outw.print(i.next().getName());
257 if (i.hasNext())
258 outw.print(" ");
259 }
260 outw.print(")");
261 }
262 }
263 outw.println();
264
265 final PersonIdent author = c.getAuthorIdent();
266 outw.println(MessageFormat.format(CLIText.get().authorInfo, author.getName(), author.getEmailAddress()));
267 outw.println(MessageFormat.format(CLIText.get().dateInfo,
268 dateFormatter.formatDate(author)));
269
270 outw.println();
271 final String[] lines = c.getFullMessage().split("\n");
272 for (String s : lines) {
273 outw.print(" ");
274 outw.print(s);
275 outw.println();
276 }
277 c.disposeBody();
278
279 outw.println();
280 if (showNotes(c))
281 outw.println();
282
283 if (c.getParentCount() <= 1 && (showNameAndStatusOnly || showPatch))
284 showDiff(c);
285 outw.flush();
286 }
287
288
289
290
291
292
293
294 private boolean showNotes(RevCommit c) throws IOException {
295 if (noteMaps == null)
296 return false;
297
298 boolean printEmptyLine = false;
299 boolean atLeastOnePrinted = false;
300 for (Map.Entry<String, NoteMap> e : noteMaps.entrySet()) {
301 String label = null;
302 String notesRef = e.getKey();
303 if (! notesRef.equals(Constants.R_NOTES_COMMITS)) {
304 if (notesRef.startsWith(Constants.R_NOTES))
305 label = notesRef.substring(Constants.R_NOTES.length());
306 else
307 label = notesRef;
308 }
309 boolean printedNote = showNotes(c, e.getValue(), label,
310 printEmptyLine);
311 atLeastOnePrinted |= printedNote;
312 printEmptyLine = printedNote;
313 }
314 return atLeastOnePrinted;
315 }
316
317
318
319
320
321
322
323
324
325
326 private boolean showNotes(RevCommit c, NoteMap map, String label,
327 boolean emptyLine)
328 throws IOException {
329 ObjectId blobId = map.get(c);
330 if (blobId == null)
331 return false;
332 if (emptyLine)
333 outw.println();
334 outw.print("Notes");
335 if (label != null) {
336 outw.print(" (");
337 outw.print(label);
338 outw.print(")");
339 }
340 outw.println(":");
341 try {
342 RawText rawText = new RawText(argWalk.getObjectReader()
343 .open(blobId).getCachedBytes(Integer.MAX_VALUE));
344 for (int i = 0; i < rawText.size(); i++) {
345 outw.print(" ");
346 outw.println(rawText.getString(i));
347 }
348 } catch (LargeObjectException e) {
349 outw.println(MessageFormat.format(
350 CLIText.get().noteObjectTooLargeToPrint, blobId.name()));
351 }
352 return true;
353 }
354
355 private void showDiff(RevCommit c) throws IOException {
356 final RevTree a = c.getParentCount() > 0 ? c.getParent(0).getTree()
357 : null;
358 final RevTree b = c.getTree();
359
360 if (showNameAndStatusOnly)
361 Diff.nameStatus(outw, diffFmt.scan(a, b));
362 else {
363 outw.flush();
364 diffFmt.format(a, b);
365 diffFmt.flush();
366 }
367 outw.println();
368 }
369 }