Gitにコミット済みのファイルを取り消して元の状態に戻したいな。
コミット直後に修正箇所が見つかったり、過去のコミット履歴を取り消したいときがあると思います。
今回は、コミットを取り消すコマンドや使い方について詳しく解説します。
コミットを取り消す2つのGitコマンド
コミットを取り消しするgitコマンドにはgit resetとgit revertの2つがあります。
ざっくり説明すると、特徴は下記の通りです。
- git reset
- :HEADとブランチを指定のコミットに移動する
- git revert
- :指定したコミットを打ち消す
実感が湧かないと思うので、例を交えながら解説します。
git resetでコミットを取り消しする方法
git resetはHEADとブランチを指定したコミットに移動させるコマンドです。
どういう事か、例で見てみましょう。
git resetの仕組み
現在、mainブランチには「commit1」~「commit4」の4つのコミット履歴があるとします。
git logで見てみましょう。
現在HEADとmainブランチは最新の「commit4」を参照していることがわかります((HEAD -> main)と書かれた場所に注目)。
図で書くとこうですね。
右に行くほど、新しいコミットです。
たとえば「commit3と4を取り消してcommit2に戻したい」という場合、git resetを使うと下図のようにHEADとmainブランチをcommit2に移動させることができます。
commit3とcommit4は参照不可の状態になるため、実質的にcommit2が最新のコミットとなります。
なお、commit3とcommit4は暫くしてから自動的に削除されます。
git resetの使い方
git resetの基本的な使い方を解説します。
直前のコミットに戻りたい場合
直前のコミットに戻りたい場合は、「HEAD^」と指定すればOKです。
$ git reset HEAD^
上記コマンドを実行すると、HEADとmainが直前のコミットに戻ります。
先ほどの例だと「commit3に移動」ですね。
実質上commit4が取り消され、commit3が最新のコミットとなります。
git logコマンドで確認してみましょう。
HEADとmainブランチがcommit3を参照しているのがわかります。
同時に、履歴からcommit4が消えているのも確認できますね。
なお、commit4は実際に消えているのではなく、現時点では「見えていない」だけです。
暫くした後に自動削除されますが、それまではいつでも戻すことができます(方法については後述します)。
ちなみに、「HEAD^^」と書くと2つ前のコミットに、「HEAD^^^」と書くと3つ前のコミットに戻せます。
コミット番号を指定して戻したい場合
特定のコミットを指定して戻すも可能です。
$ git reset [戻りたいコミットのSHA-1値]
たとえば、下記の「commit2」に戻りたいしましょう。
その場合はcommit2の「SHA-1値」を指定します。SHA-1値とは、簡単に言えば「コミットのID」です。
ちなみに、全部書く必要はなくて、冒頭の4~5桁でOKです。
$ git reset bcdf0
再度git logで確認すると、HEADとmainブランチがcommit2に移動していることがわかります。
ついでに、commit3とcommit4が消えているのも確認できますね。
先述のとおり、取り消し直後であればcommit3やcommit4に戻すことが可能です。
たとえば「戻すのはcommt2じゃなくてcommit3だった!」という場合でも、再度git resetでcommit3のSHA-1値を指定すれば大丈夫です。
$ git reset 532f7 [commit3のSHA-1値]
無事にcommit3に戻れます。
git resetのオプション
git resetはHEADとブランチを指定のコミットに移動させるコマンドですが、3つのオプションが用意されています。
それが、softとmixed、そしてhardです。
- git reset --soft
- :コミット前の状態に戻す(リセット度:弱)
- git reset --mixed(git resetと同じ)
- :ステージング前の状態に戻す(リセット度:中)
- git reset --hard
- :ワーキングスペースも元に戻す(リセット度:強)
git reset --soft
softオプションは、3つの中でもっとも弱いリセットです。
下記コマンドを実行すると、直前のコミットだけを取り消してステージングエリアに戻します。
$ git reset --soft HEAD^
図がわかりやすいかもですね。
git reset --mixed(git reset)
mixedオプションは中程度のリセットになります。
下記コマンドを実行すると、直前のコミットとステージングが取り消されます。
なお、git reset HEAD^のようにオプションを指定しない場合は、mixedオプションを指定したとみなされます。
$ git reset --mixed HEAD^
git reset --hard
hardオプションは、git resetの中でもっとも強力です。
下記コマンドを実行すると、コミットやステージングだけでなく、ワークツリーの変更内容もすべて「無かったこと」になります。
$ git reset --hard HEAD^
ファイルの変更内容まですべて消えてしまうので、取扱いに注意が必要です。
git revertでコミットを取り消しする方法
git revertは指定したコミットを打ち消すコマンドです。
git revertの仕組み
たとえば、下記のようなコミット履歴があって、「commit4を取り消したい」とします。
git revertを実行すると、commit4を打ち消すコミットが追加され、実質的にcommit3の状態に戻ります。
git revertの使い方
git revertは、取り消したいコミットのSHA-1(コミットのID)を指定します。
$ git revert [取り消したいコミットのSHA-1]
最新のコミット(HEAD)を取り消したい場合は、下のように書きます。
$ git revert HEAD
実際にやってみましょう。
先ほどと同じく、commit1~commit4の4つのコミットがあるとします。
たとえば「commit4を取り消したい」と思ったら、commit4のSHA-1値(コミットID)を指定します。
$ git revert 110339
なお、commit4は最新のコミットなので、下記コマンドでもOKです。
$ git revert HEAD
上記コマンドを実行するとエディタが開き、コミットメッセージを入力するよう求められます。
コミットメッセージは自由ですが、わかりやすい方がいいので「commit4の取り消し」としました。
入力後、エディタを閉じるとコミットが完了します。
git logでコミット履歴を確認してみましょう。
「commit4の取り消し」という、打ち消しのコミットが反映されていますね。
念のため、git diffコマンドで差分を確認すると、commit4の内容が削除され、commit3に戻っているのがわかります。
git resetとgit revertどちらがおすすめ?
コミットの取り消しは、基本的にgit revertを使うのがおすすめです。
git revertがおすすめな理由
コミットを取り消したい場合git resetとgit revertがあるが特別な事情がない限りgit revertを使うのがおすすめ。git resetはコミット履歴が消えてしまうがgit revertは履歴が残るから。gitはバージョン管理ツールである以上できる限りコミット履歴は残すべき。
— たけち@Web制作フリーランス (@takechi_web) November 12, 2023
理由は上記の通りで、git revertは取り消す前のコミットも履歴に残るからです。
git resetは取り消しのコミット履歴が残らない
一方、git resetはコミットを取り消すと履歴も削除されるという特徴があります。
下記はgit resetを実行した際の挙動ですが、取り消したcommit4には実質上アクセスできない状態となり、いずれ消えてしまいます。
「不要なんだし、消えても問題ないじゃん」と思うかもしれません。
確かに個人開発ならOKかもですが、共同開発で安易にコミット履歴を削除してしまうと、他の開発者のコミット履歴にも影響を及ぼす危険があります。
ですので、特に複数人で共同作業を行う際には、コミット履歴を削除するのは避ける方がベターです。
まとめ
最後に、コミットを取り消すコマンドをまとめます。
- git reset
- :HEADとブランチを指定のコミットに移動する
- :soft、mixed、hardの3つのオプションがある
- :コミット履歴も消えるので注意が必要
- git revert
- :指定したコミットを打ち消す
- :コミット履歴が残るのでおすすめ
コミットの取り消しは、実務でも結構使うので、この機会に整理しておきましょう。