Allow resets of RevWalk while retaining some application flags
[egit/zawir.git] / org.spearce.jgit / src / org / spearce / jgit / revwalk / RevCommit.java
blob3662fb6c62f2296110dafab44203d3b11f17a8e5
1 /*
2 * Copyright (C) 2008 Shawn Pearce <spearce@spearce.org>
4 * This library is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU General Public
6 * License, version 2, as published by the Free Software Foundation.
8 * This library is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
11 * General Public License for more details.
13 * You should have received a copy of the GNU General Public
14 * License along with this library; if not, write to the Free Software
15 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301
17 package org.spearce.jgit.revwalk;
19 import java.io.IOException;
20 import java.nio.charset.Charset;
22 import org.spearce.jgit.errors.IncorrectObjectTypeException;
23 import org.spearce.jgit.errors.MissingObjectException;
24 import org.spearce.jgit.lib.AnyObjectId;
25 import org.spearce.jgit.lib.Commit;
26 import org.spearce.jgit.lib.Constants;
27 import org.spearce.jgit.lib.MutableObjectId;
28 import org.spearce.jgit.lib.ObjectLoader;
29 import org.spearce.jgit.lib.PersonIdent;
30 import org.spearce.jgit.util.RawParseUtils;
32 /** A commit reference to a commit in the DAG. */
33 public class RevCommit extends RevObject {
34 static final RevCommit[] NO_PARENTS = {};
36 private static final String TYPE_COMMIT = Constants.TYPE_COMMIT;
38 private RevTree tree;
40 RevCommit[] parents;
42 int commitTime;
44 int inDegree;
46 private byte[] buffer;
48 /**
49 * Create a new commit reference.
51 * @param id
52 * object name for the commit.
54 protected RevCommit(final AnyObjectId id) {
55 super(id);
58 @Override
59 void parse(final RevWalk walk) throws MissingObjectException,
60 IncorrectObjectTypeException, IOException {
61 final ObjectLoader ldr = walk.db.openObject(walk.curs, this);
62 if (ldr == null)
63 throw new MissingObjectException(this, TYPE_COMMIT);
64 final byte[] data = ldr.getCachedBytes();
65 if (Constants.OBJ_COMMIT != ldr.getType())
66 throw new IncorrectObjectTypeException(this, TYPE_COMMIT);
67 parseCanonical(walk, data);
70 void parseCanonical(final RevWalk walk, final byte[] raw) {
71 final MutableObjectId idBuffer = walk.idBuffer;
72 idBuffer.fromString(raw, 5);
73 tree = walk.lookupTree(idBuffer);
75 int ptr = 46;
76 if (parents == null) {
77 RevCommit[] pList = new RevCommit[1];
78 int nParents = 0;
79 for (;;) {
80 if (raw[ptr] != 'p')
81 break;
82 idBuffer.fromString(raw, ptr + 7);
83 final RevCommit p = walk.lookupCommit(idBuffer);
84 if (nParents == 0)
85 pList[nParents++] = p;
86 else if (nParents == 1) {
87 pList = new RevCommit[] { pList[0], p };
88 nParents = 2;
89 } else {
90 if (pList.length <= nParents) {
91 RevCommit[] old = pList;
92 pList = new RevCommit[pList.length + 32];
93 System.arraycopy(old, 0, pList, 0, nParents);
95 pList[nParents++] = p;
97 ptr += 48;
99 if (nParents != pList.length) {
100 RevCommit[] old = pList;
101 pList = new RevCommit[nParents];
102 System.arraycopy(old, 0, pList, 0, nParents);
104 parents = pList;
107 // extract time from "committer "
108 ptr = RawParseUtils.committer(raw, ptr);
109 if (ptr > 0) {
110 ptr = RawParseUtils.nextLF(raw, ptr, '>');
111 commitTime = RawParseUtils.parseBase10(raw, ptr, null);
114 buffer = raw;
115 flags |= PARSED;
118 static void carryFlags(RevCommit c, final int carry) {
119 for (;;) {
120 final RevCommit[] pList = c.parents;
121 if (pList == null)
122 return;
123 final int n = pList.length;
124 if (n == 0)
125 return;
127 for (int i = 1; i < n; i++) {
128 final RevCommit p = pList[i];
129 p.flags |= carry;
130 carryFlags(p, carry);
133 c = pList[0];
134 c.flags |= carry;
138 void carryFlags(final int carryMask) {
139 final int carry = flags & carryMask;
140 if (carry == 0)
141 return;
142 carryFlags(this, carry);
146 * Carry a RevFlag set on this commit to its parents.
147 * <p>
148 * If this commit is parsed, has parents, and has the supplied flag set on
149 * it we automatically add it to the parents, grand-parents, and so on until
150 * an unparsed commit or a commit with no parents is discovered. This
151 * permits applications to force a flag through the history chain when
152 * necessary.
154 * @param flag
155 * the single flag value to carry back onto parents.
157 public void carry(final RevFlag flag) {
158 carryFlags(flags & flag.mask);
162 * Time from the "committer " line of the buffer.
164 * @return time, expressed as seconds since the epoch.
166 public final int getCommitTime() {
167 return commitTime;
171 * Parse this commit buffer for display.
173 * @param walk
174 * revision walker owning this reference.
175 * @return parsed commit.
177 public final Commit asCommit(final RevWalk walk) {
178 return new Commit(walk.db, this, buffer);
182 * Get a reference to this commit's tree.
184 * @return tree of this commit.
186 public final RevTree getTree() {
187 return tree;
191 * Get the number of parent commits listed in this commit.
193 * @return number of parents; always a positive value but can be 0.
195 public final int getParentCount() {
196 return parents.length;
200 * Get the nth parent from this commit's parent list.
202 * @param nth
203 * parent index to obtain. Must be in the range 0 through
204 * {@link #getParentCount()}-1.
205 * @return the specified parent.
206 * @throws ArrayIndexOutOfBoundsException
207 * an invalid parent index was specified.
209 public final RevCommit getParent(final int nth) {
210 return parents[nth];
214 * Obtain an array of all parents (<b>NOTE - THIS IS NOT A COPY</b>).
215 * <p>
216 * This method is exposed only to provide very fast, efficient access to
217 * this commit's parent list. Applications relying on this list should be
218 * very careful to ensure they do not modify its contents during their use
219 * of it.
221 * @return the array of parents.
223 public final RevCommit[] getParents() {
224 return parents;
228 * Obtain the raw unparsed commit body (<b>NOTE - THIS IS NOT A COPY</b>).
229 * <p>
230 * This method is exposed only to provide very fast, efficient access to
231 * this commit's message buffer within a RevFilter. Applications relying on
232 * this buffer should be very careful to ensure they do not modify its
233 * contents during their use of it.
235 * @return the raw unparsed commit body. This is <b>NOT A COPY</b>.
236 * Altering the contents of this buffer may alter the walker's
237 * knowledge of this commit, and the results it produces.
239 public final byte[] getRawBuffer() {
240 return buffer;
244 * Parse the author identity from the raw buffer.
245 * <p>
246 * This method parses and returns the content of the author line, after
247 * taking the commit's character set into account and decoding the author
248 * name and email address. This method is fairly expensive and produces a
249 * new PersonIdent instance on each invocation. Callers should invoke this
250 * method only if they are certain they will be outputting the result, and
251 * should cache the return value for as long as necessary to use all
252 * information from it.
253 * <p>
254 * RevFilter implementations should try to use {@link RawParseUtils} to scan
255 * the {@link #getRawBuffer()} instead, as this will allow faster evaluation
256 * of commits.
258 * @return identity of the author (name, email) and the time the commit was
259 * made by the author; null if no author line was found.
261 public final PersonIdent getAuthorIdent() {
262 final byte[] raw = buffer;
263 final int nameB = RawParseUtils.author(raw, 0);
264 if (nameB < 0)
265 return null;
266 return RawParseUtils.parsePersonIdent(raw, nameB);
270 * Parse the committer identity from the raw buffer.
271 * <p>
272 * This method parses and returns the content of the committer line, after
273 * taking the commit's character set into account and decoding the committer
274 * name and email address. This method is fairly expensive and produces a
275 * new PersonIdent instance on each invocation. Callers should invoke this
276 * method only if they are certain they will be outputting the result, and
277 * should cache the return value for as long as necessary to use all
278 * information from it.
279 * <p>
280 * RevFilter implementations should try to use {@link RawParseUtils} to scan
281 * the {@link #getRawBuffer()} instead, as this will allow faster evaluation
282 * of commits.
284 * @return identity of the committer (name, email) and the time the commit
285 * was made by the comitter; null if no committer line was found.
287 public final PersonIdent getCommitterIdent() {
288 final byte[] raw = buffer;
289 final int nameB = RawParseUtils.committer(raw, 0);
290 if (nameB < 0)
291 return null;
292 return RawParseUtils.parsePersonIdent(raw, nameB);
296 * Parse the complete commit message and decode it to a string.
297 * <p>
298 * This method parses and returns the message portion of the commit buffer,
299 * after taking the commit's character set into account and decoding the
300 * buffer using that character set. This method is a fairly expensive
301 * operation and produces a new string on each invocation.
303 * @return decoded commit message as a string. Never null.
305 public final String getFullMessage() {
306 final byte[] raw = buffer;
307 final int msgB = RawParseUtils.commitMessage(raw, 0);
308 if (msgB < 0)
309 return "";
310 final Charset enc = RawParseUtils.parseEncoding(raw);
311 return RawParseUtils.decode(enc, raw, msgB, raw.length);
315 * Parse the commit message and return the first "line" of it.
316 * <p>
317 * The first line is everything up to the first pair of LFs. This is the
318 * "oneline" format, suitable for output in a single line display.
319 * <p>
320 * This method parses and returns the message portion of the commit buffer,
321 * after taking the commit's character set into account and decoding the
322 * buffer using that character set. This method is a fairly expensive
323 * operation and produces a new string on each invocation.
325 * @return decoded commit message as a string. Never null. The returned
326 * string does not contain any LFs, even if the first paragraph
327 * spanned multiple lines. Embedded LFs are converted to spaces.
329 public final String getShortMessage() {
330 final byte[] raw = buffer;
331 final int msgB = RawParseUtils.commitMessage(raw, 0);
332 if (msgB < 0)
333 return "";
335 final Charset enc = RawParseUtils.parseEncoding(raw);
336 final int msgE = RawParseUtils.endOfParagraph(raw, msgB);
337 String str = RawParseUtils.decode(enc, raw, msgB, msgE);
338 if (hasLF(raw, msgB, msgE))
339 str = str.replace('\n', ' ');
340 return str;
343 private static boolean hasLF(final byte[] r, int b, final int e) {
344 while (b < e)
345 if (r[b++] == '\n')
346 return true;
347 return false;
351 * Reset this commit to allow another RevWalk with the same instances.
352 * <p>
353 * Subclasses <b>must</b> call <code>super.reset()</code> to ensure the
354 * basic information can be correctly cleared out.
356 public void reset() {
357 inDegree = 0;
360 public void dispose() {
361 flags &= ~PARSED;
362 buffer = null;