Files
git-workshop/01_essentials/05-revert

Module 05: Git Revert - Safe Undoing

About This Module

Welcome to Module 05, where you'll learn the safe, team-friendly way to undo changes in Git. Unlike destructive commands that erase history, git revert creates new commits that undo previous changes while preserving the complete project history.

Why revert is important:

  • Safe for shared/pushed commits
  • Preserves complete history and audit trail
  • Transparent to your team
  • Can be undone itself if needed
  • Works with any commit in history

Key principle: Revert doesn't erase mistakes—it documents how you fixed them.

Learning Objectives

By completing this module, you will:

  1. Revert regular commits safely while preserving surrounding changes
  2. Revert merge commits using the -m flag
  3. Understand merge commit parent numbering
  4. Handle the re-merge problem that occurs after reverting merges
  5. Revert multiple commits at once
  6. Know when to use revert vs. other undo strategies

Prerequisites

Before starting this module, you should be comfortable with:

  • Creating commits (git commit)
  • Viewing commit history (git log)
  • Understanding branches and merging (Module 03)

Setup

Run the setup script to create the challenge environment:

./setup.ps1

This creates a challenge/ directory with three branches demonstrating different revert scenarios:

  • regular-revert - Basic commit reversion
  • merge-revert - Merge commit reversion
  • multi-revert - Multiple commit reversion

Challenge 1: Reverting a Regular Commit

Scenario

You're working on a calculator application. A developer added a divide function that crashes when dividing by zero. The bug was discovered after subsequent commits were made, so you can't just delete it—you need to revert it while keeping the commits that came after.

Your Task

  1. Navigate to the challenge directory:

    cd challenge
    
  2. You should be on the regular-revert branch. View the commit history:

    git log --oneline
    
  3. Find the commit with the broken divide function (message: "Add broken divide function - needs to be reverted!")

  4. Revert that specific commit:

    git revert <commit-hash>
    
  5. Git will open your editor for the revert commit message. The default message is fine—save and close.

What to Observe

After reverting, check:

# View the new revert commit
git log --oneline

# Check that divide function is gone
cat calculator.py | grep "def divide"  # Should return nothing

# Check that modulo function still exists (it came after the bad commit)
cat calculator.py | grep "def modulo"  # Should find it

# Check that multiply function still exists (it came before the bad commit)
cat calculator.py | grep "def multiply"  # Should find it

Key insight: Revert creates a new commit that undoes the changes from the target commit, but leaves all other commits intact.

Understanding the Timeline

Before revert:
main.py (initial) → multiply (good) → divide (BAD) → modulo (good)
                                        ↑
                                    We want to undo THIS

After revert:
main.py (initial) → multiply (good) → divide (BAD) → modulo (good) → revert divide (new commit)
                                                                        ↑
                                                                    Removes divide, keeps modulo

The revert commit adds a new point in history that undoes the divide changes.

Challenge 2: Reverting a Merge Commit

Scenario

Your team merged a feature-auth branch that added authentication functionality. After deployment, you discovered the authentication system has critical security issues. You need to revert the entire merge while the security team redesigns the feature.

This is different from reverting a regular commit! Merge commits have two parents, so you must tell Git which parent to keep.

Understanding Merge Commit Parents

When you merge a feature branch into main:

    feature-auth (parent 2)
         ↓
    C---D
   /     \
A---B-----M  ← Merge commit (has TWO parents)
    ↑
parent 1 (main)

The merge commit M has:

  • Parent 1: The branch you merged INTO (main)
  • Parent 2: The branch you merged FROM (feature-auth)

When reverting a merge, you must specify which parent to keep using the -m flag:

  • -m 1 means "keep parent 1" (main) - Most common
  • -m 2 means "keep parent 2" (feature-auth) - Rare

In practice: You almost always use -m 1 to keep the main branch and undo the feature branch changes.

Your Task

  1. Switch to the merge-revert branch:

    git switch merge-revert
    
  2. View the commit history and find the merge commit:

    git log --oneline --graph
    

    Look for: "Merge feature-auth branch"

  3. Revert the merge commit using -m 1:

    git revert -m 1 <merge-commit-hash>
    

    Explanation:

    • -m 1 tells Git to keep parent 1 (main branch)
    • This undoes all changes from the feature-auth branch
    • Creates a new "revert merge" commit
  4. Save the default commit message and check the result:

    # Verify auth.py is gone
    ls auth.py  # Should not exist
    
    # Verify calculator.py no longer imports auth
    cat calculator.py | grep "from auth"  # Should return nothing
    

What Happens Without -m?

If you try to revert a merge commit without the -m flag:

git revert <merge-commit-hash>
# Error: commit <hash> is a merge but no -m option was given

Git doesn't know which parent you want to keep, so it refuses to proceed.

The Re-Merge Problem

Important gotcha: After reverting a merge, you cannot simply re-merge the same branch!

