From git diff to perfect PR description in 2 minutes using Claude
Writing PR descriptions used to be the last thing I wanted to do after finishing a feature. I would stare at the diff, try to remember why I made each change, and end up with a description that said "Added user filtering" over 200 changed lines. Then I built a shell alias that pipes my git diff to Claude and gets a structured, human-readable PR description back in under two minutes. Here is the exact setup.
The core idea
git diff main...HEAD contains everything Claude needs to write a good PR description: what changed, which files, what was added or removed. Claude can infer intent from the code far better than most engineers can articulate it under time pressure.
The shell function
# Add to ~/.zshrc or ~/.bashrc
pr-describe() {
local base_branch="${1:-main}"
local diff_output
diff_output=$(git diff "${base_branch}...HEAD" 2>&1)
if [ -z "$diff_output" ]; then
echo "No diff found against ${base_branch}. Are you on a feature branch?"
return 1
fi
echo "Generating PR description..."
echo "$diff_output" | claude --print --system "You are a senior engineer writing a GitHub pull request description.
Output a structured PR description with these sections:
## Summary
One paragraph explaining what this PR does and why.
## Changes
Bullet list of specific changes made.
## Testing
How the changes were tested or how a reviewer can verify them.
## Notes
Any migration steps, breaking changes, or important reviewer callouts.
Be specific and technical. Infer intent from the code changes.
Format for GitHub markdown." "Write a PR description for this diff:"
}
# Also useful: copy to clipboard
pr-describe-copy() {
pr-describe "$@" | pbcopy
echo "PR description copied to clipboard"
}
Using the Claude CLI
This uses the claude CLI. Install it with:
npm install -g @anthropic-ai/claude-code
# Set your API key
export ANTHROPIC_API_KEY="your-key-here"
A version that uses the API directly
If you prefer not to use the CLI, here is a Python script you can call instead:
#!/usr/bin/env python3
# pr-describe.py — place in ~/bin/ and chmod +x
import sys
import subprocess
import anthropic
def get_diff(base_branch: str = "main") -> str:
result = subprocess.run(
["git", "diff", f"{base_branch}...HEAD"],
capture_output=True, text=True
)
return result.stdout
def generate_pr_description(diff: str) -> str:
client = anthropic.Anthropic()
# Truncate very large diffs to fit context window
max_diff_chars = 50_000
if len(diff) > max_diff_chars:
diff = diff[:max_diff_chars] + "
[Diff truncated — showing first 50k chars]"
response = client.messages.create(
model="claude-opus-4-5",
max_tokens=1500,
system="""You are a senior engineer writing a GitHub pull request description.
Write a clear, structured PR description with:
- ## Summary: What this PR does and why (1-2 paragraphs)
- ## Changes: Specific changes as bullet points
- ## Testing: How changes were tested
- ## Notes: Breaking changes, migrations, or reviewer callouts (if any)
Be specific and technical. Infer intent from the code.""",
messages=[
{
"role": "user",
"content": f"Write a PR description for this git diff:
{diff}"
}
],
)
return response.content[0].text
if __name__ == "__main__":
base = sys.argv[1] if len(sys.argv) > 1 else "main"
diff = get_diff(base)
if not diff.strip():
print(f"No diff against {base}")
sys.exit(1)
print(generate_pr_description(diff))
# In ~/.zshrc
pr-describe() {
python3 ~/bin/pr-describe.py "${1:-main}"
}
pr-describe-copy() {
pr-describe "$@" | pbcopy && echo "Copied to clipboard"
}
What great output looks like
For a recent auth refactor, the tool produced:
## Summary
Migrates JWT validation from HS256 (shared secret) to RS256 (public/private key pair).
This improves security posture by allowing downstream services to validate tokens
without access to the signing secret. Public key is distributed via environment variable.
## Changes
- Replace `jwt.sign(payload, SECRET)` with `jwt.sign(payload, PRIVATE_KEY, { algorithm: 'RS256' })`
- Add `RS256_PRIVATE_KEY` and `RS256_PUBLIC_KEY` to environment config
- Update all downstream service token validation to use public key
- Add `scripts/generate-keys.sh` for local key generation
- Update integration tests to use RSA key fixtures
## Testing
- All 47 existing auth tests pass with new implementation
- New tests added for: key rotation, malformed key errors, algorithm mismatch
- Tested locally with `scripts/generate-keys.sh` workflow
## Notes
⚠️ Breaking change: requires `RS256_PRIVATE_KEY` and `RS256_PUBLIC_KEY` env vars.
Update deployment configs before merging.
Old `JWT_SECRET` env var is no longer read.
The time savings add up
I open about 3-5 PRs per week. Writing a good description used to take 10-15 minutes when I was doing it properly. Now it takes 2 minutes to generate and 2 minutes to review and tweak. That is about 30 minutes a week I get back — time I spend on actual code review instead.
More importantly, the quality is higher. Claude catches things I would have glossed over in my own description, like noting that a change is a breaking migration or that tests were added for a specific edge case.