【问题标题】:Why does git only allow fast-forward merges when pushing?为什么git在推送时只允许快进合并?
【发布时间】:2020-08-10 06:08:32
【问题描述】:

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

提示:更新被拒绝,因为远程包含您在本地没有的工作。

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

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

【问题讨论】:

  • 因为绝对不能保证可以以安全和明智的方式解决问题,而且可能还有其他人在跟踪同一个遥控器。这取决于将本地副本变为兼容状态,因为你有context
  • 你只是想打破 git 的分布式特性。事情发生在回购......你的回购。你说的是在不同的 repo 上进行操作。
  • @jonrsharpe 如果我们限制没有冲突的推送的 3 路合并怎么办?在这种情况下,合并应该是一个安全操作,无需重写历史记录,即使其他人正在跟踪该远程分支,他们也​​可以通过 git pull 获取最新更新。
  • @eftshift0 你能否提供一个更具体的“打破 git 的分布式特性”的例子?这种方法在什么情况下会失效?
  • 那有什么意义呢?既然如此微不足道就能解决,那你为什么不这样做呢? FWIW 我们很少合并,主要是pull -r 以保持线性历史。

标签: git github system conceptual


【解决方案1】:

如您所见,事实是git push 从不 进行合并。 (另一种说法——我认为也许更好——是 Git 的“快进合并”根本不是合并。)

作为matt noted,当你使用 GitHub 的 Pull Request 功能时,1 GitHub 向操作目标存储库的人提供了一个一键式的方法来真正地进行合并使用您git push-ed 的提交,前提是此合并可以自动完成。但是git push 甚至从不检查是否可能进行合并:它只是说需要进行合并或其他操作, 将您的新提交添加到目标存储库而不删除一些从目标分支提交。

快进的定义是:2

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

Git 命令git merge-base --is-ancestor 测试的那个有趣的符号≼(Unicode PRECEDES OR EQUAL TO,U+227c)是Git 命令git merge-base --is-ancestor 测试的,即旧提交的哈希ID是新提交的前身,或者与新提交的提交相同。换句话说,旧的提交仍然可以从新的提交中访问:参见Think Like (a) Git

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

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

无论如何,您可以自己编写。 (如果git push 为“由于非快进而被拒绝”保留一个特定的退出状态可能会有所帮助,但这里还有另一个问题:您可以在git push 中使用多个 names一次,可能有一些成功而另一些失败,每个原因都不同。)随意编写它 - 毕竟,Git 是一个大工具箱,而不是一个特定的解决方案 - 但请记住,一些合并失败合并冲突。您的命令需要将内部推送步骤限制为仅推送 current 分支。


1这确实是一个 GitHub 功能,而不是基本 Git 的一部分。其他虚拟主机站点确实提供了具有相同 name 和非常相似的操作的功能。显然有强烈的欲望。但是 Git 本身仍然没有它,至少出于技术原因。特别是,谁应该是合并提交的作者和提交者? GitHub 的回答是:等到有人点击按钮,然后使用那个人的信息。这个人通常不是首先提出拉取请求的人,但通常也不是一个特定的人:可能有一整组人被允许批准 PR。在任何情况下,总有一个人参与其中。

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

【讨论】:

  • 感谢您的详细回复!我认为现在只允许快进合并的决定是有意义的,因为我意识到即使 git 在推送时允许非冲突的 3 路合并,这也需要git pull 之后才能获得远程的新状态。所以这样做不会有任何好处,因为不是在推之前拉,而是在推之后拉 + git 必须在幕后做很多事情,这并不总是最好的主意:)
【解决方案2】:

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

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

【讨论】:

  • 感谢您的回复!如果我们只允许无冲突的合并,就不需要“有人在家”,那还会有问题吗?
【解决方案3】:

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

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

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

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

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2017-09-08
    • 2021-09-08
    • 2019-10-29
    • 2020-08-27
    • 2012-04-06
    • 1970-01-01
    • 2013-12-26
    • 2012-02-25
    相关资源
    最近更新 更多