LoginSignup
10
0

More than 5 years have passed since last update.

git reset --soft でステージング状態に戻る謎

Last updated at Posted at 2017-07-02

git reset --soft は、HEADを移動させるが index は変化させないとされています。

しかしながら、実際には未ステージングからステージング状態に変化します。

これはどういうことだろう、git add で登録するindexが変化しないのであれば、ステージング状態も変化しないはずでは?
との疑問から調べた結果、以下のことが分かったのでまとめてみました。

  • ステージング状態は、indexの内容では無く、commitとindexの差分を表示したものである

commitとindexとworking tree

まずは、これら3者の関係を整理しておきます。

コミット直後

image.png

HEADは、カレントのコミット位置です。
A, B, Cは、commitです。
indexは、git add により登録される領域です。
working treeが、実際に編集作業を行っているファイル領域です。

コミット直後では、これら3者の内容が一致しています。

working treeを編集

image.png

この時、git diff コマンドの出力には、indexとworking treeの差分が表示されます。
git statusコマンドでは、Modified や Untracked ファイルとして表示されています。

indexに登録

image.png

git add で、working treeの内容がindexに登録されます。

その結果、git diff では、indexとworking treeが一致しているので差が出ません。
git diff --cached では、commitとindexを比較するので、差が出ます。

この、commitとindexの"差"がステージング状態として見えているのです。

commit する

image.png

commitとindexとworking treeが一致するので、差が出なくなります。
commitとindexに差が無いのでステージング状態もクリアされます。

indexは、commit後も維持されます。

resetコマンドのオプションによる違い

git reset

オプションの --mixed 指定と同じです。(デフォルト動作)
commitとindexを移動させます。(commitの移動 = HEADの移動)

移動前:
image.png

2つ前のコミットに移動させてみます。
git reset HEAD~2

移動後:
image.png

HEADの移動先のcommit内容がindexに反映されます。
ローカルのworking treeのファイルはそのままです。

結果、ステージング状態のファイルは無く、git diff で差分が出ます。

git reset --hard

commitとindexとworking treeを移動させます。

移動前:
image.png

git reset --hard HEAD~2

移動後:
image.png

HEADの移動先のcommit内容がindexとworking treeに反映されます。

結果、ステージング状態のファイルが無く、git diff でも差が出ません。

git reset --soft

commitだけ動かします。

移動前:
image.png

git reset --soft HEAD~2

移動後:
image.png

indexとworking treeは変化しないので git diffで差が出ませんが、
commitとindexには差が有るので、ステージング状態となります。

あくまでもcommitとindexの"差"ですので、このケースではステージングの内容もコミット2つ分となります。
つまり、最後にgit addした内容に関わらず、git reset --soft による移動先次第でステージング状態が変化するということです。

なので、この状態からさらに、git reset --soft HEAD^ とすると、ステージング内容がコミット3つ分に変化します。

おまけ

checkoutで別コミットからファイルを取得するとステージング状態になる理由

例えば、変更しないはずのファイルを間違ってコミットしてしまった場合に、
git checkout HEAD^ -- hello.c
これで、変更前のファイルを取ってくることが出来ます。
この時、取得したファイルが何故かステージング状態となります。

その理由は。
git checkoutコマンドは、git reset --hard 同様、
commitの内容をindexとworking treeに反映するという動作をするので、

取得前:
image.png

git checkout HEAD^ -- hello.c

取得後:
image.png

コミットBのhello.cの情報が、現在(HEAD位置)のindexとworking treeに、反映(マージ)されます。
結果、indexと現在のHEAD位置のコミットとの間に差が生じるのでステージング状態になる、という訳です。

参考資料

Git - git-reset Documentation
[Git] 脱初級!誤コミットはもう怖くない

10
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
10
0