Git Workflow
Complete git workflow patterns including GitHub Flow branching, atomic commits with interactive staging, merge and rebase strategies, and recovery operations using reflog. Essential patterns for clean history. Use when managing branches, defining branching strategy, or recovering git history.
Primary Agent: git-operations-engineer
Git Workflow
Complete git workflow patterns: GitHub Flow branching, atomic commits, and recovery operations. Essential for maintaining clean, reviewable history.
Branch Naming Convention
# Feature branches (link to issue)
issue/<number>-<brief-description>
issue/123-add-user-auth
# When no issue exists
feature/<description>
fix/<description>
hotfix/<description>Branch Rules:
mainis always deployable- Branch from
main, PR back tomain - Branches live < 1-3 days
- Delete branch after merge
Atomic Commit Checklist
[ ] Does ONE logical thing
[ ] Leaves codebase working (tests pass)
[ ] Message doesn't need "and" in title
[ ] Can be reverted independently
[ ] Title < 50 chars, body wraps at 72Interactive Staging
# Stage changes hunk-by-hunk
git add -p
# Options:
# y - stage this hunk
# n - skip this hunk
# s - split into smaller hunks
# e - manually edit the hunk
# q - quit
# Review what's staged
git diff --staged # What will be committed
git diff # What won't be committedCommit Patterns
# Separate concerns
git add -p && git commit -m "refactor: Extract database pool"
git add -p && git commit -m "feat(#456): Add query caching"
# Never combine unrelated changes
# BAD: "feat: Add auth and fix formatting"
# GOOD: Two separate commitsRecovery Quick Reference
The Safety Net
# ALWAYS check reflog first - it has everything
git reflog
# Shows ALL recent HEAD movements
# Even "deleted" commits live here for 90 daysCommon Recovery Scenarios
| Scenario | Not Pushed | Already Pushed |
|---|---|---|
| Undo commit | git reset --soft HEAD~1 | git revert HEAD |
| Wrong branch | cherry-pick + reset | cherry-pick + revert |
| Lost commits | git reset --hard HEAD@\{N\} | N/A |
| Bad rebase | git rebase --abort or reflog | reflog + force-with-lease |
Quick Recovery Commands
# Undo last commit, keep changes staged
git reset --soft HEAD~1
# Find lost commits
git reflog | grep "your message"
# Recover to previous state
git reset --hard HEAD@{1}
# Safe force push (feature branches only)
git push --force-with-leaseStandard Workflow
# 1. Start fresh
git checkout main && git pull origin main
git checkout -b issue/123-my-feature
# 2. Work with atomic commits
git add -p
git commit -m "feat(#123): Add User model"
# 3. Stay updated
git fetch origin && git rebase origin/main
# 4. Push and PR
git push -u origin issue/123-my-feature
gh pr create --fill
# 5. Cleanup after merge
git checkout main && git pull
git branch -d issue/123-my-featureAnti-Patterns
Avoid:
- Long-lived branches (> 1 week)
- Merging main into feature (use rebase)
- Direct commits to main
- Force push to shared branches
- Commits that need "and" in message
- Committing broken codeBest Practices Summary
- Branch from main - Always start fresh
- Stage interactively - Use
git add -p - One thing per commit - If you say "and", split it
- Rebase, don't merge - Keep history clean
- Check reflog first - When something goes wrong
- Force-with-lease - Safer than force push
- Delete after merge - No stale branches
Related Skills
ork:commit- Create commits with conventional format and pre-commit validationgit-recovery- Quick recovery from common git mistakes using reflog operationsstacked-prs- Multi-PR development for large features with dependent PRsork:create-pr- Comprehensive PR creation with proper formatting
Key Decisions
| Decision | Choice | Rationale |
|---|---|---|
| Branching model | GitHub Flow | Simple single-branch workflow, main is always deployable |
| Merge strategy | Rebase over merge | Keeps history clean and linear, easier to bisect |
| Branch naming | issue/<number>-<desc> | Links work to tracking, enables automation |
| Commit granularity | Atomic (one thing) | Independent revert, clear history, easier review |
| Force push | --force-with-lease only | Prevents overwriting others' work on shared branches |
Rules
Each category has individual rule files in rules/ loaded on-demand:
| Category | Rule | Impact | Key Pattern |
|---|---|---|---|
| Branch Protection | rules/branch-protection.md | CRITICAL | Protected branches, required PR workflow |
| Merge Strategy | rules/merge-strategy.md | HIGH | Rebase-first, conflict resolution, force-with-lease |
| History Hygiene | rules/history-hygiene.md | HIGH | Squash WIP, fixup commits, clean history |
| Recovery | rules/recovery-reflog.md | CRITICAL | Reflog recovery for lost commits and branches |
| Recovery | rules/recovery-reset.md | CRITICAL | Safe vs dangerous reset modes |
| Recovery | rules/recovery-stash.md | HIGH | Stash management and dropped stash recovery |
| Stacked PRs | rules/stacked-pr-workflow.md | HIGH | Stack planning, PR creation, dependency tracking |
| Stacked PRs | rules/stacked-pr-rebase.md | HIGH | Rebase management, force-with-lease, retargeting |
| Monorepo | rules/monorepo-context.md | MEDIUM | --add-dir, per-service CLAUDE.md, workspace detection |
Total: 9 rules across 6 categories
References
Checklists
- Branch Checklist - Pre-flight checks before creating branches
- Pre-Commit Checklist - Validation before committing
Rules (9)
Configure branch protection rules to prevent direct commits to protected branches — CRITICAL
Branch Protection Rules
Protected branches are the deployment pipeline. Never commit or push directly to them.
Protected Branches
| Branch | Purpose | Direct Commit | Force Push |
|---|---|---|---|
main | Production-ready code | BLOCKED | BLOCKED |
master | Legacy production | BLOCKED | BLOCKED |
dev | Integration branch | BLOCKED | BLOCKED |
release/* | Release candidates | BLOCKED | BLOCKED |
Required Workflow
# 1. Create feature branch FROM main
git checkout main && git pull
git checkout -b issue/123-add-feature
# 2. Work on feature branch
git add -p && git commit -m "feat(#123): Add feature"
# 3. Push feature branch
git push -u origin issue/123-add-feature
# 4. Create PR targeting main (or dev)
gh pr create --base main
# 5. Merge via PR only (after review + CI)Branch Naming Convention
# Issue-linked (preferred)
issue/<number>-<brief-description>
issue/123-add-user-auth
# Type-based (when no issue exists)
feature/<description>
fix/<description>
hotfix/<description> # For urgent production fixes
docs/<description>
refactor/<description>
test/<description>
chore/<description>Emergency Procedures
For production hotfixes that need to bypass normal flow:
# Create hotfix branch from main
git checkout main && git pull
git checkout -b hotfix/critical-fix
# Fix, test, push
git add . && git commit -m "fix: Resolve critical auth bypass"
git push -u origin hotfix/critical-fix
# Create PR with expedited review
gh pr create --base main --title "HOTFIX: Critical auth bypass"Incorrect — Direct commit to main:
# Bypasses code review and CI
git checkout main
git commit -m "quick fix"
git push origin mainCorrect — Feature branch workflow:
# PR-based deployment
git checkout -b fix/issue-123
git commit -m "fix(#123): Resolve auth bug"
git push -u origin fix/issue-123
gh pr create --base mainKey Rules
- Never commit directly to main, dev, or release branches
- Always use feature branches with descriptive names
- All changes reach protected branches through PRs only
- Force push is only allowed on your own feature branches with
--force-with-lease - Delete feature branches after merge
Keep git history clean by squashing WIP commits and fixups before merge — HIGH
History Hygiene Rules
A clean git history makes debugging (bisect), code review, and onboarding dramatically easier.
Before Pushing: Clean Up
# Squash WIP commits before pushing
git rebase -i HEAD~3
# In the editor:
pick abc1234 feat(#123): Add user validation
fixup def5678 WIP: more validation
fixup ghi9012 fix typo
# Result: One clean commit instead of three messy onesFixup Commits (During Development)
Use --fixup to mark commits that should be squashed later:
# Initial commit
git commit -m "feat(#123): Add user model"
# Later fixes to the same work
git commit --fixup HEAD # Auto-squash into previous
git commit --fixup HEAD~2 # Auto-squash into specific commit
# Before pushing, auto-squash all fixups
git rebase -i --autosquash HEAD~5What to Squash
| Squash | Keep Separate |
|---|---|
| WIP commits | Each logical feature |
| "Fix typo" after feature | Bug fixes (different concern) |
| "Address review feedback" | Refactoring (different intent) |
| Multiple attempts at same thing | Test additions (reviewable unit) |
| Formatting fixes mixed with logic | Documentation updates |
Commit Message Quality
# BAD history
abc1234 WIP
def5678 stuff
ghi9012 fix
jkl3456 more fixes
mno7890 finally works
# GOOD history
abc1234 feat(#123): Add user validation with Zod schemas
def5678 test(#123): Add validation edge case tests
ghi9012 docs(#123): Document validation error codesInteractive Rebase Commands
| Command | Effect |
|---|---|
pick | Keep commit as-is |
reword | Change commit message |
squash | Merge into previous, combine messages |
fixup | Merge into previous, discard message |
drop | Remove commit entirely |
Incorrect — Dirty WIP history:
# Pushed without cleanup
abc1234 WIP
def5678 fix
ghi9012 more fixes
jkl3456 finally worksCorrect — Cleaned history:
# Squashed before pushing
git rebase -i --autosquash HEAD~4
# Result: One clean commit
abc1234 feat(#123): Add user validation with edge case handlingKey Rules
- Clean up history before pushing — squash WIP and fixup commits
- Each commit in final history should be meaningful and atomic
- Use
--fixupduring development,--autosquashbefore pushing - Never rewrite published history (commits others have pulled)
- Commit messages should explain WHY, not just WHAT
Choose the right merge strategy for clean, bisectable commit history — HIGH
Merge Strategy Rules
Use rebase-first workflow to maintain clean, linear history on feature branches.
Decision Table
| Scenario | Strategy | Command |
|---|---|---|
| Update feature branch with main | Rebase | git rebase origin/main |
| Merge PR into main | Squash merge or merge commit | Via GitHub PR |
| Resolve diverged branches | Rebase onto target | git rebase origin/main |
| Shared feature branch | Merge (preserve history) | git merge origin/main |
| Release branch | Merge commit | Via GitHub PR |
Rebase-First Workflow
# Keep feature branch up to date
git fetch origin
git rebase origin/main
# If conflicts arise
# 1. Resolve conflicts in each file
# 2. Stage resolved files
git add <resolved-files>
# 3. Continue rebase
git rebase --continue
# If rebase goes wrong
git rebase --abort # Start overWhen NOT to Rebase
DO NOT rebase if:
- Branch is shared with other developers (changes published history)
- You've already pushed and others have pulled
- Working on a release branch
USE merge instead:
git merge origin/mainConflict Resolution
# 1. Understand the conflict
git diff # Shows conflict markers
# 2. Resolve by choosing correct code
# <<<<<<< HEAD ← your changes
# ...
# =======
# ... ← incoming changes
# >>>>>>> main
# 3. Test after resolution
npm test # Verify nothing broke
# 4. Continue
git add <resolved-files>
git rebase --continueForce Push Safety
# SAFE: Force push your own feature branch
git push --force-with-lease origin issue/123-feature
# DANGEROUS: Never force push protected branches
git push --force origin main # NEVER DO THIS
# --force-with-lease prevents overwriting others' pushes
# It fails if remote has commits you don't have locallyIncorrect — Merge main into feature:
# Creates noisy merge commits
git checkout feature-branch
git merge origin/main # Creates "Merge branch 'main' into feature" commitCorrect — Rebase onto main:
# Linear history
git checkout feature-branch
git rebase origin/main # Replays feature commits on top of main
git push --force-with-leaseKey Rules
- Rebase feature branches onto main — don't merge main into feature branches
- Use
--force-with-leaseinstead of--forcefor rebased branches - Resolve conflicts during rebase, not with merge commits
- Test after every conflict resolution
- Abort rebase if unsure — you can always start over
Scope Claude Code context in monorepos with per-service directories and CLAUDE.md — MEDIUM
Monorepo Context Patterns
Configure Claude Code for monorepo workflows with per-service CLAUDE.md and multi-directory context.
Incorrect — single flat context for entire monorepo:
# WRONG: Claude Code only sees root, misses service-specific patterns
claude # From monorepo root — no service contextCorrect — multi-directory context with --add-dir:
# Start Claude Code with additional service context
claude --add-dir ../shared
claude --add-dir ../shared --add-dir ../web
# Enable CLAUDE.md loading from added directories
export CLAUDE_CODE_ADDITIONAL_DIRECTORIES_CLAUDE_MD=1Per-service CLAUDE.md structure:
monorepo/
CLAUDE.md # Root: workspace-wide rules
packages/
api/
CLAUDE.md # API: FastAPI patterns, test commands
package.json
web/
CLAUDE.md # Web: Next.js patterns, component library
package.json
shared/
CLAUDE.md # Shared: library API, versioning rules
package.jsonRoot CLAUDE.md (workspace-wide):
# Monorepo Root
## Conventions
- Commit format: conventional commits
- Branch naming: issue/<number>-<desc>
- CI: Turborepo pipeline, affected-only builds
## Cross-Service Rules
- Shared types live in packages/shared
- API changes require web compatibility checkService CLAUDE.md (service-specific):
# API Service
## Stack
- FastAPI 0.115+, Python 3.12+
- SQLAlchemy 2.x async
## Test Commands
npm run test:api # Unit tests
npm run test:api:e2e # Integration tests
## Patterns
- All endpoints return RFC 9457 errors
- Use dependency injection via FastAPI Depends()Monorepo detection indicators:
| Indicator | Tool |
|---|---|
pnpm-workspace.yaml | pnpm |
lerna.json | Lerna |
nx.json | Nx |
turbo.json | Turborepo |
rush.json | Rush |
3+ nested package.json | Generic |
Key rules:
- Root CLAUDE.md: workspace conventions, cross-service rules, CI pipeline
- Service CLAUDE.md: framework patterns, test commands, API contracts
- Use
--add-dirwhen working across service boundaries - Set
CLAUDE_CODE_ADDITIONAL_DIRECTORIES_CLAUDE_MD=1for full context loading - Keep service CLAUDE.md focused — avoid duplicating root-level rules
Recover lost commits using git reflog within the 90-day retention window — HIGH
Reflog Recovery
Use git reflog to recover lost commits, deleted branches, and botched rebases.
Incorrect — assuming commits are gone after reset:
git reset --hard HEAD~3 # "Oh no, those commits are lost forever\!"
# Wrong — reflog still has themCorrect — reflog finds everything:
# View all recent HEAD movements
git reflog --date=relative
# Find lost commits by message
git reflog | grep "search-term"
# Show reflog for specific branch
git reflog show branch-name
# Once found (e.g., abc1234), recover:
git branch recovered-work abc1234
# Or cherry-pick specific commits:
git cherry-pick abc1234Recover deleted branch:
# Find the branch last commit
git reflog | grep -i "branch-name"
# Or check all recent activity:
git reflog --all | head -30
# Recreate the branch at that commit
git checkout -b recovered-branch abc1234
# Verify
git log --oneline -5Undo a bad rebase:
# Find the pre-rebase state in reflog
git reflog | head -20
# Look for: "rebase (start): checkout main"
# The entry BEFORE that is your pre-rebase state
# Alternative: ORIG_HEAD is set before rebase
git reset --hard ORIG_HEAD
# WARNING: ORIG_HEAD is overwritten by other operations
# Use reflog if ORIG_HEAD might be staleKey rules:
- Reflog keeps entries for ~90 days by default — rarely truly "lost"
- Always check reflog before assuming data is gone
ORIG_HEADis a shortcut but can be overwritten — reflog is more reliable- Reflog is local only — not shared with remotes
Use git reset and restore safely by saving a backup reference first — HIGH
Reset and Restore
Undo commits and restore files safely using the right reset mode.
Incorrect — hard reset without backup:
git reset --hard HEAD~1 # Changes gone, no backup reference saved
# If this was wrong, recovery requires digging through reflogCorrect — always save a backup reference first:
# Save backup before destructive operation
BACKUP_REF=$(git rev-parse HEAD)
echo "Backup ref: $BACKUP_REF"
# Show what will be lost
git show HEAD --stat
# Then execute
git reset --hard HEAD~1
# Recovery if needed:
git reset --hard $BACKUP_REFReset modes — choose carefully:
| Mode | Commits | Staging | Working Dir | Use Case |
|---|---|---|---|---|
--soft | Undo | Keep staged | Keep | Amend/recombine commits |
--mixed (default) | Undo | Unstage | Keep | Re-stage selectively |
--hard | Undo | Discard | Discard | Throw everything away |
Undo last commit, keep changes:
git reset --soft HEAD~1
git status # Changes are still stagedRestore single file (non-destructive to other files):
# Show changes to file first
git diff path/to/file
# Restore using modern git (2.23+)
git restore path/to/file
# Or older syntax
git checkout HEAD -- path/to/fileUnstage files (keep changes in working directory):
# Modern syntax
git restore --staged path/to/file
# Older syntax
git reset HEAD path/to/filePushed vs not-pushed decision:
| Scenario | Not Pushed | Already Pushed |
|---|---|---|
| Undo commit | git reset --soft HEAD~1 | git revert HEAD |
| Wrong branch | cherry-pick + reset | cherry-pick + revert |
| Bad merge | git reset --hard ORIG_HEAD | git revert -m 1 <merge> |
Key rules:
- Always save a backup ref before
--hardreset - Prefer
--softwhen you want to re-commit differently - Use
git revert(not reset) for commits already pushed to shared branches git restoreis the modern replacement forgit checkout -- file
Recover git stash entries using named stashes and reflog before garbage collection — MEDIUM
Stash Recovery
Save and recover work-in-progress using git stash safely.
Incorrect — unnamed stash that gets lost:
git stash # Generic "WIP on main" message
git stash # Another generic stash
git stash pop # Which one was it? Wrong one applied
git stash drop # Lost the other oneCorrect — named stashes with safe recovery:
# Save with descriptive name
git stash push -m "auth-flow: halfway through token refresh"
# List stashes to find the right one
git stash list
# stash@{0}: On main: auth-flow: halfway through token refresh
# stash@{1}: On main: fix: database connection pooling
# Apply specific stash (keeps it in stash list)
git stash apply stash@{1}
# Apply and remove from list
git stash pop stash@{0}Recover a dropped stash:
# Stash entries appear in reflog briefly
git fsck --no-reflog | grep commit
# Once found, apply it:
git stash apply <commit-hash>Stash specific files:
# Stash only specific files
git stash push -m "partial work" -- path/to/file1 path/to/file2
# Stash including untracked files
git stash push -u -m "including new files"Key rules:
- Always use
-mwith a descriptive message when stashing - Prefer
git stash applyoverpopuntil you verify the result is correct - Dropped stashes can be recovered via
git fsckbut only before garbage collection - Use
git stash push -- <files>to stash specific files, not everything - Stash includes staged changes by default; use
--keep-indexto stash only unstaged
Rebase dependent PRs systematically after base branch changes to prevent conflicts — HIGH
Stacked PR Rebase Management
Keep stacked PR branches synchronized after feedback, merges, or base branch changes.
Incorrect — merging main into feature branches:
# WRONG: Merge commits pollute history
git checkout feature/auth-service
git merge main # Creates unnecessary merge commit
git merge feature/auth-base # More merge commits
# History becomes unreadable messCorrect — rebase after base PR feedback:
# When base PR (auth-base) gets review feedback:
git checkout feature/auth-base
git add -p && git commit -m "fix: Address review feedback"
git push
# Rebase dependent branches in order:
git checkout feature/auth-service
git rebase feature/auth-base
git push --force-with-lease # Safe: won't overwrite others' work
git checkout feature/auth-ui
git rebase feature/auth-service
git push --force-with-leaseAfter base PR merges to main:
# PR #1 merged to main
git checkout main && git pull origin main
# Retarget PR #2 to main (via GitHub CLI)
gh pr edit 102 --base main
# Rebase PR #2 on updated main
git checkout feature/auth-service
git rebase main
git push --force-with-lease
# After PR #2 merges, repeat for PR #3Automation script for rebasing entire stack:
#!/bin/bash
# stack-rebase.sh — Rebase entire stack in sequence
STACK=(
"feature/auth-base"
"feature/auth-service"
"feature/auth-ui"
)
BASE="main"
for branch in "${STACK[@]}"; do
echo "Rebasing $branch onto $BASE..."
git checkout "$branch"
git rebase "$BASE" || { echo "Conflict in $branch! Resolve and re-run."; exit 1; }
git push --force-with-lease
BASE="$branch"
done
echo "Stack rebased successfully!"Key rules:
- Always rebase, never merge main into feature branches
- Use
--force-with-lease(never--force) to prevent overwriting others' work - Rebase in dependency order: base first, then each dependent branch
- After a base PR merges, retarget the next PR to
mainviagh pr edit - If rebase conflicts occur, resolve in the lowest branch first, then cascade
- Never force push to already-approved PRs without re-requesting review
Split large features into small, reviewable, independently mergeable stacked PRs — HIGH
Stacked PR Workflow
Break large features into small, dependent PRs that merge in sequence for faster reviews and cleaner history.
Incorrect — single massive PR:
# WRONG: One 1500-line PR that takes days to review
git checkout -b feature/auth
# ... 3 days of work, 40 files changed ...
gh pr create --title "feat: Add complete auth system"
# Reviewer: "This is too large to review effectively"Correct — stacked PRs with clear dependencies:
# PR 1: User model (base) — targets main
git checkout main && git pull origin main
git checkout -b feature/auth-base
git add -p && git commit -m "feat(#100): Add User model"
git push -u origin feature/auth-base
gh pr create --base main --title "feat(#100): Add User model [1/3]" \
--body "## Stack
- **PR 1/3: User model (this PR)**
- PR 2/3: Auth service (depends on this)
- PR 3/3: Login UI (depends on #2)
## Changes
- Add User model with validation
- Add database migrations"
# PR 2: Auth service — targets PR 1's branch
git checkout -b feature/auth-service # branches from auth-base
git add -p && git commit -m "feat(#100): Add auth service"
git push -u origin feature/auth-service
gh pr create --base feature/auth-base \
--title "feat(#100): Add auth service [2/3]" \
--body "**Depends on #101** — merge that first"
# PR 3: Login UI — targets PR 2's branch
git checkout -b feature/auth-ui
git commit -m "feat(#100): Add login form"
git push -u origin feature/auth-ui
gh pr create --base feature/auth-service \
--title "feat(#100): Add login UI [3/3]"Stack visualization in PR body:
## PR Stack for Auth Feature (#100)
| Order | PR | Status | Branch |
|-------|-----|--------|--------|
| 1 | #101 | Merged | feature/auth-base |
| 2 | #102 | Review | feature/auth-service |
| 3 | #103 | Draft | feature/auth-ui |
**Merge order**: #101 -> #102 -> #103Key rules:
- Keep each PR under 400 lines for effective review
- Number PRs clearly:
[1/3],[2/3],[3/3] - Each PR should be independently reviewable and leave tests passing
- Use draft PRs for incomplete stack items
- Do not stack more than 4-5 PRs deep
- Never merge out of order
References (4)
Github Flow
GitHub Flow Guide
The recommended branching strategy for most teams in 2026.
Core Principles
- main is always deployable
- Branch for every change
- Short-lived branches (< 3 days)
- PR for all changes
- Delete after merge
Workflow Diagram
main ─────●───────●───────●───────●───────●──────
\ / \ / \ / \ /
●───● ●───● ●───● ●───●
issue/ fix/ feat/ hotfix/
123 456 789 critical
1-2 0.5 2-3 0.5
days days days daysDaily Workflow
Morning
git checkout main
git pull origin main
git checkout -b issue/123-my-taskDuring Day
# Make changes, commit atomically
git add -p
git commit -m "feat(#123): Part 1"
# Stay updated with main
git fetch origin
git rebase origin/mainEnd of Day (or when ready)
# Push and create PR
git push -u origin issue/123-my-task
gh pr create --fillAfter Merge
git checkout main
git pull origin main
git branch -d issue/123-my-taskWhy Not GitFlow?
| GitFlow | GitHub Flow |
|---|---|
| develop + main + release + hotfix | Just main |
| Complex merging | Simple merging |
| Scheduled releases | Continuous deployment |
| Merge conflicts | Few conflicts |
| Weeks-long branches | Days-long branches |
When to Use Release Branches
Only for:
- Supporting multiple versions (v1.x, v2.x)
- Regulatory compliance requiring sign-off
- Mobile apps with app store review cycles
# Create release branch only when needed
git checkout -b release/v1.2 main
# Cherry-pick specific fixes
git cherry-pick abc1234Feature Flags Integration
When feature takes > 3 days:
// Merge incomplete work behind flag
if (process.env.FF_NEW_FEATURE) {
return <NewFeature />;
}
return <CurrentFeature />;This allows:
- Merging to main daily
- Testing in production (flag off)
- Gradual rollout (flag percentage)
- Instant rollback (disable flag)
Interactive Staging
Interactive Staging with git add -p
Master git add -p (patch mode) for precise, atomic commits.
Basic Usage
git add -p # Stage all modified files interactively
git add -p file.ts # Stage specific file interactivelyPrompt Options
When Git shows each hunk, you can respond with:
| Key | Action |
|---|---|
y | Stage this hunk |
n | Don't stage this hunk |
s | Split into smaller hunks |
e | Manually edit the hunk |
q | Quit (keeps already staged) |
a | Stage this and all remaining hunks in file |
d | Don't stage this or any remaining hunks in file |
? | Help |
Example Session
$ git add -p
diff --git a/src/auth.ts b/src/auth.ts
@@ -10,6 +10,10 @@ export function login(user: string) {
+ // Validate input
+ if (!user) throw new Error('User required');
+
const token = generateToken(user);
+ logAudit('login', user); // Added for audit
return token;
}
Stage this hunk [y,n,q,a,d,s,e,?]? s # Split it!
# Now shows smaller hunks...
@@ -10,6 +10,8 @@
+ // Validate input
+ if (!user) throw new Error('User required');
Stage this hunk? y # Stage validation
@@ -14,6 +16,7 @@
+ logAudit('login', user); # Added for audit
Stage this hunk? n # Skip audit (different commit)Editing Hunks
Use e to manually edit when Git can't split small enough:
Stage this hunk? e
# Opens editor with:
# -context line
# +added line
# +another added line
# Delete lines you DON'T want staged
# Keep lines you DO want staged
# Save and exitViewing What's Staged
# What will be committed
git diff --staged
# What won't be committed
git diff
# Summary
git statusTips
- Commit after staging: Don't stage more until you commit
- Review before commit: Always
git diff --staged - Use
sliberally: Split whenever possible - Use
efor precision: Edit when split isn't enough - Stage by file:
git add -p file.tsfor focused work
Recovery Decision Tree
Recovery Decision Tree
Quick reference for choosing the right recovery approach.
Decision Tree
What happened?
│
├─ Committed to wrong branch?
│ ├─ Not pushed → cherry-pick + reset
│ └─ Pushed → cherry-pick + revert
│
├─ Need to undo commit?
│ ├─ Keep changes → git reset --soft HEAD~1
│ ├─ Discard changes → git reset --hard HEAD~1
│ └─ Already pushed → git revert HEAD
│
├─ Lost commits?
│ └─ Check reflog → git reset --hard HEAD@{N}
│
├─ Deleted branch?
│ └─ Check reflog → git checkout -b name SHA
│
├─ Bad merge?
│ ├─ Not pushed → git reset --hard HEAD~1
│ └─ Pushed → git revert -m 1 SHA
│
└─ Rebase disaster?
├─ Still rebasing → git rebase --abort
└─ Completed → reflog → resetDetailed Recovery Commands
Undo Last Commit
# Keep changes staged
git reset --soft HEAD~1
# Keep changes unstaged
git reset --mixed HEAD~1
# Discard changes completely (DANGER)
git reset --hard HEAD~1
# Already pushed - create reverting commit
git revert HEADCommitted to Wrong Branch
Not pushed:
# Note the commit SHA
git log -1 # abc1234
# Undo on wrong branch
git reset --hard HEAD~1
# Apply to correct branch
git checkout correct-branch
git cherry-pick abc1234Already pushed:
# Cherry-pick to correct branch
git checkout correct-branch
git cherry-pick abc1234
git push origin correct-branch
# Revert on wrong branch
git checkout wrong-branch
git revert abc1234
git push origin wrong-branchRecover Lost Commits
# Find in reflog
git reflog
# Reset to before the bad operation
git reset --hard HEAD@{N}
# Or cherry-pick specific commits
git cherry-pick abc1234Recover Deleted Branch
# Find branch's last commit
git reflog | grep "checkout: moving from deleted-branch"
# Recreate branch
git checkout -b recovered-branch abc1234Fix Rebase Disasters
Mid-rebase:
# Abort and start over
git rebase --abort
# Skip problematic commit
git rebase --skip
# Continue after fixing conflicts
git add .
git rebase --continueAfter rebase completed:
# Find pre-rebase state in reflog
git reflog
# Reset to before rebase
git reset --hard HEAD@{N}
# Force push if needed (feature branches only!)
git push --force-with-leaseUndo Merge
Not pushed:
git reset --hard HEAD~1Already pushed:
# -m 1 keeps first parent (your branch)
git revert -m 1 <merge-commit-sha>
git pushRecover Stashed Changes
# List stashes
git stash list
# Apply most recent
git stash pop
# Apply specific stash
git stash apply stash@{2}
# Recover dropped stash (search reflog)
git reflog | grep "WIP on"Prevention Tips
# Backup before dangerous operations
git branch backup-before-rebase
# Use safer force push
git push --force-with-lease
# Commit early, commit often
# More recovery points = easier recoveryKey Principle
Stay calm - Git almost never loses data permanently. The reflog keeps everything for 90 days.
Reflog Recovery
Reflog Recovery
The reflog is Git's safety net. It tracks every HEAD movement for ~90 days.
What Reflog Captures
Every time HEAD changes, reflog records it:
- Commits
- Checkouts
- Resets
- Rebases
- Merges
- Cherry-picks
- Pulls
Reading the Reflog
$ git reflog
abc1234 HEAD@{0}: commit: feat: Add auth
def5678 HEAD@{1}: checkout: moving from main to feature
ghi9012 HEAD@{2}: pull: Fast-forward
jkl3456 HEAD@{3}: reset: moving to HEAD~3
mno7890 HEAD@{4}: rebase: (finish)
pqr2345 HEAD@{5}: rebase: (start)Format Explained
abc1234 HEAD@{0}: commit: feat: Add auth
─────── ──────── ─────── ──────────────
│ │ │ │
│ │ │ └── Description
│ │ └───────────── Action type
│ └─────────────────────── Position (0 = most recent)
└──────────────────────────────── Commit SHACommon Recovery Patterns
Find Lost Commit
# Search by message
git reflog | grep "important feature"
# Search by date
git reflog --since="2 hours ago"
# Search by author action
git reflog | grep "commit:"Recover After Bad Reset
$ git reflog
abc1234 HEAD@{0}: reset: moving to HEAD~5 # Bad reset!
def5678 HEAD@{1}: commit: Important work # Lost commit
$ git reset --hard HEAD@{1} # Recovered!Recover After Bad Rebase
$ git reflog
abc1234 HEAD@{0}: rebase (finish)
def5678 HEAD@{1}: rebase (pick)
ghi9012 HEAD@{2}: rebase (start) # Find pre-rebase state
jkl3456 HEAD@{3}: commit: My work # This is what we want
$ git reset --hard HEAD@{3} # Back to pre-rebaseFind Deleted Branch
# Find last commit on deleted branch
git reflog | grep "checkout: moving from deleted-branch"
# Recreate branch
git checkout -b recovered abc1234Branch-Specific Reflog
# Reflog for specific branch
git reflog show feature-branch
# Reflog for remote tracking
git reflog show origin/mainReflog Expiration
# Default: 90 days for reachable, 30 days for unreachable
git config gc.reflogExpire # Default: 90.days
git config gc.reflogExpireUnreachable # Default: 30.days
# Keep forever (not recommended)
git config gc.reflogExpire neverPro Tips
- Check reflog FIRST when something goes wrong
- Use HEAD@{n} syntax in any git command
- Branch before dangerous ops:
git branch backup - Reflog is local only - not pushed to remote
- Each repo has separate reflog
Checklists (2)
Branch Checklist
Branch Checklist
Pre-flight checks before creating and working with branches.
Before Creating a Branch
[ ] main is up to date (`git pull origin main`)
[ ] No uncommitted changes (`git status` is clean)
[ ] Issue/ticket exists for the work
[ ] Branch name follows conventionBranch Naming Validation
# Valid patterns
issue/123-add-user-auth # Linked to issue (preferred)
feature/oauth-integration # No issue, feature work
fix/null-pointer-api # Bug fix
hotfix/security-patch # Urgent production fix
# Invalid patterns
my-branch # No prefix
ISSUE-123 # Wrong format
feature/Add_User_Auth # No underscores/capitalsQuick Validation
# Check branch name format
BRANCH=$(git branch --show-current)
if [[ ! "$BRANCH" =~ ^(issue|feature|fix|hotfix|release)/ ]]; then
echo "WARNING: Branch '$BRANCH' doesn't follow naming convention"
fiBefore First Commit
[ ] Correct branch (not main/dev)
[ ] Branch tracks remote (`git push -u origin <branch>`)
[ ] Working directory is clean except intended changesBefore Push
[ ] All tests pass locally
[ ] No debug code (console.log, print, debugger)
[ ] No secrets or credentials
[ ] Commits are atomic (one logical change each)
[ ] Commit messages follow conventional formatBefore PR
[ ] Branch rebased on latest main
[ ] No merge conflicts
[ ] CI passing on branch
[ ] Self-reviewed the diff
[ ] Issue linked in PR descriptionBranch Lifecycle
1. Create → git checkout -b issue/123-feature
2. Work → atomic commits, frequent pushes
3. Sync → git fetch && git rebase origin/main
4. PR → gh pr create
5. Review → address feedback
6. Merge → squash or rebase merge
7. Cleanup → git branch -d issue/123-featureEmergency: Wrong Branch
# Accidentally committed to main?
# 1. Create branch from current state
git branch fix/my-changes
# 2. Reset main to origin
git reset --hard origin/main
# 3. Switch to new branch
git checkout fix/my-changesRelated
Pre Commit Checklist
Pre-Commit Checklist
Validation steps before creating a commit.
Atomic Commit Check
[ ] Does ONE logical thing (no "and" in description)
[ ] Can be reverted independently
[ ] Leaves codebase in working state
[ ] Tests pass after this commitSigns of Non-Atomic Commits
BAD: "Add auth and fix typos" → Split into 2 commits
BAD: "Refactor users, update tests" → Split into 2 commits
BAD: "WIP" → Finish or stash
GOOD: "feat(auth): Add JWT validation" → Single concernStaged Changes Review
# What's staged (will be committed)
git diff --staged
# What's NOT staged (will NOT be committed)
git diff
# Files overview
git statusInteractive Staging
# Stage hunks selectively
git add -p
# Keybindings:
# y = stage this hunk
# n = skip this hunk
# s = split into smaller hunks
# e = edit hunk manually
# q = quit (staged so far remains)Commit Message Format
<type>(<scope>): <description>
[optional body]
[optional footer]Valid Types
| Type | Use For |
|---|---|
feat | New feature |
fix | Bug fix |
docs | Documentation only |
style | Formatting (no code change) |
refactor | Code change (no feature/fix) |
test | Adding/fixing tests |
chore | Build, deps, tooling |
Message Rules
[ ] Title < 50 characters
[ ] Title uses imperative mood ("Add" not "Added")
[ ] Title doesn't end with period
[ ] Body wraps at 72 characters
[ ] Body explains WHY, not WHAT
[ ] Footer references issues (Closes #123)Examples
# Good
git commit -m "feat(api): Add rate limiting to /users endpoint"
# With body (use editor or heredoc)
git commit -m "$(cat <<'EOF'
fix(auth): Handle expired refresh tokens gracefully
Previously, expired refresh tokens caused 500 errors. Now returns
401 with clear error message and invalidates the session.
Closes #456
EOF
)"Final Checks
# Verify staged content is correct
git diff --staged --stat
# Verify not committing to protected branch
BRANCH=$(git branch --show-current)
if [[ "$BRANCH" =~ ^(main|master|dev|develop)$ ]]; then
echo "ERROR: Cannot commit directly to $BRANCH"
exit 1
fi
# Check for debug code
git diff --staged | grep -E "(console\.log|debugger|print\(|pdb)" && \
echo "WARNING: Debug code detected"
# Check for secrets
git diff --staged | grep -iE "(password|secret|api.?key|token)" && \
echo "WARNING: Possible secrets detected"Quick Commit Flow
# 1. Review changes
git status
git diff
# 2. Stage selectively
git add -p
# 3. Verify staged
git diff --staged
# 4. Commit with message
git commit -m "feat(scope): Description"
# 5. Verify commit
git log --oneline -1
git show --statUndo Last Commit
# Keep changes staged
git reset --soft HEAD~1
# Keep changes unstaged
git reset HEAD~1
# Discard everything (DANGEROUS)
git reset --hard HEAD~1Related
Fix Issue
Fixes GitHub issues with parallel analysis. Use to debug errors, resolve regressions, fix bugs, or triage issues.
Github Operations
GitHub CLI operations for issues, PRs, milestones, and Projects v2. Covers gh commands, REST API patterns, and automation scripts. Use when managing GitHub issues, PRs, milestones, or Projects with gh.
Last updated on