Skip to content

daojyun/git-everref

Repository files navigation

everref

Append-only backup for git branches and tags. everref runs inside a repository you already work in and copies selected refs to one or more backup remotes as an event log that only ever grows: nothing on a backup is ever deleted, overwritten, or force-updated. When history is rewritten, the old history stays; when a branch is deleted, its last state stays. From the log, the refs active at any past moment can be reconstructed.

It is a single binary with one runtime dependency — the git you already have. No daemon, no database, no server component, no sudo, no credentials of its own: every action is a plain git command, configuration lives in git config, and removing everref leaves the repository exactly as it was.

git everref add --remote backup     # protect the current branch
git everref run                     # back it up

A protected ref can also be a branch of a remote you fetch from (origin/main): everref fetches it first and backs it up, so one clone can bridge an upstream project — a GitHub repository, a team server — straight into the append-only backup.

Why

git push --mirror follows deletions and force-pushes, so a mirror is only as safe as the source. Hosting-platform "protected branches" guard one platform's copy under that platform's rules. everref instead makes the backup immune by construction: its only write operations are create a new ref and fast-forward an existing one, and git itself refuses everything else — on every transport, with no server configuration.

Every run decides from the backup remote's live state, never a local cache, so several clones can safely back up the same project to the same remote; git's rejection of non-fast-forward pushes is the final safeguard.

How it works

For each protected ref, the backup remote holds an event log under an everref/ namespace, plus a journal recording when each tip was observed:

refs/heads/everref/heads/main/created_1752566400   # a lineage; fast-forwards as main advances
refs/heads/everref/heads/main/created_1752739200   # history was rewritten: new lineage, old one frozen
refs/heads/everref/heads/main/deleted_1752912000   # branch deleted: tombstone at its last tip
refs/heads/everref/journal/heads/main/journal      # timeline of every observed tip

A normal commit fast-forwards the active lineage. A rewrite starts a new lineage and freezes the old one. A deletion writes a tombstone. A source remote that cannot be reached is an error, never a deletion — an outage cannot masquerade as intent. Start with docs/getting-started.md; the documentation index maps the rest, and the full reasoning, invariants, and failure analysis live in docs/design.md.

Status

Complete — released as v1.0.0. Every feature of the design document is implemented and covered by unit tests and an end-to-end integration suite that runs on Linux and macOS in CI. Development proceeded in milestones, each merged after a full adversarial review cycle:

Milestone Scope
Core engine add, rm, list, run, status, log, reset; local and bridged branches; journals; concurrent-clone safety
Restore restore, restore --at, multi-remote reconciliation — the log proves itself
Trigger reconciler --trigger git hooks (including reference-transaction for bridged branches), --schedule systemd user timers
Tags & expire Tag backup (tags command), expire, man pages, shell completions
Hardening Test-suite rebuild and documentation restructuring

Install

Requires git ≥ 2.20 — the only runtime dependency (≥ 2.28 for the reference-transaction trigger). The binary is named git-everref, which is exactly what makes git everref … work: git runs any git-<name> executable found on your PATH as git <name>.

What works where

Everything that goes through git — backup, bridge mode, restore, hook triggers, expire — works wherever git does. Only schedules need an OS scheduler, and today that means systemd:

Capability Linux macOS Windows
Backup, bridge mode, restore, expire
Hook triggers (post-commit, pre-push, reference-transaction)
--schedule timers ✓ systemd user units

Hooks are plain sh blocks; on Windows, git's own shell runs them. Where --schedule is unavailable, hook triggers cover the event-driven cases and any scheduler you already have (launchd, cron, Task Scheduler) can run git everref -C <repo> run --all — a scheduled run needs no everref-managed plumbing. Linux and macOS are the tested platforms: every change runs the full unit and integration suites on both in CI. Windows builds and runs but is untested and does not yet serialise concurrent runs per repository.

Prebuilt binary — Linux and macOS, amd64 and arm64

Download from the releases page; each archive carries the binary and the man pages, and a checksums.txt sits alongside for verification:

