Git is the most common surface Claude Code touches and also the most consequential. The model commits, pushes, opens PRs, switches accounts, merges. A wrong move on any of those leaves a trail that other humans see. So the git rule is the biggest one in my ~/.claude/rules/ folder and the one I have rewritten the most times.

This is the version I have used for about three months without changes, which is the longest any version has survived.

The story behind the rule

Three separate failures shaped the rule:

The first was a commit message format that bounced off the team’s CI. Our CI required prefix(ticket): description. The model produced Add background task upload. The CI rejected. I rewrote the message. Two days later it happened again with a different sub-task. The model is not going to learn the convention from one fix. It needs the rule in writing.

The second was a multi-account mishap. I have two GitHub accounts (one personal, one for work). The model ran gh pr create while the active account was the wrong one. The PR was opened as the wrong user on a private work repo. I had to delete it and re-open under the correct account. The model did not know which account was active and did not check.

The third was the deletion of a branch after merge. The model finished a feature, merged the PR with gh pr merge --delete-branch, and the source branch was gone. Two hours later I realized I needed to look at one of the commits in that branch for a related issue. The branch was recoverable from the merge SHA, but the principle was the same: branch cleanup deserves its own deliberate session, not an automatic flag.

Each of these is in the rule now.

The rule

The full rule is long. I will show it in sections.

# Git Convention

## Pre-commit Check

- Before `git commit`, check `git status` for `.env.*` files
  being staged.
- If found, verify `.gitignore` has `.env.*` with `!.env.example`
  whitelist.
- Only `.env.example` (placeholder values, no secrets) should be
  tracked.

This is the smallest gate. A .env file accidentally committed is a credential leak. The rule names the file pattern (.env.*) and the safe exception (!.env.example). The model checks before commit, sees the issue, asks the user before proceeding.

## Commit Format

`<prefix>(<ticket-id>): <description>` (max 50 chars, imperative form)

- MUST run `git branch --show-current` before every commit to get
  the actual current branch name. NEVER rely on git status from
  conversation start, the user may have switched branches.
- Extract ticket ID from that branch name (e.g., `TICKET-714` from
  branch `TICKET-714` or `feature/TICKET-714-description`).
- Include ticket ID in parentheses after prefix.
- If branch has no ticket ID, use the branch name instead
  (e.g., `feat(main): ...`, `fix(staging): ...`).

Prefixes: `feat:`, `fix:`, `chore:`, `docs:`, `style:`, `refactor:`,
`perf:`, `test:`, `ci:`, `build:`, `revert:`, `security:`, `deps:`.

Breaking changes: add `!` after prefix (e.g., `feat!:`).

Examples:

- `feat(TICKET-714): add background task upload`
- `fix(SCRUM-123): resolve login timeout issue`
- `refactor(JIRA-456): simplify auth logic`

The “run git branch --show-current before every commit” line is critical. Without it, the model uses the branch name it saw at the start of the conversation, which is stale if the user switched branches mid-conversation. I have shipped a commit with the wrong ticket prefix because of stale state. The rule fixes it by requiring a fresh check.

## Branch Naming

`<type>/<ticket-id>-<short-description>`

Types: `feature/`, `fix/`, `hotfix/`, `refactor/`, `docs/`, `test/`

Standard. Branch names map cleanly to ticket IDs.

## Attribution

NEVER add to commit messages or PR descriptions:

- `Co-Authored-By:` trailers (any author)
- `Generated with Claude`, `Generated with AI`, or similar lines
- Emojis

Keep commit messages and PRs clean and professional.

This is taste, but it is also defense. A commit message that brags about being AI-generated invites a code review with “did you actually read this?” written all over it. A clean commit message lets the diff speak for itself. The emoji ban here is the same rule as in code-style, scoped to git.

The multi-account section

This is the part the second failure drove.

## GitHub CLI Multi-Account

User manages multiple `gh` accounts. Before any `gh` operation
(create repo, PR, issue, etc.):

1. List available accounts: `gh auth status`.
2. Check active account vs. required account.
3. Infer correct account from remote URL:

git remote -v # extract owner/org, match against gh auth status

4. Switch if mismatch: `gh auth switch --user <account>`.
5. Then proceed.

### No remote URL (new repo)

Cannot infer from remote, MUST ask user before proceeding which
account to use.

### General rule

NEVER assume which account to use. Directory path is NOT a reliable
signal. When in doubt for any reason, ask the user explicitly.

