「Gitが、おもしろいほどわかる基本の使い方33」を読んでおもしろいなと思ったことを図解してみる

# 始めに
　読書メモです。タイトルにある「Gitが、おもしろいほどわかる基本の使い方33」を読んで、面白いと思ったことや知らなかったことを自分なりにまとめていきます。
都合上、自分の気になったところのみ抜粋して記載しています。ご了承ください。
(もしかしたら、一部抜粋して別の記事に統合するかも)

# ほんの紹介
　ちょっとだけ本の紹介をします(ステマじゃないですよ)。
この本は、Gitを使ってどのようにソース管理をしていけばいいか、分かり易く・段階的に解説しています。
沢山の図を用いて説明してくれる上、環境を整えるところから紹介してくれるのも嬉しいですね！

# マージによってコミットツリーはどう変化するか
　**マージ(merge)**とは、「中央リポジトリの最新情報を正として、自分のローカルリポジトリに取り込む作業」。ここでは、プルによって発生するマージを理解していきます。
### マージには3種類

**１．Already up-to-date**
　リモートリポジトリとローカルリポジトリに差分が無く、マージが発生しなかった。従って、コミットツリーも変化しない。自分が最後にプッシュしたとか、前回プルしてから次にプルするまでに、誰もプッシュしてなかったとか、そういう時に起きるやつ。
　つまり、マージしても何も起きない。

**２．Fast Forward**
　ローカルリポジトリが古いケースがこれに該当。つまり、リモートリポジトリが
　「ローカルが持っている情報が全て存在している」 & 「ローカルにはない情報をもっている」
という時とか。Fast Forwardな状態でのマージを図解してみると、 ↓ な感じかな
![git_merge.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/314204/e59a5685-c783-9911-8b3e-6c49aa0b7b2c.png)

 マージ前(左)と、マージ後(右)でのコミットの変化の図。丸いのがコミット、矢印が指すコミットがより新しいものを表す(厳密に正しい図ではないのでイメージ程度に[^1])。

Fast Forwardは要するに、**自分の持っている情報(ローカルリポジトリのコミット)が古いので、他の人が加えた情報(リモートリポジトリのコミット)を取り込んでリポジトリを最新化している**訳です。


**３．Auto merge**
　ローカルリポジトリとリモートリポジトリ、お互いに相手のリポジトリがもっていないコミットがある時のマージ。Fast Forwardの説明と同様に図を書いて、マージの前後を比較してみる。

![git_automerge.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/314204/34864d59-dd67-794e-1be5-467414dc3fe0.png)

状況として、ローカルリポジトリは最後にプルしてからコミットされていて、リモートリポジトリには既に他の人のコミットが反映されているとする(左図)。

　ローカルでプルを実行すると、ローカルになかったリモートのコミットを取り込む。更に自動で、ローカルとリモートのコミットから**マージコミット**を作る。こんな風にして、自分の変更と他人の変更をイイ感じ[^2]に合わせてくれる。
　マージコミットはコミットメッセージも自動作成のため、一旦リモートの変更をとりこんでその内容を確認したいときは、マージコミットを作らないようにしないといけない(あるいは、フェッチするか)。複数人で同じファイルを触っている時とかに使っていきたいですね(というか、そうするパターンの方が多そう、コンフリクトは怖いし)。


## マージに失敗したその時は
　同じファイルの同じ箇所に複数の変更が加えられた時、マージが失敗してエラーになってしまう(**コンフリクト**)。Gitは複数の変更の内、どれを採用していいか分からない訳です。コンフリクトした場合は、

　・人が解決(手動で修正)するしかない
　・マージの際にコンフリクトすると、そのファイルだけがマージされず、差分としてワークツリーに現れる
　・**コンフリクトしたファイルを修正してステージングすることで、コンフリクト解決**となる

解決方法があるとはいえ、コンフリクトを発生させないことが一番ですので、「**プッシュの前にプル**」するクセをつけると良さそうですね！

