git reset は 何をリセットするのか

`git reset`について、わからないことが多かったので、まとめました。
中でも、よく使うコマンドである、以下について、実験的に調べたことをまとめました。
間違いなどがありましたら、ご指摘いただけるとありがたいです。

```
git reset [ --soft | --hard ] [ HEAD | HEAD^ ]
```
## 結論
結論から書くと、このような挙動となる。

#### `git reset オプション HEAD`

|オプション|コミットされたもの|ステージングされたもの|ワークツリーで変更されたもの|ワークツリーで新規に追加されたもの|
|---|---|---|---|---|
|--soft|変化なし|変化なし|変化なし|変化なし|
|オプションなし|変化なし|ワークツリーへ移動する。|変化なし|変化なし|
|--hard|変化なし|変更が削除される。|変更が削除される。|変化なし|

#### `git reset オプション HEAD^`

|オプション|コミットされたもの|ステージングされたもの|ワークツリーで変更されたもの|ワークツリーで新規に追加されたもの|
|---|---|---|---|---|
|--soft| ステージングされた状態に戻る。|変化なし|変化なし|変化なし|
|オプションなし|ワークツリーへ移動する。|ワークツリーへ移動する。|変化なし|変化なし|
|--hard|変更が削除される。|変更が削除される。|変更が削除される。|変化なし|

## 言葉の説明

#### オプション( --soft と --hard )

どのくらいresetするかの度合いを決めるもの。
`hard` > `オプションなし` > `--soft` の順に、resetの度合いが大きい。
オプションをつけていない時は、`--soft`と`--hard`の間みたいな挙動となる。言葉からイメージしやすい。

#### HEAD と HEAD^
`HEAD`最新のコミットの状態。
`HEAD^`最新から一個前のコミットの状態。

以下で、実際にコマンドをうって、`git reset`の挙動を確かめる。見るだけではよく理解できない箇所もあるかと思いますので、一緒にターミナルで実行してみてください。

#### ワークツリー、インデックス、ステージング
ワークツリーで、ファイルを変更したり新規に追加したりする。
`git add`をすると、ワークツリーで変更されたものがステージングされ、インデックスへ移動する。
`git add`をすると、ステージングされインデックスにあるものが、コミットされる。

## 準備
こちらの4つのファイルを準備する。
`commit.txt`: コミットされたもの
`staging.txt`: ステージングされたもの
`modified.txt`: ワークツリーで変更されたもの
`untracked.txt`: ワークツリーで新規に追加されたもの

||コミットされたもの|ステージングされたもの|ワークツリー|
|---|---|---|---|
|コマンドを打つ前|commit.txt|staging.txt|modified.txt, untracked.txt|



ここで、`modified.txt`は、新規に追加されたものではなく、変更を加えられたものにしたいため、予め準備のためにcommitしてから、変更を加える。

```zsh
# modified.txt
touch modified.txt
git add modified.txt
git commit -m 'modified.txtをコミット'
echo 'modified.txtを変更する' > modified.txt

# commit.txt
touch commit.txt 
git add commit.txt
git commit -m 'commit.txtをコミット'

# staging.txt
touch staging.txt
git add staging.txt

# untracked.txt
touch untracked.txt
```

`git status`して、状況を確認。

```zsh
Changes to be committed:
  (use "git reset HEAD <file>..." to unstage)

        new file:   staging.txt

Changes not staged for commit:
  (use "git add <file>..." to update what will be committed)
  (use "git checkout -- <file>..." to discard changes in working directory)

        modified:   modified.txt

Untracked files:
  (use "git add <file>..." to include in what will be committed)

        untracked.txt
```

また、`git log`して、コミット状況を確認。

```zsh
commit 875f4d280c450dcddfd3e799448c33cb07215854 (HEAD -> master)
Author: host <host@gmail.com>
Date:   Sun Aug 9 23:03:39 2020 +0900

    commit.txtをコミット

commit bb977083c0aa34911e9dd517f274b15378c64cfe
Author: host <host@gmail.com>
Date:   Sun Aug 9 23:03:15 2020 +0900

    modified.txtをコミット
```
この状況から、以下の6条件での`git reset`を行う。それぞれのコマンドを打つ前の状態は、上の状態に統一している。

## `git reset オプション HEAD`

#### 1. `git reset --soft HEAD`
これは何も変わらない。

