为什么git默认快速合并? [英] Why does git fast-forward merges by default?

查看:142
本文介绍了为什么git默认快速合并?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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



我使用git开始了我的新项目并完成了我的第一个功能。在合并这个特性时,我意识到git使用快进,即如果可能的话,它直接将我的修改应用到master分支,并忘记了我的分支。

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



通过这个推理,我不想快速前进合并,并不明白为什么它是默认的。有什么好处呢?

解决方案

快速合并对短期分支有意义,但在更多, Vincent Driessen ,发布 一个成功的Git分支模型


在开发中包含完成的功能

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



  $ git checkout develop 
切换到分支'develop'
$ git merge --no-ff myfeature
更新ea1b82a..05e9557
(变更摘要)
$ git branch -d myfeature
已删除的分支myfeature(是05e9557)。
$ git push origin develop




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

JakubNarębski提及 config merge.ff


默认情况下,Git在合并作为当前提交的后代的提交时不会创建额外的合并提交。相反,当前分支的尖端被快速转发。

当设置为 false 时,这个变量告诉Git在这样的地方创建一个额外的合并提交(相当于从命令行给出 - no-ff 选项)。

仅当设置为'',只允许这样的快进合并(相当于从命令行给出 -ff-only 选项)。







快进是默认的,因为:


  • 短期分支很容易在Git中创建和使用

  • 短暂分支常常隔离许多可以在内部自由重组的提交该分支
  • 这些提交实际上是主分支的一部分:一旦进行了重新组织,主分支就会被快速转发以包含它们。



但是,如果您预计在一个主题/功能分支上有一个迭代工作流程(即,我合并,那么我会返回到此功能b ranch并添加一些提交),那么仅在主分支中包含合并,而不是功能分支的所有中间提交是有用的。



在此那么您最终可能会设置这种配置文件

  [branchmaster] 
#这是应该添加到git-merge
#中的cmdline选项列表我将提交合并到主分支中。

#选项--no-commit指示git默认不提交合并
#。这允许我在提交之前对提交日志
#消息进行一些最终调整。我经常使用它来为
#合并消息添加额外信息,或者在提交消息
#中将我的本地分支名称重写为分支名称,这对于git日志的临时读者来说是更易于理解的。

#选项--no-ff指示git总是记录合并提交,即使合并的分支
#可以被快速转发。这通常是
#的情况,当你创建一个追踪master的短期主题分支时,在主题分支上做一些
#的变化,然后把这些变化合并到剩下的
#master中当您在
#主题分支上完成工作时保持不变。在这种情况下,主分支可以进行快进(即
#是主分支的尖端可以更新为指向
#的主题分支),这就是git所做的默认。通过设置--no-ff
#选项,git创建了一个真正的合并提交,它记录了
#另一个分支被合并的事实。我发现这更容易理解并阅读
#日志。

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






OP在评论中增加了:


我认为快速前进-lived]分支,但将其设为默认操作意味着git会假设您...经常有[短命]分支。合理吗?


Jefromi回答:
$ b


我认为分支机构的生命周期因用户而异。然而,在有经验的用户中,可能存在更多短命分支的趋势。 对我来说,短期分支是我为了使某个操作更轻松而创建的分支(重新绑定,可能或快速修补和测试),然后立即删除一次。

这意味着可能应该吸收到分支的主题分支中,并且主题分支将合并为一个分支。没有人需要知道我在内部做了什么,才能创建实现该特定功能的一系列提交。

更一般地说,我补充说:


它确实取决于您的开发工作流程




  • 如果它是线性的,则一个分支是有意义的。

  • 如果您需要隔离功能并长时间处理它们并重复合并它们,则有几个分支是有意义的。



请参阅

其实,当您考虑Mercurial分支模型,它的核心是 每个存储库一个分支(即使您可以创建匿名头像,书签,甚至命名分支

参见


Mercurial,默认情况下,使用匿名轻量级代码行,其术语称为头。

Git使用轻量级名称分支,注入将映射远程存储库中的分支名称映射到远程跟踪分支的名称。
Git强制您命名分支(当然,除了一个未命名的分支,这是一种称为一个
分离HEAD ),但我认为这个工程更好地利用分支机构繁重的工作流程,例如主题分支工作流程,意味着单个存储库范例中的多个分支。



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

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.

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?

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?

解决方案

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.


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

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-ff flag 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.

Jakub Narębski also mentions the config 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-ff option from the command line).
When set to 'only', only such fast-forward merges are allowed (equivalent to giving the --ff-only option from the command line).


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.

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:

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?

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.

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 one branch per repository (even though you can create anonymous heads, bookmarks and even named branches)
See "Git and Mercurial - Compare and Contrast".

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.

这篇关于为什么git默认快速合并?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