Nevar pievienot vairāk kā 25 tēmas Tēmai ir jāsākas ar burtu vai ciparu, tā var saturēt domu zīmes ('-') un var būt līdz 35 simboliem gara.

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