This article started on Github, welcome issue/FXXK.

preface

This article combines a standard Git workflow with examples to illustrate what conditions trigger git Revert failures. Through this article, you learn

  • Strengthen your use and understanding of Git;
  • When a problem arises, you can freely revert it in the right manner.

To prepare

In order to better describe this article, we will do some preparatory work from scratch.

First, create a REPO:

mkdir git-revert-merge-commit
cd git-revert-merge-commit
git init
Copy the code

First create two commit records to simulate existing commit records on the master:

echo 'file1' > file1 
git add . && git commit -m 'commit 1'
echo 'file2' > file2
git add . && git commit -m 'commit 2'
Copy the code

As the project continues to evolve, we will continue to make new features. Suppose that now we want to make a new feature, in order not to block the master CI pipeline, all features should be developed based on the new branch. So we create a branch and switch to that branch:

git branch feature
git checkout feature Git checkout -b feature
Copy the code

Create two commits to simulate this feature:

echo 'file3' > file3
git add . && git commit -m 'feature - commit 1'
echo 'file4' > file4
git add . && git commit -m 'feature - commit 2'
Copy the code

During the feature development phase, there are usually new commits from other people on the master, so let’s go back to the master and simulate these commits:

git checkout master
echo 'file5' > file5
git add . && git commit -m 'commit 3'
echo 'file6' > file6
git add . && git commit -m 'commit 4' 
Copy the code

At this point, the QA team has finished testing your feature and given you a thumbup tag.

git ck -
git tag thumbup-feature
Copy the code

Then, you can use this tag merge:

git checkout master
git pull origin master # pull remote master to merge with local master
git merge thumbup-feature --no-ff # --no-ff is used to avoid fast-forward
Copy the code

Git log from Webstorm

Later on, there are some more commits on the master:

echo 'file7' > file7
git add . && git commit -m 'commit 5'
Copy the code

The UT in the production environment has failed due to your merge, so you can’t quick fix it or block release it, so you have to revert your change. Revert.

Git log

Find the MERGE COMMIT ID to revert:

git revert cae5381
Copy the code

Haha, here comes the question:

Here we go, nice.

Of course, since the most powerful nature of a programmer is laziness, I have prepared a one-click script for those who want to get their hands dirty:

Git merge Commit ID git Revert init git merge Commit ID git revert

echo 'file1' > file1; \
git add . && git commit -m 'commit 1'; \
echo 'file2' > file2; \
git add . && git commit -m 'commit 2'; \
git branch feature; \
git checkout feature; \
echo 'file3' > file3; \
git add . && git commit -m 'feature - commit 1'; \
echo 'file4' > file4; \
git add . && git commit -m 'feature - commit 2'; \
git checkout master; \
echo 'file5' > file5; \
git add . && git commit -m 'commit 3'; \
echo 'file6' > file6; \
git add . && git commit -m 'commit 4'; \ 
git ck -; \
git tag thumbup-feature; \
git checkout master; \
git pull origin master; \
git merge thumbup-feature --no-ff; \
echo 'file7' > file7; \
git add . && git commit -m 'commit 5';
Copy the code

Analysis of the

Error message given again:

error: commit cae5381823aad7c285d017e5cf7e8bc4b7b12240 is a merge but no -m option was given.

Let’s take a look at what -m actually refers to. Quoting the official document, we can see:

Usually you cannot revert a merge because you do not know which side of the merge should be considered the mainline. This option specifies the parent number (starting from 1) of the mainline and allows revert to reverse the change relative to the specified parent.

Reverting a merge commit declares that you will never want the tree changes brought in by the merge. As a result, later merges will only bring in tree changes introduced by commits that are not ancestors of the previously reverted merge. This may or may not be what you want.

Translation:

In general, you cannot revert a merge because you do not know which line of the merge should be considered the main line. This option (-m) specifies the parent code of the main line (starting at 1) and allows revert relative to the specified parent.

To revert a Merge Commit means that you do not want the tree change from the Merge Commit at all. Therefore, subsequent merges will only introduce tree changes that are not introduced by the ancestors of the previous merged, which may or may not be what you want.

A merge commit is a merge of two lines into one line, so the merge commit will have two ancestors. So git doesn’t know which parent base diff is selected for, so it complains, so you explicitly tell Git which parent to use with the -m attribute.

So how do you see how many ancestors the current COMMIT has?

  1. git show cae5381
commit cae5381823aad7c285d017e5cf7e8bc4b7b12240
Merge: edf99ca 125cfdd
Author: ULIVZ <[email protected]>
Date:   Thu Apr 12 18:27:21 2018 +0800

    Merge tag 'thumbup-feature'
Copy the code

Merge indicates the current parent, edF99CA and 125CFDD, respectively

  1. You can also see this on Github by clicking Commit:

Git log diagram: Git log diagram

Is that intuitive? Parent1 and parent2 is the last commit for branch. If you merge A into B on the branch B, then B is the parent1 of the merge commit, and A is parent2.

To solve

With the analysis in the previous section, we can give the following code that works quite directly:

git revert cae5381 -m 1
Copy the code

Output the following log:

Revert "Merge tag 'thumbup-feature'"

This reverts commit cae5381823aad7c285d017e5cf7e8bc4b7b12240, reversing
changes made to edf99ca31755a27b0a43b290263ed810833a95c4.
Copy the code

:wq exit

[master f0aac26] Revert "Merge tag 'thumbup-feature'"
 2 files changed, 2 deletions(-)
 delete mode 100644 file3
 delete mode 100644 file4
Copy the code

File3 and file4 are files imported from the feature Branch commit and deleted correctly, cool.

Git revert CAe5381-m 2 Maybe you already have the answer, but let’s give it a try:

First, reset hard is the last commit before you revert, so you can try another kind of Revert. In this case, you can use the cuter Git reflog.

git reset --hard d208cba 
git revert cae5381 -m 2
Copy the code
[master 2c5a0ee] Revert "Merge tag 'thumbup-feature'"
 2 files changed, 2 deletions(-)
 delete mode 100644 file5  
 delete mode 100644 file6
Copy the code

Sure enough, this sort of revert kills all commit requests made by the master during feature Branch. This is normal because the diff is based on feature Branch.

At this point, does it make sense to you? Do you know that you have the feeling to revert to this kind of problem again?

The code for this article has been placed in this repositorygit-revert-merge-commit

conclusion

The conclusions are as follows:

  1. For single-parent commit, use it directlygit revert commit_id;
  2. For a COMMIT with more than one parent, you need to combine-mProperties:git revert commit_id -m parent_id;
  3. For merge commit from branch to master, the parent_id of master is 1, the parent_id of branch is 2, and vice versa.

Finally, add my own lovely git log configuration (copy to [alias] in ~/.gitConfig) :

lo = log --color --pretty=format:' %C(white bold dim) %C(cyan bold dim)[%h] %Creset %Cgreen%cn %C(white bold dim)| %C(yellow dim)%cr %n%Creset %C(white dim italic)%s%n%n%Creset'
Copy the code

In other words, a year ago I had a library for how to enhance git workflow with shell, git-shell. Currently, there is no documentation. If you are interested, you can take a look at the idea.

That’s all for this article. Thank you for reading.