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