Module 11: Advanced Git Tips and Tricks for Test Engineers

Master advanced Git features that solve real problems test engineers face. Use stash for context switching, cherry-pick for selective test imports, tags for test releases, and bisect for finding bugs in test code. Learn Git hooks to automate test validation and explore productivity-boosting aliases and configurations.

Git Stash and Cherry-Pick for Test Context Switching

Why This Matters

As a test automation engineer, you’re constantly interrupted. You’re writing tests for a new feature when a critical bug is reported in production. You need to drop everything, switch branches, and create regression tests immediately. But what happens to your unfinished work? Or consider this: your colleague wrote a brilliant API test on their feature branch, and you need that exact test in your branch—but you don’t want the other 47 commits they made.

These scenarios happen daily in real test engineering workflows:

The Context-Switching Problem: You’re mid-way through writing a complex end-to-end test suite when you need to urgently switch to another branch. Your tests aren’t ready to commit—some are incomplete, others won’t even run. Committing half-baked code pollutes your Git history, but losing your work isn’t an option.

The Selective Import Challenge: A team member created a perfect data-driven test on the feature-payment branch. You need that test in your feature-checkout branch, but their branch contains database migrations and configuration changes you can’t merge. You need surgical precision to extract just that one test.

The Work-In-Progress Juggling Act: You’re maintaining three different test environments simultaneously—dev, staging, and a special client demo setup. Each has slightly different test configurations that aren’t ready to merge. You need to switch between these contexts multiple times a day without losing work or creating messy commits.

Git provides two powerful commands designed exactly for these situations: git stash for temporarily shelving work and git cherry-pick for selectively copying commits. Together, these tools transform chaotic context switching into a smooth, controlled workflow that keeps your test repository clean and your productivity high.

What You’ll Accomplish

By the end of this lesson, you’ll have mastered the art of test context management in Git. Here’s how we’ll address each learning objective:

Stashing Work-in-Progress Tests: You’ll learn when and how to use git stash to save incomplete test changes—whether it’s a half-written test case, modified configuration files, or experimental test data. We’ll work through real scenarios where stashing saves your progress without polluting your commit history.

Retrieving Your Stashed Work: You’ll understand the crucial difference between git stash pop (apply and remove) and git stash apply (apply but keep), and learn when to use each. We’ll practice recovering stashed test changes and integrating them back into your working directory.

Managing Multiple Stashes: As you accumulate multiple stashed contexts, you’ll master the stash management commands: list to view all stashes with descriptive messages, show to preview what’s in a stash before applying it, and drop to clean up stashes you no longer need. You’ll learn to name stashes meaningfully so you can identify “login-tests-wip” versus “payment-tests-experimental” weeks later.

Cherry-Picking Test Cases: You’ll learn to use git cherry-pick to extract specific test commits from other branches, copying just the tests you need without merging entire branch histories. We’ll practice identifying commit hashes and applying them to your current branch.

Resolving Cherry-Pick Conflicts: When the same test file has been modified in different ways on different branches, conflicts arise. You’ll learn to recognize cherry-pick conflicts in test files, resolve them intelligently (preserving both test cases when appropriate), and complete the cherry-pick operation successfully.

Advanced Cherry-Pick Operations: Finally, you’ll master cherry-picking multiple commits at once using commit ranges and multiple hash arguments—essential when you need to import an entire test suite that was developed across several commits on another branch.

Each objective includes hands-on exercises with realistic test automation scenarios, so you’ll practice these skills in contexts you’ll encounter in your daily work. Let’s begin with Git stash and the art of saving work-in-progress.


Core Content

Core Content: Git Stash and Cherry-Pick for Test Context Switching

Core Concepts Explained

Understanding Test Context Switching

When working on test automation, you frequently need to switch between different test scenarios, bug fixes, or feature branches. Two powerful Git commands help manage this workflow efficiently:

  • Git Stash: Temporarily saves uncommitted changes so you can switch contexts cleanly
  • Git Cherry-Pick: Selectively applies specific commits from one branch to another

When to Use These Commands in Test Automation

Git Stash scenarios:

  • You’re writing tests for Feature A, but need to urgently fix a failing test on the main branch
  • You want to pull the latest changes but have work-in-progress tests
  • You need to switch branches without committing incomplete test code

