The #113 sweep rested on CLAUDE.md's premise that rtk either filters or passes through unchanged, so prefixing is always safe. Measured against rtk 0.42.4, that premise is false for several of the commands the sweep prefixed, and two skills were left giving wrong answers silently. Why: - `rtk git worktree list --porcelain -z` discards both flags and renders its own format. The `locked`/`lock_reason` fields git-worktrees Step 2 must emit are absent entirely, and paths under $HOME are abbreviated to `~/`. - `rtk git branch --list <name>` prints a phantom `* ` line even when nothing matches, so git-branches' stated ambiguity test — "output from both means the name is ambiguous" — reported every name as ambiguous. `tag --list` is a clean passthrough, so only one half broke. - `rtk git diff --name-only`/`--name-status` append a `Changes:` trailer to output documented as "one per line"; `--word-diff` emits none of the `[-removed-] {+added+}` markers its table describes; `rtk git log -L` truncates each line at ~72 chars, on the one command whose purpose is showing line content. - `rtk git stash pop` prints only `FAILED: git stash pop`, swallowing the conflict diagnostic and retained-entry message the surrounding prose tells the agent to rely on. Implementation notes: - Eleven sites reverted to bare `git`, each carrying its reason inline so the next sweep does not undo it. `mergetool` and `rebase -i` are reverted on clause 3's interactive limb only: the TTY defect does not reproduce — rtk filters exactly twelve subcommands and execs the rest — and ADR-0023 records that measurement rather than a convenient one. - ADR-0023 states the rule repo-wide with a third clause: a command whose output the skill parses, or which is interactive, stays bare. `plugins/git/README.md` is reduced to a pointer; its claim that gitea skills "contain no git/rtk mentions at all" was false, and its citation of `hard-rules.md` pointed at a file containing no occurrence of "rtk". - Eight gitea sites swept, all verified byte-identical passthroughs first. - `scripts/check-rtk-prefix.sh` gates clause 1. Run against main's pre-sweep corpus it reports 99 findings including every gitea site, so it would have caught the drift #113 was filed about. Impact: the gate covers clause 1 only, in shell-tagged fences and the opening span of Run cells. Clause 2 is not gateable — "Run `git switch`" and "`git switch` refuses" are the same tokens — and prose bullets are invisible to it. Both limits are recorded in gates.md rather than left implied. Refs: #113 ADR: 0023 Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01EeH8SCbcrCAQrtymkNuhKP
4.9 KiB
source_keys
| source_keys | ||
|---|---|---|
|
Rewriting existing commits
Every flow on this page rewrites history. None of them runs before the caller has explicitly approved it, and none is followed by a force-push to main/master — refuse that and explain why instead.
Amend the last commit
- Stage the new changes, or the changes that undo something.
- Run
rtk git commit --amend, adding--no-editwhen the message stays as it is. - If the message should change, show the current one and prompt for the replacement.
- The branch has now diverged from its remote. Amending is safe only on a branch nobody else has based work on; on
main/master, refuse the force-push and explain, rather than warning and proceeding.
Fold a commit into an earlier one (autosquash — preferred)
Prefer this whenever a commit is written to be folded, because git does the marking:
rtk git commit --fixup=<commit>keeps the target's message;rtk git commit --squash=<commit>lets you edit the combined message later. Both prefix the message withfixup!/squash!and name the target commit.- Get explicit approval — the rebase still rewrites history.
- Run
git rebase -i --autosquash HEAD~N— bare, notrtk:-iopens an interactive sequence editor (ADR-0023). Git pre-fills the todo list with the tagged commits already reordered against their targets; save it unchanged to apply.
-i is not optional here. On Git 2.39.5, git rebase --autosquash HEAD~N without -i prints Successfully rebased and updated refs/heads/<branch>. and exits 0 while leaving the fixup! commit in place at its original SHA — --autosquash is honoured only by the interactive machinery, and the false success is the trap: the fold is reported as done, and the surviving fixup! subject then fails the Conventional Commits commit-msg hook. Later Git versions taught the non-interactive rebase to honour the flag, but -i --autosquash is correct on every version, so always write that.
Squash by hand (interactive rebase)
Use this when the commits were not tagged at commit time. Interactive rebase has no undo once rebase -i starts — git reflog is the recovery path.
- Identify the commits to squash — typically the last N on the current branch.
- Get explicit approval.
- Run
git rebase -i HEAD~N— bare, notrtk, for the same interactive-editor reason — marking the older commitssquashto keep their messages for editing, orfixupto discard them. - Compose the combined message when the rebase stops to ask. For a non-trivial combined message, follow the structure in
references/commit-template.md.
When a rebase halts on a conflict
Offer conflict resolution or rtk git rebase --abort. Do not resolve conflicts automatically without confirmation.
Rebase the branch onto a new base
Replays this branch's commits on top of another branch's tip — bringing a feature branch up to date without a merge commit.
- Confirm nothing being replayed has been pushed anywhere someone else has based work on. A rebase gives every replayed commit a new SHA, which breaks any clone that already has the old ones.
- Get explicit approval — this rewrites history like every other flow on this page.
rtk git fetch originfirst, so<newbase>is the real tip rather than a stale local copy.rtk git rebase <newbase>— for examplertk git rebase main. Usertk git rebase --onto <newbase> <upstream> <branch>to replay only the commits after<upstream>, which is how a branch started from the wrong base gets moved.- The branch has now diverged from its remote. It needs
--force-with-lease --force-if-includesto push, never a bare--force, and never onmain/master— refuse that and explain.
Move the branch pointer back (git reset)
reset moves the current branch to another commit. The mode decides what survives:
| Mode | Branch pointer | Index | Working tree |
|---|---|---|---|
--soft |
moves | untouched — the changes stay staged | untouched |
--mixed (default) |
moves | reset — the changes become unstaged | untouched |
--hard |
moves | reset | overwritten; uncommitted work is destroyed |
- "Undo my last commit but keep the changes" is
rtk git reset --soft HEAD~1. That is the default answer to the request; reach for anything else only when the caller asked for it. rtk git reset --mixed HEAD~1when the staging should be redone from scratch too.rtk git reset --hard <ref>is gated like a force-push: state exactly which uncommitted changes will be discarded, get approval for that specific reset, and offerrtk git stash push -ufirst. The commits it drops stay reachable throughgit reflog; the uncommitted edits never entered git at all and nothing recovers them.
Reset does not rewrite the commits it leaves behind, so no force-push is needed unless the branch was already pushed at the newer commit.