ChatGPT解决这个技术问题 Extra ChatGPT

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

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

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

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

通过这种推理,我不希望快进合并,也看不出它为什么是默认值。它有什么好?

注意:另请参阅 sandofsky.com/blog/git-workflow.html,并避免使用“no-ff”及其“检查点提交”打破平分或指责。
绝对不!在我的工作文件夹中,我有 7 个使用 git 的单人项目。让我换个说法:自从我提出这个问题以来,我开始了许多项目,所有项目都是通过 git 进行版本控制的。据我所知,只有 git 和 mercurial 支持本地版本控制,这对我来说是必不可少的,因为我已经习惯了。设置起来很容易,而且您始终拥有完整的历史记录。在小组项目中它甚至更好,因为您可以提交而不会干扰任何人使用您正在进行的代码。此外,我使用 github 来分享一些需要 git 的项目(例如 micro-optparse)。
@VonC 您更新的微小评论似乎几乎破坏了您使用 no-ff 的所有答案基础,即使仅针对特定分支也是如此。你愿意解释一下吗?在我看来,图表和整个 nvie 关于使用它的想法应该被避免,我现在看不到 no-ff 的一个好的用例。像这样打破指责确实是一个很大的禁忌。现在我很高兴我之前什至没有偶然发现或开始使用 gitflow
@Cawas 是的,-no-ff 很少是一个好主意,但仍然可以帮助保留功能内部历史记录,同时在主分支上只记录一次提交。当您不时合并其在主分支上的进展时,这对于较长的功能历史是有意义的。
顺便说一句,对于您的问题“那 [线性分支历史] 看起来不专业吗?”。使用默认的源代码系统并没有什么不专业的。这与专业无关。这是关于确定您订阅哪种分支哲学。例如,@VonC 链接到桑多夫斯基的文章,他在文章中支持使用快进作为一种优越的方法。没有对错之分,只是不同环境下的不同哲学。

C
Community

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

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

https://i.stack.imgur.com/vRdkr.png

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

$ 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ębskimentions config merge.ff

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

快进是默认设置,因为:

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

短暂的分支通常会隔离许多可以在该分支内自由重组的提交

这些提交实际上是主分支的一部分:一旦重组,主分支就会快速转发以包含它们。

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

在这种情况下,您最终可以设置 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

OP在评论中添加:

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

杰弗罗米回答:

我认为分支的生命周期因用户而异。但是,在有经验的用户中,可能倾向于拥有更多短命的分支。对我来说,一个短暂的分支是我创建的,目的是使某个操作更容易(变基、可能或快速修补和测试),然后在我完成后立即删除。这意味着它可能应该被吸收到它派生出来的主题分支中,并且主题分支将合并为一个分支。没有人需要知道我在内部做了什么来创建实现该给定功能的一系列提交。

更一般地说,我补充说:

这实际上取决于您的开发工作流程:如果它是线性的,那么一个分支是有意义的。如果您需要隔离特征并长时间处理它们并反复合并它们,那么几个分支是有意义的。请参阅“何时应该分支?”

实际上,当您考虑 Mercurial 分支模型时,它的核心是 one branch per repository(即使您可以创建 anonymous heads, bookmarks and even named branches
请参阅 "Git and Mercurial - Compare and Contrast"

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


哇,真快。 ;) 我知道 --no-ff 选项,但只有在我搞砸了我的功能后才知道快进。对于短命分支,我认为快进有些意义,但将其设为默认操作意味着 git 假设您通常拥有如此短命的分支。合理的?
@Florian:我相信这是对该过程的合理看法。我添加了一个配置示例,它将设置您想要管理合并到 master 的方式。
谢谢,配置文件应该有助于避免这样的陷阱。 :) 仅使用“git config branch.master.mergeoptions '--no-ff'”在本地应用此配置
@BehrangSaeedzadeh:变基是另一个主题(本页中没有提到一次):只要您没有将 feature 分支推送到公共仓库,您就可以在 master 之上将其变基为你想要的很多次。请参阅stackoverflow.com/questions/5250817/…
@BehrangSaeedzadeh:rebase 本身不会使历史成为线性的。 如何feature 分支的更改重新集成到 master 中,从而使所述历史成为线性与否。一个简单的快进合并将使它成为线性的。如果您在快进合并之前 清除了该feature 分支的历史记录,只留下重要的提交,那么这是有道理的,如stackoverflow.com/questions/7425541/… 中所述。
J
Jakub Narębski

让我稍微扩展一下 VonCvery comprehensive answer

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

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

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

高温高压


关于稳定版本的功能分支:如果我正在为下一个版本开发的功能取决于我已经开发并合并到主版本中的另一个功能的更改怎么办?当然这意味着我必须从 master 创建第二个功能分支?
@dOxxx:是的,有例外,例如一个分支建立在另一个分支上(直接或在将前一个分支合并到主分支之后)。