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.

RevCommit.java 22KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686
  1. /*
  2. * Copyright (C) 2008-2009, Google Inc.
  3. * Copyright (C) 2008, Shawn O. Pearce <spearce@spearce.org>
  4. * and other copyright owners as documented in the project's IP log.
  5. *
  6. * This program and the accompanying materials are made available
  7. * under the terms of the Eclipse Distribution License v1.0 which
  8. * accompanies this distribution, is reproduced below, and is
  9. * available at http://www.eclipse.org/org/documents/edl-v10.php
  10. *
  11. * All rights reserved.
  12. *
  13. * Redistribution and use in source and binary forms, with or
  14. * without modification, are permitted provided that the following
  15. * conditions are met:
  16. *
  17. * - Redistributions of source code must retain the above copyright
  18. * notice, this list of conditions and the following disclaimer.
  19. *
  20. * - Redistributions in binary form must reproduce the above
  21. * copyright notice, this list of conditions and the following
  22. * disclaimer in the documentation and/or other materials provided
  23. * with the distribution.
  24. *
  25. * - Neither the name of the Eclipse Foundation, Inc. nor the
  26. * names of its contributors may be used to endorse or promote
  27. * products derived from this software without specific prior
  28. * written permission.
  29. *
  30. * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
  31. * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
  32. * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
  33. * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
  34. * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
  35. * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
  36. * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
  37. * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
  38. * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
  39. * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
  40. * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
  41. * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
  42. * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  43. */
  44. package org.eclipse.jgit.revwalk;
  45. import static java.nio.charset.StandardCharsets.UTF_8;
  46. import java.io.IOException;
  47. import java.nio.charset.Charset;
  48. import java.nio.charset.IllegalCharsetNameException;
  49. import java.nio.charset.UnsupportedCharsetException;
  50. import java.util.ArrayList;
  51. import java.util.Collections;
  52. import java.util.List;
  53. import org.eclipse.jgit.annotations.Nullable;
  54. import org.eclipse.jgit.errors.IncorrectObjectTypeException;
  55. import org.eclipse.jgit.errors.MissingObjectException;
  56. import org.eclipse.jgit.lib.AnyObjectId;
  57. import org.eclipse.jgit.lib.Constants;
  58. import org.eclipse.jgit.lib.MutableObjectId;
  59. import org.eclipse.jgit.lib.ObjectInserter;
  60. import org.eclipse.jgit.lib.ObjectReader;
  61. import org.eclipse.jgit.lib.PersonIdent;
  62. import org.eclipse.jgit.util.RawParseUtils;
  63. import org.eclipse.jgit.util.StringUtils;
  64. /**
  65. * A commit reference to a commit in the DAG.
  66. */
  67. public class RevCommit extends RevObject {
  68. private static final int STACK_DEPTH = 500;
  69. /**
  70. * Parse a commit from its canonical format.
  71. *
  72. * This method constructs a temporary revision pool, parses the commit as
  73. * supplied, and returns it to the caller. Since the commit was built inside
  74. * of a private revision pool its parent pointers will be initialized, but
  75. * will not have their headers loaded.
  76. *
  77. * Applications are discouraged from using this API. Callers usually need
  78. * more than one commit. Use
  79. * {@link org.eclipse.jgit.revwalk.RevWalk#parseCommit(AnyObjectId)} to
  80. * obtain a RevCommit from an existing repository.
  81. *
  82. * @param raw
  83. * the canonical formatted commit to be parsed.
  84. * @return the parsed commit, in an isolated revision pool that is not
  85. * available to the caller.
  86. */
  87. public static RevCommit parse(byte[] raw) {
  88. try {
  89. return parse(new RevWalk((ObjectReader) null), raw);
  90. } catch (IOException ex) {
  91. throw new RuntimeException(ex);
  92. }
  93. }
  94. /**
  95. * Parse a commit from its canonical format.
  96. * <p>
  97. * This method inserts the commit directly into the caller supplied revision
  98. * pool, making it appear as though the commit exists in the repository,
  99. * even if it doesn't. The repository under the pool is not affected.
  100. * <p>
  101. * The body of the commit (message, author, committer) is always retained in
  102. * the returned {@code RevCommit}, even if the supplied {@code RevWalk} has
  103. * been configured with {@code setRetainBody(false)}.
  104. *
  105. * @param rw
  106. * the revision pool to allocate the commit within. The commit's
  107. * tree and parent pointers will be obtained from this pool.
  108. * @param raw
  109. * the canonical formatted commit to be parsed. This buffer will
  110. * be retained by the returned {@code RevCommit} and must not be
  111. * modified by the caller.
  112. * @return the parsed commit, in an isolated revision pool that is not
  113. * available to the caller.
  114. * @throws java.io.IOException
  115. * in case of RevWalk initialization fails
  116. */
  117. public static RevCommit parse(RevWalk rw, byte[] raw) throws IOException {
  118. try (ObjectInserter.Formatter fmt = new ObjectInserter.Formatter()) {
  119. RevCommit r = rw.lookupCommit(fmt.idFor(Constants.OBJ_COMMIT, raw));
  120. r.parseCanonical(rw, raw);
  121. r.buffer = raw;
  122. return r;
  123. }
  124. }
  125. static final RevCommit[] NO_PARENTS = {};
  126. private RevTree tree;
  127. RevCommit[] parents;
  128. int commitTime; // An int here for performance, overflows in 2038
  129. int inDegree;
  130. private byte[] buffer;
  131. /**
  132. * Create a new commit reference.
  133. *
  134. * @param id
  135. * object name for the commit.
  136. */
  137. protected RevCommit(AnyObjectId id) {
  138. super(id);
  139. }
  140. @Override
  141. void parseHeaders(RevWalk walk) throws MissingObjectException,
  142. IncorrectObjectTypeException, IOException {
  143. parseCanonical(walk, walk.getCachedBytes(this));
  144. }
  145. @Override
  146. void parseBody(RevWalk walk) throws MissingObjectException,
  147. IncorrectObjectTypeException, IOException {
  148. if (buffer == null) {
  149. buffer = walk.getCachedBytes(this);
  150. if ((flags & PARSED) == 0)
  151. parseCanonical(walk, buffer);
  152. }
  153. }
  154. void parseCanonical(RevWalk walk, byte[] raw) throws IOException {
  155. if (!walk.shallowCommitsInitialized) {
  156. walk.initializeShallowCommits(this);
  157. }
  158. final MutableObjectId idBuffer = walk.idBuffer;
  159. idBuffer.fromString(raw, 5);
  160. tree = walk.lookupTree(idBuffer);
  161. int ptr = 46;
  162. if (parents == null) {
  163. RevCommit[] pList = new RevCommit[1];
  164. int nParents = 0;
  165. for (;;) {
  166. if (raw[ptr] != 'p') {
  167. break;
  168. }
  169. idBuffer.fromString(raw, ptr + 7);
  170. final RevCommit p = walk.lookupCommit(idBuffer);
  171. if (nParents == 0) {
  172. pList[nParents++] = p;
  173. } else if (nParents == 1) {
  174. pList = new RevCommit[] { pList[0], p };
  175. nParents = 2;
  176. } else {
  177. if (pList.length <= nParents) {
  178. RevCommit[] old = pList;
  179. pList = new RevCommit[pList.length + 32];
  180. System.arraycopy(old, 0, pList, 0, nParents);
  181. }
  182. pList[nParents++] = p;
  183. }
  184. ptr += 48;
  185. }
  186. if (nParents != pList.length) {
  187. RevCommit[] old = pList;
  188. pList = new RevCommit[nParents];
  189. System.arraycopy(old, 0, pList, 0, nParents);
  190. }
  191. parents = pList;
  192. }
  193. // extract time from "committer "
  194. ptr = RawParseUtils.committer(raw, ptr);
  195. if (ptr > 0) {
  196. ptr = RawParseUtils.nextLF(raw, ptr, '>');
  197. // In 2038 commitTime will overflow unless it is changed to long.
  198. commitTime = RawParseUtils.parseBase10(raw, ptr, null);
  199. }
  200. if (walk.isRetainBody()) {
  201. buffer = raw;
  202. }
  203. flags |= PARSED;
  204. }
  205. /** {@inheritDoc} */
  206. @Override
  207. public final int getType() {
  208. return Constants.OBJ_COMMIT;
  209. }
  210. static void carryFlags(RevCommit c, int carry) {
  211. FIFORevQueue q = carryFlags1(c, carry, 0);
  212. if (q != null)
  213. slowCarryFlags(q, carry);
  214. }
  215. private static FIFORevQueue carryFlags1(RevCommit c, int carry, int depth) {
  216. for(;;) {
  217. RevCommit[] pList = c.parents;
  218. if (pList == null || pList.length == 0)
  219. return null;
  220. if (pList.length != 1) {
  221. if (depth == STACK_DEPTH)
  222. return defer(c);
  223. for (int i = 1; i < pList.length; i++) {
  224. RevCommit p = pList[i];
  225. if ((p.flags & carry) == carry)
  226. continue;
  227. p.flags |= carry;
  228. FIFORevQueue q = carryFlags1(p, carry, depth + 1);
  229. if (q != null)
  230. return defer(q, carry, pList, i + 1);
  231. }
  232. }
  233. c = pList[0];
  234. if ((c.flags & carry) == carry)
  235. return null;
  236. c.flags |= carry;
  237. }
  238. }
  239. private static FIFORevQueue defer(RevCommit c) {
  240. FIFORevQueue q = new FIFORevQueue();
  241. q.add(c);
  242. return q;
  243. }
  244. private static FIFORevQueue defer(FIFORevQueue q, int carry,
  245. RevCommit[] pList, int i) {
  246. // In normal case the caller will run pList[0] in a tail recursive
  247. // fashion by updating the variable. However the caller is unwinding
  248. // the stack and will skip that pList[0] execution step.
  249. carryOneStep(q, carry, pList[0]);
  250. // Remaining parents (if any) need to have flags checked and be
  251. // enqueued if they have ancestors.
  252. for (; i < pList.length; i++)
  253. carryOneStep(q, carry, pList[i]);
  254. return q;
  255. }
  256. private static void slowCarryFlags(FIFORevQueue q, int carry) {
  257. // Commits in q have non-null parent arrays and have set all
  258. // flags in carry. This loop finishes copying over the graph.
  259. for (RevCommit c; (c = q.next()) != null;) {
  260. for (RevCommit p : c.parents)
  261. carryOneStep(q, carry, p);
  262. }
  263. }
  264. private static void carryOneStep(FIFORevQueue q, int carry, RevCommit c) {
  265. if ((c.flags & carry) != carry) {
  266. c.flags |= carry;
  267. if (c.parents != null)
  268. q.add(c);
  269. }
  270. }
  271. /**
  272. * Carry a RevFlag set on this commit to its parents.
  273. * <p>
  274. * If this commit is parsed, has parents, and has the supplied flag set on
  275. * it we automatically add it to the parents, grand-parents, and so on until
  276. * an unparsed commit or a commit with no parents is discovered. This
  277. * permits applications to force a flag through the history chain when
  278. * necessary.
  279. *
  280. * @param flag
  281. * the single flag value to carry back onto parents.
  282. */
  283. public void carry(RevFlag flag) {
  284. final int carry = flags & flag.mask;
  285. if (carry != 0)
  286. carryFlags(this, carry);
  287. }
  288. /**
  289. * Time from the "committer " line of the buffer.
  290. *
  291. * @return commit time
  292. */
  293. public final int getCommitTime() {
  294. return commitTime;
  295. }
  296. /**
  297. * Get a reference to this commit's tree.
  298. *
  299. * @return tree of this commit.
  300. */
  301. public final RevTree getTree() {
  302. return tree;
  303. }
  304. /**
  305. * Get the number of parent commits listed in this commit.
  306. *
  307. * @return number of parents; always a positive value but can be 0.
  308. */
  309. public final int getParentCount() {
  310. return parents.length;
  311. }
  312. /**
  313. * Get the nth parent from this commit's parent list.
  314. *
  315. * @param nth
  316. * parent index to obtain. Must be in the range 0 through
  317. * {@link #getParentCount()}-1.
  318. * @return the specified parent.
  319. * @throws java.lang.ArrayIndexOutOfBoundsException
  320. * an invalid parent index was specified.
  321. */
  322. public final RevCommit getParent(int nth) {
  323. return parents[nth];
  324. }
  325. /**
  326. * Obtain an array of all parents (<b>NOTE - THIS IS NOT A COPY</b>).
  327. * <p>
  328. * This method is exposed only to provide very fast, efficient access to
  329. * this commit's parent list. Applications relying on this list should be
  330. * very careful to ensure they do not modify its contents during their use
  331. * of it.
  332. *
  333. * @return the array of parents.
  334. */
  335. public final RevCommit[] getParents() {
  336. return parents;
  337. }
  338. /**
  339. * Obtain the raw unparsed commit body (<b>NOTE - THIS IS NOT A COPY</b>).
  340. * <p>
  341. * This method is exposed only to provide very fast, efficient access to
  342. * this commit's message buffer within a RevFilter. Applications relying on
  343. * this buffer should be very careful to ensure they do not modify its
  344. * contents during their use of it.
  345. *
  346. * @return the raw unparsed commit body. This is <b>NOT A COPY</b>.
  347. * Altering the contents of this buffer may alter the walker's
  348. * knowledge of this commit, and the results it produces.
  349. */
  350. public final byte[] getRawBuffer() {
  351. return buffer;
  352. }
  353. /**
  354. * Parse the author identity from the raw buffer.
  355. * <p>
  356. * This method parses and returns the content of the author line, after
  357. * taking the commit's character set into account and decoding the author
  358. * name and email address. This method is fairly expensive and produces a
  359. * new PersonIdent instance on each invocation. Callers should invoke this
  360. * method only if they are certain they will be outputting the result, and
  361. * should cache the return value for as long as necessary to use all
  362. * information from it.
  363. * <p>
  364. * RevFilter implementations should try to use
  365. * {@link org.eclipse.jgit.util.RawParseUtils} to scan the
  366. * {@link #getRawBuffer()} instead, as this will allow faster evaluation of
  367. * commits.
  368. *
  369. * @return identity of the author (name, email) and the time the commit was
  370. * made by the author; null if no author line was found.
  371. */
  372. public final PersonIdent getAuthorIdent() {
  373. final byte[] raw = buffer;
  374. final int nameB = RawParseUtils.author(raw, 0);
  375. if (nameB < 0)
  376. return null;
  377. return RawParseUtils.parsePersonIdent(raw, nameB);
  378. }
  379. /**
  380. * Parse the committer identity from the raw buffer.
  381. * <p>
  382. * This method parses and returns the content of the committer line, after
  383. * taking the commit's character set into account and decoding the committer
  384. * name and email address. This method is fairly expensive and produces a
  385. * new PersonIdent instance on each invocation. Callers should invoke this
  386. * method only if they are certain they will be outputting the result, and
  387. * should cache the return value for as long as necessary to use all
  388. * information from it.
  389. * <p>
  390. * RevFilter implementations should try to use
  391. * {@link org.eclipse.jgit.util.RawParseUtils} to scan the
  392. * {@link #getRawBuffer()} instead, as this will allow faster evaluation of
  393. * commits.
  394. *
  395. * @return identity of the committer (name, email) and the time the commit
  396. * was made by the committer; null if no committer line was found.
  397. */
  398. public final PersonIdent getCommitterIdent() {
  399. final byte[] raw = buffer;
  400. final int nameB = RawParseUtils.committer(raw, 0);
  401. if (nameB < 0)
  402. return null;
  403. return RawParseUtils.parsePersonIdent(raw, nameB);
  404. }
  405. /**
  406. * Parse the complete commit message and decode it to a string.
  407. * <p>
  408. * This method parses and returns the message portion of the commit buffer,
  409. * after taking the commit's character set into account and decoding the
  410. * buffer using that character set. This method is a fairly expensive
  411. * operation and produces a new string on each invocation.
  412. *
  413. * @return decoded commit message as a string. Never null.
  414. */
  415. public final String getFullMessage() {
  416. byte[] raw = buffer;
  417. int msgB = RawParseUtils.commitMessage(raw, 0);
  418. if (msgB < 0) {
  419. return ""; //$NON-NLS-1$
  420. }
  421. return RawParseUtils.decode(guessEncoding(), raw, msgB, raw.length);
  422. }
  423. /**
  424. * Parse the commit message and return the first "line" of it.
  425. * <p>
  426. * The first line is everything up to the first pair of LFs. This is the
  427. * "oneline" format, suitable for output in a single line display.
  428. * <p>
  429. * This method parses and returns the message portion of the commit buffer,
  430. * after taking the commit's character set into account and decoding the
  431. * buffer using that character set. This method is a fairly expensive
  432. * operation and produces a new string on each invocation.
  433. *
  434. * @return decoded commit message as a string. Never null. The returned
  435. * string does not contain any LFs, even if the first paragraph
  436. * spanned multiple lines. Embedded LFs are converted to spaces.
  437. */
  438. public final String getShortMessage() {
  439. byte[] raw = buffer;
  440. int msgB = RawParseUtils.commitMessage(raw, 0);
  441. if (msgB < 0) {
  442. return ""; //$NON-NLS-1$
  443. }
  444. int msgE = RawParseUtils.endOfParagraph(raw, msgB);
  445. String str = RawParseUtils.decode(guessEncoding(), raw, msgB, msgE);
  446. if (hasLF(raw, msgB, msgE)) {
  447. str = StringUtils.replaceLineBreaksWithSpace(str);
  448. }
  449. return str;
  450. }
  451. static boolean hasLF(byte[] r, int b, int e) {
  452. while (b < e)
  453. if (r[b++] == '\n')
  454. return true;
  455. return false;
  456. }
  457. /**
  458. * Determine the encoding of the commit message buffer.
  459. * <p>
  460. * Locates the "encoding" header (if present) and returns its value. Due to
  461. * corruption in the wild this may be an invalid encoding name that is not
  462. * recognized by any character encoding library.
  463. * <p>
  464. * If no encoding header is present, null.
  465. *
  466. * @return the preferred encoding of {@link #getRawBuffer()}; or null.
  467. * @since 4.2
  468. */
  469. @Nullable
  470. public final String getEncodingName() {
  471. return RawParseUtils.parseEncodingName(buffer);
  472. }
  473. /**
  474. * Determine the encoding of the commit message buffer.
  475. * <p>
  476. * Locates the "encoding" header (if present) and then returns the proper
  477. * character set to apply to this buffer to evaluate its contents as
  478. * character data.
  479. * <p>
  480. * If no encoding header is present {@code UTF-8} is assumed.
  481. *
  482. * @return the preferred encoding of {@link #getRawBuffer()}.
  483. * @throws IllegalCharsetNameException
  484. * if the character set requested by the encoding header is
  485. * malformed and unsupportable.
  486. * @throws UnsupportedCharsetException
  487. * if the JRE does not support the character set requested by
  488. * the encoding header.
  489. */
  490. public final Charset getEncoding() {
  491. return RawParseUtils.parseEncoding(buffer);
  492. }
  493. private Charset guessEncoding() {
  494. try {
  495. return getEncoding();
  496. } catch (IllegalCharsetNameException | UnsupportedCharsetException e) {
  497. return UTF_8;
  498. }
  499. }
  500. /**
  501. * Parse the footer lines (e.g. "Signed-off-by") for machine processing.
  502. * <p>
  503. * This method splits all of the footer lines out of the last paragraph of
  504. * the commit message, providing each line as a key-value pair, ordered by
  505. * the order of the line's appearance in the commit message itself.
  506. * <p>
  507. * A footer line's key must match the pattern {@code ^[A-Za-z0-9-]+:}, while
  508. * the value is free-form, but must not contain an LF. Very common keys seen
  509. * in the wild are:
  510. * <ul>
  511. * <li>{@code Signed-off-by} (agrees to Developer Certificate of Origin)
  512. * <li>{@code Acked-by} (thinks change looks sane in context)
  513. * <li>{@code Reported-by} (originally found the issue this change fixes)
  514. * <li>{@code Tested-by} (validated change fixes the issue for them)
  515. * <li>{@code CC}, {@code Cc} (copy on all email related to this change)
  516. * <li>{@code Bug} (link to project's bug tracking system)
  517. * </ul>
  518. *
  519. * @return ordered list of footer lines; empty list if no footers found.
  520. */
  521. public final List<FooterLine> getFooterLines() {
  522. final byte[] raw = buffer;
  523. int ptr = raw.length - 1;
  524. while (raw[ptr] == '\n') // trim any trailing LFs, not interesting
  525. ptr--;
  526. final int msgB = RawParseUtils.commitMessage(raw, 0);
  527. final ArrayList<FooterLine> r = new ArrayList<>(4);
  528. final Charset enc = guessEncoding();
  529. for (;;) {
  530. ptr = RawParseUtils.prevLF(raw, ptr);
  531. if (ptr <= msgB)
  532. break; // Don't parse commit headers as footer lines.
  533. final int keyStart = ptr + 2;
  534. if (raw[keyStart] == '\n')
  535. break; // Stop at first paragraph break, no footers above it.
  536. final int keyEnd = RawParseUtils.endOfFooterLineKey(raw, keyStart);
  537. if (keyEnd < 0)
  538. continue; // Not a well formed footer line, skip it.
  539. // Skip over the ': *' at the end of the key before the value.
  540. //
  541. int valStart = keyEnd + 1;
  542. while (valStart < raw.length && raw[valStart] == ' ')
  543. valStart++;
  544. // Value ends at the LF, and does not include it.
  545. //
  546. int valEnd = RawParseUtils.nextLF(raw, valStart);
  547. if (raw[valEnd - 1] == '\n')
  548. valEnd--;
  549. r.add(new FooterLine(raw, enc, keyStart, keyEnd, valStart, valEnd));
  550. }
  551. Collections.reverse(r);
  552. return r;
  553. }
  554. /**
  555. * Get the values of all footer lines with the given key.
  556. *
  557. * @param keyName
  558. * footer key to find values of, case insensitive.
  559. * @return values of footers with key of {@code keyName}, ordered by their
  560. * order of appearance. Duplicates may be returned if the same
  561. * footer appeared more than once. Empty list if no footers appear
  562. * with the specified key, or there are no footers at all.
  563. * @see #getFooterLines()
  564. */
  565. public final List<String> getFooterLines(String keyName) {
  566. return getFooterLines(new FooterKey(keyName));
  567. }
  568. /**
  569. * Get the values of all footer lines with the given key.
  570. *
  571. * @param keyName
  572. * footer key to find values of, case insensitive.
  573. * @return values of footers with key of {@code keyName}, ordered by their
  574. * order of appearance. Duplicates may be returned if the same
  575. * footer appeared more than once. Empty list if no footers appear
  576. * with the specified key, or there are no footers at all.
  577. * @see #getFooterLines()
  578. */
  579. public final List<String> getFooterLines(FooterKey keyName) {
  580. final List<FooterLine> src = getFooterLines();
  581. if (src.isEmpty())
  582. return Collections.emptyList();
  583. final ArrayList<String> r = new ArrayList<>(src.size());
  584. for (FooterLine f : src) {
  585. if (f.matches(keyName))
  586. r.add(f.getValue());
  587. }
  588. return r;
  589. }
  590. /**
  591. * Reset this commit to allow another RevWalk with the same instances.
  592. * <p>
  593. * Subclasses <b>must</b> call <code>super.reset()</code> to ensure the
  594. * basic information can be correctly cleared out.
  595. */
  596. public void reset() {
  597. inDegree = 0;
  598. }
  599. /**
  600. * Discard the message buffer to reduce memory usage.
  601. * <p>
  602. * After discarding the memory usage of the {@code RevCommit} is reduced to
  603. * only the {@link #getTree()} and {@link #getParents()} pointers and the
  604. * time in {@link #getCommitTime()}. Accessing other properties such as
  605. * {@link #getAuthorIdent()}, {@link #getCommitterIdent()} or either message
  606. * function requires reloading the buffer by invoking
  607. * {@link org.eclipse.jgit.revwalk.RevWalk#parseBody(RevObject)}.
  608. *
  609. * @since 4.0
  610. */
  611. public final void disposeBody() {
  612. buffer = null;
  613. }
  614. /** {@inheritDoc} */
  615. @Override
  616. public String toString() {
  617. final StringBuilder s = new StringBuilder();
  618. s.append(Constants.typeString(getType()));
  619. s.append(' ');
  620. s.append(name());
  621. s.append(' ');
  622. s.append(commitTime);
  623. s.append(' ');
  624. appendCoreFlags(s);
  625. return s.toString();
  626. }
  627. }