RevCommit.java

  1. /*
  2.  * Copyright (C) 2008-2009, Google Inc.
  3.  * Copyright (C) 2008, Shawn O. Pearce <spearce@spearce.org> and others
  4.  *
  5.  * This program and the accompanying materials are made available under the
  6.  * terms of the Eclipse Distribution License v. 1.0 which is available at
  7.  * https://www.eclipse.org/org/documents/edl-v10.php.
  8.  *
  9.  * SPDX-License-Identifier: BSD-3-Clause
  10.  */

  11. package org.eclipse.jgit.revwalk;

  12. import static java.nio.charset.StandardCharsets.UTF_8;

  13. import java.io.IOException;
  14. import java.nio.charset.Charset;
  15. import java.nio.charset.IllegalCharsetNameException;
  16. import java.nio.charset.UnsupportedCharsetException;
  17. import java.util.ArrayList;
  18. import java.util.Arrays;
  19. import java.util.Collections;
  20. import java.util.List;

  21. import org.eclipse.jgit.annotations.Nullable;
  22. import org.eclipse.jgit.errors.IncorrectObjectTypeException;
  23. import org.eclipse.jgit.errors.MissingObjectException;
  24. import org.eclipse.jgit.lib.AnyObjectId;
  25. import org.eclipse.jgit.lib.Constants;
  26. import org.eclipse.jgit.lib.MutableObjectId;
  27. import org.eclipse.jgit.lib.ObjectInserter;
  28. import org.eclipse.jgit.lib.ObjectReader;
  29. import org.eclipse.jgit.lib.PersonIdent;
  30. import org.eclipse.jgit.util.RawParseUtils;
  31. import org.eclipse.jgit.util.StringUtils;

  32. /**
  33.  * A commit reference to a commit in the DAG.
  34.  */
  35. public class RevCommit extends RevObject {
  36.     private static final int STACK_DEPTH = 500;

  37.     /**
  38.      * Parse a commit from its canonical format.
  39.      *
  40.      * This method constructs a temporary revision pool, parses the commit as
  41.      * supplied, and returns it to the caller. Since the commit was built inside
  42.      * of a private revision pool its parent pointers will be initialized, but
  43.      * will not have their headers loaded.
  44.      *
  45.      * Applications are discouraged from using this API. Callers usually need
  46.      * more than one commit. Use
  47.      * {@link org.eclipse.jgit.revwalk.RevWalk#parseCommit(AnyObjectId)} to
  48.      * obtain a RevCommit from an existing repository.
  49.      *
  50.      * @param raw
  51.      *            the canonical formatted commit to be parsed.
  52.      * @return the parsed commit, in an isolated revision pool that is not
  53.      *         available to the caller.
  54.      */
  55.     public static RevCommit parse(byte[] raw) {
  56.         try {
  57.             return parse(new RevWalk((ObjectReader) null), raw);
  58.         } catch (IOException ex) {
  59.             throw new RuntimeException(ex);
  60.         }
  61.     }

  62.     /**
  63.      * Parse a commit from its canonical format.
  64.      * <p>
  65.      * This method inserts the commit directly into the caller supplied revision
  66.      * pool, making it appear as though the commit exists in the repository,
  67.      * even if it doesn't. The repository under the pool is not affected.
  68.      * <p>
  69.      * The body of the commit (message, author, committer) is always retained in
  70.      * the returned {@code RevCommit}, even if the supplied {@code RevWalk} has
  71.      * been configured with {@code setRetainBody(false)}.
  72.      *
  73.      * @param rw
  74.      *            the revision pool to allocate the commit within. The commit's
  75.      *            tree and parent pointers will be obtained from this pool.
  76.      * @param raw
  77.      *            the canonical formatted commit to be parsed. This buffer will
  78.      *            be retained by the returned {@code RevCommit} and must not be
  79.      *            modified by the caller.
  80.      * @return the parsed commit, in an isolated revision pool that is not
  81.      *         available to the caller.
  82.      * @throws java.io.IOException
  83.      *             in case of RevWalk initialization fails
  84.      */
  85.     public static RevCommit parse(RevWalk rw, byte[] raw) throws IOException {
  86.         try (ObjectInserter.Formatter fmt = new ObjectInserter.Formatter()) {
  87.             RevCommit r = rw.lookupCommit(fmt.idFor(Constants.OBJ_COMMIT, raw));
  88.             r.parseCanonical(rw, raw);
  89.             r.buffer = raw;
  90.             return r;
  91.         }
  92.     }

  93.     static final RevCommit[] NO_PARENTS = {};

  94.     private RevTree tree;

  95.     RevCommit[] parents;

  96.     int commitTime; // An int here for performance, overflows in 2038

  97.     int inDegree;

  98.     private byte[] buffer;

  99.     /**
  100.      * Create a new commit reference.
  101.      *
  102.      * @param id
  103.      *            object name for the commit.
  104.      */
  105.     protected RevCommit(AnyObjectId id) {
  106.         super(id);
  107.     }

  108.     @Override
  109.     void parseHeaders(RevWalk walk) throws MissingObjectException,
  110.             IncorrectObjectTypeException, IOException {
  111.         parseCanonical(walk, walk.getCachedBytes(this));
  112.     }

  113.     @Override
  114.     void parseBody(RevWalk walk) throws MissingObjectException,
  115.             IncorrectObjectTypeException, IOException {
  116.         if (buffer == null) {
  117.             buffer = walk.getCachedBytes(this);
  118.             if ((flags & PARSED) == 0)
  119.                 parseCanonical(walk, buffer);
  120.         }
  121.     }

  122.     void parseCanonical(RevWalk walk, byte[] raw) throws IOException {
  123.         if (!walk.shallowCommitsInitialized) {
  124.             walk.initializeShallowCommits(this);
  125.         }

  126.         final MutableObjectId idBuffer = walk.idBuffer;
  127.         idBuffer.fromString(raw, 5);
  128.         tree = walk.lookupTree(idBuffer);

  129.         int ptr = 46;
  130.         if (parents == null) {
  131.             RevCommit[] pList = new RevCommit[1];
  132.             int nParents = 0;
  133.             for (;;) {
  134.                 if (raw[ptr] != 'p') {
  135.                     break;
  136.                 }
  137.                 idBuffer.fromString(raw, ptr + 7);
  138.                 final RevCommit p = walk.lookupCommit(idBuffer);
  139.                 switch (nParents) {
  140.                 case 0:
  141.                     pList[nParents++] = p;
  142.                     break;
  143.                 case 1:
  144.                     pList = new RevCommit[] { pList[0], p };
  145.                     nParents = 2;
  146.                     break;
  147.                 default:
  148.                     if (pList.length <= nParents) {
  149.                         RevCommit[] old = pList;
  150.                         pList = new RevCommit[pList.length + 32];
  151.                         System.arraycopy(old, 0, pList, 0, nParents);
  152.                     }
  153.                     pList[nParents++] = p;
  154.                     break;
  155.                 }
  156.                 ptr += 48;
  157.             }
  158.             if (nParents != pList.length) {
  159.                 RevCommit[] old = pList;
  160.                 pList = new RevCommit[nParents];
  161.                 System.arraycopy(old, 0, pList, 0, nParents);
  162.             }
  163.             parents = pList;
  164.         }

  165.         // extract time from "committer "
  166.         ptr = RawParseUtils.committer(raw, ptr);
  167.         if (ptr > 0) {
  168.             ptr = RawParseUtils.nextLF(raw, ptr, '>');

  169.             // In 2038 commitTime will overflow unless it is changed to long.
  170.             commitTime = RawParseUtils.parseBase10(raw, ptr, null);
  171.         }

  172.         if (walk.isRetainBody()) {
  173.             buffer = raw;
  174.         }
  175.         flags |= PARSED;
  176.     }

  177.     /** {@inheritDoc} */
  178.     @Override
  179.     public final int getType() {
  180.         return Constants.OBJ_COMMIT;
  181.     }

  182.     static void carryFlags(RevCommit c, int carry) {
  183.         FIFORevQueue q = carryFlags1(c, carry, 0);
  184.         if (q != null)
  185.             slowCarryFlags(q, carry);
  186.     }

  187.     private static FIFORevQueue carryFlags1(RevCommit c, int carry, int depth) {
  188.         for(;;) {
  189.             RevCommit[] pList = c.parents;
  190.             if (pList == null || pList.length == 0)
  191.                 return null;
  192.             if (pList.length != 1) {
  193.                 if (depth == STACK_DEPTH)
  194.                     return defer(c);
  195.                 for (int i = 1; i < pList.length; i++) {
  196.                     RevCommit p = pList[i];
  197.                     if ((p.flags & carry) == carry)
  198.                         continue;
  199.                     p.flags |= carry;
  200.                     FIFORevQueue q = carryFlags1(p, carry, depth + 1);
  201.                     if (q != null)
  202.                         return defer(q, carry, pList, i + 1);
  203.                 }
  204.             }

  205.             c = pList[0];
  206.             if ((c.flags & carry) == carry)
  207.                 return null;
  208.             c.flags |= carry;
  209.         }
  210.     }

  211.     private static FIFORevQueue defer(RevCommit c) {
  212.         FIFORevQueue q = new FIFORevQueue();
  213.         q.add(c);
  214.         return q;
  215.     }

  216.     private static FIFORevQueue defer(FIFORevQueue q, int carry,
  217.             RevCommit[] pList, int i) {
  218.         // In normal case the caller will run pList[0] in a tail recursive
  219.         // fashion by updating the variable. However the caller is unwinding
  220.         // the stack and will skip that pList[0] execution step.
  221.         carryOneStep(q, carry, pList[0]);

  222.         // Remaining parents (if any) need to have flags checked and be
  223.         // enqueued if they have ancestors.
  224.         for (; i < pList.length; i++)
  225.             carryOneStep(q, carry, pList[i]);
  226.         return q;
  227.     }

  228.     private static void slowCarryFlags(FIFORevQueue q, int carry) {
  229.         // Commits in q have non-null parent arrays and have set all
  230.         // flags in carry. This loop finishes copying over the graph.
  231.         for (RevCommit c; (c = q.next()) != null;) {
  232.             for (RevCommit p : c.parents)
  233.                 carryOneStep(q, carry, p);
  234.         }
  235.     }

  236.     private static void carryOneStep(FIFORevQueue q, int carry, RevCommit c) {
  237.         if ((c.flags & carry) != carry) {
  238.             c.flags |= carry;
  239.             if (c.parents != null)
  240.                 q.add(c);
  241.         }
  242.     }

  243.     /**
  244.      * Carry a RevFlag set on this commit to its parents.
  245.      * <p>
  246.      * If this commit is parsed, has parents, and has the supplied flag set on
  247.      * it we automatically add it to the parents, grand-parents, and so on until
  248.      * an unparsed commit or a commit with no parents is discovered. This
  249.      * permits applications to force a flag through the history chain when
  250.      * necessary.
  251.      *
  252.      * @param flag
  253.      *            the single flag value to carry back onto parents.
  254.      */
  255.     public void carry(RevFlag flag) {
  256.         final int carry = flags & flag.mask;
  257.         if (carry != 0)
  258.             carryFlags(this, carry);
  259.     }

  260.     /**
  261.      * Time from the "committer " line of the buffer.
  262.      *
  263.      * @return commit time
  264.      */
  265.     public final int getCommitTime() {
  266.         return commitTime;
  267.     }

  268.     /**
  269.      * Get a reference to this commit's tree.
  270.      *
  271.      * @return tree of this commit.
  272.      */
  273.     public final RevTree getTree() {
  274.         return tree;
  275.     }

  276.     /**
  277.      * Get the number of parent commits listed in this commit.
  278.      *
  279.      * @return number of parents; always a positive value but can be 0.
  280.      */
  281.     public final int getParentCount() {
  282.         return parents.length;
  283.     }

  284.     /**
  285.      * Get the nth parent from this commit's parent list.
  286.      *
  287.      * @param nth
  288.      *            parent index to obtain. Must be in the range 0 through
  289.      *            {@link #getParentCount()}-1.
  290.      * @return the specified parent.
  291.      * @throws java.lang.ArrayIndexOutOfBoundsException
  292.      *             an invalid parent index was specified.
  293.      */
  294.     public final RevCommit getParent(int nth) {
  295.         return parents[nth];
  296.     }

  297.     /**
  298.      * Obtain an array of all parents (<b>NOTE - THIS IS NOT A COPY</b>).
  299.      * <p>
  300.      * This method is exposed only to provide very fast, efficient access to
  301.      * this commit's parent list. Applications relying on this list should be
  302.      * very careful to ensure they do not modify its contents during their use
  303.      * of it.
  304.      *
  305.      * @return the array of parents.
  306.      */
  307.     public final RevCommit[] getParents() {
  308.         return parents;
  309.     }

  310.     /**
  311.      * Obtain the raw unparsed commit body (<b>NOTE - THIS IS NOT A COPY</b>).
  312.      * <p>
  313.      * This method is exposed only to provide very fast, efficient access to
  314.      * this commit's message buffer within a RevFilter. Applications relying on
  315.      * this buffer should be very careful to ensure they do not modify its
  316.      * contents during their use of it.
  317.      *
  318.      * @return the raw unparsed commit body. This is <b>NOT A COPY</b>.
  319.      *         Altering the contents of this buffer may alter the walker's
  320.      *         knowledge of this commit, and the results it produces.
  321.      */
  322.     public final byte[] getRawBuffer() {
  323.         return buffer;
  324.     }

  325.     /**
  326.      * Parse the gpg signature from the raw buffer.
  327.      * <p>
  328.      * This method parses and returns the raw content of the gpgsig lines. This
  329.      * method is fairly expensive and produces a new byte[] instance on each
  330.      * invocation. Callers should invoke this method only if they are certain
  331.      * they will need, and should cache the return value for as long as
  332.      * necessary to use all information from it.
  333.      * <p>
  334.      * RevFilter implementations should try to use
  335.      * {@link org.eclipse.jgit.util.RawParseUtils} to scan the
  336.      * {@link #getRawBuffer()} instead, as this will allow faster evaluation of
  337.      * commits.
  338.      *
  339.      * @return contents of the gpg signature; null if the commit was not signed.
  340.      * @since 5.1
  341.      */
  342.     public final byte[] getRawGpgSignature() {
  343.         final byte[] raw = buffer;
  344.         final byte[] header = {'g', 'p', 'g', 's', 'i', 'g'};
  345.         final int start = RawParseUtils.headerStart(header, raw, 0);
  346.         if (start < 0) {
  347.             return null;
  348.         }
  349.         final int end = RawParseUtils.headerEnd(raw, start);
  350.         return Arrays.copyOfRange(raw, start, end);
  351.     }

  352.     /**
  353.      * Parse the author identity from the raw buffer.
  354.      * <p>
  355.      * This method parses and returns the content of the author line, after
  356.      * taking the commit's character set into account and decoding the author
  357.      * name and email address. This method is fairly expensive and produces a
  358.      * new PersonIdent instance on each invocation. Callers should invoke this
  359.      * method only if they are certain they will be outputting the result, and
  360.      * should cache the return value for as long as necessary to use all
  361.      * information from it.
  362.      * <p>
  363.      * RevFilter implementations should try to use
  364.      * {@link org.eclipse.jgit.util.RawParseUtils} to scan the
  365.      * {@link #getRawBuffer()} instead, as this will allow faster evaluation of
  366.      * commits.
  367.      *
  368.      * @return identity of the author (name, email) and the time the commit was
  369.      *         made by the author; null if no author line was found.
  370.      */
  371.     public final PersonIdent getAuthorIdent() {
  372.         final byte[] raw = buffer;
  373.         final int nameB = RawParseUtils.author(raw, 0);
  374.         if (nameB < 0)
  375.             return null;
  376.         return RawParseUtils.parsePersonIdent(raw, nameB);
  377.     }

  378.     /**
  379.      * Parse the committer identity from the raw buffer.
  380.      * <p>
  381.      * This method parses and returns the content of the committer line, after
  382.      * taking the commit's character set into account and decoding the committer
  383.      * name and email address. This method is fairly expensive and produces a
  384.      * new PersonIdent instance on each invocation. Callers should invoke this
  385.      * method only if they are certain they will be outputting the result, and
  386.      * should cache the return value for as long as necessary to use all
  387.      * information from it.
  388.      * <p>
  389.      * RevFilter implementations should try to use
  390.      * {@link org.eclipse.jgit.util.RawParseUtils} to scan the
  391.      * {@link #getRawBuffer()} instead, as this will allow faster evaluation of
  392.      * commits.
  393.      *
  394.      * @return identity of the committer (name, email) and the time the commit
  395.      *         was made by the committer; null if no committer line was found.
  396.      */
  397.     public final PersonIdent getCommitterIdent() {
  398.         final byte[] raw = buffer;
  399.         final int nameB = RawParseUtils.committer(raw, 0);
  400.         if (nameB < 0)
  401.             return null;
  402.         return RawParseUtils.parsePersonIdent(raw, nameB);
  403.     }

  404.     /**
  405.      * Parse the complete commit message and decode it to a string.
  406.      * <p>
  407.      * This method parses and returns the message portion of the commit buffer,
  408.      * after taking the commit's character set into account and decoding the
  409.      * buffer using that character set. This method is a fairly expensive
  410.      * operation and produces a new string on each invocation.
  411.      *
  412.      * @return decoded commit message as a string. Never null.
  413.      */
  414.     public final String getFullMessage() {
  415.         byte[] raw = buffer;
  416.         int msgB = RawParseUtils.commitMessage(raw, 0);
  417.         if (msgB < 0) {
  418.             return ""; //$NON-NLS-1$
  419.         }
  420.         return RawParseUtils.decode(guessEncoding(), raw, msgB, raw.length);
  421.     }

  422.     /**
  423.      * Parse the commit message and return the first "line" of it.
  424.      * <p>
  425.      * The first line is everything up to the first pair of LFs. This is the
  426.      * "oneline" format, suitable for output in a single line display.
  427.      * <p>
  428.      * This method parses and returns the message portion of the commit buffer,
  429.      * after taking the commit's character set into account and decoding the
  430.      * buffer using that character set. This method is a fairly expensive
  431.      * operation and produces a new string on each invocation.
  432.      *
  433.      * @return decoded commit message as a string. Never null. The returned
  434.      *         string does not contain any LFs, even if the first paragraph
  435.      *         spanned multiple lines. Embedded LFs are converted to spaces.
  436.      */
  437.     public final String getShortMessage() {
  438.         byte[] raw = buffer;
  439.         int msgB = RawParseUtils.commitMessage(raw, 0);
  440.         if (msgB < 0) {
  441.             return ""; //$NON-NLS-1$
  442.         }

  443.         int msgE = RawParseUtils.endOfParagraph(raw, msgB);
  444.         String str = RawParseUtils.decode(guessEncoding(), raw, msgB, msgE);
  445.         if (hasLF(raw, msgB, msgE)) {
  446.             str = StringUtils.replaceLineBreaksWithSpace(str);
  447.         }
  448.         return str;
  449.     }

  450.     static boolean hasLF(byte[] r, int b, int e) {
  451.         while (b < e)
  452.             if (r[b++] == '\n')
  453.                 return true;
  454.         return false;
  455.     }

  456.     /**
  457.      * Determine the encoding of the commit message buffer.
  458.      * <p>
  459.      * Locates the "encoding" header (if present) and returns its value. Due to
  460.      * corruption in the wild this may be an invalid encoding name that is not
  461.      * recognized by any character encoding library.
  462.      * <p>
  463.      * If no encoding header is present, null.
  464.      *
  465.      * @return the preferred encoding of {@link #getRawBuffer()}; or null.
  466.      * @since 4.2
  467.      */
  468.     @Nullable
  469.     public final String getEncodingName() {
  470.         return RawParseUtils.parseEncodingName(buffer);
  471.     }

  472.     /**
  473.      * Determine the encoding of the commit message buffer.
  474.      * <p>
  475.      * Locates the "encoding" header (if present) and then returns the proper
  476.      * character set to apply to this buffer to evaluate its contents as
  477.      * character data.
  478.      * <p>
  479.      * If no encoding header is present {@code UTF-8} is assumed.
  480.      *
  481.      * @return the preferred encoding of {@link #getRawBuffer()}.
  482.      * @throws IllegalCharsetNameException
  483.      *             if the character set requested by the encoding header is
  484.      *             malformed and unsupportable.
  485.      * @throws UnsupportedCharsetException
  486.      *             if the JRE does not support the character set requested by
  487.      *             the encoding header.
  488.      */
  489.     public final Charset getEncoding() {
  490.         return RawParseUtils.parseEncoding(buffer);
  491.     }

  492.     private Charset guessEncoding() {
  493.         try {
  494.             return getEncoding();
  495.         } catch (IllegalCharsetNameException | UnsupportedCharsetException e) {
  496.             return UTF_8;
  497.         }
  498.     }

  499.     /**
  500.      * Parse the footer lines (e.g. "Signed-off-by") for machine processing.
  501.      * <p>
  502.      * This method splits all of the footer lines out of the last paragraph of
  503.      * the commit message, providing each line as a key-value pair, ordered by
  504.      * the order of the line's appearance in the commit message itself.
  505.      * <p>
  506.      * A footer line's key must match the pattern {@code ^[A-Za-z0-9-]+:}, while
  507.      * the value is free-form, but must not contain an LF. Very common keys seen
  508.      * in the wild are:
  509.      * <ul>
  510.      * <li>{@code Signed-off-by} (agrees to Developer Certificate of Origin)
  511.      * <li>{@code Acked-by} (thinks change looks sane in context)
  512.      * <li>{@code Reported-by} (originally found the issue this change fixes)
  513.      * <li>{@code Tested-by} (validated change fixes the issue for them)
  514.      * <li>{@code CC}, {@code Cc} (copy on all email related to this change)
  515.      * <li>{@code Bug} (link to project's bug tracking system)
  516.      * </ul>
  517.      *
  518.      * @return ordered list of footer lines; empty list if no footers found.
  519.      */
  520.     public final List<FooterLine> getFooterLines() {
  521.         final byte[] raw = buffer;
  522.         int ptr = raw.length - 1;
  523.         while (raw[ptr] == '\n') // trim any trailing LFs, not interesting
  524.             ptr--;

  525.         final int msgB = RawParseUtils.commitMessage(raw, 0);
  526.         final ArrayList<FooterLine> r = new ArrayList<>(4);
  527.         final Charset enc = guessEncoding();
  528.         for (;;) {
  529.             ptr = RawParseUtils.prevLF(raw, ptr);
  530.             if (ptr <= msgB)
  531.                 break; // Don't parse commit headers as footer lines.

  532.             final int keyStart = ptr + 2;
  533.             if (raw[keyStart] == '\n')
  534.                 break; // Stop at first paragraph break, no footers above it.

  535.             final int keyEnd = RawParseUtils.endOfFooterLineKey(raw, keyStart);
  536.             if (keyEnd < 0)
  537.                 continue; // Not a well formed footer line, skip it.

  538.             // Skip over the ': *' at the end of the key before the value.
  539.             //
  540.             int valStart = keyEnd + 1;
  541.             while (valStart < raw.length && raw[valStart] == ' ')
  542.                 valStart++;

  543.             // Value ends at the LF, and does not include it.
  544.             //
  545.             int valEnd = RawParseUtils.nextLF(raw, valStart);
  546.             if (raw[valEnd - 1] == '\n')
  547.                 valEnd--;

  548.             r.add(new FooterLine(raw, enc, keyStart, keyEnd, valStart, valEnd));
  549.         }
  550.         Collections.reverse(r);
  551.         return r;
  552.     }

  553.     /**
  554.      * Get the values of all footer lines with the given key.
  555.      *
  556.      * @param keyName
  557.      *            footer key to find values of, case insensitive.
  558.      * @return values of footers with key of {@code keyName}, ordered by their
  559.      *         order of appearance. Duplicates may be returned if the same
  560.      *         footer appeared more than once. Empty list if no footers appear
  561.      *         with the specified key, or there are no footers at all.
  562.      * @see #getFooterLines()
  563.      */
  564.     public final List<String> getFooterLines(String keyName) {
  565.         return getFooterLines(new FooterKey(keyName));
  566.     }

  567.     /**
  568.      * Get the values of all footer lines with the given key.
  569.      *
  570.      * @param keyName
  571.      *            footer key to find values of, case insensitive.
  572.      * @return values of footers with key of {@code keyName}, ordered by their
  573.      *         order of appearance. Duplicates may be returned if the same
  574.      *         footer appeared more than once. Empty list if no footers appear
  575.      *         with the specified key, or there are no footers at all.
  576.      * @see #getFooterLines()
  577.      */
  578.     public final List<String> getFooterLines(FooterKey keyName) {
  579.         final List<FooterLine> src = getFooterLines();
  580.         if (src.isEmpty())
  581.             return Collections.emptyList();
  582.         final ArrayList<String> r = new ArrayList<>(src.size());
  583.         for (FooterLine f : src) {
  584.             if (f.matches(keyName))
  585.                 r.add(f.getValue());
  586.         }
  587.         return r;
  588.     }

  589.     /**
  590.      * Reset this commit to allow another RevWalk with the same instances.
  591.      * <p>
  592.      * Subclasses <b>must</b> call <code>super.reset()</code> to ensure the
  593.      * basic information can be correctly cleared out.
  594.      */
  595.     public void reset() {
  596.         inDegree = 0;
  597.     }

  598.     /**
  599.      * Discard the message buffer to reduce memory usage.
  600.      * <p>
  601.      * After discarding the memory usage of the {@code RevCommit} is reduced to
  602.      * only the {@link #getTree()} and {@link #getParents()} pointers and the
  603.      * time in {@link #getCommitTime()}. Accessing other properties such as
  604.      * {@link #getAuthorIdent()}, {@link #getCommitterIdent()} or either message
  605.      * function requires reloading the buffer by invoking
  606.      * {@link org.eclipse.jgit.revwalk.RevWalk#parseBody(RevObject)}.
  607.      *
  608.      * @since 4.0
  609.      */
  610.     public final void disposeBody() {
  611.         buffer = null;
  612.     }

  613.     /** {@inheritDoc} */
  614.     @Override
  615.     public String toString() {
  616.         final StringBuilder s = new StringBuilder();
  617.         s.append(Constants.typeString(getType()));
  618.         s.append(' ');
  619.         s.append(name());
  620.         s.append(' ');
  621.         s.append(commitTime);
  622.         s.append(' ');
  623.         appendCoreFlags(s);
  624.         return s.toString();
  625.     }
  626. }