変更履歴(VCS)メトリクス
The big_code_analysis.vcs submodule ranks files and scores commits by change-history risk: signals derived from git history rather than the source AST. It is the Python analogue of the bca vcs CLI command, and the same Rust engine backs both, so the returned dicts match the CLI’s structured output field-for-field. This page is for a caller who already knows what the signals mean and wants the Python call shapes.
from big_code_analysis import vcs
report = vcs.rank("path/to/repo", top=20)
trend = vcs.trend("path/to/repo", points=6)
commit = vcs.commit("path/to/repo", commit="HEAD")
diff = vcs.score_diff(unified_diff_text)
4 つのエントリポイントは bca vcs のサブコマンドに対応します。vcs.rank はファイルをランク付けし(bca vcs)、vcs.trend はそのランキングを時間軸に沿ってサンプリングし(bca vcs trend)、vcs.commit は 1 つのコミットをスコアリングし(bca vcs commit)、vcs.score_diff は素の unified diff をスコアリングします(bca vcs commit --diff)。シグナル、複合リスクスコア、その背後にある欠陥予測の文献については CLI の章を参照してください。このページでは Python 側のインターフェースを扱います。
これは、単一ファイルのメトリクスに vcs ブロックを付加する analyze(..., vcs=True) とは別物です。vcs サブモジュールはリポジトリ全体の履歴を一度だけ走査するため、ランク付けにはこちらを優先してください。analyze のキーワード引数は、ファイルの AST メトリクスと並べて変更履歴の数値が欲しい場合にのみ使います。リポジトリ内のファイル全体で履歴走査を償却する analyze_batch(..., vcs=True) の経路についてはバッチ処理を参照してください。
ファイルのランク付け
vcs.rank(repo_path, *, options=None, top=None, no_cache=False, cache_dir=None) は、対象範囲内のすべてのファイルをリスク降順でランク付けし、VcsReportDict を返します。呼び出しごとに変わるキーワード専用の設定は rank にあり、trend や commit と共有される履歴走査の設定は共通の Options オブジェクト(後述)にあります。
from big_code_analysis import vcs
report = vcs.rank("path/to/repo", top=20)
print(f"long window: {report['long_window_days']} days")
print(f"recent window: {report['recent_window_days']} days")
for ranked in report["files"]:
block = ranked["vcs"]
print(f"{block['risk_score']:6.2f} {ranked['path']}")
top はランキングに残すファイル数の上限を指定します。0 または None はすべてを残します。レポートは、解決済みのウィンドウ長と risk_score_version / vcs_schema_version スタンプを(各ファイルの vcs ブロックではなく)トップレベルに一度だけ持ちます。files リストは vcs.risk_score の降順で並び、計算された場合はリポジトリのバスファクターサマリーを vcs_aggregate キーが保持します。
aggregate = report.get("vcs_aggregate")
if aggregate is not None:
bus = aggregate["bus_factor"]
print(f"repo bus factor: {bus['repo']['bus_factor']}")
コミットのスコアリング
vcs.commit(repo_path, *, commit="HEAD", options=None) は、単一のコミットを第一親に対する just-in-time(コミットレベル)リスクの観点でスコアリングし、JitCommitReportDict を返します。commit 引数には任意の git リビジョン表記("HEAD"、"HEAD~3"、ブランチ、タグ、SHA)を指定できます。
from big_code_analysis import vcs
report = vcs.commit("path/to/repo", commit="HEAD")
print(f"risk score: {report['risk_score']}")
print(f"is merge: {report['commit']['is_merge']}")
size = report["features"]["size"]
print(f"+{size['lines_added']} -{size['lines_deleted']} "
f"across {size['files_touched']} files")
# 各特徴グループが順序スコアに与える符号付きの寄与。
for group, value in report["contributions"].items():
print(f" {group:<11} {value}")
このスコアは順序尺度です。コミット同士のランク付けや、リポジトリ自身の分布との比較には使えますが、その大きさを確率として読んではいけません。contributions ブロックは各特徴グループの符号付き寄与を報告するため、コミットが 「なぜ」 その順位になったのかを利用側が確認できます。
任意の diff のスコアリング
vcs.score_diff(diff) scores a git-style unified diff that has not been committed yet, the shape a pre-commit hook or code-review bot works with. It returns a JitDiffReportDict.
import subprocess
from big_code_analysis import vcs
staged = subprocess.run(
["git", "diff", "--cached"],
capture_output=True, text=True, check=True,
).stdout
report = vcs.score_diff(staged)
print(f"partial risk: {report['partial_risk_score']}")
素の diff には作者・親コミット・ファイル履歴の情報が含まれないため、source はリテラルの "diff" になり、計算可能なのは size と diffusion のグループだけで、partial_risk_score はコミットの risk_score と比較できません。history、experience、purpose の各グループはゼロではなく、欠落しています。
トレンドのサンプリング
vcs.trend(repo_path, *, options=None, points=12, span=None, top=None, top_deltas=None) は、ファイルのランキングを複数の時点でサンプリングし、VcsTrendDict を返します。各時点はその瞬間のメインラインの先頭に改めてアンカーされるため、この系列は現在のランキングを過去に投影し直したものではなく、真の歴史的スナップショットの並びになります。
from big_code_analysis import vcs
trend = vcs.trend("path/to/repo", points=6, span="6mo", top_deltas=10)
# サンプリング時刻。古い順(Unix 秒)。
print("sampled at:", trend["as_of_points"])
# 期間中に最も悪化したファイル。
for delta in trend["deltas"]["regressed"]:
print(f" +{delta['delta']:.2f} {delta['path']}")
The trend contains points samples, with a minimum of 2. They cover the interval specified by span (default 12mo) and end at options.as_of. The files map aligns each file’s series 1:1 with as_of_points, with a None element where the file did not yet exist. The deltas summary splits files into improved and regressed lists; top_deltas trims each list and top caps how many files are kept.
共通オプション
The three repository-walking entry points (rank, trend, and commit) accept the same vcs.Options object, so one configuration can drive a rank plus a trend pass without restating the common knobs. Every field is keyword-only and optional, and the defaults reproduce the bca vcs CLI defaults, so Options() matches the default ranking.
from datetime import datetime, timezone
from big_code_analysis import vcs
options = vcs.Options(
long_window="2y",
recent_window="60d",
risk_formula="percentile",
file_types=["rs", "py"],
as_of=datetime(2026, 1, 1, tzinfo=timezone.utc),
)
report = vcs.rank("path/to/repo", options=options, top=20)
拡張されたオプションのキーワード引数(issue #619)は、それぞれ単なる文字列以外の値も受け付けます:
file_typesselects which files to rank:"metrics"(the default: only files bca computes metrics for),"all"(every tracked text file), a comma-separated extension allow-list ("rs,py"), or aSequence[str]of extensions (["rs", "py"]).as_ofpins the reference “now” for reproducible snapshots, as either adatetimeor a string (RFC 3339,@unix, or a git date). Withas_ofpinned, the ranking is computed as it stood at that moment, not against the wall clock.cache_dir(onrank, notOptions) accepts astror anyos.PathLike; apathlib.Pathpasses straight through.
The history-walk toggles mirror the CLI flags: full_history, include_merges, follow_renames (default True), exclude_bots (default True), and bot_pattern to override the bot-author regular expression. bus_factor_threshold sets the coverage fraction for the bus-factor flag (default 0.5), and emit_author_details includes SHA-256-hashed canonical author identities. author_hash_key (requires emit_author_details) hardens those digests into a keyed HMAC-SHA256, the same opt-in described under Author-detail privacy.
キャッシュ
vcs.rank keeps a persistent cache of each history walk, on by default. A cache hit is bit-identical to a fresh walk, and the time windows are recomputed against the current moment on every run. The caching section lists what invalidates an entry, and the one walk input that does not.
from big_code_analysis import vcs
# 最初の呼び出しがキャッシュを準備し、2 回目はそれを再生します。
vcs.rank("path/to/repo")
vcs.rank("path/to/repo") # 前回の走査を再利用
vcs.rank("path/to/repo", no_cache=True) # キャッシュを無視
vcs.rank("path/to/repo", cache_dir="/tmp/bca") # ディレクトリを上書き
By default the cache lives under the platform cache directory. Author identities are stored only as their SHA-256 digests, never plaintext. Hashing is pseudonymization, not anonymization: the digests are recoverable against a candidate email set. See Author-detail privacy.
GIL の解放
リポジトリを走査する呼び出し(vcs.rank、vcs.trend、および vcs.commit のコミットスコア)は、履歴走査の間 GIL を解放します(issue #620)。そのため ThreadPoolExecutor を使えば、インタープリターのロックで直列化されることなく、複数のリポジトリを並列にランク付けできます:
from concurrent.futures import ThreadPoolExecutor
from big_code_analysis import vcs
repos = ["service-a", "service-b", "service-c"]
with ThreadPoolExecutor() as pool:
reports = list(pool.map(lambda r: vcs.rank(r, top=20), repos))
これは 非同期パターン のページがファイル単位の analyze 呼び出しに適用しているのと同じパターンです。
エラー
vcs の各関数は、bca.VcsError(それ自体が ValueError)を頂点とする型付き例外階層を送出します。完全な分類と、どの呼び出しがどの型を送出するかについてはエラー処理を参照してください。
関連項目
- Change-history (VCS) metrics: the CLI chapter, with the signal definitions, the composite risk-score formula, and the defect-prediction literature behind them.
- Batch processing:
analyze_batch(..., vcs=True)attaches a per-filevcsblock while sharing one history index per repository. - Error handling: the VCS exception taxonomy.