1

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

从那时起,发生了许多变化。

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

4

1 回答 1

1

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

在我看来,如果您可以进行历史重写,那么这是更好的选择。问题是它需要使用回购协议的每个人的一定程度的合作。(对于这种彻底的更改,理想情况下,您可以安排一个日期,让每个人都将所有工作推送到原点 - 不必合并或任何东西,但必须全部放在一个单一的原点 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)

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

git checkuot master~3

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

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应该具有与(您可以使用 验证)相同的内容( TREE),但它具有添加的历史记录。所以剩下的就是 re-parent 。这个重新育儿步骤是彻底重写的地方;因此,如果您不打算进行重写,您可以在此处标记新的合并并留给单独的克隆使用。Bgit diffCgit replace

您可以使用git filter-branch; 但又git filter-branch是一个旧工具,它的文档建议您改用它git filter-repo。我不熟悉较新的工具,可能不应该花时间宣传使用旧工具的方法,所以在这一步我将向您推荐文档。(通常,如果你用谷歌搜索git <any-git-command>任何命令的官方文档都不难,只要你知道要使用哪个命令。)

最后,您可以删除history遥控器,然后您就有了一个适合用作origin(或从中创建新来源)的新仓库。

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

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

于 2020-08-12T20:55:43.630 回答