開発ブログ

WWWクリエイターズが送る、Git、CSS、HTML、コマンドライン、Macの便利機能など、開発に関する役立ち情報発信します。気まぐれに更新。

git resetを完全攻略!softとhardの違いと利用方法。

最終更新:2017-07-05 by Joe

わかったようで、分からない git reset。

オプション無し(--mixed)、--hard、--softの3種類があるのですが、具体的な振る舞いについて、正しく理解しないままの方もいるのではないでしょうか。今回、詳しく調査したので、できるだけ分かりやすく解説します。

git reset とは?

git reset は「現在のブランチの先頭(とブランチ履歴)」と「インデックスの状態」を、指定されたコミットに強制的に書き換えるコマンドです。代表的な用途に「addしたファイルを元に戻す」という局面があります。

git addを打ち消す「git reset HEAD」

HEADは通常「現在のブランチの先頭コミット」を参照していますので、「git reset HEAD」と、HEADの指している状態へのリセットに施す事により、インデックスの状態がコミットのと同じ状態になる、すなわち、インデックスが元に戻る、というわけです。

実際は「git reset」引数を省略すると、デフォルトでHEADが指定されたことになります。

「git add の取り消し」ではない

上記の図を書くとしばしば誤解されるので、注意が必要なのが、例えば「最後のcommitから2回以上のaddした」という場合、1回目の変更含めて、すべてリセットされてしまいます。ですので、本当の意味での、「git addコマンドの実行1回分の取り消し」はgitでは不可能です。

git reset はあくまでリセットであり、1回のadd実行を取り消してくれるわけではないのです。

git では一般的に、コミットしたり、stashしていない変更内容は、一度失われると取り返せません。

hard、soft、mixed モードについて

代表的な3種類のモードを使い分けです。

モード指定 動作
git reset --soft 現在のbranchの先頭だけをリセット。(インデックスはそのまま)
git reset
(git reset --mixed)
現在のbranchの先頭と、インデックスをリセット
git reset --hard 現在のbranchの先頭、インデックスも作業ツリーも全部リセット

冒頭の「addの取り消し」の操作で使ったのは、引数なしのmixedモードです。mixedモードでは、インデックスとブランチ先頭をそれぞれをリセットしました。

それぞれのモードはリセットする対象の範囲が異なります。当然、hardが最も深くまでリセットされます。

git resetのモードの違い

これらのモードは「このモードを使ってどうしよう?」というよりは、下記のような特定の局面で、特定の使い方をすることが多いです。

コミットをやり直す git reset --soft HEAD^

--soft は、例えば、最新のコミットの内容をちょっとだけ修正したい時に git reset --soft HEAD^ とすると、直前のgit インデックスには最新の変更を取り出して、それがまだコミットされていない状態に戻れます。

もし「デバッグコード削除し忘れ」の修正であれば、あとはコードを削除して git commit を実行するだけでコミットをやり直せます。

 

ブランチ状態を強制的に変更する「git rest --hard <commit>」

--hard は、例えば、「ローカルブランチをリモートブランチで強制上書きする」などする時に、一撃必殺技的に使うことが多いです。ローカルブランチの歴史もろとも、不要なものを全部消し去って、特定の基準点に一気に帰りたい時、ということだと思います。

下図は、git reset --hard origin/master のイメージです。

とにかく、インデックス、作業ツリーの状態を無視して(変更があろうがなかろうが吹き飛ばして)origin/masterと同じ状態を作り出します。脅威の--hardです。

特定のファイルだけ元に戻す「git reset <commit> <path>」

git reset <commit> <path>とファイル名やパスを指定すると、上記のようなブランチのリセットは起きず、特定のファイルのインデックス(--hardのときは、作業ツリーも)がリセットされます。

この原理で、「git reset <commit> . 」とドットを使ってすべてのファイルを指定すると、HEADをそのままにして、インデックスのファイルをすべてそのコミットに合わせる事になります。

// 正確には同義ではないが、実質同じ効果
git reset HEAD
git reset HEAD .

ブランチを指定するか、ファイルなで指定するか?で、現在のブランチの歴史が書き換わるかどうか?の振る舞いが異なる、というわけです。この動きは「git checkout」のときと似ています。git checkoutでは、「ブランチを切り替えるかどうか」の振る舞いが変わりました。

実際、ファイルを限定するときは、resetより、checkout を使うことの方が多いです。

参考リンク

 

閉じる