Subversion悩が学ぶGit

## 背景
時代に取り残されたSubversion（SVN）脳がGitを使った際に混乱した部分を整理。
（簡単という人の方が多いと思いますが、
　似たように苦戦している人の力になれればと思っています）

## 最低限抑えること
SVNとGitの違いは他にもあるが、通常利用する上で抑えるべきと感じたのは以下の通り

| 特徴                | SVN                                      | Git                                      |
|---------------------|------------------------------------------|------------------------------------------|
| リポジトリ構成      | サーバにリポジトリが存在                  | サーバとローカルにリポジトリが存在 |
| 修正の反映          | サーバに直接反映(commit)                    | ローカルに反映（commit）し、後でサーバに反映（push） |
| 修正の取込          | サーバから直接反映(update)                      | リモートに反映（fetch）してローカルに反映（merge）または同時に反映（pull）    |
| 物理ファイル操作    | ブランチ毎にディレクトリを作成     | 1つのディレクトリでブランチを切替  |
| 履歴操作            | 書換えは基本的に不可                      | 書換えや修正が可能 (rebase, amend)       |

Gitの優れた点ではあるが、
Subversion脳にとって**ローカルリポジトリの存在**が理解するのに苦労した点。

次からは、Gitを理解する上で苦労したと感じた点への解決方法を記載。

## 現状把握
（SVNではブランチ毎にファイルが物理的に分かれているので基本使わない手順①）
```sh
# 現在のリポジトリ内のすべてのローカルブランチを一覧表示
git branch
# 現在のローカルブランチ(*)のHEADを含むすべてのブランチを表示
git branch --contains
```

## ブランチの切替
（SVNではブランチ毎にファイルが物理的に分かれているので基本使わない手順②）
```sh
git branch
# 出力例:
#  feature/branch001
#* feature/branch002
#  main

# feature/branch002 ⇒ feature/branch001
git switch feature/branch001

git branch
# 出力例:
#* feature/branch001
#  feature/branch002
#  main
```

## シチュエーション別

#### 修正の反映（Branchの修正をサーバ上に反映）
（SVNでは作業用ブランチの修正をコミットする流れに相当）
ローカルリポジトリに反映してからサーバリポジトリに反映する
```sh
# ステージング設定（ファイルが存在するディレクトリで実行）
git add manual.md
# 取り消す場合は、「git reset HEAD manual.md」

# ローカル（ステージング）への反映
git commit -m "manualを追加"

# 現在のローカルブランチ(*)のHEADを含むブランチは他にないので
# 「git branch --contains」は必ず１つ
git branch --contains
# 出力例:
#* feature/branch002

# 「git branch」はブランチの状態は関係ないので結果は変わらない
git branch
# 出力例:
#  feature/branch001
#* feature/branch002
#  main

# サーバへの反映
git push

```

#### 修正の反映（Branchの修正をサーバ上の上位Branchに反映）
（SVNでは作業用ブランチの修正を上位Branchにマージする流れに相当）
ローカルリポジトリに反映してからサーバリポジトリに反映する


```sh
# 上位Branch（main）に切り替え
git switch main

# origin(main)にfeature/branch002の修正をpull
#（fetch&mergeの流れもあるがここではpullの例）
git pull origin feature/branch002

# 現在のローカルブランチ(*)のHEADがmainに反映されたので
# 「git branch --contains」はマージ元のローカルブランチと上位Branch
git branch --contains
# 出力例:
#* feature/branch002
#  main

# 「git branch」はブランチの状態は関係ないので結果は変わらない
git branch
# 出力例:
#  feature/branch001
#* feature/branch002
#  main
```


#### 修正の取込（他者が同一Branchに反映した修正をローカルに反映）
（SVNでは作業用ブランチの更新に相当）
ローカルリポジトリに反映する

```sh
# 先に更新した人の最新のコミットのハッシュ値（現在値）
git show --format='%H' --no-patch
# f5182566894da5cef8e4b8cecbef116b92b1dcca
```

```sh
# 作業者の最新のコミットのハッシュ値（古い値）
git show --format='%H' --no-patch
# 3feaa3d7e4c108676ac8f9d04ae65618de8b2938

# git pullでも可能
git fetch
git merge

# 先に更新した人の最新のコミットのハッシュ値（現在値）
git show --format='%H' --no-patch
# f5182566894da5cef8e4b8cecbef116b92b1dcca
```


#### 修正の取込（他者が上位Branchに反映した修正をサーバとローカルに反映）
（SVNでは上位Branchにマージされた修正を作業用Branchにマージする流れに相当）
サーバリポジトリに反映してからローカルリポジトリに反映する

```sh
# 現在のローカルブランチ(*)のHEADがfeature/branch001に反映されていない
git branch --contains
#  feature/branch002
#* main

git switch feature/branch001

# 現在のローカルブランチ(*)のHEADがmainとfeature/branch002に反映されている
# （mainのHEADをfeature/branch001に反映する）
git branch --contains
#* feature/branch001
#  feature/branch002
#  main

git fetch origin
git merge origin

git switch main

# 現在のローカルブランチ(*)のHEADがfeature/branch001に反映されている
git branch --contains       
#  feature/branch001
#  feature/branch002
#* main

```