Troubleshooting¶
Every regit error is printed to stderr with a regit: prefix, and the process
exit code tells you which kind of failure occurred. In the error listings
below, angle-bracket parts like <branch> or <hash> are filled in with your
actual branch name, commit hash, or path — the surrounding text is exactly what
regit prints.
Exit codes¶
Every verb uses the same exit-code contract:
| Code | Meaning |
|---|---|
0 |
Success — the preview printed, or the apply completed |
1 |
Usage error — bad flag, missing required argument, unusable LLM selection |
2 |
Guard blocked — a safety check refused the operation (shallow clone, dirty tree, moved HEAD, merges in range, pushed history without --force, undo guards) |
3 |
Apply / LLM error — the rewrite or the LLM call itself failed at runtime |
Preview is the default
Mutating verbs preview by default and only rewrite with --execute. Most
guard blocks below fire at apply time — a preview (exit 0) can succeed and
the subsequent --execute still be refused (exit 2).
Guard blocks (exit 2)¶
Shallow clone¶
regit: refusing to rewrite a shallow clone — history is incomplete; run 'git fetch --unshallow' first
That is the wording of the plan verbs (redate, retitle, reauthor,
drop-bodies, reword); split, squash, purge, redact, and linearize
report the same condition without the leading clause:
Just before the branch moves the condition is re-checked one last time; if the repository became shallow only in that instant, the refusal reads as follows and is reported as an apply error (exit 3):
Cause: The repository was cloned with --depth (or otherwise grafted).
Rewriting a shallow history is unsafe because the replay would build on an
artificial boundary commit instead of the real history.
Fix: Fetch the full history, then retry.
Dirty working tree¶
A last-instant re-check just before the branch moves reports the same condition as an apply error (exit 3):
Cause: There are uncommitted changes (staged or unstaged). regit refuses to rewrite history while the working tree differs from HEAD.
Fix: Commit or stash, then retry.
git stash # or: git commit -am "wip"
regit redate --strategy=Even-Spread --start=2026-01-01 --end=2026-03-01 --execute
git stash pop
Branch moved since the plan was created (HEAD moved)¶
regit: branch moved since the plan was created: branch <branch> (plan base <hash>, now <hash>); reload and re-stage
Cause: Between computing the plan/proposal and applying it, the branch tip changed — a new commit landed, another regit apply ran, or something else moved the ref. The apply is anchored to the exact head the plan was computed from (a compare-and-swap check), so a moved head aborts before any ref is touched.
Fix: Re-run the command against the current head — the preview and the apply are recomputed from the new tip.
In the TUI, reload the repository view and stage the edit again.
Merge commits in the range¶
Each history-rewriting verb that cannot cleanly cross a merge refuses with its own message:
regit: range contains <N> merge commit(s); their side branches keep their original dates — pass --allow-merges to proceed
Cause: regit's replay only remaps a rewritten commit's first parent. For
redate, a merge's side branch keeps its original dates —
surprising enough that applying is gated behind an explicit opt-in. For
squash, purge, and
redact, a merge inside the rewritten range is a hard block.
(The squash refusal fires inside the guarded apply and is reported as an
apply error — exit 3; the redate, purge, and redact blocks are guard
blocks — exit 2.)
Fix:
-
redate— opt in explicitly if partial coverage of side branches is acceptable: -
squash— pick a range that does not span the merge, or flatten the branch first withlinearize: -
purge/redact— runregit linearize --executefirst, or use git filter-repo for full-history (all-refs) rewrites. Note thatpurgealso blocks when the path has history on a merged side branch:
Pushed history requires --execute --force¶
regit: some commits already exist on a remote; rewriting them will require a force-push and can disrupt collaborators — re-run with --force to rewrite them
That is the plan verbs' wording (redate, retitle, reauthor,
drop-bodies, reword). squash, purge, redact, and linearize print:
and split names the commit:
Cause: The rewrite range includes commits that already exist on the upstream. Rewriting them changes their hashes, so publishing the result needs a force-push — regit demands an explicit acknowledgement. (In the TUI this surfaces as a typed branch-name confirmation instead.)
Fix: Confirm you accept the force-push consequence:
After the apply, publish with git push --force-with-lease.
Coordinate with collaborators
Anyone who already pulled the old commits will have to rebase onto the rewritten history. Prefer rewriting only unpushed commits when you can.
Undo¶
Backup belongs to another branch¶
regit: backup belongs to another branch: <ref> was taken on branch <other>; you are on <branch> — check out <other> to undo it
When you name the backup explicitly with --to, the same refusal reads:
Cause: Backups are branch-qualified
(refs/regit/backup/<branch>/<ts>-<seq>). The backup you asked to restore was
taken on a different branch than the one currently checked out; restoring it
here would point this branch at that branch's old tip.
Fix: Switch to the branch the backup belongs to, then undo:
Use regit undo --list to see every backup with its branch.
HEAD moved past the backup¶
regit: HEAD moved past this backup (apply produced <hash>, branch is now <hash>); re-run with --force to unwind it
If the guard itself cannot be evaluated, the refusal degrades closed:
regit: HEAD moved past this backup: cannot check the advanced-HEAD guard (<reason>); re-run with --force to unwind anyway
Cause: Work happened after the apply this backup belongs to — another apply, or plain commits. Restoring the backup would silently discard those newer commits, so regit refuses unless forced.
Fix: If you genuinely want to discard everything after the backup:
Otherwise pick a newer backup explicitly:
Undo is itself reversible
Before moving the branch, regit undo backs up the current tip. If you
force-unwound too far, regit undo --list shows that pre-undo backup and
regit undo --to=<ref> restores it.
LLM (split / reword / squash)¶
No LLM configured¶
Cause: split, reword, and
squash need an LLM backend, and neither the
--llm / --agent flags nor the llm: config block (nor the
REGIT_LLM_PROVIDER / REGIT_LLM_MODEL env vars) name one. This is a
fail-fast usage error (exit 1) — nothing was contacted or changed.
Fix: Pick a backend one of three ways.
Hosted providers read their API key from the environment only:
REGIT_LLM_API_KEY, falling back to OPENAI_API_KEY /
OPENROUTER_API_KEY.
Local providers (ollama, lmstudio) need no key.
Malformed or unknown --llm value¶
regit: unknown LLM provider "<value>"; known: ollama, lmstudio, openai, openrouter (or pass --llm-base-url)
Cause: --llm takes provider/model, split on the first slash. Passing
just a model name (--llm=gpt-4o) parses as a provider with no model; a
provider outside the built-in table has no default endpoint.
Fix:
regit reword HEAD --llm=openai/gpt-4o
# self-hosted / other OpenAI-compatible endpoint:
regit reword HEAD --llm=openai/my-model --llm-base-url=http://localhost:8000/v1
Configuration¶
regit loads config strictly: unknown YAML keys and unusable values are hard
errors at startup, never silent fallbacks. Config errors are printed with a
load config: prefix.
Unknown YAML key¶
regit: load config: parse <path>/config.yaml: yaml: unmarshal errors:
line <N>: field <key> not found in type config.Config
Cause: The config file contains a key regit does not know — usually a typo
(e.g. default_stratgy). Strict decoding rejects it so a misspelled setting
cannot silently fall back to a default.
Fix: Fix or remove the offending key on the reported line. The recognized top-level keys are listed in the configuration reference.
Invalid timezone¶
Cause: The timezone key (or REGIT_TIMEZONE) is not a valid IANA zone
name and is not the literal local.
Fix: Use an IANA name or local:
Inverted workday window¶
Cause: workday_end is at or before workday_start, so
time-of-day models like Working hours would have an empty
window to place commits in.
Fix: Make the window strictly positive:
The same values can come from REGIT_WORKDAY_START / REGIT_WORKDAY_END —
environment overrides are validated identically.
Install (git re integration)¶
A foreign git-re already exists¶
Cause: regit install creates a git-re symlink next to the
PATH-resolved regit binary so git re works as a subcommand. Something else
already occupies that name — a different program, a plain file, or a dangling
symlink — and regit will not overwrite what it cannot prove is its own.
Fix: If you are sure the existing git-re should be replaced:
Otherwise inspect it first: ls -l <path>/git-re.
regit not on PATH¶
regit: regit is not on your $PATH; put the binary on PATH first (e.g. `just install`), then re-run `regit install`
Cause: The installer places the symlink next to the PATH-resolved regit
binary; if regit itself is not findable on $PATH, there is nowhere valid to
put the shim.
Fix:
Diagnostic steps¶
When something is wrong and you don't have a specific error message:
- Check the version:
regit --version - Check repository preconditions:
git status(clean tree?) andgit rev-parse --is-shallow-repository(must befalse) - List available backups:
regit undo --list - Review what regit actually ran:
.git/regit/command.logrecords the headless-equivalent command for every action applied in the TUI (for plain headless runs, the command is whatever you typed — check your shell history) - Check the process exit code (
echo $?) against the table above to classify the failure
Still broken?¶
Open an issue and include:
- Output of
regit --version - The full
regit: …error line and the exit code - The exact command you ran (or the relevant
.git/regit/command.loglines) - Your config file, if you use one (it contains no secrets)