Module 8: Fixing Mistakes and Resolving Merge Conflicts

Learn to handle the inevitable: mistakes and conflicts in test code. Practice undoing commits, recovering deleted test files, and resolving merge conflicts in test scripts, configuration files, and test data. Build confidence in recovering from common Git problems without losing work.

Advanced Recovery: Reflog, Cherry-pick, and Lost Commit Rescue

Why This Matters

Every test engineer eventually faces that heart-stopping moment: you’ve just executed a hard reset, force-pushed to the wrong branch, or accidentally deleted a feature branch containing days of carefully crafted test automation work. The commit with your critical API test validations seems gone forever. Your carefully configured test data setup has vanished. The panic sets in.

This is exactly when advanced Git recovery skills become invaluable.

In real-world test automation projects, mistakes happen constantly:

  • You accidentally reset to the wrong commit, losing your latest integration tests
  • A teammate force-pushes and overwrites your performance test suite
  • You cherry-pick a bug fix from another branch but encounter conflicts in test configurations
  • You realize you need a specific test assertion from a branch you thought was merged
  • You experimented with refactoring and now can’t find the original working version

Unlike production code, test code often undergoes aggressive experimentation, refactoring, and branching. You might try different testing frameworks, test multiple approaches simultaneously, or maintain separate test configurations for various environments. This makes advanced recovery techniques not just useful—they’re essential survival skills.

Common pain points this lesson addresses:

  • “I lost my commits!” - Learn to recover seemingly deleted work from Git’s safety net
  • “I need just one fix from another branch” - Master selective commit application without full merges
  • “My hard reset destroyed everything” - Discover how to undo even the most destructive operations
  • “I’m in detached HEAD and lost my work” - Understand how to rescue commits from detached states

What You’ll Accomplish

This lesson transforms you from someone who fears Git operations into a confident engineer who can recover from virtually any mistake. You’ll gain the expertise to be your team’s Git recovery expert—the person others turn to when disaster strikes.

Your Learning Journey

Understanding the Safety Net (Reflog Mastery) You’ll start by exploring Git’s hidden safety mechanism: the reflog. You’ll learn how Git silently records every HEAD movement, creating an insurance policy for your work. By the end, you’ll confidently navigate reflog entries to find any lost commit, understanding exactly how long Git keeps your data and how to interpret cryptic reflog references.

Selective Commit Application (Cherry-pick Techniques) Next, you’ll master cherry-picking—Git’s tool for surgical precision. You’ll practice copying specific commits across branches, essential when you need one bug fix without merging an entire feature branch. You’ll encounter real-world scenarios like cherry-picking test data configurations or selectively applying assertion updates while avoiding conflicts in test framework setup.

Recovering from Disasters (Lost Commit Rescue) You’ll then tackle worst-case scenarios: recovering from hard resets, restoring deleted branches, and rescuing work from detached HEAD states. Through hands-on exercises with actual test automation files, you’ll learn the exact commands and techniques to recover seemingly lost test suites, configuration files, and test data.

Advanced Recovery Patterns (Real-world Scenarios) Finally, you’ll apply everything in complex, realistic recovery scenarios: handling force-push recovery, finding commits by content when you’ve forgotten the commit hash, and combining reflog with branch operations to restore entire feature branches of test automation work.

By lesson completion, you’ll have a comprehensive recovery toolkit. You’ll understand not just the how but the why behind each technique, enabling you to adapt these skills to any recovery challenge you encounter in your test automation career.

Confidence Building: Each exercise uses realistic test automation scenarios—API test scripts, Selenium configurations, test data files, CI/CD pipeline definitions—so you’ll practice recovery in contexts directly applicable to your daily work. You’ll finish this lesson knowing that virtually no Git mistake is permanent, and you have the skills to prove it.


Core Content

Core Content: Advanced Recovery: Reflog, Cherry-pick, and Lost Commit Rescue

Core Concepts Explained