The “directory path is not a reliable signal” line is the key one. The model wants to assume “this folder is in ~/work/, so use the work account” or “this folder mentions <your-org>/<repo> in its name, so use that account.” Both are wrong half the time, because directory paths drift, repos move, accounts are sometimes used cross-purpose. The only reliable signal is git remote -v for the remote URL, then matching against gh auth status.

The merge rules

This is the part the worktree-incident in the agent-safety rule prevents on the agent side. The git rule prevents it on the main-session side.

## Branch Merge Rules

### NEVER merge environment branches into ticket branches

# FORBIDDEN
git merge origin/development   # NEVER
git merge origin/release       # NEVER

### Only `production` (or main) can be merged into a ticket branch

# ALLOWED, production/main is the source of truth
git merge origin/production    # OK

### Ticket-to-ticket dependency is allowed

# ALLOWED, explicit dependency between tickets
git merge origin/SCRUM-A       # OK if SCRUM-B depends on A

The merge direction matters. development should flow into production, not the other way around (production-flow is main upward). Merging development into a ticket branch pulls in everyone else’s unmerged work, polluting the ticket branch with unrelated changes. Merging main (or production) into a ticket branch is fine because main is the source of truth.

The cross-branch section forbids git checkout as a way to perform a merge:

## Cross-branch merges, prefer cloud PR, never switch branch

When merging between two existing branches: stay on whatever branch
you are already on. The hard rule is: do not `git checkout` to
another branch just to merge.

Cloud PR (preferred, gives audit trail plus CI gate):
- `gh pr create --base <target> --head <source>` works from any
  branch, no checkout.
- `gh pr merge <PR#> --merge` (or `--squash`/`--rebase` per
  project convention).

Local merge (acceptable when you do not need review or CI):
- If you are already on `<target>`: pull `<source>` directly into
  your current branch. No checkout, no switch.

Forbidden pattern: `git checkout <target> && git merge <source>
&& git push`. Switching branches mid-session resets the working
tree, orphans scratch files, and breaks any monitoring or debug
state.

The “forbidden pattern” line is the most-cited part of the whole rule in my own debugging sessions. I have lost scratch files in a checkout more than once. Now the model knows not to suggest the pattern, and I do not suggest it to myself either.

Branch cleanup

## Branch Cleanup After Merge

After a PR is merged, do NOT delete the source branch, neither
local nor remote.

# FORBIDDEN after merge
git branch -d feature/SCRUM-123        # NEVER (local)
git branch -D feature/SCRUM-123        # NEVER (local, force)
git push origin --delete feature/...   # NEVER (remote)
gh pr merge --delete-branch ...        # NEVER (use --no-delete-branch
                                       #         or omit)

The user manages branch cleanup themselves on their own schedule.

The justification is the third failure I described. Branch cleanup deserves its own focused session. Mixing it with implementation work means a typo or a confused merge state can delete the wrong branch. Cleanup is a separate task, run in a separate session, with a clean head.

When the rule does not apply

The rule is global, but there is one repo where it is loosened: my ~/.claude/ config repo, branch-per-machine, low blast radius. In that repo, the commit-and-push convention is auto-push after commit, because the alternative is constant noise. The override is local to that directory’s CLAUDE.md, not a relaxation of the global rule. Everywhere else, the global wins.

The other near-exception is open-source contributions. Some upstream maintainers prefer commit messages without ticket IDs (because they have no ticket system). For those projects, I set a per-repo override that says “no ticket prefix needed, just prefix: description.”

Variants you might want

  • Conventional commits with scope. A common variant adds a scope: feat(api): add upload. The current rule uses prefix(ticket): ... because tickets are my project’s primary identifier. If your project uses scopes instead, swap them in.
  • Squash-vs-merge defaults. I default to --squash for ticket branches and --merge for environment branches. The rule could enumerate these, but I prefer to leave the choice to per-PR judgment.
  • PR template enforcement. If your team uses a PR description template, mention it in the rule. The model will fill in the template instead of writing free-form.
  • Forbidden file patterns. The .env.* check is one example. You might extend to *.pem, secrets/*, credentials.json. The rule is a natural place to enumerate these.
  • Author identity rules. If you have specific git config user.email requirements per project, document them in the rule so the model can check before committing.

Closing note

This rule is the one I lean on most across machines. The branch-per-machine post in the claude-code from zero series explains why I sync my ~/.claude/ config across machines via git, and the git rule is the largest single thing in there. Same git, three different machines, same conventions everywhere.

If you copy any of these rules into your own setup, the multi-account section and the merge-without-checkout section are the two with the highest leverage. The commit format is taste; the merge discipline is safety.