This question already has answers here:
How to move files from one git repo to another (not a clone), preserving history

(16个回答)


3年前关闭。




我有两个git存储库,分别是A和B,都包含一个名为file1.cc的文件。
是否可以将存储库A中的file1.cc的历史记录 merge/复制到存储库B中的file1.cc?

问题是我们已经将文件从存储库A移到存储库B,所有文件的历史记录都丢失了。但是现在,一些开发人员已经开始着手处理repo B并 push 他们的更改。因此,现在我希望将某些文件从存储库A到存储库B的 merge/复制历史记录仅适用于某些文件。有可能这样做吗?还是曾经丢失的文件历史会永远丢失?

请帮忙。提前致谢。

最佳答案

可以做到,但可能并不容易。但是首先要注意的是:没有“移动文件的历史记录”。只有移动的提交,因此,如果您想要代表文件子集历史的提交,那么创建这些提交是第一个挑战。

最简单的事情是转移所有历史记录。 (实际上,如果碰巧您将Repo B作为Repo A的浅表克隆,则可以将其浅化并完成。但是我想那不是您创建Repo B的方式...)

无论如何,由于您正从存储库A移至存储库B,因此可能有一些您特别想删除的历史记录。这可能是一个完整的主题,但让我们假设您确实只想要几个文件的历史记录。

在特殊情况下,您想要的所有文件(没有其他文件)都在子目录中,并且您想要(或至少可以接受)将这些文件移动到存储库的根目录中,则可以将filter-branch--subdirectory-filter一起使用。

更一般而言,如果我们假设路径不应更改,并且想要的文件可以在树中的任何位置,则可以将filter-branch--index-filter一起使用。

git filter-branch --index-filter 'git rm --cached --ignore-unmatch each file or *glob* you do NOT want' --prune-empty -- all

如果 repo 包含大量提交,则可能需要一段时间。如果要发送给rm的文件列表不平凡,则可能要在 shell 程序脚本中放置多个git rm命令,并将其用作--index-filter参数,而不是如上所示对其进行内联。

好吧,希望以某种方式您有想要移植到 repo 协议(protocol)B中的历史记录。
cd repo-b
git remote add repo-a path/to/repo-a
git fetch repo-a

现在,您在 repo B中:
... A -- B <--(repo-a/master)
  \
   (repo-a/other-branches-maybe)

B' -- C -- D (master)(origin/master)

因此,我在这里假设,将 repo A中最后一个TREE提交中的master-我们的历史记录从中重写的那个B-或至少那棵树的一部分,作为 repo B中的根提交导入。

现在,您有三个选择:重新设定父级,重新设定基准或替换

由于我认为最近的历史记录状态比旧的历史记录状态更重要,并且仅添加了旧的历史记录以供引用,因此最安全的方法是将C重置为B。 (您可以选择将B'改为A,但我认为这没有太大的区别...)

因此,您可以从https://git-scm.com/docs/git-filter-branch上的filter-branch文档中提取
# be sure you're on master
echo "$commit-id $graft-id" >> .git/info/grafts
git filter-branch $graft-id..HEAD

其中$commit-idB的SHA,而$graft-idC的SHA

重新设置可能会更简单一些(假设历史记录之间保持一定程度的一致性),但是可能会导致您最终在D处修改树。如果您决定尝试重新设置基准,那将是
git rebase --onto repo-A/master B' master

其中B'是存储库B根提交的SHA ID。 (交替
git rebase --interactive --onto repo-A/master --root master

然后删除B'的条目。)

这些选项中的任何一个都将重写commit CD。 (即使重新 parent 确定TREE不变,提交仍将被替换。)您的开发人员必须将其视为上游资源库(请参阅“从上游资源库恢复”下的git rebase文档)。为了减轻这种情况,我通常建议进行协调过渡,在此情况下,开发人员将检查他们拥有的所有内容,丢弃其克隆,然后进行重写,然后从新的仓库中重新克隆它们。

如果要避免重写,可以使用第三个选项:git replace。已知这有一些怪癖,并且它要求正确设置每个克隆才能“看到”已拼接的历史记录。

因此,要支持此功能,您只需标记B(也许也可以标记B'):
git tag old-history repo-a/master
git tag new-root B'

(其中B'是适当的SHA值ID或等效表达式)。

当有人克隆存储库时,他们只会看到新的历史记录,但是他们可以说
git replace new-root old-history

这将记录历史的突破。

完成父项,重新设置基础或替换后,您可以删除repo-a Remote 。

关于Git:将文件历史记录从一个存储库复制到另一个存储库,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/44777043/

10-14 16:56