Understanding Git’s Safety Net: The Reflog

Git’s reflog (reference log) is your ultimate safety net—a chronological record of every HEAD movement in your repository. Unlike the commit history shown by git log, the reflog tracks where your HEAD has been, including commits that are no longer reachable from any branch.

Key Characteristics:

  • Local to your repository (not pushed to remotes)
  • Expires after 90 days by default (configurable)
  • Records branch switches, commits, resets, rebases, and cherry-picks
  • Each entry has a unique reference like HEAD@{n}

Cherry-picking: Selective Commit Application

Cherry-picking allows you to apply specific commits from one branch to another without merging entire branches. This is essential when:

  • You need a specific bug fix from another branch
  • You committed to the wrong branch
  • You want to selectively migrate features

Lost Commit Recovery Strategy

Commits in Git are rarely truly “lost”—they become unreachable. Understanding this distinction is crucial:

graph TD
    A[Commits Exist] --> B{Reachable?}
    B -->|Yes| C[Visible in git log]
    B -->|No| D[Visible in reflog]
    D --> E[Recoverable via reflog]
    E --> F[Create new branch or reset]

Practical Code Examples

1. Exploring and Using the Reflog

Basic Reflog Examination:

# View complete reflog history
$ git reflog
a1b2c3d HEAD@{0}: commit: Add login test suite
d4e5f6g HEAD@{1}: checkout: moving from feature-x to main
h7i8j9k HEAD@{2}: commit: Fix homepage navigation
l0m1n2o HEAD@{3}: reset: moving to HEAD~1
p3q4r5s HEAD@{4}: commit: Accidentally committed config files

# View reflog for a specific branch
$ git reflog show feature-branch

# See reflog with dates
$ git reflog --date=iso

Reflog Output Example:

$ git reflog
e4f2a9c (HEAD -> main) HEAD@{0}: commit: Add Playwright config for CI
b3d1e5f HEAD@{1}: commit: Update test dependencies
7a8c2d1 HEAD@{2}: reset: moving to HEAD~3
9f4e3b2 HEAD@{3}: commit: WIP: broken test implementation
c5d6e7f HEAD@{4}: commit: Add user registration tests
2a3b4c5 HEAD@{5}: commit: Setup test data fixtures

2. Recovering Lost Commits

Scenario: Accidental Hard Reset

# Current situation: You accidentally reset and lost commits
$ git log --oneline
b3d1e5f Update test dependencies
a1b2c3d Initial Playwright setup

# Check reflog to find lost commits
$ git reflog
e4f2a9c HEAD@{0}: reset: moving to HEAD~3
9f4e3b2 HEAD@{1}: commit: Important test scenarios  # ← This is lost!
c5d6e7f HEAD@{2}: commit: Add user registration tests  # ← This too!

# Option 1: Create a new branch from the lost commit
$ git branch recovery-branch 9f4e3b2
$ git checkout recovery-branch

# Option 2: Reset current branch to the lost commit
$ git reset --hard 9f4e3b2

# Option 3: Cherry-pick the lost commits
$ git cherry-pick 9f4e3b2 c5d6e7f

Scenario: Deleted Branch Recovery

# You deleted a branch accidentally
$ git branch -D feature-login-tests
Deleted branch feature-login-tests (was x7y8z9a)

# Find the last commit from that branch in reflog
$ git reflog | grep feature-login-tests
x7y8z9a HEAD@{10}: commit: Complete login test suite

# Restore the branch
$ git branch feature-login-tests x7y8z9a
$ git checkout feature-login-tests

# Verify recovery
$ git log --oneline
x7y8z9a Complete login test suite
w6v5u4t Add password validation tests

3. Cherry-picking Commits

Basic Cherry-pick:

# Apply a specific commit from another branch
$ git checkout main
$ git cherry-pick a1b2c3d

