BlogGit and worktrees
Should I let Claude Code commit on its own?
Letting Claude Code commit is a reasonable default as long as it commits on its own branch or worktree. A local commit is easy to read and easy to undo. Pushing is the step to keep behind your approval: add an ask or deny rule for git push, and review the commits before anything leaves your machine.
The question is less "commit or not" and more "where does the agent stop". Here is how to set that line in Claude Code.
What Claude Code does by default
- It knows how to commit. Claude Code gives Claude built-in instructions for writing commits and pull requests, plus a git status snapshot at the start of the conversation. The
includeGitInstructionssetting removes both if you use your own git workflow. - It signs its commits. By default commits get a
Co-Authored-Bytrailer and PR descriptions get an attribution line. Theattributionsetting changes or hides them. - The permission mode decides whether it asks. In Manual mode (
default), every Bash command outside a built-in read-only set prompts, so eachgit commitasks first. As of v2.1.283, auto mode is the built-in starting mode for interactive sessions. In auto mode a classifier reviews actions instead of you. By default it blocks force pushes,git reset --hard,git clean -fd, andgit commit --amendon a commit the session didn't create. It trusts the remotes configured when the session started, so an ordinary push may go through without a prompt.
Other tools pick a different default. Aider, for example, commits every edit it makes, and you turn that off with --no-auto-commits.
A setup that works: commit yes, push asks
1. Give the agent its own branch
claude --worktree fix-rounding
The session works under .claude/worktrees/fix-rounding/ on the branch worktree-fix-rounding. Its commits never land on main by accident.
2. Allow commits, gate pushes
In .claude/settings.json (shared with the team) or ~/.claude/settings.json (only you):
{
"permissions": {
"allow": [
"Bash(git add *)",
"Bash(git commit *)"
],
"ask": [
"Bash(git push *)"
]
}
}
Rules are evaluated in the order deny, then ask, then allow. An ask rule still forces a prompt in auto mode, so this works whichever mode you start in. Replace ask with deny if you never want the agent to push.
3. Close the gaps the rule leaves
Bash rules match the command text, not the program. The docs list forms that Bash(git push *) doesn't stop: git -C . push origin main, git -c push.default=current push origin main, git 'push' origin main.
A PreToolUse hook sees the full command and can block it. Save this as .claude/hooks/no-push.sh and make it executable with chmod +x:
#!/bin/bash
CMD=$(jq -r '.tool_input.command')
if echo "$CMD" | grep -Eq '(^|[[:space:]])push([[:space:]]|$)'; then
echo "Pushing is reserved for the human. Commit and stop." >&2
exit 2
fi
exit 0
Register it in .claude/settings.json:
{
"hooks": {
"PreToolUse": [
{
"matcher": "Bash",
"hooks": [
{
"type": "command",
"if": "Bash(git *)",
"command": "\"$CLAUDE_PROJECT_DIR\"/.claude/hooks/no-push.sh"
}
]
}
]
}
}
Exit code 2 blocks the call and sends the stderr message to Claude so it can adjust. The check is blunt on purpose: it also blocks a commit message that contains the word "push" on its own. Neither rules nor hooks are a security boundary around git. For enforcement that doesn't depend on the command text, Claude Code's docs point to the sandbox.
4. Set the attribution you want
{
"attribution": {
"commit": "Generated with AI\n\nCo-Authored-By: AI <ai@example.com>",
"pr": ""
}
}
This replaces the commit trailer with your own text and removes the PR line.
"attribution": false hides everything, on Claude Code v2.1.281 or later.
5. Review before you push
git log --oneline main..worktree-fix-rounding
git diff main...worktree-fix-rounding
If a commit is wrong, undo it and keep the changes:
git reset --soft HEAD~1
Don't rely on /rewind for this. Checkpoints cover Claude's file edits in the session, files changed only by Bash commands aren't tracked, and the docs say plainly that checkpoints are not a replacement for version control.
Limits of this approach
- Text rules are not a boundary. A determined or confused agent can phrase a command your rule doesn't match. The hook narrows that, it doesn't close it.
- Many small commits. An agent that commits often leaves a noisy history. Squash before merging, or ask it to commit once at the end.
- The commit message can be wrong. It describes what the agent thinks it did. Read the diff, not just the message.
- An ask rule means a waiting agent. If you run several sessions, a push prompt in a background tab can sit there unnoticed.
With Kanbell
Kanbell doesn't commit, push or enforce permission rules. Claude Code does that. Kanbell helps with the two parts around it:
- Seeing what was committed. Each project window has a Git panel next to the terminals: changes, diffs, history and worktrees. You read an agent's commits and its uncommitted changes without leaving the window where it runs.
- Seeing the push prompt. When a session waits on a permission, Kanbell shows an orange dot in the window, the menu bar and the overview, and sends a macOS notification. ⌘⇧J jumps to the next agent that is waiting. This works for Claude Code, Codex CLI and Gemini CLI.
Kanbell is a macOS app for Apple Silicon. There is no account and no Kanbell server.
FAQ
Does auto mode let Claude Code push without asking?
It can. Auto mode's classifier trusts the remotes configured when the session started and blocks force pushes by default, but an ordinary push to a trusted remote may run. An explicit ask rule such as Bash(git push *) still forces a prompt.
How do I remove the Co-Authored-By line from Claude Code commits?
Set attribution in settings.json. attribution.commit changes or hides the trailer, attribution.pr the PR text, and attribution: false hides all attribution on Claude Code v2.1.281 or later.
Can /rewind undo a commit?
No. Checkpoints are for quick session-level recovery and the docs say they are not a replacement for version control. Use git, for example git reset --soft HEAD~1 to undo the last commit and keep its changes.
Is a deny rule on git push enough?
It stops the usual form, git push origin main, but Bash rules match command text, so git -C . push is not matched. Add a PreToolUse hook or the sandbox if you need a stronger boundary.