【问题标题】:How to push a "git replace --graft"如何推送“git replace --graft”
【发布时间】:2017-07-18 06:47:35
【问题描述】:

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

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

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

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

【问题讨论】:

    标签: git git-push


    【解决方案1】:

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

    例如:

    git push origin refs/replace/5c714d7798d1dc9c18d194fa6448680515c0ccdb
    

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

    推送所有替换:

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

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

    获取替换的注意事项

    如果某些远程 R 有替换,并且您希望将它们的所有替换到您的存储库中,请使用 git fetch <em>R</em> 'refs/replace/*:refs/replace/*'(或者如果您希望替换它们,则使用前缀 + 覆盖任何你已经拥有的)。您可以为任何给定的存储库和远程自动执行此操作。例如,如果您运行git config --edit,您会发现现有的origin 遥控器有几个如下所示的设置:

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

    只需添加一行:

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

    或:

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

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


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

    【讨论】:

    • 我注意到您需要使用git pull origin 'refs/replace/*:refs/replace/*' 来取回移植物。我有一个奇怪的情况,其他人也推送了一些移植,并且出于某种原因从干净的存储库中提取被认为是合并。是否有另一种可能导致这种行为的自动拉出的移植/替换?
    • @BruceAdams:你会想要git fetch 这些(正如我在第一段中所说),而不是git pull 他们,因为git pull 的意思是“进行提取,然后进行合并或rebase”,并且使用检索到的替换进行合并或 rebase 很少有好处。在任何情况下,不,这不是自动的——但您可以将 fetch 设置添加到 per-repo-per-remote 配置。 (使用--global 设置它有点不明智,因为在评论中不太适合:-))我会在答案中添加fetch 命令。
    【解决方案2】:

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

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

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

    【讨论】:

      【解决方案3】:

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

      参见Christian Couder (chriscool)@commit ee521eccommit f8e44a8commit 5876170commit 502d87b(2019 年 3 月 31 日)。
      (由 Junio C Hamano -- gitster -- 合并到 commit ce2a18f,2019 年 5 月 8 日)

      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'."
      

      此补丁通过在传递带注释的标记时使用底层提交的哈希来解决此问题。

      【讨论】:

        猜你喜欢
        • 2015-08-29
        • 2017-10-17
        • 1970-01-01
        • 2017-01-16
        • 2017-03-16
        • 2012-11-24
        • 1970-01-01
        • 2012-03-09
        • 2015-10-02
        相关资源
        最近更新 更多