Here's why:

Initial merge:
A---B---M (merged feature-auth)
        ↑
    All changes from feature-auth are now in main

After revert:
A---B---M---R (reverted merge)
            ↑
        Changes removed, but Git remembers they were merged

Attempting to re-merge:
A---B---M---R---M2 (try to merge feature-auth again)
                 ↑
             Git thinks: "I already merged these commits,
             nothing new to add!" (Empty merge)

Solutions if you need to re-merge:

  1. Revert the revert (recommended):

    git revert <revert-commit-hash>
    

    This brings back all the feature-auth changes.

  2. Cherry-pick new commits from the feature branch:

    git cherry-pick <new-commits>
    
  3. Merge with --no-ff and resolve conflicts manually (advanced).

When to Revert Merges

Revert merge commits when:

  • Feature causes production issues
  • Need to temporarily remove a feature
  • Discovered critical bugs after merging
  • Security issues require immediate rollback

Don't revert merges when:

  • You just need to fix a small bug (fix it with a new commit instead)
  • You plan to re-merge the same branch soon (use reset if local, or revert-the-revert later)

Challenge 3: Reverting Multiple Commits

Scenario

Two separate commits added broken mathematical functions (square_root and logarithm). Both have critical bugs and need to be removed. You can revert multiple commits at once.

Your Task

  1. Switch to the multi-revert branch:

    git switch multi-revert
    
  2. View the commit history:

    git log --oneline
    

    Find the two commits:

    • "Add broken square_root - REVERT THIS!"
    • "Add broken logarithm - REVERT THIS TOO!"
  3. Revert both commits in one command:

    git revert <commit-hash-1> <commit-hash-2>
    

    Important: List commits from oldest to newest for cleanest history.

    Alternatively, revert them one at a time:

    git revert <commit-hash-1>
    git revert <commit-hash-2>
    
  4. Git will prompt for a commit message for each revert. Accept the defaults.

  5. Verify the result:

    # Check that both bad functions are gone
    cat calculator.py | grep "def square_root"  # Should return nothing
    cat calculator.py | grep "def logarithm"    # Should return nothing
    
    # Check that good functions remain
    cat calculator.py | grep "def power"        # Should find it
    cat calculator.py | grep "def absolute"     # Should find it
    

Multi-Revert Strategies

Reverting a range of commits:

# Revert commits from A to B (inclusive)
git revert A^..B

# Example: Revert last 3 commits
git revert HEAD~3..HEAD

Reverting without auto-commit:

# Stage revert changes without committing
git revert --no-commit <commit-hash>

# Review changes
git diff --staged

# Commit when ready
git commit

This is useful when reverting multiple commits and you want one combined revert commit.

Verification

Verify your solutions by running the verification script:

cd ..  # Return to module directory
./verify.ps1

The script checks that:

  • Revert commits were created (not destructive deletion)
  • Bad code is removed
  • Good code before and after is preserved
  • Merge commits still exist in history
  • Proper use of -m flag for merge reverts

Command Reference

Basic Revert

# Revert a specific commit
git revert <commit-hash>

# Revert the most recent commit
git revert HEAD

# Revert the second-to-last commit
git revert HEAD~1

Merge Commit Revert

# Revert a merge commit (keep parent 1)
git revert -m 1 <merge-commit-hash>

# Revert a merge commit (keep parent 2) - rare
git revert -m 2 <merge-commit-hash>

Multiple Commits

# Revert multiple specific commits
git revert <hash1> <hash2> <hash3>

# Revert a range of commits (oldest^..newest)
git revert <oldest-hash>^..<newest-hash>

# Revert last 3 commits
git revert HEAD~3..HEAD

Revert Options

# Revert but don't commit automatically
git revert --no-commit <commit-hash>

# Revert and edit the commit message
git revert --edit <commit-hash>

# Revert without opening editor (use default message)
git revert --no-edit <commit-hash>

# Abort a revert in progress (if conflicts)
git revert --abort

# Continue revert after resolving conflicts
git revert --continue

When to Use Git Revert

Use git revert when:

  • Commits are already pushed - Safe for shared history
  • Working in a team - Transparent to everyone
  • Need audit trail - Shows what was undone and why
  • Public repositories - Can't rewrite public history
  • Undoing old commits - Can revert commits from weeks ago
  • Production hotfixes - Safe emergency rollback

Golden Rule: If others might have your commits, use revert.

When NOT to Use Git Revert

Consider alternatives when:

  • Commits are still local - Use git reset instead (Module 06)
  • Just want to edit a commit - Use git commit --amend
  • Haven't pushed yet - Reset is cleaner for local cleanup
  • Need to combine commits - Use interactive rebase
  • Reverting creates complex conflicts - Might need manual fix forward

Revert vs. Reset vs. Rebase

Command History Safety Use Case
revert Preserves Safe Undo pushed commits
reset Erases ⚠️ Dangerous Clean up local commits
rebase Rewrites ⚠️ Dangerous Polish commit history

