Git for AI Agents: Rules, Guardrails, and Scale
"An agent with good Git habits is worth ten that just move fast."
You now know the toolkit — the safety loop, branches, the undo arsenal, the advanced moves. This chapter turns that knowledge into a system your AI assistants follow as reliably as you do: written rules they read (AGENTS.md and skills), mechanical guardrails they cannot skip (hooks), and a layout that lets several agents work the same repository at once without stepping on each other (worktrees).
6.1 Teaching Your AI to Use Git
The next level is not just using Git to manage AI — it's encoding your Git workflow so agents follow it automatically. Modern tools share a simple pattern: always-on project instructions for conventions that apply everywhere, plus skills for detailed procedures that load only when relevant.
Always-on instructions: AGENTS.md & friends
AGENTS.md is the cross-tool standard for project-wide rules — an open spec (now stewarded by the Linux
Foundation) read by OpenAI Codex, Cursor, GitHub Copilot, Gemini CLI, and twenty-plus other
agents. In monorepos, the nearest file to the code you're editing takes precedence. The one
big exception: Claude Code reads CLAUDE.md, not AGENTS.md (as of mid-2026). The documented bridge is a one-line CLAUDE.md containing @AGENTS.md — an import that pulls the shared file in — or a symlink (ln -s AGENTS.md CLAUDE.md). In VS Code, add .github/copilot-instructions.md for Copilot-specific standards. Put your team's
Git constitution here: branch naming, Conventional Commits, "never reset shared branches,"
PR expectations. Keep it concise — this loads on every session.
Agent Skills (SKILL.md)
Skills are the open Agent Skills format: a folder with a SKILL.md file (name + description in YAML frontmatter, instructions in the body). The format is portable;
the folder isn't yet — each tool has its own discovery path, like .agents/skills/ for Codex and .claude/skills/ for Claude Code — so check where your agent looks. Either way they're version-controlled workflows
your whole team shares. Agents discover skills at startup and load the full instructions only
when a task matches — perfect for detailed Git procedures without bloating every chat. Example:
a git-save-game/ skill that walks through the full save-game ritual — branch,
review, stage, commit — before every PR.
VS Code: scoped rules & custom agents
For rules that apply only to certain files, add *.instructions.md files under .github/instructions/ with an applyTo glob in the frontmatter. For specialized personas, define custom agents as .github/agents/*.agent.md — YAML frontmatter sets the name, description, tools, and model; the body holds focused instructions
(e.g., a "Git Review" agent that inspects staged diffs and suggests commit splits). Use /create-instruction or /create-agent in VS Code chat to scaffold these files.
CI & repository automations
Repo instructions also power GitHub Copilot code review and cloud agents on pull requests. Combine with GitHub Actions to auto-review PRs, flag risky Git operations, or update docs when code changes — Git remains the audit trail even when agents operate in CI.
A complete AGENTS.md you can steal
Theory is nice, but what does a Git constitution actually look like? Here's a complete, copy-pastable example. Drop it in your repo root, adjust the branch prefixes and test command to match your project, and every agent that opens the repo inherits your rules.
# Git Rules for AI Agents
## Branches
- Never commit directly to main. Always work on a branch.
- Branch names: feature/<topic> for features, fix/<topic> for
bug fixes, agent/<topic> for autonomous or experimental work.
- One branch per task. Do not reuse branches across tasks.
## Before staging anything
- Run `git status` and `git diff` first. Read the diff.
- Stage only files you intentionally changed. Never `git add .`
when the diff contains files you don't recognize.
## Commits
- Use Conventional Commits with a scope:
feat(auth): ..., fix(api): ..., refactor(parser): ...,
docs(readme): ..., test(payments): ..., chore(deps): ...
- Keep commits small and focused: one logical change per commit.
- Run the test suite (`npm test`) before every commit.
If tests fail, fix them or ask — do not commit broken code.
## Pushing and history
- Never use `git push --force`. If a push is rejected after a
rebase, use `git push --force-with-lease` — and only on your
own feature branches, never on main or shared branches.
- Never rewrite history that has been pushed and shared
(no rebase/reset/amend on public commits — use `git revert`).
## When unsure
- Stop and ask before any destructive command
(reset --hard, clean, filter-repo, branch -D).For multi-step procedures, a skill keeps the detail out of your always-on instructions. Here's a small worked example: a "save-game checkpoint" skill that encodes the status-review-stage-commit ritual from Part 2 so the agent runs it the same way every time.
---
name: save-game-checkpoint
description: Create a clean Git checkpoint after a working change.
---
# Save-game checkpoint
1. Run `git status` — list every modified and untracked file.
2. Run `git diff` and summarize the changes for the user.
3. Flag anything unexpected (unrelated files, debug prints,
secrets, lockfile churn) before proceeding.
4. Stage only the files that belong to this change:
`git add <file> <file>` — never `git add .`
5. Commit with a Conventional Commit message and scope, e.g.
`git commit -m "feat(payments): add refund endpoint"`
6. Confirm with `git log --oneline -1` and report the hash.Reviewing large AI diffs
Even well-instructed agents produce big diffs, and a 40-file diff read top-to-bottom is
where review discipline goes to die. Triage instead: run git diff --stat first to see which files changed and by how much — that one screen tells you whether the change
matches the task you assigned. Then review file by file with git diff <file>, starting with the files you'd least expect to change. When
only part of the work is good, use git add -p to stage the keepers hunk by hunk and discard the rest.
git cherry-pick the few good commits onto a fresh branch, and re-prompt for the
rest with tighter scope. Cheap branches (Part 3) exist precisely so throwing work away costs nothing.Who Wrote This — You or the Machine?
Six months from now, someone running git blame will want to know whether a human decided this line or an agent generated it. Three layers of
attribution, weakest to strongest:
- Trailers. Claude Code and Copilot append
Co-Authored-By:lines to their commits by default — you'll see these in your own history. Useful as a hint, but easy to strip or forget, so never treat them as a measurement of anything. - Per-worktree identity. Running agents in worktrees (6.3)? Give each its
own name so blame tells Agent A from Agent B:
git config extensions.worktreeConfig true, thengit config --worktree user.name "Agent A (auth)"inside each worktree. - Signing. The strong claim runs the other way: commits you sign are verifiably yours. Modern setup is SSH-based and reuses the key from Part 1 (
gpg.format ssh+commit.gpgsign true); GitHub shows "Verified" badges, and its vigilant mode flags anything unsigned that claims to be you. In a world of agents committing under borrowed names, "the human signed off here" is worth making cryptographic.
"Read AGENTS.md and follow its Git rules for every change in this session"
"Draft an AGENTS.md with our Git branch naming and Conventional Commits rules, then create a save-game-checkpoint SKILL.md to match"
6.2 Automating Quality with Git Hooks
You told your agent: "always run the tests before committing." It did — for the first three commits. Then, deep in a refactor, it forgot, and a broken commit landed in history. Rules that live in a prompt are suggestions. Rules that live in Git are law. That's what hooks are for.
A Git hook is a script that Git runs
automatically at a specific moment — before a commit is created, after a merge, before a
push. Hooks live in the .git/hooks/ directory of your repository, where Git puts a set of .sample files to get you started. One crucial detail: the .git directory is never committed, so hooks are not versioned — they don't travel with a clone. We'll fix that in a moment.
A Minimal pre-commit Hook
The pre-commit hook runs before Git
creates a commit. If the script exits with a non-zero status, the commit is blocked. Create
a file named .git/hooks/pre-commit (no file extension) and make it executable:
#!/bin/sh
echo "Running checks before commit..."
npm run lint || { echo "Lint failed - commit blocked." >&2; exit 1; }
npm test || { echo "Tests failed - commit blocked." >&2; exit 1; }chmod +x .git/hooks/pre-commitFrom now on, every git commit in this repository runs your lint and test suite
first. If either fails, nothing gets committed — no exceptions, no forgetting.
Enforcing Commit Message Standards
The commit-msg hook receives the path to
a file containing the proposed commit message. Here's a compact one that enforces Conventional Commits (messages like feat: add login form):
#!/bin/sh
# Git's own merge and revert commits ("Merge branch ...", "Revert ...")
# also pass through this hook - wave them through.
grep -qE '^(Merge|Revert)' "$1" && exit 0
if ! grep -qE '^(feat|fix|docs|style|refactor|perf|test|build|ci|chore)(\(.+\))?!?: .+' "$1"; then
echo "Commit message must follow Conventional Commits, e.g. 'feat: add login form'" >&2
exit 1
fiMaking Hooks Shareable
Since .git/hooks/ isn't versioned, your teammates (and their agents) won't get your hooks automatically. The lightweight
fix is a committed hooks folder plus core.hooksPath:
mkdir .githooks
# move your hook scripts into .githooks/, then:
git config core.hooksPath .githooks
# commit the folder - each teammate runs the config command onceIn the JavaScript world, Husky is the
popular tool that automates exactly this — it wires up the hooks path when anyone runs npm install:
npm install --save-dev husky
npx husky init
# init already created .husky/pre-commit (it runs "npm test").
# Overwrite it with whatever checks you want:
echo "npm run lint && npm test" > .husky/pre-commitgit add -p, the tests pass on code that isn't what you're committing. Tools
like lint-staged exist to close exactly that gap.git commit, your pre-commit and commit-msg hooks run exactly as if you'd
typed the command yourself. This is how you enforce standards on AI-authored code without trusting the AI to remember — the agent literally cannot commit failing code
or a sloppy message. Better yet, agents read the error output and usually fix the problem and
retry on their own.One escape hatch to know about: git commit --no-verify skips the pre-commit and commit-msg hooks entirely. It exists for genuine emergencies — a broken
hook blocking a critical hotfix — but use it sparingly. Every --no-verify is a hole in your safety net, and it's a habit you especially don't
want your agents learning.
git commit can also run it with --no-verify — and agents do learn that trick from error loops. The layer nothing can skip lives on the server: branch protection rulesets on main (require a PR, require passing CI, require an approval — section 3.3). Hooks catch problems in
seconds on the developer's machine; the ruleset is the law at the gate. Use both.Try It: The Hooks Say No
BREAKPOINT in src/app.py — try to commit it and watch pre-commit block you, then get vetoed
by commit-msg for a sloppy message. Fix both properly.Loading playground...
"Set up a pre-commit hook that runs our lint and test scripts and blocks the commit if either fails"
"Add a commit-msg hook enforcing Conventional Commits, and make the hooks shareable with the team via Husky"
6.3 Parallel Agents with Git Worktrees
Here's the endgame scenario: you want Agent A refactoring the auth module while Agent B
builds the payments feature — in the same repository, at the same time. If both agents share
one working directory, they'll trample each other's files, and every git switch by one agent yanks the floor out from under the other. Worktrees solve this.
A worktree is an extra working directory
attached to the same repository. All worktrees share one .git database — same commits, same branches, same remotes — but each directory
has its own checked-out branch, its own files on disk, and its own staging area. Creating one
takes a second, because nothing is copied except the files of the branch you check out.
# From your main checkout (~/repos/proj on main)
git worktree add ../proj-auth feature/auth-refactor
git worktree add ../proj-payments feature/payments
# See every working directory attached to this repo
git worktree list
# /Users/you/repos/proj abc1234 [main]
# /Users/you/repos/proj-auth def5678 [feature/auth-refactor]
# /Users/you/repos/proj-payments 9ab0cde [feature/payments]
# Open a terminal in each directory and start one agent per worktree:
# Terminal 1: cd ../proj-auth -> Agent A refactors auth
# Terminal 2: cd ../proj-payments -> Agent B builds paymentsIf the branch doesn't exist yet, git worktree add -b feature/payments ../proj-payments creates it in one step. Note that a branch can only be checked out in one worktree at a time
— Git enforces this, which is exactly the isolation guarantee you want: two agents can never be
editing the same branch's files.
Every worktree commits into the same repository — the branches just live in different folders until they're merged.
Why not just clone the repo twice? Clones work, but worktrees are better on every axis: the object database is shared (no duplicated gigabytes), creation is instant, remotes and config come along for free, and a commit made in one worktree is immediately visible from all the others — no pushing and pulling between your own directories. When both agents are done, merging their branches is the normal PR flow from Part 3: push each branch, open a pull request, review the diff, merge. Nothing about worktrees changes how integration works.
A few habits keep this tidy: name each worktree directory after its branch (proj-auth for feature/auth-refactor) so you always know which terminal belongs to which
agent, and clean up when a branch merges.
# Remove a worktree you're done with (its branch survives)
git worktree remove ../proj-auth
# If you deleted a worktree folder manually, clear the stale record
git worktree prune.git/config) and the stash list are per-repo, not per-worktree, so a git stash made in one worktree is visible (and poppable) from all of them. Second, always create worktrees outside the main repo folder (../proj-auth, not ./proj-auth) — a worktree nested inside the repo shows up as an untracked directory, and an agent
running git add . will sweep it up as a confusing embedded-repo pointer (Git prints a
warning — which everyone ignores).node_modules/, no .venv, no untracked .env. Agent B's first command will fail until someone runs the install step
there, and two agents starting dev servers will fight over the same port. Budget one setup
command per worktree (and a port per agent) into your plan — or into the agent's
instructions.And a sign of how central this pattern has become: the agent tools now create worktrees for you. Claude Code has claude --worktree (agents get isolated worktrees under .claude/worktrees/, auto-cleaned when untouched), Cursor spins up a worktree
per parallel agent, and desktop agent apps do it per session. When you find a mystery
directory or a worktree-quiet-fox branch, that's what it was — git worktree list is how you audit them, and now you know how to clean them up.
Two monorepo-scale companions worth knowing by name: git sparse-checkout set pkg/api materializes only the directories an agent actually needs (a worktree per agent, a package per
worktree, nothing else on disk), and git maintenance start schedules background housekeeping — worth turning on once a fleet of agents starts churning out
objects faster than any human team ever did.
Try It: One Repo, Three Working Directories
git worktree remove. (The playground simulates the
bookkeeping — the terminal stays in the main worktree.)Loading playground...
"Create a worktree for branch feature/payments at ../proj-payments and start working there"
"List all worktrees in this repo, and remove any whose branches are already merged into main"
Loading challenge...