# Cherry-pick output
[main e5f6g7h] Fix critical login bug
 Date: Mon Jan 15 10:30:00 2024 -0500
 2 files changed, 15 insertions(+), 3 deletions(-)

Cherry-picking Multiple Commits:

# Cherry-pick a range of commits
$ git cherry-pick a1b2c3d..f4e5d6c

# Cherry-pick multiple specific commits
$ git cherry-pick abc123 def456 ghi789

# Cherry-pick without committing (for review/modification)
$ git cherry-pick --no-commit abc123
$ git status
Changes to be committed:
  modified:   tests/login.spec.ts
  
# Review changes, then commit
$ git commit -m "Cherry-picked: Fix login validation"

Practical Test Automation Example:

# Scenario: You built a test utility on the wrong branch
$ git log --oneline feature-branch
d4e5f6g Add API response validator utility  # ← Need this on main
c3b2a1d Feature-specific test

# Switch to main and cherry-pick just the utility
$ git checkout main
$ git cherry-pick d4e5f6g

# If there are conflicts
$ git status
both modified:   utils/validators.js

# Resolve conflicts in your editor, then continue
$ git add utils/validators.js
$ git cherry-pick --continue

4. Advanced Recovery Techniques

Finding Orphaned Commits:

# List all unreachable commits (lost commits)
$ git fsck --lost-found
dangling commit 9a8b7c6d5e4f3g2h1

# Examine the dangling commit
$ git show 9a8b7c6d5e4f3g2h1
commit 9a8b7c6d5e4f3g2h1
Author: Test Engineer
Date: Mon Jan 15 14:20:00 2024

    Critical test suite before rebase disaster
    
    diff --git a/tests/checkout.spec.ts
    ...

# Recover it
$ git branch recovered-tests 9a8b7c6d5e4f3g2h1

Time-based Recovery:

# Find HEAD position from 2 hours ago
$ git reflog --date=relative
e4f2a9c HEAD@{2 hours ago}: commit: Add login tests

# Reset to that position
$ git reset --hard HEAD@{2 hours ago}

# Or checkout that state for inspection
$ git checkout HEAD@{yesterday}

Cherry-pick with Mainline (for merge commits):

# When cherry-picking a merge commit, specify which parent to follow
$ git cherry-pick -m 1 abc123

# -m 1 uses the first parent (usually the branch you merged into)
# -m 2 uses the second parent (the branch you merged from)

5. Complete Recovery Workflow Example

# Step 1: Identify the problem
$ git log --oneline
a1b2c3d Fix broken test  # Current HEAD
# Missing: Several test suite commits!

# Step 2: Check reflog for history
$ git reflog --all --date=relative
z9y8x7w HEAD@{30 minutes ago}: reset: moving to a1b2c3d
m5n6o7p HEAD@{35 minutes ago}: commit: Complete E2E test suite  # ← Found it!
k3l4m5n HEAD@{40 minutes ago}: commit: Add test data setup
j1k2l3m HEAD@{45 minutes ago}: commit: Configure test environment

# Step 3: Create recovery branch
$ git branch test-suite-recovery m5n6o7p

# Step 4: Examine recovered content
$ git checkout test-suite-recovery
$ git log --oneline
m5n6o7p Complete E2E test suite
k3l4m5n Add test data setup
j1k2l3m Configure test environment

# Step 5: Cherry-pick needed commits to main
$ git checkout main
$ git cherry-pick j1k2l3m k3l4m5n m5n6o7p

# Step 6: Verify integration
$ npm test  # Run your test suite

Common Mistakes Section

Mistake 1: Assuming Deleted Commits Are Gone Forever

Problem:

$ git reset --hard HEAD~5
# Panic: "I just lost all my work!"

Solution: Remember reflog exists for exactly this scenario. Commits remain for 90 days.

$ git reflog  # Always check reflog first
$ git branch recovery HEAD@{1}

Mistake 2: Cherry-picking Without Understanding Context

Problem:

