teachyou.ai academy
← All posts
Claude Code

Claude Code Git Workflow: Commits, Branches and PRs

Pramod Dutta · Jun 23, 2026 · 15 min read

Why Git Discipline Matters Even More With AI Coding Agents

The moment you hand an AI agent shell access, your git history becomes a liability or an asset depending on how you set the guardrails. Claude Code can stage files, write commit messages, open branches, and even file pull requests on your behalf. That is enormously useful when you are moving fast, but it also means a careless setup can produce a repository full of vague commit messages, force-pushed branches, and PRs nobody can review. This article is about the workflow that actually works in practice: how to get Claude Code to commit cleanly, branch sensibly, and ship pull requests that your teammates will thank you for, instead of ones they mute in Slack.

The core idea is simple. Git is not just a backup mechanism, it is the audit trail of every decision your team made. When an agent is writing half your code, that audit trail matters more, not less. You want commits that explain *why* a change happened, branches that map cleanly to units of work, and PRs that give a human reviewer everything they need without re-deriving the diff from scratch. Claude Code can do all of this if you tell it how you want it done, and this guide walks through the exact configuration and prompting patterns to get there.

Setting Ground Rules With CLAUDE.md

Claude Code reads a CLAUDE.md file at the start of a session, and this is where your git conventions belong. Anything you would tell a new hire on day one about "how we commit here" should live in this file, either at the project root or in your global ~/.claude/CLAUDE.md for rules that apply across every repo you touch.

A common first requirement is stripping AI attribution from commit messages. Some teams want it, many do not, and it is trivial to configure either way:

## Git commits

- Never add "Co-Authored-By" trailers to commits.
- Do not add a "Generated with Claude Code" footer.
- Write commit messages that explain why the change was made, not just what changed.
- Prefer creating new commits over amending existing ones, unless explicitly told to amend.
- Never run destructive commands (reset --hard, push --force, branch -D) without explicit confirmation.

Claude Code treats these instructions as binding for the session, and because CLAUDE.md is just a markdown file checked into your repo (or living globally on your machine), the whole team inherits the same conventions the moment they clone the project and start using the CLI. This is the single highest-leverage thing you can do to keep an AI-assisted git workflow sane: write the rules down once, and stop repeating them in every prompt.

It is also worth being explicit about scope. Global rules in ~/.claude/CLAUDE.md apply to every project on your machine, while a project-level CLAUDE.md can override or add to them for that specific repo. If you contract for multiple clients with different commit conventions, this separation is what keeps one client's house style from leaking into another's repository.

There is a nuance worth calling out for teams that are still deciding where they land on AI attribution. Some open source projects want every AI-assisted commit clearly labeled, because it matters for license provenance and contributor trust. Other teams, particularly ones shipping proprietary product code, prefer commits that read exactly like any other commit in the log, because six months from now nobody scanning git blame needs to know or care which commits were typed by a human and which were typed by an agent under human direction, only that someone reviewed and owns the change. Neither position is wrong, but you do need to pick one and encode it, because the default behavior of any AI coding tool will lean toward over-attribution unless told otherwise, and an inconsistent history where some commits carry a footer and others do not just looks sloppy.

How Claude Code Actually Builds a Commit

When you ask Claude Code to commit, it does not just run git commit -m "stuff" and move on. The default workflow (and the one baked into the tool's internal behavior) runs a small pipeline:

  1. Check git status for untracked files
  2. Check git diff for staged and unstaged changes
  3. Check git log for the recent commit style in the repo
  4. Draft a message that matches that style
  5. Stage the relevant files by name
  6. Commit
  7. Verify with git status again

That third step matters more than people give it credit for. Claude Code looks at your last several commits to infer whether your team uses Conventional Commits (feat:, fix:, chore:), a more narrative style, or something in between, and it tries to match that convention rather than imposing its own. If your log is a mess of styles, you will get a mess of a suggestion back, so it pays to have at least the last dozen commits in your target branch be reasonably consistent.

Here is a realistic example of the kind of commit that results from a bug fix session:

git status
git diff
git log --oneline -10

git add src/lib/payments/razorpay-webhook.ts

git commit -m "$(cat <<'EOF'
fix: harden Razorpay webhook signature check against timing attacks

Switched the signature comparison to crypto.timingSafeEqual instead
of a plain string equality check, since the naive comparison leaked
timing information that could theoretically be used to forge a valid
signature over many requests.
EOF
)"

Note the heredoc. Multi-line commit messages passed through -m "..." on the command line are notoriously easy to mangle with escaping issues, so Claude Code (and honestly, any competent engineer scripting git) pipes the message through a heredoc to preserve line breaks and quoting exactly as written.

