为什么git默认快速合并?
来自mercurial,我使用分支来组织function。 当然,我也希望在我的历史中看到这个工作stream程。
我使用git开始了我的新项目,并完成了我的第一个function。 合并function时,我意识到git使用快进,也就是说,如果可能的话,它直接将我的更改应用到主分支,忘记了我的分支。
所以想想未来:我是唯一一个在这个项目上工作的人。 如果我使用git的默认方法(快速合并),我的历史将导致一个巨大的主分支。 没有人知道我为每个function使用了一个单独的分支,因为最后我只有那个巨大的主分支。 这不看起来不专业?
通过这个推理,我不想快速合并,也不知道为什么它是默认的。 这有什么好处呢?
快速合并对短期分支是有意义的,但是在更复杂的历史中 ,非快速合并可能会使历史更容易理解,并且更容易恢复一组提交。
警告 :非快速转发也有潜在的副作用。 请查看https://sandofsky.com/blog/git-workflow.html ,避免使用“checkpoint commitits”的“no-ff”破坏对分或责备,仔细考虑是否应该作为master
的默认方法。
(来自nvie.com , Vincent Driessen ,发表“ 一个成功的Git分支模型 ”)
在开发中join完成的function
完成的function可以合并到开发分支中,将它们添加到即将发布的版本中:
$ 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
标志会导致合并始终创build一个新的提交对象,即使合并可以用快进执行。 这样可以避免丢失有关function分支历史存在的信息,并将所有提交的function组合在一起。
JakubNarębski也提到了configurationmerge.ff
:
默认情况下,Git在合并作为当前提交的后代的提交时不会创build额外的合并提交。 相反,当前分支的尖端被快速转发。
当设置为false
,这个variables告诉Git在这种情况下创build一个额外的合并提交(相当于从命令行提供--no-ff
选项)。
当设置为“only
”时,只允许这样的快进合并(相当于--ff-only
给出--ff-only
选项)。
快进是默认的,因为:
- 短命的分支在Git中很容易创build和使用
- 短命的分支往往孤立许多可以在该分支内自由重组的提交
- 这些提交实际上是主要分支的一部分:一旦重新组织,主要分支被快速转发以包括它们。
但是,如果您预期在一个主题/function分支上进行迭代工作stream程(即,我合并,然后返回到此function分支并添加更多的提交),那么仅在主分支中包含合并,而不是特征分支的所有中间提交。
在这种情况下,你可以最终设置这种configuration文件 :
[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回答:
我认为分支的生命周期因用户而异。 在有经验的用户中,可能会有更多的短命分支。
对我来说, 一个短暂的分支是我为了使某个操作更容易 (重新绑定,可能或快速修补和testing)而创build的分支 ,然后在完成后马上删除。
这意味着它可能应该被吸收到主题分支中 ,主题分支将被合并为一个分支。 没有人需要知道我在内部做了什么,才能创build一系列实现该特性的提交。
更一般地说,我补充说:
这真的取决于你的开发工作stream程 :
- 如果是线性的,则一个分支是有意义的。
- 如果需要隔离function并长时间工作并重复合并,则有几个分支是有意义的。
请参阅“ 什么时候分支? ”
实际上,当你考虑Mercurial分支模型时,它的核心是每个资源库一个分支 (尽pipe你可以创build匿名头,书签甚至命名分支 )
请参阅“Git和Mercurial – 比较和对比” 。
默认情况下,Mercurial使用匿名轻量级代码行,其术语称为“头”。
Git使用轻量级命名分支,使用内射映射将远程仓库中分支的名称映射到远程跟踪分支的名称。
Git“迫使”你命名分支(除了一个未命名的分支,这种情况被称为“ 分离HEAD ”),但我认为这适用于分支繁重的工作stream程,如主题分支工作stream程,意义单个存储库范例中的多个分支。
让我对VonC的非常全面的回答进行一下扩展:
首先,如果我没有记错,事实上Git默认情况下不会在快进的情况下创build合并提交 ,这是考虑到单分支“相同的存储库”,其中互相拉是用来同步这两个存储库(a工作stream程可以在大多数用户的文档中find,包括“The Git User's Manual”和“Version Control by Example”)。 在这种情况下,您不使用pull来合并完全实现的分支,而是使用它来跟上其他工作。 当你碰巧做一个保存并存储在版本库中的同步时,你不希望有短暂的和不重要的事实,保存将来。
请注意,function分支和单个存储库中具有多个分支的实用性仅在后来才出现,具有更好的合并支持的VCS的广泛使用以及尝试各种基于合并的工作stream程。 这就是为什么例如Mercurial最初只支持每个存储库一个分支(加上跟踪远程分支的匿名提示),如“Mercurial:权威指南”的旧版本中所见。
其次,当遵循使用特征分支的 最佳实践时,即特征分支都应该从稳定版本(通常从上一版本)开始,通过select要合并的特征分支来挑选和select要包括的特征, 通常不在快速前进的情况下 ……这使得这个问题没有实际意义。 在合并第一个分支时,你需要考虑创build一个真正的合并而不是快进(假设你不直接在'master'上进行单提交修改)。 所有其他的合并当然都是非快速的。
HTH