$ git cherry-pick abc123
# Creates conflicts because dependent commits are missing

Solution: Check commit dependencies first:

# View commit with context
$ git show abc123

# Check what changed
$ git log -p abc123

# Consider cherry-picking a range if commits are related
$ git cherry-pick abc123^..def456

Mistake 3: Not Handling Cherry-pick Conflicts Properly

Problem:

$ git cherry-pick abc123
error: could not apply abc123... Add test
# User abandons the operation without cleanup

Solution: Always complete or abort:

# Option 1: Resolve and continue
$ git add resolved-file.js
$ git cherry-pick --continue

# Option 2: Abort cleanly
$ git cherry-pick --abort

# Check status first
$ git status  # Shows cherry-pick in progress

Mistake 4: Relying on Reflog in CI/CD Environments

Problem: Reflog is local and won’t exist in fresh CI clones.

Solution: For automation:

  • Use proper branching strategies
  • Tag important commits
  • Don’t depend on reflog for reproducible builds
# Instead of reflog references in scripts
git checkout HEAD@{5}  # ❌ Won't work in CI

# Use explicit references
git checkout v1.2.3    # ✅ Works everywhere

Debugging Recovery Issues

# Reflog entries disappeared
$ git config gc.reflogExpire  # Check expiration setting
# Default: 90 days

# Can't find a specific action
$ git reflog --all  # Check all refs, not just HEAD

# Cherry-pick creates wrong changes
$ git diff abc123^..abc123  # Preview what will be applied
$ git cherry-pick --no-commit abc123  # Apply without committing for review

# Recovery branch has wrong commits
$ git log --graph --all --oneline  # Visualize history
$ git show-ref  # See all references

Best Practices Summary

  1. Check reflog immediately after any destructive operation
  2. Create recovery branches before resetting to preserve both states
  3. Use --no-commit with cherry-pick for complex scenarios
  4. Verify with git log after recovery operations
  5. Test your recovered code before pushing to shared branches
  6. Document recovery steps for team knowledge sharing

Pro Tip: Create an alias for quick reflog access:

$ git config --global alias.rl "reflog --date=relative"
$ git rl  # Now shows reflog with readable timestamps

Hands-On Practice

Hands-On Exercise

Exercise: Rescue a Lost Feature Branch

Scenario: You’re working on a critical feature when disaster strikes! You accidentally deleted your feature branch before merging it, then performed several other operations. Your colleague needs that work ASAP. Use reflog, cherry-pick, and advanced recovery techniques to save the day.

Task

Simulate a real-world disaster scenario and recover lost commits using reflog and cherry-pick operations.

Step-by-Step Instructions

Setup Phase:

  1. Create a new practice repository:
mkdir git-recovery-practice
cd git-recovery-practice
git init
  1. Create initial commit on main:
echo "# Project" > README.md
git add README.md
git commit -m "Initial commit"
  1. Create a feature branch with important work:
git checkout -b feature/payment-integration
echo "payment_gateway = 'stripe'" > payment.py
git add payment.py
git commit -m "Add payment gateway config"

echo "def process_payment():\n    pass" >> payment.py
git add payment.py
git commit -m "Add payment processing function"

echo "# Payment Integration Complete" >> README.md
git add README.md
git commit -m "Update docs for payment feature"
  1. Simulate the disaster - switch to main and delete the feature branch:
git checkout main
git branch -D feature/payment-integration
  1. Make more commits to “bury” the lost work:
echo "config.ini" > .gitignore
git add .gitignore
git commit -m "Add gitignore"

echo "v1.0.0" > VERSION
git add VERSION
git commit -m "Add version file"

Recovery Phase - Your Task:

  1. Use git reflog to find the lost commits from the deleted branch

  2. Identify the commit hash of “Update docs for payment feature”

  3. Create a new branch from that commit to recover all the work

  4. Cherry-pick only the two payment-related commits (not the docs update) onto main

  5. Verify the recovery by checking:

    • The reflog shows all operations
    • Main now has the payment.py file
    • The commit history is clean

