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

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

我使用git开始了我的新项目,并完成了我的第一个功能。当合并该特性时,我意识到git使用快进,也就是说,如果可能的话,它会将我的更改直接应用到主分支,而忘记我的分支。

所以想想未来:我是唯一一个在做这个项目的人。如果我使用git的默认方法(快进合并),我的历史记录将导致一个巨大的主分支。 没有人知道我为每个特性使用了一个单独的分支,因为最后我只有那个巨大的主分支。那样会不会显得不专业?< / p >

根据这个推理,我不想快进合并,也看不出为什么它是默认的。它有什么好呢?

162544 次浏览

快进合并对于短期的分支是有意义的,但是对于复杂的历史来说,非快进合并可能使历史更容易理解,并且更容易恢复一组提交。

警告:非快进也有潜在的副作用。请回顾https://sandofsky.com/blog/git-workflow.html,避免'no-ff'与它的“检查点提交”打破平分或指责,并仔细考虑它是否应该是master的默认方法。

alt text
(From nvie.com文森特Driessen, post "一个成功的Git分支模型")

在开发中加入一个完成的功能

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

$ 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标志导致合并总是创建一个新的提交对象,即使合并可以用快进执行。这避免了丢失关于特性分支的历史存在信息,并将所有添加了该特性的提交组合在一起。

Jakub Narębski提到配置<强> # EYZ0 < / >强:

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

快进是默认的,因为:

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

但是如果你预期在一个主题/特性分支上有一个迭代的工作流程(例如,我合并,然后我回到这个特性分支并添加更多的提交),那么在主分支中只包含合并是有用的,而不是在特性分支中所有的中间提交。

在这种情况下,你可以设置这种配置文件:

[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在评论中补充道:

对于[短寿命]分支,我认为快进是有意义的,但将其作为默认操作意味着git假设您……通常有[短命的]分支。合理吗?

Jefromi回答:

我认为分支的生命周期因用户而异。但是,在有经验的用户中,可能倾向于使用寿命更短的分支。

对我来说,短寿命分支是我为了使某个操作更容易而创建的分支(重基,可能,或快速修补和测试),然后一旦我完成立即删除 这意味着它可能是应该被吸收到它所分叉的主题分支中吗,主题分支将合并为一个分支。没有人需要知道为了创建实现给定特性的一系列提交,我在内部做了什么。< / p >

更笼统地说,我补充说:

这真的取决于你的开发工作流程:

  • 如果它是线性的,一个分支是有意义的。
  • 如果您需要隔离特性,并在它们上花费很长一段时间并反复地合并它们,那么几个分支是有意义的。

看到“# EYZ0”

实际上,当您考虑Mercurial分支模型时,它的核心是每个存储库一个分支(即使您可以创建匿名头,书签,甚至命名分支)
看到# EYZ0。< / p >
默认情况下,Mercurial使用匿名轻量级代码线,在其术语中称为“heads” Git使用轻量级的命名分支,使用内射映射将远程存储库中的分支名称映射到远程跟踪分支的名称 Git“强制”您为分支命名(好吧,除了单个未命名的分支,这种情况称为“分离的头”),但我认为这对于分支较多的工作流(例如主题分支工作流)更有效,这意味着在单个存储库范例中有多个分支

让我在VonC非常全面的回答上展开一点:


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

请注意,特性分支和在一个存储库中拥有多个分支的有用性是后来才出现的,随着VCS的广泛使用和良好的合并支持,以及尝试各种基于合并的工作流。这就是为什么Mercurial最初只支持每个存储库一个分支(加上匿名跟踪远程分支的提示),正如在“Mercurial: The Definitive Guide”的旧版本中看到的那样。


其次,当遵循最佳实践使用特性分支时,即特性分支都应该从稳定版本开始(通常是从上一个版本开始),能够通过选择合并哪些特性分支来选择包含哪些特性,你通常不会处于快进状态…这使得这个问题变得毫无意义。你需要担心创建一个真正的合并,而不是在合并第一个分支时快进(假设你没有把单次提交的更改直接放在'master'上);所有其他后来的合并当然是在非快进的情况。

HTH