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