Git初心者のためのGitのあいまいさ完全に理解するガイド

# はじめに
Gitについて意外とあいまいな理解になっちゃいがちなところを正しく理解するためのガイドです。


# 前提
Gitの基本的な用語や操作はできることを前提にしています。その中で理解があいまいになってしまうところにフォーカスして解説をしています。

# ゴール
このガイドを読んだ効果は下の効果です。
- リモートとローカルの立ち位置を理解する

- 差分のステータスについて正しく理解する

- ブランチがどういうものか正しく理解する(リポジトリとディレクトリの違い)

# ローカルリポジトリとリモートリポジトリ
## 2つのリポジトリについて
Gitのリポジトリは2つ種類があります。

*_ポケ〇トモンスター リモートリポジトリ/ローカルリポジトリ_*

Gitを扱うときにはこの2つを理解する運命からは逃れられませんので正しく理解していきましょう。


### リモートリポジトリ
リモートリポジトリはその名の通り、リモートサーバのgitリポジトリのことです。

githubとかgitlabみたいなサービスを使ってる人はそれそのもののイメージ。そういったサービスを使っていなくて独自でgitのリモートリポジトリ用のサーバがある場合はそのリモートサーバになります。



### ローカルリポジトリ
ローカルリポジトリはその名の通り、実際に作業のPCにあるリポジトリのことです。

初めの勘違いポイントとして、

「リモートとローカルって普通おんなじもの管理してるんだから、リモートとローカルは同じリポジトリじゃないの」

って思っている人が多いかもですが、わざわざ別の要素として説明するぐらいなので全然別物だと思ってください。

ローカルリポジトリはあくまで作業PC内の差分を管理するためのリポジトリです。後で詳しく説明しますが、普通に使うとリモートのリポジトリの情報とローカルのリポジトリの情報を紐づけるのでおんなじっぽく見えちゃっています。なので、ローカルリポジトリ単体ではリモートのリポジトリとは何の関係もないただの差分管理ツールにしか過ぎないのです。

### まとめ
リモートリポジトリとローカルリポジトリは、おんなじっぽい振る舞いをしてるだけで実は全然違うもの

## リモートリポジトリとローカルリポジトリの関係性について
ここまででリモートとローカルのリポジトリは全然違うってことはわかってもらえたと思います。じゃあそれぞれどんな関係性があるのかってところを解明します。

### ローカルとリモートの情報をリンクさせる
普通にリモートからローカルPCにソースを持ってくる時はクローン(`git clone リポジトリのURL`)すると思います。これはリモートのリポジトリをローカルにコピーしていることになります。コピーしているのでローカルとリモートが一緒っぽく見えているって感じです。ローカルPCで初めからリポジトリを作成(`git init`)するとリモートとは紐づかないリポジトリを作成できたりします。

ローカルでできた差分がリモートに反映されないのは、リポジトリが別物だからなのはもうお分かりいただけたと思います。

じゃあどうやって反映してるの？っていうと、ローカルの情報を反映させる`git push`とリモートの情報をローカルに反映させる`git fetch(git pull)`コマンドの二つを使ってお互いに反映させています。

