为什么 git 在推送时只允许快进合并?

Why does git only allow fast-forward merges when pushing?

所以我知道,当您执行 git push 时,git 会尝试将更改从本地分支集成到相应的远程分支。但是,如果分支已经分叉,git 将不允许您推送,说

hint: Updates were rejected because the remote contains work that you do not have locally.

当用户推送成功时,他希望他刚刚推送的远程分支是他自己的镜像。如果我们在推送时允许 3 路合并(只有在没有冲突的情况下),那么远程分支显然不会匹配他自己的分支,因为它有我们本地副本不知道的提交,以及合并提交。但是,如果需要 3 路合并,git 就不能在推送后进行拉取吗?以这种方式推动时允许三向合并有什么缺点,为什么 Git 的创建者决定只允许快进提交?

下图说明情况:https://imgur.com/a/800v5Eq

因为根据定义,您要推送到的 git 的远程(在线)副本上有 "nobody home"。那么如果出现问题(冲突)谁来解决,如何解决?

但是,如果您使用 GitHub 或类似的方法,则有一种解决方法:提出合并请求。这样,在服务器 上就有一个解决问题的接口 ,因此 Pull Request 可以执行任何类型的合并。

原因是因为在没有强制推送的情况下覆盖远程会丢失数据,而其他行为(例如自动执行合并)会令人惊讶且难以推理。

在很多情况下,执行合并时,结果是非常合乎逻辑和直接的,因为一个分支的更改相对较少。但是,有时合并会产生令人惊讶或意外的行为,例如产生无法编译的干净合并。因此,如果您的更改不是分支上内容的超集,最好快速放弃并让用户决定如何处理它。

此外,如果用户确实想要当前的行为,那么仍然必须有一个选项,以及一个选项来覆盖当前分支,因为后者在变基时很有用。因此,不是有两个选项(常规和强制推送),而是需要三个选项。

最后,在某些工作流程中,人们不希望合并提交,因此执行合并会创建不希望的非线性历史记录。同样,即使确实使用基于合并的工作流程,也经常需要额外的合并提交(例如,来自 master 的合并),并且用户会不高兴地发现他们意外地弄乱了他们的历史记录。

如您所见,事实是 git push 从不 进行合并。 (另一种表达方式——我认为可能更好——是 Git 的 "fast-forward merge" 根本不是合并。)

一样,当您使用 GitHub 的 Pull Request 功能时,1 GitHub 向操作人员提供目标存储库,一种一键式单击方法,可以使用您 git push 编辑的提交真正、实际地进行合并,前提是此合并可以自动完成。但是 git push 甚至从不检查是否可以合并:它只是说合并或其他一些操作 将是 所必需的,以将您的新提交添加到目标存储库而不从目标分支删除一些提交。

快进的定义是:2

  • Cold 为当前由名称 N 标识的提交哈希 ID。
  • Cnew 为您建议 N 应该标识的提交哈希 ID。
  • 操作快进当且仅当ColdCnew .

Git 命令 git merge-base --is-ancestor 测试的有趣符号 ≼(Unicode PRECEDES OR EQUAL TO,U+227c)看起来像一个弯曲的小于或等于,即,旧提交的哈希 ID 是新提交的前身,或者与新提交的哈希 ID 相同。换句话说,旧提交仍然可以从新提交 reachable:见 Think Like (a) Git.

合并提交具有特殊的 属性,即拥有多个父提交哈希 ID,使得整个提交 DAG 的多个子图 可访问 。因此,在添加新图片段的同时保持一些旧子图可访问的简单方法是添加合并提交。 git merge 命令可以添加这样的提交,但在此过程中,它对作为合并输入的三个提交 快照 做了一些可以说有用的事情。

由于 git push 可以 检测到推送失败并自动调用 git fetchgit merge。您可以自己编写一个脚本来做同样的事情。但这并不总是正确的做法,因此它还需要更多的配置旋钮。我自己认为 git pull,它为你运行 fetch 然后合并,已经做了太多了,git push 的这样一个控制旋钮将是 far 太多了。

无论如何,您可以自己写。 (如果 git push 为 "rejected due to non-fast-forward" 保留一个特定的退出状态可能会有所帮助,但这里还有另一个问题:您可以 git push 多个 names 一次完成,并且可能有一些成功,而另一些失败,原因各不相同。)请自行编写 — Git 毕竟是一大箱工具,而不是一个特定的解决方案 — 但是请记住,某些合并因合并冲突而失败。您的命令需要将内部推送步骤限制为仅推送 current 分支。


1这确实是一个 GitHub 功能,而不是基础 Git 的一部分。其他网站托管站点确实提供了具有相同 名称 和非常相似操作的功能。显然有强烈的愿望。但是 Git 本身仍然没有它,至少出于技术原因。特别是,谁应该是合并提交的作者和提交者? GitHub 的回答是:等到有人点击按钮,然后使用那个人的 信息。这个人通常 不是 一开始提出拉取请求的人,但通常也不是一个特定的人:可能有一整群人被允许批准 PR。无论如何,循环中总是有人。

2嗯,这是a的定义。您还可以制定其他等效的。