如何合并 Git 个存储库?
How do I combine Git repositories?
我正在尝试通过嫁接历史将 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 带来的所有更改:
None 个原始提交正在删除文件。
我错过了什么,我使用的过滤器分支和嫁接错误吗?还是我只需要使用 cherry-pick 或 rebase 来保留当前目录中的所有更改?
TL;DR
您需要组合树木。例如,您可以使用 git merge
。如果您的 Git 足够新,您将需要 --allow-unrelated-histories
标志。这样的merge会使用一个空树作为merge base,这样它认为从merge base到L的变化是"add all files in commit L",从merge base到[=]的变化93=]R 是 "add all files in commit R"(其中 L 和 R 是按照我喜欢的方式定义的git merge
;参见,例如,this answer).
长
提交是快照。 (我希望这部分没有争议。)
Git 的 git replace
对象,从字面上看,是 替换 。也就是说,每当 Git 将要通过其哈希 ID 1234567...
(或其他)查找对象时,Git 首先检查:是否列出了替代 1234567...
in refs/replace/
? 如果有这样的替换,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...
,将您列为作者和提交者,具有消息 "initial commit",使用创建的树来保存快照,并且 - 因为它是第一次提交 - 没有父提交:这是一个新的根提交。
现在我们有:
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
标识的提交复制到一个新的 提交,就像 原始的,除了它有一个 parent 散列,这是提交 b/master
标识的任何内容。假设 b/master
标识提交 fedcba9...
.
假设 git replace
提交的新提交具有 ID 8888888...
。其内容为:
- 您作为作者和提交者,从
1234567...
复制或重新创建(这并不重要);
- 从
1234567...
复制或重新创建的日期戳(这也不重要);
- 从
1234567...
复制的消息;
- 从
1234567...
复制的树(快照)(这部分很关键);和
fedcba9...
的父散列。
您现有的 master
仍然识别 1234567...
,但现在当您要求 Git 显示 您 1234567...
时,您的 1234567...
Git 看到 refs/replace/1234567...
存在并说 "don't use that one, use 8888888...
instead"。所以你的 Git 查找对象 8888888...
并找到你用 1234567...
保存的树,其中只有一个文件。 before这个提交——替换1234567...
——有不同的文件,所以从那时到现在的变化必须是:删除所有这些文件,并创建Hello.txt
代替。
要使您的 下一个 保存的快照以某种方式使用 两个 树,您需要为您的 master
与 b/master
的树。那永远不会是 git replace
(尽管它是 git merge
还是什么 different/fancier 取决于您)。
我正在尝试通过嫁接历史将 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 带来的所有更改:
None 个原始提交正在删除文件。
我错过了什么,我使用的过滤器分支和嫁接错误吗?还是我只需要使用 cherry-pick 或 rebase 来保留当前目录中的所有更改?
TL;DR
您需要组合树木。例如,您可以使用 git merge
。如果您的 Git 足够新,您将需要 --allow-unrelated-histories
标志。这样的merge会使用一个空树作为merge base,这样它认为从merge base到L的变化是"add all files in commit L",从merge base到[=]的变化93=]R 是 "add all files in commit R"(其中 L 和 R 是按照我喜欢的方式定义的git merge
;参见,例如,this answer).
长
提交是快照。 (我希望这部分没有争议。)
Git 的 git replace
对象,从字面上看,是 替换 。也就是说,每当 Git 将要通过其哈希 ID 1234567...
(或其他)查找对象时,Git 首先检查:是否列出了替代 1234567...
in refs/replace/
? 如果有这样的替换,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...
,将您列为作者和提交者,具有消息 "initial commit",使用创建的树来保存快照,并且 - 因为它是第一次提交 - 没有父提交:这是一个新的根提交。
现在我们有:
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
标识的提交复制到一个新的 提交,就像 原始的,除了它有一个 parent 散列,这是提交 b/master
标识的任何内容。假设 b/master
标识提交 fedcba9...
.
假设 git replace
提交的新提交具有 ID 8888888...
。其内容为:
- 您作为作者和提交者,从
1234567...
复制或重新创建(这并不重要); - 从
1234567...
复制或重新创建的日期戳(这也不重要); - 从
1234567...
复制的消息; - 从
1234567...
复制的树(快照)(这部分很关键);和 fedcba9...
的父散列。
您现有的 master
仍然识别 1234567...
,但现在当您要求 Git 显示 您 1234567...
时,您的 1234567...
Git 看到 refs/replace/1234567...
存在并说 "don't use that one, use 8888888...
instead"。所以你的 Git 查找对象 8888888...
并找到你用 1234567...
保存的树,其中只有一个文件。 before这个提交——替换1234567...
——有不同的文件,所以从那时到现在的变化必须是:删除所有这些文件,并创建Hello.txt
代替。
要使您的 下一个 保存的快照以某种方式使用 两个 树,您需要为您的 master
与 b/master
的树。那永远不会是 git replace
(尽管它是 git merge
还是什么 different/fancier 取决于您)。