为什么git默认执行快进合并?


641

来自Mercurial,我使用分支来组织功能。当然,我也想在我的历史中看到这个工作流程。

我使用git开始了新项目,并完成了我的第一个功能。合并功能时,我意识到git使用了快进功能,即如果可能的话,它将更改直接应用到master分支,而忽略了我的分支。

因此,请思考一下未来:我是从事此项目的唯一人员。如果我使用git的默认方法(快速合并),则我的历史记录将导致一个巨型master分支。没人知道我为每个功能都使用了一个单独的分支,因为最后我将只有那个巨大的master分支。那看起来不专业吗?

出于这种原因,我不想进行快速合并,也无法理解为什么它是默认合并。这有什么好处?


38
注意:另请参阅sandofsky.com/blog/git-workflow.html,并避免使用no-ff带有“检查点提交”的“”,以免造成二等分或怪罪。
VonC

15
您后悔在一个人的项目上使用git吗?
HaveAGuess 2011年

27
绝对不!在我的工作文件夹中,我有7个单人项目,其中使用git。我再说一遍:自从我问了这个问题以来,我开始了许多项目,并且所有项目都是通过git版本化的。据我所知,只有git和mercurial支持本地版本控制,这对我来说是必不可少的,因为我已经习惯了。设置起来很容易,并且您始终拥有完整的历史记录。在小组项目中,它甚至更好,因为您可以提交而不会干扰正在进行的代码。另外,我使用github共享一些我的项目(例如micro-optparse),其中需要git。
弗洛里安·皮尔兹

7
@Cawas是对的,-no-ff很少有什么好主意,但是在只记录一次主分支上的提交时,仍然可以帮助保留功能内部的历史记录。对于较长的功能历史记录,当您不时合并其在主分支上的进度时,这很有意义。
VonC 2012年

12
顺便提一下,您的问题“(线性分支历史)看起来不专业吗?”。使用带有默认值的源代码系统并没有什么不专业的地方。这与专业无关。这与确定您订阅哪种分支机构有关。例如,@ VonC链接到sandofsky的文章,其中他主张使用快进作为一种出色的方法。没有对与错,只是针对不同环境的不同哲学。
Marplesoft

Answers:


718

快进合并对于短暂的分支是有意义的,但是在更复杂的历史中,非快进合并可能使历史更易于理解,并且更易于还原一组提交。

警告:非快进也有潜在的副作用。请查看https://sandofsky.com/blog/git-workflow.html,避免带有“检查点提交”的“ no-ff”打破对等或怪罪,并仔细考虑它是否是您的默认方法master

替代文字
(来自nvie.com文森特· 德里森Vincent Driessen)发布了“ 成功的Git分支模型 ”)

在开发中加入完成的功能

可以将完成的功能合并到developer分支中,以将其添加到即将发布的版本中:

$ git checkout develop
Switched to branch 'develop'
$ git merge --no-ff myfeature
Updating ea1b82a..05e9557
(Summary of changes)
$ git branch -d myfeature
Deleted branch myfeature (was 05e9557).
$ git push origin develop

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

JakubNarębski提到配置merge.ff

默认情况下,当合并作为当前提交的后代的提交时,Git不会创建额外的合并提交。相反,当前分支的尖端是快进的。
设置false为时,此变量告诉Git在这种情况下创建一个额外的合并提交(相当于--no-ff从命令行提供选项)。
设置为' only'时,仅允许这种快速合并(等同于--ff-only从命令行提供选项)。


快进是默认设置,因为:

  • 短期分支很容易在Git中创建和使用
  • 寿命短的分支通常会隔离许多可在该分支内自由重组的提交
  • 这些提交实际上是main分支的一部分:重新组织后,main分支将快速转发以包括它们。

但是,如果您期望在一个主题/功能分支上进行迭代的工作流(即,我合并,然后回到该功能分支并添加更多提交),那么仅将合并包含在主分支中将很有用,而不是功能分支的所有中间提交。

在这种情况下,您最终可以设置这种配置文件

[branch "master"]
# This is the list of cmdline options that should be added to git-merge 
# when I merge commits into the master branch.

# The option --no-commit instructs git not to commit the merge
# by default. This allows me to do some final adjustment to the commit log
# message before it gets commited. I often use this to add extra info to
# the merge message or rewrite my local branch names in the commit message
# to branch names that are more understandable to the casual reader of the git log.

# Option --no-ff instructs git to always record a merge commit, even if
# the branch being merged into can be fast-forwarded. This is often the
# case when you create a short-lived topic branch which tracks master, do
# some changes on the topic branch and then merge the changes into the
# master which remained unchanged while you were doing your work on the
# topic branch. In this case the master branch can be fast-forwarded (that
# is the tip of the master branch can be updated to point to the tip of
# the topic branch) and this is what git does by default. With --no-ff
# option set, git creates a real merge commit which records the fact that
# another branch was merged. I find this easier to understand and read in
# the log.

