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

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/2850369/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-09-10 08:24:17  来源:igfitidea点击:

Why does git perform fast-forward merges by default?

gitgit-branchgit-mergefast-forward

提问by Florian Pilz

Coming from mercurial, I use branches to organize features. Naturally, I want to see this work-flow in my history as well.

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

I started my new project using git and finished my first feature. When merging the feature, I realized git uses fast-forward, i.e. it applies my changes directly to the master branch if possible and forgets about my branch.

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

So to think into the future: I'm the only one working on this project. If I use git's default approach (fast-forward merging), my history would result in one giant master branch. Nobody knows I used a separate branch for every feature, because in the end I'll have only that giant master branch. Won't that look unprofessional?

所以展望未来:我是唯一一个从事这个项目的人。如果我使用 git 的默认方法(快进合并),我的历史将导致一个巨大的主分支。没有人知道我为每个功能使用了一个单独的分支,因为最终我只会拥有那个巨大的主分支。会不会显得不专业?

By this reasoning, I don't want fast-forward merging and can't see why it is the default. What's so good about it?

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

回答by VonC

Fast-forward merging makes sense for short-lived branches, but in a more complex history, non-fast-forward merging may make the history easier to understand, and make it easier to revert a group of commits.

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

Warning: Non-fast-forwarding has potential side effects as well. Please review https://sandofsky.com/blog/git-workflow.html, avoid the 'no-ff' with its "checkpoint commits" that break bisect or blame, and carefully consider whether it should be your default approach for master.

警告:非快进也有潜在的副作用。请查看https://sandofsky.com/blog/git-workflow.html,避免使用“检查点提交”来破坏平分或归责的“no-ff”,并仔细考虑它是否应该成为master.

alt text
(From nvie.com, Vincent Driessen, post "A successful Git branching model")

替代文字
(来自nvie.comVincent Driessen,发表“一个成功的 Git 分支模型”)

Incorporating a finished feature on develop

Finished features may be merged into the develop branch to add them to the upcoming release:

在开发中合并完成的功能

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

$ 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

The --no-ffflag causes the merge to always create a new commit object, even if the merge could be performed with a fast-forward. This avoids losing information about the historical existence of a feature branch and groups together all commits that together added the feature.

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

Jakub Nar?bskialso mentionsthe config merge.ff:

Jakub Nar?bski提到配置merge.ff

By default, Git does not create an extra merge commit when merging a commit that is a descendant of the current commit. Instead, the tip of the current branch is fast-forwarded.
When set to false, this variable tells Git to create an extra merge commit in such a case (equivalent to giving the --no-ffoption from the command line).
When set to 'only', only such fast-forward merges are allowed (equivalent to giving the --ff-onlyoption from the command line).

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



The fast-forward is the default because:

快进是默认设置,因为:

  • short-lived branches are very easy to create and use in Git
  • short-lived branches often isolate many commits that can be reorganized freely within that branch
  • those commits are actually part of the main branch: once reorganized, the main branch is fast-forwarded to include them.
  • 短期分支在 Git 中很容易创建和使用
  • 短期分支通常会隔离许多可以在该分支内自由重组的提交
  • 这些提交实际上是主分支的一部分:一旦重组,主分支就会快速转发以包含它们。

But if you anticipate an iterative workflow on one topic/feature branch (i.e., I merge, then I go back to this feature branch and add some more commits), then it is useful to include only the merge in the main branch, rather than all the intermediate commits of the feature branch.

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

In this case, you can end up setting this kind of config file:

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

