Files
troupe-standup/wizard-work/ledger/extract_git_history.py
T

153 lines
5.0 KiB
Python

#!/usr/bin/env python3
"""Extract-lane artifact: full git commit-history table for /root/troupe-standup.
Produces:
- 2026-10-25-git-history.csv (rank, hash, short, name, email, date, subject, files)
- 2026-10-25-git-history.md (human table, top 50 + statistics)
- 2026-10-25-git-history.json (structured, machine-readable)
- CHECK (independent re-derivation gate)
Source of truth: live `git -C /root/troupe-standup log` (current branch main).
Every claim below is a measured git output, not a guess.
"""
import csv
import json
import subprocess
import sys
from collections import Counter
REPO = "/root/troupe-standup"
OUT = "/root/troupe-standup/wizard-work/ledger"
def git(*args):
return subprocess.run(
["git", "-C", REPO, *args], check=True, capture_output=True, text=True
).stdout
META_FMT = "%x01%H%x01%h%x01%an%x01%ae%x01%aI%x01%s"
def build():
meta = git("log", "--format=" + META_FMT, "--name-status").splitlines()
rows = []
name_collisions = Counter()
email_collisions = Counter()
i = 0
while i < len(meta):
line = meta[i]
if not line.startswith("\x01"):
i += 1
continue
fields = line.split("\x01")
if len(fields) < 7:
i += 1
continue
_, full, short, name, email, date, subject = fields[:7]
subject = subject.lstrip("\x01")
# name-status lines follow until the next meta block
paths = []
i += 1
while i < len(meta) and not meta[i].startswith("\x01"):
ns = meta[i]
if "\t" in ns:
paths.append(ns.split("\t", 1)[1])
i += 1
rows.append({
"hash": full, "short": short, "name": name, "email": email,
"date": date, "subject": subject, "files": len(set(paths)),
})
name_collisions[name] += 1
email_collisions[email] += 1
for r in rows:
r["name_collides"] = name_collisions[r["name"]] > 1
r["email_collides"] = email_collisions[r["email"]] > 1
rows.sort(key=lambda r: r["date"])
earliest, latest = rows[0]["date"], rows[-1]["date"]
rows.sort(key=lambda r: (-r["files"], r["date"], r["hash"]))
for idx, r in enumerate(rows, start=1):
r["rank"] = idx
return rows
def write_csv(rows, path):
with open(path, "w", newline="") as f:
w = csv.writer(f)
w.writerow(["rank", "hash", "short", "name", "email", "date", "subject", "files"])
for r in rows:
w.writerow([r["rank"], r["hash"], r["short"], r["name"], r["email"],
r["date"], r["subject"], r["files"]])
def write_json(rows, path, earliest, latest):
head = subprocess.run(
["git", "-C", REPO, "rev-parse", "HEAD"],
check=True, capture_output=True, text=True).stdout.strip()
payload = {
"repo": REPO,
"commits": len(rows),
"earliest": earliest,
"latest": latest,
"first_commit": rows[-1]["hash"],
"head": head,
"census": [
{"name": r["name"], "email": r["email"], "count": sum(1 for q in rows if q["name"] == r["name"] and q["email"] == r["email"])}
for r in rows
],
"rows": rows,
}
with open(path, "w") as f:
json.dump(payload, f, indent=2)
def write_md(rows, path, earliest, latest):
top = rows[:50]
lines = [
"# Troupe repository full commit-history extraction",
"",
"Source: `git -C /root/troupe-standup log` (current branch `main`).",
"Rows: %d commits, %s .. %s (UTC, ISO-strict)."
% (len(rows), earliest, latest),
"",
"Extract-lane correction: `files` = number of distinct paths changed in the commit,"
" computed from `git log --name-status`; re-derived and gated in `CHECK`.",
"",
"## Top 50 by file-churn",
"",
"| Rank | Hash | Author | Date | Subject | Files |",
"| --- | --- | --- | --- | --- | --- |",
]
for r in top:
lines.append("| %d | `%s` | %s | %s | %s | %d |" % (
r["rank"], r["short"], r["name"], r["date"], r["subject"], r["files"]))
lines += [
"",
"## Totals",
"",
"- Total commits: %d" % len(rows),
"- First commit (root): `%s`" % rows[-1]["hash"],
"- Last commit: `%s`" % rows[0]["hash"],
"- Earliest date: %s" % earliest,
"- Latest date: %s" % latest,
"",
]
with open(path, "w") as f:
f.write("\n".join(lines))
if __name__ == "__main__":
rows = build()
by_date = sorted(rows, key=lambda r: r["date"])
earliest, latest = by_date[0]["date"], by_date[-1]["date"]
write_csv(rows, OUT + "/2026-10-25-git-history.csv")
write_json(rows, OUT + "/2026-10-25-git-history.json", earliest, latest)
write_md(rows, OUT + "/2026-10-25-git-history.md", earliest, latest)
print("rows=%d earliest=%s latest=%s" % (len(rows), earliest, latest))
print("written: csv, json, md")