一言でいうと「git merge は、ファン目線＝物まねで満足」「git rebase は、ストーカー目線＝相手の家に押しかける」

# これは何？
- `git merge` と `git rebase` の違いを図で説明します。
- 個人的に「commit を１つの箱と見立て、上に積み上げるイメージ」でも、うまく書けそうな気がしたので試してみました。
- 結果、箱が積み重なった図が家（ビル）に見えてきました。
- そうすると **『git merge は、ファン目線＝物まねで満足」「git rebase は、ストーカー目線＝相手の家に押しかける』** だなぁ。と感じ始めました。

# リポジトリの初期状態

- この状態から、`git merge` あるいは `git rebase` を行います。
    - 今現在、２つのブランチ `origin/develop` `feature` があります。
    - 現在位置(HEAD) は `feature` です。

![1-original.png](https://qiita-image-store.s3.amazonaws.com/0/275337/1d72f802-782b-7cc5-13e3-d259358f4d45.png)

# git merge origin/develop (自分 `feature` に、相手 `origin/develop` の commit を積む)

- `git merge origin/develop` すると次のようになります。
- 自分 `feature` に、相手 `origin/develop` の全ての成果物（commit）を取り込みます。
    - 共通の祖先である `A` を基準とします。
    - 基準以降の相手の成果物である、`C` `E` が取込対象となります。
    - 取込対象は１つにまとめられ、`C'E'` として 自分に commit されます。
    - このように「相手の複数の成果物を１つにまとめて取り込む」のが、この場合の `git merge` の動きです。

![2-git-merge.png](https://qiita-image-store.s3.amazonaws.com/0/275337/3f6e0d53-c9e7-a2fe-bfd0-59c76a503453.png)

# git rebase origin/develop (相手 `origin/develop` に、自分 `feature` の commit を積み直す)

- `git rebase origin/develop` すると次のようになります。
- 相手 `origin/develop` に、自分 `feature` の成果物（commit）を付け直します。
    - 共通の祖先である `A` を基準とします。
    - 基準以降の自分の成果物である `B` `D` が移動対象になります。
    - 移動対象は１commitずつ、相手に移動します。（正確には、移動ではなく、全く同じ内容でコピーが行われます。つまり、ハッシュ値が変わります）
- ちなみに、`B` `D` は、git log 上からは見えなくなりますが、完全に削除されたわけではなく、`git reflog HEAD` で見えます。（詳細は、以下の参考を参照ください）

![3-git-rebase.png](https://qiita-image-store.s3.amazonaws.com/0/275337/0b627259-2e93-5c82-8f76-660cf0834036.png)


# まとめ
- 違いを簡潔に説明すると、、、
    - 「`git merge` は、自分に相手のcommitを積む。`git rebase` は、相手に自分のcommitを積み直す。」
    - 「`git merge` は、相手の家財道具と同じものを購入して自宅に置く。`git rebase` は、自宅の家財道具一式持って、相手の家に引っ越す」
    - 「`git merge` は、相手の物まねをする。`git rebase` は、自宅は引き払って相手の家に住む」
    - つまり「`git merge` は、ファン目線。`git rebase` は、ストーカー目線」


# 参考：上で説明したケースの実例

## git merge の例

- 実行コマンド

```sh
(
# set -x

mkdir try-git-merge
cd try-git-merge

git init

git config user.name "hoge"
git config user.email "hoge@example.com"

sleep 1
git checkout -b develop
echo "aaa" > a.txt
git add a.txt
git commit -m "add a.txt"

sleep 1
git checkout -b feature
echo "bbb" > b.txt
git add b.txt
git commit -m "add b.txt"

sleep 1
git checkout develop
echo "ccc" > c.txt
git add c.txt
git commit -m "add c.txt"

sleep 1
git checkout feature
echo "ddd" > d.txt
git add d.txt
git commit -m "add d.txt"

sleep 1
git checkout develop
echo "eee" > e.txt
git add e.txt
git commit -m "add e.txt"

git log --all --graph --oneline --date-order

sleep 1
git checkout feature
git merge --no-edit develop

git log --all --graph --oneline --date-order
git reflog HEAD
)
```

- 実行結果

```
Initialized empty Git repository in /home/ubuntu/20190307/try-git-merge/.git/
Switched to a new branch 'develop'
[develop (root-commit) 9ce6cfc] add a.txt
 1 file changed, 1 insertion(+)
 create mode 100644 a.txt
Switched to a new branch 'feature'
[feature c6edd06] add b.txt
 1 file changed, 1 insertion(+)
 create mode 100644 b.txt![3-git-rebase.png](https://qiita-image-store.s3.amazonaws.com/0/275337/de3dcaaa-f3c8-ee43-cacb-cf5c778685c1.png)

Switched to branch 'develop'
[develop b0e661e] add c.txt
 1 file changed, 1 insertion(+)
 create mode 100644 c.txt
Switched to branch 'feature'
[feature 861542b] add d.txt
 1 file changed, 1 insertion(+)
 create mode 100644 d.txt
Switched to branch 'develop'
[develop 05140da] add e.txt
 1 file changed, 1 insertion(+)
 create mode 100644 e.txt
* 05140da (HEAD -> develop) add e.txt ・・・・ merge 前
| * 861542b (feature) add d.txt
* | b0e661e add c.txt
| * c6edd06 add b.txt
|/  
* 9ce6cfc add a.txt
Switched to branch 'feature'
Merge made by the 'recursive' strategy.
 c.txt | 1 +
 e.txt | 1 +
 2 files changed, 2 insertions(+)
 create mode 100644 c.txt
 create mode 100644 e.txt
*   35d2fc8 (HEAD -> feature) Merge branch 'develop' into feature ・・・ merge 後
|\  
| * 05140da (develop) add e.txt
* | 861542b add d.txt
| * b0e661e add c.txt
* | c6edd06 add b.txt
|/  
* 9ce6cfc add a.txt
35d2fc8 (HEAD -> feature) HEAD@{0}: merge develop: Merge made by the 'recursive' strategy.
861542b HEAD@{1}: checkout: moving from develop to feature
05140da (develop) HEAD@{2}: commit: add e.txt
b0e661e HEAD@{3}: checkout: moving from feature to develop
861542b HEAD@{4}: commit: add d.txt
c6edd06 HEAD@{5}: checkout: moving from develop to feature
b0e661e HEAD@{6}: commit: add c.txt
9ce6cfc HEAD@{7}: checkout: moving from feature to develop
c6edd06 HEAD@{8}: commit: add b.txt
9ce6cfc HEAD@{9}: checkout: moving from develop to feature
9ce6cfc HEAD@{10}: commit (initial): add a.txt
```

## git rebase の例

- 実行コマンド

```sh
(
#set -x

mkdir try-git-rebase
cd try-git-rebase

git init

git config user.name "hoge"
git config user.email "hoge@example.com"

sleep 1
git checkout -b develop
echo "aaa" > a.txt
git add a.txt
git commit -m "add a.txt"

sleep 1
git checkout -b feature
echo "bbb" > b.txt
git add b.txt
git commit -m "add b.txt"

sleep 1
git checkout develop
echo "ccc" > c.txt
git add c.txt
git commit -m "add c.txt"

sleep 1
git checkout feature
echo "ddd" > d.txt
git add d.txt
git commit -m "add d.txt"

sleep 1
git checkout develop
echo "eee" > e.txt
git add e.txt
git commit -m "add e.txt"

git log --all --graph --oneline --date-order

sleep 1
git checkout feature
git rebase develop

git log --all --graph --oneline --date-order
git reflog HEAD
)
```

- 実行結果

```
Initialized empty Git repository in /home/ubuntu/20190307/try-git-rebase/.git/
Switched to a new branch 'develop'
[develop (root-commit) bf10f4f] add a.txt
 1 file changed, 1 insertion(+)
 create mode 100644 a.txt
Switched to a new branch 'feature'
[feature 532fb77] add b.txt
 1 file changed, 1 insertion(+)
 create mode 100644 b.txt
Switched to branch 'develop'
[develop fff94fd] add c.txt
 1 file changed, 1 insertion(+)
 create mode 100644 c.txt
Switched to branch 'feature'
[feature 5ef0acd] add d.txt
 1 file changed, 1 insertion(+)
 create mode 100644 d.txt
Switched to branch 'develop'
[develop 91dfce3] add e.txt
 1 file changed, 1 insertion(+)
 create mode 100644 e.txt
* 91dfce3 (HEAD -> develop) add e.txt ・・・ rebase 前
| * 5ef0acd (feature) add d.txt
* | fff94fd add c.txt
| * 532fb77 add b.txt
|/  
* bf10f4f add a.txt
Switched to branch 'feature'
First, rewinding head to replay your work on top of it...
Applying: add b.txt
Applying: add d.txt
* f097caa (HEAD -> feature) add d.txt ・・・ rebase 後
* cda4d58 add b.txt
* 91dfce3 (develop) add e.txt
* fff94fd add c.txt
* bf10f4f add a.txt
f097caa (HEAD -> feature) HEAD@{0}: rebase finished: returning to refs/heads/feature
f097caa (HEAD -> feature) HEAD@{1}: rebase: add d.txt ・・・ D がコピーされ、rebase後 D' (f097caa) となった
cda4d58 HEAD@{2}: rebase: add b.txt ・・・・・・・・・・・・・ B がコピーされ、rebase後 B' (cda4d58) となった
91dfce3 (develop) HEAD@{3}: rebase: checkout develop
5ef0acd HEAD@{4}: checkout: moving from develop to feature
91dfce3 (develop) HEAD@{5}: commit: add e.txt
fff94fd HEAD@{6}: checkout: moving from feature to develop
5ef0acd HEAD@{7}: commit: add d.txt ・・・・・・・・・・・・・・ rebase後、使われなくなった D(5ef0acd)
532fb77 HEAD@{8}: checkout: moving from develop to feature
fff94fd HEAD@{9}: commit: add c.txt
bf10f4f HEAD@{10}: checkout: moving from feature to develop
532fb77 HEAD@{11}: commit: add b.txt ・・・・・・・・・・・・・ rebase後、使われなくなった B(532fb77)
bf10f4f HEAD@{12}: checkout: moving from develop to feature
bf10f4f HEAD@{13}: commit (initial): add a.txt
```