什么时候 git merge 只是“挪一下指针”?--no-ff 到底多出了什么?--ff-only 又在什么情况下拒绝?这篇逐种实测,数新增了几个对象,看历史的形状,以及 --squash 为什么会让 git branch -d 说“没合并”。
$ git merge feature 2>&1 | head -4 | sed -E "s/[0-9a-f]{7}\.\.[0-9a-f]{7}/<旧>..<新>/"
Updating <旧>..<新>
Fast-forward
f | 1 +
1 file changed, 1 insertion(+)
对象数 8 → 8(新增 0 个);main 现在等于 feature: True
$ git reflog show main --format='%gd: %gs' | head -2; git log --graph --oneline | sed -E 's/[0-9a-f]{7} //'
main@{0}: merge feature: Fast-forward
main@{1}: commit (initial): M1
* F2
* F1
* M1当前分支是被合并分支的祖先时,合并只是把分支指针挪到对方的位置:没有新提交、没有新对象,历史是一条直线。reflog 里记着 merge feature: Fast-forward。
$ git merge --no-ff -m "合并 feature" feature 2>&1 | head -3
Merge made by the 'ort' strategy.
f | 1 +
1 file changed, 1 insertion(+)
对象数 8 → 9(只新增 1 个:合并提交本身,tree 复用)
合并提交的 tree == feature 末端的 tree: True;parent 数: 2
$ git log --graph --oneline | sed -E 's/[0-9a-f]{7} //'
* 合并 feature
|\
| * F2
| * F1
|/
* M1强制创建合并提交,即使可以快进。新提交的 tree 就是 feature 末端的 tree(复用同一个对象),有两个 parent,整个合并只新增 1 个对象。好处是主线上留下功能边界,--first-parent 可以把一个功能折叠成一行。
$ git merge --ff-only feature 2>&1 | grep -E "fatal|hint: *git|Merge"; git log --oneline | wc -l
fatal: Not possible to fast-forward, aborting.
2
$ git config merge.ff only; git merge feature 2>&1 | grep -E "fatal|Merge"; git config --unset merge.ff
fatal: Not possible to fast-forward, aborting.
$ git merge -m "合并 feature" feature 2>&1 | head -2
Merge made by the 'ort' strategy.
f | 1 +双方都有对方没有的提交时无法快进。默认生成合并提交;--ff-only(或 merge.ff=only、pull --ff-only)改为直接拒绝,提示 Not possible to fast-forward。
[快进]
$ git log --graph --oneline | sed -E 's/[0-9a-f]{7} //'; echo ' -- --first-parent:'; git log --first-parent --oneline | sed -E 's/[0-9a-f]{7} //'; echo ' -- --merges 的个数:'; git log --merges --oneline | wc -l
* F2
* F1
* M1
-- --first-parent:
F2
F1
M1
-- --merges 的个数:
0
[--no-ff]
$ git log --graph --oneline | sed -E 's/[0-9a-f]{7} //'; echo ' -- --first-parent:'; git log --first-parent --oneline | sed -E 's/[0-9a-f]{7} //'; echo ' -- --merges 的个数:'; git log --merges --oneline | wc -l
* 合并feature
|\
| * F2
| * F1
|/
* M1
-- --first-parent:
合并feature
M1
-- --merges 的个数:
1快进是直线,看不出哪些提交属于同一个功能;--no-ff 有菱形,git log --merges 能数出合并次数。
$ git merge --squash feature 2>&1 | head -3; git status --short Updating 1f97caf..dbed7d4 Fast-forward Squash commit -- not updating HEAD A f 新提交的 parent 数: 1;tree == feature 末端 tree: True $ git branch --merged | tr -d " *" | tr "\n" " "; echo "(--merged 里没有 feature)"; git branch -d feature 2>&1 | head -2 | cut -c1-70 main (--merged 里没有 feature) error: the branch 'feature' is not fully merged hint: If you are sure you want to delete it, run 'git branch -D featur
--squash 把对方分支的所有改动放进暂存区,由你提交为一个单 parent 的普通提交。结果的 tree 相同,但提交图里没有指向 feature 的连线,所以 git 不认为它“已合并”,git branch -d 会拒绝。