akiyoko blog

akiyoko の IT技術系ブログです

Git で コミットを無かったことにする方法 (git revert の使い方)

Git でコミット(とかマージ)を無かったことにする方法といえば、「git revert」なのですが、使い方にちょっとひとクセ(?)あったのでメモっておきます。


f:id:akiyoko:20170105231620p:plain:w350

「git revert」は正確には、commit を無かったことにするのではなく、対象の commit の変更を相殺するような差分commitを自動で生成するというものです。


コミットを文字通り無かったことにする(コミットログから特定のコミットだけ抜き出す=履歴が残らないようにする)には、「git reset --hard」「git rebase」「git cherry-pick」「git push -f」などを駆使すればできるのですが、いくつかコミットやマージが重ねられた後にそれをするのは、あまり現実的とは言えませんよね。


そこで、「git revert」の登場です。


 

実践

例えば、以下のような commit があったとします。

$ git diff 9c294b4^ 9c294b4
diff --git a/tomcat-standalone/roles/tomcat/tasks/main.yml b/tomcat-standalone/roles/tomcat/tasks/main.yml
index 7dd329c..784ce8b 100644
--- a/tomcat-standalone/roles/tomcat/tasks/main.yml
+++ b/tomcat-standalone/roles/tomcat/tasks/main.yml
@@ -37,7 +37,7 @@
   copy: src=tomcat-initscript.sh dest=/etc/init.d/tomcat mode=0755

 - name: Start Tomcat
-  service: name=tomcat state=restarted enabled=yes
+  service: name=tomcat state=started enabled=yes

 - name: deploy iptables rules
   template: src=iptables-save dest=/etc/sysconfig/iptables

https://github.com/akiyoko/ansible-examples/commit/9c294b4dd7cff70f8cf9cc05a488b7ffec9f68e5


コミット履歴をワンラインでグラフ化すると、こういう状況です。
(抜粋。「9c294b4」でコミットされ、「090a520」でマージされています。)

$ git log --graph --oneline
    ・
    ・
*   090a520 Merge pull request #59 from rasputnik/patch-1
|\
| * 9c294b4 don't bounce tomcat unless needed
|/
*   497e49c Merge pull request #57 from KyleJamesWalker/master



このコミットを打ち消すには、以下のようにします。

$ git revert 9c294b4

もちろん、commit id をフルで指定しても同じです。

$ git revert 9c294b4dd7cff70f8cf9cc05a488b7ffec9f68e5


git revert すると、このようなコミットが作成されます。(なお、コミットメッセージを修正することもできます。)

commit 9f872f462326e5b146d97c94064dcb14c2ed8299
Author: akiyoko <akiyoko@example.com>
Date:   Fri Aug 22 00:19:13 2014 +0900

    Revert "don't bounce tomcat unless needed"

    This reverts commit 9c294b4dd7cff70f8cf9cc05a488b7ffec9f68e5.




ここまでは簡単に理解できますよね。




プルリク(Pull Request)開発においては、コミット → コミット → ・・ → プルリクエスト → マージ という形で進められるのですが、このマージごと打ち消したいということは少なからずあると思います。


そこで、

$ git revert 090a520

とすると、以下のようなエラーになります。

error: Commit 090a5203ceb98685c90610b2f378ed6118095e23 is a merge but no -m option was given.
fatal: revert failed

マージ(Merge Commit)への revert は、単に「git revert」するだけではダメなようです。



マージを無かったことにする場合は、「-m 1」を付けるのが正解です。

git revert -m 1 090a520


この「-m」オプションには、親番号(parent-number)を指定するのですが、「090a520」の Merge Commit を見てみると、

commit 090a5203ceb98685c90610b2f378ed6118095e23
Merge: 497e49c 9c294b4
Author: Tim Gerla <tim@gerla.net>
Date:   Mon Mar 10 06:58:06 2014 -0700

    Merge pull request #59 from rasputnik/patch-1

    don't bounce tomcat unless needed

のようになっていて、この「Merge: 497e49c 9c294b4」は「このコミットには(本線とブランチという)親が2つありますよ」ということを示しています。


グラフをもう一度じっくり見ていただければ分かると思うのですが、

*   090a520 Merge pull request #59 from rasputnik/patch-1
|\
| * 9c294b4 don't bounce tomcat unless needed
|/
*   497e49c Merge pull request #57 from KyleJamesWalker/master

本線が「497e49c」、派生してマージされたブランチが「9c294b4」ということが見て取れます。


「-m」オプションは「本線はどっち?」というのを指定するので、ここでは「1」を指定すればよいということになるのです(ちなみに、左側から「1」「2」・・という順になります)。


あまり深く考えなくても、プルリク開発においては、ほとんどの場合は「-m 1」と指定すれば問題なかろうと思います(ホントかな??)。




あと、「git revert」には「--no-commit」という便利なオプションもあります。
ご想像通り、コミットせずに相殺する差分だけを生成してくれます。






 

まとめ

プルリク開発で Commit Merge を「無かったこと」にするには、「git reset」するしかなさそうです。その場合、対象の commit を相殺するような差分commit が生成されます(コミットログからは消せない)。


そもそも Git のポリシーとして、Commit Merge しちゃったものは「無かったことに」できないということなのですね、きっと。



関連本

オススメ Git 本です。