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