2 * Copyright (C) 2008, Shawn O. Pearce <spearce@spearce.org>
6 * Redistribution and use in source and binary forms, with or
7 * without modification, are permitted provided that the following
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
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
.ObjectLoader
;
50 import org
.spearce
.jgit
.lib
.PersonIdent
;
51 import org
.spearce
.jgit
.util
.RawParseUtils
;
53 /** A commit reference to a commit in the DAG. */
54 public class RevCommit
extends RevObject
{
55 static final RevCommit
[] NO_PARENTS
= {};
57 private static final String TYPE_COMMIT
= Constants
.TYPE_COMMIT
;
67 private byte[] buffer
;
70 * Create a new commit reference.
73 * object name for the commit.
75 protected RevCommit(final AnyObjectId id
) {
80 void parse(final RevWalk walk
) throws MissingObjectException
,
81 IncorrectObjectTypeException
, IOException
{
82 final ObjectLoader ldr
= walk
.db
.openObject(walk
.curs
, this);
84 throw new MissingObjectException(this, TYPE_COMMIT
);
85 final byte[] data
= ldr
.getCachedBytes();
86 if (Constants
.OBJ_COMMIT
!= ldr
.getType())
87 throw new IncorrectObjectTypeException(this, TYPE_COMMIT
);
88 parseCanonical(walk
, data
);
91 void parseCanonical(final RevWalk walk
, final byte[] raw
) {
92 final MutableObjectId idBuffer
= walk
.idBuffer
;
93 idBuffer
.fromString(raw
, 5);
94 tree
= walk
.lookupTree(idBuffer
);
97 if (parents
== null) {
98 RevCommit
[] pList
= new RevCommit
[1];
103 idBuffer
.fromString(raw
, ptr
+ 7);
104 final RevCommit p
= walk
.lookupCommit(idBuffer
);
106 pList
[nParents
++] = p
;
107 else if (nParents
== 1) {
108 pList
= new RevCommit
[] { pList
[0], p
};
111 if (pList
.length
<= nParents
) {
112 RevCommit
[] old
= pList
;
113 pList
= new RevCommit
[pList
.length
+ 32];
114 System
.arraycopy(old
, 0, pList
, 0, nParents
);
116 pList
[nParents
++] = p
;
120 if (nParents
!= pList
.length
) {
121 RevCommit
[] old
= pList
;
122 pList
= new RevCommit
[nParents
];
123 System
.arraycopy(old
, 0, pList
, 0, nParents
);
128 // extract time from "committer "
129 ptr
= RawParseUtils
.committer(raw
, ptr
);
131 ptr
= RawParseUtils
.nextLF(raw
, ptr
, '>');
132 commitTime
= RawParseUtils
.parseBase10(raw
, ptr
, null);
140 public int getType() {
141 return Constants
.OBJ_COMMIT
;
144 static void carryFlags(RevCommit c
, final int carry
) {
146 final RevCommit
[] pList
= c
.parents
;
149 final int n
= pList
.length
;
153 for (int i
= 1; i
< n
; i
++) {
154 final RevCommit p
= pList
[i
];
155 if ((p
.flags
& carry
) == carry
)
158 carryFlags(p
, carry
);
162 if ((c
.flags
& carry
) == carry
)
169 * Carry a RevFlag set on this commit to its parents.
171 * If this commit is parsed, has parents, and has the supplied flag set on
172 * it we automatically add it to the parents, grand-parents, and so on until
173 * an unparsed commit or a commit with no parents is discovered. This
174 * permits applications to force a flag through the history chain when
178 * the single flag value to carry back onto parents.
180 public void carry(final RevFlag flag
) {
181 final int carry
= flags
& flag
.mask
;
183 carryFlags(this, carry
);
187 * Time from the "committer " line of the buffer.
189 * @return time, expressed as seconds since the epoch.
191 public final int getCommitTime() {
196 * Parse this commit buffer for display.
199 * revision walker owning this reference.
200 * @return parsed commit.
202 public final Commit
asCommit(final RevWalk walk
) {
203 return new Commit(walk
.db
, this, buffer
);
207 * Get a reference to this commit's tree.
209 * @return tree of this commit.
211 public final RevTree
getTree() {
216 * Get the number of parent commits listed in this commit.
218 * @return number of parents; always a positive value but can be 0.
220 public final int getParentCount() {
221 return parents
.length
;
225 * Get the nth parent from this commit's parent list.
228 * parent index to obtain. Must be in the range 0 through
229 * {@link #getParentCount()}-1.
230 * @return the specified parent.
231 * @throws ArrayIndexOutOfBoundsException
232 * an invalid parent index was specified.
234 public final RevCommit
getParent(final int nth
) {
239 * Obtain an array of all parents (<b>NOTE - THIS IS NOT A COPY</b>).
241 * This method is exposed only to provide very fast, efficient access to
242 * this commit's parent list. Applications relying on this list should be
243 * very careful to ensure they do not modify its contents during their use
246 * @return the array of parents.
248 public final RevCommit
[] getParents() {
253 * Obtain the raw unparsed commit body (<b>NOTE - THIS IS NOT A COPY</b>).
255 * This method is exposed only to provide very fast, efficient access to
256 * this commit's message buffer within a RevFilter. Applications relying on
257 * this buffer should be very careful to ensure they do not modify its
258 * contents during their use of it.
260 * @return the raw unparsed commit body. This is <b>NOT A COPY</b>.
261 * Altering the contents of this buffer may alter the walker's
262 * knowledge of this commit, and the results it produces.
264 public final byte[] getRawBuffer() {
269 * Parse the author identity from the raw buffer.
271 * This method parses and returns the content of the author line, after
272 * taking the commit's character set into account and decoding the author
273 * name and email address. This method is fairly expensive and produces a
274 * new PersonIdent instance on each invocation. Callers should invoke this
275 * method only if they are certain they will be outputting the result, and
276 * should cache the return value for as long as necessary to use all
277 * information from it.
279 * RevFilter implementations should try to use {@link RawParseUtils} to scan
280 * the {@link #getRawBuffer()} instead, as this will allow faster evaluation
283 * @return identity of the author (name, email) and the time the commit was
284 * made by the author; null if no author line was found.
286 public final PersonIdent
getAuthorIdent() {
287 final byte[] raw
= buffer
;
288 final int nameB
= RawParseUtils
.author(raw
, 0);
291 return RawParseUtils
.parsePersonIdent(raw
, nameB
);
295 * Parse the committer identity from the raw buffer.
297 * This method parses and returns the content of the committer line, after
298 * taking the commit's character set into account and decoding the committer
299 * name and email address. This method is fairly expensive and produces a
300 * new PersonIdent instance on each invocation. Callers should invoke this
301 * method only if they are certain they will be outputting the result, and
302 * should cache the return value for as long as necessary to use all
303 * information from it.
305 * RevFilter implementations should try to use {@link RawParseUtils} to scan
306 * the {@link #getRawBuffer()} instead, as this will allow faster evaluation
309 * @return identity of the committer (name, email) and the time the commit
310 * was made by the comitter; null if no committer line was found.
312 public final PersonIdent
getCommitterIdent() {
313 final byte[] raw
= buffer
;
314 final int nameB
= RawParseUtils
.committer(raw
, 0);
317 return RawParseUtils
.parsePersonIdent(raw
, nameB
);
321 * Parse the complete commit message and decode it to a string.
323 * This method parses and returns the message portion of the commit buffer,
324 * after taking the commit's character set into account and decoding the
325 * buffer using that character set. This method is a fairly expensive
326 * operation and produces a new string on each invocation.
328 * @return decoded commit message as a string. Never null.
330 public final String
getFullMessage() {
331 final byte[] raw
= buffer
;
332 final int msgB
= RawParseUtils
.commitMessage(raw
, 0);
335 final Charset enc
= RawParseUtils
.parseEncoding(raw
);
336 return RawParseUtils
.decode(enc
, raw
, msgB
, raw
.length
);
340 * Parse the commit message and return the first "line" of it.
342 * The first line is everything up to the first pair of LFs. This is the
343 * "oneline" format, suitable for output in a single line display.
345 * This method parses and returns the message portion of the commit buffer,
346 * after taking the commit's character set into account and decoding the
347 * buffer using that character set. This method is a fairly expensive
348 * operation and produces a new string on each invocation.
350 * @return decoded commit message as a string. Never null. The returned
351 * string does not contain any LFs, even if the first paragraph
352 * spanned multiple lines. Embedded LFs are converted to spaces.
354 public final String
getShortMessage() {
355 final byte[] raw
= buffer
;
356 final int msgB
= RawParseUtils
.commitMessage(raw
, 0);
360 final Charset enc
= RawParseUtils
.parseEncoding(raw
);
361 final int msgE
= RawParseUtils
.endOfParagraph(raw
, msgB
);
362 String str
= RawParseUtils
.decode(enc
, raw
, msgB
, msgE
);
363 if (hasLF(raw
, msgB
, msgE
))
364 str
= str
.replace('\n', ' ');
368 private static boolean hasLF(final byte[] r
, int b
, final int e
) {
376 * Reset this commit to allow another RevWalk with the same instances.
378 * Subclasses <b>must</b> call <code>super.reset()</code> to ensure the
379 * basic information can be correctly cleared out.
381 public void reset() {
385 public void dispose() {