Skip to main content
OrchestKit v6.7.1 — 67 skills, 38 agents, 77 hooks with Opus 4.6 support
OrchestKit
Skills

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.

Command medium

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:

  1. main is always deployable
  2. Branch from main, PR back to main
  3. Branches live < 1-3 days
  4. 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 72

Interactive 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 committed

Commit 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 commits

Recovery 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 days

Common Recovery Scenarios

ScenarioNot PushedAlready Pushed
Undo commitgit reset --soft HEAD~1git revert HEAD
Wrong branchcherry-pick + resetcherry-pick + revert
Lost commitsgit reset --hard HEAD@\{N\}N/A
Bad rebasegit rebase --abort or reflogreflog + 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-lease

Standard 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-feature

Anti-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 code

Best Practices Summary

  1. Branch from main - Always start fresh
  2. Stage interactively - Use git add -p
  3. One thing per commit - If you say "and", split it
  4. Rebase, don't merge - Keep history clean
  5. Check reflog first - When something goes wrong
  6. Force-with-lease - Safer than force push
  7. Delete after merge - No stale branches
  • ork:commit - Create commits with conventional format and pre-commit validation
  • git-recovery - Quick recovery from common git mistakes using reflog operations
  • stacked-prs - Multi-PR development for large features with dependent PRs
  • ork:create-pr - Comprehensive PR creation with proper formatting

Key Decisions

DecisionChoiceRationale
Branching modelGitHub FlowSimple single-branch workflow, main is always deployable
Merge strategyRebase over mergeKeeps history clean and linear, easier to bisect
Branch namingissue/<number>-<desc>Links work to tracking, enables automation
Commit granularityAtomic (one thing)Independent revert, clear history, easier review
Force push--force-with-lease onlyPrevents overwriting others' work on shared branches

Rules

Each category has individual rule files in rules/ loaded on-demand:

CategoryRuleImpactKey Pattern
Branch Protectionrules/branch-protection.mdCRITICALProtected branches, required PR workflow
Merge Strategyrules/merge-strategy.mdHIGHRebase-first, conflict resolution, force-with-lease
History Hygienerules/history-hygiene.mdHIGHSquash WIP, fixup commits, clean history
Recoveryrules/recovery-reflog.mdCRITICALReflog recovery for lost commits and branches
Recoveryrules/recovery-reset.mdCRITICALSafe vs dangerous reset modes
Recoveryrules/recovery-stash.mdHIGHStash management and dropped stash recovery
Stacked PRsrules/stacked-pr-workflow.mdHIGHStack planning, PR creation, dependency tracking
Stacked PRsrules/stacked-pr-rebase.mdHIGHRebase management, force-with-lease, retargeting
Monoreporules/monorepo-context.mdMEDIUM--add-dir, per-service CLAUDE.md, workspace detection

Total: 9 rules across 6 categories

References

Checklists


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

