Remove System.out.println from RevWalkFilterTest
[egit/chris.git] / org.spearce.jgit / src / org / spearce / jgit / revwalk / RevCommit.java
blobf211dfd601c2db6b47def782574f7798a2830ab4
1 /*
2 * Copyright (C) 2008, Shawn O. Pearce <spearce@spearce.org>
4 * All rights reserved.
6 * Redistribution and use in source and binary forms, with or
7 * without modification, are permitted provided that the following
8 * conditions are met:
10 * - Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
13 * - Redistributions in binary form must reproduce the above
14 * copyright notice, this list of conditions and the following
15 * disclaimer in the documentation and/or other materials provided
16 * with the distribution.
18 * - Neither the name of the Git Development Community nor the
19 * names of its contributors may be used to endorse or promote
20 * products derived from this software without specific prior
21 * written permission.
23 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
24 * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
25 * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
26 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
27 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
28 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
29 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
30 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
31 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
32 * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
33 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
34 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
35 * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
38 package org.spearce.jgit.revwalk;
40 import java.io.IOException;
41 import java.nio.charset.Charset;
43 import org.spearce.jgit.errors.IncorrectObjectTypeException;
44 import org.spearce.jgit.errors.MissingObjectException;
45 import org.spearce.jgit.lib.AnyObjectId;
46 import org.spearce.jgit.lib.Commit;
47 import org.spearce.jgit.lib.Constants;
48 import org.spearce.jgit.lib.MutableObjectId;
49 import org.spearce.jgit.lib.PersonIdent;
50 import org.spearce.jgit.util.RawParseUtils;
52 /** A commit reference to a commit in the DAG. */
53 public class RevCommit extends RevObject {
54 static final RevCommit[] NO_PARENTS = {};
56 private RevTree tree;
58 RevCommit[] parents;
60 int commitTime; // An int here for performance, overflows in 2038
62 int inDegree;
64 private byte[] buffer;
66 /**
67 * Create a new commit reference.
69 * @param id
70 * object name for the commit.
72 protected RevCommit(final AnyObjectId id) {
73 super(id);
76 @Override
77 void parseHeaders(final RevWalk walk) throws MissingObjectException,
78 IncorrectObjectTypeException, IOException {
79 parseCanonical(walk, loadCanonical(walk));
82 @Override
83 void parseBody(final RevWalk walk) throws MissingObjectException,
84 IncorrectObjectTypeException, IOException {
85 if (buffer == null) {
86 buffer = loadCanonical(walk);
87 if ((flags & PARSED) == 0)
88 parseCanonical(walk, buffer);
92 void parseCanonical(final RevWalk walk, final byte[] raw) {
93 final MutableObjectId idBuffer = walk.idBuffer;
94 idBuffer.fromString(raw, 5);
95 tree = walk.lookupTree(idBuffer);
97 int ptr = 46;
98 if (parents == null) {
99 RevCommit[] pList = new RevCommit[1];
100 int nParents = 0;
101 for (;;) {
102 if (raw[ptr] != 'p')
103 break;
104 idBuffer.fromString(raw, ptr + 7);
105 final RevCommit p = walk.lookupCommit(idBuffer);
106 if (nParents == 0)
107 pList[nParents++] = p;
108 else if (nParents == 1) {
109 pList = new RevCommit[] { pList[0], p };
110 nParents = 2;
111 } else {
112 if (pList.length <= nParents) {
113 RevCommit[] old = pList;
114 pList = new RevCommit[pList.length + 32];
115 System.arraycopy(old, 0, pList, 0, nParents);
117 pList[nParents++] = p;
119 ptr += 48;
121 if (nParents != pList.length) {
122 RevCommit[] old = pList;
123 pList = new RevCommit[nParents];
124 System.arraycopy(old, 0, pList, 0, nParents);
126 parents = pList;
129 // extract time from "committer "
130 ptr = RawParseUtils.committer(raw, ptr);
131 if (ptr > 0) {
132 ptr = RawParseUtils.nextLF(raw, ptr, '>');
134 // In 2038 commitTime will overflow unless it is changed to long.
135 commitTime = RawParseUtils.parseBase10(raw, ptr, null);
138 if (walk.isRetainBody())
139 buffer = raw;
140 flags |= PARSED;
143 @Override
144 public final int getType() {
145 return Constants.OBJ_COMMIT;
148 static void carryFlags(RevCommit c, final int carry) {
149 for (;;) {
150 final RevCommit[] pList = c.parents;
151 if (pList == null)
152 return;
153 final int n = pList.length;
154 if (n == 0)
155 return;
157 for (int i = 1; i < n; i++) {
158 final RevCommit p = pList[i];
159 if ((p.flags & carry) == carry)
160 continue;
161 p.flags |= carry;
162 carryFlags(p, carry);
165 c = pList[0];
166 if ((c.flags & carry) == carry)
167 return;
168 c.flags |= carry;
173 * Carry a RevFlag set on this commit to its parents.
174 * <p>
175 * If this commit is parsed, has parents, and has the supplied flag set on
176 * it we automatically add it to the parents, grand-parents, and so on until
177 * an unparsed commit or a commit with no parents is discovered. This
178 * permits applications to force a flag through the history chain when
179 * necessary.
181 * @param flag
182 * the single flag value to carry back onto parents.
184 public void carry(final RevFlag flag) {
185 final int carry = flags & flag.mask;
186 if (carry != 0)
187 carryFlags(this, carry);
191 * Time from the "committer " line of the buffer.
193 * @return time, expressed as seconds since the epoch.
195 public final int getCommitTime() {
196 return commitTime;
200 * Parse this commit buffer for display.
202 * @param walk
203 * revision walker owning this reference.
204 * @return parsed commit.
206 public final Commit asCommit(final RevWalk walk) {
207 return new Commit(walk.db, this, buffer);
211 * Get a reference to this commit's tree.
213 * @return tree of this commit.
215 public final RevTree getTree() {
216 return tree;
220 * Get the number of parent commits listed in this commit.
222 * @return number of parents; always a positive value but can be 0.
224 public final int getParentCount() {
225 return parents.length;
229 * Get the nth parent from this commit's parent list.
231 * @param nth
232 * parent index to obtain. Must be in the range 0 through
233 * {@link #getParentCount()}-1.
234 * @return the specified parent.
235 * @throws ArrayIndexOutOfBoundsException
236 * an invalid parent index was specified.
238 public final RevCommit getParent(final int nth) {
239 return parents[nth];
243 * Obtain an array of all parents (<b>NOTE - THIS IS NOT A COPY</b>).
244 * <p>
245 * This method is exposed only to provide very fast, efficient access to
246 * this commit's parent list. Applications relying on this list should be
247 * very careful to ensure they do not modify its contents during their use
248 * of it.
250 * @return the array of parents.
252 public final RevCommit[] getParents() {
253 return parents;
257 * Obtain the raw unparsed commit body (<b>NOTE - THIS IS NOT A COPY</b>).
258 * <p>
259 * This method is exposed only to provide very fast, efficient access to
260 * this commit's message buffer within a RevFilter. Applications relying on
261 * this buffer should be very careful to ensure they do not modify its
262 * contents during their use of it.
264 * @return the raw unparsed commit body. This is <b>NOT A COPY</b>.
265 * Altering the contents of this buffer may alter the walker's
266 * knowledge of this commit, and the results it produces.
268 public final byte[] getRawBuffer() {
269 return buffer;
273 * Parse the author identity from the raw buffer.
274 * <p>
275 * This method parses and returns the content of the author line, after
276 * taking the commit's character set into account and decoding the author
277 * name and email address. This method is fairly expensive and produces a
278 * new PersonIdent instance on each invocation. Callers should invoke this
279 * method only if they are certain they will be outputting the result, and
280 * should cache the return value for as long as necessary to use all
281 * information from it.
282 * <p>
283 * RevFilter implementations should try to use {@link RawParseUtils} to scan
284 * the {@link #getRawBuffer()} instead, as this will allow faster evaluation
285 * of commits.
287 * @return identity of the author (name, email) and the time the commit was
288 * made by the author; null if no author line was found.
290 public final PersonIdent getAuthorIdent() {
291 final byte[] raw = buffer;
292 final int nameB = RawParseUtils.author(raw, 0);
293 if (nameB < 0)
294 return null;
295 return RawParseUtils.parsePersonIdent(raw, nameB);
299 * Parse the committer identity from the raw buffer.
300 * <p>
301 * This method parses and returns the content of the committer line, after
302 * taking the commit's character set into account and decoding the committer
303 * name and email address. This method is fairly expensive and produces a
304 * new PersonIdent instance on each invocation. Callers should invoke this
305 * method only if they are certain they will be outputting the result, and
306 * should cache the return value for as long as necessary to use all
307 * information from it.
308 * <p>
309 * RevFilter implementations should try to use {@link RawParseUtils} to scan
310 * the {@link #getRawBuffer()} instead, as this will allow faster evaluation
311 * of commits.
313 * @return identity of the committer (name, email) and the time the commit
314 * was made by the committer; null if no committer line was found.
316 public final PersonIdent getCommitterIdent() {
317 final byte[] raw = buffer;
318 final int nameB = RawParseUtils.committer(raw, 0);
319 if (nameB < 0)
320 return null;
321 return RawParseUtils.parsePersonIdent(raw, nameB);
325 * Parse the complete commit message and decode it to a string.
326 * <p>
327 * This method parses and returns the message portion of the commit buffer,
328 * after taking the commit's character set into account and decoding the
329 * buffer using that character set. This method is a fairly expensive
330 * operation and produces a new string on each invocation.
332 * @return decoded commit message as a string. Never null.
334 public final String getFullMessage() {
335 final byte[] raw = buffer;
336 final int msgB = RawParseUtils.commitMessage(raw, 0);
337 if (msgB < 0)
338 return "";
339 final Charset enc = RawParseUtils.parseEncoding(raw);
340 return RawParseUtils.decode(enc, raw, msgB, raw.length);
344 * Parse the commit message and return the first "line" of it.
345 * <p>
346 * The first line is everything up to the first pair of LFs. This is the
347 * "oneline" format, suitable for output in a single line display.
348 * <p>
349 * This method parses and returns the message portion of the commit buffer,
350 * after taking the commit's character set into account and decoding the
351 * buffer using that character set. This method is a fairly expensive
352 * operation and produces a new string on each invocation.
354 * @return decoded commit message as a string. Never null. The returned
355 * string does not contain any LFs, even if the first paragraph
356 * spanned multiple lines. Embedded LFs are converted to spaces.
358 public final String getShortMessage() {
359 final byte[] raw = buffer;
360 final int msgB = RawParseUtils.commitMessage(raw, 0);
361 if (msgB < 0)
362 return "";
364 final Charset enc = RawParseUtils.parseEncoding(raw);
365 final int msgE = RawParseUtils.endOfParagraph(raw, msgB);
366 String str = RawParseUtils.decode(enc, raw, msgB, msgE);
367 if (hasLF(raw, msgB, msgE))
368 str = str.replace('\n', ' ');
369 return str;
372 static boolean hasLF(final byte[] r, int b, final int e) {
373 while (b < e)
374 if (r[b++] == '\n')
375 return true;
376 return false;
380 * Reset this commit to allow another RevWalk with the same instances.
381 * <p>
382 * Subclasses <b>must</b> call <code>super.reset()</code> to ensure the
383 * basic information can be correctly cleared out.
385 public void reset() {
386 inDegree = 0;
389 final void disposeBody() {
390 buffer = null;
393 @Override
394 public String toString() {
395 final StringBuilder s = new StringBuilder();
396 s.append(Constants.typeString(getType()));
397 s.append(' ');
398 s.append(name());
399 s.append(' ');
400 s.append(commitTime);
401 s.append(' ');
402 appendCoreFlags(s);
403 return s.toString();