git reset , git checkoutでcommitを戻した時の状態の違い

#git reset , git checkoutでcommitを戻した時の状態の違い

##概要
gitの勉強の一つとしてgit resetコマンドとgit checkoutコマンドを使って
commitを前のcommitに戻したいと思います。

また、その時の状態の違いについて見ていきます。


##環境
Git 2.13.1
Windows8

##詳細

###リポジトリの作成
検証用にデスクトップ上でgit_testと名前の付いたディレクトリを作成し
その中でgit initコマンドを使い、検証用のリポジトリを作ります。

```bash:コマンド
git init
```

```bash:コマンドプロンプトの表示
Initialized empty Git repository in C:/Users/tanakatarou/Desktop/git_test/.git/
```
これで、検証用のリポジトリが作成できました。


###検証用のcommit履歴を作成する

resetとcheckoutを検証するためのcommit履歴を作成していきます。
今回は全部で3回commitします。


fileAだけ追加して１回目のcommmit

```bash:コマンド
touch fileA
git add .
git commit -m "[add] fileA"
```

fileBとfileCを追加して2回目のcommit

```bash:コマンド
touch fileB fileC
git add .
git commit -m "[add] fileB fileC"
```

ファイルAに文字を書き込んで3回目のcommit

```bash:コマンド
echo "This is fileA" > fileA
git add .
git commit -m "[modify] fileA"
```

今までのcommit履歴を見てみます。

```bash:コマンドプロンプトの表示
commit 8211a5151ec31f5e7132604e79cbec007080260c (HEAD -> master)
Author: kashiwara <ーーーーーーーーーーーーーーー>
Date:   Sat Nov 11 11:16:23 2017 +0900

    [modify] fileA

commit 12656ac4a113aa8dd4ae76b71813ec6bac15dfcb
Author: kashiwara <ーーーーーーーーーーーーーーー>
Date:   Sat Nov 11 11:15:31 2017 +0900

    [add] fileB fileC

commit b6aa0936e4c08f3a00b1ce8466aa05883cba971f
Author: kashiwara <ーーーーーーーーーーーーーーー>
Date:   Sat Nov 11 11:15:01 2017 +0900

    [add] fileA

```

これで検証用のcommit履歴が作成できたので
git resetとgit checkoutを検証できる環境になりました。


###検証用のbranchを作成しておく

失敗しておかしな事になるのは、嫌なのでbranchを２つ作っておきたいと思います。

```bash:コマンド
git branch test/reset
git branch test/checkout
```

本当に作られたのか見てみます。

```bash:コマンド
git branch
```

```bash:コマンドプロンプトの表示
* master
  test/checkout
  test/reset
```
検証用のbranchが作成できました。

###検証する
ようやく本題です。
全ての準備は整いました。
test/resetブランチとtest/checkoutブランチを使い検証していきましょう。

####git resetを使う

branchをtest/resetに切り替えましょう。

```bash:コマンド
git checkout test/reset
```
一番最初のcommitに戻します。
commitメッセージが[add] fileAのやつですね。

```bash:コマンド
git reset HEAD~~
```

```bash:コマンドプロンプトの表示
Unstaged changes after reset:
M       fileA
```

本当に戻っているのかgit logコマンドをたたいてみます。

```bash:コマンド
git log
```

```bash:コマンドプロンプトの表示
commit b6aa0936e4c08f3a00b1ce8466aa05883cba971f (HEAD -> test/reset)
Author: kashiwara <ーーーーーーーーーーーーーーーーー>
Date:   Sat Nov 11 11:15:01 2017 +0900

    [add] fileA
```
確かに戻っています。

現在のファイルの状態を確認するためにstatusコマンドを叩いてみます。

```bash:コマンド
git status
```

```bash:コマンドプロンプトの表示
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:   fileA

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

        fileB
        fileC

no changes added to commit (use "git add" and/or "git commit -a")

```

resetを使いcommitを戻すと**今までのファイルが付いてきました。**

fileAの内容も見てみます。

```bash:コマンド
cat fileA
```

```bash:コマンドプロンプトの表示
This is fileA
```
resetを使いcommitを戻すと**今までのファイルの変更内容も付いてきます。**


resetはこれで終わりです。次はcheckoutを使います。
branchを切り替えるために適当にcommitしておきます。

```bash:コマンド
git add .
git commit -m "[finish] reset"
```

####git checkoutを使う


branchをtest/checkoutに切り替えましょう。

```bash:コマンド
git checkout test/checkout
```
先ほどと同様一番最初のcommitに戻します。

```bash:コマンド
git　checkout HEAD~~
```

```bash:コマンドプロンプトの表示
Note: checking out 'HEAD~~'.

You are in 'detached HEAD' state. You can look around, make experimental
changes and commit them, and you can discard any commits you make in this
state without impacting any branches by performing another checkout.

If you want to create a new branch to retain commits you create, you may
do so (now or later) by using -b with the checkout command again. Example:

  git checkout -b <new-branch-name>

HEAD is now at b6aa093... [add] fileA

```

この状態になったら、さらにbranchを作り、そのbranchに移動します。

```bash:コマンド
git branch test/checkout2
git checkout test/checkout2
```


本当に戻っているのかgit logコマンドを叩いてみます。

```bash:コマンド
git log
```

```bash:コマンドプロンプトの表示
commit b6aa0936e4c08f3a00b1ce8466aa05883cba971f (HEAD -> test/checkout2)
Author: kashiwara <--------------------------------->
Date:   Sat Nov 11 11:15:01 2017 +0900

    [add] fileA

```
確かに戻っています。

現在のファイルの状態を確認するためにstatusコマンドを叩いてみます。

```bash:コマンド
git status
```

```bash:コマンドプロンプトの表示
On branch test/checkout2
nothing to commit, working tree clean
```

checkoutを使いcommitを戻すと**今までのファイルは付いてきませんでした。**
また、**ファイルの変更内容なども付いてきませんでした。**


###まとめ

resetを使うと**変更した内容や追加したファイルが付いてくる**
なので、現在の状態を保持したまま前のcommitに戻りたい時に使えます。

checkoutを使うと**変更した内容やファイルは付いてこない**
なので、完全に前のcommitの状態を再現したいときに使えます。



###追記
resetでも完全に前のcommitの状態（ファイルが付いてこない状態）を再現できました。
git初心者の愚行をお許しください。
git reset にはオプションが存在しました。

|オプション|説明                                                           | 
|:------|----------------------------------------------------------------|
| --soft   | HEADの位置を変更                                            
| --mixed | オプションが指定されていなかったら自動でこれになる。HEADの位置とインデックスを変更                                             | 
| --hard   | HEADの位置、インデックスの位置、ワーキングツリーの位置を変更                                              

--hardオプションを使えば全ての位置が変わるのでファイルは付いてきません。
また、記事で使っていたresetはオプションに何も指定していなかったので
--mixedオプションが指定された状態のものと同じでした。

ちなみにオプションはこんな感じに使います。

```bash:コマンド
git reset --hard HEAD~~
```

以上です。