Expected Outcome

After completing this exercise, you should have:

  • ✅ A recovered branch with all original feature commits
  • ✅ Two cherry-picked commits on main (payment config and processing function)
  • ✅ Understanding of how to navigate reflog
  • ✅ The payment.py file in your main branch
  • ✅ Clean commit history without the documentation commit

Solution Approach

Click to reveal solution

Step 6-7: Finding lost commits

# View reflog to find deleted branch
git reflog

# Look for entry like: abc1234 HEAD@{5}: commit: Update docs for payment feature
# Note the commit hash (abc1234 in this example)

Step 8: Recover the branch

# Create new branch from the lost commit
git branch feature/payment-recovered <commit-hash>

# Verify recovery
git log feature/payment-recovered --oneline

Step 9: Cherry-pick specific commits

# Ensure you're on main
git checkout main

# Cherry-pick the payment commits (find their hashes from log)
git cherry-pick <payment-config-hash>
git cherry-pick <payment-function-hash>

# Alternative: Cherry-pick a range
git cherry-pick <first-hash>^..<last-hash>

Step 10: Verification

# Check reflog shows all operations
git reflog | head -20

# Verify files exist
ls -la payment.py

# Check commit history
git log --oneline --graph --all

# Verify only 2 commits were cherry-picked
git log main --oneline | grep payment

Advanced: If you had conflicts during cherry-pick

# View conflict
git status

# Resolve conflicts in files
# Then continue
git add .
git cherry-pick --continue

# Or abort if needed
git cherry-pick --abort

Key Takeaways

🔑 Main Concepts Learned:

  • Reflog is your safety net: Git reflog records every HEAD movement for ~90 days (configurable). Even “deleted” branches and reset commits are recoverable through reflog - nothing is truly lost until garbage collection runs.

  • Cherry-pick enables surgical commit recovery: When you need specific commits without entire branches, cherry-pick lets you apply individual commits to any branch. This is invaluable for extracting work from deleted branches or reorganizing history.

  • Branch deletion doesn’t delete commits: Deleting a branch only removes the reference pointer, not the commits themselves. Understanding Git’s object model means knowing that commits remain accessible through their SHA-1 hashes.

  • Recovery workflow patterns: The standard recovery pattern is: reflog → identify hash → create reference (branch/tag) → cherry-pick or merge as needed. This workflow solves 90% of “lost work” scenarios.

  • Prevention through knowledge: Understanding these recovery tools reduces fear of Git operations. Knowing you can recover from mistakes enables confident experimentation and faster problem-solving.


Next Steps

Practice These Scenarios

  1. Simulate hard reset recovery: Use git reset --hard to move HEAD back several commits, then recover using reflog
  2. Practice detached HEAD recovery: Make commits in detached HEAD state, switch branches, then recover the orphaned commits
  3. Interactive rebase gone wrong: Start an interactive rebase, abandon it midway, and recover the original state
  4. Accidental stash drop: Use git fsck combined with reflog to recover dropped stashes
  • Git fsck and dangling commits: Learn to find truly orphaned objects beyond reflog’s scope
  • Git object internals: Understanding blobs, trees, and commits deepens recovery knowledge
  • Bisect with recovered commits: Use git bisect to find bugs in recovered commit ranges
  • Advanced rebase strategies: Combining reflog safety with interactive rebase power
  • Git hooks for safety: Create pre-delete hooks to warn before branch deletion
  • Repository forensics: Techniques for auditing and recovering data in complex multi-user scenarios
  • git help revisions - Understanding commit reference syntax
  • git help reflog - Deep dive into reflog options and expiration
  • Pro Git Book, Chapter 10: Git Internals - Understanding Git’s object database

Pro tip: Set an alias for quick reflog access: git config --global alias.undo 'reflog show --all'