初心者向けgitのあれこれ

<h2> はじめに</h2>
こんにちは！
今年の DMM WEBCAMP Advent Calendar 2023:christmas_tree:12/4の担当します。<br>
今回のQiitaの執筆が初めてになります。

https://qiita.com/advent-calendar/2023/infratop

そこそこの頻度で、Gitで躓く人を見かけるので今回書いてみました！

<h2>目次</h2>

1. そもそもGitってなに？？
2. git add /git commit が行っていること
3. よくわからない時に使えるコマンド
4. 今まで説明したコマンドの優位性
5. エラーを紐解く

<h2>1. そもそもGitってなに？？</h2>

簡単に説明すれば、**バージョン**を管理するもの
もっと詳しく言えば、**差分**を管理するもの
差分とは、二つのデータ集合を比較して、両者の間で異なっている部分のことです

多分、これだけだとイメージが付かないと思うので例
```diff
データ.txt
  aaa

データ.txt
  aaa
+ bbb
```
この時の差分は、bbbになります(gitだとスペース1個単位で認識されます。)
```git diff```コマンドを使用すると差分のあるファイルを表示してくれます。

<h2>2. git add /git commit が行っていること

**関係図**

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3625910/c0000b92-95ea-b4ba-9131-e27fc1d9a343.png)


用語説明
working dir ... 自分が作業する場所（ファイル変更を行う場所）
ステージ ... commitしたい差分だけを一時的に置ける場所
ローカルリポジトリ ... indexにある差分を永久保存場所
リモートリポジトリ ... github上にあるもの

つまり、上からわかることは
working dirで作業を行い、addすると作業分の差分がindexに一時的に置かれる
commitすることでindexの差分が正式にローカルリポジトリに保存される

もっと詳しく知りたい人は以下のリンクを参照してください↓

https://zenn.dev/kaityo256/articles/inside_the_index


<h2>3. よくわからない時に使えるコマンド</h2>

**現状を把握できるコマンド**

```
git log --oneline
``` 
**commit履歴とHEADの位置の参照ができる**
読み終わったときに、このコマンド優秀ってなります。(多分...)

```
git status
```
簡単にまとめると以下3点(画像参照推奨)
- ```git add```したけどまだ、git commitされてないファイル一覧の表示(Changes to be committed: )
- 編集・削除・更新されているけど、まだ ```git add ```されてないファイルの一覧(Changes not staged for commit:)
- gitで管理されてない、かつ.gitignoreで管理対象外にもされてないファイルの一覧(Untracked files:)

```bash 
$ git status
On branch main
Changes to be committed: 
  (use "git restore --staged <file>..." to unstage)
        modified:   "A\343\203\207\343\203\274\343\202\277.txt"

Changes not staged for commit:
  (use "git add <file>..." to update what will be committed)
  (use "git restore <file>..." to discard changes in working directory)
        modified:   "B\343\203\207\343\203\274\343\202\277.txt"

Untracked files:
  (use "git add <file>..." to include in what will be committed)
        "C\343\203\207\343\203\274\343\202\277.txt"
```

これだけ…？と思った人は、このまま読み進めてください。

<h2>4. 今まで説明したコマンドの優位性</h2>

1. commit履歴が見れると何がいいの？
過去の履歴がわかれば、過去のバージョンまで戻ることができる。
例えば、注文機能までのcommitではバクが無いけどそれ以降のバグが多すぎて困っている。とりあえず、そこまで作業が進んでないからバクが無いところまで戻そう！みたいな感じで使う。
~~巻き戻しの説明するのが面倒くさいので~~参考リンク↓

https://www.r-staffing.co.jp/engineer/entry/20191129_1

2. HEADの位置が見れると何がいいのか？
 - リモートリポジトリのバージョンがわかる
 - ローカルリポジトリのバージョンがわかる
 **headと書かれている位置が今自分がいる場所**のことです。
これだけだと良くわからないと思うので素晴らしさを知ってもらいます。

<h2>5. エラーを紐解く </h2>

Gitでよく見るreject (non-fast-forward)エラー
```bash
$ git push origin main
To github.com:*******
 ! [rejected]        main -> main (non-fast-forward)
error: failed to push some refs to '******/qiita.git'
hint: Updates were rejected because a pushed branch tip is behind its remote
hint: counterpart. If you want to integrate the remote changes, use 'git pull'
hint: before pushing again.
hint: See the 'Note about fast-forwards' in 'git push --help' for details.
```


