feat(auto-pull): opt-in background fast-forward for registered repos #60

Merged
rootiest merged 2 commits from feat-auto-pull-registry into main 2026-06-19 06:57:26 +00:00
Owner

Summary

Adds an opt-in system that background fast-forwards git repos when you cd into them — so your local clones (starting with the fish-config repo itself) stay current without manually remembering to pull. Motivated by repeatedly forgetting to pull the docs-regen CI commit on main.

Safety is the whole design: it only ever fast-forwards a clean repo whose branch has an upstream, and never in repos you haven't explicitly registered. It cannot rebase, merge, or overwrite committed or uncommitted work — worst case it's a no-op.

How it works

  • conf.d/auto-pull.fish (C2-guarded) — a --on-variable PWD handler. On directory change it matches $PWD against the fish-config repo (always-covered baseline) + the user registry, via pure string prefix matching (no git call for unrelated dirs). A throttle fires it once per repo entry, not on every sub-directory cd, and clears on leaving so re-entry re-syncs. The sync runs backgrounded (&; disown) — zero prompt lag.
  • functions/_auto_pull_sync.fish — the worker. Proceeds only when: clean work tree and branch has @{u} and git merge --ff-only is possible. Any failed guard → no-op. Works on any branch (ff-only makes it safe).
  • functions/auto-pull.fish — registry management: add / remove / list / status / -h. Usable even when C2 is disabled (it just edits a file); status reports the C2 state.
  • completions/auto-pull.fish — subcommand + registered-repo-basename completions.

Registry

Machine-local plain text at ~/.config/.user-dots/fish/auto-pull.list (one absolute git-toplevel path per line) — never committed. The shipped config stays generic; your personal repo list lives in your git-ignored user-dots. add creates the file if missing.

cd ~/src/qmk_firmware; and auto-pull add   # register current repo
auto-pull add ~/work/api                   # register by path
auto-pull list                             # fish-config (baseline) + registered
auto-pull remove qmk_firmware              # by basename or path
auto-pull status                           # enabled? count? list path

Opinionated guard (C2)

Classified C2 (auto-execution). When __fish_config_op_autoexec is off, the PWD handler is never registered → no auto-pull anywhere (including fish-config). The auto-pull management command still works.

Scope note

Fires on repo entry, so it won't catch a commit that lands on the remote while you're already sitting in the repo (the exact CI-commit case that motivated this). Catching that needs a fish_prompt throttle — deferred as a future enhancement, noted in AGENTS task #12.

Tests performed (inline)

  • Worker fast-forwards a clean repo behind upstream (1→2 commits, status 0); no-ops on dirty tree, no upstream, and divergent branches.
  • auto-pull add (cwd + path), dup detection, non-repo rejection, baseline rejection; remove by basename + missing-target error; list/status output; bad-subcommand error.
  • Handler registers under C2-on, is skipped under C2-off; match + throttle verified via real cd (entry fires, subdirs throttle, leaving clears, re-entry re-fires).
  • All four files pass fish -n.

Manual Verification

  • auto-pull add inside a repo with a remote registers it; auto-pull list shows it alongside the fish-config baseline.
  • cd out and back into that repo triggers a background fast-forward when it's cleanly behind its upstream (verify with git log).
  • With local commits or a dirty tree, entering the repo does not alter it.
  • An unregistered repo (e.g. a work repo) is never touched and gets no git fetch.
  • auto-pull remove <name> stops it from auto-pulling.
  • set -U __fish_config_op_autoexec off disables auto-pull (new shell); auto-pull status reports DISABLED but add/remove/list still work.
  • auto-pull -h shows colored help on stdout.
  • Tab-completion: auto-pull <tab> lists subcommands; auto-pull remove <tab> lists registered basenames.
