Advanced Git Workflows
This reference consolidates advanced Git workflows that are used less frequently but remain valuable for specific situations.
Core workflows (used daily) are in separate workflow files:
- Commit (workflows/Commit.md)
- Branch (workflows/Branch.md)
- Rebase (workflows/Rebase.md)
- Merge (workflows/Merge.md)
Stash - Temporarily Save Changes
Temporarily save changes without committing.
When to Use
- “stash git changes”
- “save work in progress”
- “temporarily save changes”
- “switch branches with uncommitted changes”
Quick Commands
Basic Stash
# Stash current changes
git stash
# Stash with message
git stash push -m "WIP: working on auth feature"
# Stash including untracked files
git stash -u
# Stash including untracked and ignored files
git stash -a
# Stash specific files
git stash push -m "temp changes" file1.txt file2.txt
Viewing Stashes
# List all stashes
git stash list
# Show stash contents
git stash show
# Show specific stash
git stash show stash@{1}
# Show diff
git stash show -p
git stash show -p stash@{1}
Applying Stashes
# Apply most recent stash (keep stash)
git stash apply
# Apply specific stash
git stash apply stash@{2}
# Apply and remove stash
git stash pop
# Apply specific and remove
git stash pop stash@{1}
Managing Stashes
# Delete specific stash
git stash drop stash@{0}
# Delete all stashes
git stash clear
# Create branch from stash
git stash branch feature/new-work stash@{0}
Common Use Cases
Quick Context Switch
# You're working on feature A
# Urgent bug needs fixing
# 1. Stash current work
git stash push -m "WIP: feature A implementation"
# 2. Switch to main and create hotfix
git checkout main
git checkout -b hotfix/urgent-bug
# 3. Fix, commit, and merge
git commit -m "fix: urgent bug"
git checkout main
git merge hotfix/urgent-bug
# 4. Return to feature A
git checkout feature/a
git stash pop
Pull with Uncommitted Changes
# You have uncommitted changes
# Need to pull latest updates
# 1. Stash changes
git stash
# 2. Pull
git pull
# 3. Reapply changes
git stash pop
# Handle conflicts if any
Try Experimental Changes
# Stash current stable work
git stash push -m "stable implementation"
# Try experimental approach
# ... make changes ...
# If experiment fails, restore stable
git reset --hard HEAD
git stash pop
# If experiment works, keep it and drop stash
git stash drop
Stash with Partial Changes
Interactive Stash
# Stash interactively (like git add -p)
git stash -p
# For each hunk:
# y - stash this hunk
# n - don't stash this hunk
# s - split into smaller hunks
# q - quit
Stash Specific Files
# Stash only certain files
git stash push -m "temp API changes" src/api.ts src/types.ts
# Stash everything except certain files
git stash push -- src/
Working with Multiple Stashes
Organized Stash Management
# Create named stashes
git stash push -m "feature: user auth half done"
git stash push -m "experiment: alternative approach"
git stash push -m "bugfix: in progress"
# List with messages
git stash list
# stash@{0}: On main: bugfix: in progress
# stash@{1}: On main: experiment: alternative approach
# stash@{2}: On main: feature: user auth half done
# Apply specific stash
git stash apply stash@{2}
Creating Branches from Stashes
# Create branch and apply stash
git stash branch feature/auth-completion stash@{0}
# This:
# 1. Creates new branch
# 2. Checks it out
# 3. Applies stash
# 4. Drops stash if successful
Stash Conflicts
Resolving Stash Conflicts
# When applying stash causes conflicts
git stash pop
# CONFLICT (content): Merge conflict in file.txt
# 1. Resolve conflicts manually
# 2. Stage resolved files
git add file.txt
# 3. No need to commit, changes are in working directory
# Stash is already removed by pop
# If you used apply instead:
git stash drop stash@{0}
Advanced Stash Techniques
Stash Keeping Index
# Stash unstaged changes, keep staged
git stash --keep-index
# Useful when you want to:
# 1. Keep staged changes
# 2. Temporarily remove unstaged
# 3. Run tests on staged only
Stash Untracked Files Only
# Stash only untracked files
git stash -u --keep-index
Recover Dropped Stash
# If you accidentally dropped a stash
# Find it in reflog
git fsck --unreachable | grep commit | cut -d' ' -f3 | xargs git log --merges --no-walk --grep=WIP
# Or
git log --graph --oneline --decorate $(git fsck --no-reflog | awk '/dangling commit/ {print $3}')
# Recover with:
git stash apply <commit-hash>
Stash Workflow Patterns
Daily Work Pattern
# End of day
git stash push -m "EOD: $(date +%Y-%m-%d) work in progress"
# Start of day
git stash list # Review what was stashed
git stash pop # Continue work
Code Review Pattern
# You're working on feature
git stash push -m "WIP: before code review"
# Check out PR for review
git fetch origin pull/123/head:pr-123
git checkout pr-123
# Review...
# Return to your work
git checkout feature/my-work
git stash pop
Best Practices
- Always use messages:
git stash push -m "description" - Clean up old stashes: Review and drop unneeded stashes
- Prefer commits over stash: For long-term work
- Use branches for experiments: Better than multiple stashes
- Pop when you can: Don’t accumulate many stashes
- Include untracked with -u: If you have new files
- Review before popping: Check stash contents first
Common Pitfalls
Forgetting About Stashes
# Regularly check for stashes
git stash list
# If you see old ones, review and clean up
git stash show stash@{0}
git stash drop stash@{0}
Stashing on Wrong Branch
# If you stashed on wrong branch
# 1. Go to correct branch
git checkout correct-branch
# 2. Apply stash from other branch
git stash apply stash@{0}
# 3. Go back and drop from original
git checkout original-branch
git stash drop stash@{0}
Configuration
# Show untracked files in stash show
git config --global stash.showIncludeUntracked true
# Show stash in git log
git config --global log.showSignature false
Resources
Bisect - Find Bug-Introducing Commit
Find the commit that introduced a bug using binary search.
When to Use
- “find bad commit”
- “git bisect”
- “when did this break”
- “regression debugging”
Quick Commands
Basic Bisect
# Start bisect
git bisect start
# Mark current commit as bad
git bisect bad
# Mark known good commit
git bisect good v1.0.0
# Or use commit hash
git bisect good abc1234
# Git will checkout middle commit
# Test and mark:
git bisect bad # if broken
git bisect good # if working
# Repeat until found
# Git will show: "abc1234 is the first bad commit"
# End bisect session
git bisect reset
Automated Bisect
# Start bisect
git bisect start HEAD v1.0.0
# Run automated test
git bisect run ./test.sh
# Git will automatically find bad commit
# Bisect session ends automatically
How Bisect Works
Binary Search Process
Commits: A---B---C---D---E---F---G
good bad
# Round 1: Test D (middle)
A---B---C---D---E---F---G
good ? bad
# D is bad
# Round 2: Test B
A---B---C---D
? bad
# B is good
# Round 3: Test C
B---C---D
good ? bad
# Found! C is first bad commit
Efficiency
# For 100 commits: ~7 tests (log2(100))
# For 1000 commits: ~10 tests (log2(1000))
# Much faster than linear search!
Manual Bisect Process
Step-by-Step Example
# 1. Start bisect
git bisect start
# 2. Mark current state as bad
git bisect bad
# 3. Find known good commit
git log --oneline
git bisect good v2.0.0 # or commit hash
# 4. Test current state
npm test # or whatever test
# If it fails:
git bisect bad
# If it passes:
git bisect good
# 5. Repeat step 4 until done
# 6. Git will show:
# abc1234 is the first bad commit
# commit abc1234
# Author: ...
# Date: ...
# feat: add user authentication
# 7. View the bad commit
git show abc1234
# 8. End bisect
git bisect reset
Automated Bisect
Using Test Script
# Create test script: test.sh
#!/bin/bash
npm test
exit $? # Return test exit code
# Make executable
chmod +x test.sh
# Run automated bisect
git bisect start HEAD v1.0.0
git bisect run ./test.sh
Test Script Requirements
# Script must:
# - Exit 0 if commit is GOOD
# - Exit 1-127 (except 125) if commit is BAD
# - Exit 125 if commit cannot be tested (will skip)
# Example test script
#!/bin/bash
# Build project
make build || exit 125 # Skip if build fails
# Run tests
make test
exit $?
Complex Test Script
#!/bin/bash
# Example: Testing for specific bug
# Bug: API returns 500 on /users endpoint
# Start server
npm start &
SERVER_PID=$!
sleep 5 # Wait for startup
# Test endpoint
response=$(curl -s -o /dev/null -w "%{http_code}" http://localhost:3000/users)
# Cleanup
kill $SERVER_PID
# Check result
if [ "$response" = "200" ]; then
exit 0 # Good commit
else
exit 1 # Bad commit
fi
Bisect with Specific Path
Limit to Specific Files
# Only bisect commits that touched specific files
git bisect start -- src/auth.ts src/user.ts
# Or specific directory
git bisect start -- src/api/
Handling Build Failures
Skip Untestable Commits
# During manual bisect, if commit won't build:
git bisect skip
# Git will try a different commit nearby
# Skip multiple commits
git bisect skip v1.0..v1.5
In Automated Script
#!/bin/bash
# Try to build
npm install && npm run build
if [ $? -ne 0 ]; then
exit 125 # Cannot test this commit, skip it
fi
# Run tests
npm test
exit $?
Bisect Subcommands
Viewing Bisect State
# See bisect log
git bisect log
# See current bisect state
cat .git/BISECT_LOG
# See remaining commits to test
git bisect visualize
git bisect view # Same as visualize
Replaying Bisect
# Save bisect session
git bisect log > bisect-session.txt
# Replay later
git bisect reset
git bisect replay bisect-session.txt
Common Use Cases
Finding Performance Regression
#!/bin/bash
# performance-test.sh
# Run benchmark
result=$(go test -bench=. -count=5 | grep "BenchmarkMain" | awk '{print $3}')
# Extract ns/op (assuming format like "1234 ns/op")
nsop=$(echo $result | awk '{print $1}')
# Good if under 1000 ns/op
if [ "$nsop" -lt 1000 ]; then
exit 0
else
exit 1
fi
# Run automated bisect
git bisect start HEAD v2.0.0
git bisect run ./performance-test.sh
Finding Broken Feature
#!/bin/bash
# feature-test.sh
# Setup test environment
docker-compose up -d
sleep 10
# Run specific test
pytest tests/test_user_feature.py
result=$?
# Cleanup
docker-compose down
exit $result
Finding Compilation Error
#!/bin/bash
# build-test.sh
# Attempt build
make clean
make 2>/dev/null
if [ $? -eq 0 ]; then
exit 0 # Builds successfully
else
exit 1 # Build fails
fi
Advanced Techniques
Bisect Terms (Good/Bad Aliases)
# Use custom terms instead of good/bad
git bisect start --term-old=fast --term-new=slow
# Now use:
git bisect fast # instead of good
git bisect slow # instead of bad
Bisect with Complex Logic
#!/bin/bash
# complex-test.sh
# Multiple test conditions
build_passes=false
tests_pass=false
# Check build
if make build; then
build_passes=true
fi
# Check tests
if npm test; then
tests_pass=true
fi
# Complex logic
if $build_passes && $tests_pass; then
exit 0 # Good
elif ! $build_passes; then
exit 125 # Skip - can't test
else
exit 1 # Bad - builds but tests fail
fi
Bisect Best Practices
- Know your good commit: Find a commit where feature definitely worked
- Make test specific: Test only the broken behavior, not everything
- Keep test fast: Faster tests = faster bisect
- Use skip for build failures: Don’t waste time on unbuildable commits
- Automate when possible: Manual testing is error-prone
- Test before bisecting: Verify your test works on good and bad commits
- Document the bug: Clear understanding helps write better tests
Troubleshooting
Bisect Not Finding Bug
# Verify test works
git checkout <known-bad-commit>
./test.sh # Should fail
git checkout <known-good-commit>
./test.sh # Should pass
# If both pass or both fail, fix your test script
Inconsistent Test Results
# Some tests are flaky (pass/fail randomly)
# Solution: Run test multiple times
#!/bin/bash
# stable-test.sh
for i in {1..5}; do
npm test || exit 1
done
exit 0
Too Many Commits
# Narrow down range first
git log --oneline v1.0..v2.0 # See all commits
# Try to narrow by date
git bisect start HEAD "$(git rev-list -1 --before='2024-01-01' HEAD)"
# Or by specific commit
git bisect start HEAD abc1234
Real-World Examples
Finding Memory Leak
#!/bin/bash
# memory-test.sh
# Run app with memory profiling
node --expose-gc --max-old-space-size=100 app.js &
PID=$!
# Monitor memory for 30 seconds
sleep 30
# Check memory usage
mem=$(ps -o rss= -p $PID)
kill $PID
# Good if under 80MB
if [ "$mem" -lt 80000 ]; then
exit 0
else
exit 1
fi
Finding Test Flakiness
#!/bin/bash
# flaky-test-finder.sh
# Run test 100 times
for i in {1..100}; do
npm test -- test/flaky.test.js || exit 1
done
exit 0
Finding Breaking Change
#!/bin/bash
# api-compatibility-test.sh
# Run API compatibility tests
npm run test:api
if [ $? -eq 0 ]; then
exit 0 # API compatible
else
exit 1 # Breaking change
fi
Bisect Configuration
# Show progress
git config --global bisect.showProgress true
# Default number of commits to show in visualize
git config --global bisect.visualizeCommits 20
Resources
Cherry-Pick - Apply Specific Commits
Apply specific commits from one branch to another.
When to Use
- “cherry pick commit”
- “apply specific commit”
- “copy commit to another branch”
- “backport fix”
Quick Commands
Basic Cherry-Pick
# Cherry-pick single commit
git cherry-pick abc1234
# Cherry-pick multiple commits
git cherry-pick abc1234 def5678 ghi9012
# Cherry-pick range of commits (exclusive start)
git cherry-pick abc1234..def5678
# Cherry-pick range (inclusive)
git cherry-pick abc1234^..def5678
# Continue after resolving conflicts
git cherry-pick --continue
# Abort cherry-pick
git cherry-pick --abort
# Skip current commit
git cherry-pick --skip
Cherry-Pick Options
# Cherry-pick without committing
git cherry-pick -n abc1234
git cherry-pick --no-commit abc1234
# Cherry-pick with different author
git cherry-pick abc1234 --edit
# Cherry-pick and add signoff
git cherry-pick -s abc1234
git cherry-pick --signoff abc1234
# Keep original author
git cherry-pick -x abc1234
# Adds "(cherry picked from commit abc1234)"
When to Cherry-Pick
Good Use Cases
Backporting Bug Fixes
# Fix was committed to main
# Need same fix in release branch
git checkout release/v1.0
git cherry-pick <fix-commit-hash>
Applying Specific Features
# Large feature branch, but only need one commit
# Instead of merging entire branch
git checkout develop
git cherry-pick <specific-feature-commit>
Emergency Hotfixes
# Critical fix needed in multiple branches
git checkout main
# Fix and commit
git checkout develop
git cherry-pick <fix-commit>
git checkout release/v2.0
git cherry-pick <fix-commit>
When NOT to Cherry-Pick
❌ Regular merging: Use merge or rebase instead ❌ Multiple dependent commits: Merge the branch instead ❌ Entire feature branches: Use merge ❌ Maintaining parallel branches: Use merge to avoid divergence
Cherry-Pick Process
Step-by-Step Example
# 1. Find commit to cherry-pick
git log --oneline feature/auth
# abc1234 fix: prevent null pointer in login
# 2. Switch to target branch
git checkout develop
# 3. Cherry-pick the commit
git cherry-pick abc1234
# 4. If conflicts occur:
# Auto-merging src/auth.ts
# CONFLICT (content): Merge conflict in src/auth.ts
# 5. Resolve conflicts
vim src/auth.ts
# Fix conflicts manually
# 6. Stage resolved files
git add src/auth.ts
# 7. Continue cherry-pick
git cherry-pick --continue
# 8. Verify
git log -1
git show HEAD
Resolving Conflicts
Conflict Resolution Process
# Cherry-pick causes conflict
git cherry-pick abc1234
# CONFLICT (content): Merge conflict in file.txt
# See conflicting files
git status
# View conflict
git diff
# Edit and resolve
vim file.txt
# Stage resolution
git add file.txt
# Continue
git cherry-pick --continue
# Or abort if needed
git cherry-pick --abort
Conflict Markers
<<<<<<< HEAD (current branch)
const API_URL = "https://api.example.com/v1";
=======
const API_URL = "https://api.example.com/v2"; // from cherry-picked commit
>>>>>>> abc1234 (fix: update API version)
Resolve to:
const API_URL = "https://api.example.com/v2";
Cherry-Pick Multiple Commits
Individual Commits
# Cherry-pick multiple specific commits
git cherry-pick abc1234 def5678 ghi9012
# If conflicts in first commit:
# Resolve and continue
git add conflicted-file.txt
git cherry-pick --continue
# Process continues with next commit
Commit Range
# Cherry-pick range (abc1234 not included)
git cherry-pick abc1234..def5678
# Cherry-pick range (abc1234 included)
git cherry-pick abc1234^..def5678
# View commits that will be picked
git log --oneline abc1234..def5678
Cherry-Pick Options
No Commit (-n)
# Apply changes without committing
git cherry-pick -n abc1234
# Changes staged but not committed
git status
# Changes to be committed:
# modified: file.txt
# Make additional changes if needed
vim file.txt
# Commit when ready
git commit
Edit Message (-e)
# Cherry-pick and edit commit message
git cherry-pick -e abc1234
# Opens editor with original message
# Modify as needed and save
Add Note (-x)
# Add reference to original commit
git cherry-pick -x abc1234
# Commit message becomes:
# fix: original message
#
# (cherry picked from commit abc1234def5678...)
Signoff (-s)
# Add signoff line
git cherry-pick -s abc1234
# Adds to commit message:
# Signed-off-by: Your Name <your@email.com>
Mainline (-m)
# Cherry-pick a merge commit
# Must specify which parent to follow
git cherry-pick -m 1 <merge-commit>
# -m 1: Use first parent (usually main branch)
# -m 2: Use second parent (usually feature branch)
Common Workflows
Backport to Release Branch
# Fix committed to main
git checkout main
git log --oneline
# abc1234 fix: critical security issue
# Apply to release branch
git checkout release/v2.0
git cherry-pick abc1234
# Add note about backport
git commit --amend -m "fix: critical security issue
Backported from main (abc1234)
Signed-off-by: Your Name <your@email.com>"
# Push to release
git push origin release/v2.0
Selective Feature Migration
# Feature branch has 10 commits
# Only want commits 3, 5, and 7
git log --oneline feature/new-ui
# aaa commit 10
# bbb commit 9
# ccc commit 8
# ddd commit 7 ← want this
# eee commit 6
# fff commit 5 ← want this
# ggg commit 4
# hhh commit 3 ← want this
# iii commit 2
# jjj commit 1
git checkout develop
git cherry-pick hhh fff ddd
Apply Same Fix to Multiple Branches
# Fix in develop
git checkout develop
git commit -m "fix: memory leak in parser"
# abc1234
# Apply to release branches
git checkout release/v1.0
git cherry-pick abc1234
git checkout release/v2.0
git cherry-pick abc1234
git checkout main
git cherry-pick abc1234
Cherry-Pick and Merge Conflicts
Strategy Selection
# Use recursive strategy with ours/theirs
git cherry-pick -X ours abc1234 # Prefer current branch
git cherry-pick -X theirs abc1234 # Prefer cherry-picked changes
Complex Conflicts
# Start cherry-pick
git cherry-pick abc1234
# Multiple conflicts
# Resolve each file
git status # See all conflicts
# both modified: file1.txt
# both modified: file2.txt
# Resolve one by one
vim file1.txt
git add file1.txt
vim file2.txt
git add file2.txt
# Verify all resolved
git status
# Continue
git cherry-pick --continue
Cherry-Pick from Another Repository
Using Remote
# Add other repository as remote
git remote add other-repo https://github.com/user/other-repo.git
# Fetch commits
git fetch other-repo
# Cherry-pick commit
git cherry-pick other-repo/main~3
Using Patch
# In source repository
git format-patch -1 abc1234
# Creates 0001-commit-message.patch
# Copy patch to target repository
# In target repository
git apply 0001-commit-message.patch
git commit
Undoing Cherry-Pick
Before Committing
# Cherry-pick in progress
git cherry-pick --abort
After Committing
# Remove last cherry-picked commit
git reset --hard HEAD~1
# Or revert the cherry-pick
git revert HEAD
Cherry-Pick Best Practices
- Document the cherry-pick: Use
-xflag to add reference - Test after cherry-pick: Code may behave differently in target branch
- Prefer merge for features: Cherry-pick for fixes, not feature development
- Consider dependencies: Ensure cherry-picked commit doesn’t depend on others
- Clean commit history: Cherry-pick atomic commits, not messy WIP commits
- Communicate with team: Let others know about cherry-picks to shared branches
- Avoid duplicate commits: Don’t cherry-pick then merge same commits later
Advanced Techniques
Cherry-Pick Range Excluding Commits
# Pick all except specific commits
git cherry-pick main~10..main
# Skip specific commits during range
git cherry-pick main~10..main
# When specific commit causes conflict you want to skip:
git cherry-pick --skip
Interactive Cherry-Pick
# Cherry-pick without committing
git cherry-pick -n abc1234
# Review changes
git diff --staged
# Make modifications
vim file.txt
# Commit with modified changes
git commit
Cherry-Pick Merge Commits
# See merge commit parents
git show --format="%P" <merge-commit>
# Cherry-pick merge commit
# -m 1: Use first parent (main branch)
# -m 2: Use second parent (feature branch)
git cherry-pick -m 1 <merge-commit>
Troubleshooting
Empty Commit
# Cherry-pick results in no changes
git cherry-pick abc1234
# The previous cherry-pick is now empty, possibly due to conflict resolution.
# Allow empty commit
git cherry-pick --allow-empty abc1234
# Or skip
git cherry-pick --skip
Wrong Branch
# Cherry-picked to wrong branch
# Before pushing:
git reset --hard HEAD~1
git checkout correct-branch
git cherry-pick abc1234
Duplicate Commits
# Find duplicate commits
git log --oneline --all --graph | grep "same message"
# Avoid by checking before cherry-pick
git log --oneline | grep "message to cherry-pick"
Cherry-Pick vs Other Approaches
Cherry-Pick vs Merge
| Cherry-Pick | Merge |
|---|---|
| Specific commits | Entire branch |
| Creates new commits | Preserves history |
| Can cause duplicates | Tracks branch relationships |
| Good for fixes | Good for features |
Cherry-Pick vs Rebase
| Cherry-Pick | Rebase |
|---|---|
| Apply to different branch | Replay on same lineage |
| Select specific commits | All commits |
| No history rewrite | Rewrites history |
| Safe for public branches | Risky for public branches |
Resources
Reset - Undo Commits
Undo changes and move branch pointers using Git reset.
When to Use
- “undo git commit”
- “reset git branch”
- “discard changes”
- “move branch pointer”
Quick Commands
Basic Reset
# Undo last commit, keep changes staged
git reset --soft HEAD~1
# Undo last commit, keep changes unstaged
git reset --mixed HEAD~1
git reset HEAD~1 # mixed is default
# Undo last commit, discard all changes
git reset --hard HEAD~1
# Reset to specific commit
git reset --hard abc1234
# Unstage files
git reset HEAD file.txt
git reset # Unstage all
Reset Modes
–soft (Keep Everything)
# Undo commit, keep changes staged
git reset --soft HEAD~1
# Result:
# - Commit removed from history
# - Changes stay staged
# - Working directory unchanged
# Use case: Redo commit with different message
git reset --soft HEAD~1
git commit -m "better message"
–mixed (Keep Working Changes)
# Undo commit, unstage changes
git reset --mixed HEAD~1
git reset HEAD~1 # Same (default mode)
# Result:
# - Commit removed from history
# - Changes unstaged
# - Working directory unchanged
# Use case: Re-stage files differently
git reset HEAD~1
git add specific-files.txt
git commit
–hard (Discard Everything)
# Undo commit, discard all changes
git reset --hard HEAD~1
# Result:
# - Commit removed from history
# - Staging area cleared
# - Working directory reverted
# ⚠️ WARNING: Loses uncommitted changes!
# Use case: Completely abandon recent work
git reset --hard HEAD~1
Common Use Cases
Undo Last Commit (Keep Changes)
# Made commit too early
git commit -m "incomplete work"
# Undo commit, keep changes staged
git reset --soft HEAD~1
# Continue working
vim file.txt
git add .
git commit -m "complete work"
Undo Multiple Commits
# Undo last 3 commits, keep changes
git reset --soft HEAD~3
# Or undo to specific commit
git reset --soft abc1234
# All changes from 3 commits now staged
git status
Unstage Files
# Staged wrong files
git add .
git status
# Changes to be committed:
# modified: wanted.txt
# modified: unwanted.txt
# Unstage specific file
git reset HEAD unwanted.txt
# Or unstage all
git reset HEAD
Discard All Local Changes
# Working directory is messy
# Want to start fresh from last commit
git reset --hard HEAD
# ⚠️ All uncommitted changes lost!
Move Branch to Different Commit
# Branch points to wrong commit
git reset --hard abc1234
# Branch now points to abc1234
# All commits after abc1234 are "removed"
# (recoverable via reflog)
Reset Safety
Before Using –hard
# Check what will be lost
git diff HEAD
git status
# Create backup branch
git branch backup-before-reset
# Now safe to reset
git reset --hard HEAD~5
# If needed, restore
git reset --hard backup-before-reset
Recovering from Reset
# Find lost commits
git reflog
# abc1234 HEAD@{0}: reset: moving to HEAD~3
# def5678 HEAD@{1}: commit: important work
# ghi9012 HEAD@{2}: commit: more work
# Restore to before reset
git reset --hard HEAD@{1}
# Or
git reset --hard def5678
Reset vs Revert
Reset (Rewrite History)
# Remove commits from history
git reset --hard HEAD~2
# ⚠️ Changes history
# ✓ Clean history
# ✗ Dangerous on shared branches
Revert (Preserve History)
# Create new commit that undoes changes
git revert HEAD
# ✓ Safe for shared branches
# ✓ Preserves history
# ✗ Creates additional commit
When to Use Each
| Scenario | Use Reset | Use Revert |
|---|---|---|
| Local commits not pushed | ✓ | |
| Already pushed to shared branch | ✓ | |
| Want clean history | ✓ | |
| Want to preserve history | ✓ | |
| Undoing merge commits | ✓ |
Reset Specific Files
Unstage Files
# Unstage specific file
git reset HEAD file.txt
# Unstage all files
git reset HEAD
# New syntax (Git 2.23+)
git restore --staged file.txt
Reset File to Specific Commit
# Reset file to version from 3 commits ago
git reset HEAD~3 -- file.txt
# File changes staged
# Commit or discard as needed
git commit -m "restore old version of file"
Reset with Paths
Reset Specific Files to HEAD
# Discard changes to specific file
git reset --hard HEAD -- file.txt
# Better alternative (clearer intent):
git checkout HEAD -- file.txt
# Or (Git 2.23+):
git restore file.txt
Advanced Reset Techniques
Reset and Squash
# Squash last 5 commits into one
git reset --soft HEAD~5
git commit -m "squashed commit message"
Partial Reset
# Reset some files, keep others
git reset HEAD~1 -- src/
# Only src/ files reset, rest unchanged
Reset to Remote
# Discard all local commits, match remote
git fetch origin
git reset --hard origin/main
# ⚠️ Loses all local commits!
Reset Scenarios
Wrong Branch
# Made commits on main instead of feature branch
git branch feature/my-work # Save work
git reset --hard origin/main # Reset main
git checkout feature/my-work # Continue on feature
Undo Merge
# Just merged but it was wrong
git reset --hard HEAD~1
# Or if merge not committed yet
git merge --abort
Clean Slate
# Want to completely match remote
git fetch origin
git reset --hard origin/main
git clean -fdx # Also remove untracked files
Reset and Reflog
Viewing Reflog
# See all HEAD movements
git reflog
# Output:
# abc1234 HEAD@{0}: reset: moving to HEAD~3
# def5678 HEAD@{1}: commit: important work
# ghi9012 HEAD@{2}: commit: feature added
Recovering Lost Commits
# After bad reset
git reset --hard HEAD~10
# Oh no! Lost important work
# Find it in reflog
git reflog
# def5678 HEAD@{1}: commit: important work
# Restore
git reset --hard def5678
# Or
git reset --hard HEAD@{1}
Reflog Expiration
# Reflog keeps history for 90 days (default)
# After that, commits are garbage collected
# Immediately expire reflog (dangerous!)
git reflog expire --expire=now --all
git gc --prune=now
Reset Best Practices
- Never reset public branches: Don’t reset commits pushed to shared branches
- Use –soft for commit cleanup: Safest way to redo commits
- Create backup before –hard: Use backup branch or check reflog
- Prefer revert for public branches: Safer than reset for shared history
- Check diff before –hard: Know what you’re losing
- Communicate resets on shared branches: Team needs to know
- Use –force-with-lease after reset: Safer than –force when pushing
Dangerous Reset Patterns
❌ Reset Public Branch
# BAD: Others have this commit
git reset --hard HEAD~5
git push --force origin main
# Other developers' work now conflicts
❌ Reset Without Checking
# BAD: Don't know what's being lost
git reset --hard HEAD~10
# Oops, lost important work
✓ Safe Alternative
# GOOD: Create backup first
git branch backup-main
git reset --hard HEAD~10
# Can restore if needed
git reset --hard backup-main
Reset and Push
After Local Reset
# Reset local commits
git reset --hard HEAD~3
# Try to push
git push
# error: Updates were rejected
# Must force push (dangerous!)
git push --force-with-lease
Force Push Safety
# Safer than --force (checks remote hasn't changed)
git push --force-with-lease
# Protects against overwriting others' work
Reset Alternatives
For Committed Changes
# Instead of reset --hard
# Use revert (safe for shared branches)
git revert HEAD
For Uncommitted Changes
# Instead of reset --hard
# Use stash (recoverable)
git stash
git stash drop # If sure you don't need it
For Specific Files
# Instead of reset --hard file.txt
# Use restore (clearer intent, Git 2.23+)
git restore file.txt
git restore --staged file.txt
Configuration
# Require explicit mode for reset
git config --global advice.resetNoMode true
# Always show what reset will do
git config --global advice.resetQuiet false
Emergency Recovery
Lost Commits
# Find all unreachable commits
git fsck --lost-found
# Or search reflog
git reflog --all | grep "search term"
# Recover specific commit
git cherry-pick <lost-commit-hash>
Completely Destroyed Branch
# Find branch in reflog
git reflog show feature/my-work
# Restore branch
git checkout -b feature/my-work-recovered <commit-hash>
Resources
Worktree - Multiple Working Directories
Work on multiple branches simultaneously using Git worktrees.
When to Use
- “work on multiple branches”
- “git worktree”
- “parallel development”
- “test multiple versions”
Quick Commands
Creating Worktrees
# Create worktree for new branch
git worktree add ../my-project-feature feature/new-feature
# Create worktree from existing branch
git worktree add ../my-project-bugfix bugfix/issue-123
# Create worktree with new branch from current
git worktree add -b feature/experiment ../my-project-exp
# Create temporary worktree
git worktree add --detach ../my-project-temp HEAD~5
Managing Worktrees
# List all worktrees
git worktree list
# Remove worktree
git worktree remove ../my-project-feature
# Remove and prune
git worktree prune
# Move worktree
git worktree move ../old-location ../new-location
What Are Worktrees?
Traditional Workflow Problem
# Working on feature
vim src/auth.ts
# Urgent bug needs fixing
git stash # Save work
git checkout main # Switch branch
git checkout -b hotfix/urgent
# Fix bug...
git checkout feature/auth
git stash pop # Restore work
# Lots of context switching!
Worktree Solution
# Main checkout
~/projects/myapp (feature/auth)
# Create worktree for hotfix
git worktree add ../myapp-hotfix hotfix/urgent
# Now you have two directories:
# ~/projects/myapp (feature/auth)
# ~/projects/myapp-hotfix (hotfix/urgent)
# Work in both simultaneously!
cd ../myapp-hotfix
# Fix bug...
git commit
git push
# Return to feature work
cd ~/projects/myapp
# Continue where you left off, no stash needed
Basic Worktree Operations
Create Worktree
# Create worktree for existing branch
git worktree add ../myapp-develop develop
# Create worktree with new branch
git worktree add -b feature/new-ui ../myapp-ui
# Create from specific commit
git worktree add ../myapp-v1 v1.0.0
# Create detached HEAD (no branch)
git worktree add --detach ../myapp-temp abc1234
List Worktrees
# Show all worktrees
git worktree list
# Output:
# /home/user/myapp abc1234 [main]
# /home/user/myapp-dev def5678 [develop]
# /home/user/myapp-hotfix ghi9012 [hotfix/urgent]
# Verbose output
git worktree list --porcelain
Remove Worktree
# Remove worktree (directory must be clean)
git worktree remove ../myapp-hotfix
# Force remove (even with uncommitted changes)
git worktree remove --force ../myapp-hotfix
# Remove worktree directory manually
rm -rf ../myapp-hotfix
# Then clean up Git's internal state
git worktree prune
Common Use Cases
Parallel Feature Development
# Main feature work
~/projects/myapp (feature/user-auth)
# Create worktree for related feature
git worktree add -b feature/user-profile ../myapp-profile
# Work on both features simultaneously
cd ../myapp-profile
# Implement user profile
git commit
cd ~/projects/myapp
# Continue auth work
Code Review
# Continue working on your feature
~/projects/myapp (feature/my-work)
# Create worktree to review PR
git fetch origin pull/123/head:pr-123
git worktree add ../myapp-review pr-123
# Review in separate directory
cd ../myapp-review
# Test, read code...
# No need to stash or commit WIP
# Your main work directory unchanged
Testing Branches
# Create worktree for testing
git worktree add ../myapp-test feature/experimental
cd ../myapp-test
npm install
npm test
npm start # Test in browser
# If it works, merge from main worktree
cd ~/projects/myapp
git merge feature/experimental
# If it doesn't work, just remove
git worktree remove ../myapp-test
Release Preparation
# Main development continues
~/projects/myapp (develop)
# Create worktree for release
git worktree add -b release/v2.0 ../myapp-release
cd ../myapp-release
# Prepare release: version bumps, changelog
git commit -m "chore: prepare v2.0 release"
# Development continues in main worktree
cd ~/projects/myapp
# Continue feature work uninterrupted
Worktree Directory Structure
Recommended Layout
~/projects/myapp/
.git/
main/ # main branch
worktrees/
develop/ # develop branch
feature-x/ # feature branch
hotfix/ # hotfix branch
Creating Organized Structure
# From main repository
cd ~/projects/myapp
# Create worktree subdirectory
mkdir -p worktrees
# Add worktrees to subdirectory
git worktree add worktrees/develop develop
git worktree add -b feature/ui worktrees/feature-ui
Worktree Best Practices
Naming Conventions
# Use descriptive names matching branch
git worktree add ../myapp-feature-auth feature/auth
git worktree add ../myapp-bugfix-login bugfix/login-error
git worktree add ../myapp-release-v2 release/v2.0
# Or use prefix pattern
git worktree add ../feature-auth feature/auth
git worktree add ../bugfix-login bugfix/login-error
Cleanup Strategy
# Regularly review worktrees
git worktree list
# Remove finished worktrees
git worktree remove ../myapp-feature-done
# Prune stale references
git worktree prune
# Script to clean old worktrees
#!/bin/bash
git worktree list --porcelain | grep -A2 "^worktree" | while read line; do
if [[ $line =~ ^worktree ]]; then
path=$(echo $line | awk '{print $2}')
if [ ! -d "$path" ]; then
echo "Removing stale worktree: $path"
git worktree prune
fi
fi
done
Working with Worktrees
Independent Operations
# Each worktree can:
# - Have different uncommitted changes
# - Be on different branches
# - Have different stashes
# - Run different processes
# Example: Run different versions
cd ~/projects/myapp
npm start # Start main branch on port 3000
cd ~/projects/myapp-v2
npm start -- --port 3001 # Start v2 on port 3001
# Compare both versions side-by-side
Shared Repository State
# All worktrees share:
# - Commit history
# - Branches
# - Tags
# - Remote tracking branches
# - Configuration
# Example: Fetch affects all worktrees
cd ~/projects/myapp
git fetch origin
cd ~/projects/myapp-dev
# Fetch results visible here too
git log origin/main # Shows newly fetched commits
Restrictions
# Cannot checkout same branch in multiple worktrees
git worktree add ../myapp-2 feature/auth
# error: 'feature/auth' is already checked out at '/home/user/myapp'
# Workaround: Create new branch from same commit
git worktree add -b feature/auth-copy ../myapp-2 feature/auth
Advanced Worktree Techniques
Temporary Worktrees for Testing
# Quick test of a commit
git worktree add --detach /tmp/test-commit abc1234
cd /tmp/test-commit
# Run tests...
cd -
git worktree remove /tmp/test-commit
Worktree for Each PR
#!/bin/bash
# checkout-pr.sh <PR-number>
PR=$1
WORKTREE_PATH="../myapp-pr-$PR"
# Fetch PR
git fetch origin pull/$PR/head:pr-$PR
# Create worktree
git worktree add "$WORKTREE_PATH" pr-$PR
echo "PR #$PR checked out to: $WORKTREE_PATH"
cd "$WORKTREE_PATH"
Lock Worktree
# Prevent worktree from being removed
git worktree lock ../myapp-important
# With reason
git worktree lock ../myapp-important --reason "Long-running process"
# Unlock
git worktree unlock ../myapp-important
# List shows locked status
git worktree list
Worktree with Build Artifacts
Separate Build Directories
# Main worktree
~/projects/myapp/ (develop)
src/
build/ # Build outputs
# Feature worktree with own build
~/projects/myapp-feature/ (feature/new-ui)
src/
build/ # Different build outputs
# Each has independent node_modules, build artifacts
Shared Dependencies
# Share node_modules to save space
cd ~/projects/myapp-feature
ln -s ../myapp/node_modules .
# Or use pnpm which shares by default
cd ~/projects/myapp
pnpm install
cd ~/projects/myapp-feature
pnpm install # Reuses packages from store
Migration from Stash to Worktree
Before (Stash Workflow)
# Working on feature
vim src/feature.ts
# Need to fix bug
git stash
git checkout main
git checkout -b hotfix/bug
# Fix bug
git commit
git checkout feature/work
git stash pop
After (Worktree Workflow)
# Working on feature
~/projects/myapp (feature/work)
vim src/feature.ts
# Need to fix bug - create worktree
git worktree add ../myapp-hotfix -b hotfix/bug main
# Fix in worktree
cd ../myapp-hotfix
# Fix bug
git commit
# Return to feature (no stash/pop needed!)
cd ~/projects/myapp
# Continue working, all changes intact
Troubleshooting
Worktree Directory Moved
# If you moved worktree directory manually
git worktree list
# Shows old path
# Update worktree location
git worktree repair ../new-location
Cannot Remove Worktree
# Error: Cannot remove worktree with uncommitted changes
git worktree remove ../myapp-feature
# error: --force to override
# Force remove
git worktree remove --force ../myapp-feature
# Or commit/stash changes first
cd ../myapp-feature
git stash
cd -
git worktree remove ../myapp-feature
Stale Worktree References
# Worktree directory deleted manually
rm -rf ../myapp-old
# Clean up Git state
git worktree prune
# Verify
git worktree list
Worktree vs Other Approaches
Worktree vs Stash
| Worktree | Stash |
|---|---|
| Separate directory | Same directory |
| Multiple branches simultaneously | One branch at a time |
| No context switching | Requires stash/pop |
| More disk space | Less disk space |
| Better for long-term parallel work | Better for quick switches |
Worktree vs Clone
| Worktree | Clone |
|---|---|
Shares .git directory |
Independent .git |
| Less disk space | More disk space |
| Shared configuration | Independent configuration |
| Cannot checkout same branch | Can checkout same branch |
| Faster to create | Slower to create |
Configuration
# Default path for new worktrees
git config worktree.guessRemote true
# Automatically prune worktrees on fetch
git config fetch.prune true
git config fetch.pruneTags true