【问题标题】:Join commit history of a subtree加入子树的提交历史
【发布时间】:2020-12-02 15:13:48
【问题描述】:

有一个 git 存储库,其中一部分是从另一个存储库复制粘贴的,并在某个时间点在一次提交中全部提交。

从那以后进行了许多更改。

我想跨多个分支将过去的提交历史添加到该子树。有没有办法轻松做到这一点?

【问题讨论】:

    标签: git git-commit git-history


    【解决方案1】:

    假设您拥有(或可以创建)一个带有您想要添加的历史记录的 git 存储库,那么它就可以完成。首先要决定是否要重写历史记录。

    在我看来,如果您可以重写历史记录,那么这是更好的选择。问题是它需要使用回购协议的每个人的一定程度的合作。 (对于这种彻底的更改,理想情况下,您可以安排一个日期,让每个人都将所有工作推送到原点 - 不必合并或任何东西,但必须全部放在一个单一的原点 repo 中 - 然后丢弃他们的克隆,这样他们就可以在重写后重新克隆。)

    但如果重写不切实际,还有另一种选择:您可以使用git replace 逐个回购地拼接历史记录。请参阅 git replace 文档以获取注意事项列表,但最明显的问题是您必须在每个想要查看组合历史记录的克隆上进行设置。

    在任何情况下,一旦你决定了要走哪条路并做了必要的准备(即,如果你要进行硬切换,让每个人都推动),你会想要导入另一个历史进入回购。您很可能希望创建一个原始的镜像克隆并完成他们的工作。

    git clone --mirror <origin-url>
    

    将添加的代码的历史存储库添加为远程并从中获取

    git remote add history <history-repo-url>
    git fetch history
    

    现在history 中的某个地方应该是一个提交,当代码添加到您的存储库时,文件是从该提交中复制的。这是历史可能看起来的简化图:

    A -- B -- C -- D -- E <--(master)
    
    a -- b -- c -- d <--(history/master)
    

    也许c 的代码作为提交B 的一部分被复制到你的repo 中。真实的历史可能更复杂,但无论如何我能想到的都无所谓。您需要做的是检查将文件添加到您的存储库的提交(示例中为B)。在示例中,这只是master 的第三个祖先(遵循第一父链接);实际上,您可能需要查找其提交 ID。

    git checkuot master~3
    

    现在,如果 only ting B 所做的是将 c 中的文件添加到您的 repo,那么您可能想要完全替换它。所以你会检查它的父母

    git checkout HEAD^
    

    如果B 进行了其他更改,那么您需要保留它们。您希望如何执行此操作可能取决于这些更改是否需要添加的代码。 (如果不是,您可能希望在合并历史之前提交其他更改;如果是,您可能希望在之后重新添加它们。)与其分支到三个相似但不同的过程,现在我假设文件被添加到他们自己的提交中。所以现在你已经签出了那个提交的父级。

    接下来,您将合并其他历史记录。在我们的示例中,它是 history/master 的父级;同样,您可能需要不同的表达式来识别提交,或者可能只需要查找其提交 ID。

    更大的问题是,您希望代码位于您的 repo 的子目录中;但大概它是另一个回购的根源。有几种方法可以解决这个问题;这是其中之一。

    git merge --s ours --no-commit --allow-unrelated history/master^
    git read-tree --prefix=<path-to-subdirectory> history/master^
    git commit
    

    (您的工作树现在可能缺少您合并的文件,因此您会看到未暂存的删除;您可以使用git restore 刷新工作树。)

    现在你有这样的东西:

              A -- B -- C -- D -- E <--(master)
               \
                M <-(HEAD)
               /
    a -- b -- c -- d <--(history/master)
    

    M 应该与B 具有相同的内容(TREE)(您可以使用git diff 进行验证),但它具有添加的历史记录。所以剩下的就是重新养育C。这个重新育儿步骤是彻底重写的地方;因此,如果您不打算进行重写,您可以在此处标记新的合并并留给个人克隆使用git replace

    您可以使用git filter-branch 进行重新育儿;但话说回来git filter-branch 是一个旧工具,它的文档建议您改用git filter-repo。我不熟悉较新的工具,可能不应该花时间宣传使用旧工具的方法,所以在这一步我会推荐你​​参考文档。 (通常,如果你用 google git &lt;any-git-command&gt; 搜索任何命令的官方文档并不难,只要你知道要使用哪个命令。)

    最后,您可以删除history 远程,然后您有一个适合用作origin(或从中创建新源)的新存储库。

    请注意,此过程确实会在您的存储库中留下两个不同的历史记录。从“当前”提交中,您将能够“查看”任何文件的完整历史记录,但是如果您将 checkout 放入一个历史记录,那么另一个将从您的索引和工作树中消失,直到您移回较新的共享历史记录。

    拥有一个真正统一的历史会相当困难,但在技术上并非不可能。您可以使用filter-repo 重写“其他”历史记录,使其看起来始终位于其子目录中,但是您必须弄清楚如何合并历史记录的时间线,而我只看到手动方法来做到这一点.

    【讨论】:

    • 感谢您的详细回答。从 filter-repo 的文档看来,除了 git-replace 之外,这只是一个步骤:git replace --graft $commit-id $graft-idgit filter-repo --force
    猜你喜欢
    • 2018-08-06
    • 2014-06-30
    • 2020-08-02
    • 1970-01-01
    • 1970-01-01
    • 2015-10-29
    • 2011-10-26
    • 2013-01-26
    • 1970-01-01
    相关资源
    最近更新 更多