[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


The OP adds in the comments:

OP 在评论中添加:

I see some sense in fast-forward for [short-lived] branches, but making it the default action means that git assumes you... often have [short-lived] branches. Reasonable?

我在 [short-lived] 分支的快进中看到了一些意义,但是将其设为默认操作意味着 git 假设您......经常有 [short-lived] 分支。合理的?

Jefromi answers:

杰弗罗米回答:

I think the lifetime of branches varies greatly from user to user. Among experienced users, though, there's probably a tendency to have far more short-lived branches.

To me, a short-lived branch is one that I create in order to make a certain operation easier(rebasing, likely, or quick patching and testing), and then immediately delete once I'm done.
That means it likely should be absorbed into the topic branch it forked from, and the topic branch will be merged as one branch. No one needs to know what I did internally in order to create the series of commits implementing that given feature.

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

对我来说,一个短期分支是我为了使某个操作更容易(rebase、可能或快速修补和测试)而创建的分支,然后在我完成后立即删除。
这意味着它可能应该被吸收到它派生的主题分支中,并且主题分支将合并为一个分支。没有人需要知道我在内部做了什么,以便创建实现该给定功能的一系列提交。

More generally, I add:

更一般地说,我补充说:

it really depends on your development workflow:

  • if it is linear, one branch makes sense.
  • If you need to isolate features and work on them for a long period of time and repeatedly merge them, several branches make sense.

See "When should you branch?"

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

  • 如果它是线性的,一个分支是有意义的。
  • 如果您需要隔离功能并长时间处理它们并反复合并它们,那么几个分支是有意义的。

参见“什么时候应该分支?

Actually, when you consider the Mercurial branch model, it is at its core onebranch per repository(even though you can create anonymous heads, bookmarks and even named branches)
See "Git and Mercurial - Compare and Contrast".

实际上,当您考虑 Mercurial 分支模型时,它的核心每个存储库一个分支(即使您可以创建匿名头、书签甚至命名分支),
请参阅“Git 和 Mercurial - 比较和对比”

Mercurial, by default, uses anonymous lightweight codelines, which in its terminology are called "heads".
Git uses lightweight named branches, with injective mapping to map names of branches in remote repository to names of remote-tracking branches.
Git "forces" you to name branches (well, with the exception of a single unnamed branch, which is a situation called a "detached HEAD"), but I think this works better with branch-heavy workflows such as topic branch workflow, meaning multiple branches in a single repository paradigm.

默认情况下,Mercurial 使用匿名轻量级代码行,在其术语中称为“头部”。
Git 使用轻量级命名分支,通过内射映射将远程存储库中的分支名称映射到远程跟踪分支的名称。
Git“强制”你命名分支(嗯,除了单个未命名的分支,这种情况称为“分离的 HEAD”),但我认为这更适用于分支密集的工作流,例如主题分支工作流,意思是单个存储库范例中的多个分支。

回答by Jakub Nar?bski

Let me expand a bit on a VonC's very comprehensive answer:

让我展开一个有点VonC非常全面的回答



First, if I remember it correctly, the fact that Git by default doesn't create merge commitsin the fast-forwardcase has come from considering single-branch "equal repositories", where mutual pull is used to syncthose two repositories (a workflow you can find as first example in most user's documentation, including "The Git User's Manual" and "Version Control by Example"). In this case you don't use pull to merge fully realized branch, you use it to keep up with other work. You don't want to have ephemeral and unimportant fact when you happen to do a sync saved and stored in repository, saved for the future.

首先,如果我没记错的话,Git 默认情况下不会在快进的情况下创建合并提交的事实来自考虑单分支“平等存储库”,其中相互拉取用于同步这两个存储库(一个您可以在大多数用户文档中找到第一个示例,包括“The Git User's Manual”和“Version Control by Example”)。在这种情况下,您不使用 pull 来合并完全实现的分支,而是使用它来跟上其他工作。当您碰巧将同步保存并存储在存储库中时,您不希望拥有短暂和不重要的事实,以备将来使用。

Note that usefulness of feature branches and of having multiple branches in single repository came only later, with more widespread usage of VCS with good merging support, and with trying various merge-based workflows. That is why for example Mercurial originally supported only one branch per repository (plus anonymous tips for tracking remote branches), as seen in older revisions of "Mercurial: The Definitive Guide".

请注意,功能分支和在单个存储库中拥有多个分支的用处是后来才出现的,随着 VCS 的更广泛使用和良好的合并支持,以及尝试各种基于合并的工作流程。这就是为什么 Mercurial 最初只支持每个存储库一个分支(加上用于跟踪远程分支的匿名提示),如“Mercurial:权威指南”的旧版本中所见。



Second, when following best practicesof using feature branches, namely that feature branches should all start from stable version (usually from last release), to be able to cherry-pick and select which features to include by selecting which feature branches to merge, you are usually not in fast-forward situation... which makes this issue moot. You need to worry about creating a true merge and not fast-forward when merging a very first branch (assuming that you don't put single-commit changes directly on 'master'); all other later merges are of course in non fast-forward situation.

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

HTH

HTH