feat: expose git blame via Repository.blame() - #19
Merged
Conversation
Enables the `blame` feature of gix (which pulls in `blob-diff`) and wraps `Repository::blame_file()` as `Repository.blame(path, rev=None)`, returning a list of `BlameHunk` objects with 1-based, inclusive line numbers to match `git blame` — gix itself counts from 0. Consecutive lines sharing a commit arrive already grouped into a hunk, so the result is typically far shorter than the file. Cost on macOS arm64: the wheel grows 1.51 MB -> 1.99 MB (+31.7%) and a cold release build goes from 26s to ~38s. The twelve new transitive crates (gix-blame, gix-imara-diff, gix-filter, gix-worktree, ...) are all pure Rust, so wheels stay self-contained and still need no C toolchain. Note that `blob-diff` brings in gix-filter, which executes a configured clean/smudge filter (Git LFS, for instance) when blaming such a file, just as git does. README now qualifies the "no subprocess" claim accordingly.
shenxianpeng
added a commit
that referenced
this pull request
Sep 1, 2026
v0.3.0 can never be released from this repository. The tag name was
consumed by an immutable release that was published and then deleted,
and GitHub reserves such names permanently ("tag name was used by an
immutable release, published releases must have a valid tag"). Turning
the setting off does not free an already-consumed name.
0.4.0 rather than 0.3.1: main now carries the blame binding (#19), which
is a feature and warrants a minor bump, and 0.3.0 never reached PyPI —
publishing a 0.3.1 with no 0.3.0 preceding it would only confuse.
Only Cargo.toml (and the lockfile) change here. Since #18 the Python
`__version__` is read from installed package metadata, so it follows
along on its own.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Wraps
gix's blame engine asRepository.blame(path, rev=None), the firstpiece of the feature-gated
gixsurface this binding has reached for.What's here
Cargo.toml— enables theblamefeature ofgix(which pulls inblob-diff→attributes).Repository.blame(path, rev=None)—revdefaults toHEAD.BlameHunk—start_line,end_line,line_count,orig_start_line,commit_id,short_id, plus a__repr__.git blame.gixcounts from 0, so passing its values straight through would have been off by
one in every hunk.
sample_repofixture, including the exactcommit attribution for a two-line file, blame at an older revision, and the
error path for a missing file.
blamemoved from"not wrapped yet" to "wrapped".
Cost
Measured on macOS arm64, release + thin LTO:
The twelve new crates (
gix-blame,gix-imara-diff,gix-filter,gix-worktree,gix-attributes,gix-dir, …) are all pure Rust — wheelsstay self-contained and still need no C toolchain.
Worth knowing: enabling the feature without calling it only costs ~1%,
because LTO strips the unreachable code. The +31.7% above is the real,
measured cost with the binding actually wired up.
One caveat, reflected in the README
blob-diffbrings ingix-filter, which will execute a configuredclean/smudge filter (Git LFS, for example) when blaming such a file — the
same thing
gitdoes. Nothing shells out to thegitCLI, so the comparisonagainst GitPython still holds, but the blanket "no subprocess" line in the
README now carries a footnote.
Verification
pytest -q→ 20 passed (16 existing + 4 new).cargo fmt --checkandcargo clippy --all-targets -- -D warningsboth clean. The README example iscopied from a real run against this repository.
Not included
repo.is_dirty()was floated alongside this, but it lives behindgix'sstatusfeature, notblame— andstatusdrags indirwalk+indexon top of
blob-diff. Separate change, separate cost.