简体   繁体   中英

Revert a git commit not in current branch

I have 2 branches: master and feature.

• master
• feature

I make two commits on feature branch

• master
• C1 • C2 • feature

Now, I want to merge feature in master. I use git merge --squash feature to merge.

• C3 • master     // C3 is the squashed commit
• C1 • C2 • feature

At this moment, is there a way to revert C1 from master?

One option is to revert C1 on feature, and squash merge feature again in master.

Yes, you can revert some commit even though it was included in the aggregate, using merge --squash . git-revert works by identifying the changes that were introduced in the commit that you identified, and then creating a new change that undoes ("reverts") those changes.

It uses the three-way merge algorithm to do this - using the commit to revert as the base, and then comparing against that commit's ancestor and the current commit ( HEAD ). This will isolate the changes that were introduced only in that commit.

To look at a very contrived example, imagine that you had some file that had three changes ( C0 , C1 and C2 ) and these are the contents of that file at each version:

| C0    | C1    | C2    |
|-------|-------|-------|
| one   | one   | one   |
| two   | 2     | 2     |
| three | three | three |
| four  | four  | FOUR  |

Now, if you want to revert C1 , we set up a three-way merge with it as the base, and C0 and C2 as each side to take changes from.

In a three-way merge algorithm, you look at each side, compared to the base. If all three lines are equal , you take that line into the result unmodified. If one side has made a change, you take the changed line into the result. (If both sides have made a change on the same line, you mark that line as a conflict.)

Setting up a revert gives you:

base     sides     result
----     -----     ------
         one
       / two   \
      /  three  \
one  /   four    \ one
2                  two
three              three
four \   one     / FOUR
      \  2      /
       \ three /
         FOUR

You can see that the result (on the right) that has undone the changes that were introduced in C1 , because one of the sides ( C0 , in this case) was unique, so its changes were kept in the result. This has the logical effect of undoing ("reverting") the changes introduced therein.

This would be true even if you had done a squash merge - in this case, it's looking at the repository's contents. It doesn't matter that CM doesn't actually have C1 as a common ancestor.

You can prove this to yourself in a simple repository with these contents. Even after a squash merge:

commit 9e7497c7ae34aa35cdb7d7b965a00d56bf0b9dfa
Author: Edward Thomson <ethomson@edwardthomson.com>
Date:   Thu Nov 9 10:20:31 2017 +0000

    Squashed commit of the following:

    commit 8a8a9e73e62e21683e15269d89e1fbfbbf35cfa1
    Author: Edward Thomson <ethomson@edwardthomson.com>
    Date:   Thu Nov 9 10:20:18 2017 +0000

        C2

    commit d984b27140e48c5faa8968364c415d29dcd7034c
    Author: Edward Thomson <ethomson@edwardthomson.com>
    Date:   Thu Nov 9 10:20:08 2017 +0000

        C1

You can still revert one of the components correctly. In this case, I'll revert C1 :

> git revert d984b27
[master 405f108] Revert "C1"
 1 file changed, 1 insertion(+), 1 deletion(-)

> git show HEAD
commit 405f1080e24504fa418d423a0755a2123b85ecd8 (HEAD -> master)
Author: Edward Thomson <ethomson@edwardthomson.com>
Date:   Thu Nov 9 10:20:42 2017 +0000

    Revert "C1"

    This reverts commit d984b27140e48c5faa8968364c415d29dcd7034c.

diff --git a/hello.txt b/hello.txt
index 9d980ae..14cf0bc 100644
--- a/hello.txt
+++ b/hello.txt
@@ -1,5 +1,5 @@
 Hello, world!
 one
-2
+two
 three
 four

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM