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