【问题标题】:How do I combine Git repositories?如何合并 Git 存储库?
【发布时间】:2018-02-10 05:11:58
【问题描述】:

我试图通过嫁接历史将 2 个存储库合并为 1 个。我认为这是获得干净的线性历史记录的最简单方法。

我尝试通过将另一个作为远程添加到初始存储库来做到这一点:

git init    
echo "Hello" > Hello.txt
git add -A
git commit -m "initial commit"
git remote add b c:\pathToB
git replace --graft master b/master   

树看起来不错,问题是我没有得到当前目录下repo B的内容。

我也试过这个(提交哈希是 b/master 的提示)

git filter-branch -f --parent-filter 'sed "s~^\$~-p b34fc98295273c41aeb203213ad5fe4f95ba205b~"'

当我检查树时,我可以看到每个提交都包含它的更改,但是主存储库中的第一次提交基本上是删除了存储库 B 带来的所有更改:

所有原始提交都没有删除文件。

我错过了什么,我使用过滤器分支和移植错误吗?还是我只需要使用cherry-pick 或rebase 来保留当前目录中的所有更改?

【问题讨论】:

    标签: git git-branch git-rebase git-history-graph .git-info-grafts


    【解决方案1】:

    TL;DR

    您需要合并树木。例如,您可以使用git merge。如果您的 Git 足够新,您将需要 --allow-unrelated-histories 标志。这样的合并会使用一棵空树作为合并基,所以它认为从合并基到L的变化是“在提交L中添加所有文件”并且从合并基础更改为 R 是“在提交 R 中添加所有文件”(其中定义了 LR我喜欢为 git merge 定义它们的方式;参见,例如,this answer)。

    提交是快照。 (我希望这部分没有争议。)

    Git 的git replace 对象实际上是替换。也就是说,每当 Git 要通过其哈希 ID 1234567...(或其他)查​​找对象时,Git 首先检查:refs/replace/ 中是否列出了 1234567... 的替代品? 如果有这样一个替换,Git 通过将 refs/replace/1234567... 解析为 不同的 哈希 ID 并读取该对象来读取替换对象。

    所以:

    git init    
    echo "Hello" > Hello.txt
    git add -A
    git commit -m "initial commit"
    

    这个序列首先创建一个新的、完全空的存储库(假设还没有 Git 存储库,因此 git init 进行创建)。 echo 命令在工作树中创建一个文件; git add -A 将工作树文件添加到索引中(这具有将文件数据作为 blob 对象存储到存储库中的副作用,尽管这在这里并不重要)。最后一步,git commit ...,创建一个 tree 对象来保存快照(其中有一个文件 Hello.txt,其中包含您放入的内容)然后创建一个提交对象,例如1234567... 将您列为作者和提交者,具有“初始提交”消息,使用创建的树来保存快照,并且——因为这是有史以来的第一次提交——没有父提交:这是一个新的根提交。

    现在我们有了:

    git remote add b c:\pathToB
    

    这只是为新的远程 b 添加 URL(和 fetch 设置)。

    少了一个步骤:

    git fetch b
    

    调用另一个 Git(在您的本地机器上,因为 c:\pathToB 是本地的——通常我们会通过 HTTPS 或 SSH 或其他方式调用另一台机器上的 Git,但这很好)并从中下载对象.具体来说,它会获取您没有的任何提交(这是他们的所有提交)以及完成这些提交所需的任何对象(这是他们所有的其他对象)并将它们复制到您的存储库中。这些都有一些 ID 不是 1234567...,因为每个提交都有一个保证唯一的哈希 ID。

    最后:

    git replace --graft master b/master
    

    这会告诉您的 Git 设置其中一个替换项。特别是,它说它应该将master 标识的提交(我们在上面说过是1234567...)复制到一个新的提交,就像原来的一样,除了它有一个parent 哈希,即提交 b/master 标识的任何内容。假设b/master 标识提交fedcba9...

    假设git replace 提交的新提交具有ID 8888888...。它的内容是:

    • 您作为作者和提交者,从1234567... 复制或重新创建(这并不重要);
    • 1234567... 复制或重新创建的日期戳(这也无关紧要);
    • 1234567...复制的消息;
    • 1234567...复制的树(快照)(这部分很关键);和
    • fedcba9... 的父哈希。

    你现有的master 仍然标识1234567...,但是现在当你让Git 显示1234567... 时,你的Git 看到refs/replace/1234567... 存在并说“不要使用它一,改用8888888...”。因此,您的 Git 查找对象 8888888... 并找到您使用 1234567... 保存的树,其中只有一个文件。提交之前这个——替换替换1234567...——有不同的文件,所以从那时到现在的改变必须是:删除所有这些文件,并创建Hello.txt。 p>

    要让您下一个保存的快照以某种方式使用两个树,您需要将master 的树与b/master 的树结合起来。那永远不会是 git replace(尽管是 git merge 还是其他/更高级的取决于您)。

    【讨论】:

    • 感谢您写下这个答案,这是我怀疑发生的事情。 filter-branch 怎么样,应该遍历树并重写所有提交,不应该能够进行类似 rebase 的合并吗?如果移植物不能带来变化,那么它们有什么用?
    • 确实 filter-branch 复制每个提交,应用过滤器;但您必须提供过滤器。您想要的过滤器必须完成所有的树合并,这是困难的部分。嫁接有利于拼接一些人工历史,这有时是一种在 filter-branch 的“复制所有提交”通过之前进行设置的体面方式,之后您将原始提交折腾以支持新复制的提交使替换(移植)永久化(在新副本中)。
    猜你喜欢
    • 2018-04-25
    • 2017-02-03
    • 2010-11-28
    相关资源
    最近更新 更多