auto-update-daily-20260202

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

  1. Always use messages: git stash push -m "description"
  2. Clean up old stashes: Review and drop unneeded stashes
  3. Prefer commits over stash: For long-term work
  4. Use branches for experiments: Better than multiple stashes
  5. Pop when you can: Don’t accumulate many stashes
  6. Include untracked with -u: If you have new files
  7. 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

  1. Know your good commit: Find a commit where feature definitely worked
  2. Make test specific: Test only the broken behavior, not everything
  3. Keep test fast: Faster tests = faster bisect
  4. Use skip for build failures: Don’t waste time on unbuildable commits
  5. Automate when possible: Manual testing is error-prone
  6. Test before bisecting: Verify your test works on good and bad commits
  7. 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

  1. Document the cherry-pick: Use -x flag to add reference
  2. Test after cherry-pick: Code may behave differently in target branch
  3. Prefer merge for features: Cherry-pick for fixes, not feature development
  4. Consider dependencies: Ensure cherry-picked commit doesn’t depend on others
  5. Clean commit history: Cherry-pick atomic commits, not messy WIP commits
  6. Communicate with team: Let others know about cherry-picks to shared branches
  7. 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

  1. Never reset public branches: Don’t reset commits pushed to shared branches
  2. Use –soft for commit cleanup: Safest way to redo commits
  3. Create backup before –hard: Use backup branch or check reflog
  4. Prefer revert for public branches: Safer than reset for shared history
  5. Check diff before –hard: Know what you’re losing
  6. Communicate resets on shared branches: Team needs to know
  7. 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

~/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

Resources