Git Version Control
This skill covers Git commit standards, branch strategy, and LLM-assisted development workflows. It emphasizes atomic commits, meaningful commit messages, and high-frequency integration.
Core Philosophy
Integration frequency is the most powerful determinant of branching success. Elite teams integrate multiple times daily. "If it hurts, do it more often." — Martin Fowler
Revertability principle: Commits should represent meaningful units of work that could be reverted independently without breaking the system.
Commit Practices
When larger commits are acceptable: Initial prototyping (squash before review), closely coupled changes, when over-granularity loses context.
Time guideline: 30-60 min ideal, max 4 hours. See <commit_triggers> for event-based commit points.
Commit Triggers
Commit discipline isn't about remembering to commit—it's about recognizing completion signals.
| Event | Action | Rationale |
|---|---|---|
| Tests pass after a change | Commit | Green is a save point |
| Build succeeds after a change | Commit | Working state confirmed |
| Linter/type checker passes | Commit | Code meets standards |
| Todo item marked complete | Commit | Logical unit finished |
| User confirms functionality | Commit | Acceptance achieved |
| Configuration value tweaked | Commit | Discrete, working change |
Key insight: Passing tests/builds are commit signals, not just validation. Green means save your progress.
| Transition | Action | Rationale |
|---|---|---|
| Before starting different task | Commit current work | Clean separation |
| Before risky/experimental change | Commit as checkpoint | Safe rollback point |
| After reverting failed approach | Commit clean state | Document decision |
| Before context window compaction | Commit all work | Preserve across sessions |
In LLM-assisted development, user messages signal completion:
| User Says | Likely Meaning | Action |
|---|---|---|
| "That works", "looks good", "perfect" | Acceptance | Commit |
| "Done", "ship it", "let's move on" | Task complete | Commit |
| "Now let's work on..." | Topic change | Commit previous work first |
| "Can you also..." | Scope expansion | Consider committing current state |
Anti-pattern: Batching multiple unrelated changes because "I'll commit later." Each completion event deserves its own commit.
Branch Discipline
| Situation | Action |
|---|---|
| On main, multi-commit work expected | Create feature branch first |
| On main, single quick-fix commit | Acceptable to commit directly |
| On feature branch | Commit freely |
| Unsure how many commits needed | Create feature branch (safer default) |
LLM-assisted pattern: When starting work that might need multiple commits, proactively create a feature branch before the first commit. Ask the user if uncertain about scope.
| Branch State | Recommended Merge | Rationale |
|---|---|---|
| 1-2 clean commits | ff-merge or rebase-merge | Preserves meaningful history |
| 3-5 commits | Consider squash-merge | Balance history vs. noise |
| >5 commits | Strongly prefer squash-merge | Collapse implementation noise |
| Messy/WIP commits | Squash-merge | Hide the sausage-making |
Squash preference: Feature branches with more than 3-5 commits should generally be squash-merged. The intermediate commits represent implementation exploration, not meaningful history. Mainline should tell the story of what changed, not how you figured it out.
Imperative mood test: Subject line should complete the sentence "If applied, this commit will _____."[^beams] Examples: "Add caching for API responses" ✓, "Added caching" ✗, "Adds caching" ✗.
Content principles:
- Delta, not journey — Describe what changed, not how you discovered what to change. The debugging process doesn't belong in the permanent record.
- Neutral framing — Avoid judgmental language about prior state ("fix broken", "remove wrong", "correct mistake"). Prefer neutral verbs: "update", "change", "revise".
- Outcome, not process — Don't document how you verified, tested, or arrived at the change. The commit message records what, not how you figured it out.
- Let the diff speak — Implementation details belong in the diff. The message explains intent and scope; the diff shows specifics.
Anti-pattern: "Fix incorrect attribution that cited Apple docs when the quote was actually from a community blog post" — this documents the debugging journey, uses judgmental framing, and includes detail the diff already shows.
Better: "Update source attributions" — neutral, outcome-focused, appropriate abstraction level.
Branching Strategies
Trunk-based: Short-lived branches (<24h), feature flags for incomplete work.
Branch naming: <category>/<ticket-id>-<description> (e.g., feature/PROJ-4521-add-oauth)
GOLDEN RULE: Never rebase commits others may have based work on.
Force push safety: After rebasing a personal branch, use --force-with-lease instead of --force. It fails if someone else pushed, preventing you from overwriting their work.
git push --force-with-lease # Safe
git push --force # Dangerous
CRITICAL: NEVER force push to main/master branches. Even with --force-with-lease, force pushing to primary branches can destroy team history, break CI/CD pipelines, and cause widespread disruption. If this is ever requested, warn about the consequences first.
LLM-Assisted Development Patterns
Checkpoint Commits
Git aliases (adapted from Nathan Orick's checkpoint pattern[^orick]):
[alias]
checkpoint = "!f() { git add -A && git commit --no-verify -m \"SAVEPOINT\"; git tag \"checkpoint/$(date +%Y_%m_%d_%H_%M_%S)\"; git reset HEAD~1 --mixed; }; f"
listCheckpoints = tag -l "checkpoint/*"
loadCheckpoint = "!f() { git reset --hard checkpoint/$1 && git reset HEAD~1 --mixed; }; f"
deleteCheckpoint = "!f() { git tag -d checkpoint/$1; }; f"
Workflow:
git checkpoint # Before LLM changes
git listCheckpoints # View savepoints
git loadCheckpoint 2024_11_15_13_25_55 # Restore if bad
Commit Frequency by Context
Generate-Test-Commit-or-Revert
Key insight: AI excels at generating plausible-looking but subtly incorrect code. Time saved by being careless will dwarf the mess you'll face later.
If LLM fails 3+ times, break it down further — abstraction level too high.
Context Management
Recovery Patterns
Rerere — Auto-resolve repeated conflicts. Enable: git config --global rerere.enabled true
Bisect — O(log n) regression hunting.
git bisect start && git bisect bad HEAD && git bisect good v1.0
git bisect run make test # Automated
Tool Selection
Multi-agent worktrees:
git worktree add ../agent-1-workspace feature-auth
git worktree add ../agent-2-workspace feature-payment
Anti-Patterns
Branch Anti-Patterns:
- Long-lived branches (>1 week): Merge conflicts compound exponentially. Integrate frequently.
- Rebasing public branches: Rewrites shared history. Use merge for shared branches.
- No feature flags on trunk: Incomplete features on main without flags block releases.
- Force pushing to main/master: Destroys team history. Never do this.
LLM-Assisted Anti-Patterns:
- "Vibe coding" without review: LLM output requires human verification before commit.
- No checkpoints before LLM changes: Always checkpoint before LLM modifications for easy rollback.
- Context drift from large windows: Long conversations lose context. Break into smaller tasks.
Common Mistakes by Background
From Solo Developers
- Not considering rebase vs merge implications (matters when collaborating)
- Treating main as scratch space (use feature branches even when solo)
- Giant commits because "only I use this" (future you will regret it)
- No commit message discipline (you'll forget why in 6 months)
From GUI-Only Users
- Not understanding what commands the GUI executes (learn the underlying operations)
- Panic when something goes wrong (reflog saves almost everything)
- Not leveraging command-line power for scripting and automation
Resources
Patterns:
Sources
[^orick]: Nathan Orick. Git Checkpoints. https://nathanorick.com/git-checkpoints/