Module 7: Integrations: Git, SDK, and Remote Control
Git workflows with Claude Code
Git workflows with Claude Code
Overview
Git and Claude Code are a natural pair. Both live in the terminal, both work with code files, and both follow structured workflows. The difference is that now you don't have to type every git add, git commit -m "...", and git push by hand — Claude Code can run the whole Git flow for you, from creating branches to opening Pull Requests.
But "Claude Code knows Git" does not mean "let it do whatever it wants with your history". This capsule covers how to integrate Git with Claude Code under control: which operations to delegate, which patterns to follow, how to guarantee semantic commits, and how to keep a clean history when an AI agent is contributing code.
The skill isn't getting Claude to run Git commands — it's designing a workflow where Git and Claude Code make each other stronger.
Claude Code and Git: what it can do
Claude Code has full access to Git through the terminal. It doesn't use a special API — it runs the same commands you would run. That means it can do everything Git allows:
Basic operations
| Operation | Command | Claude Code can... |
|---|---|---|
| Check status | git status | Verify which files changed |
| See differences | git diff | Analyze changes line by line |
| Stage files | git add | Stage selectively or completely |
| Commit | git commit -m "..." | Commits with descriptive messages |
| See history | git log | Analyze previous commits |
| Create a branch | git checkout -b | Branches with descriptive names |
| Switch branch | git checkout / git switch | Navigate between branches |
| Merge | git merge | Integrate branches |
| Push | git push | Send changes to the remote |
Advanced operations
| Operation | What Claude Code does |
|---|---|
| Pull Requests | Uses gh pr create to open PRs with a detailed description |
| Code review | Analyzes diffs and explains what changed and why |
| Merge conflicts | Reads the conflicts, understands both sides, proposes a resolution |
| Interactive rebase | Helps reorganize, squash, or edit commits |
| Amend commits | Modifies the last commit (message or content) |
| Cherry-pick | Picks specific commits from other branches |
| Stash | Saves changes temporarily |
| Bisect | Finds the commit that introduced a bug |
What Claude Code brings that Git doesn't have
The key difference: Claude Code understands the context of the changes. Git knows you changed line 42 of auth.py. Claude Code knows you changed token validation because the old version didn't handle expired tokens.
Git alone:
git commit -m "update auth.py" ← what changed? why?
Claude Code + Git:
git commit -m "fix(auth): handle expired JWT tokens
Previously, expired tokens returned a generic 401 error.
Now returns a specific 'token_expired' error code with
the expiration timestamp, allowing clients to refresh
automatically."
Workflow Pattern 1: Feature Branch
The most common pattern. Claude Code creates a branch, implements the feature, makes progressive commits, and opens a PR.
Basic example
You: Create a /api/health endpoint that returns the server's
status. Work on a separate branch and make a commit
when you're done.
Claude Code runs:
git checkout -b feature/health-endpoint
# ... implements the endpoint ...
git add src/routes/health.py
git commit -m "feat(api): add /health endpoint
Returns server status including uptime, version, and
database connection status. Responds with 200 for healthy
and 503 for degraded state."
Intermediate example
You: Implement the email notification system. Create a branch,
make separate commits for each logical step, and open a PR
when you're done.
Claude Code runs:
git checkout -b feature/email-notifications
# Step 1: model and schema
git add src/models/notification.py src/schemas/notification.py
git commit -m "feat(notifications): add Notification model and schema
SQLAlchemy model with fields: id, user_id, type, subject,
body, sent_at, read_at. Pydantic schema for validation."
# Step 2: sending service
git add src/services/email_service.py
git commit -m "feat(notifications): add email sending service
Uses SMTP with async support. Includes retry logic (3 attempts)
and template rendering with Jinja2."
# Step 3: API endpoint
git add src/routes/notifications.py
git commit -m "feat(api): add notification endpoints
POST /notifications/send - send notification
GET /notifications - list user notifications
PATCH /notifications/:id/read - mark as read"
# Step 4: tests
git add tests/test_notifications.py
git commit -m "test(notifications): add unit and integration tests
Covers email sending, retry logic, template rendering,
and all API endpoints. Uses mock SMTP for unit tests."
# Open the PR
gh pr create \
--title "feat: email notification system" \
--body "## Summary
- New Notification model with SQLAlchemy
- Email service with async SMTP and retry logic
- REST endpoints for sending and managing notifications
- Full test coverage
## Testing
- Run: pytest tests/test_notifications.py
- Manual: POST to /notifications/send with valid payload"
When to use this pattern
- New features that touch multiple files
- Changes that need code review before merge
- Work that may take several Claude Code sessions
- Projects with CI/CD that runs tests on PRs
Workflow Pattern 2: Commit-as-you-go
Claude Code makes a commit after every meaningful change. The history reflects the progress step by step.
Basic example
You: Refactor the authentication module. After each logical
change, make a commit explaining what you did and why.
Claude Code works incrementally:
git commit -m "refactor(auth): extract token validation to separate module
Moves JWT validation logic from auth_middleware.py to
token_validator.py. Reduces middleware complexity and
makes validation reusable across routes."
# ... next change ...
git commit -m "refactor(auth): replace hardcoded expiry with config
Token expiration was hardcoded to 3600s. Now reads from
AUTH_TOKEN_EXPIRY env var with 3600s default."
# ... next change ...
git commit -m "refactor(auth): add refresh token rotation
Implements refresh token rotation: each refresh generates
a new refresh token and invalidates the previous one.
Prevents refresh token replay attacks."
When to use this pattern
- Refactors where you want a granular history
- Long sessions where every step must be reversible
- When you work without a PR (commits straight to a development branch)
- To learn: each commit documents what Claude did and why
Workflow Pattern 3: Review Before Commit
Claude Code shows the changes, you review them, and then Claude commits. The safest pattern for critical code.
Basic example
You: Fix the bug in the price calculation. Show me the diff
before committing — I want to review it.
Claude Code:
I've identified the bug and applied the fix. Here's the diff:
--- a/src/services/pricing.py
+++ b/src/services/pricing.py
@@ -45,7 +45,7 @@ def calculate_total(items, discount):
- total = sum(item.price for item in items)
- return total - discount
+ subtotal = sum(item.price * item.quantity for item in items)
+ discount_amount = subtotal * (discount / 100)
+ return max(subtotal - discount_amount, 0)
The bug was twofold:
1. It didn't multiply price × quantity
2. It applied the discount as an absolute value instead of a percentage
Want me to make the commit?
You: Yes, go ahead.
git add src/services/pricing.py
git commit -m "fix(pricing): correct total calculation with quantity and percentage discount
Two bugs fixed:
1. Price calculation now multiplies by item quantity
2. Discount applied as percentage instead of absolute value
3. Added floor of 0 to prevent negative totals"
When to use this pattern
- Production code or critical code
- When you're not 100% sure what Claude is going to change
- To build trust: you review first, you trust more later
- Areas of the codebase you don't fully know
Conventional Commits with Claude Code
Conventional commits are a standard for commit messages that makes it easy to generate changelogs, do semantic versioning, and understand the history.
The format
<type>(<scope>): <description>
[optional body]
[optional footer(s)]
The types Claude Code uses
| Type | When it's used | Example |
|---|---|---|
feat | New feature | feat(api): add user registration endpoint |
fix | Bug fix | fix(auth): handle expired tokens correctly |
docs | Documentation only | docs(readme): add installation instructions |
style | Formatting, no logic change | style(lint): fix indentation in models |
refactor | Code change that's neither a fix nor a feature | refactor(db): extract query builder |
test | Adding or fixing tests | test(auth): add token expiration tests |
chore | Maintenance (deps, configs) | chore(deps): update fastapi to 0.104 |
perf | Performance improvement | perf(queries): add index on user_email |
ci | CI/CD changes | ci(github): add Python 3.12 to test matrix |
How to ask Claude Code to use conventional commits
In your CLAUDE.md:
# Git conventions
- Use conventional commits: type(scope): description
- Types: feat, fix, docs, style, refactor, test, chore, perf, ci
- Scope: the module or area affected (auth, api, db, ui, etc.)
- Description: imperative, lowercase, no trailing period
- Body: explain the "why", not the "what" (the diff already shows the what)
- Max 72 characters on the first line
With that instruction in CLAUDE.md, Claude Code will produce consistent commits automatically.
Pull Requests with Claude Code
Claude Code can create complete Pull Requests using the gh CLI (GitHub CLI). It doesn't just run the command — it generates detailed descriptions based on the actual changes.
Basic example
You: Open a PR for this branch.
gh pr create \
--title "feat: add email notification system" \
--body "## Summary
Adds a complete email notification system with async SMTP support.
## Changes
- New \`Notification\` model (SQLAlchemy)
- Email service with retry logic and Jinja2 templates
- REST endpoints: send, list, mark as read
- Full test coverage (unit + integration)
## Testing
\`\`\`bash
pytest tests/test_notifications.py -v
\`\`\`
## Screenshots
N/A (backend only)"
Advanced example: a PR with full context
You: Open a PR with a detailed description. Include what changed,
why, how to test it, and any breaking changes.
Claude Code analyzes every commit on the branch, the diffs, and generates:
gh pr create \
--title "refactor: migrate auth from sessions to JWT" \
--body "## Summary
Migrates the authentication system from server-side sessions to
stateless JWT tokens. This eliminates the Redis session store
dependency and enables horizontal scaling.
## Motivation
The current session-based auth requires a shared Redis instance
across all server replicas. With JWT, each request is self-contained
and any replica can validate it independently.
## Changes
- \`src/auth/\`: replaced session middleware with JWT middleware
- \`src/models/user.py\`: added \`refresh_token_hash\` field
- \`src/routes/auth.py\`: new login/refresh/logout endpoints
- \`config/\`: added JWT_SECRET, TOKEN_EXPIRY settings
- Removed: \`src/sessions/\`, Redis configuration
## Breaking Changes
⚠️ **All existing sessions will be invalidated.** Users need to
log in again after deployment.
API changes:
- \`POST /auth/login\` now returns \`{ access_token, refresh_token }\`
- All authenticated endpoints require \`Authorization: Bearer <token>\`
- \`POST /auth/refresh\` replaces automatic session refresh
## Migration Guide
1. Deploy the new code
2. Run \`python manage.py migrate\` for the new DB field
3. Remove Redis session store configuration
4. Update frontend to use token-based auth
## Testing
\`\`\`bash
pytest tests/test_auth.py -v
pytest tests/test_integration.py -v
\`\`\`
## Checklist
- [x] Unit tests pass
- [x] Integration tests pass
- [x] Migration script tested
- [x] Documentation updated"
Code review with Claude Code
Claude Code can act as a code reviewer. It can analyze diffs, spot problems, and explain changes.
Reviewing a diff
You: Review the changes I made today. Anything to improve?
Claude Code runs git diff (or git diff --staged), analyzes the changes, and answers:
I reviewed your changes. Observations:
1. **src/services/user.py:34** - The query has no limit. If the table
has millions of rows, this is going to be a problem:
users = db.query(User).filter(User.active == True).all()
I'd suggest adding pagination or at least a .limit().
2. **src/routes/users.py:67** - The endpoint accepts a user_id as a
string but doesn't validate it. An invalid UUID will cause a 500
instead of a 400.
3. **tests/test_users.py** - Good coverage, but there's no test for the
user-not-found case (404).
Want me to apply these fixes?
Reviewing someone else's PR
You: Review PR #42 on GitHub. Focus on security and performance.
gh pr diff 42
Claude Code reads the PR diff and gives feedback focused on what you asked for.
Resolving merge conflicts
When a merge produces conflicts, Claude Code can understand both sides and propose an informed resolution.
Example
You: I have merge conflicts. Resolve them, keeping the
functionality from both branches.
Claude Code reads the conflicted files:
<<<<<<< HEAD
def calculate_price(item, quantity):
base = item.price * quantity
return base * 0.9 # 10% member discount
=======
def calculate_price(item, quantity, discount_code=None):
base = item.price * quantity
if discount_code:
discount = get_discount(discount_code)
return base * (1 - discount)
return base
>>>>>>> feature/discount-codes
And proposes:
def calculate_price(item, quantity, discount_code=None):
base = item.price * quantity
if discount_code:
discount = get_discount(discount_code)
return base * (1 - discount)
return base * 0.9 # 10% member discount (default when no code)
Claude Code understands that HEAD added a fixed member discount and the other branch added support for discount codes. The resolution keeps both behaviors.
Comparisons and decisions
When should Claude Code make the commits vs when should you?
| Situation | Recommendation |
|---|---|
| New feature on a separate branch | ✅ Claude Code makes the commits |
| Production fix | ⚠️ Review before commit |
| Large refactor | ✅ Claude Code with commit-as-you-go |
| Changes to sensitive configuration | ❌ Do them yourself, by hand |
| Merge into main/master | ⚠️ You do the merge, Claude Code prepares the PR |
Branch or direct commit?
| Situation | Recommendation |
|---|---|
| Any new feature | Always a branch |
| Small fix (typo, formatting) | A direct commit is acceptable |
| Refactor | Branch + PR |
| Documentation | Depends on the team |
| Experimenting with Claude Code | Always a branch (easy to throw away) |
Common patterns
Pattern: Git status at the start of a session
Add this to your CLAUDE.md:
# At the start of a session
- Run git status to check the state of the repo
- If there are uncommitted changes, ask me what to do before continuing
- If we're on a feature branch, continue the work
Pattern: Atomic commits
You: Implement these changes. Make atomic commits — each commit
should do exactly one thing, and the project must build
after every commit.
Claude Code makes sure that:
- Every commit is independent and builds
- The tests pass after every commit
- The message describes exactly what changed
Pattern: Branch protection
In your CLAUDE.md:
# Git rules
- NEVER push directly to main
- NEVER force push
- Always work on feature branches
- Name branches: feature/*, fix/*, docs/*, refactor/*
Git safety protocols in Claude Code
Claude Code has built-in safety rules to protect your repository. These rules are automatic — you don't have to configure them.
Mandatory rules
1. Co-Authored-By on every commit:
Every commit created by Claude Code automatically includes:
Co-Authored-By: Claude <noreply@anthropic.com>
This makes it possible to trace which code was AI-generated — important for audits, compliance, and transparency.
2. Never force push:
Claude Code will never run git push --force unless you explicitly ask for it. And even if you ask, it will warn you if the target is main or master.
3. Never skip hooks:
The --no-verify and --no-gpg-sign flags are never used automatically. If a pre-commit hook fails, Claude Code investigates and fixes the problem instead of skipping the validation.
4. New commits over amend:
Claude Code always prefers creating a new commit over using --amend. Why? If a pre-commit hook failed, the commit was NOT created — running --amend would modify the PREVIOUS commit, potentially destroying earlier work.
5. Selective staging:
Claude Code stages specific files by name (git add file.py) instead of git add -A or git add ., to avoid accidentally including sensitive files (.env, credentials) or large binaries.
What this means for you
✅ You can trust that Claude Code won't destroy your history
✅ Pre-commit hooks always run
✅ Commits carry AI traceability
✅ Sensitive files don't get included by accident
Pitfalls and edge cases
Pitfall 1: Claude Code pushes without you asking
By default, Claude Code asks permission before running git push. But if you configured Execute(git push*) in allowedTools, it will do it without asking. Make sure your permissions are the ones you want.
Fix: Don't put git push in allowedTools if you want to review before pushing.
Pitfall 2: Giant commits
Sometimes Claude Code piles up a lot of changes and makes one enormous commit. That makes code review and rollback painful.
Fix: Explicitly ask for granular commits:
Make separate commits for each logical change.
Don't pile multiple changes into a single commit.
Pitfall 3: Generic commit messages
If you don't tell Claude Code what commit style to use, it may produce messages like "update files" or "fix bug".
Fix: Conventional commits in CLAUDE.md (see the section above).
Pitfall 4: Conflicts with local changes
If you have uncommitted changes and you ask Claude Code to check out another branch, it can fail or lose your work.
Fix: Claude Code should run git stash before switching branches. Add this to CLAUDE.md:
# Before switching branches
- If there are uncommitted changes, run git stash
- After returning to the original branch, run git stash pop
Pitfall 5: Accidental force push
Claude Code should never run git push --force on shared branches. But if you ask it to, it will.
Fix: Block force push in your permissions or in CLAUDE.md:
# FORBIDDEN
- git push --force (on any branch)
- git push --force-with-lease (only allowed on personal branches)
- git reset --hard on shared branches
Complete worked example
A full workflow from start to finish:
Claude Code session
───────────────────
You: I need to add a rate limiting system to the API.
Work on a branch, make incremental commits with
conventional commits, and open a PR when you're done.
Claude Code:
─────────────
# 1. Check the state of the repo
$ git status
On branch main
nothing to commit, working tree clean
# 2. Create the branch
$ git checkout -b feature/rate-limiting
# 3. Implement the middleware
# ... writes src/middleware/rate_limiter.py ...
$ git add src/middleware/rate_limiter.py
$ git commit -m "feat(middleware): add token bucket rate limiter
Implements token bucket algorithm with configurable rate
and burst size. Supports per-IP and per-user limiting.
Uses Redis for distributed rate limiting across replicas."
# 4. Configuration
# ... writes src/config/rate_limits.py ...
$ git add src/config/rate_limits.py
$ git commit -m "feat(config): add rate limiting configuration
Default limits: 100 req/min for authenticated users,
20 req/min for anonymous. Configurable per-endpoint
via RATE_LIMIT_* environment variables."
# 5. Wire it into the routes
# ... modifies src/main.py ...
$ git add src/main.py
$ git commit -m "feat(api): apply rate limiting middleware
Rate limiter applied globally with per-endpoint overrides.
Auth endpoints: 10 req/min (brute force protection).
Upload endpoints: 5 req/min (resource protection)."
# 6. Tests
# ... writes tests/test_rate_limiter.py ...
$ git add tests/test_rate_limiter.py
$ git commit -m "test(rate-limiting): add comprehensive test suite
Tests cover: token bucket refill, burst handling, Redis
failure fallback, per-user vs per-IP limits, and
concurrent request handling."
# 7. Push and PR
$ git push -u origin feature/rate-limiting
$ gh pr create \
--title "feat: add API rate limiting with token bucket" \
--body "## Summary
Adds rate limiting to all API endpoints using token bucket
algorithm with Redis backend.
## Changes
- Token bucket rate limiter middleware
- Configurable limits per endpoint and user type
- Redis backend for distributed limiting
- Comprehensive test suite
## Testing
pytest tests/test_rate_limiter.py -v
## Configuration
Set RATE_LIMIT_DEFAULT=100 for custom limits.
See src/config/rate_limits.py for all options."
Practice exercises
Exercise 1: Basic — Feature branch with commits
Ask Claude Code to implement a simple piece of functionality (for example, an endpoint that returns the current date) using the feature branch pattern.
Requirements:
- Create the branch
feature/current-date - Implement the endpoint
- Commit with a conventional commit
- Verify with
git log --oneline
Solution
You: Create a branch feature/current-date. Implement a GET /api/date
endpoint that returns the current date and time in ISO format.
Commit with a conventional commit.
Claude Code runs:
git checkout -b feature/current-date
It creates the file and then:
git add src/routes/date.py
git commit -m "feat(api): add /date endpoint returning current ISO datetime"
git log --oneline -3
You check the result with git log --oneline.
Exercise 2: Intermediate — Conventional commits in CLAUDE.md
Configure conventional commits in your CLAUDE.md and verify that Claude Code follows them.
Requirements:
- Add a "Git conventions" section to CLAUDE.md
- Ask Claude Code for 3 different changes (feat, fix, docs)
- Verify that the commits follow the format
Solution
Add this to CLAUDE.md:
# Git conventions
- Conventional commits are mandatory: type(scope): description
- Allowed types: feat, fix, docs, style, refactor, test, chore
- Scope: the module affected
- Description: imperative, in English, no trailing period, max 72 chars
- Body: explain the "why" when it isn't obvious
Then ask for three changes:
1. You: Add an /api/version endpoint that returns the app version
2. You: The /api/health endpoint returns 500 when the DB is disconnected. It should return 503.
3. You: Add a README explaining how to run the project
Verify:
git log --oneline -3
# feat(api): add /version endpoint returning app version
# fix(api): return 503 instead of 500 when DB is disconnected
# docs(readme): add project setup and run instructions
Exercise 3: Intermediate — A PR with a full description
After implementing a feature on a branch, ask Claude Code to open a PR with a detailed description.
Requirements:
- Have at least 2 commits on a feature branch
- A PR with: summary, changes, testing instructions, checklist
- Use
gh pr create
Solution
You: Open a PR for this branch. Include a summary of all the
changes, testing instructions, and a review checklist.
Claude Code analyzes the commits and diffs:
gh pr create \
--title "feat: add user notification preferences" \
--body "## Summary
Adds the ability for users to configure their notification
preferences (email, push, SMS) per event type.
## Changes
- \`NotificationPreference\` model with per-event settings
- CRUD endpoints for preferences
- Integration with existing notification service
- Migration script for new DB table
## Testing
\`\`\`bash
pytest tests/test_preferences.py -v
\`\`\`
Manual: POST to /api/users/me/preferences with payload
## Checklist
- [x] Unit tests
- [x] Integration tests
- [x] Migration tested
- [ ] Frontend integration (separate PR)"
Exercise 4: Advanced — Resolve merge conflicts
Simulate a merge conflict and ask Claude Code to resolve it.
Steps:
- Create two branches from main
- On each branch, modify the same file in a different way
- Merge one branch into main
- Try to merge the second — there will be a conflict
- Ask Claude Code to resolve the conflict
Solution
Setup:
# Branch A: adds email validation
git checkout -b feature/email-validation
# edit src/validators.py → add validate_email()
git add . && git commit -m "feat: add email validation"
# Branch B: adds phone validation
git checkout main
git checkout -b feature/phone-validation
# edit src/validators.py → add validate_phone()
git add . && git commit -m "feat: add phone validation"
# Merge A into main
git checkout main
git merge feature/email-validation
# Merge B → conflict
git merge feature/phone-validation
# CONFLICT in src/validators.py
Ask Claude Code:
You: I have a merge conflict in src/validators.py. Resolve it,
keeping the functionality from both branches (email and phone
validation).
Claude Code reads the conflict, understands that both branches added different functions to the same file, and resolves it by keeping both functions.
Exercise 5: Advanced — A complete Git workflow
Simulate a full workflow: analyze the project, plan the changes, implement on a branch, incremental commits, tests, and a PR.
Requirements:
- Use Explore → Plan → Code
- A feature branch with at least 3 commits
- Passing tests
- A PR with a detailed description
- All of it handled by Claude Code
Solution guide
You: I want to add a caching system for the most-used endpoints.
First analyze which endpoints would benefit most, then design
the solution, and finally implement it on a branch with
incremental commits. Open a PR at the end.
Claude Code should:
- Explore: analyze the routes, identify the most expensive ones
- Plan: design the caching strategy (in-memory vs Redis, TTL, invalidation)
- Code:
git checkout -b feature/api-caching- Commit 1:
feat(cache): add caching middleware with TTL support - Commit 2:
feat(api): apply caching to /users and /products endpoints - Commit 3:
test(cache): add cache hit/miss and invalidation tests
- PR:
gh pr createwith a complete description
Check with git log --oneline feature/api-caching that the commits are clear and incremental.
Summary
What you learned in this capsule:
- Claude Code runs Git commands natively from the terminal — it needs no special APIs
- There are 3 workflow patterns: Feature branch (the most common), Commit-as-you-go (granular history), and Review before commit (maximum control)
- Conventional commits (
feat:,fix:,docs:) are configured in CLAUDE.md for automatic consistency - Claude Code can create PRs with
gh, generating detailed descriptions based on the real diffs - Claude Code can do code review by analyzing diffs and explaining changes
- For merge conflicts, Claude Code understands the context of both sides and proposes informed resolutions
- The main pitfalls: pushing without review, giant commits, generic messages, and accidental force push
- Configure clear rules in CLAUDE.md and permissions in settings.json for a safe workflow
Next capsule: 03 - Python and TypeScript SDK — how to access Claude Code programmatically for automation, CI/CD, and custom scripts.
Additional resources
Official documentation
- Git Integration — Using Git with Claude Code
- CLI Reference — CLI commands for Git
- Best Practices — Recommended Git workflows
- Settings — Configuring permissions for Git operations
Git and GitHub
- Conventional Commits — The conventional commits specification
- GitHub CLI — Documentation for the
ghCLI - GitHub Actions + Claude Code — CI/CD with Claude Code
Complementary
- Hooks — Hooks to validate Git operations
- Headless Mode — Git automation with headless mode