The head-tree fingerprint keeps the security win (approval binds to the exact
workflow files and blob SHAs at the pushed head), but the guard was firing on
every push because it no longer compared against a base. This change re-adds
change detection using a base fetched from the authenticated remote at guard
time:
- Fetches the pushed branch from the remote; if it does not exist (new branch),
fetches the remote's default branch via ls-remote and a fallback chain.
- Compares the head workflow tree (ls-tree) against the freshly fetched base
(FETCH_HEAD), not against any local refs/remotes/origin/* ref.
- Returns None (no guard) when the workflow trees are identical, so code-only
pushes to a branch that already contains workflow files are not blocked.
- Updated test_non_workflow_push_runs_without_approval to use a non-empty-but
unchanged workflow tree.
Refs: 98