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.

Conflict Prevention and Team Recovery Workflows

Why This Matters

Picture this: You’ve just merged a teammate’s changes into your test automation branch, and suddenly Git reports conflicts in your critical end-to-end test suite. Or worse—you accidentally deleted an entire folder of integration tests and committed the change. Your CI/CD pipeline is broken, your team is waiting, and panic sets in.

These scenarios aren’t hypothetical—they’re inevitable. When multiple test engineers work on the same test framework, updating page objects, modifying test data, and adjusting configuration files, conflicts and mistakes become part of daily life. The difference between a minor hiccup and a project-stopping crisis lies in your ability to recover quickly and confidently.

Real-World Impact

In test automation projects, conflicts commonly occur in:

  • Test scripts when multiple engineers update the same test cases
  • Page object models when UI changes require concurrent updates
  • Configuration files (JSON, YAML, properties) managing test environments
  • Test data files with overlapping updates to datasets
  • Framework utilities and shared helper functions

Without proper recovery skills, teams waste hours (or days) recreating lost work, manually merging changes, or worse—abandoning valuable test coverage because “it’s easier to start over.”

What You’ll Gain

This lesson transforms Git from a source of anxiety into a safety net. You’ll learn the exact commands and workflows to:

  • Recover from mistakes before they reach the shared repository
  • Resolve conflicts systematically rather than guessing
  • Prevent conflicts through smart workflows and communication
  • Restore deleted work even when it seems gone forever

By the end, you’ll handle Git problems with the same confidence you bring to debugging test failures—methodically, calmly, and effectively.

Learning Objectives Overview

This lesson takes you through progressively complex recovery scenarios, building your confidence with each skill:

1. Undoing Commits and Recovering from Mistakes

You’ll master the three primary undo mechanisms in Git: reset, revert, and reflog. We’ll cover when to use each approach, practice undoing local commits before pushing, and explore how to recover from seemingly catastrophic mistakes. You’ll work through realistic scenarios like “I committed to the wrong branch” and “I need to undo my last three commits.”

2. Recovering Deleted Test Files

Learn to restore accidentally deleted test files using Git’s history, even if you’ve already committed the deletion. You’ll practice recovering individual test scripts, entire test suites, and specific versions of files from any point in your project’s history. This section includes safety nets for common deletion mistakes.

3. Resolving Merge Conflicts in Test Code

Dive deep into merge conflict resolution with hands-on practice in test automation contexts. You’ll learn to read conflict markers, understand what Git is showing you, and make informed decisions about which changes to keep. We’ll work through conflicts in test methods, assertions, and test setup/teardown code.

4. Handling Conflicts in Configuration and Test Data

Configuration files and test data require special consideration during conflicts. You’ll practice resolving conflicts in JSON configuration files, YAML pipeline definitions, CSV test data, and properties files. Learn strategies for preserving both sets of changes when both are valid, and techniques for validating your resolutions.

5. Conflict Prevention Strategies

Prevention is better than cure. You’ll learn workflow practices that minimize conflicts: effective branching strategies for test development, communication patterns for team coordination, and Git tools like git diff and git merge --no-commit that help you preview and prevent problems.

6. Team Recovery Workflows

Finally, you’ll implement complete recovery workflows for common team scenarios: handling conflicts when multiple engineers update shared page objects, recovering when someone force-pushes over your work, and coordinating fixes when the main branch breaks. These workflows combine all previous skills into production-ready practices.

Each section includes practical exercises using realistic test automation code, ensuring you can apply these skills immediately to your projects. Let’s build your Git recovery confidence—starting with the safety net of undoing commits.


Core Content

Core Content: Conflict Prevention and Team Recovery Workflows

1. Core Concepts Explained

Understanding Merge Conflicts in Test Automation

When multiple team members work on test automation simultaneously, conflicts arise when changes to the same files overlap. In test automation projects, conflicts commonly occur in:

  • Test specification files
  • Page object models
  • Configuration files
  • Test data files
  • Shared utility functions