This module teaches revert. You'll learn reset in Module 06.

Handling Revert Conflicts

Sometimes reverting causes conflicts if subsequent changes touched the same code:

# Start revert
git revert <commit-hash>

# If conflicts occur:
# Conflict in calculator.py
# CONFLICT (content): Merge conflict in calculator.py

To resolve:

  1. Open conflicted files and fix conflicts (look for <<<<<<< markers)
  2. Stage resolved files:
    git add <resolved-files>
    
  3. Continue the revert:
    git revert --continue
    

Or abort if you change your mind:

git revert --abort

Common Mistakes

1. Forgetting -m for Merge Commits

# ❌ Wrong - will fail
git revert <merge-commit>

# ✅ Correct
git revert -m 1 <merge-commit>

2. Trying to Re-Merge After Revert

# After reverting a merge:
git revert -m 1 <merge-commit>

# ❌ This won't work as expected
git merge feature-branch  # Empty merge!

# ✅ Do this instead
git revert <the-revert-commit>  # Revert the revert

3. Using Reset on Pushed Commits

# ❌ NEVER do this with pushed commits
git reset --hard HEAD~3

# ✅ Do this instead
git revert HEAD~3..HEAD

4. Reverting Commits in Wrong Order

When reverting multiple related commits, revert from newest to oldest:

# If you have: A → B → C (and C depends on B)

# ✅ Correct order
git revert C
git revert B

# ❌ Wrong order (may cause conflicts)
git revert B  # Conflict! C still references B
git revert C

Best Practices

  1. Write clear revert messages:

    git revert <hash> -m "Revert authentication - security issue #1234"
    
  2. Link to issue tracking:

    Revert "Add new payment system"
    
    This reverts commit abc123.
    
    Critical bug in payment processing.
    See bug tracker: ISSUE-1234
    
  3. Test after reverting:

    • Run your test suite
    • Verify the application still works
    • Check no unintended changes occurred
  4. Communicate with team:

    • Announce reverts in team chat
    • Explain why the revert was necessary
    • Provide timeline for re-introducing the feature
  5. Keep reverts focused:

    • Revert the minimum necessary
    • Don't bundle multiple unrelated reverts
    • One problem = one revert commit

Troubleshooting

"Commit is a merge but no -m option was given"

Problem: Trying to revert a merge commit without -m.

Solution:

git revert -m 1 <merge-commit-hash>

"Empty Revert / No Changes"

Problem: Revert doesn't seem to do anything.

Possible causes:

  • Commit was already reverted
  • Subsequent commits already undid the changes
  • Wrong commit hash

Solution:

# Check what the commit actually changed
git show <commit-hash>

# Check if already reverted
git log --grep="Revert"

"Conflicts During Revert"

Problem: Revert causes merge conflicts.

Why: Subsequent commits modified the same code.

Solution:

  1. Manually resolve conflicts in affected files
  2. git add <resolved-files>
  3. git revert --continue

Or consider fixing forward with a new commit instead of reverting.

"Can't Re-Merge After Reverting Merge"

Problem: After reverting a merge, re-merging the branch brings no changes.

Solution: Revert the revert commit:

# Find the revert commit
git log --oneline

# Revert the revert (brings changes back)
git revert <revert-commit-hash>

Advanced: Revert Internals

Understanding what revert does under the hood:

# Revert creates a new commit with inverse changes
git revert <commit-hash>

# This is equivalent to:
git diff <commit-hash>^..<commit-hash> > changes.patch
patch -R < changes.patch  # Apply in reverse
git add .
git commit -m "Revert '<original message>'"

Key insight: Revert computes the diff of the target commit, inverts it, and applies it as a new commit.

Going Further

Now that you understand revert, you're ready for:

  • Module 06: Git Reset - Learn the dangerous but powerful local history rewriting
  • Module 07: Git Stash - Temporarily set aside uncommitted changes
  • Module 08: Multiplayer Git - Collaborate with advanced workflows

Summary

You've learned:

  • git revert creates new commits that undo previous changes
  • Revert is safe for shared/pushed commits
  • Merge commits require -m 1 or -m 2 flag
  • Parent 1 = branch merged into, Parent 2 = branch merged from
  • Can't simply re-merge after reverting a merge
  • Multiple commits can be reverted in one command
  • Revert preserves complete history for audit trails

The Golden Rule of Revert: Use revert for any commit that might be shared with others.

Next Steps

  1. Complete all three challenge scenarios
  2. Run ./verify.ps1 to check your solutions
  3. Experiment with reverting different commits
  4. Move on to Module 06: Git Reset (dangerous but powerful!)

Need Help?

  • Review the command reference above
  • Check the troubleshooting section
  • Re-run ./setup.ps1 to start fresh
  • Practice reverting in different orders to understand the behavior