||コミットされたもの|ステージングされたもの|ワークツリー|
|---|---|---|---|---|
|コマンドを打つ前|commit.txt|staging.txt|modified.txt, untracked.txt|
|コマンドを打った後|commit.txt|staging.txt|modified.txt, untracked.txt|

```zsh
Changes to be committed:
  (use "git reset HEAD <file>..." to unstage)

        new file:   staging.txt

Changes not staged for commit:
  (use "git add <file>..." to update what will be committed)
  (use "git checkout -- <file>..." to discard changes in working directory)

        modified:   modified.txt

Untracked files:
  (use "git add <file>..." to include in what will be committed)

        untracked.txt
```

#### 2. `git reset HEAD`
ステージングされていた、`staging.txt`がインデックスからワークツリーへ移動。

||コミットされたもの|ステージングされたもの|ワークツリー|
|---|---|---|---|---|
|コマンドを打つ前|commit.txt|staging.txt|modified.txt, untracked.txt|
|コマンドを打った後|commit.txt||modified.txt, untracked.txt, staging.txt|

```zsh
Changes not staged for commit:
  (use "git add <file>..." to update what will be committed)
  (use "git checkout -- <file>..." to discard changes in working directory)

        modified:   modified.txt

Untracked files:
  (use "git add <file>..." to include in what will be committed)

        staging.txt
        untracked.txt
```

#### 3. `git reset --hard HEAD`
ステージングされていた`staging.txt`の変更が削除される！！
ワークツリーの`modified.txt`の変更が削除される！！
ただし、ワークツリーに新規追加された`untracked.txt`はそのまま残る。


||コミットされたもの|ステージングされたもの|ワークツリー|
|---|---|---|---|---|
|コマンドを打つ前|commit.txt|staging.txt|modified.txt, untracked.txt|
|コマンドを打った後|commit.txt||untracked.txt|


```zsh
Untracked files:
  (use "git add <file>..." to include in what will be committed)

        untracked.txt
```

## `git reset オプション HEAD^`

#### 1. `git reset --soft HEAD^`
コミットしたはずの`commit.txt`が、ステージングされた状態に戻る。

||コミットされたもの|ステージングされたもの|ワークツリー|
|---|---|---|---|---|
|コマンドを打つ前|commit.txt|staging.txt|modified.txt, untracked.txt|
|コマンドを打った後||staging.txt, commit.txt|modified.txt, untracked.txt|
```zsh
Changes to be committed:
  (use "git reset HEAD <file>..." to unstage)

        new file:   commit.txt
        new file:   staging.txt

Changes not staged for commit:
  (use "git add <file>..." to update what will be committed)
  (use "git checkout -- <file>..." to discard changes in working directory)

        modified:   modified.txt

Untracked files:
  (use "git add <file>..." to include in what will be committed)

        untracked.txt
```


#### 2. `git reset HEAD^`

コミットしたはずの`commit.txt`が、ワークツリーへ移動。
ステージングされていた、`staging.txt`がインデックスからワークツリーへ移動。

||コミットされたもの|ステージングされたもの|ワークツリー|
|---|---|---|---|---|
|コマンドを打つ前|commit.txt|staging.txt|modified.txt, untracked.txt|
|コマンドを打った後|||modified.txt, untracked.txt, staging.txt, commit.txt|

```zsh
Changes not staged for commit:
  (use "git add <file>..." to update what will be committed)
  (use "git checkout -- <file>..." to discard changes in working directory)

        modified:   modified.txt

Untracked files:
  (use "git add <file>..." to include in what will be committed)

        commit.txt
        staging.txt
        untracked.txt
```

#### 3. `git reset --hard HEAD^`
コミットしたはずの`commit.txt`の変更が、削除される。
ステージングされていた`staging.txt`の変更が削除される！！
ワークツリーの`modified.txt`の変更が削除される！！
ただし、ワークツリーに新規追加された`untrached.txt`はそのまま残る。

||コミットされたもの|ステージングされたもの|ワークツリー|
|---|---|---|---|---|
|コマンドを打つ前|commit.txt|staging.txt|modified.txt, untracked.txt|
|コマンドを打った後|||untracked.txt|

```zsh
Untracked files:
  (use "git add <file>..." to include in what will be committed)

        untracked.txt
```