Mark locations that break in the year 2038
[egit/imyousuf.git] / org.spearce.jgit / src / org / spearce / jgit / revwalk / RevCommit.java
blob3e158c274342d78ad9288eecddbe5b9d6a9f52c6
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 PURPOSormE
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;
59 private RevTree tree;
61 RevCommit[] parents;
63 int commitTime; // An int here for performance, overflows in 2038
65 int inDegree;
67 private byte[] buffer;
69 /**
70 * Create a new commit reference.
72 * @param id
73 * object name for the commit.
75 protected RevCommit(final AnyObjectId id) {
76 super(id);
79 @Override
80 void parse(final RevWalk walk) throws MissingObjectException,
81 IncorrectObjectTypeException, IOException {
82 final ObjectLoader ldr = walk.db.openObject(walk.curs, this);
83 if (ldr == null)
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);
96 int ptr = 46;
97 if (parents == null) {
98 RevCommit[] pList = new RevCommit[1];
99 int nParents = 0;
100 for (;;) {
101 if (raw[ptr] != 'p')
102 break;
103 idBuffer.fromString(raw, ptr + 7);
104 final RevCommit p = walk.lookupCommit(idBuffer);
105 if (nParents == 0)
106 pList[nParents++] = p;
107 else if (nParents == 1) {
108 pList = new RevCommit[] { pList[0], p };
109 nParents = 2;
110 } else {
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;
118 ptr += 48;
120 if (nParents != pList.length) {
121 RevCommit[] old = pList;
122 pList = new RevCommit[nParents];
123 System.arraycopy(old, 0, pList, 0, nParents);
125 parents = pList;
128 // extract time from "committer "
129 ptr = RawParseUtils.committer(raw, ptr);
130 if (ptr > 0) {
131 ptr = RawParseUtils.nextLF(raw, ptr, '>');
133 // In 2038 commitTime will overflow unless it is changed to long.
134 commitTime = RawParseUtils.parseBase10(raw, ptr, null);
137 buffer = raw;
138 flags |= PARSED;
141 @Override
142 public int getType() {
143 return Constants.OBJ_COMMIT;
146 static void carryFlags(RevCommit c, final int carry) {
147 for (;;) {
148 final RevCommit[] pList = c.parents;
149 if (pList == null)
150 return;
151 final int n = pList.length;
152 if (n == 0)
153 return;
155 for (int i = 1; i < n; i++) {
156 final RevCommit p = pList[i];
157 if ((p.flags & carry) == carry)
158 continue;
159 p.flags |= carry;
160 carryFlags(p, carry);
163 c = pList[0];
164 if ((c.flags & carry) == carry)
165 return;
166 c.flags |= carry;
171 * Carry a RevFlag set on this commit to its parents.
172 * <p>
173 * If this commit is parsed, has parents, and has the supplied flag set on
174 * it we automatically add it to the parents, grand-parents, and so on until
175 * an unparsed commit or a commit with no parents is discovered. This
176 * permits applications to force a flag through the history chain when
177 * necessary.
179 * @param flag
180 * the single flag value to carry back onto parents.
182 public void carry(final RevFlag flag) {
183 final int carry = flags & flag.mask;
184 if (carry != 0)
185 carryFlags(this, carry);
189 * Time from the "committer " line of the buffer.
191 * @return time, expressed as seconds since the epoch.
193 public final int getCommitTime() {
194 return commitTime;
198 * Parse this commit buffer for display.
200 * @param walk
201 * revision walker owning this reference.
202 * @return parsed commit.
204 public final Commit asCommit(final RevWalk walk) {
205 return new Commit(walk.db, this, buffer);
209 * Get a reference to this commit's tree.
211 * @return tree of this commit.
213 public final RevTree getTree() {
214 return tree;
218 * Get the number of parent commits listed in this commit.
220 * @return number of parents; always a positive value but can be 0.
222 public final int getParentCount() {
223 return parents.length;
227 * Get the nth parent from this commit's parent list.
229 * @param nth
230 * parent index to obtain. Must be in the range 0 through
231 * {@link #getParentCount()}-1.
232 * @return the specified parent.
233 * @throws ArrayIndexOutOfBoundsException
234 * an invalid parent index was specified.
236 public final RevCommit getParent(final int nth) {
237 return parents[nth];
241 * Obtain an array of all parents (<b>NOTE - THIS IS NOT A COPY</b>).
242 * <p>
243 * This method is exposed only to provide very fast, efficient access to
244 * this commit's parent list. Applications relying on this list should be
245 * very careful to ensure they do not modify its contents during their use
246 * of it.
248 * @return the array of parents.
250 public final RevCommit[] getParents() {
251 return parents;
255 * Obtain the raw unparsed commit body (<b>NOTE - THIS IS NOT A COPY</b>).
256 * <p>
257 * This method is exposed only to provide very fast, efficient access to
258 * this commit's message buffer within a RevFilter. Applications relying on
259 * this buffer should be very careful to ensure they do not modify its
260 * contents during their use of it.
262 * @return the raw unparsed commit body. This is <b>NOT A COPY</b>.
263 * Altering the contents of this buffer may alter the walker's
264 * knowledge of this commit, and the results it produces.
266 public final byte[] getRawBuffer() {
267 return buffer;
271 * Parse the author identity from the raw buffer.
272 * <p>
273 * This method parses and returns the content of the author line, after
274 * taking the commit's character set into account and decoding the author
275 * name and email address. This method is fairly expensive and produces a
276 * new PersonIdent instance on each invocation. Callers should invoke this
277 * method only if they are certain they will be outputting the result, and
278 * should cache the return value for as long as necessary to use all
279 * information from it.
280 * <p>
281 * RevFilter implementations should try to use {@link RawParseUtils} to scan
282 * the {@link #getRawBuffer()} instead, as this will allow faster evaluation
283 * of commits.
285 * @return identity of the author (name, email) and the time the commit was
286 * made by the author; null if no author line was found.
288 public final PersonIdent getAuthorIdent() {
289 final byte[] raw = buffer;
290 final int nameB = RawParseUtils.author(raw, 0);
291 if (nameB < 0)
292 return null;
293 return RawParseUtils.parsePersonIdent(raw, nameB);
297 * Parse the committer identity from the raw buffer.
298 * <p>
299 * This method parses and returns the content of the committer line, after
300 * taking the commit's character set into account and decoding the committer
301 * name and email address. This method is fairly expensive and produces a
302 * new PersonIdent instance on each invocation. Callers should invoke this
303 * method only if they are certain they will be outputting the result, and
304 * should cache the return value for as long as necessary to use all
305 * information from it.
306 * <p>
307 * RevFilter implementations should try to use {@link RawParseUtils} to scan
308 * the {@link #getRawBuffer()} instead, as this will allow faster evaluation
309 * of commits.
311 * @return identity of the committer (name, email) and the time the commit
312 * was made by the committer; null if no committer line was found.
314 public final PersonIdent getCommitterIdent() {
315 final byte[] raw = buffer;
316 final int nameB = RawParseUtils.committer(raw, 0);
317 if (nameB < 0)
318 return null;
319 return RawParseUtils.parsePersonIdent(raw, nameB);
323 * Parse the complete commit message and decode it to a string.
324 * <p>
325 * This method parses and returns the message portion of the commit buffer,
326 * after taking the commit's character set into account and decoding the
327 * buffer using that character set. This method is a fairly expensive
328 * operation and produces a new string on each invocation.
330 * @return decoded commit message as a string. Never null.
332 public final String getFullMessage() {
333 final byte[] raw = buffer;
334 final int msgB = RawParseUtils.commitMessage(raw, 0);
335 if (msgB < 0)
336 return "";
337 final Charset enc = RawParseUtils.parseEncoding(raw);
338 return RawParseUtils.decode(enc, raw, msgB, raw.length);
342 * Parse the commit message and return the first "line" of it.
343 * <p>
344 * The first line is everything up to the first pair of LFs. This is the
345 * "oneline" format, suitable for output in a single line display.
346 * <p>
347 * This method parses and returns the message portion of the commit buffer,
348 * after taking the commit's character set into account and decoding the
349 * buffer using that character set. This method is a fairly expensive
350 * operation and produces a new string on each invocation.
352 * @return decoded commit message as a string. Never null. The returned
353 * string does not contain any LFs, even if the first paragraph
354 * spanned multiple lines. Embedded LFs are converted to spaces.
356 public final String getShortMessage() {
357 final byte[] raw = buffer;
358 final int msgB = RawParseUtils.commitMessage(raw, 0);
359 if (msgB < 0)
360 return "";
362 final Charset enc = RawParseUtils.parseEncoding(raw);
363 final int msgE = RawParseUtils.endOfParagraph(raw, msgB);
364 String str = RawParseUtils.decode(enc, raw, msgB, msgE);
365 if (hasLF(raw, msgB, msgE))
366 str = str.replace('\n', ' ');
367 return str;
370 private static boolean hasLF(final byte[] r, int b, final int e) {
371 while (b < e)
372 if (r[b++] == '\n')
373 return true;
374 return false;
378 * Reset this commit to allow another RevWalk with the same instances.
379 * <p>
380 * Subclasses <b>must</b> call <code>super.reset()</code> to ensure the
381 * basic information can be correctly cleared out.
383 public void reset() {
384 inDegree = 0;
387 public void dispose() {
388 flags &= ~PARSED;
389 buffer = null;