/** * Parse the footer lines (e.g. "Signed-off-by") for machine processing. * * <p>This method splits all of the footer lines out of the last paragraph of the commit message, * providing each line as a key-value pair, ordered by the order of the line's appearance in the * commit message itself. * * <p>A footer line's key must match the pattern {@code ^[A-Za-z0-9-]+:}, while the value is * free-form, but must not contain an LF. Very common keys seen in the wild are: * * <ul> * <li>{@code Signed-off-by} (agrees to Developer Certificate of Origin) * <li>{@code Acked-by} (thinks change looks sane in context) * <li>{@code Reported-by} (originally found the issue this change fixes) * <li>{@code Tested-by} (validated change fixes the issue for them) * <li>{@code CC}, {@code Cc} (copy on all email related to this change) * <li>{@code Bug} (link to project's bug tracking system) * </ul> * * @return ordered list of footer lines; empty list if no footers found. */ public final List<FooterLine> getFooterLines() { final byte[] raw = buffer; int ptr = raw.length - 1; while (raw[ptr] == '\n') // trim any trailing LFs, not interesting ptr--; final int msgB = RawParseUtils.commitMessage(raw, 0); final ArrayList<FooterLine> r = new ArrayList<FooterLine>(4); final Charset enc = getEncoding(); for (; ; ) { ptr = RawParseUtils.prevLF(raw, ptr); if (ptr <= msgB) break; // Don't parse commit headers as footer lines. final int keyStart = ptr + 2; if (raw[keyStart] == '\n') break; // Stop at first paragraph break, no footers above it. final int keyEnd = RawParseUtils.endOfFooterLineKey(raw, keyStart); if (keyEnd < 0) continue; // Not a well formed footer line, skip it. // Skip over the ': *' at the end of the key before the value. // int valStart = keyEnd + 1; while (valStart < raw.length && raw[valStart] == ' ') valStart++; // Value ends at the LF, and does not include it. // int valEnd = RawParseUtils.nextLF(raw, valStart); if (raw[valEnd - 1] == '\n') valEnd--; r.add(new FooterLine(raw, enc, keyStart, keyEnd, valStart, valEnd)); } Collections.reverse(r); return r; }
void parseCanonical(final RevWalk walk, final byte[] raw) throws IOException { if (!walk.shallowCommitsInitialized) walk.initializeShallowCommits(); final MutableObjectId idBuffer = walk.idBuffer; idBuffer.fromString(raw, 5); tree = walk.lookupTree(idBuffer); int ptr = 46; if (parents == null) { RevCommit[] pList = new RevCommit[1]; int nParents = 0; for (; ; ) { if (raw[ptr] != 'p') break; idBuffer.fromString(raw, ptr + 7); final RevCommit p = walk.lookupCommit(idBuffer); if (nParents == 0) pList[nParents++] = p; else if (nParents == 1) { pList = new RevCommit[] {pList[0], p}; nParents = 2; } else { if (pList.length <= nParents) { RevCommit[] old = pList; pList = new RevCommit[pList.length + 32]; System.arraycopy(old, 0, pList, 0, nParents); } pList[nParents++] = p; } ptr += 48; } if (nParents != pList.length) { RevCommit[] old = pList; pList = new RevCommit[nParents]; System.arraycopy(old, 0, pList, 0, nParents); } parents = pList; } // extract time from "committer " ptr = RawParseUtils.committer(raw, ptr); if (ptr > 0) { ptr = RawParseUtils.nextLF(raw, ptr, '>'); // In 2038 commitTime will overflow unless it is changed to long. commitTime = RawParseUtils.parseBase10(raw, ptr, null); } if (walk.isRetainBody()) buffer = raw; flags |= PARSED; }
void parseCanonical(final RevWalk walk, final byte[] rawTag) throws CorruptObjectException { final MutableInteger pos = new MutableInteger(); final int oType; pos.value = 53; // "object $sha1\ntype " oType = Constants.decodeTypeString(this, rawTag, (byte) '\n', pos); walk.idBuffer.fromString(rawTag, 7); object = walk.lookupAny(walk.idBuffer, oType); int p = pos.value += 4; // "tag " final int nameEnd = RawParseUtils.nextLF(rawTag, p) - 1; tagName = RawParseUtils.decode(Constants.CHARSET, rawTag, p, nameEnd); if (walk.isRetainBody()) buffer = rawTag; flags |= PARSED; }