如何推送“git replace --graft”

How to push a "git replace --graft"

提问人:Raedwald 提问时间:2/27/2017 最后编辑:AmericanUmlautRaedwald 更新时间:3/11/2023 访问量:5114

问:

我曾经记录过一个版本实际上是两个版本之间的(手动执行的)合并:git replace --graft

 git replace --graft <merged-version> <predecessor-version> <version-merged-from>

这对我的(本地、私有)存储库进行了更改。

我现在想通过将更改“推送”到我们的共享存储库(在 Github 上,碰巧是这样)来使团队的其他成员可以使用该更改。我该怎么做?一个简单的似乎没有效果。git push

git git-push

评论


答:

25赞 torek 2/28/2017 #1

移植物存在于层次结构中。(或者,最好说,“它们的存在归功于”这些引用。因此,要将它们从一个存储库传输到另一个存储库,您必须推送或获取此类引用。refs/replace/

例如:

git push origin refs/replace/5c714d7798d1dc9c18d194fa6448680515c0ccdb

当提交有替换时(在我的情况下,替换是新的提交对象,即 Git 通常不会“看到”,而是寻找替换对象)。5c714d7798d1dc9c18d194fa6448680515c0ccdbceba978ce6dad3b52d12134f4ef2720c5f3a90025c714d7ceba978

要推送所有替换项,请执行以下操作:

git push origin 'refs/replace/*:refs/replace/*'

(有时需要引号来防止 shell 使用星号;确切的时间以及使用哪种引号在某种程度上取决于 shell,尽管单引号和双引号都适用于所有 Unix-y shell)。

有关获取替换件的注意事项

如果某些远程 R 有替换项,并且您希望将其所有替换项都引入存储库,请使用(如果您希望它们的替换项覆盖您已有的任何替换项,请使用前缀)。您可以对任何给定的存储库和远程存储库自动执行此操作。例如,如果运行 ,您会发现现有的遥控器具有如下所示的几个设置:git fetch R 'refs/replace/*:refs/replace/*'+git config --editorigin

[remote "origin"]
    url = ...
    fetch = +refs/heads/*:refs/remotes/origin/*

只需添加以下行:

    fetch = refs/replace/*:refs/replace/*

艺术

    fetch = +refs/replace/*:refs/replace/*

让你的 Git 带来他们的 Git 的 .(注意:这里不需要引号,因为 shell 不会处理这一行。前导加号的含义与往常相同:1 没有它,如果您已经有一些参考,则保留您的并忽略他们的。使用前导加号,您可以丢弃您的加号并改用他们的加号。与标签一样,如果您的引用和它们的引用已经匹配,那么无论您是保留您的标签还是用他们的标签替换您的标签都无关紧要;仅当您对某个引用应该命名哪个对象有不同的想法时,这才重要。refs/replace/*


1事实上,前导加号的“通常含义”取决于引用是否应该移动,例如分支名称,或者不应该移动,例如标记名称。加号设置强制标志,即“始终采用建议的新设置”,但对于预计“向前移动”的分支名称,当且仅当它是“向前”(或“快进”)移动时,才允许在没有强制的情况下进行更新。Git 最初也将此规则应用于其他引用(如标签),但 Git 人员在 Git 1.8.2 中修复了它。我不清楚 Git 将哪些规则应用于引用,这些规则不应该移动,但不会像标签那样被特别对待。refs/replace/

评论

0赞 Bruce Adams 6/19/2017
我注意到您需要用于取回移植物。我有一个奇怪的情况,其他人也推送了一些移植物,并且出于某种原因从干净存储库中提取被认为是合并。是否有另一种自动拉动的移植物/替换物可能会导致这种行为?git pull origin 'refs/replace/*:refs/replace/*'
0赞 torek 6/19/2017
@BruceAdams:你会想要这些(正如我在第一段中所说),而不是它们,因为这意味着“做一个获取,然后做一个合并或变基”,而且使用检索到的替换来合并或变基很少是好的。无论如何,不,这不是自动的,但您可以向每个存储库每个远程配置添加设置。(由于不适合评论的原因,设置它有点不明智:-))不过,我会将命令添加到答案中。git fetchgit pullgit pullfetch--globalfetch
2赞 VonC 5/12/2019 #2

使用时要小心:Git 2.22(2019 年第 2 季度)修复了一个错误,当给定指向提交的标签时,“”在编写替换引用之前无法剥离标签,这没有意义,因为该功能想要模仿的旧移植机制只允许将一个提交对象替换为另一个提交对象。git replace --graftgit replace --graft

参见 Christian Couder (chriscoolcommit ee521ec、commit f8e44a8、commit 5876170commit 502d87b(2019 年 3 月 31 日)。
(由 Junio C Hamano -- gitster -- 在 2019 年 5 月 8 日的 commit ce2a18f 中合并)

replace:首先将标签传递给--graft

将标签作为第一个参数传递给 时, 接受它并将基础提交用作 将被替换的提交。git replace --graft

这已经适用于轻量级标签,但不幸的是 对于带注释的标签,我们一直在使用标签对象的哈希值 而不是基础提交的哈希值。

特别是我们会将标记对象的哈希值传递到我们可能会因错误而失败的地方 喜欢:replace_object_oid()

"error: Objects must be of the same type.
'annotated_replaced_object' points to a replaced object of type 'tag'
while 'replacement' points to a replacement object of type 'commit'."

此修补程序通过在传递带注释的标签时使用底层提交的哈希来修复此问题。

6赞 Ichthyo 7/30/2021 #3

为了完整性:git 替换是“虚拟的”,而不是永久性的。纵的提交的原始版本仍然存在 - 它只是被替换提交所掩盖。公认的答案描述了如何将这些“虚拟替换”也发布到共享存储库中,以及如何安排在获取时获取此类替换。通常这是正确的做法。

但是,有时我们希望使这样的历史记录修复永久化。使用 Git,做到这一点的唯一方法是合成一个新的历史。这可以通过(脆的、低级的)或 Github 上非常好的工具 git-filter-repo 来完成(由 Git 项目官方推荐)。git filter-branch

但请注意,无法强制共享存储库的其他用户使用重写的历史记录。您需要要求他们切换,例如通过重置他们的主分支或切换到另一个新分支。因此,在公共设置中,永久改写历史是不可行的;但是,对于封闭的用户组,例如在商业设置中,这是一个非常有效的选择(并且可能确实需要删除一些合理的内容,例如凭据)