How do I revert a merge commit that has already been pushed to remote?

Learn how do i revert a merge commit that has already been pushed to remote? with practical examples, diagrams, and best practices. Covers git development techniques with visual explanations.

Reverting a Pushed Merge Commit in Git

Hero image for How do I revert a merge commit that has already been pushed to remote?

Learn how to safely undo a merge commit that has already been pushed to a remote repository, understanding the implications and best practices.

Reverting a merge commit in Git can be a tricky operation, especially when it has already been pushed to a remote repository and potentially shared with other collaborators. Unlike regular commits, a merge commit has two or more parent commits, which complicates a simple git revert operation. This article will guide you through the process of safely reverting a pushed merge commit, explaining the different scenarios and the commands required.

Understanding Merge Commits and Reverts

A merge commit is a special type of commit that combines the history of two or more branches. When you revert a regular commit, Git creates a new commit that undoes the changes introduced by the specified commit. For a merge commit, Git needs to know which parent's history to 'undo' relative to. This is where the -m or --mainline option comes into play with git revert.

flowchart TD
    A[Feature Branch] --> M(Merge Commit)
    B[Main Branch] --> M
    M --> R[Revert Commit]
    subgraph Original History
        A
        B
        M
    end
    subgraph Reverted History
        M --> R
    end
    style Original History fill:#f9f,stroke:#333,stroke-width:2px
    style Reverted History fill:#ccf,stroke:#333,stroke-width:2px

Conceptual flow of a merge commit and its revert.

Identifying the Mainline Parent

When reverting a merge commit, you must specify which parent is the 'mainline' or the branch you want to keep the changes from. The other parent's changes will be undone. Git numbers the parents starting from 1. Typically, parent 1 is the branch you were on when you performed the merge (e.g., main or develop), and parent 2 is the branch being merged in (e.g., a feature branch).

You can inspect the merge commit to determine its parents using git show <merge-commit-hash>.

git show <merge-commit-hash>

Viewing details of a merge commit to identify parents.

Look for lines starting with Merge: to see the parent hashes. The first hash listed is parent 1, the second is parent 2, and so on.

Performing the Revert

Once you've identified the merge commit hash and the mainline parent, you can perform the revert. The command will look like this:

git revert -m <parent-number> <merge-commit-hash>

For example, if you want to undo the changes from the merged feature branch (parent 2) and keep the main branch's history (parent 1), you would use -m 1.

# Example: Revert merge commit 'abcdef1' keeping parent 1's changes
git revert -m 1 abcdef1

Command to revert a merge commit, specifying the mainline parent.

Pushing the Revert to Remote

After successfully creating the revert commit locally, you need to push it to the remote repository. Since this is a new commit that undoes previous changes, it's a standard push operation.

git push origin <your-branch-name>

Pushing the revert commit to the remote repository.

What if the Reverted Branch is Merged Again?

A common pitfall with git revert of merge commits is when the same feature branch is merged again after being reverted. Git sees that the changes from the feature branch were already applied and then reverted, so it might not re-apply them correctly in a subsequent merge, leading to missing code.

To avoid this, if you need to re-introduce the changes from the feature branch, you should either:

  1. Revert the revert commit (creating a new commit that re-applies the original merge's changes).
  2. Rebase the feature branch onto the current main (or develop) and then merge the rebased feature branch.
sequenceDiagram
    participant DevA as Developer A
    participant DevB as Developer B
    participant Remote as Remote Repository

    DevA->>Remote: Push Feature Branch
    DevB->>Remote: Merge Feature into Main
    Remote-->>DevB: Main updated
    DevB->>Remote: Push Main (with merge)

    Note over DevA,Remote: Realizes merge was incorrect

    DevA->>DevA: git pull origin main
    DevA->>DevA: git revert -m 1 <merge-commit-hash>
    DevA->>Remote: git push origin main

    Note over DevA,Remote: Main branch now without feature changes

    DevB->>DevB: git pull origin main
    DevB->>DevB: (Sees revert commit)
    DevB->>DevB: git checkout feature-branch
    DevB->>DevB: git rebase main
    DevB->>Remote: Push rebased feature-branch
    DevB->>Remote: Merge rebased feature-branch into Main
    Remote-->>DevB: Main updated
    DevB->>Remote: Push Main (with new merge)

Sequence of reverting a merge and then re-merging the feature branch safely.

Summary of Revert Options

Here's a quick recap of the options for reverting a merge commit:

1. Identify the Merge Commit

Use git log or git reflog to find the hash of the merge commit you want to revert.

2. Inspect the Merge Commit

Run git show <merge-commit-hash> to determine the parent commits. Parent 1 is usually the branch you merged into, and Parent 2 is the branch you merged from.

3. Perform the Revert

Execute git revert -m <parent-number> <merge-commit-hash>. Choose 1 if you want to undo the changes from the merged branch, or 2 if you want to undo the changes from the branch you were on (less common).

4. Push the Revert

After the revert commit is created locally, push it to the remote repository using git push origin <your-branch-name>.

5. Communicate

Inform your team about the revert to avoid confusion and potential conflicts.