VERSION=1.0.0   # pick the release you want
curl -LO \
  "https://github.com/daojyun/git-everref/releases/download/v${VERSION}/git-everref_${VERSION}_linux_amd64.tar.gz"
tar -xzf "git-everref_${VERSION}_linux_amd64.tar.gz"
mkdir -p ~/.local/bin ~/.local/share/man/man1
install -m 755 git-everref ~/.local/bin/git-everref
ln -sf git-everref ~/.local/bin/everref                    # optional short alias
install -m 644 docs/man/everref.1 docs/man/git-everref.1 ~/.local/share/man/man1/

(The snippet sticks to flags BSD tools understand, so it works verbatim on macOS; swap linux for darwin in the archive name there.)

go install — any OS with a Go toolchain

The one command below drops the git-everref binary into ~/go/bin; put that directory on your PATH and git everref works immediately. On Windows this is the way to install — no prebuilt archives are published for it:

go install github.com/daojyun/git-everref@latest

From a checkout — Linux and macOS

make install   # binary, everref alias link, and man pages into ~/.local

Quick start

cd your-project
git remote add backup git@backup.example.com:acme/your-project.git

git everref add main --remote backup          # protect a branch
git everref add origin/main --remote backup   # bridge an upstream branch
git everref run --all --dry-run               # preview
git everref run --all                         # back up
git everref status --all                      # compare against the backup
git everref log main                          # event history

everref reuses your existing git remotes and credentials; the backup remote is best a dedicated bare repository — including a plain local folder (git init --bare ~/backups/your-project.git, then git remote add backup with that path; walkthrough in docs/getting-started.md). Nothing everref does ever removes a remote ref — the one rule operators must keep: never hand-rewrite anything under everref/ on a backup (troubleshooting).

AI-assisted development disclosure

All code, tests, and documentation in this repository were written by Claude (Anthropic's AI assistant) working under human direction — the code is 100% AI-generated, and humans have not reviewed every line.

What stands behind it instead:

  • every feature commit passed an adversarial review by an independent AI reviewer (findings were reproduced against real repositories before being accepted, and each cycle ran until an explicit approval);
  • the full test suite — unit tests for all pure logic plus an end-to-end integration suite of ~50 scenarios that verifies the append-only guarantee directly against real git repositories — must pass before any commit;
  • provenance is recorded per commit (Co-Authored-By trailers).

Treat everref like any dependency you have not audited yourself: read the code that matters to you before trusting it with production data, and report anything questionable. AI review and thorough tests reduce risk; they do not replace human judgment.

Development

make build          # build ./everref
make test           # unit tests (pure logic: decision table, naming, parsing)
make integration    # end-to-end tests against real git repositories
make lint-shell     # shellcheck + shfmt over the integration suites
make staticcheck    # optional deeper Go analysis
make ci             # gofmt + vet + tidy check + shell lint + everything above

The integration runner explains its own footprint: test/integration/run.sh --help documents that everything happens inside one temporary directory, with systemctl disabled and git's global config isolated.

The suites are written against GNU coreutils. On macOS, put Homebrew's gnubin on PATH first (as the CI job does):

brew install coreutils
export PATH="$(brew --prefix coreutils)/libexec/gnubin:$PATH"

The layout follows the design's separation of concerns:

internal/refpath    canonical ref paths, event names, reserved components
internal/gitx       thin exec layer over the system git (the only I/O path)
internal/gitcfg     declarative config in git config
internal/eventlog   pure core: snapshot parsing, lineages, the decision table
internal/journal    per-ref timeline commits
internal/backup     the run engine
internal/cli        cobra commands, git-style conventions

Exit codes are stable: 0 success, 1 at least one ref failed or was skipped, 2 usage or configuration error.

License

MIT

About

Never lose a commit: append-only backup for git branches and tags. Backs up automatically on commit, push, and fetch, or on a schedule; bridge mode carries an upstream remote's branches and tags through your clone to any backup remote. Plain git plumbing — no daemon, no server config — and thoroughly tested.

Topics

Resources

License

Stars

1 star

Watchers

0 watching

Forks

Packages

 
 
 

Contributors