Git merge 允许我们执行快进和非快进分支合并。有什么想法何时使用快进合并以及何时不使用快进合并吗?
当您想要清楚地了解功能分支时,
--no-ff
选项非常有用。因此,即使在此期间没有进行任何提交,FF 也是可能的 - 有时您仍然希望主线中的每个提交对应于一个功能。因此,您将具有一堆提交的功能分支视为一个单元,并将它们合并为一个单元。当您使用 --no-ff
进行功能分支合并时,从您的历史记录中可以清楚地看出。
如果你不关心这样的事情 - 只要有可能,你可能就可以逃脱 FF 的惩罚。这样你会有更多类似 svn 的工作流程感觉。
例如,这篇文章的作者认为
--no-ff
选项应该是默认的,他的推理与我上面概述的很接近:
考虑“feature”分支上的一系列次要提交共同构成一个新功能的情况:如果您只执行“git merge feature_branch”而不使用
--no-ff
,“不可能从 Git 历史记录中看到哪些提交对象一起实现了一个功能 - 您必须手动读取所有日志消息,恢复整个功能(即一组提交)是一个真正令人头痛的事情[如果不使用 --no-ff
],而这很容易完成。如果使用了 --no-ff
标志[因为这只是一次提交]。”
我可以举一个项目中常见的例子。
这里,选项
--no-ff
(即 true merge)创建具有多个父级的新提交,并提供更好的历史记录跟踪。否则,默认为 --ff
(即 快进合并)。
$ git checkout master
$ git checkout -b newFeature
$ ...
$ git commit -m 'work from day 1'
$ ...
$ git commit -m 'work from day 2'
$ ...
$ git commit -m 'finish the feature'
$ git checkout master
$ git merge --no-ff newFeature -m 'add new feature'
$ git log
// something like below
commit 'add new feature' // => commit created at merge with proper message
commit 'finish the feature'
commit 'work from day 2'
commit 'work from day 1'
$ gitk // => see details with graph
$ git checkout -b anotherFeature // => create a new branch (*)
$ ...
$ git commit -m 'work from day 3'
$ ...
$ git commit -m 'work from day 4'
$ ...
$ git commit -m 'finish another feature'
$ git checkout master
$ git merge anotherFeature // --ff is by default, message will be ignored
$ git log
// something like below
commit 'work from day 4'
commit 'work from day 3'
commit 'add new feature'
commit 'finish the feature'
commit ...
$ gitk // => see details with graph
(*) 请注意,如果重复使用
newFeature
分支,git 无论如何都必须进行 --no-ff
合并,而不是创建新分支。这意味着快进合并并不总是符合条件。
当我们在开发环境上工作并将代码合并到暂存/生产分支时,Git no fastforward 可能是更好的选择。通常,当我们在开发分支中开发单个功能时,我们往往会进行多次提交。以后跟踪多次提交的更改可能会很不方便。如果我们使用 Git no fastforward 与暂存/生产分支合并,那么它将只有 1 次提交。现在,只要我们想要恢复该功能,只需恢复该提交即可。生活很简单。
以下是具有差异的 git 日志可视化。这些树看起来就像从 dev 创建三个分支(称为 1 2 3),然后在有或没有快进的情况下合并。我将把设置代码放在底部。您可以将一段命令粘贴到终端中,以快速设置和重置不同的 git 场景,这对学习 git 非常有帮助。
请注意,使用快进时,git 甚至不指示合并。
--no-ff --ff (default)
* Merge 3
| * 3
* | Merge 2 * Merge 3
| | * 2 | * 3
| |/ * | Merge 2
* / Merge 1 | | * 2
|/ | |/
| * 1 * / 1
|/ |/
* main * main
将此方法与变基方法进行比较是值得的。安装/拆卸
rd
删除目录命令即可。要查看快进的行为,请从末尾的合并命令中删除
--no-ff
。如果您想查看提交 ID,请删除
--pretty
部分。
cd \user\docs\code\learning\github\sandbox
rd /s /q 0.git 1 2 3
git init --bare 0.git
git clone 0.git 1
cd 1
git config user.name "user"
git config user.email "[email protected]"
git commit --allow-empty -m main
git switch -c 1 main
git commit --allow-empty -m 1
git switch -c 2 main
git commit --allow-empty -m 2
git switch -c 3 main
git commit --allow-empty -m 3
git switch main
git merge --no-ff 1
git merge --no-ff 2
git merge --no-ff 3
git log --graph --oneline --first-parent --all --pretty=%s
--no-ff
的原因之外,在目标分支中保持正确的父订单是另一个原因。使用
--no-ff
,结果分支中最后一次提交的父顺序将独立于您要合并的分支的最后一次提交中的父顺序,其第一个父级将位于目标分支中,而不是您所在的分支中合并了。如果执行 ff,要合并的分支的最后一次提交将作为最后一个合并到目标分支中,因此目标分支中最后一次提交的第一个父级将指向主线之外的提交。这可能不是您想要的。例如,您可以使用
--first-parent
来过滤分支历史记录以仅查看分支的主要更改,或者想要执行默认使用第一个父级的其他操作,例如
revert
。如果您的分支没有保留正确的父引用,结果将与预期不同。
我不想用非工作代码污染主开发,因此 --no-ff 可能正是人们正在寻找的。
顺便说一句,可能没有必要在个性化分支上提交工作代码,因为只要没有其他人在同一个分支上工作,历史记录就可以被重写
git rebase -i
并强制在服务器上。