開発ブログ

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

git add の取り消し方法、徹底まとめ。

最終更新:2017-11-22 by Joe

git add してしまったファイルを取り消しするgit コマンドについてです。コミット履歴を整然と意図をもって育てて行くには、git add をうまく使いこなして、インデックスの作り方を覚えておくことはすごく重要となるはずです。

git add を取り消す方法

git add は、「インデックス」と呼ばれる一時領域に、次のコミットを生成するための変更内容を記録する保存領域でした。(インデックスは実際はバイナリで、通常「.git/index」のファイル名で保存されています。)今回、インデックスの更新を取り消そうとしているのですが「どの範囲で取り消すのか?」において、いくつかの選択肢があります。

add したファイルを全て一括で取り消す場合

git add したファイルの変更内容を、一括でインデックスから削除するには、git reset コマンドを利用します。

// git add したファイルの更新をインデックスから削除
git reset

// 上と同義
git reset HEAD

git reset の引数に「HEAD」と指定すれば、作業ツリーの状態はそのままで、インデックスへaddした変更が、元の状態に戻ります。なお、引数なしでデフォルトでHEADを指定した事になります。一旦インデックスを元に戻したら、その後、必要なファイルをaddし直せばまたコミットできます。

イメージとしては、git add がインデックスを更新し・・・、

git add はインデックスを更新する。

git reset (= git reset HEAD)がそれを取り消すように作用します。作業ツリーに影響はありません。

git reset が git add による更新を取り消す。

ファイルを指定して取り消す場合

git reset 実行時に、パスを指定することで、特定のファイルについての変更内容をだけをインデックスから削除します。こちらも作業ツリーはそのままです。

// git add したファイルをインデックスから削除
git reset <path>

// 上と同義
git reset HEAD <path>

<path>と書いた部分に、ディレクトリ名を指定することで、フォルダ配下のすべてのファイルを指定できます。これは git add のときも一緒ですね。

ファイルを限定してaddを取り消す。

「git reset」は「git add の取り消し」か?

1点注意が必要なのが、例えば「最後のcommitから2回以上のaddを実行した。」という場合、1回目の変更含めて、すべて一度にリセットされてしまいます。ですので、本当の意味での「git addコマンドの実行1回分の取り消し」はgitでは不可能です。addしただけの変更も含め、git では一般的に、commit や stash していない変更内容は、一度失われると取り返せません。

ですので、git reset によるインデックスの操作は、実際「git addの取り消し」と呼ぶよりは、やはり「インデックスを(直前のコミット状態に)リセットする」と言った方が実際に起きていることを表しているといえます。いずれにしても、git add をやり直すには、この方法を使うことになります。

git add したファイルとHEADの差分を確認

何度も [gitc]git add[/gitc] したり、[gitc]git reset[/gitc] したりしていると「いまインデックスがどんな状態か」分かりにくくなりそうですが、下記を実行すると、インデックスとHEADの差分を見る事ができます。

// HEAD → インデックスの差分を確認する
git diff --cached HEAD

// 同義。(引数なしは、デフォルトでHEADが差分元になります)
git diff --cached

「--cached」のオプションにより、普段は作業ツリーを対象としているdiffの振る舞いを、インデックスへ変更することができるのでした。[gitc]git diff[/gitc]の振る舞いについては、別記事に詳しくまとめたので、ぜひご覧下さい。

「git reset」の振る舞いについて考える

それでは、良い機会なので、もう少しこの「git reset」コマンドについて、詳しく見ていきます。

git reset のリセットの適応対象。

上記でのgit reset の利用は、基本的にはインデックスの状態(全て、または一部)を書き換えるコマンドでした。ただ実際には、git reset のオプションにより、「モード」が指定可能で、これにより、インデックスだけでなく同時に作業ツリーや、ブランチの先頭(HEAD)をまとめて、指定したコミットの状態にに合わせることもできます。

git reset のモードを使い分ける

git reset は、デフォルトでは、引数なしで(mixedモード)とみなされます。その他「soft」「hard」というモードが存在し、用途によって使い分けます。

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

このようにリセットされる範囲が異なります。

git reset のオプションによる適応範囲の違い

モードごとに、用途を考えてみます。

git reset --soft はいつ使うの?

このオプションは、上記の通り、ブランチの先頭(HEAD)だけを書き換えて、インデックスと作業ツリーには影響を与えません。

このオプションは、例えば「最新のコミットの内容をちょっとだけ修正したい時」に使う事があります。例えば「git reset --soft HEAD^ 」と実行すると、 インデックスと作業ツリーはそのままで、HEADの参照が一つ前に戻ります。もし「デバッグコード削除し忘れ」の修正であれば、あとはその不要なコードを削除してgit commit を実行するだけで直前のコミットを修正できます。

git reset --soft の使い所。

これは「git commit . --amend」の動きによく似ていますね。HEAD^にリセットするのであれば、amendで十分でしょう。ただし、git reset であれば、いかなる過去のコミットにもリセットし、やり直せるのです。(そんなケースは少ないですが・・)

git reset はいつ使うの?

上述の例のように、git add を取り消してやり直す時など、インデックスを整える時に使うでしょう。

git rest --hard はいつ使うの?

このコマンドは、インデックス、作業ツリーもろとも、すべて指定したコミットと同じ状態に強制的に書き換える、つよい効果をもったコマンドです。

例えば、「ローカルブランチをリモートブランチで強制上書きする」などする時に、一撃必殺技的に使うことが多いでしょう。ローカルブランチの歴史もろとも、不要なものを全部消し去って、特定の基準点に一気に帰りたい時、というケースは、時々発生するものだと思います。これにより、インデックス、作業ツリーの現在の状態を無視して(変更があろうがなかろうが吹き飛ばして)指定されたコミットと全く同じ状態を作り出します。

git resetについて、より詳しくは、こちらをご覧下さい:

作業途中の変更は、このコマンドにより、取り返しがつかなくなりますので、注意を払って使う必要があります。一時的な変更はgit stash などで対比しておくのも1つのやり方だと思います。

参考リンク

git add も、git resetもだいたいマニュアルには書いてありますね。情報が多いため、少々読みにくいのが難点です。

また、より詳しくgit reset についてまとめました。

git commit の取り消しに関しては、こちらを御覧ください

以上、git add の取り消しについてでした。それでは、楽しいgit ライフを。

閉じる