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 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() throws Exception {
197 diffFmt.setRepository(db);
198 try {
199 diffFmt.setPathFilter(pathFilter);
200 if (detectRenames != null)
201 diffFmt.setDetectRenames(detectRenames.booleanValue());
202 if (renameLimit != null && diffFmt.isDetectRenames()) {
203 RenameDetector rd = diffFmt.getRenameDetector();
204 rd.setRenameLimit(renameLimit.intValue());
205 }
206
207 if (!noStandardNotes || !additionalNoteRefs.isEmpty()) {
208 createWalk();
209 noteMaps = new LinkedHashMap<>();
210 if (!noStandardNotes) {
211 addNoteMap(Constants.R_NOTES_COMMITS);
212 }
213 if (!additionalNoteRefs.isEmpty()) {
214 for (String notesRef : additionalNoteRefs) {
215 if (!notesRef.startsWith(Constants.R_NOTES)) {
216 notesRef = Constants.R_NOTES + notesRef;
217 }
218 addNoteMap(notesRef);
219 }
220 }
221 }
222
223 if (decorate)
224 allRefsByPeeledObjectId = getRepository()
225 .getAllRefsByPeeledObjectId();
226
227 super.run();
228 } finally {
229 diffFmt.close();
230 }
231 }
232
233 private void addNoteMap(String notesRef) throws IOException {
234 Ref notes = db.exactRef(notesRef);
235 if (notes == null)
236 return;
237 RevCommit notesCommit = argWalk.parseCommit(notes.getObjectId());
238 noteMaps.put(notesRef,
239 NoteMap.read(argWalk.getObjectReader(), notesCommit));
240 }
241
242
243 @Override
244 protected void show(RevCommit c) throws Exception {
245 outw.print(CLIText.get().commitLabel);
246 outw.print(" ");
247 c.getId().copyTo(outbuffer, outw);
248 if (decorate) {
249 Collection<Ref> list = allRefsByPeeledObjectId.get(c);
250 if (list != null) {
251 outw.print(" (");
252 for (Iterator<Ref> i = list.iterator(); i.hasNext(); ) {
253 outw.print(i.next().getName());
254 if (i.hasNext())
255 outw.print(" ");
256 }
257 outw.print(")");
258 }
259 }
260 outw.println();
261
262 final PersonIdent author = c.getAuthorIdent();
263 outw.println(MessageFormat.format(CLIText.get().authorInfo, author.getName(), author.getEmailAddress()));
264 outw.println(MessageFormat.format(CLIText.get().dateInfo,
265 dateFormatter.formatDate(author)));
266
267 outw.println();
268 final String[] lines = c.getFullMessage().split("\n");
269 for (String s : lines) {
270 outw.print(" ");
271 outw.print(s);
272 outw.println();
273 }
274 c.disposeBody();
275
276 outw.println();
277 if (showNotes(c))
278 outw.println();
279
280 if (c.getParentCount() <= 1 && (showNameAndStatusOnly || showPatch))
281 showDiff(c);
282 outw.flush();
283 }
284
285
286
287
288
289
290
291 private boolean showNotes(RevCommit c) throws IOException {
292 if (noteMaps == null)
293 return false;
294
295 boolean printEmptyLine = false;
296 boolean atLeastOnePrinted = false;
297 for (Map.Entry<String, NoteMap> e : noteMaps.entrySet()) {
298 String label = null;
299 String notesRef = e.getKey();
300 if (! notesRef.equals(Constants.R_NOTES_COMMITS)) {
301 if (notesRef.startsWith(Constants.R_NOTES))
302 label = notesRef.substring(Constants.R_NOTES.length());
303 else
304 label = notesRef;
305 }
306 boolean printedNote = showNotes(c, e.getValue(), label,
307 printEmptyLine);
308 atLeastOnePrinted |= printedNote;
309 printEmptyLine = printedNote;
310 }
311 return atLeastOnePrinted;
312 }
313
314
315
316
317
318
319
320
321
322
323 private boolean showNotes(RevCommit c, NoteMap map, String label,
324 boolean emptyLine)
325 throws IOException {
326 ObjectId blobId = map.get(c);
327 if (blobId == null)
328 return false;
329 if (emptyLine)
330 outw.println();
331 outw.print("Notes");
332 if (label != null) {
333 outw.print(" (");
334 outw.print(label);
335 outw.print(")");
336 }
337 outw.println(":");
338 try {
339 RawText rawText = new RawText(argWalk.getObjectReader()
340 .open(blobId).getCachedBytes(Integer.MAX_VALUE));
341 for (int i = 0; i < rawText.size(); i++) {
342 outw.print(" ");
343 outw.println(rawText.getString(i));
344 }
345 } catch (LargeObjectException e) {
346 outw.println(MessageFormat.format(
347 CLIText.get().noteObjectTooLargeToPrint, blobId.name()));
348 }
349 return true;
350 }
351
352 private void showDiff(RevCommit c) throws IOException {
353 final RevTree a = c.getParentCount() > 0 ? c.getParent(0).getTree()
354 : null;
355 final RevTree b = c.getTree();
356
357 if (showNameAndStatusOnly)
358 Diff.nameStatus(outw, diffFmt.scan(a, b));
359 else {
360 outw.flush();
361 diffFmt.format(a, b);
362 diffFmt.flush();
363 }
364 outw.println();
365 }
366 }