--- name: repo-backup description: Use when backing up a git repository before risky work. Makes tarballs with checksums, runs a secret scan, pushes to a private backup remote and verifies each ref. version: 1.0.0 author: Pixel Rainbow (shagghiesuperstar) license: MIT metadata: hermes: tags: [backup, git, secrets-scan, github, reliability] category: devops related_skills: [incident-forensics, repo-sanitization-publishing, repo-ssot-hygiene] --- # Repo Backup ## Overview A zero-data-loss snapshot of a repository and its worktrees, taken before anything riskier touches them. The pipeline writes full tarballs with checksums, scans for secrets, pushes every branch and tag to a private remote, verifies each ref, and reports in a fixed key-value shape. Uncommitted work is the part most at risk, so the tarball is the primary copy, not the remote. ## When to Use - Before long-running work that leaves uncommitted changes in a repository or worktree with no remote. - Before a delete, rotate or wipe operation on a local directory that holds the only copy of some work. - When a brief asks for a backup with checksums, a secret scan and verified evidence. - For a freeze across many checkouts, run this once per repository and combine the results. ## Features - A full tarball of each working tree, including its `.git` directory. - One checksum file beside the tarballs. - A secret scan with triage rules by path and by key type. - A private backup remote under its own name, so the origin is not touched. - Per-ref verification that compares maps, not raw text. - A report with a fixed field list. ## Benefits - Uncommitted files and local-only branches survive a bad operation. - A backup is proven, not assumed. - Secret findings are reported without exposing any value. ## Procedure 1. **Inventory with counts.** For each tree, record the HEAD SHA, the branch, the number of uncommitted files (`git status --porcelain | wc -l`), and any dependency versions the brief asks for. Read versions from the lockfile or manifest, not from memory. 2. **Write the tarballs and checksums.** Create `~/Backups/-/`. For each tree, run `tar -czf .tar.gz -C `. Do not use `git archive`, because it drops uncommitted files. Then run `shasum -a 256 *.tar.gz > SHA256SUMS` in that directory before anything reports the backup as existing. 3. **Back up worktrees and their parents.** For a git worktree, back up the worktree directory and its parent repository. The worktree's `.git` is a pointer into the parent, which holds every object. Uncommitted worktree files exist only in the tarball, so say so in the report. 4. **Scan for secrets.** Run a secret scanner on each tree (for example `gitleaks detect --source --no-git --report-path `). Triage by path first: build output, dependency folders and generated manifests produce generic false positives. Then triage by key type. Publishable client keys are safe by design. Live server-side keys, private keys and JWT-shaped strings are real findings. Stop, do not push, and escalate. 5. **Create the backup repository as private.** Run `gh repo create / --private`. If it already exists, view it instead of recreating it, and confirm visibility with `gh repo view / --json visibility`. A public backup is a leak. 6. **Push to a second remote.** Add a remote with its own name (for example `github-backup`), then push all branches, then all tags. Never force-push a backup. If a ref already exists, compare its SHA instead of overwriting it. 7. **Verify each ref.** Parse the local ref list and the remote ref list into maps of ref to SHA, then compare per ref. Do not diff the two raw listings line by line: their separators differ, so a clean push looks like missing rows. Proceed only when no ref is missing or mismatched. 8. **Report.** Write the brief's fields as KEY=value lines in a result file, and reply with the same block. Name steps that other people own, such as an off-host copy or a restore test, as pending. Never fold them into done. ## Pitfalls - **Never print a secret value.** Report findings as a count, a file path, and the key type or prefix. Apply this to the report, the result file and chat. - **GitHub rejects ref path components that begin with a dot.** When you turn checkout paths into ref names, rename those components, for example `.config/x` to `dot.config__x`. - **Very large repositories are rejected on a bulk push.** Push in chunks of about 100 refs. - **Save uncommitted work without touching the user's index.** Build a temporary index file, write the working tree to a throwaway commit with `git commit-tree`, and push that commit under a clearly named `wip/` ref. Archive untracked files separately, and exclude environment files, dependency folders and build caches. - **Squash merges make merged branches look unique.** Find local-only work by counting commits not reachable from any remote, after a fetch. Back up branches merged by name too, rather than trusting a patch-equivalence check. - **A broken worktree is invisible to git.** When a worktree's pointer leads into a deleted repository, archive it as a tarball. - **Do not call work stale, dead or shipped without a citation.** Cite the commit or pull request that proves each status. A status with no citation is unclear. - **Define the terms before proposing a delete.** A branch is a working copy. Merged means the change is in the main branch. Shipped means the change is in the deployed build, which you check by ancestry against the deployed commit. - **A pull request whose base branch was merged is not automatically obsolete.** Measure whether its files match the main branch before proposing that it be closed. ## Verification - `shasum -a 256 -c SHA256SUMS` passes in the backup directory. - A secret scan report exists for each tree, and no unresolved live-key finding remains. - The backup repository reports private visibility. - The ref comparison reports zero missing and zero mismatched refs. - The result block lists the HEAD SHA, the uncommitted-file count and the push status for each repository.