A rule worth internalizing: never let an agent use git add -A or git add . by default. Both will happily stage a stray .env file, a debug log, or a large binary that got dropped into the working tree during testing. Ask for files to be staged by name, or review the git status output yourself before the commit runs. This is exactly the kind of guardrail that belongs in CLAUDE.md rather than something you re-explain every session.

Branching Strategy for Agent-Driven Work

Branches are where an AI-assisted workflow either stays organized or turns into chaos. The pattern that works well is to treat each Claude Code session as scoped to one branch, one topic. Do not let a single long-running session accumulate unrelated changes across multiple features on the same branch, because when it comes time to write the PR description, you will be stuck trying to explain three unrelated things in one summary.

A sensible branch-per-task flow looks like this:

git checkout main
git pull origin main
git checkout -b fix/webhook-timing-attack

# ... Claude Code makes changes, runs tests, commits ...

git push -u origin fix/webhook-timing-attack

If you are running multiple Claude Code sessions in parallel on the same repository, git worktrees solve a real problem: two sessions cannot safely share one working directory because checking out a different branch in one will yank the files out from under the other. Worktrees give each session its own directory backed by the same .git history:

git worktree add ../teachyou-fix-webhook fix/webhook-timing-attack
git worktree add ../teachyou-feat-coupons feat/coupon-codes

# each worktree is an independent checkout;
# run a separate Claude Code session in each one

This is particularly useful when you want one agent fixing a bug while another builds a feature, without either one's file changes interfering with the other's test runs. When you are done, clean up with git worktree remove ../teachyou-fix-webhook rather than just deleting the directory, since git keeps metadata about the worktree that a manual rm -rf leaves behind as a dangling reference.

Naming matters too. Prefixing branches with fix/, feat/, chore/, or refactor/ costs nothing and makes the intent of a branch legible at a glance in git branch -a output, in your CI dashboard, and in the eventual PR title. Tell Claude Code this convention once in CLAUDE.md and it will follow it for every branch it creates afterward.

There is also a question of how long a branch should live before it merges. Agent-assisted branches tend to move faster than human-only ones simply because the iteration loop, write code, run tests, fix failures, is compressed into a single session instead of spread across a day. That speed is an argument for keeping branches short-lived rather than long-lived. A branch that sits open for two weeks while main moves underneath it accumulates merge debt, and rebasing a large agent-authored diff against a moved main is exactly the kind of operation you want to do deliberately, with a clean working tree, rather than as an afterthought:

git fetch origin
git rebase origin/main

# if conflicts appear, resolve them file by file,
# then continue
git add path/to/resolved-file.ts
git rebase --continue

Prefer rebase over merge for keeping a feature branch current with main if your team's convention favors a linear history, but confirm this preference explicitly, since some teams intentionally avoid rebase workflows to preserve the true chronological record of when work happened. Either way, this is a team-level decision that belongs in CLAUDE.md, not something an agent should guess at mid-session.

Reviewing the Diff Before It Becomes a Commit

The single biggest failure mode in AI-assisted git workflows is not a bad commit message, it is an unreviewed diff. Before you let Claude Code commit anything, get in the habit of asking it to show you git diff output, or better, walk through the reasoning behind each changed file.

A useful pattern is to explicitly separate "make the change" from "commit the change" into two distinct asks:

# First: understand what changed and why
git diff --stat
git diff src/lib/payments/

# Then, only after you've reviewed it, ask for the commit

If the diff touches more files than you expected, that is a signal worth stopping on. Claude Code is generally good about scoping changes to what was asked, but agents can wander, especially in tasks that touch shared utility files or types used across a codebase. Catching that at diff-review time is far cheaper than catching it in a PR review three days later, and dramatically cheaper than catching it in production.

It also helps to run your test suite as part of this review step rather than after the commit:

npm test -- --watchAll=false
npm run lint
npm run typecheck

Asking Claude Code to run these before committing, and to fix any failures before proposing the commit message, keeps your git history free of "fix lint errors from previous commit" follow-up commits that clutter the log and make git bisect painful later.

Generating Pull Requests That Reviewers Actually Want to Read

Once your branch has a clean set of commits, the PR is where the story gets told. Claude Code's PR workflow uses the GitHub CLI (gh) under the hood, and the quality of the resulting PR description depends heavily on how much history it is given to work from.

The key instruction to give Claude Code here is: look at *all* commits in the PR, not just the latest one. A PR that spans five commits needs a summary that covers the arc of all five, not a recap of the last thing that happened to get committed. The practical sequence looks like this:

git status
git diff origin/main...HEAD
git log origin/main..HEAD --oneline

That middle command, the triple-dot diff, is the one people forget. git diff main...HEAD shows you the changes introduced on your branch since it diverged from main, which is exactly what a reviewer will see in the PR, as opposed to git diff main HEAD which can pull in unrelated changes if main has moved forward since your branch was cut.