mergeoptions = --no-commit --no-ff

OP在注释中添加:

我认为[short-lived]分支在某种意义上是快进的,但是将其设置为默认操作意味着git假设您...经常拥有[short-lived]分支。合理?

Jefromi的答案:

我认为分支的生命周期因用户而异。不过,在经验丰富的用户中,可能存在寿命更长的分支机构。

对我来说,短暂创建的分支是我创建的分支,目的是使某些操作更容易(重新定级,可能的执行或快速修补和测试),然后在完成后立即删除。
这意味着它可能应该被吸收到从其分支的主题分支中,并且该主题分支将合并为一个分支。没有人需要知道我在内部所做的事情,以创建实现该给定功能的一系列提交。

更一般而言,我添加:

这实际上取决于您的开发工作流程

  • 如果是线性的,则一个分支有意义。
  • 如果您需要隔离功能并长时间使用它们并重复合并它们,则可以使用多个分支。

请参阅“ 您应该何时分支?

其实,当你考虑到水银分支模式,它是在其核心的一个每库分支(即使你可以创建匿名头,书签,甚至命名分支
“的Git和Mercurial -比较和对比”

默认情况下,Mercurial使用匿名的轻量级代码行,在其术语中称为“ heads”。
Git使用轻量级的命名分支,并使用注入式映射将远程存储库中的分支名称映射到远程跟踪分支的名称。
Git会“强迫”您命名分支(好吧,除了一个未命名的分支,这种情况称为“ 分离头 ”),但是我认为这对于需要大量分支的工作流(例如主题分支工作流)更有效。单个存储库范式中的多个分支。


哇,太快了。;)我知道--no-ff选项,但是只有在弄乱了功能之后才知道快速转发。对于短寿命分支,我看到了某种意义上的快进,但是将其设置为默认操作意味着git假设您最经常拥有短寿命分支。合理?
弗洛里安·皮尔兹

@弗洛里安:我相信这是一个合理的过程视图。我添加了一个配置示例,该配置将设置您想要管理合并到主服务器的方式。
VonC 2010年

谢谢,配置文件应有助于避免此类陷阱。:)仅在本地使用“ git config branch.master.mergeoptions'--no-ff'”应用此配置
Florian Pilz 2010年

2
@BehrangSaeedzadeh:变基是另一个主题(在此页面中没有提到过):只要您没有将feature分支推送到公共存储库,就可以master根据需要进行多次变基。参见stackoverflow.com/questions/5250817/…–
VonC

4
@BehrangSaeedzadeh:本身的重新设置不会使历史成为线性。通过这种方式您可以将feature分支的更改重新集成到其中master,从而使所说的历史与否成线性关系。一个简单的快速合并将使其线性。如果您在快进合并之前清除了该feature分支的历史记录,这很有意义,如stackoverflow.com/questions/7425541/…中所述,仅留下了重要的提交。
VonC

42

让我进一步谈谈VonC非常全面的答案


首先,如果我没记错的话,Git默认情况下不会在快进情况下创建合并提交的事实是由于考虑了单分支“相等存储库”,其中使用相互拉动来同步这两个存储库(您可以在大多数用户文档中找到第一个示例的工作流程,包括“ The Git用户手册”和“示例版本控制”)。在这种情况下,您不使用pull来合并完全实现的分支,而是使用它来跟上其他工作。当您碰巧将同步保存并存储在存储库中以备将来使用时,您不想拥有短暂而无关紧要的事实。

请注意,功能分支的用途以及在单个存储库中具有多个分支的用途是后来才出现的,VCS在具有良好的合并支持的情况下得到了更广泛的使用,并且尝试了各种基于合并的工作流程。因此,例如,Mercurial最初每个存储库仅支持一个分支(以及用于跟踪远程分支的匿名提示),如“ Mercurial:权威指南”的较早版本中所见。


其次,当遵循使用功能分支的最佳做法时,即功能分支都应从稳定版本(通常从上一版本开始)开始,以便能够挑选并通过选择要合并的功能分支来选择要包括的功能,您可以通常不处于快进状态 ...这使这个问题无济于事。合并第一个分支时,您需要担心创建真正的合并而不是快进(假设您没有将单提交更改直接放在“ master”上);当然,所有其他以后的合并都处于非快进状态。

高温超导


关于稳定发行版中的功能分支:如果我要为下一个发行版开发的功能取决于我已经开发并合并到母版中的另一个功能的更改,该怎么办?当然这意味着我必须从master创建第二个功能分支吗?
dOxxx

1
@dOxxx:是的,也有例外,例如,其中一个分支建立在另一个分支上(直接或在将先前的分支合并到master之后)。
2013年
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.