## Summary Adds an opt-in system that **background fast-forwards** git repos when you `cd` into them — so your local clones (starting with the fish-config repo itself) stay current without manually remembering to pull. Motivated by repeatedly forgetting to pull the docs-regen CI commit on `main`. Safety is the whole design: it **only ever fast-forwards a clean repo whose branch has an upstream**, and never in repos you haven't explicitly registered. It cannot rebase, merge, or overwrite committed or uncommitted work — worst case it's a no-op. ## How it works - **`conf.d/auto-pull.fish`** (C2-guarded) — a `--on-variable PWD` handler. On directory change it matches `$PWD` against the fish-config repo (always-covered baseline) + the user registry, via pure string prefix matching (no `git` call for unrelated dirs). A throttle fires it **once per repo entry**, not on every sub-directory `cd`, and clears on leaving so re-entry re-syncs. The sync runs backgrounded (`&; disown`) — zero prompt lag. - **`functions/_auto_pull_sync.fish`** — the worker. Proceeds only when: clean work tree **and** branch has `@{u}` **and** `git merge --ff-only` is possible. Any failed guard → no-op. Works on any branch (ff-only makes it safe). - **`functions/auto-pull.fish`** — registry management: `add` / `remove` / `list` / `status` / `-h`. Usable even when C2 is disabled (it just edits a file); `status` reports the C2 state. - **`completions/auto-pull.fish`** — subcommand + registered-repo-basename completions. ## Registry Machine-local plain text at `~/.config/.user-dots/fish/auto-pull.list` (one absolute git-toplevel path per line) — **never committed**. The shipped config stays generic; your personal repo list lives in your git-ignored user-dots. `add` creates the file if missing. ``` cd ~/src/qmk_firmware; and auto-pull add # register current repo auto-pull add ~/work/api # register by path auto-pull list # fish-config (baseline) + registered auto-pull remove qmk_firmware # by basename or path auto-pull status # enabled? count? list path ``` ## Opinionated guard (C2) Classified C2 (auto-execution). When `__fish_config_op_autoexec` is off, the PWD handler is never registered → no auto-pull anywhere (including fish-config). The `auto-pull` management command still works. ## Scope note Fires on repo **entry**, so it won't catch a commit that lands on the remote while you're already sitting in the repo (the exact CI-commit case that motivated this). Catching that needs a `fish_prompt` throttle — deferred as a future enhancement, noted in AGENTS task #12. ## Tests performed (inline) - Worker fast-forwards a clean repo behind upstream (1→2 commits, status 0); no-ops on dirty tree, no upstream, and divergent branches. - `auto-pull add` (cwd + path), dup detection, non-repo rejection, baseline rejection; `remove` by basename + missing-target error; `list`/`status` output; bad-subcommand error. - Handler registers under C2-on, is skipped under C2-off; match + throttle verified via real `cd` (entry fires, subdirs throttle, leaving clears, re-entry re-fires). - All four files pass `fish -n`. ## Manual Verification - [x] `auto-pull add` inside a repo with a remote registers it; `auto-pull list` shows it alongside the fish-config baseline. - [x] `cd` out and back into that repo triggers a background fast-forward when it's cleanly behind its upstream (verify with `git log`). - [x] With local commits or a dirty tree, entering the repo does **not** alter it. - [x] An unregistered repo (e.g. a work repo) is never touched and gets no `git fetch`. - [x] `auto-pull remove <name>` stops it from auto-pulling. - [x] `set -U __fish_config_op_autoexec off` disables auto-pull (new shell); `auto-pull status` reports DISABLED but `add`/`remove`/`list` still work. - [x] `auto-pull -h` shows colored help on stdout. - [x] Tab-completion: `auto-pull <tab>` lists subcommands; `auto-pull remove <tab>` lists registered basenames.
rootiest added 2 commits 2026-06-19 06:24:02 +00:00
Add a C2-guarded PWD handler that background fast-forwards the fish-config
repo (baseline) plus any repos in a machine-local registry. Safety is
guaranteed by _auto_pull_sync: clean tree + has-upstream + --ff-only, else
no-op. Never rebases, merges, or overwrites work.

- conf.d/auto-pull.fish: --on-variable PWD handler, throttled to fire once
  per repo entry; spawns a --no-config child to run the worker
- functions/_auto_pull_sync.fish: the ff-only worker (safe on any branch)
- functions/auto-pull.fish: add/remove/list/status registry management,
  usable even when C2 is disabled
- completions/auto-pull.fish: subcommand + registered-repo completions

Registry lives at ~/.config/.user-dots/fish/auto-pull.list (machine-local,
never committed).
Add the auto-pull function entry (5.4 Git) and index entry, a C2 component
reference row plus explanatory paragraph in fish-config.md, and update the
README C2 disable-list. (AGENTS.md C2 table, file tree, and task #12 are
tracked in the git-ignored AGENTS/ dir.)
rootiest merged commit cf8dc8d7e1 into main 2026-06-19 06:57:26 +00:00
rootiest deleted branch feat-auto-pull-registry 2026-06-19 06:57:26 +00:00
Sign in to join this conversation.