From there, opening the PR looks like this:

git push -u origin fix/webhook-timing-attack

gh pr create --title "Harden Razorpay webhook signature check" --body "$(cat <<'EOF'
## Summary
- Replace naive string comparison with crypto.timingSafeEqual for
  webhook signature verification
- Add a regression test covering malformed and mismatched signatures

## Test plan
- [ ] Run the payments test suite locally
- [ ] Trigger a test webhook from the Razorpay dashboard in staging
- [ ] Confirm invalid signatures are still rejected with a 400
EOF
)"

A PR title under about 70 characters keeps it readable in GitHub's UI and in Slack notifications, which is why the detail belongs in the body, not crammed into the title. The test plan section is not decoration. It tells the reviewer, and future you, exactly what "verified" means for this change, and it doubles as a checklist during the actual review.

If your team uses required checks, mention them explicitly in your CLAUDE.md so Claude Code knows to wait for CI rather than declaring the PR done the moment gh pr create returns:

## PR checklist

- Do not report a PR as ready until CI checks pass.
- Run `gh pr checks <number> --watch` after opening a PR and report the result.
- Never merge a PR without explicit approval from the user.

Handling Pre-Commit Hooks and Failed Checks

Pre-commit hooks (via Husky, pre-commit, or a custom git hook) are where a lot of AI-driven commits go sideways if the agent is not told how to react to a failure. The correct behavior when a hook fails is straightforward but easy to get wrong: fix the underlying issue, re-stage, and create a new commit. Do not amend.

The reasoning is subtle but important. If a pre-commit hook rejects a commit, the commit never actually happened, git aborted before writing it. So an --amend at that point does not touch the commit that just failed, it reaches backward and modifies whatever commit came *before* it, potentially destroying or silently altering work that was already reviewed or pushed. This is exactly the kind of destructive-by-accident behavior that a strict git policy should prevent.

git commit -m "feat: add coupon code validation"
# pre-commit hook fails: eslint error in coupon-validator.ts

# fix the lint error, then:
git add src/lib/coupons/coupon-validator.ts
git commit -m "feat: add coupon code validation"
# this is a fresh commit attempt, not an amend

Similarly, never skip hooks with --no-verify unless you explicitly asked for that as an escape hatch, and never bypass commit signing with --no-gpg-sign if your org requires signed commits. Both flags exist for legitimate emergency use, but an agent reaching for them by default defeats the entire point of having the hook or the signing requirement in the first place. Put this explicitly in your CLAUDE.md:

- Never skip git hooks with --no-verify unless explicitly requested.
- Never bypass commit signing with --no-gpg-sign or -c commit.gpgsign=false.
- If a pre-commit hook fails, fix the issue and create a new commit. Never amend.

Recovering When Something Goes Wrong

Even with good guardrails, you will occasionally end up somewhere you did not intend, a commit on the wrong branch, a message that needs fixing before it is pushed, or a merge conflict that needs a human's judgment call. The recovery commands themselves are ordinary git, the discipline is in insisting Claude Code explain the current state before it touches anything destructive.

For an unpushed commit with a message you want to fix:

git commit --amend -m "fix: correct webhook signature comparison"

This is safe specifically because the commit has not been pushed yet, nobody else has based work on it, so rewriting it does not orphan anyone else's history. The moment a commit is pushed and potentially pulled by a teammate, amending becomes a coordination problem, not just a local one.

For a merge conflict, resist the urge to let an agent auto-resolve everything silently. Ask it to surface the conflicting hunks and explain what each side changed:

git status
git diff --name-only --diff-filter=U

That second command lists only the files with unresolved conflicts, which is the fastest way to scope the problem before deciding, together with the agent, how each conflict should resolve. Auto-resolving semantic conflicts (two different bug fixes touching the same function, for instance) is exactly the kind of judgment call that should stay with a human, even when the mechanical parts of git are being driven by an agent.

Building the Habit, Not Just the Commands

None of this works as a one-time setup. The value compounds when the conventions in your CLAUDE.md are actually followed session after session, when every branch name tells you what it is for at a glance, and when every PR description reads like someone who understood the change wrote it, because in a real sense, they did, with an AI doing the typing under clear instructions. The commands in this article are not exotic. git diff, git log, gh pr create, heredocs for message formatting, worktrees for parallel work, none of it is new. What is new is having an agent that can run all of it correctly and consistently, provided you have told it, once and clearly, what "correctly" means for your team.

If you want to go deeper into how Claude Code handles the rest of the development lifecycle beyond git, from slash commands and hooks to multi-file refactors and test-driven workflows, that is exactly what we cover hands-on in the Claude Code Tutorial for Beginners course on teachyou.ai, where you will build real projects with the same discipline this article describes, instead of just reading about it.