Git で発生する「取り消し」の操作において重要なコマンドの一つ「git reset」。今回、git reset の詳細な仕様の理解を焦点をあて、できるだけ分かりやすくまとめてみました。
目次
git reset の概要
git reset は、2種類の機能を持つコマンドです。
- インデックスを、任意の状態にリセットする
- ブランチの先頭(HEAD)を、指定したコミットに強制移動する
コマンド実行時のパラメータにファイルパスを指定すれば(1)、そうでなくコミットやコミットへの参照を指定すれば(2)の動作です。
このコマンドは、下記のような引数のとり方をします。
// [1] インデックスの状態をリセットする
$ git reset <commit> <file>
$ git reset <file>
// [2] ブランチの先頭を、指定したコミットに強制移動する
$ git reset <commit>
なお、ファイルパスの指定は「<コミット> <パス>」または「<コミット> — <パス>」のように、どのコミットにおけるファイルなのかを指定することができます。
git reset コマンドの基本的な使い方
1インデックスの状態をリセットする
「git reset <ファイルパス>」はインデックスの変更内容を取り消すコマンドです。上記(1)にあたるこの機能は主に、インデックスを直前のコミット直後にリセットする目的で利用されます。
例1)git add を取り消す「git reset <ファイルパス>」
まず前提として、「git reset <ファイルパス>」として、2番目の引数(=コミット識別)を省略すると、デフォルトで HEAD が指定された事と同義になります。
Git において、「HEAD」は通常「現在のブランチの先頭コミット」を参照しています。「git reset HEAD」と指定すれば、インデックスの状態が直近のコミットと同じ状態になりますので、これはすなわち「インデックスの変更を取り消し、元に戻す(=HEADの状態に戻す)」という動作になります。
git reset は「git add の正反対」ではない
上記の図を書くとしばしば誤解されるので、注意が必要なのが、例えば「最後のcommitから2回以上のaddした」という場合、1回目の変更含めて、すべてリセットされてしまいます。ですので、本当の意味での、「git addコマンドの実行1回分の取り消し」は Git では不可能です。git reset はあくまでインデックスのリセットであり、1回の git add 実行を取り消してくれるわけではないのです。
Git では一般的に、コミットしたり、stashしていない変更内容は、一度失われると取り返せませんので注意しましょう。
補足)ファイル削除を git add した場合の git reset
「ファイルの削除」という変更を Git add すると、
$ git status
On branch master
Changes to be committed:
(use "git reset HEAD <file>..." to unstage)
deleted: css/style.css
すでに出力されていますが。「use git reset HEAD <file> to unstage 」と指示されています。
試しにやってみましょう。
$ git reset css/style.css
fatal: ambiguous argument 'css/style.css': unknown revision or path not in the working tree.
Use '--' to separate paths from revisions, like this:
'git <command> [<revision>...] -- [<file>...]'
やはり、実際に、ファイルの削除は、git reset <file> では、インデックスから削除することができません。作業ツリーにパスが見つからない、というエラーとなっています。この場合、やはり指示の通り、2番めの引数に「HEAD」を追加すると、うまくいきます。(「–」は省略しても同義です。)
$ git reset HEAD css/style.css
これは、ふるまいとしては、次に紹介する[2]の機能を利用している事になるようです。
それでは、git reset のもう一つの機能を見てみましょう。
2ブランチの先頭を、指定したコミットに強制移動する
「git reset <commit>」として、ファイルパスの指定をしなければ、現在のブランチの先頭である「HEAD」を強制的に指定したコミットに移動させる事ができます。これは、上記の(2)の機能にあたり、インデックスを書き換えるだけの(1)とは明確に異なる機能です。
さらに、git reset は、デフォルトでは「HEADの移動」と同時に「インデックスの状態」をリセット先のコミットと一致させます(作業ツリーはそのままです)。
git reset の「モード」というオプションで、「HEADの移動」「作業ツリーの状態」「インデックスの状態」、それぞれをリセットするかどうかに関して、意図したとおりに、切り替えることができます。
それでは、次のセクションで詳しくオプションについて見ていきましょう。
git reset の hard/soft オプションについて
さて、git reset の代表的な3種類のモードを使い分けについてです。
git reset はデフォルトでは、HEADとインデックスを更新し、作業ツリーをそのままにします。一方で、--softモードでは、HEADを移動して、インデックスも作業ツリーもそのままです。さらに、逆に --hard モードでは、作業ツリーもろともリセットを適応します。
これらの違いを、図で比べてみましょう。
表でも比較してみます。
モード | 影響範囲 |
git reset --soft | HEAD(現在のブランチの先頭)だけをリセットします。 |
git reset git reset --mixed | HEAD(現在のブランチの先頭)とインデックスをリセットします。作業ツリーはそのままです。 |
git reset --hard | HEAD(現在のブランチの先頭)とインデックス、および作業ツリーをリセットします。 |
git reset の hard/soft モードオプションの使用例
下記にオプションを使った具体的な使用例を紹介します。
例1git commit の実行を取り消す「git reset --soft HEAD^」
最新のコミットの内容をちょっとだけ修正したい時に「 git reset --soft HEAD^」とすると、インデックスには最新の変更をのこしたまま、HEAD だけを一つ前に巻き戻します。これによって、直前のgit commit 実行を取り消して、それがまだコミットされていない状態に戻れます。
例えば、もし「デバッグコード削除し忘れ」の修正であれば、あとはコードを削除して git commit を実行するだけでコミットをやり直せる、というわけです。
これは「git commit --amend」でも代用できる振る舞いです。--amend オプションのほうが直感的ですので、あえて soft モードを使う必要はないかもしれません。
例2過去のコミット直後に強制的に戻す「git rest --hard <commit>」
--hard は、例えば、「ローカルブランチをリモートブランチで強制上書きする」などする時に、手元の開発環境を強制的に特定の状態に戻したい時、一撃必殺技的に使うことが多いと思います。
例えば、ローカルの master ブランチが、なぜか origin/master と食い違ってしまって、「とりあえずリモートに合わせたい!」などという状況では、コマンド一撃でその状態に合わせる事ができるのです。
下図は食い違ってしまった master を、origin/master に強制的に合わせるイメージです。
とにかく、インデックス、作業ツリーの状態を無視して(変更があろうがなかろうが吹き飛ばして)origin/masterと同じ状態を作り出します。
非常にパワフルですが、一歩間違えると、作業中の変更を抹消してしまうので、実行までにgit status で、状態を確認してから行いましょう。
git reset の主要なオプション
その他の主要なオプションです。soft/hard/mixerd モードについても改めておさらいします。
出力しない「-q, --quite」
出力を抑制します。
ひとつづつリセット内容を検証する「-p, --patch」
git add --patch と同じように、変更のhunk(変更の箇所の塊)をひとつずつレビューしながら、リセットを進めることができます。このオプションは、「--hard」「--soft 」などのモード指定と共存することができません。
モード指定 –<mode>
作業ツリーとインデックスを変更しない「--soft」
作業ツリーとインデックスを変更せず。ブランチ参照(HEAD)だけを指定したコミットに移動します。
作業ツリーを変更しない「--mixed」
これはデフォルト(オプションなし)の振る舞いと同じです。ブランチ参照を指定したコミットに移動し、インデックスをそのコミット状態に戻します。
コミット参照を含めてリセットする「--hard」
ブランチ参照を指定したコミットに移動し、かつ、作業ツリーとインデックスの両方を指定したコミットにリセットします。
git reset の参考情報
当サイトの、git reset に関するリンクです。Git を利用していると、git reset コマンドの利用ケースには多く出くわします。
Git 公式ドキュメントです。困ったときはこちら。
まだ Commit してないのに間違って git reset しちゃうと、変更はもう取り戻せなくなるね・・。気をつけよう。