これは何?
-
git merge
とgit rebase
の違いを図で説明します。 - 個人的に「commit を1つの箱と見立て、上に積み上げるイメージ」でも、うまく書けそうな気がしたので試してみました。
- 結果、箱が積み重なった図が家(ビル)に見えてきました。
- そうすると 『git merge は、ファン目線=物まねで満足」「git rebase は、ストーカー目線=相手の家に押しかける』 だなぁ。と感じ始めました。
リポジトリの初期状態
- この状態から、
git merge
あるいはgit rebase
を行います。- 今現在、2つのブランチ
origin/develop
feature
があります。 - 現在位置(HEAD) は
feature
です。
- 今現在、2つのブランチ
git merge origin/develop (自分 feature
に、相手 origin/develop
の commit を積む)
-
git merge origin/develop
すると次のようになります。 - 自分
feature
に、相手origin/develop
の全ての成果物(commit)を取り込みます。- 共通の祖先である
A
を基準とします。 - 基準以降の相手の成果物である、
C
E
が取込対象となります。 - 取込対象は1つにまとめられ、
C'E'
として 自分に commit されます。 - このように「相手の複数の成果物を1つにまとめて取り込む」のが、この場合の
git merge
の動きです。
- 共通の祖先である
git rebase origin/develop (相手 origin/develop
に、自分 feature
の commit を積み直す)
-
git rebase origin/develop
すると次のようになります。 - 相手
origin/develop
に、自分feature
の成果物(commit)を付け直します。- 共通の祖先である
A
を基準とします。 - 基準以降の自分の成果物である
B
D
が移動対象になります。 - 移動対象は1commitずつ、相手に移動します。(正確には、移動ではなく、全く同じ内容でコピーが行われます。つまり、ハッシュ値が変わります)
- 共通の祖先である
- ちなみに、
B
D
は、git log 上からは見えなくなりますが、完全に削除されたわけではなく、git reflog HEAD
で見えます。(詳細は、以下の参考を参照ください)
まとめ
- 違いを簡潔に説明すると、、、
- 「
git merge
は、自分に相手のcommitを積む。git rebase
は、相手に自分のcommitを積み直す。」 - 「
git merge
は、相手の家財道具と同じものを購入して自宅に置く。git rebase
は、自宅の家財道具一式持って、相手の家に引っ越す」 - 「
git merge
は、相手の物まねをする。git rebase
は、自宅は引き払って相手の家に住む」 - つまり「
git merge
は、ファン目線。git rebase
は、ストーカー目線」
- 「
参考:上で説明したケースの実例
git merge の例
- 実行コマンド
(
# 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
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 の例
- 実行コマンド
(
# 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