119 lines
4.1 KiB
Python
119 lines
4.1 KiB
Python
#!/usr/bin/env python3
|
|
"""Extract the git commit census of the troupe-standup repo.
|
|
|
|
Lane: Extract -- data extraction and tables.
|
|
|
|
Source (single source of truth):
|
|
git (local clone, branch main) -- every commit reachable from HEAD.
|
|
|
|
Artifacts written beside this script (all re-derivable, none hand-typed):
|
|
- commits-census.csv commits per author (unique author name)
|
|
- commits-by-day.csv commits per calendar day (author date)
|
|
- commits-full.csv one row per commit: hash, author, email, date, subject
|
|
- README-commits-census.md
|
|
|
|
Verification: verify_commits_census.py re-derives every table cell from git
|
|
and exits 0 iff the committed tables match. The CHECK file runs that script.
|
|
|
|
Run: python3 extract_commits_census.py
|
|
"""
|
|
import csv
|
|
import os
|
|
import subprocess
|
|
import sys
|
|
from collections import Counter
|
|
|
|
REPO = "/root/troupe-standup"
|
|
OUT = "/root/troupe-standup/wizard-work/ledger"
|
|
|
|
# Pinned revision so the census is deterministic: other agents keep pushing to
|
|
# main while this artifact is being produced. Verify against this hash only.
|
|
PIN = "d8d8e3292db6c435b174e39a3ca18792874f9c19"
|
|
REPO_HEAD = "HEAD" # value only; PIN below overrides where git is invoked
|
|
|
|
# Separator guaranteed not to appear in any git field value.
|
|
SEP = "\x1f"
|
|
# Format: hash \x1f author \x1f email \x1f author-date \x1f subject
|
|
FMT = "%H" + SEP + "%an" + SEP + "%ae" + SEP + "%aI" + SEP + "%s"
|
|
|
|
|
|
def git_log():
|
|
cmd = ["git", "-C", REPO, "log",
|
|
"--pretty=format:" + FMT, "--date=iso-strict", PIN]
|
|
out = subprocess.run(cmd, check=True, capture_output=True, text=True).stdout
|
|
rows = []
|
|
for c in out.splitlines():
|
|
if not c:
|
|
continue
|
|
h, an, ae, ad, s = c.split(SEP)
|
|
rows.append({"hash": h, "author": an, "email": ae,
|
|
"author_date": ad, "subject": s})
|
|
return rows
|
|
|
|
|
|
def write_csv(name, header, data):
|
|
path = os.path.join(OUT, name)
|
|
with open(path, "w", newline="", encoding="utf-8") as f:
|
|
w = csv.writer(f)
|
|
w.writerow(header)
|
|
w.writerows(data)
|
|
return path
|
|
|
|
|
|
def main():
|
|
rows = git_log()
|
|
total = len(rows)
|
|
by_author = Counter(r["author"] for r in rows)
|
|
by_day = Counter(r["author_date"][:10] for r in rows) # YYYY-MM-DD
|
|
|
|
census_rows = sorted(by_author.items(), key=lambda kv: (-kv[1], kv[0]))
|
|
day_rows = sorted(by_day.items(), key=lambda kv: kv[0])
|
|
|
|
os.makedirs(OUT, exist_ok=True)
|
|
|
|
write_csv("commits-census.csv",
|
|
["author", "commits"], [(a, c) for a, c in census_rows])
|
|
write_csv("commits-by-day.csv",
|
|
["date", "commits"], [(d, c) for d, c in day_rows])
|
|
write_csv("commits-full.csv",
|
|
["hash", "author", "email", "author_date", "subject"],
|
|
[(r["hash"], r["author"], r["email"], r["author_date"], r["subject"]) for r in rows])
|
|
|
|
lines = []
|
|
lines.append("# Git commit census — troupe-standup")
|
|
lines.append("")
|
|
lines.append("Source: `git log --pretty=format:` over the pinned revision %s (fields: "
|
|
"hash, author, email, author-date, subject)." % PIN)
|
|
lines.append("")
|
|
lines.append("Pinned revision (sha): `%s`" % PIN)
|
|
lines.append("Total commits: **%d**" % total)
|
|
lines.append("")
|
|
lines.append("## Commits by author")
|
|
lines.append("")
|
|
lines.append("| author | commits |")
|
|
lines.append("|---|---|")
|
|
for a, c in census_rows:
|
|
lines.append("| %s | %d |" % (a, c))
|
|
lines.append("| **total** | **%d** |" % total)
|
|
lines.append("")
|
|
lines.append("## Commits by calendar day")
|
|
lines.append("")
|
|
lines.append("| date | commits |")
|
|
lines.append("|---|---|")
|
|
for d, c in day_rows:
|
|
lines.append("| %s | %d |" % (d, c))
|
|
lines.append("| **total** | **%d** |" % total)
|
|
lines.append("")
|
|
|
|
with open(os.path.join(OUT, "README-commits-census.md"), "w", encoding="utf-8") as f:
|
|
f.write("\n".join(lines))
|
|
|
|
print("commits:", total)
|
|
print("authors:", len(census_rows))
|
|
print("days:", len(day_rows))
|
|
print("wrote: commits-census.csv, commits-by-day.csv, commits-full.csv, README-commits-census.md")
|
|
|
|
|
|
if __name__ == "__main__":
|
|
sys.exit(main())
|