View Javadoc
1   /*
2    * Copyright (C) 2011, Google Inc.
3    * and other copyright owners as documented in the project's IP log.
4    *
5    * This program and the accompanying materials are made available
6    * under the terms of the Eclipse Distribution License v1.0 which
7    * accompanies this distribution, is reproduced below, and is
8    * available at http://www.eclipse.org/org/documents/edl-v10.php
9    *
10   * All rights reserved.
11   *
12   * Redistribution and use in source and binary forms, with or
13   * without modification, are permitted provided that the following
14   * conditions are met:
15   *
16   * - Redistributions of source code must retain the above copyright
17   *   notice, this list of conditions and the following disclaimer.
18   *
19   * - Redistributions in binary form must reproduce the above
20   *   copyright notice, this list of conditions and the following
21   *   disclaimer in the documentation and/or other materials provided
22   *   with the distribution.
23   *
24   * - Neither the name of the Eclipse Foundation, Inc. nor the
25   *   names of its contributors may be used to endorse or promote
26   *   products derived from this software without specific prior
27   *   written permission.
28   *
29   * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
30   * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
31   * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
32   * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
33   * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
34   * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
35   * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
36   * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
37   * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
38   * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
39   * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
40   * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
41   * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
42   */
43  
44  package org.eclipse.jgit.blame;
45  
46  import static org.eclipse.jgit.lib.Constants.OBJ_BLOB;
47  import static org.eclipse.jgit.lib.FileMode.TYPE_FILE;
48  import static org.eclipse.jgit.lib.FileMode.TYPE_MASK;
49  
50  import java.io.IOException;
51  import java.util.Collection;
52  import java.util.Collections;
53  
54  import org.eclipse.jgit.blame.Candidate.BlobCandidate;
55  import org.eclipse.jgit.blame.Candidate.ReverseCandidate;
56  import org.eclipse.jgit.blame.ReverseWalk.ReverseCommit;
57  import org.eclipse.jgit.diff.DiffAlgorithm;
58  import org.eclipse.jgit.diff.DiffEntry;
59  import org.eclipse.jgit.diff.DiffEntry.ChangeType;
60  import org.eclipse.jgit.diff.EditList;
61  import org.eclipse.jgit.diff.HistogramDiff;
62  import org.eclipse.jgit.diff.RawText;
63  import org.eclipse.jgit.diff.RawTextComparator;
64  import org.eclipse.jgit.diff.RenameDetector;
65  import org.eclipse.jgit.internal.JGitText;
66  import org.eclipse.jgit.lib.AnyObjectId;
67  import org.eclipse.jgit.lib.MutableObjectId;
68  import org.eclipse.jgit.lib.ObjectId;
69  import org.eclipse.jgit.lib.ObjectLoader;
70  import org.eclipse.jgit.lib.ObjectReader;
71  import org.eclipse.jgit.lib.PersonIdent;
72  import org.eclipse.jgit.lib.Repository;
73  import org.eclipse.jgit.revwalk.RevCommit;
74  import org.eclipse.jgit.revwalk.RevFlag;
75  import org.eclipse.jgit.revwalk.RevWalk;
76  import org.eclipse.jgit.treewalk.TreeWalk;
77  import org.eclipse.jgit.treewalk.filter.PathFilter;
78  import org.eclipse.jgit.treewalk.filter.TreeFilter;
79  
80  /**
81   * Generate author information for lines based on a provided file.
82   * <p>
83   * Applications that want a simple one-shot computation of blame for a file
84   * should use {@link #computeBlameResult()} to prepare the entire result in one
85   * method call. This may block for significant time as the history of the
86   * repository must be traversed until information is gathered for every line.
87   * <p>
88   * Applications that want more incremental update behavior may use either the
89   * raw {@link #next()} streaming approach supported by this class, or construct
90   * a {@link BlameResult} using {@link BlameResult#create(BlameGenerator)} and
91   * incrementally construct the result with {@link BlameResult#computeNext()}.
92   * <p>
93   * This class is not thread-safe.
94   * <p>
95   * An instance of BlameGenerator can only be used once. To blame multiple files
96   * the application must create a new BlameGenerator.
97   * <p>
98   * During blame processing there are two files involved:
99   * <ul>
100  * <li>result - The file whose lines are being examined. This is the revision
101  * the user is trying to view blame/annotation information alongside of.</li>
102  * <li>source - The file that was blamed with supplying one or more lines of
103  * data into result. The source may be a different file path (due to copy or
104  * rename). Source line numbers may differ from result line numbers due to lines
105  * being added/removed in intermediate revisions.</li>
106  * </ul>
107  * <p>
108  * The blame algorithm is implemented by initially assigning responsibility for
109  * all lines of the result to the starting commit. A difference against the
110  * commit's ancestor is computed, and responsibility is passed to the ancestor
111  * commit for any lines that are common. The starting commit is blamed only for
112  * the lines that do not appear in the ancestor, if any. The loop repeats using
113  * the ancestor, until there are no more lines to acquire information on, or the
114  * file's creation point is discovered in history.
115  */
116 public class BlameGenerator implements AutoCloseable {
117 	private final Repository repository;
118 
119 	private final PathFilter resultPath;
120 
121 	private final MutableObjectId idBuf;
122 
123 	/** Revision pool used to acquire commits from. */
124 	private RevWalk revPool;
125 
126 	/** Indicates the commit was put into the queue at least once. */
127 	private RevFlag SEEN;
128 
129 	private ObjectReader reader;
130 
131 	private TreeWalk treeWalk;
132 
133 	private DiffAlgorithm diffAlgorithm = new HistogramDiff();
134 
135 	private RawTextComparator textComparator = RawTextComparator.DEFAULT;
136 
137 	private RenameDetector renameDetector;
138 
139 	/** Potential candidates, sorted by commit time descending. */
140 	private Candidate queue;
141 
142 	/** Number of lines that still need to be discovered. */
143 	private int remaining;
144 
145 	/** Blame is currently assigned to this source. */
146 	private Candidate outCandidate;
147 	private Region outRegion;
148 
149 	/**
150 	 * Create a blame generator for the repository and path (relative to
151 	 * repository)
152 	 *
153 	 * @param repository
154 	 *            repository to access revision data from.
155 	 * @param path
156 	 *            initial path of the file to start scanning (relative to the
157 	 *            repository).
158 	 */
159 	public BlameGenerator(Repository repository, String path) {
160 		this.repository = repository;
161 		this.resultPath = PathFilter.create(path);
162 
163 		idBuf = new MutableObjectId();
164 		setFollowFileRenames(true);
165 		initRevPool(false);
166 
167 		remaining = -1;
168 	}
169 
170 	private void initRevPool(boolean reverse) {
171 		if (queue != null)
172 			throw new IllegalStateException();
173 
174 		if (revPool != null)
175 			revPool.close();
176 
177 		if (reverse)
178 			revPool = new ReverseWalk(getRepository());
179 		else
180 			revPool = new RevWalk(getRepository());
181 
182 		SEEN = revPool.newFlag("SEEN"); //$NON-NLS-1$
183 		reader = revPool.getObjectReader();
184 		treeWalk = new TreeWalk(reader);
185 		treeWalk.setRecursive(true);
186 	}
187 
188 	/** @return repository being scanned for revision history. */
189 	public Repository getRepository() {
190 		return repository;
191 	}
192 
193 	/** @return path file path being processed. */
194 	public String getResultPath() {
195 		return resultPath.getPath();
196 	}
197 
198 	/**
199 	 * Difference algorithm to use when comparing revisions.
200 	 *
201 	 * @param algorithm
202 	 * @return {@code this}
203 	 */
204 	public BlameGenerator setDiffAlgorithm(DiffAlgorithm algorithm) {
205 		diffAlgorithm = algorithm;
206 		return this;
207 	}
208 
209 	/**
210 	 * Text comparator to use when comparing revisions.
211 	 *
212 	 * @param comparator
213 	 * @return {@code this}
214 	 */
215 	public BlameGenerator setTextComparator(RawTextComparator comparator) {
216 		textComparator = comparator;
217 		return this;
218 	}
219 
220 	/**
221 	 * Enable (or disable) following file renames, on by default.
222 	 * <p>
223 	 * If true renames are followed using the standard FollowFilter behavior
224 	 * used by RevWalk (which matches {@code git log --follow} in the C
225 	 * implementation). This is not the same as copy/move detection as
226 	 * implemented by the C implementation's of {@code git blame -M -C}.
227 	 *
228 	 * @param follow
229 	 *            enable following.
230 	 * @return {@code this}
231 	 */
232 	public BlameGenerator setFollowFileRenames(boolean follow) {
233 		if (follow)
234 			renameDetector = new RenameDetector(getRepository());
235 		else
236 			renameDetector = null;
237 		return this;
238 	}
239 
240 	/**
241 	 * Obtain the RenameDetector if {@code setFollowFileRenames(true)}.
242 	 *
243 	 * @return the rename detector, allowing the application to configure its
244 	 *         settings for rename score and breaking behavior.
245 	 */
246 	public RenameDetector getRenameDetector() {
247 		return renameDetector;
248 	}
249 
250 	/**
251 	 * Push a candidate blob onto the generator's traversal stack.
252 	 * <p>
253 	 * Candidates should be pushed in history order from oldest-to-newest.
254 	 * Applications should push the starting commit first, then the index
255 	 * revision (if the index is interesting), and finally the working tree
256 	 * copy (if the working tree is interesting).
257 	 *
258 	 * @param description
259 	 *            description of the blob revision, such as "Working Tree".
260 	 * @param contents
261 	 *            contents of the file.
262 	 * @return {@code this}
263 	 * @throws IOException
264 	 *             the repository cannot be read.
265 	 */
266 	public BlameGenerator push(String description, byte[] contents)
267 			throws IOException {
268 		return push(description, new RawText(contents));
269 	}
270 
271 	/**
272 	 * Push a candidate blob onto the generator's traversal stack.
273 	 * <p>
274 	 * Candidates should be pushed in history order from oldest-to-newest.
275 	 * Applications should push the starting commit first, then the index
276 	 * revision (if the index is interesting), and finally the working tree copy
277 	 * (if the working tree is interesting).
278 	 *
279 	 * @param description
280 	 *            description of the blob revision, such as "Working Tree".
281 	 * @param contents
282 	 *            contents of the file.
283 	 * @return {@code this}
284 	 * @throws IOException
285 	 *             the repository cannot be read.
286 	 */
287 	public BlameGenerator push(String description, RawText contents)
288 			throws IOException {
289 		if (description == null)
290 			description = JGitText.get().blameNotCommittedYet;
291 		BlobCandidate c = new BlobCandidate(description, resultPath);
292 		c.sourceText = contents;
293 		c.regionList = new Region(0, 0, contents.size());
294 		remaining = contents.size();
295 		push(c);
296 		return this;
297 	}
298 
299 	/**
300 	 * Push a candidate object onto the generator's traversal stack.
301 	 * <p>
302 	 * Candidates should be pushed in history order from oldest-to-newest.
303 	 * Applications should push the starting commit first, then the index
304 	 * revision (if the index is interesting), and finally the working tree copy
305 	 * (if the working tree is interesting).
306 	 *
307 	 * @param description
308 	 *            description of the blob revision, such as "Working Tree".
309 	 * @param id
310 	 *            may be a commit or a blob.
311 	 * @return {@code this}
312 	 * @throws IOException
313 	 *             the repository cannot be read.
314 	 */
315 	public BlameGenerator push(String description, AnyObjectId id)
316 			throws IOException {
317 		ObjectLoader ldr = reader.open(id);
318 		if (ldr.getType() == OBJ_BLOB) {
319 			if (description == null)
320 				description = JGitText.get().blameNotCommittedYet;
321 			BlobCandidate c = new BlobCandidate(description, resultPath);
322 			c.sourceBlob = id.toObjectId();
323 			c.sourceText = new RawText(ldr.getCachedBytes(Integer.MAX_VALUE));
324 			c.regionList = new Region(0, 0, c.sourceText.size());
325 			remaining = c.sourceText.size();
326 			push(c);
327 			return this;
328 		}
329 
330 		RevCommit commit = revPool.parseCommit(id);
331 		if (!find(commit, resultPath))
332 			return this;
333 
334 		Candidate c = new Candidate(commit, resultPath);
335 		c.sourceBlob = idBuf.toObjectId();
336 		c.loadText(reader);
337 		c.regionList = new Region(0, 0, c.sourceText.size());
338 		remaining = c.sourceText.size();
339 		push(c);
340 		return this;
341 	}
342 
343 	/**
344 	 * Configure the generator to compute reverse blame (history of deletes).
345 	 * <p>
346 	 * This method is expensive as it immediately runs a RevWalk over the
347 	 * history spanning the expression {@code start..end} (end being more recent
348 	 * than start) and then performs the equivalent operation as
349 	 * {@link #push(String, AnyObjectId)} to begin blame traversal from the
350 	 * commit named by {@code start} walking forwards through history until
351 	 * {@code end} blaming line deletions.
352 	 * <p>
353 	 * A reverse blame may produce multiple sources for the same result line,
354 	 * each of these is a descendant commit that removed the line, typically
355 	 * this occurs when the same deletion appears in multiple side branches such
356 	 * as due to a cherry-pick. Applications relying on reverse should use
357 	 * {@link BlameResult} as it filters these duplicate sources and only
358 	 * remembers the first (oldest) deletion.
359 	 *
360 	 * @param start
361 	 *            oldest commit to traverse from. The result file will be loaded
362 	 *            from this commit's tree.
363 	 * @param end
364 	 *            most recent commit to stop traversal at. Usually an active
365 	 *            branch tip, tag, or HEAD.
366 	 * @return {@code this}
367 	 * @throws IOException
368 	 *             the repository cannot be read.
369 	 */
370 	public BlameGenerator reverse(AnyObjectId start, AnyObjectId end)
371 			throws IOException {
372 		return reverse(start, Collections.singleton(end.toObjectId()));
373 	}
374 
375 	/**
376 	 * Configure the generator to compute reverse blame (history of deletes).
377 	 * <p>
378 	 * This method is expensive as it immediately runs a RevWalk over the
379 	 * history spanning the expression {@code start..end} (end being more recent
380 	 * than start) and then performs the equivalent operation as
381 	 * {@link #push(String, AnyObjectId)} to begin blame traversal from the
382 	 * commit named by {@code start} walking forwards through history until
383 	 * {@code end} blaming line deletions.
384 	 * <p>
385 	 * A reverse blame may produce multiple sources for the same result line,
386 	 * each of these is a descendant commit that removed the line, typically
387 	 * this occurs when the same deletion appears in multiple side branches such
388 	 * as due to a cherry-pick. Applications relying on reverse should use
389 	 * {@link BlameResult} as it filters these duplicate sources and only
390 	 * remembers the first (oldest) deletion.
391 	 *
392 	 * @param start
393 	 *            oldest commit to traverse from. The result file will be loaded
394 	 *            from this commit's tree.
395 	 * @param end
396 	 *            most recent commits to stop traversal at. Usually an active
397 	 *            branch tip, tag, or HEAD.
398 	 * @return {@code this}
399 	 * @throws IOException
400 	 *             the repository cannot be read.
401 	 */
402 	public BlameGenerator reverse(AnyObjectId start,
403 			Collection<? extends ObjectId> end) throws IOException {
404 		initRevPool(true);
405 
406 		ReverseCommit result = (ReverseCommit) revPool.parseCommit(start);
407 		if (!find(result, resultPath))
408 			return this;
409 
410 		revPool.markUninteresting(result);
411 		for (ObjectId id : end)
412 			revPool.markStart(revPool.parseCommit(id));
413 
414 		while (revPool.next() != null) {
415 			// just pump the queue
416 		}
417 
418 		ReverseCandidate c = new ReverseCandidate(result, resultPath);
419 		c.sourceBlob = idBuf.toObjectId();
420 		c.loadText(reader);
421 		c.regionList = new Region(0, 0, c.sourceText.size());
422 		remaining = c.sourceText.size();
423 		push(c);
424 		return this;
425 	}
426 
427 	/**
428 	 * Allocate a new RevFlag for use by the caller.
429 	 *
430 	 * @param name
431 	 *            unique name of the flag in the blame context.
432 	 * @return the newly allocated flag.
433 	 * @since 3.4
434 	 */
435 	public RevFlag newFlag(String name) {
436 		return revPool.newFlag(name);
437 	}
438 
439 	/**
440 	 * Execute the generator in a blocking fashion until all data is ready.
441 	 *
442 	 * @return the complete result. Null if no file exists for the given path.
443 	 * @throws IOException
444 	 *             the repository cannot be read.
445 	 */
446 	public BlameResult computeBlameResult() throws IOException {
447 		try {
448 			BlameResult r = BlameResult.create(this);
449 			if (r != null)
450 				r.computeAll();
451 			return r;
452 		} finally {
453 			close();
454 		}
455 	}
456 
457 	/**
458 	 * Step the blame algorithm one iteration.
459 	 *
460 	 * @return true if the generator has found a region's source. The getSource*
461 	 *         and {@link #getResultStart()}, {@link #getResultEnd()} methods
462 	 *         can be used to inspect the region found. False if there are no
463 	 *         more regions to describe.
464 	 * @throws IOException
465 	 *             repository cannot be read.
466 	 */
467 	public boolean next() throws IOException {
468 		// If there is a source still pending, produce the next region.
469 		if (outRegion != null) {
470 			Region r = outRegion;
471 			remaining -= r.length;
472 			if (r.next != null) {
473 				outRegion = r.next;
474 				return true;
475 			}
476 
477 			if (outCandidate.queueNext != null)
478 				return result(outCandidate.queueNext);
479 
480 			outCandidate = null;
481 			outRegion = null;
482 		}
483 
484 		// If there are no lines remaining, the entire result is done,
485 		// even if there are revisions still available for the path.
486 		if (remaining == 0)
487 			return done();
488 
489 		for (;;) {
490 			Candidate n = pop();
491 			if (n == null)
492 				return done();
493 
494 			int pCnt = n.getParentCount();
495 			if (pCnt == 1) {
496 				if (processOne(n))
497 					return true;
498 
499 			} else if (1 < pCnt) {
500 				if (processMerge(n))
501 					return true;
502 
503 			} else if (n instanceof ReverseCandidate) {
504 				// Do not generate a tip of a reverse. The region
505 				// survives and should not appear to be deleted.
506 
507 			} else /* if (pCnt == 0) */{
508 				// Root commit, with at least one surviving region.
509 				// Assign the remaining blame here.
510 				return result(n);
511 			}
512 		}
513 	}
514 
515 	private boolean done() {
516 		close();
517 		return false;
518 	}
519 
520 	private boolean result(Candidate n) throws IOException {
521 		n.beginResult(revPool);
522 		outCandidate = n;
523 		outRegion = n.regionList;
524 		return true;
525 	}
526 
527 	private boolean reverseResult(Candidate parent, Candidate source)
528 			throws IOException {
529 		// On a reverse blame present the application the parent
530 		// (as this is what did the removals), however the region
531 		// list to enumerate is the source's surviving list.
532 		Candidate res = parent.copy(parent.sourceCommit);
533 		res.regionList = source.regionList;
534 		return result(res);
535 	}
536 
537 	private Candidate pop() {
538 		Candidate n = queue;
539 		if (n != null) {
540 			queue = n.queueNext;
541 			n.queueNext = null;
542 		}
543 		return n;
544 	}
545 
546 	private void push(BlobCandidate toInsert) {
547 		Candidate c = queue;
548 		if (c != null) {
549 			c.remove(SEEN); // will be pushed by toInsert
550 			c.regionList = null;
551 			toInsert.parent = c;
552 		}
553 		queue = toInsert;
554 	}
555 
556 	private void push(Candidate toInsert) {
557 		if (toInsert.has(SEEN)) {
558 			// We have already added a Candidate for this commit to the queue,
559 			// this can happen if the commit is a merge base for two or more
560 			// parallel branches that were merged together.
561 			//
562 			// It is likely the candidate was not yet processed. The queue
563 			// sorts descending by commit time and usually descendant commits
564 			// have higher timestamps than the ancestors.
565 			//
566 			// Find the existing candidate and merge the new candidate's
567 			// region list into it.
568 			for (Candidate p = queue; p != null; p = p.queueNext) {
569 				if (p.canMergeRegions(toInsert)) {
570 					p.mergeRegions(toInsert);
571 					return;
572 				}
573 			}
574 		}
575 		toInsert.add(SEEN);
576 
577 		// Insert into the queue using descending commit time, so
578 		// the most recent commit will pop next.
579 		int time = toInsert.getTime();
580 		Candidate n = queue;
581 		if (n == null || time >= n.getTime()) {
582 			toInsert.queueNext = n;
583 			queue = toInsert;
584 			return;
585 		}
586 
587 		for (Candidate p = n;; p = n) {
588 			n = p.queueNext;
589 			if (n == null || time >= n.getTime()) {
590 				toInsert.queueNext = n;
591 				p.queueNext = toInsert;
592 				return;
593 			}
594 		}
595 	}
596 
597 	private boolean processOne(Candidate n) throws IOException {
598 		RevCommit parent = n.getParent(0);
599 		if (parent == null)
600 			return split(n.getNextCandidate(0), n);
601 		revPool.parseHeaders(parent);
602 
603 		if (find(parent, n.sourcePath)) {
604 			if (idBuf.equals(n.sourceBlob))
605 				return blameEntireRegionOnParent(n, parent);
606 			return splitBlameWithParent(n, parent);
607 		}
608 
609 		if (n.sourceCommit == null)
610 			return result(n);
611 
612 		DiffEntry r = findRename(parent, n.sourceCommit, n.sourcePath);
613 		if (r == null)
614 			return result(n);
615 
616 		if (0 == r.getOldId().prefixCompare(n.sourceBlob)) {
617 			// A 100% rename without any content change can also
618 			// skip directly to the parent.
619 			n.sourceCommit = parent;
620 			n.sourcePath = PathFilter.create(r.getOldPath());
621 			push(n);
622 			return false;
623 		}
624 
625 		Candidate next = n.create(parent, PathFilter.create(r.getOldPath()));
626 		next.sourceBlob = r.getOldId().toObjectId();
627 		next.renameScore = r.getScore();
628 		next.loadText(reader);
629 		return split(next, n);
630 	}
631 
632 	private boolean blameEntireRegionOnParent(Candidate n, RevCommit parent) {
633 		// File was not modified, blame parent.
634 		n.sourceCommit = parent;
635 		push(n);
636 		return false;
637 	}
638 
639 	private boolean splitBlameWithParent(Candidate n, RevCommit parent)
640 			throws IOException {
641 		Candidate next = n.create(parent, n.sourcePath);
642 		next.sourceBlob = idBuf.toObjectId();
643 		next.loadText(reader);
644 		return split(next, n);
645 	}
646 
647 	private boolean split(Candidate parent, Candidate source)
648 			throws IOException {
649 		EditList editList = diffAlgorithm.diff(textComparator,
650 				parent.sourceText, source.sourceText);
651 		if (editList.isEmpty()) {
652 			// Ignoring whitespace (or some other special comparator) can
653 			// cause non-identical blobs to have an empty edit list. In
654 			// a case like this push the parent alone.
655 			parent.regionList = source.regionList;
656 			push(parent);
657 			return false;
658 		}
659 
660 		parent.takeBlame(editList, source);
661 		if (parent.regionList != null)
662 			push(parent);
663 		if (source.regionList != null) {
664 			if (source instanceof ReverseCandidate)
665 				return reverseResult(parent, source);
666 			return result(source);
667 		}
668 		return false;
669 	}
670 
671 	private boolean processMerge(Candidate n) throws IOException {
672 		int pCnt = n.getParentCount();
673 
674 		// If any single parent exactly matches the merge, follow only
675 		// that one parent through history.
676 		ObjectId[] ids = null;
677 		for (int pIdx = 0; pIdx < pCnt; pIdx++) {
678 			RevCommit parent = n.getParent(pIdx);
679 			revPool.parseHeaders(parent);
680 			if (!find(parent, n.sourcePath))
681 				continue;
682 			if (!(n instanceof ReverseCandidate) && idBuf.equals(n.sourceBlob))
683 				return blameEntireRegionOnParent(n, parent);
684 			if (ids == null)
685 				ids = new ObjectId[pCnt];
686 			ids[pIdx] = idBuf.toObjectId();
687 		}
688 
689 		// If rename detection is enabled, search for any relevant names.
690 		DiffEntry[] renames = null;
691 		if (renameDetector != null) {
692 			renames = new DiffEntry[pCnt];
693 			for (int pIdx = 0; pIdx < pCnt; pIdx++) {
694 				RevCommit parent = n.getParent(pIdx);
695 				if (ids != null && ids[pIdx] != null)
696 					continue;
697 
698 				DiffEntry r = findRename(parent, n.sourceCommit, n.sourcePath);
699 				if (r == null)
700 					continue;
701 
702 				if (n instanceof ReverseCandidate) {
703 					if (ids == null)
704 						ids = new ObjectId[pCnt];
705 					ids[pCnt] = r.getOldId().toObjectId();
706 				} else if (0 == r.getOldId().prefixCompare(n.sourceBlob)) {
707 					// A 100% rename without any content change can also
708 					// skip directly to the parent. Note this bypasses an
709 					// earlier parent that had the path (above) but did not
710 					// have an exact content match. For performance reasons
711 					// we choose to follow the one parent over trying to do
712 					// possibly both parents.
713 					n.sourcePath = PathFilter.create(r.getOldPath());
714 					return blameEntireRegionOnParent(n, parent);
715 				}
716 
717 				renames[pIdx] = r;
718 			}
719 		}
720 
721 		// Construct the candidate for each parent.
722 		Candidate[] parents = new Candidate[pCnt];
723 		for (int pIdx = 0; pIdx < pCnt; pIdx++) {
724 			RevCommit parent = n.getParent(pIdx);
725 
726 			Candidate p;
727 			if (renames != null && renames[pIdx] != null) {
728 				p = n.create(parent,
729 						PathFilter.create(renames[pIdx].getOldPath()));
730 				p.renameScore = renames[pIdx].getScore();
731 				p.sourceBlob = renames[pIdx].getOldId().toObjectId();
732 			} else if (ids != null && ids[pIdx] != null) {
733 				p = n.create(parent, n.sourcePath);
734 				p.sourceBlob = ids[pIdx];
735 			} else {
736 				continue;
737 			}
738 
739 			EditList editList;
740 			if (n instanceof ReverseCandidate
741 					&& p.sourceBlob.equals(n.sourceBlob)) {
742 				// This special case happens on ReverseCandidate forks.
743 				p.sourceText = n.sourceText;
744 				editList = new EditList(0);
745 			} else {
746 				p.loadText(reader);
747 				editList = diffAlgorithm.diff(textComparator,
748 						p.sourceText, n.sourceText);
749 			}
750 
751 			if (editList.isEmpty()) {
752 				// Ignoring whitespace (or some other special comparator) can
753 				// cause non-identical blobs to have an empty edit list. In
754 				// a case like this push the parent alone.
755 				if (n instanceof ReverseCandidate) {
756 					parents[pIdx] = p;
757 					continue;
758 				}
759 
760 				p.regionList = n.regionList;
761 				n.regionList = null;
762 				parents[pIdx] = p;
763 				break;
764 			}
765 
766 			p.takeBlame(editList, n);
767 
768 			// Only remember this parent candidate if there is at least
769 			// one region that was blamed on the parent.
770 			if (p.regionList != null) {
771 				// Reverse blame requires inverting the regions. This puts
772 				// the regions the parent deleted from us into the parent,
773 				// and retains the common regions to look at other parents
774 				// for deletions.
775 				if (n instanceof ReverseCandidate) {
776 					Region r = p.regionList;
777 					p.regionList = n.regionList;
778 					n.regionList = r;
779 				}
780 
781 				parents[pIdx] = p;
782 			}
783 		}
784 
785 		if (n instanceof ReverseCandidate) {
786 			// On a reverse blame report all deletions found in the children,
787 			// and pass on to them a copy of our region list.
788 			Candidate resultHead = null;
789 			Candidate resultTail = null;
790 
791 			for (int pIdx = 0; pIdx < pCnt; pIdx++) {
792 				Candidate p = parents[pIdx];
793 				if (p == null)
794 					continue;
795 
796 				if (p.regionList != null) {
797 					Candidate r = p.copy(p.sourceCommit);
798 					if (resultTail != null) {
799 						resultTail.queueNext = r;
800 						resultTail = r;
801 					} else {
802 						resultHead = r;
803 						resultTail = r;
804 					}
805 				}
806 
807 				if (n.regionList != null) {
808 					p.regionList = n.regionList.deepCopy();
809 					push(p);
810 				}
811 			}
812 
813 			if (resultHead != null)
814 				return result(resultHead);
815 			return false;
816 		}
817 
818 		// Push any parents that are still candidates.
819 		for (int pIdx = 0; pIdx < pCnt; pIdx++) {
820 			if (parents[pIdx] != null)
821 				push(parents[pIdx]);
822 		}
823 
824 		if (n.regionList != null)
825 			return result(n);
826 		return false;
827 	}
828 
829 	/**
830 	 * Get the revision blamed for the current region.
831 	 * <p>
832 	 * The source commit may be null if the line was blamed to an uncommitted
833 	 * revision, such as the working tree copy, or during a reverse blame if the
834 	 * line survives to the end revision (e.g. the branch tip).
835 	 *
836 	 * @return current revision being blamed.
837 	 */
838 	public RevCommit getSourceCommit() {
839 		return outCandidate.sourceCommit;
840 	}
841 
842 	/** @return current author being blamed. */
843 	public PersonIdent getSourceAuthor() {
844 		return outCandidate.getAuthor();
845 	}
846 
847 	/** @return current committer being blamed. */
848 	public PersonIdent getSourceCommitter() {
849 		RevCommit c = getSourceCommit();
850 		return c != null ? c.getCommitterIdent() : null;
851 	}
852 
853 	/** @return path of the file being blamed. */
854 	public String getSourcePath() {
855 		return outCandidate.sourcePath.getPath();
856 	}
857 
858 	/** @return rename score if a rename occurred in {@link #getSourceCommit}. */
859 	public int getRenameScore() {
860 		return outCandidate.renameScore;
861 	}
862 
863 	/**
864 	 * @return first line of the source data that has been blamed for the
865 	 *         current region. This is line number of where the region was added
866 	 *         during {@link #getSourceCommit()} in file
867 	 *         {@link #getSourcePath()}.
868 	 */
869 	public int getSourceStart() {
870 		return outRegion.sourceStart;
871 	}
872 
873 	/**
874 	 * @return one past the range of the source data that has been blamed for
875 	 *         the current region. This is line number of where the region was
876 	 *         added during {@link #getSourceCommit()} in file
877 	 *         {@link #getSourcePath()}.
878 	 */
879 	public int getSourceEnd() {
880 		Region r = outRegion;
881 		return r.sourceStart + r.length;
882 	}
883 
884 	/**
885 	 * @return first line of the result that {@link #getSourceCommit()} has been
886 	 *         blamed for providing. Line numbers use 0 based indexing.
887 	 */
888 	public int getResultStart() {
889 		return outRegion.resultStart;
890 	}
891 
892 	/**
893 	 * @return one past the range of the result that {@link #getSourceCommit()}
894 	 *         has been blamed for providing. Line numbers use 0 based indexing.
895 	 *         Because a source cannot be blamed for an empty region of the
896 	 *         result, {@link #getResultEnd()} is always at least one larger
897 	 *         than {@link #getResultStart()}.
898 	 */
899 	public int getResultEnd() {
900 		Region r = outRegion;
901 		return r.resultStart + r.length;
902 	}
903 
904 	/**
905 	 * @return number of lines in the current region being blamed to
906 	 *         {@link #getSourceCommit()}. This is always the value of the
907 	 *         expression {@code getResultEnd() - getResultStart()}, but also
908 	 *         {@code getSourceEnd() - getSourceStart()}.
909 	 */
910 	public int getRegionLength() {
911 		return outRegion.length;
912 	}
913 
914 	/**
915 	 * @return complete contents of the source file blamed for the current
916 	 *         output region. This is the contents of {@link #getSourcePath()}
917 	 *         within {@link #getSourceCommit()}. The source contents is
918 	 *         temporarily available as an artifact of the blame algorithm. Most
919 	 *         applications will want the result contents for display to users.
920 	 */
921 	public RawText getSourceContents() {
922 		return outCandidate.sourceText;
923 	}
924 
925 	/**
926 	 * @return complete file contents of the result file blame is annotating.
927 	 *         This value is accessible only after being configured and only
928 	 *         immediately before the first call to {@link #next()}. Returns
929 	 *         null if the path does not exist.
930 	 * @throws IOException
931 	 *             repository cannot be read.
932 	 * @throws IllegalStateException
933 	 *             {@link #next()} has already been invoked.
934 	 */
935 	public RawText getResultContents() throws IOException {
936 		return queue != null ? queue.sourceText : null;
937 	}
938 
939 	/**
940 	 * Release the current blame session.
941 	 *
942 	 * @since 4.0
943 	 */
944 	@Override
945 	public void close() {
946 		revPool.close();
947 		queue = null;
948 		outCandidate = null;
949 		outRegion = null;
950 	}
951 
952 	private boolean find(RevCommit commit, PathFilter path) throws IOException {
953 		treeWalk.setFilter(path);
954 		treeWalk.reset(commit.getTree());
955 		if (treeWalk.next() && isFile(treeWalk.getRawMode(0))) {
956 			treeWalk.getObjectId(idBuf, 0);
957 			return true;
958 		}
959 		return false;
960 	}
961 
962 	private static final boolean isFile(int rawMode) {
963 		return (rawMode & TYPE_MASK) == TYPE_FILE;
964 	}
965 
966 	private DiffEntry findRename(RevCommit parent, RevCommit commit,
967 			PathFilter path) throws IOException {
968 		if (renameDetector == null)
969 			return null;
970 
971 		treeWalk.setFilter(TreeFilter.ANY_DIFF);
972 		treeWalk.reset(parent.getTree(), commit.getTree());
973 		renameDetector.reset();
974 		renameDetector.addAll(DiffEntry.scan(treeWalk));
975 		for (DiffEntry ent : renameDetector.compute()) {
976 			if (isRename(ent) && ent.getNewPath().equals(path.getPath()))
977 				return ent;
978 		}
979 		return null;
980 	}
981 
982 	private static boolean isRename(DiffEntry ent) {
983 		return ent.getChangeType() == ChangeType.RENAME
984 				|| ent.getChangeType() == ChangeType.COPY;
985 	}
986 }