“--no-ff”标志对“git合并”有什么影响?

使用gitk log,我无法发现git mergegit merge --no-ff的效果之间的差异。我如何观察差异(使用git命令或一些工具)?

712838 次浏览

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

然而,有时你想防止这种行为发生,通常是因为你想维护一个特定的分支拓扑(例如,你正在合并一个主题分支,并且你想确保它在读取历史记录时看起来是这样的)。为此,你可以传递--no-ff标志,git merge总是构造一个合并而不是快速转发。

类似地,如果你想执行git pull或使用git merge来显式快进,如果它不能快进,你想退出,那么你可以使用--ff-only标志。这样你就可以不假思索地定期做类似git pull --ff-only的事情,然后如果它出错了,你可以返回并决定是要合并还是重写。

对这个问题的直观回答

这里有一个网站带有使用git merge --no-ff的清晰解释和图形说明:

git合并--no-ff和git合并的区别

在我看到这个之前,我完全迷失在git中。使用--no-ff可以让人们查看历史记录,清楚地处理看看你签出的分行。(该链接指向github的“网络”可视化工具)这里是另一个很好的参考的插图。这个参考很好地补充了第一个参考,更多地关注那些不太熟悉git的人。


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

如果你像我一样,而不是Git-guru,获取当前代码6753592">我的答案在这里描述了处理从git跟踪中删除文件而不从本地文件系统中删除它们,这似乎记录得很差,但经常发生。另一个newb情况是获取当前代码,它仍然设法逃避我。


示例工作流程

我更新了一个包到我的网站,不得不回到我的笔记中查看我的工作流程;我认为在这个答案中添加一个例子很有用。

我的git命令工作流程:

git checkout -b contact-form(do your work on "contact-form")git statusgit commit -am  "updated form in contact module"git checkout mastergit merge --no-ff contact-formgit branch -d contact-formgit 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.phpUnstaged changes after reset:M       passthru-adapter.igsM       shop/mickey/index.php
$ git commit -m "Webgility ecc desktop connector files; integrates with Quickbooks"
$ git checkout masterD       passthru-adapter.igsD       shop/mickey/index.phpSwitched to branch 'master'$ git merge --no-ff ecc$ git branch -d eccDeleted branch ecc (was 98269a2).$ git push origin masterCounting 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.git8a0d9ec..333eff5  master -> master



用于自动化上述操作的脚本

在一天内使用这个过程10多次后,我已经开始编写批处理脚本来执行命令,所以我做了一个几乎合适的git_update.sh <branch> <"commit message">脚本来执行上述步骤。这里是Gist来源用于该脚本。

而不是git commit -am,我从git status生成的“修改”列表中选择文件,然后将它们粘贴到这个脚本中。这是因为我做了几十次编辑,但想要不同的分支名称来帮助对更改进行分组。

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

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

这是一个老问题,这在其他帖子中有些微妙地提到,但对我来说,这个点击的解释是非快进合并将需要单独提交

合并策略

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

在此处输入图片描述

快进合并:快速前进,不创建新的提交:

在此处输入图片描述

重新定位:建立一个新的基础级别:

在此处输入图片描述

壁球:用力挤压或挤压(某物)使其变得扁平:

在此处输入图片描述

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

其他答案很好地表明--no-ff会导致合并提交。这保留了有关特征分支的历史信息,这很有用,因为特征分支会定期清理和删除。

这个答案可以提供何时使用或不使用--no-ff的上下文。

从功能合并到主分支:使用--no-ff

工作示例:

$ git checkout -b NewFeature[work...work...work]$ git commit -am  "New feature complete!"$ git checkout main$ git merge --no-ff NewFeature$ git push origin main$ git branch -d NewFeature

将主分支的更改合并到功能分支:离开--no-ff

工作示例:

$ git checkout -b NewFeature[work...work...work][New changes made for HotFix in the main branch! Lets get them...]$ git commit -am  "New feature in progress"$ git pull origin main[shortcut for "git fetch origin main", "git merge origin main"]

什么是快进?

快进是当您在签出的分支之前合并或重新基于分支时Git所做的事情。

给定以下分支设置:

两个分支都引用了相同的提交。它们都有完全相同的历史记录。现在提交一些功能。

主分支仍在引用7ddac6c,而功能已提前两次提交。现在可以认为功能分支在主分支之前。

现在很容易看到Git快进时会发生什么。它只是更新master分支以引用该功能所做的确切提交。不会对存储库本身进行任何更改,因为来自该功能的提交已经包含所有必要的更改。

您的存储库历史记录现在看起来像这样:

什么时候没有快进发生?

当在原始分支和新分支中进行更改时,不会发生快进。

如果你要将功能合并或重新定位到master上,Git将无法进行快进,因为树都发散了。考虑到Git提交是不可变的,Git无法在不更改其父引用的情况下将功能提交到master。