GitとGit Hub 入門(3/3)


#前の記事はこちら
[GitとGit Hub 入門(1/3)](https://qiita.com/maruuuuu/items/9efce79e3f0dd8e152ee)
[GitとGit Hub 入門(2/3)](https://qiita.com/maruuuuu/items/bf50f9290e277c2fce3e)

# こんな時どうする？
仕事中によくあるシーンをまとめたので、一緒に練習してみましょう。

## ステージングへの追加を取り消す
勢い余ってステージングにファイルを追加してしまったけど、次のコミットにはやっぱり含めたくない！という場合です。

試しに、ファイル「test2」をステージングに追加します。

```
$ git add test2
$ git status
On branch master
Changes to be committed:
  (use "git reset HEAD <file>..." to unstage)

	new file:   test2
```
答えが表示されていますね。
` (use "git reset HEAD <file>..." to unstage)`

案内の通りに、ステージングからtest2を元に戻します。

```
$ git reset HEAD test2
$ git status
On branch master
Untracked files:
  (use "git add <file>..." to include in what will be committed)

	test2

nothing added to commit but untracked files present (use "git add" to track)
```
ステージングに追加する前に元に戻りましたね。
ここで、新しい言葉が出てきました。`HEAD`って何でしょう。

### HEADとは
HEADは、今作業しているブランチの最新のコミットのことです。
`git log`でコミットログを確認してみましょう。

```
$ git log
commit 7022b44cb174e7d1d932a950d052fcdec9b36408 (HEAD -> master, origin/master)
Author: hoge <53826023+hoge@users.noreply.github.com>
Date:   Sat Oct 12 22:15:23 2019 +0900

    Create README.md

commit f733bfe11003832f28f4803481cbe086e1903e4a
Author: hoge <hoge@gmail.com>
Date:   Sat Oct 12 19:13:31 2019 +0900

    first commit
```

今、HEADは一番上のREADMEを追加した時のコミットになっています。
`git show HEAD`で最新のコミットの内容を見ることができます。

```
$ git show HEAD
commit 7022b44cb174e7d1d932a950d052fcdec9b36408 (HEAD -> master, origin/master)
Author: hoge <53826023+hoge@users.noreply.github.com>
Date:   Sat Oct 12 22:15:23 2019 +0900

    Create README.md

diff --git a/README.md b/README.md
new file mode 100644
index 0000000..bab6eaf
--- /dev/null
+++ b/README.md
@@ -0,0 +1,2 @@
+# sample
+sample for remote repository

```
ついでに、最新のコミットの１つ前のコミットは`HEAD^`になります。
前のコミットになるほど、`^`が増えていきます。

最新のコミットの１つ前のコミットは一番最初のコミットになりますね。

```
$ git show HEAD^
commit f733bfe11003832f28f4803481cbe086e1903e4a
Author: hoge <hoge@gmail.com>
Date:   Sat Oct 12 19:13:31 2019 +0900

    first commit

diff --git a/test1 b/test1
new file mode 100644
index 0000000..e69de29
```

つまり、`git reset HEAD test2`は`HEADの状態`まで元に戻す、ということになります。
commit の後にある `f733bfe11003832f28f4803481cbe086e1903e4a`はコミットIDで、HEADの代わりにこれを入力しても同じになります。

## コミットした時のコメントを修正する
直前のコミット
のコメントを修正したい場合です。

まず、ファイル「test2」をステージングに追加し、コミットまで行いましょう。
コメントは`"add hoge"`にします。

```
$ git add test2
$ git commit -m "add hoge"
[master c26d97e] add hoge
 1 file changed, 0 insertions(+), 0 deletions(-)
 create mode 100644 test2
```
`git log`でコミットされたことを確認します。

```
$ git log
commit c26d97e9a6c9460a0b9790f3040b12beca1750f0 (HEAD -> master)
Author: hoge <hoge@gmail.com>
Date:   Sun Oct 13 15:11:59 2019 +0900

    add hoge
```

コメントを`"add test2"`に修正します。

```
$ git commit --amend -m "add test2"
```

コミットログのコメントが修正されていることを確認しましょう。

```
$ git log
commit 255795fc007dc6c5e64cc4acd2a54ca1e62f64c4 (HEAD -> master)
Author: hoge <hoge@gmail.com>
Date:   Sun Oct 13 15:11:59 2019 +0900

    add test2
```
コメントが修正できましたね。

## コミットを取り消す
間違えてコミットしてしまったけど、変更箇所が漏れてた時など、コミットを無かったことにしたい場合です。

これも、`git reset`を使います。

```
$ git reset --soft HEAD^
```
`HEAD^`は、最新のコミットの１つ前を指しますから、最新のコミットの１つ前まで戻す、という意味です。
`--soft`をつけると、コミットの操作のみが取り消されて、ステージングに追加された状態まで戻ります。

コミットログを見てみましょう。
先ほどの、`add test2`のコミットがなくなりましたね。

```
$ git log
commit 7022b44cb174e7d1d932a950d052fcdec9b36408 (HEAD -> master, origin/master)
Author: hoge <53826023+hoge@users.noreply.github.com>
Date:   Sat Oct 12 22:15:23 2019 +0900

    Create README.md

commit f733bfe11003832f28f4803481cbe086e1903e4a
Author: hoge <hoge@gmail.com>
Date:   Sat Oct 12 19:13:31 2019 +0900

    first commit
```

`git status`でステージングの状態を見てみましょう。
test2のファイルが追加されていますね。

```
$ git status
On branch master
Changes to be committed:
  (use "git reset HEAD <file>..." to unstage)

	new file:   test2
```
ちなみに、`--soft`を付けないと、ステージングに追加した操作も一緒に取り消されます。
また、`--hard`とすると、作業中のファイルの状態も`HEAD^`の状態（最新のコミットの１つ前の状態）になります。つまり、test2のファイルが削除されます。（気をつけて。。）

## プッシュしたファイルを元に戻す
間違えてプッシュしちゃった・・・という時プッシュを取り消すことができます。
この作業は複数人で同じリモートリポジトリを見ながら作業している場合、少し危険です。ちゃんとプッシュを取り消すことを周知しておきましょう。

### 無かったことにする
まず、ファイル「test2」をコミットし、リモートリポジトリにプッシュします。

```
$ git commit -m "add test2"
[master 72225df] add test2
 1 file changed, 0 insertions(+), 0 deletions(-)
 create mode 100644 test2

$ git push origin master
Enumerating objects: 3, done.
Counting objects: 100% (3/3), done.
Delta compression using up to 4 threads
Compressing objects: 100% (2/2), done.
Writing objects: 100% (2/2), 264 bytes | 264.00 KiB/s, done.
Total 2 (delta 0), reused 0 (delta 0)
To https://github.com/hoge/sample.git
   7022b44..72225df  master -> master
```

リモートリポジトリにtest2が追加されました。
<img width="991" alt="sample_for_remote_repository.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/386017/58258d5e-58f3-be19-bc23-4a3f8c1949bd.png">

コミットログも追加されていますね。

```
$ git log
commit 72225dfff26c3e09114614739f584b5b6c9701af (HEAD -> master, origin/master)
Author: hoge <hoge@gmail.com>
Date:   Sun Oct 13 16:54:55 2019 +0900

    add test2

commit 7022b44cb174e7d1d932a950d052fcdec9b36408
Author: hoge <53826023+hoge@users.noreply.github.com>
Date:   Sat Oct 12 22:15:23 2019 +0900

    Create README.md

commit f733bfe11003832f28f4803481cbe086e1903e4a
Author: hoge <hoge@gmail.com>
Date:   Sat Oct 12 19:13:31 2019 +0900

    first commit
```

まず、先ほど説明した方法でコミットを取り消します。

```
$ git reset --soft HEAD^
$ git log
commit 7022b44cb174e7d1d932a950d052fcdec9b36408 (HEAD -> master)
Author: hoge <53826023+hoge@users.noreply.github.com>
Date:   Sat Oct 12 22:15:23 2019 +0900

    Create README.md

commit f733bfe11003832f28f4803481cbe086e1903e4a
Author: hoge <hoge@gmail.com>
Date:   Sat Oct 12 19:13:31 2019 +0900

    first commit
```

`add test2`のコミットが消えましたね。

この状態で、プッシュし直します。

```
$ git push origin mastser
error: src refspec mastser does not match any.
error: failed to push some refs to 'https://github.com/hoge/sample.git'
```

おっと、エラーになりましたね。
リモートリポジトリで記録されたコミットログと、プッシュされたコミットログに乖離があったからです。
具体的には、リモートリポジトリには`add test2`というログがまだ残っているのに、新たにプッシュされたコミットログには`add test2`が無くなっているからです。

あまりお行儀はよくありませんが、`-f`を付けて強制的にプッシュします。

```
$ git push -f origin master
Total 0 (delta 0), reused 0 (delta 0)
To https://github.com/hoge/sample.git
 + 72225df...7022b44 master -> master (forced update)
```
エラーが出ず、プッシュできましたね。

リモートリポジトリからもtest2が削除されています。
<img width="986" alt="sample_for_remote_repository2.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/386017/c4c48512-228a-a96b-721b-250e9ca5bf15.png">

## コンフリクトを修正する
リモートリポジトリとローカルリポジトリで同じところに更新が入った場合、ローカルリポジトリからリモートリポジトリにプッシュするとエラーが出てプッシュできません。この状態を`コンフリクト`していると言います。
複数人で同じリモートリポジトリを更新していたら、こんなことも起こる可能性があります。

簡単に試してみましょう。まず、リモートリポジトリ側でREADMEの内容を全て削除して、「modify comment」と入力します。
<img width="794" alt="sample_for_remote_repository3.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/386017/f5df323f-03d0-dc3b-c51a-dbddcc74ae2d.png">

次に、ローカルリポジトリでもREADMEの内容を修正します。
viコマンドで、READMEの内容を全て削除して、「test」と入力しましょう。

そして、ステージングに追加し、コミット・プッシュまで一気にやってみましょう。

```
$ vi README.md
$ git add README.md
$ git commit -m "mod README"
$ git push origin master
To https://github.com/maru-e/sample.git
 ! [rejected]        master -> master (fetch first)
error: failed to push some refs to 'https://github.com/hoge/sample.git'
hint: Updates were rejected because the remote contains work that you do
hint: not have locally. This is usually caused by another repository pushing
hint: to the same ref. You may want to first integrate the remote changes
hint: (e.g., 'git pull ...') before pushing again.
hint: See the 'Note about fast-forwards' in 'git push --help' for details.
```
プッシュしたらエラーが出ましたね。
リモートリポジトリでの変更とローカルリポジトリでの変更が衝突してしまって、どちらを優先すれば良いのか分からないからです。

このような状態になったら、リモートリポジトリでどんな変更がされたんだろう？と気になりますよね。
そんな時は`git fetch`でリモートリポジトリの変更内容をローカルリポジトリに取得することができます。

```
$ git fetch
remote: Enumerating objects: 5, done.
remote: Counting objects: 100% (5/5), done.
remote: Compressing objects: 100% (2/2), done.
remote: Total 3 (delta 0), reused 0 (delta 0), pack-reused 0
Unpacking objects: 100% (3/3), done.
From https://github.com/hoge/sample
   7022b44..7beb0cd  master     -> origin/master
```

そして、ローカルリポジトリ`master`ブランチの最新のコミット`HEAD`とリモートリポジトリ(origin)の`master`ブランチとの差分を次のコマンドで確認できます。

```
$ git diff HEAD origin/master
diff --git a/README.md b/README.md
index 9daeafb..a099415 100644
--- a/README.md
+++ b/README.md
@@ -1 +1 @@
-test
+modify comment
```
README.mdファイルが修正されていて、ファイルの中身がローカルリポジトリでは「test」、リモートリポジトリでは「modify comment」となっているのがわかります。

もしローカルリポジトリの内容をリモートリポジトリに上書きして良いのであれば、`git push -f origin master`で強制的にプッシュします。

リモートリポジトリの内容をローカルリポジトリに上書きしたい場合は、`git pull origin master`で上書きされます。

差分を見て、どちらかの方法で内容を修正しましょう。