BranchPurposeDirect CommitForce Push
mainProduction-ready codeBLOCKEDBLOCKED
masterLegacy productionBLOCKEDBLOCKED
devIntegration branchBLOCKEDBLOCKED
release/*Release candidatesBLOCKEDBLOCKED

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 main

Correct — 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 main

Key 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 ones

Fixup 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~5

What to Squash

SquashKeep Separate
WIP commitsEach logical feature
"Fix typo" after featureBug fixes (different concern)
"Address review feedback"Refactoring (different intent)
Multiple attempts at same thingTest additions (reviewable unit)
Formatting fixes mixed with logicDocumentation 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 codes

Interactive Rebase Commands

CommandEffect
pickKeep commit as-is
rewordChange commit message
squashMerge into previous, combine messages
fixupMerge into previous, discard message
dropRemove commit entirely

Incorrect — Dirty WIP history:

# Pushed without cleanup
abc1234 WIP
def5678 fix
ghi9012 more fixes
jkl3456 finally works

Correct — Cleaned history:

# Squashed before pushing
git rebase -i --autosquash HEAD~4
# Result: One clean commit
abc1234 feat(#123): Add user validation with edge case handling

Key Rules

  • Clean up history before pushing — squash WIP and fixup commits
  • Each commit in final history should be meaningful and atomic
  • Use --fixup during development, --autosquash before 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

ScenarioStrategyCommand
Update feature branch with mainRebasegit rebase origin/main
Merge PR into mainSquash merge or merge commitVia GitHub PR
Resolve diverged branchesRebase onto targetgit rebase origin/main
Shared feature branchMerge (preserve history)git merge origin/main
Release branchMerge commitVia 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 over

When 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/main

Conflict 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 --continue

Force 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 locally

Incorrect — Merge main into feature:

# Creates noisy merge commits
git checkout feature-branch
git merge origin/main  # Creates "Merge branch 'main' into feature" commit

Correct — Rebase onto main:

# Linear history
git checkout feature-branch
git rebase origin/main  # Replays feature commits on top of main
git push --force-with-lease

Key Rules

  • Rebase feature branches onto main — don't merge main into feature branches
  • Use --force-with-lease instead of --force for 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 context

Correct — 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=1

Per-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.json

Root 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 check

Service 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:

IndicatorTool
pnpm-workspace.yamlpnpm
lerna.jsonLerna
nx.jsonNx
turbo.jsonTurborepo
rush.jsonRush
3+ nested package.jsonGeneric

Key rules:

  • Root CLAUDE.md: workspace conventions, cross-service rules, CI pipeline
  • Service CLAUDE.md: framework patterns, test commands, API contracts
  • Use --add-dir when working across service boundaries
  • Set CLAUDE_CODE_ADDITIONAL_DIRECTORIES_CLAUDE_MD=1 for 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 them

Correct — 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 abc1234

Recover 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 -5

Undo 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 stale

Key rules:

  • Reflog keeps entries for ~90 days by default — rarely truly "lost"
  • Always check reflog before assuming data is gone
  • ORIG_HEAD is 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 reflog

Correct — 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_REF

Reset modes — choose carefully:

ModeCommitsStagingWorking DirUse Case
--softUndoKeep stagedKeepAmend/recombine commits
--mixed (default)UndoUnstageKeepRe-stage selectively
--hardUndoDiscardDiscardThrow everything away

Undo last commit, keep changes:

git reset --soft HEAD~1
git status  # Changes are still staged

Restore 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/file

Unstage files (keep changes in working directory):

# Modern syntax
git restore --staged path/to/file

# Older syntax
git reset HEAD path/to/file

Pushed vs not-pushed decision:

ScenarioNot PushedAlready Pushed
Undo commitgit reset --soft HEAD~1git revert HEAD
Wrong branchcherry-pick + resetcherry-pick + revert
Bad mergegit reset --hard ORIG_HEADgit revert -m 1 &lt;merge&gt;

Key rules:

  • Always save a backup ref before --hard reset
  • Prefer --soft when you want to re-commit differently
  • Use git revert (not reset) for commits already pushed to shared branches
  • git restore is the modern replacement for git 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 one

Correct — 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 -m with a descriptive message when stashing
  • Prefer git stash apply over pop until you verify the result is correct
  • Dropped stashes can be recovered via git fsck but only before garbage collection
  • Use git stash push -- &lt;files&gt; to stash specific files, not everything
  • Stash includes staged changes by default; use --keep-index to 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 mess

Correct — 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-lease

After 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 #3

Automation 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 main via gh 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 -> #103

Key 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

  1. main is always deployable
  2. Branch for every change
  3. Short-lived branches (< 3 days)
  4. PR for all changes
  5. 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    days

Daily Workflow

Morning

git checkout main
git pull origin main
git checkout -b issue/123-my-task

During 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/main

End of Day (or when ready)

# Push and create PR
git push -u origin issue/123-my-task
gh pr create --fill

After Merge

git checkout main
git pull origin main
git branch -d issue/123-my-task

Why Not GitFlow?

GitFlowGitHub Flow
develop + main + release + hotfixJust main
Complex mergingSimple merging
Scheduled releasesContinuous deployment
Merge conflictsFew conflicts
Weeks-long branchesDays-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 abc1234

Feature 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 interactively

Prompt Options

When Git shows each hunk, you can respond with:

KeyAction
yStage this hunk
nDon't stage this hunk
sSplit into smaller hunks
eManually edit the hunk
qQuit (keeps already staged)
aStage this and all remaining hunks in file
dDon'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 exit

Viewing What's Staged

# What will be committed
git diff --staged

# What won't be committed
git diff

# Summary
git status

Tips

  1. Commit after staging: Don't stage more until you commit
  2. Review before commit: Always git diff --staged
  3. Use s liberally: Split whenever possible
  4. Use e for precision: Edit when split isn't enough
  5. Stage by file: git add -p file.ts for 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 → reset

Detailed 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 HEAD

Committed 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 abc1234

Already 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-branch

Recover 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 abc1234

Recover Deleted Branch

# Find branch's last commit
git reflog | grep "checkout: moving from deleted-branch"

# Recreate branch
git checkout -b recovered-branch abc1234

Fix 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 --continue

After 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-lease

Undo Merge

Not pushed:

git reset --hard HEAD~1

Already pushed:

# -m 1 keeps first parent (your branch)
git revert -m 1 <merge-commit-sha>
git push

Recover 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 recovery

Key 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 SHA

Common 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-rebase

Find Deleted Branch

# Find last commit on deleted branch
git reflog | grep "checkout: moving from deleted-branch"

# Recreate branch
git checkout -b recovered abc1234

Branch-Specific Reflog

# Reflog for specific branch
git reflog show feature-branch

# Reflog for remote tracking
git reflog show origin/main

Reflog 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 never

Pro Tips

  1. Check reflog FIRST when something goes wrong
  2. Use HEAD@{n} syntax in any git command
  3. Branch before dangerous ops: git branch backup
  4. Reflog is local only - not pushed to remote
  5. 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 convention

Branch 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/capitals

Quick 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"
fi

Before First Commit

[ ] Correct branch (not main/dev)
[ ] Branch tracks remote (`git push -u origin <branch>`)
[ ] Working directory is clean except intended changes

Before 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 format

Before PR

[ ] Branch rebased on latest main
[ ] No merge conflicts
[ ] CI passing on branch
[ ] Self-reviewed the diff
[ ] Issue linked in PR description

Branch 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-feature

Emergency: 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-changes

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 commit

Signs 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 concern

Staged Changes Review

# What's staged (will be committed)
git diff --staged

# What's NOT staged (will NOT be committed)
git diff

# Files overview
git status

Interactive 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

TypeUse For
featNew feature
fixBug fix
docsDocumentation only
styleFormatting (no code change)
refactorCode change (no feature/fix)
testAdding/fixing tests
choreBuild, 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 --stat

Undo Last Commit

# Keep changes staged
git reset --soft HEAD~1

# Keep changes unstaged
git reset HEAD~1

# Discard everything (DANGEROUS)
git reset --hard HEAD~1
Edit on GitHub

Last updated on

On this page

Git WorkflowBranch Naming ConventionAtomic Commit ChecklistInteractive StagingCommit PatternsRecovery Quick ReferenceThe Safety NetCommon Recovery ScenariosQuick Recovery CommandsStandard WorkflowAnti-PatternsBest Practices SummaryRelated SkillsKey DecisionsRulesReferencesChecklistsRules (9)Configure branch protection rules to prevent direct commits to protected branches — CRITICALBranch Protection RulesProtected BranchesRequired WorkflowBranch Naming ConventionEmergency ProceduresKey RulesKeep git history clean by squashing WIP commits and fixups before merge — HIGHHistory Hygiene RulesBefore Pushing: Clean UpFixup Commits (During Development)What to SquashCommit Message QualityInteractive Rebase CommandsKey RulesChoose the right merge strategy for clean, bisectable commit history — HIGHMerge Strategy RulesDecision TableRebase-First WorkflowWhen NOT to RebaseConflict ResolutionForce Push SafetyKey RulesScope Claude Code context in monorepos with per-service directories and CLAUDE.md — MEDIUMMonorepo Context PatternsRecover lost commits using git reflog within the 90-day retention window — HIGHReflog RecoveryUse git reset and restore safely by saving a backup reference first — HIGHReset and RestoreRecover git stash entries using named stashes and reflog before garbage collection — MEDIUMStash RecoveryRebase dependent PRs systematically after base branch changes to prevent conflicts — HIGHStacked PR Rebase ManagementSplit large features into small, reviewable, independently mergeable stacked PRs — HIGHStacked PR WorkflowReferences (4)Github FlowGitHub Flow GuideCore PrinciplesWorkflow DiagramDaily WorkflowMorningDuring DayEnd of Day (or when ready)After MergeWhy Not GitFlow?When to Use Release BranchesFeature Flags IntegrationInteractive StagingInteractive Staging with git add -pBasic UsagePrompt OptionsExample SessionEditing HunksViewing What's StagedTipsRecovery Decision TreeRecovery Decision TreeDecision TreeDetailed Recovery CommandsUndo Last CommitCommitted to Wrong BranchRecover Lost CommitsRecover Deleted BranchFix Rebase DisastersUndo MergeRecover Stashed ChangesPrevention TipsKey PrincipleReflog RecoveryReflog RecoveryWhat Reflog CapturesReading the ReflogFormat ExplainedCommon Recovery PatternsFind Lost CommitRecover After Bad ResetRecover After Bad RebaseFind Deleted BranchBranch-Specific ReflogReflog ExpirationPro TipsChecklists (2)Branch ChecklistBranch ChecklistBefore Creating a BranchBranch Naming ValidationQuick ValidationBefore First CommitBefore PushBefore PRBranch LifecycleEmergency: Wrong BranchRelatedPre Commit ChecklistPre-Commit ChecklistAtomic Commit CheckSigns of Non-Atomic CommitsStaged Changes ReviewInteractive StagingCommit Message FormatValid TypesMessage RulesExamplesFinal ChecksQuick Commit FlowUndo Last CommitRelated