Git コマンドをシーケンス図で理解したい

Git を操作するコマンドやらなんやらを調べればコマンドプロンプトなどで`$ git hogehoge` みたいなのが無限に出てくる割に，この操作がどうなっているのか図示している人はあまりいなかった．

Git で管理をしたことのない人間としては，コマンドを並べられるよりも図で示された方が分かりやすいと思う．図で覚えた方が，どのような操作をしているのか覚えやすいということもある．
ということで，[*mermaid.js*](https://mermaid-js.github.io/mermaid/#/) で描いてみた．[^sequenceDiagram]

[^sequenceDiagram]: 本記事で使用しているシーケンス図は自由に使ってもらって構いません．ただし，間違いがあった場合に関する図の流用で生じた被害の一切の責任は持ちません．（間違ってはいないはずですが．）

（対象が，“ちょっと分かっている人”でないと分からないような気がする......Git の用語も多く出てくるおかげで何が何やらという感が否めないが，その辺りは調べていただいて......）

**Git 初心者が爆速でGit & GitHub を理解して使えるようになるといいなと願いつつ．**


# ○ 本記事で図示すること

- Git でワークツリー上にローカルリポジトリを作成し，リモートリポジトリに上げる（push）
- リモートリポジトリから情報を取得してローカルリポジトリに展開する（clone, pull）

リモートリポジトリにはGitHub を想定しているが，どのようなリモートリポジトリでも問題ない書き方を心がけた．


## ■ 基本事項

- Git は`.git` というフォルダ内にプロジェクト ワークツリーのHistory を書き込んでいる．これをWeb 上のGitHub に上げて，ローカルとリモートの双方で管理しようとしている．（以下，ワークツリーのHistory を単にHistory と表記する．）[^history]
- ファイルやHistory を管理するところをリポジトリと言う．
 - ローカルリポジトリ：PC 内のリポジトリ
 - リモートリポジトリ：GitHub やGitLab など
- ブランチはプロジェクトの分岐であり，ベースとなるブランチはマスターブランチ[^main] と呼ばれる．（今回は`$ git branch` については触れない．）
- コマンド操作では`$` が頭に付いているが，今回ではワークツリーの`.git` のあるディレクトリ上で行うことを示している．

[^history]: なんだかHistory だなんて言葉を使っているが，「履歴」と読み替えてもまったく差し支えない．ちなみにHistory を見たいときには`$ git log` を使う．カタカナにしなかったのはヒステリーのように見えるのを避けるため．

[^main]: 人権的な意味合いから，master よりもmain の方がベターだと言う動きがあるらしい．現在の標準的な初期ブランチはmaster となっておりオプションで変更可能となっているようだ．（参考：[“master”は不適切？　デフォルトブランチ名の変更に対応した「Git for Windows」v2.28.0 - 窓の杜](https://forest.watch.impress.co.jp/docs/news/1267911.html)）

# ○ 準備

開発中のワークツリーをリモートリポジトリに上げたい．

1. リモートリポジトリを作成する
2. ローカル側でGit で管理するディレクトリに`.git` を作成する（init）
3. `.git` にリモートリポジトリを認識させる（remote add）


![git-preparation_v2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/590705/9bbfa24e-f00c-ed07-bf90-5ec85c11b9c8.png)


```
$ git init
$ git remote add origin [HTTPS or SSH]
```

`init` によって，ワークツリーに`.git` フォルダが作成される．このフォルダにはワークツリーのHistory が記録されていくことになる．隠しフォルダになっているので通常では見えない．

`remote add` では，リモートリポジトリは"origin" と名づけられている．慣習的にこのような名前で名づけられているようだが，他の名前にしても良い（らしい）．
また，複数のリモートリポジトリを`remote add` で紐づけることも出来るようだ．

ここでは，単にリモートリポジトリ先のHTTPS/SSH を特定の名前に置き換えているものと認識している．



# ○ リモートリポジトリを更新する（Push）

1. History として残すファイルをステージングする（add）
2. ステージングしたHistory をローカルリポジトリに更新する（commit）
3. 更新されたファイルとローカルリポジトリのHistory をリモートリポジトリ上げ，リモートリポジトリ上のファイルとHistory を更新する（push）

シーケンスでは`push` が2回の操作のようになっているが，実際は1回の操作で`push`が完了する．また，Index (Cache) [^index]とLocal Repository は双方とも`.git` に含まれている．

[^index]: ステージングする場所をIndex と言うが，歴史的な経緯からCache とも言うようだ．また，Staging area と呼称する人もいる．いずれの呼び方であっても混乱のしないようにしよう．

![git-push_v2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/590705/da6f8808-d380-c3df-9bca-5f047d5c5b74.png)


```
$ git add [files]
$ git commit -m "[comment]"
$ git push origin master
```

ステージングはコミットする前に複数回行っても良い．コミットする際にはそれまでにステージングされたHistory が更新される．

ステージングするファイルをいちいち特定するのが面倒な場合には，`-A` オプションでワークツリーに含まれるファイルすべてを選択することもできる．

```
$ git add -A
```

コミットする際にはコメントを残すことが出来る．というよりは，コメントを残す必要がある．`-m` オプションでコメントを入れることが出来る．
どのようなコメントをすべきなのかは他の記事に譲るが，変更点を簡潔に書いておけば良いだろう．（参考：[Gitのコミットメッセージの書き方 - Qiita](https://qiita.com/itosho/items/9565c6ad2ffc24c09364)）

プッシュするブランチは"master" にするとマスターブランチのHistory が更新される．[^conflict]
他のブランチが作成されている場合には，そのブランチ名にするとそのブランチのHistory が更新される．どのブランチを更新するのかは注意する必要がある．


<details><summary>`config` のエラーがあった場合（折りたたみ）</summary>
<div>
本当は準備の段階で`$ git config` についても触れるべきなのかもしれない．しかし，これも書くとどうも図で示したいという記事の体裁がアレな気がしたので，折りたたみでごまかします．

コミットしようとして`config` が無いよと警告されたら，ユーザ名とユーザのEメールアドレスを登録しておこう．これによって，誰がコミットしたのかを明らかにしている．

```
$ git config --global user.name [User Name]
$ git config --global user.email [User E-mail]
```

`--global` を`--local` にすれば，プロジェクト毎に決めることもできる．

`config` 関連でもさまざまなことをGit に与えられるようなので，余裕があるときに調べてみると良いだろう．（参考：[gitconfig の基本を理解する - Qiita](https://qiita.com/shionit/items/fb4a1a30538f8d335b35)）

</div>
</details>


# ○ リモートリポジトリを複製する（Clone）

リモートリポジトリにあるファイルとHistory をローカル側に複製する．このとき`.git` がディレクトリに作成されるので，ワークツリーをどこに作成するのか意識する必要がある．

1. リモートリポジトリのHTTPS/SSH を取得する
2. ローカル側にリモートリポジトリのファイルとHistory をクローンする（clone）


![git-clone_v2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/590705/553a9c97-2bff-0d79-9352-ab6f8a7717ef.png)


```
$ git clone [HTTPS or SSH]
```

ワークツリーを作成するディレクトリには注意する必要があるが，`.git` にはローカルのディレクトリのどこなのかを識別することはしていないようなので，`clone` してから任意のディレクトリに移動させることは出来るはずである．
移動する場合には，ワークツリーの構造を崩さないように注意する．次にプッシュしたときにリモートリポジトリのワークツリーの構造も変化してしまう．

# ○ リモートリポジトリと同期する（Pull: Fetch+Merge）

ローカルにあるプロジェクトとHistory をリモートリポジトリと同期したい．このときローカルにあるファイルは更新されるので，共同で開発している場合には意図していない編集が加えられている可能性もあることに注意．

1. リモートリポジトリからファイルとHistory を"Remote tracking branch" に複製する（情報の取得）（fetch）
2. "Remote tracking branch" に複製されたファイルとHistory をローカル側のファイルとHistory とに合わせる．（merge）

シーケンスでは`merge` が2回の操作のようになっているが，実際は1回の操作で`merge` が完了する．また，Local Repository とupstream branch は`.git` に含まれている．


![git-pull_v2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/590705/3c48056e-8164-776b-8eb8-ff06ab2b0419.png)


```
$ git fetch origin
$ git merge origin/master
$ git pull origin master
```

`fetch` でリモートリポジトリから情報を取得しているが，ローカル側には見た目の上では反映されていない．これは，"Remote tracking branch" に格納されているだけだからだ．（バイナリになっているので，人間では読めない）

上の場合では，"origin" のすべてのブランチの情報を取得している．
どのブランチをフェッチするのかを特定したい場合には，以下のようにする．このときにはマージも変わるので注意．[^conflict]

[^conflict]: リモートリポジトリ側で更新された情報とローカルリポジトリ側で更新された情報が競合（けんか）する場合がある．この状態をConflict と言う．

```
$ git fetch origin [branch name]
$ git merge origin/[branch name]
$ git pull origin [branch name]
```

`merge` することで，"Remote tracking branch" にあるファイルとHistory をローカル側のファイルとHistory に合わせる．

これらの操作を一気にするのが`pull` であり，**pull = fetch + merge** である．


# ○ 参考

Special Thanks!!

+ [GitHub](https://github.co.jp/)
+ [Git](https://git-scm.com/)
  - [Git - Book](https://git-scm.com/book/ja/v2) / 日本語
+ Qiita
  - [君には1時間でGitについて知ってもらう(with VSCode) - Qiita](https://qiita.com/jesus_isao/items/63557eba36819faa4ad9)
  - [図解！ Gitのブランチ・ツリーをちゃんと読む - Qiita](https://qiita.com/jesus_isao/items/2a0495c973a4c911c2cc)
  - [Gitの使い方メモ - Qiita](https://qiita.com/__init__/items/4729037bef5138155936)
  - [GitとGithubの基礎 - Qiita](https://qiita.com/kibinag0/items/ec6583e4e7608c4d2add)
  - [いまさらだけどGitを基本から分かりやすくまとめてみた - Qiita](https://qiita.com/gold-kou/items/7f6a3b46e2781b0dd4a0)
  - [GitHubに作成したレポジトリをVSCodeにプルからのプッシュするまで - Qiita](https://qiita.com/qrusadorz/items/9916644e1af1453fe30b)
  - [Githubに新規リポジトリを追加 - Qiita](https://qiita.com/sodaihirai/items/caf8d39d314fa53db4db)
  - [origin masterとorigin/masterの違い - Qiita](https://qiita.com/bitarx/items/4558f86e43a2c3493fa9)
  - [【初心者向け】git fetch、git merge、git pullの違いについて - Qiita](https://qiita.com/wann/items/688bc17460a457104d7d)
  - [GitとGitコマンド - Qiita](https://qiita.com/mocha_xx/items/5c0e2c4a877ed9c0e7c7)
+ Other
  - [WindowsにGitをインストールする方法 | サービス | プロエンジニア](https://proengineer.internous.co.jp/content/columnfeature/6893)
  - [サル先生のGit入門〜バージョン管理を使いこなそう〜【プロジェクト管理ツールBacklog】](https://backlog.com/ja/git-tutorial/)

# 余談

参照にQiita での分かりやすい解説を複数挙げておいた．しかしながら，記事として非常に長いものもあり，1回ですべてを読みつくそうとは思えない．初見ではよく分からないところもある．分かりやすいけれど．
その意味では，図で示した方が分かりやすいものとなっているような気がする．図だけ覚えて帰ってもらってもいいと思う．間違いがあったらごめんなさい．

個人的なプロジェクトのバックアップ程度であれば，本記事で十分活用できると思われる．（branch の説明がないﾄｶﾄｶ）GitHub のリポジトリもPrivate が無料で使うことが出来るようになった．活用していこう．

プルやプッシュはステージングやフェッチの段階を踏まないとローカルリポジトリを更新できないようになっている．操作としては面倒に感じるかもしれないが，ローカルリポジトリは直接操作しないというのがGit 製作者の思想らしい．

楽しいGit & GitHub ライフを！