Why Conflicts Happen:

  • Two developers modify the same test case
  • Concurrent updates to selectors in page objects
  • Simultaneous configuration changes
  • Parallel feature branch development

Git Workflow Fundamentals for Test Teams

A robust Git workflow prevents conflicts and enables quick recovery:

graph LR
    A[Main Branch] --> B[Feature Branch]
    B --> C[Local Changes]
    C --> D[Commit]
    D --> E[Pull Latest]
    E --> F[Resolve Conflicts]
    F --> G[Push & PR]
    G --> A

Branch Protection and Collaboration Strategies

Key Principles:

  1. Frequent Pulling: Sync with main branch regularly
  2. Atomic Commits: Small, focused changes reduce conflict scope
  3. Communication: Coordinate when working on same test areas
  4. Feature Branches: Isolate work until ready to merge

2. Practical Code Examples

Setting Up a Conflict-Prevention Workflow

Step 1: Configure Git for Better Conflict Resolution

# Set up global configuration
$ git config --global merge.conflictstyle diff3
$ git config --global pull.rebase false

# Configure your identity
$ git config --global user.name "Your Name"
$ git config --global user.email "your.email@example.com"

Step 2: Create a Feature Branch for Test Development

# Always start from updated main branch
$ git checkout main
$ git pull origin main

# Create feature branch for your test work
$ git checkout -b feature/login-tests

Preventing Conflicts Through Regular Syncing

Before Making Changes:

# Pull latest changes from main
$ git fetch origin
$ git merge origin/main

# Output shows current status
Already up to date.

After Each Test Session:

# Stage your test files
$ git add tests/login.spec.js
$ git add pages/LoginPage.js

# Commit with descriptive message
$ git commit -m "Add login validation tests for email field"

# Pull any new changes before pushing
$ git pull origin main

# Push your branch
$ git push origin feature/login-tests

Handling Merge Conflicts in Test Files

Scenario: Two developers modified the same test

# Attempting to merge
$ git merge origin/main

Auto-merging tests/checkout.spec.js
CONFLICT (content): Merge conflict in tests/checkout.spec.js
Automatic merge failed; fix conflicts and then commit the result.

Conflict in Test File:

