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