git revertについて
git revertを理解する上で必要な知識
前回, 前々回でgit reset
, git rebase
について触れた.
ここからはgit reset
を理解してることを前提に話を進めていく.
参考ページ Git チュートリアル: 変更を元に戻す | アトラシアン
git revertの概要
git revert
は, コミットを打ち消すような動作をする.
コミットの編集履歴を辿り, 前回コミットで変更された部分(差分)のみを"打ち消す"ようなコミットを行う.
git reset と git revertの違い
例として, 前回コミットした内容に間違いが含まれていたため,
前回のコミットを取り消して, 改めてコミットしたい場合を考える.
# コミット A,B,Cがあり, Cに間違いが含まれている. # そこで, Cを取り消して, 改めてコミットを行いたい. A -> B -> C (間違いを含むコミット) ↑ HEAD
これを実現する方法は, 2つある.
それがgit reset
とgit revert
である.
git resetを使った方法
git reset
は怖くないgit reset - namu_r21の日記でも触れたように, working tree, index, headを過去コミットの状態に完全に戻すコマンドである.
つまり, そもそも"コミットなんてなかった"という状態にすることができる.
# git resetでCをなかったことにする A -> B -> C (間違いを含むコミット) ↑ HEAD $ git reset --hard HEAD~1 A -> B # Cなんてなかった ↑ HEAD
この状態で改めて変更をコミットすると以下のような状態になる.
ここで, 注意すべきは改めてコミットしたものは新しいコミットとして見なされることである.
# git resetでCをなかったことにした後 A -> B ↑ HEAD # 改めてcommit $ git commit A -> B -> D (Cとは異なるコミットとして扱われる.) ↑ HEAD
git revertを使った方法
対して, git revert
は, コミットを打ち消すような動作をする.
前回コミットで変更された部分(差分)のみを"打ち消す".
コミットCはそのまま残し, コミットCによって行われた変更部分を打ち消すコミットを発行する.
# git revertでCで行われた変更を打ち消す A -> B -> C (間違いを含むコミット) ↑ HEAD $ git revert A -> B -> C -> C' ( Cによって行われた変更を打ち消すコミット) ↑ HEAD
この時, working treeはコミットBと同じ状態になっている. 改めて, コミットを行う場合は以下のような状態になる.
# git revertでCで行われた変更を打ち消した後, A -> B -> C -> C' ↑ HEAD # 改めてcommit $ git commit A -> B -> C -> C' -> D ↑ HEAD
このようにすることで,
git revert
によってログを残しながらコミットCを"なかったこと"にすることができる.
ここがgit reset
とgit revert
の違いである.
なぜgit revertが必要なのか
個人でgitを利用し, ローカルリポジトリだけで開発を行っている場合はこの2つを混同しても特に問題はない. しかし, リモートリポジトリが関係すると話は別になる.
例えば, ローカルでコミットCまで開発しリモート(origin)にpushしてしまった場合を考える.
# 間違いが含むコミットCをoriginにpushしてしまった. A -> B -> C (間違いを含むコミット) ↑ HEAD origin/HEAD
git reset
を使って, コミットCをなかったことにして
改めてコミットを行うと以下のような状態になる.
# 間違いが含むコミットCを``git reset``によってなかったことにして # 改めてコミットを行う. A -> B -> -> D ↑ ↑ origin/HEAD HEAD $ git push origin CONFLICT!
この状態でgit push origin
を行うと, コンフリクトが起きる.
origin
側からすると,
- Bと繋がっているはずのCコミットがなくなっている
- Bと繋がっていないはずのコミットDが送られてくる
という状態になり, エラーを吐く.
これをgit revert
を使うことで繋がりを守ったまま
コミットCを処理できるため, エラーを吐かない.
# 間違いが含むコミットCを``git revert``によって"打ち消し"て # 改めてコミットを行う. A -> B -> C -> C' -> D ↑ ↑ origin/HEAD HEAD $ git push origin A -> B -> C -> C' -> D ↑ HEAD origin/HEAD
チーム開発における git revertの有効性と git resetのヤバさ
チームで開発しているリポジトリのログをgit reset
で汚してし,それを共有すると全員のリポジトリでコンフリクトが起きる.
なおかつ, logが残らないので原因追及ができなくて死ぬ.
チーム開発するときは,
- リモートにpush後のコミットを訂正する際は, git revertを使う( 不用意にgit resetしない)
- 間違っても, git resetでログをめちゃくちゃにした後にリモートリポジトリに反映しない
- pushする前に本当にpushしても問題ないかを確認する.
まとめ
git revert
は,コミットを残しつつコミットの変更を打ち消す.git reset
は, コミットそのものを無かったことにする.