mirror of
https://github.com/kuhyx/testsAndMisc.git
synced 2026-11-10 18:47:04 +01:00
Some checks are pending
dependency freshness / dependencies on newest stable (push) Waiting to run
md-naming / markdown naming convention (push) Waiting to run
Pre-commit checks / pre-commit (${{ matrix.group }}) (mypy, mypy) (push) Waiting to run
Pre-commit checks / pre-commit (${{ matrix.group }}) (pylint, pylint) (push) Waiting to run
Pre-commit checks / pre-commit (${{ matrix.group }}) (rest, ) (push) Waiting to run
Pre-commit checks / pre-commit (${{ matrix.group }}) (shellcheck, shellcheck) (push) Waiting to run
Python tests / test (push) Waiting to run
repo-contract / repo contract (push) Waiting to run
Shell tests / shell-tests (push) Waiting to run
Shell tests / lib-tests (push) Waiting to run
Shell tests / arch-tests (push) Waiting to run
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01JThjboXTeZ3yJPBzxMx5wS
106 lines
3.6 KiB
Python
106 lines
3.6 KiB
Python
"""Find the commits each session made, from git rather than from tool output.
|
|
|
|
Measured 2026-10-17: of 601 recent commit commands only 35 printed a parseable
|
|
``[branch sha]`` line -- most run through ``finish_auto.sh`` with a tailed or
|
|
backgrounded output. So every commit command is stored with its time window
|
|
and repository, and the hashes come from ``git log`` instead:
|
|
|
|
* a commit whose hash a command printed belongs to that command's session;
|
|
* otherwise a commit carrying a ``Co-Authored-By: Claude`` trailer belongs to
|
|
the commit command in the same repository whose window covers its commit
|
|
time (from :data:`LEAD_S` before the call to :data:`TAIL_S` after its
|
|
result -- a backgrounded finish_auto.sh commits after the call returns),
|
|
the nearest one when several do.
|
|
|
|
Rows are only ever added, so a repository deleted later keeps its history.
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
from collections import defaultdict
|
|
import re
|
|
from typing import TYPE_CHECKING
|
|
|
|
from python_pkg.token_dashboard import gitcmd
|
|
|
|
if TYPE_CHECKING:
|
|
import sqlite3
|
|
|
|
LEAD_S = 60.0
|
|
TAIL_S = 1800.0
|
|
TRAILER = re.compile(r"^Co-Authored-By:\s*Claude", re.IGNORECASE | re.MULTILINE)
|
|
_FIELD, _RECORD = "\x1f", "\x1e"
|
|
_LOG_FIELDS = 3
|
|
|
|
|
|
def _git(repo: str, *args: str) -> str | None:
|
|
"""Run git in ``repo``; ``None`` on any failure."""
|
|
return gitcmd.run(repo, *args)
|
|
|
|
|
|
def _toplevel(hint: str, cache: dict[str, str | None]) -> str | None:
|
|
"""The work tree containing ``hint``, cached per hint."""
|
|
if hint not in cache:
|
|
out = _git(hint, "rev-parse", "--show-toplevel") if hint else None
|
|
cache[hint] = out.strip() if out else None
|
|
return cache[hint]
|
|
|
|
|
|
def _log(repo: str, since: float) -> list[tuple[str, float, bool]]:
|
|
"""``(sha, commit time, has Claude trailer)`` on HEAD since ``since``."""
|
|
out = _git(
|
|
repo,
|
|
"log",
|
|
"HEAD",
|
|
f"--since=@{int(since)}",
|
|
f"--format=%H{_FIELD}%ct{_FIELD}%B{_RECORD}",
|
|
)
|
|
rows = []
|
|
for record in (out or "").split(_RECORD):
|
|
parts = record.strip("\n").split(_FIELD, 2)
|
|
if len(parts) == _LOG_FIELDS:
|
|
rows.append((parts[0], float(parts[1]), bool(TRAILER.search(parts[2]))))
|
|
return rows
|
|
|
|
|
|
Command = tuple[str, int, float, float, str] # key, prompt, start, end, sha
|
|
|
|
|
|
def _match(
|
|
sha: str, ts: float, cmds: list[Command], *, trailer: bool
|
|
) -> Command | None:
|
|
"""The command that made this commit, if any."""
|
|
for cmd in cmds:
|
|
if cmd[4] and sha.startswith(cmd[4]):
|
|
return cmd
|
|
if not trailer:
|
|
return None
|
|
covering = [c for c in cmds if c[2] - LEAD_S <= ts <= max(c[2], c[3]) + TAIL_S]
|
|
if not covering:
|
|
return None
|
|
return min(covering, key=lambda c: abs(ts - c[2]))
|
|
|
|
|
|
def discover(conn: sqlite3.Connection) -> int:
|
|
"""Attribute git commits to sessions; returns how many rows were added."""
|
|
cache: dict[str, str | None] = {}
|
|
by_repo: dict[str, list[Command]] = defaultdict(list)
|
|
for key, prompt, start, end, sha, hint in conn.execute(
|
|
"SELECT key, prompt_idx, ts, end_ts, sha, repo FROM commits"
|
|
):
|
|
top = _toplevel(hint, cache)
|
|
if top is not None:
|
|
by_repo[top].append((key, prompt, start, end, sha))
|
|
added = 0
|
|
for repo, cmds in by_repo.items():
|
|
since = min(c[2] for c in cmds) - LEAD_S
|
|
for sha, ts, trailer in _log(repo, since):
|
|
cmd = _match(sha, ts, cmds, trailer=trailer)
|
|
if cmd is None:
|
|
continue
|
|
added += conn.execute(
|
|
"INSERT OR IGNORE INTO session_commits VALUES (?,?,?,?,?)",
|
|
(sha, repo, ts, cmd[0], cmd[1]),
|
|
).rowcount
|
|
return added
|