git-safety-net is a tiny, production-minded Bash utility that protects coding projects with automatic local snapshots.
Many projects already have their own Git repositories, but modern LLM-assisted workflows introduce a new risk profile: an agent can accidentally perform a destructive action, rewrite history, or simply work for long stretches without committing. git-safety-net adds a separate, hidden, continuously-updated safety layer so you can recover file state by time even when the main project repo becomes messy or damaged.
It has two scripts:
git-safety-net.sh(gsn): protect a project and keep snapshots up to date.git-safety-net-restore.sh(gsnr): restore one file as it existed at a chosen time.
You interact with it as protect and restore by time. Git is used internally, but hidden from normal usage.
First-time usage is meant to start directly from curl against this repository.
- very small: Bash +
git+rsync - local-first: no cloud account, no daemon, no database
- safe by default: source project Git is never touched
- efficient: incremental sync, commits only when files changed
- easy to trust: plain files, readable metadata
First use is intentionally one-off via curl from this repository. From the project you want to protect, run:
bash <(curl -fsSL https://raw.githubusercontent.com/davadev/git-safety-net/v1.0.4/git-safety-net.sh)That first run starts onboarding and can optionally install gsn / gsnr aliases in your shell rc so future use is shorter.
If you already have the script locally, you can also run:
./git-safety-net.shOn first run, onboarding asks you to confirm source path, backup root, interval, expiration, and optional aliases.
Then restore a file by time:
bash <(curl -fsSL https://raw.githubusercontent.com/davadev/git-safety-net/v1.0.4/git-safety-net-restore.sh) --file src/auth.py --time "2026-04-01 14:30"These scripts are designed to be fetched on demand (no permanent installation required).
Example aliases:
alias gsn='bash <(curl -fsSL https://raw.githubusercontent.com/davadev/git-safety-net/v1.0.4/git-safety-net.sh)'
alias gsnr='bash <(curl -fsSL https://raw.githubusercontent.com/davadev/git-safety-net/v1.0.4/git-safety-net-restore.sh)'gsn can add aliases automatically during onboarding (idempotently, in a marked block).
Setup now lets you choose alias mode: remote URL (most convenient) or local installed scripts under ~/.git-safety-net/.
You can rerun --setup later to switch alias mode.
- Start protection once from
curl(or usegsnalias after onboarding). - Keep coding while snapshots are recorded in the hidden store.
- Use
gsnr --list, then restore a specific file by time when needed.
By default, all persistent data is under:
~/.git-safety-net/
Per protected project (example backend-a1b2c3d4):
- hidden backup repo:
~/.git-safety-net/backend-a1b2c3d4/ - metadata file:
~/.git-safety-net/backend-a1b2c3d4.env - lock directory:
~/.git-safety-net/backend-a1b2c3d4.lock
Only these artifacts are persisted, plus optional alias lines in your shell rc.
Onboarding runs when metadata is missing for the project (or when --setup is passed):
- confirm project directory (default: current directory)
- confirm hidden backup root (default:
~/.git-safety-net) - choose alias auto-setup (
gsn,gsnr) - confirm interval (default:
180seconds) - confirm expiration (default: end of local day)
- review and confirm summary before starting
After onboarding, normal runs are non-interactive.
- never runs Git in your source project
- always excludes source
.git/from sync - backup repository is fully separate and hidden
- rejects unsafe path relationships (source inside backup, backup inside source)
- per-project lock prevents duplicate watchers for the same project
- different projects can be watched in parallel
- source files sync into hidden backup via
rsync --delete .gitignoreis used as an exclusion source where practical- common build/cache/junk paths are excluded by default
- extra excludes can be added with repeatable
--exclude - snapshot commit is created only when changes exist
- no-op runs clearly report: "No changes detected, skipping snapshot"
gsnr restores from the latest snapshot at or before your requested time.
--listshows recent snapshot times--listshows recent snapshot times in local time--list-filesshows files available in the selected snapshot--dry-runpreviews what would be restored- restore refuses to overwrite an existing different file unless
--forceis passed - full-project restore (
--restore-all) requires--forceto apply - restore does not mutate the hidden backup repo
- if file did not exist at that time, you get a clear message and no write occurs
Safety default: restore will not overwrite a different existing file unless you pass --force.
Protect current directory:
./git-safety-net.shProtect another directory:
./git-safety-net.sh --source ~/code/backendProtect for 3 hours:
./git-safety-net.sh --expire 3hSingle run (no loop):
./git-safety-net.sh --onceRe-run interactive setup:
./git-safety-net.sh --setupList recent snapshots:
./git-safety-net-restore.sh --listRestore one file by time:
./git-safety-net-restore.sh --file src/auth.py --time "2026-04-01 14:30"Dry-run restore:
./git-safety-net-restore.sh --file src/auth.py --time "2026-04-01 14:30" --dry-runForce overwrite during restore:
./git-safety-net-restore.sh --file src/auth.py --time "2026-04-01 14:30" --forceList files in selected snapshot:
./git-safety-net-restore.sh --list-files --time "2026-04-01 14:30"Dry-run full project restore:
./git-safety-net-restore.sh --restore-all --time "2026-04-01 14:30" --dry-run- Bash (macOS or Linux)
gitrsync
- Hidden Git is an implementation detail for reliable snapshots.
- You do not need commit hashes or Git commands to use the tool.
- Your source project's existing Git history remains untouched.
Another watcher is already running...means a watcher lock exists for that project.- Stop the other watcher (Ctrl+C in its terminal) and run again.
- If a watcher crashed, rerun
gsn; stale lock recovery is handled automatically.