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