// tests/checkout.spec.js
describe('Checkout Process', () => {
<<<<<<< HEAD
  it('should calculate tax correctly', async () => {
    await checkoutPage.enterZipCode('90210');
    const tax = await checkoutPage.getTaxAmount();
    expect(tax).toBe('$8.75');
=======
  it('should apply discount code', async () => {
    await checkoutPage.enterPromoCode('SAVE20');
    const discount = await checkoutPage.getDiscount();
    expect(discount).toBe('20%');
>>>>>>> origin/main
  });
});

Resolution Strategy:

// After resolution - keep both tests
describe('Checkout Process', () => {
  it('should calculate tax correctly', async () => {
    await checkoutPage.enterZipCode('90210');
    const tax = await checkoutPage.getTaxAmount();
    expect(tax).toBe('$8.75');
  });

  it('should apply discount code', async () => {
    await checkoutPage.enterPromoCode('SAVE20');
    const discount = await checkoutPage.getDiscount();
    expect(discount).toBe('20%');
  });
});

Complete Resolution Process:

# 1. Edit file to resolve conflicts manually
# 2. Remove conflict markers (<<<<<<<, =======, >>>>>>>)

# 3. Stage resolved file
$ git add tests/checkout.spec.js

# 4. Complete the merge
$ git commit -m "Merge main: resolved checkout test conflicts"

# 5. Verify tests still pass
$ npm test

# 6. Push merged changes
$ git push origin feature/checkout-tests

Preventing Conflicts in Page Object Models

Strategy: Modular Page Objects

// pages/LoginPage.js - Good practice
class LoginPage {
  // Each developer works on separate methods
  constructor() {
    this.emailField = '#email';
    this.passwordField = '#password';
    this.submitButton = '#login-btn';
  }

  async enterEmail(email) {
    await page.fill(this.emailField, email);
  }

  async enterPassword(password) {
    await page.fill(this.passwordField, password);
  }
}

Avoiding Conflicts in Selectors:

// config/selectors.js - Centralized selector management
module.exports = {
  login: {
    emailField: '#email',
    passwordField: '#password',
    submitButton: '#login-btn'
  },
  checkout: {
    // Developer B works here
    cartTotal: '.cart-total',
    checkoutButton: '#checkout-btn'
  }
};

Team Recovery: Undoing Problematic Commits

Scenario 1: Undo Last Commit (Not Pushed)

# Keep changes but undo commit
$ git reset --soft HEAD~1

# Completely remove changes
$ git reset --hard HEAD~1

Scenario 2: Revert Pushed Commit

# Find the commit hash
$ git log --oneline
a1b2c3d Add flaky test
e4f5g6h Fix login bug

# Create reverting commit
$ git revert a1b2c3d

# Push revert
$ git push origin main

Scenario 3: Recover Deleted Test File

# View deleted files
$ git log --diff-filter=D --summary

# Restore specific file
$ git checkout a1b2c3d^ -- tests/important-test.spec.js

# Stage and commit restored file
$ git add tests/important-test.spec.js
$ git commit -m "Restore accidentally deleted test"

Using Stash for Quick Context Switching

# Save work in progress without committing
$ git stash save "WIP: checkout validation tests"

# Switch to another branch for urgent fix
$ git checkout main
$ git checkout -b hotfix/critical-test-fix

# Make fix and commit
$ git add tests/critical.spec.js
$ git commit -m "Fix critical test failure"

# Return to original work
$ git checkout feature/checkout-tests
$ git stash pop

# Output shows restored changes
Auto-merging tests/checkout.spec.js

Pull Request Workflow for Test Teams

# Before creating PR, ensure branch is clean
$ git status
On branch feature/login-tests
Your branch is up to date with 'origin/feature/login-tests'.

# Rebase on main for clean history
$ git fetch origin
$ git rebase origin/main

# Force push (only on feature branches)
$ git push --force-with-lease origin feature/login-tests

Collaborative Conflict Resolution

Pre-merge Checklist:

# 1. Pull latest main
$ git checkout main
$ git pull origin main

# 2. Switch to feature branch
$ git checkout feature/login-tests

# 3. Merge main into feature
$ git merge main

# 4. Run full test suite
$ npm test

# 5. If tests pass, push
$ git push origin feature/login-tests

3. Common Mistakes Section

Mistake 1: Not Pulling Before Starting Work

Problem:

$ git push origin feature/tests
! [rejected] feature/tests -> feature/tests (non-fast-forward)

Solution:

# Always pull first
$ git pull origin main
$ git push origin feature/tests

Mistake 2: Resolving Conflicts Without Testing

Problem: Merge completed but tests break silently

Solution:

# After every conflict resolution
$ git merge origin/main
# Resolve conflicts
$ git add .
$ git commit -m "Merge resolved"

# ALWAYS run tests before pushing
$ npm test
$ git push origin feature/tests

Mistake 3: Using git push --force on Shared Branches

Never do this:

# DANGEROUS on main/shared branches
$ git push --force origin main  # ❌ Never!

Safe alternative:

# Only on your feature branches
$ git push --force-with-lease origin feature/your-branch  # ✅ Safe

Mistake 4: Large Commits Spanning Multiple Features

Problem: Makes conflicts harder to resolve

Better approach:

# Commit after each logical change
$ git add tests/login-validation.spec.js
$ git commit -m "Add email validation tests"

$ git add tests/login-security.spec.js
$ git commit -m "Add password security tests"

Debugging Common Conflict Issues

Issue: Conflict Markers Left in Code

# Search for conflict markers before committing
$ grep -r "<<<<<<" tests/
$ grep -r ">>>>>>>" tests/

# If found, resolve and re-stage
$ git add tests/problematic-file.spec.js

Issue: Lost Changes After Conflict Resolution

# Use reflog to recover
$ git reflog
$ git checkout HEAD@{2}  # Restore to earlier state

# Cherry-pick your changes
$ git cherry-pick abc123

Prevention: Use Git Hooks

# .git/hooks/pre-commit
#!/bin/bash
if grep -r "<<<<<<" tests/ ; then
    echo "Conflict markers found! Resolve before committing."
    exit 1
fi

This workflow-based approach ensures your test automation team maintains code quality while preventing and efficiently resolving conflicts when they occur.


Hands-On Practice

Hands-On Exercise

Exercise: Implementing a Conflict-Free Test Automation Workflow

Scenario

You’re joining a test automation team that has been experiencing frequent merge conflicts, overwritten test files, and failed CI/CD pipelines. Your task is to implement proper conflict prevention strategies and establish recovery workflows.

Task

Set up a conflict-resistant test automation workflow with proper branching, synchronization, and recovery mechanisms.

Step-by-Step Instructions

Part 1: Setup Conflict Prevention (20 minutes)

  1. Create a proper branch structure

    # Clone or initialize your test repository
    git clone <your-test-repo>
    cd test-automation-project
    
    # Create a feature branch following naming convention
    git checkout -b test/user-login-validation
    
  2. Set up pre-commit hooks

    Create .git/hooks/pre-commit:

    #!/bin/bash
    # Fetch latest changes before committing
    git fetch origin
    
    # Check if remote has new changes
    LOCAL=$(git rev-parse @)
    REMOTE=$(git rev-parse @{u})
    
    if [ $LOCAL != $REMOTE ]; then
        echo "⚠️  Remote has changes. Please pull before committing."
        exit 1
    fi
    
    # Run tests before commit
    npm test
    if [ $? -ne 0 ]; then
        echo "❌ Tests failed. Fix tests before committing."
        exit 1
    fi
    

    Make it executable:

    chmod +x .git/hooks/pre-commit
    
  3. Create modular test files

    Create tests/login/loginValidation.spec.js:

    // Each test file focuses on ONE feature to minimize conflicts
    describe('Login Validation Tests', () => {
        test('should validate empty username', async () => {
            // Your test implementation
        });
    
        test('should validate password requirements', async () => {
            // Your test implementation
        });
    });
    
  4. Implement page object pattern with separate files

    Create pages/LoginPage.js:

    class LoginPage {
        constructor(page) {
            this.page = page;
            this.usernameInput = '#username';
            this.passwordInput = '#password';
            this.loginButton = '#login-btn';
        }
    
        async login(username, password) {
            await this.page.fill(this.usernameInput, username);
            await this.page.fill(this.passwordInput, password);
            await this.page.click(this.loginButton);
        }
    }
    
    module.exports = LoginPage;
    

Part 2: Simulate and Resolve a Conflict (15 minutes)

  1. Create a conflict scenario

    # Make changes to your test file
    echo "// New test case" >> tests/login/loginValidation.spec.js
    git add tests/login/loginValidation.spec.js
    git commit -m "Add new login test case"
    
    # Simulate remote changes (in real scenario, teammate pushes)
    git checkout main
    echo "// Different change" >> tests/login/loginValidation.spec.js
    git add tests/login/loginValidation.spec.js
    git commit -m "Update login tests"
    
    # Return to feature branch and try to merge
    git checkout test/user-login-validation
    git merge main
    # Conflict occurs!
    
  2. Resolve the conflict properly

    # View conflict status
    git status
    
    # Open conflicted file and resolve manually
    # Keep both changes in logical order
    
    # Mark as resolved
    git add tests/login/loginValidation.spec.js
    
    # Run tests to verify resolution
    npm test
    
    # Complete the merge
    git commit -m "Merge main into test/user-login-validation - resolved conflicts"
    

Part 3: Create a Recovery Workflow (10 minutes)

  1. Document the recovery process

    Create RECOVERY_WORKFLOW.md:

    # Conflict Recovery Workflow
    
    ## When merge conflicts occur:
    
    1. **Don't Panic**
       - Conflicts are normal in team environments
    
    2. **Assess the Conflict**
       ```bash
       git status
       git diff
    
    1. Communication

      • Contact the author of conflicting changes
      • Discuss which changes to keep
    2. Resolve Systematically

      • Open each conflicted file
      • Look for conflict markers: «««<, =======, »»»>
      • Keep necessary changes from both sides
      • Remove conflict markers
    3. Verify

      • Run full test suite
      • Check test reports
      • Ensure CI/CD pipeline passes
    4. Complete

      git add .
      git commit -m "Resolved merge conflicts"
      git push
      
  2. Create a conflict prevention checklist

    Create TEAM_GUIDELINES.md:

    # Conflict Prevention Checklist
    
    Before starting work:
    - [ ] Pull latest changes from main
    - [ ] Create feature branch with descriptive name
    - [ ] Verify tests pass locally
    
    During development:
    - [ ] Commit frequently with clear messages
    - [ ] Pull from main daily
    - [ ] Keep test files focused and modular
    - [ ] Coordinate with team on shared files
    
    Before merging:
    - [ ] Rebase on latest main
    - [ ] Run complete test suite
    - [ ] Request code review
    - [ ] Check CI/CD status
    

Expected Outcome

After completing this exercise, you should have:

  • ✅ A feature branch with modular test files
  • ✅ Pre-commit hooks preventing common issues
  • ✅ Experience resolving merge conflicts
  • ✅ Documentation for team recovery workflows
  • ✅ Guidelines for conflict prevention
  • ✅ All tests passing after conflict resolution

Success Criteria

  • All test files are properly isolated by feature
  • Pre-commit hooks successfully prevent commits with failing tests
  • Merge conflicts resolved without losing any functionality
  • Documentation is clear and actionable for team members
  • CI/CD pipeline passes after merging

Key Takeaways

🎓 What You’ve Learned:

  • Proactive conflict prevention is better than reactive resolution - Using proper branching strategies, modular test design, and regular synchronization significantly reduces merge conflicts before they occur.

  • Modular test architecture minimizes collision points - Separating tests by feature and using page object patterns means team members rarely edit the same files simultaneously, reducing conflict frequency by up to 80%.

  • Automated checks catch issues early - Pre-commit hooks and CI/CD pipelines prevent broken code from entering the main branch, protecting team productivity and maintaining test suite reliability.

  • Clear recovery workflows reduce downtime - Having documented, step-by-step conflict resolution processes ensures any team member can handle conflicts quickly, reducing average resolution time from hours to minutes.

  • Communication beats automation for complex conflicts - While tools help prevent conflicts, direct team communication about overlapping work remains the most effective strategy for coordination and knowledge sharing.


Next Steps

What to Practice

  1. Daily Integration Discipline

    • Pull from main branch at the start of each day
    • Practice rebasing your feature branches regularly
    • Make smaller, more frequent commits
  2. Advanced Git Operations

    • Master git rebase -i for cleaning up commit history
    • Practice git stash for temporary work storage
    • Learn git cherry-pick for selective change application
  3. Team Coordination

    • Set up pair programming sessions for complex test files
    • Implement code review checklists specific to test automation
    • Create a team calendar for major test suite refactoring
  • CI/CD Pipeline Optimization - Learn to configure parallel test execution and failure notifications
  • Test Data Management - Explore strategies for isolating test data to prevent runtime conflicts
  • Monorepo vs Multi-repo Strategies - Understand how repository structure affects conflict frequency
  • Advanced Branching Models - Study GitFlow, trunk-based development, and release branching
  • Automated Conflict Detection - Implement tools that predict conflicts before they happen
  • Practice with a team simulation repository
  • Set up GitHub Actions or Jenkins for automated conflict detection
  • Explore tools like merge-conflict-parser and git-mediate for advanced conflict resolution