# ブランチを活用する
　そもそもブランチって何か、という話ですが、一言でいえば「ポインタ」[^3]。コミットの目印とでもいうべきか？ここでは、ブランチの運用についてまとめます(ブランチそのもの・コミットツリーについてもっと知りたい場合は、右記記事へ：[GitのHEADとは何者なのか](https://qiita.com/ymzk-jp/items/00ff664da60c37458aaa))。

![git_commitTree.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/314204/2620797f-66f3-6ece-6968-b05effb5a3a7.jpeg)
(図は「[図解！ Gitのブランチ・ツリーをちゃんと読む](https://qiita.com/jesus_isao/items/2a0495c973a4c911c2cc)」より引用)
上図で言うと、ブランチは「master」などの吹き出しみたいなやつのこと。



### ブランチを管理する
・使わなくなったブランチは消す
既にマージが完了して、そのブランチでの作業が完了した場合は、ブランチを消すこと！絶対にしないといけない操作でもないが、開発を進める内に切ったブランチが多すぎて見通しが悪くなる可能性があるとのこと[^5]。現在作業しているブランチを一覧からすぐに見つけるためにも、**使わなくなったブランチは消す!!**[^4]

. . . . . . そうは言っても、何かしらコミットに目印を付けたい時は**タグ**を使う。タグはコミットできないブランチみたいなもの[^6]。ブランチを消したくない場合は、代わりにタグを設定すると良いようです。


### スタッシュを使ってみる
スタッシュはワークツリーの変更を一時的に退避させておくための機能。あるブランチでの作業を一時中断して、別のブランチの作業をしたい時などに使う[^7]。退避させた変更を適用した際、コンフリクトする可能性がある操作、ということも理解しておく。

### 過去に行った作業をやり直す
ある時点のコミットまで戻って作業をやり直したいとき、方法は3つ。
　1. 過去のコミットに対して新規でブランチを作成する
　2. 既存のブランチを過去のコミットまで戻す(**リセット**)
　3. 特定の作業の逆のコミットをする(**打ち消し**)

1.は、対象のコミットを指定して、そこにブランチを作成するだけ。ちょっと動作確認したいだけなら、こちらの操作で十分。一方、2.はちょっと複雑。リセットも色々なモードがあって、それぞれ以下のようになる(参考3.より補足)。

　**Soft** : コミットのみ取り消し。ワークツリーとインデックスはそのまま。

　**Mixed** : コミットを取り消して、インデックスもクリーンする。ワークツリーはそのまま。

　**Hard** : コミットを取り消して、インデックスとワークツリーもクリーンする。

　この本では、リセットを「コミットを消失させる機能ではなく、あくまでブランチの指すコミットを変更しているだけ」(p. 136)と紹介していますが、参考3.や以降の説明を見る限り、実質的なコミットの削除のような気がしています[^8](ブランチの切り替え先のコミットより未来のコミットへ、再びブランチを切り替えようとしたとき、コミットのハッシュを指定しないといけないようなので)。
リセットする際は、コミットツリーの状態や最新のコミットのハッシュなどを確認しておく！

3.の操作は、1, 2とは打って変わって、作業の始めと終わりだけを見ればコミットができただけにしか見えない。取り消したいコミットを指定して打ち消しを行うと、対象のコミットで行った変更を無かったことにするコミットが一つできるだけ！[^9]


# 用語集
この章には、本に登場した用語の内で知らなかったものつらつら書いていきます。

・**ベアリポジトリ(bare repository)**
どのブランチもチェックアウトしておらず(というかできない)、ファイルの実体が存在しない(?)リポジトリ。
GitHubなどにホスティングしているリポジトリがこれに相当する。チェックアウトもコミットもできないが、プッシュはできる(なんで? => ワークツリーが存在しないから、らしい)。

・**HEAD**
現在作業しているブランチに付いているブランチ。チェックアウトによってこのブランチが切り替わっている。


# 参考
1. 大串肇ほか (2019) 「Gitが、おもしろいほどわかる基本の使い方33 改定新版」エムディエヌコーポレーション
2. Git公式ドキュメント : [2.6 Git の基本 - タグ](https://git-scm.com/book/ja/v2/Git-%E3%81%AE%E5%9F%BA%E6%9C%AC-%E3%82%BF%E3%82%B0), [3.1 Git のブランチ機能 - ブランチとは](https://git-scm.com/book/ja/v2/Git-%E3%81%AE%E3%83%96%E3%83%A9%E3%83%B3%E3%83%81%E6%A9%9F%E8%83%BD-%E3%83%96%E3%83%A9%E3%83%B3%E3%83%81%E3%81%A8%E3%81%AF)

3. [Gitを使っていて困ったことを軒並みメモしていく](https://qiita.com/leon-joel/items/a1ebe76f21c76e862b9b)


[^1]: 「じゃあ、記事にするなよ」と言われそうですが、本当にその通りですごめんなさい(6割くらい正しく説明ができればいいかな、と)。近いうちに修正予定です。

[^2]: Gitは**ファイルの差分を変更箇所単位(Hunk：ハンク)**で管理していて、ハンク同士が被っていなければ、上手い具合にマージしてくれています。では、上手いことマージできなかった場合は . . .

[^3]: gitのドキュメント(参考2.)にもさらっと書いてあるだけですが、結構大事なことの気が . . .

[^4]: ブランチを消しても、コミット自体が消えるわけではないです。(ポインタの削除なので)

[^5]: 何も消さなくても良いのでは、とも思ったが、一覧に表示されるブランチ = 現在進行している作業、と解釈すれば複数の作業をしているときに分かり易くなりそうですね。

[^6]: 注釈付きのタグを作成した場合、ポインタではなくGit データベース内に完全なオブジェクトとして格納されます

[^7]: ちなみに、変更を退避させずにブランチを切り替えても、コンフリクトなどのエラーにはならないらしい。とはいえ、できる限りワークツリーがクリーンな状態でチェックアウトすべき。

[^8]: とはいえ、リセットした後でプッシュせずにプルすればリセット前に戻るらしい。

[^9]: こういう操作ができるって知ったら、コミットはこまめにした方が良いなって思えますね！