【Git】GitとGitLab/GitHubの基礎をわかりやすくまとめてみた

## 目次
- [はじめに](#はじめに)
- [Gitとは](#Gitとは)
- [Gitのリポジトリとブランチの構成](#Gitのリポジトリとブランチの構成)
    - [Gitのリポジトリの構成](#Gitのリポジトリの構成)
    - [Gitのブランチの構成](#Gitのブランチの構成)
- [Gitの2種類のマージ](#Gitの2種類のマージ)
- [fetchとpullの処理について](#fetchとpullの処理について)
- [コンフリクトについて](#コンフリクトについて)
    - [コンフリクト発生時の対処](#コンフリクト発生時の対処)
    - [コンフリクトの発生を抑える方法](#コンフリクトの発生を抑える方法)
- [GitとGitLab/GitHubの違い~forkとマージ/プルリクエスト~](#GitとGitLab/GitHubの違い~forkとマージ/プルリクエスト~)
    - [forkについて](#forkについて)
    - [マージ/プルリクエストについて](#マージ/プルリクエストについて)
    - [GitとGitLab/GitHubの違いを意識しよう](#GitとGitLab/GitHubの違いを意識しよう)
- [よく使うgitコマンド集](#よく使うgitコマンド集)
- [Gitの勉強おすすめサイト](#Gitの勉強おすすめサイト)
## はじめに
Gitを感覚だけでいじっている方は多いのではないでしょうか？

- クローンとコミット＆プッシュくらいしかGitのこと知らない
- マージ、フェッチ、プルとかよく聞くけどあんましわかってない

みたいな人にGitの基礎を理解して欲しいという目的で書きました。

## Gitとは
Gitの原型は2005年にリーナス・トーバルズによって作られました。
リーナスはLinuxの生みの親として有名ですが、Linuxカーネルの開発時にソースコード管理ツールが必要となり、Gitが開発されました。
リーナスは2週間でGitを作成したという逸話があります。LinuxだけでなくGitも「チョットデキル」のでしょうね。

Gitの略史に関しては、以下の2つの記事で詳しく語られています。
[Git略史](https://git-scm.com/book/ja/v2/%E4%BD%BF%E3%81%84%E5%A7%8B%E3%82%81%E3%82%8B-Git%E7%95%A5%E5%8F%B2)
[Linusが2週間でgitを作った話。](https://hyoshiok.hatenablog.com/entry/20140729/p1)

そんな2週間で作られたGitは、分散型バージョン管理を行っています。分散型では各々がリポジトリを持つという点が集中型バージョン管理と異なります。
Gitでは分散型バージョン管理によって、効率的な開発を行える仕組みになっているのです。


## Gitのリポジトリとブランチの構成
### Gitのリポジトリの構成
Gitにはローカルリポジトリとリモートリポジトリが存在します。

リモートリポジトリ：GitLabやGitHubで管理される中央集権的なリポジトリ
ローカルリポジトリ：各々のPCで保有するリポジトリ

各々がリポジトリを保有できるという点が分散型バージョン管理の特徴となっています。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3339625/94abdfe6-f316-8945-8444-ba6b70ecdaf3.png)

ここで押さえておきたいのは、ローカルリポジトリとリモートリポジトリの通信はオンラインで行われるという点です。オフラインでは通信は失敗します。
ローカルリポジトリ内で行われる処理なのか、リモートリポジトリと通信する処理なのかを考えることで、オフラインでできるかどうかが判別できます。
例えば、cloneやpushはリモートとの接続がありますが、commitやcheckoutはリモートとの接続は不要です。
自身の打ったgitコマンドがリモートリポジトリとの通信をしているかを考えてみるのが、gitの理解に繋がると思っています。

ちなみに、基本的にリモートリポジトリの変更はローカルリポジトリの差分をpushすることで反映しますが、直接リモートリポジトリを変更することも可能です。
GitLabでは「Web IDE」、GitHubでは「Codespaces」の機能を用いることで実現出来ます。

【赤枠でリモートリポジトリをIDEで編集可】
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3339625/a57a559a-a000-62df-2a47-c7a07d8b7804.png)

余談ですが、GitLabでは以前までリモートリポジトリを直接編集する際に、GitLab専用のIDE（使いづらい）が使われていました。ですが2022年12月以降にVSCode仕様での編集ができるようになりました。
個人的にはあまり使うことはなさそうですが、、、
[GitLab、Visual Studio CodeベースのWeb IDEをβ版として提供開始。ターミナルからリモート環境へアクセス可能に](https://www.publickey1.jp/blog/22/gitlabvisual_studio_codeweb_ide.html)


### Gitのブランチの構成
Gitのローカルリポジトリ内のブランチは「ローカルブランチ」と「リモート追跡ブランチ」に大別されます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3339625/3cac412e-e377-1659-e3dc-e5c0791757e9.png)
リモート追跡ブランチはリモートリポジトリのブランチをローカルに反映したブランチで、これがあることでローカル上でリモートの内容を反映することができます。

VSCodeでGitのブランチを確認すると、以下の画像のようなブランチが確認できます。このように、リモート追跡ブランチがあることによってローカル上でもリモートのブランチを確認することができるのです。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3339625/dfc932c3-de66-c8cc-cade-eb9eaa796869.png)

リモートリポジトリとローカルリポジトリ
ローカルブランチとリモート追跡ブランチ
これらの用語を抑えた上でgitの処理を見ていくと理解がしやすいので、用語はしっかりと理解しておきましょう。

以下の記事でもわかりやすく説明しています。
[【Git】リモートからの取得とリモートへの反映で行っていること](https://qiita.com/forest1/items/db5ac003d310449743ca)

## Gitの2種類のマージ
Gitでは、ブランチを統合する際にmergeという処理を行います。
マージには大きく2種類あります。

- 3ウェイマージ
mainブランチからfeatureブランチを切ったケースを考えてみましょう。
featureで開発を進めている間にもmainでもコミットが行われていたとします。そうした場合に3ウェイマージが行われます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3339625/0828dc06-3dcc-352d-91ac-07e27c505f59.png)

①二つのブランチの共通祖先となるコミット
②マージ元ブランチのコミット
③マージ先ブランチのコミット

3ウェイマージでは①と②の差分と、①と③の差分をマージするブランチに取り込みます。
この差分が被った場合にコンフリクトが発生します。

- Fast-forwardマージ
先程と同様にfeatureブランチを切ったとします。その時にmainでコミットを行うことなくfeatureをマージしたときにFast-forwardマージになります。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3339625/5325769d-7eea-55b0-9921-dfc5c15e3a52.png)

Fast-forwardマージではmainのコミットがfeatureブランチの最新のコミットに移動することで実質的なマージをしたという扱いを取ります。
3ウェイマージのように分岐したブランチを回収するような形にはなりません。以下が誤ったFast-forwardマージの例です。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3339625/bf1d46fb-80b2-d94c-3fe9-7d9db8e78158.png)

Fast-forwardマージでは最終的に3ウェイマージのような分岐をせずコミットツリーが一本線になるのが特徴です。

ちなみにFast-forwardマージをコマンドで行った場合、ターミナルでFast-forwardマージであることが明示されます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3339625/87fa1c94-7a4e-34a3-8e04-9d0579c747aa.png)

## fetchとpullについて
今までの用語を抑えた上で、Gitの基本操作であるfetchとpullについて説明します。
- fetch
フェッチは最新のブランチ状況を取得する処理です。
ローカルリポジトリでフェッチを行うと、現在のリモートリポジトリのブランチ状況を参照し、リモート追跡ブランチを更新します。
- pull
プルはリモートリポジトリの該当ブランチにおける最新の状態をローカルに反映する処理です。
ローカルリポジトリでプルを行うと、まずフェッチが行われ、該当ブランチのリモート追跡ブランチが更新されます。その後、ローカルブランチに対して最新化したリモート追跡ブランチをマージします。こうしてローカルの該当ブランチがリモートリポジトリと同期が取れた状態となります。
つまり、プルはフェッチ＋マージの処理となるのです。

## コンフリクトについて
### コンフリクト発生時の対処
Gitのコンフリクトは二つ以上の異なる変更が同じ部分に対して行われたときに起きます。
例えば、同じファイルの同じ行を2人の開発者が別々に変更し、それらをマージしようとしたとき、Gitはどちらの変更を優先すべきか判断できず、コンフリクトが発生します。

コンフリクトを解消するには、コンフリクト箇所を手動で訂正する必要があり、時間が掛かります。
いちおう片方の編集だけを全面的に適用する機能がありますが、コンフリクト発生時はできる限りコンフリクトの箇所をしっかりと把握し、原因を理解した上で訂正をしましょう。

### コンフリクトの発生を抑える方法
コンフリクトの対処は面倒かつコードの品質低下に繋がります。なのでコンフリクトの発生をできる限り抑える必要があります。

方法として主に二つあります。
- 機能（feature）単位でブランチを切る
- 定期的にリモートリポジトリと同期を取る

二つを詳しく見ていきましょう。

#### 機能（feature）単位でブランチを切る
ブランチを機能単位で切ることで、他機能を作成しているブランチと同じ箇所を編集する可能性が低くなります。
機能単位で編集者を分ければ、同じファイルを編集することが少なくなるからです。

逆にutilパッケージ下のファイルなどの、機能を横断して利用されるファイルを編集した場合はコンフリクトが発生する可能性は高いので気をつけましょう。

#### 定期的にリモートリポジトリと同期を取る
もし同じブランチに対して複数名が編集を行っている場合は、定期的にプルを行いましょう。
定期的なプルを行わず、ローカルリポジトリとリモートリポジトリで同期が取れていないと、プッシュ時にnon-fast-forwardエラーを発します。
non-fast-forwardエラー時はプルをして対処してください。
その時にコンフリクトが発生したら手動で直しましょう、、、（またはブランチの分け方を考え直す必要があるかもしれません）

mainからfeatureブランチを生やしたとして、mainがそのfeatureをマージするときにはコンフリクトの注意が必要です。
コンフリクトはコードの品質低下にも繋がるので、できるだけmain側でコンフリクトを対処したくありません。
よってmainがfeatureをマージする前に、featureがmainの内容をマージし、コンフリクトの可能性があるかを確認する作業を行った方が良いです。
feature側でコンフリクトを対処し、main側では絶対に安全なマージができるという状態にすることで、main側のコードの品質を落とすリスクが低減します。
ブランチ間の同期を定期的に取っておくというのも対策の一つなのです。

なお、マージ元（main）でのコンフリクトを抑えるためにマージ先（feature）が定期的にマージ元をマージして都度コンフリクトを解消するという方法は、令和5年春期の応用情報技術者試験にも出題されています。
午後問題の問8（情報システム開発）の設問5にて、devlopブランチでコンフリクト（試験内では競合）が発生しにくくするために、「最新状態のfeatureが最新状態のdevelopブランチを随時マージする」という運用ルールを定めることを問うていました。
[令和5年春期　応用情報技術者試験　午後問題](https://www.ipa.go.jp/shiken/mondai-kaiotu/ps6vr70000010d6y-att/2023r05h_ap_pm_qs.pdf)

## GitとGitLab/GitHubの違い~forkとマージ/プルリクエスト~
Gitはバージョン管理を提供するツールで、GitLab/GitHubはGitをベースとしたWebベースのプラットフォームです。
GitLab/GitHubで管理するリモートリポジトリを中央集権的なリポジトリとして扱います。
そうしたリモートリポジトリの特性を活かした機能がforkとマージ/プルリクエストになります。

### forkについて
GitLab/GitHubでフォークを作成することで、該当のプロジェクトの内容をコピーした新たなプロジェクトを作成できます。
Forkしたリポジトリでは、元のリポジトリに影響を与えることなく、自由にコードを改変することができます。

また、元のリポジトリでソース編集権限を与えられていない人でも、fork先でリポジトリの編集権限を与えることができます。
権限を含め、元のリポジトリを意識することなく編集ができるのです。

ちなみに、GitLabではプライベートなプロジェクトに対して5人しかメンバーを割り当てられませんが、forkを使うことで異なるプロジェクトとして管理できるので、fork先にて新たに5人以内のメンバーを割り当てられます。
forkを複数用意することで大勢のチーム開発を実現出来るので小技として覚えておきましょう。

なお、fork先のリポジトリを元のリポジトリに取り込む際には、マージ/プルリクエストを行うことで取り込みができます。

### マージ/プルリクエストについて
GitLabではマージリクエスト（Merge Request）、GitHubではプルリクエスト（Pull Request）と呼ばれる機能では、マージ前にソースコードの変更をレビューすることができます。名称に違いはありますが機能に差はないです。

※あくまで考察
Fork元がFork先を回収するのは基本同一名のブランチのため、動きとしてはプルになります。GitHubではFork時のレビューに重きを置いてプルリクエストと名付け、GitLabでは異なるブランチのマージ時のレビューに重きを置いてマージリクエストという名付けになっていると思われます。
僕と同じ考察をしている方がいました→[プルリクエストとマージリクエストと。](https://irof.hateblo.jp/entry/2019/09/29/201813)

Gitではただマージをするだけでしたが、GitLab/GitHubではチーム開発のためにマージ前（または定期的なタイミング）にレビューを行える制度を作ったのです。
変更を可視化し、よりよいコードを編集できるようアドバイスする環境を作ることで、よりよい品質の開発を行えるようにしました。

### GitとGitLab/GitHubの違いを意識しよう
フォークやマージ/プルリクエスト以外にも、権限設定やCI/CD、issues管理など、GitLab/GitHubではGitの機能を活かしてよりよいチーム開発を行うための機能が追加されています。
このときに注意が必要なのは、GitLab/GitHubの追加機能はGitの機能ではないということです。
何が言いたいかというと、「フォークを行う」だったり「マージ/プルリクエストを行う」といったgitコマンドは存在しません。

自分がやろうとしている処理が、Gitの機能ならばコマンドを打ち、GitLab/GitHubの機能の場合はコマンドではできないという意識を持ちましょう。

## よく使うgitコマンド集
個人的によく使うgitコマンドをまとめておきます。

- git checkout ローカルブランチ名
ブランチを移動したいときに使います。

- git checkout -b 新規のブランチ名
bオプションを付けることで、git branch 新規ブランチA+git checkout 新規ブランチAの処理をやってくれます。
つまり、新規ブランチを作りかつそのブランチに移動するという処理です。

- 「git add .」+「git commit -m "コミットメッセージ"」+「git push」
「add .」でカレントディレクトリ配下の変更or新規追加ファイルをaddステージにあげ、commitでコミットします（mオプションはコミットメッセージをその場で付けるためのオプション）。
最後にコミットをプッシュしてリモートリポジトリに反映します。
- git pull
編集中のブランチに対応するリモートリポジトリをプルします。
編集中ブランチが新規作成のもので、まだリモートに反映していない場合は失敗します。
- git checkout ファイル名
addしていない編集したファイルを、最新コミットの状態（編集前の状態）に戻します。
IDEでロールバックという処理を行えば簡単にできるのでそちらの方が速いかもしれません。

## Gitの勉強おすすめサイト
これまでGitの基礎を書きましたが、まだまだGitの全てを網羅できたわけではありません。
Gitは使いながらその都度勉強するというスタイルが良いと思います。

ですがトラブル時に基本的な知識が無かったら困ってしまうので、基礎的な部分は事前に他サイトで学んでおきましょう。おすすめサイトをいくつか紹介します。

### マンガでわかるGit～コマンド編～
萌え～な絵でGitコマンドを教えてくれます。
1話にてGUIだけでGitを使える気になっている女の子に警鐘を鳴らしているのが良いです。
[第1話 リポジトリを作ってコミットしてみよう【連載】マンガでわかるGit ～コマンド編～](https://www.r-staffing.co.jp/engineer/entry/20190621_1)

### 東工大の講義
東工大の講義がYoutubeに挙げられており、Gitを扱っている回があります。
GitとGitHubを別の講義で扱っているのが好印象。
[システム開発プロジェクト応用第一 第5,6回 Gitによるバージョン管理（Youtube）](https://www.youtube.com/watch?v=WMIiPcgGC4Q)
[システム開発プロジェクト応用第一 第8,9回GitHub & Pull Request（Youtube）](https://www.youtube.com/watch?v=ALQvBsWQ2dA)

### mixi研修動画
mixiは研修動画をYoutubeで公開しており、その内のGit回です。
プルリクエスト時のコメントは丁寧に書きましょうなど、実務に沿ったアドバイスも行っています。
[Git研修講義【ミクシィ22新卒技術研修】
](https://www.youtube.com/watch?v=AQwj5z9TOJM)

### Learn Git Branching
gitコマンドの練習をするときに使えるサイトです。
gitコマンドを視覚的に確認できます。
[Learn Git Branching](http://k.swd.cc/learnGitBranching-ja/)

以上です。
他にもこんなおすすめの勉強サイトがあるなどあればコメントで教えてくれると嬉しいです^^

## 参考文献
- Gitの歴史
    - [Git略史](https://git-scm.com/book/ja/v2/%E4%BD%BF%E3%81%84%E5%A7%8B%E3%82%81%E3%82%8B-Git%E7%95%A5%E5%8F%B2)
    - [Linusが2週間でgitを作った話。](https://hyoshiok.hatenablog.com/entry/20140729/p1)
- 分散型バージョン管理と集中型バージョン管理の違い
    - [GitとSVNの違いとは？](https://aslead.nri.co.jp/products/gitlab/column/differences-between-git-and-svn.html)
- Gitのリポジトリとブランチ構成
    - [GitLab、Visual Studio CodeベースのWeb IDEをβ版として提供開始。ターミナルからリモート環境へアクセス可能に](https://www.publickey1.jp/blog/22/gitlabvisual_studio_codeweb_ide.html)
    - [【Git】リモートからの取得とリモートへの反映で行っていること(fetch,pull,push)](https://qiita.com/forest1/items/db5ac003d310449743ca)
- コンフリクトについて
    - [令和5年春期　応用情報技術者試験　午後問題　問8「バージョン管理ツールの運用」](https://www.ipa.go.jp/shiken/mondai-kaiotu/ps6vr70000010d6y-att/2023r05h_ap_pm_qs.pdf)
- GitとGitLab/GitHubの違い
    - [プルリクエストとマージリクエストと。](https://irof.hateblo.jp/entry/2019/09/29/201813)
- Gitの学習にためになるサイト
    - [マンガでわかるGit ～コマンド編～](https://www.r-staffing.co.jp/engineer/archive/category/%E3%83%9E%E3%83%B3%E3%82%AC%E3%83%BBGit)
    - [システム開発プロジェクト応用第一 第5,6回 Gitによるバージョン管理（Youtube）](https://www.youtube.com/watch?v=WMIiPcgGC4Q)
    - [システム開発プロジェクト応用第一 第8,9回GitHub & Pull Request（Youtube）
](https://www.youtube.com/watch?v=ALQvBsWQ2dA)
    - [Git研修講義【ミクシィ22新卒技術研修】
](https://www.youtube.com/watch?v=AQwj5z9TOJM)
    - [Learn Git Branching](http://k.swd.cc/learnGitBranching-ja/)