Cherry-Pick scenarios:

  • You fixed a flaky test on a feature branch and need it on main immediately
  • You want to copy a specific test helper function from another branch
  • You need to apply a bug fix test across multiple release branches

Git Stash: Saving Your Work-in-Progress

Basic Stash Operations

Stashing your changes:

# You're working on login tests
$ git status
On branch feature/login-tests
Changes not staged for commit:
    modified:   tests/login.test.js
    modified:   pages/LoginPage.js

# Save your work temporarily
$ git stash
Saved working directory and index state WIP on feature/login-tests

# Your working directory is now clean
$ git status
On branch feature/login-tests
nothing to commit, working tree clean

Viewing your stashes:

# List all stashes
$ git stash list
stash@{0}: WIP on feature/login-tests: 3a2b1c4 Add login page object
stash@{1}: WIP on feature/checkout-tests: 7f8e9d6 Start checkout flow tests

Applying stashed changes:

# Apply most recent stash and keep it in the stash list
$ git stash apply

# Apply most recent stash and remove it from the stash list
$ git stash pop

# Apply a specific stash
$ git stash apply stash@{1}

Advanced Stash Techniques for Test Automation

Stashing with a descriptive message:

# Better for tracking multiple test contexts
$ git stash save "WIP: Adding data-driven tests for user registration"
Saved working directory and index state On feature/registration: WIP: Adding data-driven tests for user registration

$ git stash list
stash@{0}: On feature/registration: WIP: Adding data-driven tests for user registration
stash@{1}: On feature/login-tests: WIP on feature/login-tests: 3a2b1c4 Add login page object

Stashing untracked files (new test files):

# You created a new test file that isn't tracked yet
$ git status
Untracked files:
    tests/new-feature.test.js

# Regular stash won't save untracked files
$ git stash  # This won't include new-feature.test.js

# Include untracked files
$ git stash -u
Saved working directory and index state WIP on feature/new-tests

Stashing only specific files:

# You want to stash only the test file, not the page object
$ git stash push -m "Login test changes only" tests/login.test.js
Saved working directory and index state On feature/login-tests: Login test changes only

Practical Example: Switching Test Contexts

# Scenario: You're adding tests for the shopping cart
$ git status
On branch feature/cart-tests
Changes not staged for commit:
    modified:   tests/cart.test.js
    modified:   pages/CartPage.js

# Urgent: A smoke test is failing on main branch
# Stash your cart test work
$ git stash save "Cart tests: adding item quantity validation"

# Switch to main branch
$ git checkout main

# Pull latest changes
$ git pull origin main

# Fix the failing test
# ... make your fixes ...

# Commit the fix
$ git add tests/smoke.test.js
$ git commit -m "Fix: Correct selector for checkout button in smoke test"
$ git push origin main

# Return to your cart tests
$ git checkout feature/cart-tests

# Restore your work-in-progress
$ git stash pop

Git Cherry-Pick: Selective Commit Application

Understanding Cherry-Pick

Cherry-pick copies a specific commit from one branch and applies it to your current branch, creating a new commit with the same changes but a different commit hash.

