gitでrevertしたら再revertが必要な例とその理由

git revertで注意すべきこと - Qiita

こちらの記事を読んで、「何が起きるか」はわかったけど「なぜこうなるか」がすぐに分からなかったのでメモ。

具体例

以下の例を考える。

  • masterブランチからdevブランチを切った。
  • devブランチでb.txtを追加してコミットを積んだ。
  • masterブランチでc.txtを追加してコミットを積んだ。
  • masterブランチにdevブランチをマージした。
  • devブランチでの変更点に不具合があったことが判明し、masterでdevのマージコミットをrevertした。
* 6589fe9 (HEAD -> master) Revert "Merge branch 'dev'"
*   c7e7528 Merge branch 'dev'
|\
| * 30f185a (dev) added b.txt
* | 5169671 added c.txt
|/
* 18f28e9 added a.txt

問題が発生するパターン

  • devブランチにパッチを当てて(d.txtを追加して)コミットを積んだ
  • devブランチを再度masterにマージした
*   436c043 (HEAD -> master) Merge branch 'dev'
|\
| * ddf8924 (dev) added d.txt  # commit 1
* | 6589fe9 Revert "Merge branch 'dev'"  # commit 2
* |   c7e7528 Merge branch 'dev'
|\ \
| |/
| * 30f185a added b.txt  # commit 3
* | 5169671 added c.txt
|/
* 18f28e9 added a.txt

ここでマージ後のmasterがどうなっているかというと…

% git checkout master
% ls
a.txt   c.txt   d.txt

当初devブランチで追加したb.txtが見当たらない。

問題が発生する理由

  • 上記のcommit 1とcommit 2をマージするときに、マージベースはcommit 3となる。
  • commit 3からcommit 1にかけての差分は「d.txtを追加した」こと
  • commit 3からcommit 2にかけての差分は「なし」(b.txtが追加され、削除された)
  • commit 3ではa.txt, b.txtが存在したので、それに上記2つの差分を足して実現するのは、a.txt, b.txt, d.txtが存在する状態

問題を回避するやり方

いずれにせよrevert以前の内容が必要なら、revertをrevertすべし。

devブランチをそのまま伸ばす
  • devブランチにmasterをマージする
  • devブランチでrevertをrevertする
  • devブランチにパッチを当てて(d.txtを追加して)コミットを積む
  • devブランチを再度masterにマージする
*   3a746ec (HEAD -> master) Merge branch 'dev'
|\
| * e3cf889 (dev) added d.txt
| * 498e552 Revert "Revert "Merge branch 'dev'""
| *   c440eb6 Merge branch 'master' into dev
| |\
| |/
|/|
* | 6589fe9 Revert "Merge branch 'dev'"
* |   c7e7528 Merge branch 'dev'
|\ \
| |/
| * 30f185a added b.txt
* | 5169671 added c.txt
|/
* 18f28e9 added a.txt

こうすれば、当初devブランチで作成したb.txtも最終的にmasterブランチに残る。

% git checkout master
% ls
a.txt   b.txt   c.txt   d.txt
ブランチを切り直す
  • masterブランチからdev2ブランチを切り直す
  • dev2ブランチでrevertをrevertする
  • dev2ブランチにパッチを当てて(d.txtを追加して)コミットを積む
  • dev2ブランチをmasterにマージする
*   ea16e7a (HEAD -> master) Merge branch 'dev2'
|\
| * a87d323 (dev2) added d.txt
| * 98c5d6f Revert "Revert "Merge branch 'dev'""
|/
* 6589fe9 Revert "Merge branch 'dev'"
*   c7e7528 Merge branch 'dev'
|\
| * 30f185a (dev) added b.txt
* | 5169671 added c.txt
|/
* 18f28e9 added a.txt

この方法でも、当初devブランチで作成したb.txtが最終的にmasterブランチに残る。

% git checkout master
% ls
a.txt   b.txt   c.txt   d.txt