一度revertで削除したファイルをバグがなくなったため復活させることにした。
開発ブランチでrevertのrevertを実施すると問題なくファイルが復活するが、releaseブランチにマージしてもreleaseブランチではファイルが復活していなかった。
開発ブランチにあるファイルがマージで反映されない事態に違和感を覚えたため調査した。
以下revertのrevertは長いのでreapplyと呼ぶ。
再現コード
簡略化したコードは下記。
echo "hello, world" > file
git add file
git commit -am "add file"
git branch dst
git revert <add fileしたcommit> --no-edit
git switch dst
git cherry-pick <revertしたcommit>
git switch src
# revertのrevert
git revert <revertしたcommit> --no-edit
git switch dst
git merge src
# fileが存在しない
最新のdstのgit log
の結果を見てみると以下の2つの歴史をマージしている
- revertしてreapplyしたブランチ(src)
- revertしたブランチ(dst)
% git log
commit 900dd818dee2762ad842428d75125e158177ad08 (HEAD -> dst)
Merge: 63f39e5 54c0ecd
Author: myoan
Date: Thu Sep 19 08:55:38 2024 +0900
Merge branch 'src' into dst
commit 54c0ecd0ef0c9c0fa0b6a003e740698b53e83961 (src)
Author: myoan
Date: Thu Sep 19 08:54:49 2024 +0900
Reapply "add file"
This reverts commit 0ac88a74c84d2822d1592b42dee42e526ec1250e.
commit 63f39e5ba70fe038863ac887ccb974e8e6428f23
Author: myoan
Date: Thu Sep 19 08:05:23 2024 +0900
Revert "add file"
This reverts commit 7e7eb9b6a126469407af3715433bb775179a4567.
commit 0ac88a74c84d2822d1592b42dee42e526ec1250e
Author: myoan
Date: Thu Sep 19 08:05:23 2024 +0900
Revert "add file"
This reverts commit 7e7eb9b6a126469407af3715433bb775179a4567.
commit 7e7eb9b6a126469407af3715433bb775179a4567
Author: myoan
Date: Thu Sep 19 08:04:36 2024 +0900
add file
3-way mergeで考えるとsrcブランチはrevertとreapplyをしているため実質base commitとの差分がない。
対してdstはrevertしてるためファイルが削除されている。
そのためマージしてもファイルが削除された(=復活していないように見えた)。
今回はrevertやcherry-pickを題材にいているが本件に関係はなく、単純なファイル追加・削除でも再現する。
問題の本質はmergeのタイミングでbaseとHEAD
, baseとMERGE_HEAD
それぞれのdiffが思い描けてなかったことにある。
対策
もしも発生してしまったら、該当ファイルを削除してdstにマージ、ファイルを復活させてdstにマージ。とするのが簡単か?
個人開発ならともかく、チーム開発しているとdiffを思い描くなんて無理な話である。
ブランチ戦略を変えずに防止・検知する仕組みも考える。
すぐ思いつくのはcherry-pickをした後に srcがdstをマージすることである。
(2つのブランチの歴史が異なるほど安全とは言えないかもしれない)
その上でreapplyをすれば、dstがsrcをマージしてもfileが追加される。
他にはsrc, dstのdiffを定期的に取って確認をすることも有用そう。
更新履歴
2024/09/20 @gyokuto さんにgit graphを記述していただきました。ありがとうございます。