1220

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

4

8 回答 8

1325

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

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

同样,如果您想执行 a git pullor usegit merge以显式地快进,并且如果它不能快进,您想退出,那么您可以使用该--ff-only标志。这样你就可以经常做一些git pull --ff-only不假思索的事情,然后如果出错,你可以回去决定是要合并还是变基。

于 2012-01-30T18:51:29.940 回答
1245

这个问题的图形答案

这是一个站点,其中包含使用的清晰说明和图形说明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

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

下面:而不是我通常会做的包罗万象git commit -am "updated ecc package",我只想在文件/ecc夹中添加文件。那些被删除的文件并不是 my 的特定部分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">脚本来执行上述步骤。这是该脚本的 Gist 源代码。

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

于 2013-02-14T00:08:21.747 回答
492

合并策略

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

在此处输入图像描述

Fast Forward Merge:快速转发,无需创建新的提交:

在此处输入图像描述

Rebase:建立一个新的基础级别:

在此处输入图像描述

Squash:用力压碎或挤压(某物),使其变平:

在此处输入图像描述

于 2018-09-04T02:21:15.130 回答
249

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

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

于 2014-02-12T02:33:07.050 回答
42

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

于 2014-04-02T19:56:06.560 回答
12

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

于 2019-08-08T11:08:15.153 回答
9

其他答案很好地表明--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"]
于 2020-12-14T14:25:20.190 回答
2

什么是快进?

快进是当您合并或变基到一个分支时,Git 所做的只是在您已签出的分支之前。

鉴于以下分支设置:

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

主分支仍在引用 7ddac6c,而该功能已提前两次提交。现在可以在 master 之前考虑 feature 分支。

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

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

什么时候不会发生快进?

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

如果您要将功能合并或变基到 master 上,Git 将无法进行快进,因为树都已经分叉了。考虑到 Git 提交是不可变的,Git 无法在不更改父引用的情况下将提交从 feature 获取到 master。

于 2021-12-04T10:36:41.047 回答