Class BlameGenerator

java.lang.Object
org.eclipse.jgit.blame.BlameGenerator
All Implemented Interfaces:
AutoCloseable

public class BlameGenerator extends Object implements AutoCloseable
Generate author information for lines based on a provided file.

Applications that want a simple one-shot computation of blame for a file should use computeBlameResult() to prepare the entire result in one method call. This may block for significant time as the history of the repository must be traversed until information is gathered for every line.

Applications that want more incremental update behavior may use either the raw next() streaming approach supported by this class, or construct a BlameResult using BlameResult.create(BlameGenerator) and incrementally construct the result with BlameResult.computeNext().

This class is not thread-safe.

An instance of BlameGenerator can only be used once. To blame multiple files the application must create a new BlameGenerator.

During blame processing there are two files involved:

  • result - The file whose lines are being examined. This is the revision the user is trying to view blame/annotation information alongside of.
  • source - The file that was blamed with supplying one or more lines of data into result. The source may be a different file path (due to copy or rename). Source line numbers may differ from result line numbers due to lines being added/removed in intermediate revisions.

The blame algorithm is implemented by initially assigning responsibility for all lines of the result to the starting commit. A difference against the commit's ancestor is computed, and responsibility is passed to the ancestor commit for any lines that are common. The starting commit is blamed only for the lines that do not appear in the ancestor, if any. The loop repeats using the ancestor, until there are no more lines to acquire information on, or the file's creation point is discovered in history.

  • Field Details

    • repository

      private final Repository repository
    • resultPath

      private final PathFilter resultPath
    • idBuf

      private final MutableObjectId idBuf
    • revPool

      private RevWalk revPool
      Revision pool used to acquire commits from.
    • SEEN

      private RevFlag SEEN
      Indicates the commit was put into the queue at least once.
    • reader

      private ObjectReader reader
    • treeWalk

      private TreeWalk treeWalk
    • diffAlgorithm

      private DiffAlgorithm diffAlgorithm
    • textComparator

      private RawTextComparator textComparator
    • renameDetector

      private RenameDetector renameDetector
    • queue

      private Candidate queue
      Potential candidates, sorted by commit time descending.
    • remaining

      private int remaining
      Number of lines that still need to be discovered.
    • outCandidate

      private Candidate outCandidate
      Blame is currently assigned to this source.
    • outRegion

      private Region outRegion
  • Constructor Details

    • BlameGenerator

      public BlameGenerator(Repository repository, String path)
      Create a blame generator for the repository and path (relative to repository)
      Parameters:
      repository - repository to access revision data from.
      path - initial path of the file to start scanning (relative to the repository).
  • Method Details

    • initRevPool

      private void initRevPool(boolean reverse)
    • getRepository

      public Repository getRepository()
      Get repository
      Returns:
      repository being scanned for revision history
    • getResultPath

      public String getResultPath()
      Get result path
      Returns:
      path file path being processed
    • setDiffAlgorithm

      public BlameGenerator setDiffAlgorithm(DiffAlgorithm algorithm)
      Difference algorithm to use when comparing revisions.
      Parameters:
      algorithm - a DiffAlgorithm
      Returns:
      this
    • setTextComparator

      public BlameGenerator setTextComparator(RawTextComparator comparator)
      Text comparator to use when comparing revisions.
      Parameters:
      comparator - a RawTextComparator
      Returns:
      this
    • setFollowFileRenames

      public BlameGenerator setFollowFileRenames(boolean follow)
      Enable (or disable) following file renames, on by default.

      If true renames are followed using the standard FollowFilter behavior used by RevWalk (which matches git log --follow in the C implementation). This is not the same as copy/move detection as implemented by the C implementation's of git blame -M -C.

      Parameters:
      follow - enable following.
      Returns:
      this
    • getRenameDetector

      @Nullable public RenameDetector getRenameDetector()
      Obtain the RenameDetector, allowing the application to configure its settings for rename score and breaking behavior.
      Returns:
      the rename detector, or null if setFollowFileRenames(false).
    • push

      public BlameGenerator push(String description, byte[] contents) throws IOException
      Push a candidate blob onto the generator's traversal stack.

      Candidates should be pushed in history order from oldest-to-newest. Applications should push the starting commit first, then the index revision (if the index is interesting), and finally the working tree copy (if the working tree is interesting).

      Parameters:
      description - description of the blob revision, such as "Working Tree".
      contents - contents of the file.
      Returns:
      this
      Throws:
      IOException - the repository cannot be read.
    • push

      public BlameGenerator push(String description, RawText contents) throws IOException
      Push a candidate blob onto the generator's traversal stack.

      Candidates should be pushed in history order from oldest-to-newest. Applications should push the starting commit first, then the index revision (if the index is interesting), and finally the working tree copy (if the working tree is interesting).

      Parameters:
      description - description of the blob revision, such as "Working Tree".
      contents - contents of the file.
      Returns:
      this
      Throws:
      IOException - the repository cannot be read.
    • prepareHead

      public BlameGenerator prepareHead() throws NoHeadException, IOException
      Pushes HEAD, index, and working tree as appropriate for blaming the file given in the constructor BlameGenerator(Repository, String) against HEAD. Includes special handling in case the file is in conflict state from an unresolved merge conflict.
      Returns:
      this
      Throws:
      NoHeadException - if the repository has no HEAD
      IOException - if an error occurs
      Since:
      5.6
    • getHeads

      private List<RevCommit> getHeads(Repository repo, ObjectId head) throws NoWorkTreeException, IOException
      Throws:
      NoWorkTreeException
      IOException
    • getBytes

      private static byte[] getBytes(String path, InputStream in, long maxLength) throws IOException
      Throws:
      IOException
    • push

      public BlameGenerator push(String description, AnyObjectId id) throws IOException
      Push a candidate object onto the generator's traversal stack.

      Candidates should be pushed in history order from oldest-to-newest. Applications should push the starting commit first, then the index revision (if the index is interesting), and finally the working tree copy (if the working tree is interesting).

      Parameters:
      description - description of the blob revision, such as "Working Tree".
      id - may be a commit or a blob.
      Returns:
      this
      Throws:
      IOException - the repository cannot be read.
    • reverse

      public BlameGenerator reverse(AnyObjectId start, AnyObjectId end) throws IOException
      Configure the generator to compute reverse blame (history of deletes).

      This method is expensive as it immediately runs a RevWalk over the history spanning the expression start..end (end being more recent than start) and then performs the equivalent operation as push(String, AnyObjectId) to begin blame traversal from the commit named by start walking forwards through history until end blaming line deletions.

      A reverse blame may produce multiple sources for the same result line, each of these is a descendant commit that removed the line, typically this occurs when the same deletion appears in multiple side branches such as due to a cherry-pick. Applications relying on reverse should use BlameResult as it filters these duplicate sources and only remembers the first (oldest) deletion.

      Parameters:
      start - oldest commit to traverse from. The result file will be loaded from this commit's tree.
      end - most recent commit to stop traversal at. Usually an active branch tip, tag, or HEAD.
      Returns:
      this
      Throws:
      IOException - the repository cannot be read.
    • reverse

      public BlameGenerator reverse(AnyObjectId start, Collection<? extends ObjectId> end) throws IOException
      Configure the generator to compute reverse blame (history of deletes).

      This method is expensive as it immediately runs a RevWalk over the history spanning the expression start..end (end being more recent than start) and then performs the equivalent operation as push(String, AnyObjectId) to begin blame traversal from the commit named by start walking forwards through history until end blaming line deletions.

      A reverse blame may produce multiple sources for the same result line, each of these is a descendant commit that removed the line, typically this occurs when the same deletion appears in multiple side branches such as due to a cherry-pick. Applications relying on reverse should use BlameResult as it filters these duplicate sources and only remembers the first (oldest) deletion.

      Parameters:
      start - oldest commit to traverse from. The result file will be loaded from this commit's tree.
      end - most recent commits to stop traversal at. Usually an active branch tip, tag, or HEAD.
      Returns:
      this
      Throws:
      IOException - the repository cannot be read.
    • newFlag

      public RevFlag newFlag(String name)
      Allocate a new RevFlag for use by the caller.
      Parameters:
      name - unique name of the flag in the blame context.
      Returns:
      the newly allocated flag.
      Since:
      3.4
    • computeBlameResult

      public BlameResult computeBlameResult() throws IOException
      Execute the generator in a blocking fashion until all data is ready.
      Returns:
      the complete result. Null if no file exists for the given path.
      Throws:
      IOException - the repository cannot be read.
    • next

      public boolean next() throws IOException
      Step the blame algorithm one iteration.
      Returns:
      true if the generator has found a region's source. The getSource* and getResultStart(), getResultEnd() methods can be used to inspect the region found. False if there are no more regions to describe.
      Throws:
      IOException - repository cannot be read.
    • done

      private boolean done()
    • result

      private boolean result(Candidate n) throws IOException
      Throws:
      IOException
    • reverseResult

      private boolean reverseResult(Candidate parent, Candidate source) throws IOException
      Throws:
      IOException
    • pop

      private Candidate pop()
    • push

      private void push(Candidate.BlobCandidate toInsert)
    • push

      private void push(Candidate toInsert)
    • processOne

      private boolean processOne(Candidate n) throws IOException
      Throws:
      IOException
    • blameEntireRegionOnParent

      private boolean blameEntireRegionOnParent(Candidate n, RevCommit parent)
    • splitBlameWithParent

      private boolean splitBlameWithParent(Candidate n, RevCommit parent) throws IOException
      Throws:
      IOException
    • split

      private boolean split(Candidate parent, Candidate source) throws IOException
      Throws:
      IOException
    • processMerge

      private boolean processMerge(Candidate n) throws IOException
      Throws:
      IOException
    • getSourceCommit

      public RevCommit getSourceCommit()
      Get the revision blamed for the current region.

      The source commit may be null if the line was blamed to an uncommitted revision, such as the working tree copy, or during a reverse blame if the line survives to the end revision (e.g. the branch tip).

      Returns:
      current revision being blamed.
    • getSourceAuthor

      public PersonIdent getSourceAuthor()
      Get source author
      Returns:
      current author being blamed
    • getSourceCommitter

      public PersonIdent getSourceCommitter()
      Get source committer
      Returns:
      current committer being blamed
    • getSourcePath

      public String getSourcePath()
      Get source path
      Returns:
      path of the file being blamed
    • getRenameScore

      public int getRenameScore()
      Get rename score
      Returns:
      rename score if a rename occurred in getSourceCommit()
    • getSourceStart

      public int getSourceStart()
      Get first line of the source data that has been blamed for the current region
      Returns:
      first line of the source data that has been blamed for the current region. This is line number of where the region was added during getSourceCommit() in file getSourcePath().
    • getSourceEnd

      public int getSourceEnd()
      Get one past the range of the source data that has been blamed for the current region
      Returns:
      one past the range of the source data that has been blamed for the current region. This is line number of where the region was added during getSourceCommit() in file getSourcePath().
    • getResultStart

      public int getResultStart()
      Get first line of the result that getSourceCommit() has been blamed for providing
      Returns:
      first line of the result that getSourceCommit() has been blamed for providing. Line numbers use 0 based indexing.
    • getResultEnd

      public int getResultEnd()
      Get one past the range of the result that getSourceCommit() has been blamed for providing
      Returns:
      one past the range of the result that getSourceCommit() has been blamed for providing. Line numbers use 0 based indexing. Because a source cannot be blamed for an empty region of the result, getResultEnd() is always at least one larger than getResultStart().
    • getRegionLength

      public int getRegionLength()
      Get number of lines in the current region being blamed to getSourceCommit()
      Returns:
      number of lines in the current region being blamed to getSourceCommit(). This is always the value of the expression getResultEnd() - getResultStart(), but also getSourceEnd() - getSourceStart().
    • getSourceContents

      public RawText getSourceContents()
      Get complete contents of the source file blamed for the current output region
      Returns:
      complete contents of the source file blamed for the current output region. This is the contents of getSourcePath() within getSourceCommit(). The source contents is temporarily available as an artifact of the blame algorithm. Most applications will want the result contents for display to users.
    • getResultContents

      public RawText getResultContents() throws IOException
      Get complete file contents of the result file blame is annotating
      Returns:
      complete file contents of the result file blame is annotating. This value is accessible only after being configured and only immediately before the first call to next(). Returns null if the path does not exist.
      Throws:
      IOException - repository cannot be read.
      IllegalStateException - next() has already been invoked.
    • close

      public void close()

      Release the current blame session.

      Specified by:
      close in interface AutoCloseable
      Since:
      4.0
    • find

      private boolean find(RevCommit commit, PathFilter path) throws IOException
      Throws:
      IOException
    • isFile

      private static final boolean isFile(int rawMode)
    • findRename

      private DiffEntry findRename(RevCommit parent, RevCommit commit, PathFilter path) throws IOException
      Throws:
      IOException
    • isRename

      private static boolean isRename(DiffEntry ent)