LoginSignup
2
1

More than 3 years have passed since last update.

[Git] revert は undo じゃないよ 〜 revert x cherry-pickの罠 〜

Last updated at Posted at 2021-03-02

経緯

git revert をよく理解せずに使ったら、トラブりました。

ver_1.2 ブランチに入れるべき修正を間違って ver_1.3 ブランチにコミットしちゃったとき。
ver_1.3 へのコミットを revert して ver_1.2cherry-pick で当該コミットを入れたらどうなるか。

しばらくは大丈夫。たぶん期待通りの状態になります。
でも後日、 ver_1.3ver_1.2 に追従したら、 ver_1.2cherry-pick して入れた修正はなくなってしまいます

どうしてこんなことに?

revert は間違った操作を undo (なかったことに)するのとは違います
[B]が最新の状態で、[A]のファイルをどこからコピーしてきて上書きコミットするイメージです。
例えるならホワイト(修正液)で塗りつぶす感じです。gitには「ホワイトで塗りつぶす作業」が追加コミットされます。

  • [A]を[B]に変える修正をコミットしたとします。
  • [B]を revert すると[B]を[A']に変える修正をコミットしたことになります。

ここで[A']はファイルの内容自体は[A]と全く同じですが、過去に一度[B]であった過去を背負った別物です。
また[B]は将来[A']に上書きされることを運命づけられたコミットになってしまいます。

一方 cherry-pick は別のコミットをその運命込みで取り込む操作です。
別ブランチで[B]を cherry-pick して取り込んでも、[A']のコミットが入ったブランチをマージすると [A']のコミットが最新になるわけです。

解決策

revert の revert

問題が発覚したあとなら、 [A']のコミットをさらに revert して [B'] にしたものを入れれば良いです。
[B]のファイルをどこからコピーしてきて上書きコミットしても同じですが、操作ミスが入る余地が減るので可能なら revert コマンドでやったほうがいいですね。

revert しない

今回の事例の場合、そもそも ver_1.3 から revert する意味があったのか

私のケースでは、「操作間違えちゃった、取り消し!」みたいな軽い気持ちでやってしまいました。
いずれ追従する予定なら、そのまま[B]が入ったままでもよかったんです。
revert が undo じゃないと知ってれば、安易に revert するよりそのままにしておく選択肢もありました。

(もちろん、プロジェクトと修正内容によっては、先のバージョンに入れると不都合があるので revert が必要なケースもあるかもしれません。)

cherry-pick じゃなかったら?

[B]を cherry-pick じゃなくてファイルをコピーしてきて新規コミットしたら・・・
おそらく追従時にコンフリクトしますね。気付けるだけマシですが。

コミット自体なかったことにできないのか

よく知らずに使うと、 revert よりはるかに危険なので、あまりおすすめはしませんが、 rebase という操作でできるらしいです。
Git-のブランチ機能-リベース

なぜ危険かと言うと、共同作業中のリポジトリだとうっかり他の人の必要なコミットまでなかったことにしてしまい、しかも過去の歴史が改ざんされてるので気づきにくい。問題が発覚したときには当事者がいなかったりして、原因を特定するのにも苦労する、なんてこともあります。

ローカルリポジトリなら reset で

まだ push しておらず、あくまでローカルリポジトリにのみコミットした状態なら、以下のようにしてコミットする前の状態に戻すことができます。

git reset --soft HEAD^

--soft オプションはコミットした差分を維持する指定です
--hard に変えるとコミットした差分は消えて修正作業前の状態になります

また HEAD^ は一つ前のコミットまで戻る指定で、 HEAD^^, HEAD^^^ のように ^ を重ねた分だけ前のコミットに戻すことができます。

参考
https://qiita.com/forest1/items/f7c821565a7a7d64d60f

reset と push

前項で まだ push しておらず と条件を付けましたが、既に push されてる場合でも -f オプションで強制的にリモートリポジトリに反映することは可能です。これは実質的に rebase と同じ操作であり、同じく危険な行為ですのでおすすめしません。

まとめ

revert は undo ではなく、逆修正による上書きコミットです。
なので別ブランチで cherry-pick していても、マージすると revert 後の状態が最新になります。
ローカルのコミットをやり直すなら reset コマンドがいいです。

以上、私もそこまで深く git を理解してるわけじゃありませんので、間違いやよりよい解決方法などあればご意見いただければ幸いです。

2
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
1