graph LR
    A[main branch] --> B[commit A]
    B --> C[commit B]
    D[feature branch] --> E[commit X]
    E --> F[commit Y - Test Fix]
    F --> G[commit Z]
    C -.cherry-pick commit Y.-> H[commit Y' on main]
    H --> C

Basic Cherry-Pick Operations

Cherry-picking a single commit:

# First, identify the commit you want to cherry-pick
$ git log feature/new-tests --oneline
a7b8c9d Fix flaky login test by adding explicit wait
3f4e5a6 Add new registration tests
1d2e3f4 Update page objects

# Switch to the branch where you want to apply the fix
$ git checkout main

# Cherry-pick the specific commit
$ git cherry-pick a7b8c9d
[main c8d9e0f] Fix flaky login test by adding explicit wait
 Date: Mon Jan 15 10:23:45 2024 -0500
 1 file changed, 3 insertions(+), 1 deletion(-)

Cherry-picking multiple commits:

# Cherry-pick a range of commits (exclusive start, inclusive end)
$ git cherry-pick 3f4e5a6..a7b8c9d

# Cherry-pick multiple specific commits
$ git cherry-pick 3f4e5a6 a7b8c9d

Practical Example: Applying Test Utilities Across Branches

// Scenario: You created a useful test helper on feature branch
// File: tests/helpers/waitHelpers.js (on feature/advanced-tests branch)

/**
 * Wait for element to be stable (no movement for specified time)
 * Useful for fixing flaky tests with animations
 */
async function waitForElementStability(page, selector, timeout = 2000) {
    const element = await page.$(selector);
    let lastPosition = await element.boundingBox();
    
    return new Promise((resolve) => {
        const checkStability = setInterval(async () => {
            const currentPosition = await element.boundingBox();
            
            if (lastPosition.x === currentPosition.x && 
                lastPosition.y === currentPosition.y) {
                clearInterval(checkStability);
                resolve(true);
            }
            
            lastPosition = currentPosition;
        }, timeout);
    });
}

module.exports = { waitForElementStability };

Applying this helper to another branch:

# Find the commit that added this helper
$ git log feature/advanced-tests --oneline --all -- tests/helpers/waitHelpers.js
b3c4d5e Add waitForElementStability helper for animations

# Switch to the branch that needs it
$ git checkout feature/checkout-tests

# Cherry-pick the commit
$ git cherry-pick b3c4d5e

# Now you can use the helper in your checkout tests

Handling Cherry-Pick Conflicts

# If there's a conflict during cherry-pick
$ git cherry-pick a7b8c9d
error: could not apply a7b8c9d... Fix flaky login test
hint: after resolving the conflicts, mark the corrected paths
hint: with 'git add <paths>' or 'git rm <paths>'
hint: and commit the result with 'git commit'

# Check which files have conflicts
$ git status
You are currently cherry-picking commit a7b8c9d.
Unmerged paths:
    both modified:   tests/login.test.js

# Resolve the conflict manually in your editor
# Then add the resolved file
$ git add tests/login.test.js

# Continue the cherry-pick
$ git cherry-pick --continue

# Or abort if you change your mind
$ git cherry-pick --abort

Cherry-Pick Without Committing (Preview Mode)

# Apply changes without creating a commit (useful for testing)
$ git cherry-pick -n a7b8c9d

# Review the changes
$ git diff --staged

# If satisfied, commit manually
$ git commit -m "Apply test stability fix from feature branch"

# If not satisfied, reset
$ git reset --hard HEAD

Combining Stash and Cherry-Pick: Real-World Workflow

Scenario: Fixing a Test Across Multiple Branches

# You're on feature/payment-tests with uncommitted work
$ git status
modified:   tests/payment.test.js

# You discover a critical test data setup issue that affects multiple branches
# Stash your current work
$ git stash save "Payment tests: adding credit card validation"

# Go to main branch to create the fix
$ git checkout main

# Create and apply the fix
# Edit tests/setup/testData.js
$ git add tests/setup/testData.js
$ git commit -m "Fix: Correct test data initialization for user accounts"

# Note the commit hash
$ git log -1 --oneline
e9f0a1b Fix: Correct test data initialization for user accounts

# Apply to release branch
$ git checkout release/v2.0
$ git cherry-pick e9f0a1b

# Apply to your feature branch
$ git checkout feature/payment-tests
$ git cherry-pick e9f0a1b

# Restore your work-in-progress
$ git stash pop

Common Mistakes and Solutions

Mistake 1: Forgetting to Stash Before Switching Branches

Problem:

$ git checkout main
error: Your local changes to the following files would be overwritten by checkout:
    tests/login.test.js
Please commit your changes or stash them before you switch branches.

Solution:

$ git stash
$ git checkout main
# Work on main...
$ git checkout feature/login-tests
$ git stash pop

Mistake 2: Losing Track of Multiple Stashes

Problem: You have multiple unnamed stashes and can’t remember what each contains.

Solution: Always use descriptive messages and check stash contents:

# View what's in a stash before applying
$ git stash show -p stash@{0}

# Or see files changed
$ git stash show stash@{1}

Mistake 3: Cherry-Picking Creates Duplicate Commits

Problem: Cherry-picking a commit that later gets merged creates duplicate changes.

Solution: Only cherry-pick when you need the change immediately and understand it may create duplicates. Document cherry-picked commits:

$ git cherry-pick a7b8c9d
$ git commit --amend -m "Fix flaky login test (cherry-picked from feature/new-tests a7b8c9d)"

Mistake 4: Stash Pop Causes Conflicts

Problem:

$ git stash pop
Auto-merging tests/login.test.js
CONFLICT (content): Merge conflict in tests/login.test.js

Solution: The stash is preserved if pop fails. Resolve conflicts and manually drop:

# Resolve conflicts in your editor
$ git add tests/login.test.js
$ git stash drop  # Remove the stash after resolving

Debugging Tips

Check what’s in your stash without applying:

$ git stash show -p

Find which commit you want to cherry-pick:

# Search commit messages
$ git log --all --grep="fix flaky" --oneline

# See commits that changed a specific file
$ git log --all --oneline -- tests/login.test.js

Undo a cherry-pick:

# If you just cherry-picked and haven't done anything else
$ git reset --hard HEAD^

# Or find the commit before cherry-pick
$ git reflog
$ git reset --hard HEAD@{1}

Hands-On Practice

Git Stash and Cherry-Pick for Test Context Switching

🎯 Learning Objectives

  • Apply git stash to temporarily save uncommitted test changes when switching contexts
  • Use git cherry-pick to selectively apply test fixes across different branches
  • Manage multiple test automation branches simultaneously without losing work
  • Resolve conflicts when applying stashed changes or cherry-picked commits

🔨 Hands-On Exercise

Scenario

You’re working on test automation for an e-commerce application. You need to juggle multiple priorities:

  1. Writing new checkout tests on a feature branch
  2. Fixing a failing login test that blocks the CI pipeline on main
  3. Applying specific test improvements across multiple release branches

Task

Practice context switching with git stash and cherry-pick to manage these competing priorities efficiently.

Step-by-Step Instructions

Part 1: Setup Your Test Repository

# Create a practice repository
mkdir test-automation-practice
cd test-automation-practice
git init

# Create initial test structure
mkdir -p tests/login tests/checkout
cat > tests/login/test_login.py << 'EOF'
def test_valid_login():
    # TODO: This test is failing in CI
    assert login("user@example.com", "password") == True

def test_invalid_login():
    assert login("user@example.com", "wrong") == False
EOF

cat > tests/checkout/test_checkout.py << 'EOF'
def test_checkout_flow():
    assert True  # Placeholder
EOF

# Commit initial state
git add .
git commit -m "Initial test structure"

# Create feature branch for checkout tests
git checkout -b feature/checkout-improvements

# Create release branches
git checkout -b release/v1.0 main
git checkout -b release/v2.0 main
git checkout main

Part 2: Practice Git Stash

# Switch to feature branch
git checkout feature/checkout-improvements

# Start working on new checkout tests
cat > tests/checkout/test_checkout.py << 'EOF'
def test_checkout_flow():
    # Adding items to cart
    cart.add_item("product-123")
    assert cart.item_count() == 1

def test_checkout_with_coupon():
    # WIP: Testing coupon application
    cart.add_item("product-123")
    cart.apply_coupon("SAVE10")
    # TODO: Complete this test
EOF

# Don't commit yet - you're interrupted by urgent bug

Your Tasks:

  1. Stash your work-in-progress checkout tests with a descriptive message
  2. Switch to main branch to fix the failing login test
  3. Fix the login test (add proper implementation)
  4. Commit the fix to main
  5. Return to feature branch and restore your stashed changes
  6. Complete the checkout tests and commit

Part 3: Practice Cherry-Pick

# You've fixed the login test on main
# Now you need to apply this fix to both release branches

Your Tasks:

  1. Identify the commit hash of your login test fix on main
  2. Cherry-pick this fix to release/v1.0 branch
  3. Cherry-pick the same fix to release/v2.0 branch
  4. Verify the fix is present in all three branches

Part 4: Handle Conflicts

# Create a conflicting scenario
git checkout release/v1.0

# Modify the login test differently
cat > tests/login/test_login.py << 'EOF'
def test_valid_login():
    # v1.0 specific implementation
    result = legacy_login("user@example.com", "password")
    assert result == True

def test_invalid_login():
    assert login("user@example.com", "wrong") == False
EOF

git add .
git commit -m "v1.0 specific login implementation"

# Now try to cherry-pick a different login improvement from main

Your Tasks:

  1. Create a new improvement commit on main that modifies test_login.py
  2. Attempt to cherry-pick it to release/v1.0
  3. Resolve the merge conflict
  4. Complete the cherry-pick

Expected Outcomes

After Part 2 (Stash):

  • You can view your stashed changes with git stash list
  • Your feature branch contains completed checkout tests
  • Main branch has the login fix
  • No work was lost during context switching

After Part 3 (Cherry-Pick):

# All branches should have the login fix
git log release/v1.0 --oneline | grep "login"
git log release/v2.0 --oneline | grep "login"
git log main --oneline | grep "login"

After Part 4 (Conflicts):

  • Successfully merged conflicting changes
  • Understood when cherry-pick creates conflicts
  • Can resolve conflicts while preserving test integrity

Solution Approach

Click to reveal solution steps

Part 2 Solution:

# Stash WIP changes
git stash push -m "WIP: Checkout tests with coupon support"

# Fix urgent bug on main
git checkout main
cat > tests/login/test_login.py << 'EOF'
def test_valid_login():
    user = authenticate("user@example.com", "password")
    assert user.is_authenticated == True

def test_invalid_login():
    result = authenticate("user@example.com", "wrong")
    assert result is None
EOF

git add tests/login/test_login.py
git commit -m "Fix: Correct login test implementation"

# Return to feature work
git checkout feature/checkout-improvements
git stash pop

# Complete the test
# ... edit file ...
git add .
git commit -m "Add checkout tests with coupon support"

Part 3 Solution:

# Get commit hash
LOGIN_FIX=$(git log main --oneline | grep "Fix: Correct login" | cut -d' ' -f1)

# Apply to v1.0
git checkout release/v1.0
git cherry-pick $LOGIN_FIX

# Apply to v2.0
git checkout release/v2.0
git cherry-pick $LOGIN_FIX

Part 4 Solution:

# When conflict occurs during cherry-pick:
git status  # See conflicted files
# Edit tests/login/test_login.py manually to merge both changes
git add tests/login/test_login.py
git cherry-pick --continue

🎓 Key Takeaways

  • Git Stash is Your Safety Net: Use git stash to temporarily save uncommitted test changes when you need to switch contexts urgently. Always add descriptive messages with git stash push -m "message" to track what you stashed.

  • Cherry-Pick for Selective Test Fixes: When a critical test fix needs to be applied across multiple release branches, git cherry-pick allows you to apply specific commits without merging entire branches—essential for maintaining stable test suites across versions.

  • Stash vs. Commit: Stash for incomplete work that isn’t ready to commit; commit when tests are complete and meaningful. Use git stash list to track multiple stashed contexts and git stash pop vs git stash apply depending on whether you want to keep or remove the stash.

  • Conflict Resolution is Normal: Cherry-picking commits across diverged branches often causes conflicts. This is expected—carefully resolve conflicts to preserve test accuracy for each branch’s specific implementation.

  • Branch Hygiene Matters: Regularly clean up stashes (git stash drop) and keep track of which fixes need cherry-picking across branches. Document cross-branch test dependencies in your team’s workflow.


🚀 Next Steps

Practice These Skills

  1. Daily Workflow: Make stashing a habit when interrupted—stash before switching branches for code reviews or urgent fixes
  2. Multi-Branch Testing: Practice maintaining test suites across 3+ active release branches
  3. Stash Management: Experiment with git stash branch to create branches from stashes when work-in-progress becomes more complex
  4. Interactive Cherry-Pick: Use git cherry-pick -n (no commit) to review changes before finalizing
  • Git Worktrees: Maintain multiple working directories for different branches simultaneously (alternative to stashing)
  • Git Rebase: Clean up test commit history before merging feature branches
  • Git Bisect: Automate finding which commit introduced a test failure
  • Conflict Resolution Strategies: Advanced merge strategies for test automation code (ours, theirs, patience)
  • CI/CD Integration: Automatically cherry-pick test fixes to protected branches using CI pipelines
  • Git Hooks: Create pre-commit hooks to validate tests before allowing commits or stashes

Challenge Yourself

Create a real-world scenario: Maintain test suites for a project with main, develop, and three release branches while juggling feature work, bug fixes, and test refactoring across all branches using only stash and cherry-pick.