![remote](https://user-images.githubusercontent.com/62328713/161948954-d3fe4b7f-5da6-4d10-b08a-bcc42062e04a.png)
図1. リモートとローカルを反映させあう図

ローカルで何か差分がでてそれをリモートに反映させたいときには差分をコミットして(コミットについては後で詳しく説明します。)プッシュすることでリモートへ作成した差分を反映させます。

逆にほかの人がリモートへ入れた修正の情報をローカルに取り込みたいときはフェッチして情報を取ってきます。プルでも情報が取ってこれるのですが、プルはフェッチとマージの合わせ技なので情報を取ってくるのはフェッチが行っています。

### まとめ
ローカルとリモートは勝手にリンクしているわけではなくて、下記のようなことを行ってお互いに情報をリンクしている。
- プッシュ: ローカルからリモートへ反映させたいとき
- フェッチ: リモートからローカルへ反映させたいとき


# 差分のステータスについて

差分のステータスには主に大きく4つのものがあります。
- [アントラッキング](#アントラッキング)
- [アンステージング](#アンステージング)
- [ステージング](#ステージング)
- [コミット](#コミット)

## アントラッキング
ファイル、ディレクトリそのものがリポジトリの管理下にない状態です。

`git status`したときに`Untracked files`として赤文字でファイルが表示される状態です。

アントラッキング状態のファイルやディレクトリに変更があってもリポジトリはその変更を無視して差分変更をします。

リポジトリからすると知らない人がいるから無視している状態です。

## アンステージング
リポジトリの管理下にあるファイル、ディレクトリになんらかの変更があってリポジトリに管理対象だと認識させていない状態です。

`git status`したときに赤文字でファイルが表示される状態です。

アンステージングの状態のファイルはコミットしてもその変更がリポジトリに反映されることはないです。

リポジトリからすると変更あるけどよくわからんから無視している状態です。

## ステージング
リポジトリ内のファイル、ディレクトリの変更をリポジトリの管理対象と認識させる状態です。

`git add`コマンドでアントラッキングまたは、アンステージング状態のファイルやフォルダをステージング状態にすることができます。

ステージングされたファイル、ディレクトリはコミットするとリポジトリの管理対象になります。

ステージングされて初めてリポジトリは変更が自分の見るべきものだと認識して受け入れる準備ができた状態になります。

## コミット
ステージングされたファイル、ディレクトリをリポジトリの管理対象としてリポジトリに記録された状態です。
また、リポジトリにファイル、ディレクトリの変更を記録する動作もコミットといいます。

`git commit`コマンドでステージングされた変更差分をリポジトリに記録して、コミットされた状態にします。

リポジトリはコミットされるとその変更を自分のものとして扱うことができるようになります。

## それぞれのステータスの関係性
アントラッキング、アンステージングのファイル、フォルダは`git add`することでステージング状態へ移行します。`git commit`をするとステージングされているファイルやディレクトリをまとまった変更差分としてリポジトリに記録します。

```mermaid
graph BT
アントラッキング & アンステージング -->|git add| ステージング
ステージング -->|git commit| コミット
```


![差分ステータス](https://user-images.githubusercontent.com/62328713/178715850-3ea787c7-9255-4699-8006-429d6ba9603a.png)


## まとめ
差分の状態には4つの状態がある。
- アントラッキング
- アンステージング
- ステージング
- コミット

アントラッキング・アンステージング状態からステージング状態へ遷移させたものをコミットすることでリポジトリに記録された状態になる。

# ブランチ
ブランチは簡単に言うとコミットの集合になります。
なのでブランチは差分記録の集合体ということになります。

ブランチはコミットの集合体なので、ブランチを切り替えることでフォルダの実態もブランチに記録された内容にすべて置き換わります。

リポジトリも差分記録の管理をしているので、リポジトリとブランチが違うものと理解しつつも、正しく分けて考えられる人が意外と少ないのではないでしょうか？

## ブランチとリポジトリの違い
ブランチもリポジトリも差分の管理をしているという点では同じです。ブランチとリポジトリで違うのはその管理対象になります。

ブランチはあくまで複数のコミットを管理しているだけなので1つのブランチ内にほかのブランチを影響が勝手に出ることはないです。

リポジトリはコミットに加えてブランチ全体も管理しています。
管理対象がブランチとリポジトリでは違います。

リポジトリとブランチが同じに見える原因は、リポジトリは自分の管理下にあるブランチのどれか一つを現在見るべきブランチとして設定していることにあると思っています。リポジトリもブランチも差分管理をしていて、リポジトリはブランチと同じものを見ていることになるから混同してしまうんだと思います。リポジトリは全体管理をしていてその中のブランチの一つを見てると認識するとブランチとリポジトリの違いが明確になると思います。

## ブランチの利便性
ブランチの概念は難しいですが、扱えればとても便利なものです。

ブランチという概念がなかったら、1つのリポジトリをベースに複数の作業を同時に行いたい場合は持っている作業の分だけディレクトリを作る必要があります。

そこを解決してくれるのがブランチという概念になります。ブランチはリポジトリ内にいくつでも持つことができ、ブランチごとに別々の差分情報を管理することができます。なので、ディレクトリを分けなくてもブランチを切ってそれぞれのブランチにコミットを行うことで1つのディレクトリの中で別々の作業内容を保持しておくことができます。

それぞれのブランチが差分情報を管理しているので、ディレクトリをそれぞれ持つよりも軽量かつ簡単に作業内容の切り替えができます。

イメージとしては1つのリポジトリの中に仮想ディレクトリを作ることができて必要に応じてそれぞれの仮想ディレクトリに切り替えをしているようなイメージになります。

## まとめ
- ブランチはコミットの集合

- ブランチとリポジトリは別物

  リポジトリは持っているブランチの中の一つを見ているので同じものと誤解しやすい

- ブランチはリポジトリの中に仮想ディレクトリをいくつも作って必要に応じて仮想ディレクトリの切り替えを行うイメージ

  ディレクトリを複数作るよりも軽量かつ簡単に作業内容の切り替えができる便利なもの

# 終わりに
今回はgitの仕組みの中でイメージがつかみにくいものや勘違いしやすいものを例を用いて解説してみました。
特にブランチなどは初学者からすると何となく理解したつもりになりがちなものだと思っています。
今回の記事でgitへの理解を深めてより良いgitライフを送ってください！

## 追記
想像以上に見ていただけていて驚いています。ありがとうございます！
他にもGitのこういうこと知りたいみたいなところをコメントいただければ記事を書こうと思います。
また、質問や指摘等あればそれもコメントにお願いします