Please check the build logs for more information.
See Builds for ideas on how to fix a failed build, or Metadata for how to configure docs.rs builds.
If you believe this is docs.rs' fault, open an issue.
rpo
Git-contribution analysis. Walks a repository and produces tabular data (via polars) about commits, file changes, and per-line authorship over time — who wrote what, when, and how much of it survives today.
For the command-line tool and local web UI built on this crate, see
rpo-cli. For Python bindings, see rpo on PyPI.
use RepoAnalyzer;
let analysis = open?
.with_exclude_globs
.all?;
println!;
println!;
# Ok::
How it works
[RepoAnalyzer::open] returns a [Builder]. Configure the walk with the
with_* methods, then call a terminal to get DataFrames back:
| Terminal | Returns |
|---|---|
[commits] |
one row per commit |
[file_changes] |
one row per (commit, file) touched |
[blame] |
per-line authorship at HEAD |
[blame_over_time] |
per-line authorship at each snapshot |
[all] |
every frame, from a single walk |
Prefer all() when you need more than one frame — it walks once.
use ;
// Only the source tree, first-parent history, monthly blame snapshots.
let timeline = open?
.with_include_globs
.with_blame_snapshots
.blame_over_time?;
# Ok::
Reports
[rpo::reports] has ready-made transforms over those frames — summary,
per-author and per-file activity, and blame timelines. [rpo::bus_factor]
computes knowledge concentration three ways (threshold, ABF, and JBF).
use ;
let analysis = open?.all?;
let report = author_report?;
# Ok::
They are ordinary polars, so writing your own is expected — the frames are the real API.
Conventions
Two things to know before reading a frame:
Identities are canonicalized. .mailmap is applied automatically,
and the result lands in canonical_author_name,
canonical_committer_email, and so on. Group on those, not the raw
author_name — otherwise one person committing under several addresses
counts as several people. [IdentityMap] adds your own aliases on top.
Datetimes are UTC milliseconds. Every time column is
Datetime(TimeUnit::Milliseconds) with no timezone.
Columns, by frame:
- commits —
sha,short_sha,author_name,author_email,committer_name,committer_email,canonical_*,author_time,commit_time,parent_count,is_merge,message_subject,files_changed,insertions,deletions - file_changes —
sha,commit_time,canonical_*,path,old_path,change_kind,insertions,deletions,extension,is_generated,is_vendored - blame —
snapshot_sha,snapshot_time,snapshot_label,path,start_line,line_count,commit_sha,commit_time,canonical_*,extension,is_generated,is_vendored
Path filters ([with_include_globs], [with_exclude_globs]) are applied
during the walk, so excluded paths never enter any frame and cost nothing
downstream. Patterns are globset syntax matched against the full
repo-relative path; separators are not special, so *.rs matches
src/web/pages.rs. Exclusion wins over inclusion.
Streaming
Repositories large enough that the blame timeline will not fit in memory
can stream it instead, one snapshot at a time, via
[blame_over_time_streaming] and a [FrameSink] — [ParquetSink],
[ParquetDirSink], or DuckDbSink.
Features
| Feature | Default | Description |
|---|---|---|
backend-gix |
on | Git access via gix |
backend-git2 |
off | git2 backend — a compile-time seam only; every method is todo!() |
sink-duckdb |
off | DuckDbSink, for streaming blame into DuckDB |
Exactly one backend must be enabled, or the crate fails to compile.
sink-duckdb is off by default because duckdb is statically bundled and
compiles a large C++ amalgamation, roughly doubling a clean build.
# with the DuckDB sink
= { = "0.1", = ["sink-duckdb"] }
Minimum supported Rust version
1.95.