Class BlameGenerator
- java.lang.Object
-
- org.eclipse.jgit.blame.BlameGenerator
-
- All Implemented Interfaces:
java.lang.AutoCloseable
public class BlameGenerator extends java.lang.Object implements java.lang.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 aBlameResult
usingBlameResult.create(BlameGenerator)
and incrementally construct the result withBlameResult.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 Summary
Fields Modifier and Type Field Description private DiffAlgorithm
diffAlgorithm
private MutableObjectId
idBuf
private Candidate
outCandidate
Blame is currently assigned to this source.private Region
outRegion
private Candidate
queue
Potential candidates, sorted by commit time descending.private ObjectReader
reader
private int
remaining
Number of lines that still need to be discovered.private RenameDetector
renameDetector
private Repository
repository
private PathFilter
resultPath
private RevWalk
revPool
Revision pool used to acquire commits from.private RevFlag
SEEN
Indicates the commit was put into the queue at least once.private RawTextComparator
textComparator
private TreeWalk
treeWalk
-
Constructor Summary
Constructors Constructor Description BlameGenerator(Repository repository, java.lang.String path)
Create a blame generator for the repository and path (relative to repository)
-
Method Summary
All Methods Static Methods Instance Methods Concrete Methods Modifier and Type Method Description private boolean
blameEntireRegionOnParent(Candidate n, RevCommit parent)
void
close()
BlameResult
computeBlameResult()
Execute the generator in a blocking fashion until all data is ready.private boolean
done()
private boolean
find(RevCommit commit, PathFilter path)
private DiffEntry
findRename(RevCommit parent, RevCommit commit, PathFilter path)
private static byte[]
getBytes(java.lang.String path, java.io.InputStream in, long maxLength)
private java.util.List<RevCommit>
getHeads(Repository repo, ObjectId head)
int
getRegionLength()
Get number of lines in the current region being blamed togetSourceCommit()
RenameDetector
getRenameDetector()
Obtain the RenameDetector, allowing the application to configure its settings for rename score and breaking behavior.int
getRenameScore()
Get rename scoreRepository
getRepository()
Get repositoryRawText
getResultContents()
Get complete file contents of the result file blame is annotatingint
getResultEnd()
Get one past the range of the result thatgetSourceCommit()
has been blamed for providingjava.lang.String
getResultPath()
Get result pathint
getResultStart()
Get first line of the result thatgetSourceCommit()
has been blamed for providingPersonIdent
getSourceAuthor()
Get source authorRevCommit
getSourceCommit()
Get the revision blamed for the current region.PersonIdent
getSourceCommitter()
Get source committerRawText
getSourceContents()
Get complete contents of the source file blamed for the current output regionint
getSourceEnd()
Get one past the range of the source data that has been blamed for the current regionjava.lang.String
getSourcePath()
Get source pathint
getSourceStart()
Get first line of the source data that has been blamed for the current regionprivate void
initRevPool(boolean reverse)
private static boolean
isFile(int rawMode)
private static boolean
isRename(DiffEntry ent)
RevFlag
newFlag(java.lang.String name)
Allocate a new RevFlag for use by the caller.boolean
next()
Step the blame algorithm one iteration.private Candidate
pop()
BlameGenerator
prepareHead()
Pushes HEAD, index, and working tree as appropriate for blaming the file given in the constructorBlameGenerator(Repository, String)
against HEAD.private boolean
processMerge(Candidate n)
private boolean
processOne(Candidate n)
BlameGenerator
push(java.lang.String description, byte[] contents)
Push a candidate blob onto the generator's traversal stack.BlameGenerator
push(java.lang.String description, RawText contents)
Push a candidate blob onto the generator's traversal stack.BlameGenerator
push(java.lang.String description, AnyObjectId id)
Push a candidate object onto the generator's traversal stack.private void
push(Candidate toInsert)
private void
push(Candidate.BlobCandidate toInsert)
private boolean
result(Candidate n)
BlameGenerator
reverse(AnyObjectId start, java.util.Collection<? extends ObjectId> end)
Configure the generator to compute reverse blame (history of deletes).BlameGenerator
reverse(AnyObjectId start, AnyObjectId end)
Configure the generator to compute reverse blame (history of deletes).private boolean
reverseResult(Candidate parent, Candidate source)
BlameGenerator
setDiffAlgorithm(DiffAlgorithm algorithm)
Difference algorithm to use when comparing revisions.BlameGenerator
setFollowFileRenames(boolean follow)
Enable (or disable) following file renames, on by default.BlameGenerator
setTextComparator(RawTextComparator comparator)
Text comparator to use when comparing revisions.private boolean
split(Candidate parent, Candidate source)
private boolean
splitBlameWithParent(Candidate n, RevCommit parent)
-
-
-
Field Detail
-
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 Detail
-
BlameGenerator
public BlameGenerator(Repository repository, java.lang.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 Detail
-
initRevPool
private void initRevPool(boolean reverse)
-
getRepository
public Repository getRepository()
Get repository- Returns:
- repository being scanned for revision history
-
getResultPath
public java.lang.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
- aDiffAlgorithm
- Returns:
this
-
setTextComparator
public BlameGenerator setTextComparator(RawTextComparator comparator)
Text comparator to use when comparing revisions.- Parameters:
comparator
- aRawTextComparator
- 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 ofgit 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
ifsetFollowFileRenames(false)
.
-
push
public BlameGenerator push(java.lang.String description, byte[] contents) throws java.io.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:
java.io.IOException
- the repository cannot be read.
-
push
public BlameGenerator push(java.lang.String description, RawText contents) throws java.io.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:
java.io.IOException
- the repository cannot be read.
-
prepareHead
public BlameGenerator prepareHead() throws NoHeadException, java.io.IOException
Pushes HEAD, index, and working tree as appropriate for blaming the file given in the constructorBlameGenerator(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 HEADjava.io.IOException
- if an error occurs- Since:
- 5.6
-
getHeads
private java.util.List<RevCommit> getHeads(Repository repo, ObjectId head) throws NoWorkTreeException, java.io.IOException
- Throws:
NoWorkTreeException
java.io.IOException
-
getBytes
private static byte[] getBytes(java.lang.String path, java.io.InputStream in, long maxLength) throws java.io.IOException
- Throws:
java.io.IOException
-
push
public BlameGenerator push(java.lang.String description, AnyObjectId id) throws java.io.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:
java.io.IOException
- the repository cannot be read.
-
reverse
public BlameGenerator reverse(AnyObjectId start, AnyObjectId end) throws java.io.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 aspush(String, AnyObjectId)
to begin blame traversal from the commit named bystart
walking forwards through history untilend
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:
java.io.IOException
- the repository cannot be read.
-
reverse
public BlameGenerator reverse(AnyObjectId start, java.util.Collection<? extends ObjectId> end) throws java.io.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 aspush(String, AnyObjectId)
to begin blame traversal from the commit named bystart
walking forwards through history untilend
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:
java.io.IOException
- the repository cannot be read.
-
newFlag
public RevFlag newFlag(java.lang.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 java.io.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:
java.io.IOException
- the repository cannot be read.
-
next
public boolean next() throws java.io.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:
java.io.IOException
- repository cannot be read.
-
done
private boolean done()
-
result
private boolean result(Candidate n) throws java.io.IOException
- Throws:
java.io.IOException
-
reverseResult
private boolean reverseResult(Candidate parent, Candidate source) throws java.io.IOException
- Throws:
java.io.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 java.io.IOException
- Throws:
java.io.IOException
-
splitBlameWithParent
private boolean splitBlameWithParent(Candidate n, RevCommit parent) throws java.io.IOException
- Throws:
java.io.IOException
-
split
private boolean split(Candidate parent, Candidate source) throws java.io.IOException
- Throws:
java.io.IOException
-
processMerge
private boolean processMerge(Candidate n) throws java.io.IOException
- Throws:
java.io.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 java.lang.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 filegetSourcePath()
.
-
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 filegetSourcePath()
.
-
getResultStart
public int getResultStart()
Get first line of the result thatgetSourceCommit()
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 thatgetSourceCommit()
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 thangetResultStart()
.
-
getRegionLength
public int getRegionLength()
Get number of lines in the current region being blamed togetSourceCommit()
- Returns:
- number of lines in the current region being blamed to
getSourceCommit()
. This is always the value of the expressiongetResultEnd() - getResultStart()
, but alsogetSourceEnd() - 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()
withingetSourceCommit()
. 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 java.io.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:
java.io.IOException
- repository cannot be read.java.lang.IllegalStateException
-next()
has already been invoked.
-
close
public void close()
Release the current blame session.
- Specified by:
close
in interfacejava.lang.AutoCloseable
- Since:
- 4.0
-
find
private boolean find(RevCommit commit, PathFilter path) throws java.io.IOException
- Throws:
java.io.IOException
-
isFile
private static final boolean isFile(int rawMode)
-
findRename
private DiffEntry findRename(RevCommit parent, RevCommit commit, PathFilter path) throws java.io.IOException
- Throws:
java.io.IOException
-
isRename
private static boolean isRename(DiffEntry ent)
-
-