You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

Log.java 9.5KB

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