これは、なぜ起こるのか？
簡単に説明すると
**ローカルリポジトリのバージョンがpush先のリモートリポジトリよりも古いこと**

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3625910/6620a155-7e2c-f643-cf13-5ea3115f7728.png)

orderブランチを切ったタイミングのmainブランチのバージョンがm1
fixブランチで修正した人がpushするとmainブランチのバージョンがm2になる
orderブランチから新しくpushしようとするとmainブランチのバージョンがm3になる
この時、mainブランチのバージョンがm2になっているためブランチを切ったタイミングのバージョンと異なっている
言い換えれば、**別の人が同じブランチにすでにプッシュしてしまった場合、Gitに自分の変更をプッシュできない**

手順
これを解決するためには、バージョンを揃えてあげればいい
1. リモートリポジトリの最新情報をローカルに取得する(```git fetch```)
2. ローカルに取得した最新情報をpushしたいブランチにmergeする(```git merge remote/origin/リモートリポジトリのブランチ名```)
3. リモートリポジトリにpushする

解説
1. リモートリポジトリの最新情報をローカルに取得する(```git fetch```)
ローカルの環境とリモートの環境とバージョンを同じにする(図であればm2になる)
```git fetch```した後に、```git branch -a```で確認するとリモートから取得できた情報には全て```remote/~~```と書かれている。

2. ローカルに取得したリモートの最新情報をpushしたいブランチにmergeする(```git merge remote/origin/リモートリポジトリのブランチ名```)
リモートリポジトリの最新情報をローカル環境に取り込んだ後、希望のブランチにmergeするとコンフリクトが起こることが良くあります。
~~コンフリクトの詳しい説明めんどくさいので~~参考リンク↓

https://www.engilaboo.com/git-conflict/#rtoc-1

3.リモートリポジトリにpushする
git add .
git commit -m "~~"
git push origin ~~

これでエラーの解決ができます🎉。

<h2>追記 +ab</h2>

+a 
reject エラーが出た時にgit push -f コマンドを使うタコ（🐙）がいますがやめましょう
**歴史改変が起きて大変なことになります。安易に ```git push -f ```コマンドを使うのはやめましょう**。~~もし、僕の知り合いが行っていたらお説教をするレベルです。~~

+b
手順1と手順2をpullで行えばいいじゃん！とおもったそこのあなた
良く勉強していると思います。git pullとは2つのことを同時に行ってます。
``` git pull = git fetch + git merge ```
したがって、```git pull```で代用可能です。
しかし、稀にgit pullするとこんな"エラー"が出ます

```bash
From xxxxxxxxx
 * branch            feature/1 -> FETCH_HEAD
hint: You have divergent branches and need to specify how to reconcile them.
hint: You can do so by running one of the following commands sometime before
hint: your next pull:
hint: 
hint:   git config pull.rebase false  # merge
hint:   git config pull.rebase true   # rebase
hint:   git config pull.ff only       # fast-forward only
hint: 
hint: You can replace "git config" with "git config --global" to set a default
hint: preference for all repositories. You can also pass --rebase, --no-rebase,
hint: or --ff-only on the command line to override the configured default per
hint: invocation.
fatal: Need to specify how to reconcile divergent branches.
```

これは、**エラーではありません**
これは、**警告 = warning です**

上の意味を軽くまとめると以下4点
- git merge 
- git rebase 
- git fast-forward only
この中のいずれかを使用してください！！
- または、configを用いてgit pullするときにpull.rebaseかpull.ffなのかを事前に設定してからgit pullコマンド使ってね～とのこと
つまり、git pullだと3つのうちどれかわからないからconfigで事前にどれに該当してるのか設定しておいてね～とのことでした。

**gitのターミナルで返信される多くのエラーやwarningには大量のヒントが隠されています**
**エラーやwarningは最後まで丁寧に読みましょう!!**

<h2>まとめ</h2>

git は差分管理システムである
git log (commit履歴やheadの位置を教えてくれる)
git status (ファイルの状態を教えてくれる)
git fetch (リモートリポジトリの最新情報をローカルに取得できる)
git merge (自分の今居るブランチに任意のブランチを指定することで任意のブランチ情報を取り込める、この時、remoteのブランチも可)
git pull (git fetch + git merge)

<h2> 終わりに </h2>
いかがだったでしょうか？
gitの操作で行う上で一番大切なことは図で書いたようなネットワーク図をイメージしてコマンドを実行していくことです。ネットワーク図のイメージが掴めたら、git log/git status/エラー文を用いてtry and errorを繰り返して経験を付ければgitを使いこなせるようになると思います！

この記事を読んでくださった皆様が快適なgitライフを送れることを願っています！👍