git merge和git merge --no-ff有什么区别?


Answers:


1078

如果该--no-ff标志git merge检测到您的当前位置HEAD是您要合并的提交的祖先,则阻止执行“快进” 。快进是git而不是构造合并提交的时候,只是将分支指针移动到指向传入的提交。这通常发生在git pull没有任何本地更改的情况下。

但是,有时您希望防止这种情况的发生,通常是因为您想要维护特定的分支拓扑(例如,您正在合并一个主题分支,并且希望确保在阅读历史记录时它看起来像这样)。为了做到这一点,你可以通过--no-ff标志和git merge始终构造一个合并,而不是快进。

同样,如果您要执行a git pull或use git merge以便显式快进,并且如果它不能快进则要纾困,则可以使用该--ff-only标志。这样,您可以定期进行某些操作git pull --ff-only而无需考虑,然后如果出错,则可以返回并决定是否要合并或重新设置基础。


87
为了更直接地回答OP的问题:他们并不总是不同的,但如果是,很明显从gitk或者git log --graph该快进合并没有创建一个合并提交,而非快进人做。
卡斯卡贝尔2012年

11
扩展避免ff的原因将是很好的:作者提到“特定分支拓扑”意味着在--no-ff情况下,额外的合并提交充当了合并的标记。优点是带有作者和合并者名称的显式合并标记。缺点是非线性历史记录,看起来像一组会聚的铁轨。一个可能的心理合并虽然副作用是由于较长的审查过程贡献者失去兴趣:blog.spreedly.com/2014/06/24/...
弗拉德

6
可以公平地说,--no-ff从要开发的功能或要开发到掌握的功能类似于合并请求请求吗?
merlinpatt '16

9
@merlinpatt当然。如果您在GitHub中合并拉取请求,则等同于--no-ff
莉莉·巴拉德

1
这是一个很好的解释。伙计,那里没有足够好的git解释供人们理解,为什么干净的git历史确实非常重要(包括我在内!)
dudewad

1037

这个问题的图形答案

这是一个网站,上面有清晰的使用说明和图形说明git merge --no-ff

git merge --no-ff和git merge之间的区别

直到我看到这个,我对git完全迷失了。使用--no-ff可以使查看历史记录的人清楚地看到您检出要处理的分支。(该链接指向github的“网络”可视化工具),这是另一个带有插图的出色参考。该参考文献很好地补充了第一个参考文献,更多地侧重于那些不熟悉git的读者。


像我这样的新手的基本信息

如果您像我一样,而不是Git专家,那么我在这里的答案描述了如何处理git跟踪中的文件删除操作,而不是从本地文件系统中删除文件,这似乎记录不佳,但经常发生。另一个新情况是获取当前代码,但仍然设法使我难以理解。


工作流程示例

我将软件包更新到了​​我的网站,不得不回到笔记中才能看到我的工作流程;我认为向该答案添加示例非常有用。

我的git命令工作流程:

git checkout -b contact-form
(do your work on "contact-form")
git status
git commit -am  "updated form in contact module"
git checkout master
git merge --no-ff contact-form
git branch -d contact-form
git push origin master

下图:实际用法,包括说明。
注意:以下输出被截断;git非常冗长。

$ git status
# On branch master
# Changed but not updated:
#   (use "git add/rm <file>..." to update what will be committed)
#   (use "git checkout -- <file>..." to discard changes in working directory)
#
#       modified:   ecc/Desktop.php
#       modified:   ecc/Mobile.php
#       deleted:    ecc/ecc-config.php
#       modified:   ecc/readme.txt
#       modified:   ecc/test.php
#       deleted:    passthru-adapter.igs
#       deleted:    shop/mickey/index.php
#
# Untracked files:
#   (use "git add <file>..." to include in what will be committed)
#
#       ecc/upgrade.php
#       ecc/webgility-config.php
#       ecc/webgility-config.php.bak
#       ecc/webgility-magento.php

注意上面的三件事:
1)在输出中,您可以看到ECC软件包升级的更改,包括添加新文件。
2)还要注意,/ecc我删除了两个文件(不在文件夹中),与此更改无关。不再将这些文件删除与混淆ecc,我稍后将创建一个不同的cleanup分支来反映这些文件的删除。
3)我没有遵循我的工作流程!我在尝试再次使ecc工作时忘记了git。

下图:git commit -am "updated ecc package"我只希望将文件添加到该文件/ecc夹中,而不是通常执行的全包操作。这些已删除的文件不是我的特定部分git add,但是由于已经在git中对其进行了跟踪,因此我需要从该分支的提交中删除它们:

$ git checkout -b ecc
$ git add ecc/*
$ git reset HEAD passthru-adapter.igs
$ git reset HEAD shop/mickey/index.php
Unstaged changes after reset:
M       passthru-adapter.igs
M       shop/mickey/index.php

$ git commit -m "Webgility ecc desktop connector files; integrates with Quickbooks"

$ git checkout master
D       passthru-adapter.igs
D       shop/mickey/index.php
Switched to branch 'master'
$ git merge --no-ff ecc
$ git branch -d ecc
Deleted branch ecc (was 98269a2).
$ git push origin master
Counting objects: 22, done.
Delta compression using up to 4 threads.
Compressing objects: 100% (14/14), done.
Writing objects: 100% (14/14), 59.00 KiB, done.
Total 14 (delta 10), reused 0 (delta 0)
To git@github.com:me/mywebsite.git
   8a0d9ec..333eff5  master -> master



自动化上述脚本

一天使用10次以上此过程,我开始编写批处理脚本来执行命令,因此我git_update.sh <branch> <"commit message">为执行上述步骤制作了几乎正确的脚本。这是该脚本的要点源

而不是git commit -am从通过生成的“已修改”列表中选择文件git status,然后将其粘贴到此脚本中。发生这种情况是因为我进行了数十次编辑,但希望使用不同的分支名称来帮助对更改进行分组。


11
--no-ff选项合并后,您仍然可以安全删除分支吗?
安迪·弗莱明

17
@DesignerGuy是的,您可以安全地删除旧分支。将分支es视为指向特定提交的指针。
Zyphrax

2
我发现链接页面上的这些文本很有帮助:如果没有--no-ff,“从Git历史记录中看不到哪个提交对象一起实现了功能—您必须手动读取所有日志消息。”
Lorne Laliberte 2014年

一个图像总是比一千个单词更好!
rupps

1
该图形未在第二个图像中显示功能分支,为什么不显示?它仍然存在不是吗?
ADJenks,

269

合并策略

显式合并:创建一个新的合并提交。(如果使用过,这将是您得到的--no-ff。)

在此处输入图片说明

快速转发合并:快速转发,而无需创建新的提交:

在此处输入图片说明

重新设定:建立新的基本等级:

在此处输入图片说明

壁球:挤压或挤(东西)与力,使其变得平坦:

在此处输入图片说明


11
有趣的图形,但是它并没有真正显示出--no-ff情况,因此在这里并不能完全回答问题
Vib

22
第一个是“显式合并”,在这里称为“合并”,是非ff合并。
bkribbs 18/09/20

3
该图形确实对我有很大帮助
-exexzian

2
不能回答每个人说的问题,但是此图太棒了,赞!
苏联边疆

3
那么Rebase和快进合并之间有什么区别?
ThanosFisherman

217

--no-ff选项确保不会发生快速前向合并,并且始终将创建一个新的提交对象。如果您希望git维护功能分支的历史记录,则可能需要这样做。             git merge --no-ff和git merge 在上图中,左侧是使用后git历史git merge --no-ff的示例,右侧git merge是可能进行ff合并的示例。

编辑:此图像的先前版本仅指示合并提交的单亲。合并提交有多个父提交,git用来维护“功能分支”和原始分支的历史记录。多个父链接以绿色突出显示。


3
绿色箭头和黑色箭头分别表示什么?
rtconner 2015年

11
绿色箭头表示提交与父级之间的链接,其中该提交具有多个父级。普通提交(黑色)只有一个父级。
丹尼尔·史密斯

9
@DanielSmith您如何绘制这个优雅的图形?
chancyWu

4
@chancyWu与Adobe Illustrator
Daniel Smith

在我公司看来,所有拉取请求都与--no-ff选项合并。在这种情况下,在创建拉取请求之前重新设置基准是否仍然有意义?
Nickpick

37

这是一个古老的问题,在其他帖子中也对此进行了微妙的提及,但是对我来说,单击此按钮的原因是,非快进合并将需要单独的commit


您能否在上面的答案中回顾工作流程:这是否意味着我现在需要做以外的其他提交?谢谢。
克里斯·K

3
@ChrisK,git merge --no-ff ecc您将在git logfor分支主机中仅具有一个附加的合并提交。从技术上讲,在master指向提交ecc的直接祖先的情况下,这是不需要的,但是通过指定--no-ff选项,您将强制创建该合并提交。标题为:Merge branch 'ecc'
Ankur Agarwal

很棒的总结!:)
爱德华

4

--no-ff标志使合并始终创建一个新的提交对象,即使合并可以通过快进来执行。这样可以避免丢失有关要素分支历史存在的信息,并将所有添加了要素的提交分组在一起

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.