可以做到,但可能并不容易。但首先要做的是:没有“移动文件的历史”。只有移动提交,所以如果您想要代表文件子集历史的提交,那么创建这些提交是第一个挑战。
最简单的方法是传输所有历史。 (事实上,如果你将 Repo B 制作为 Repo A 的浅表克隆,那么你可以将其取消浅化并完成。但我猜这不是你创建 Repo B 的方式......)
无论如何,既然您要从回购 A 转移到回购 B,也许您特别想删除一些历史记录。这可能是一个完整的主题,但我们假设您真的只想要几个文件的历史记录。
在您想要的所有文件(而不是其他文件)都在子目录中的特殊情况下,并且您希望(或至少可以接受)将这些文件移动到 repo 的根目录,您可以使用 @987654322 @ 与--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 的文件列表不重要,您可能希望将多个git rm 命令放入一个shell 脚本中,并将其用作--index-filter 参数,而不是如上所示内联它。
好吧,无论如何,希望你有一段历史你想移植到回购 B 中。
cd repo-b
git remote add repo-a path/to/repo-a
git fetch repo-a
现在你在回购 B:
... A -- B <--(repo-a/master)
\
(repo-a/other-branches-maybe)
B' -- C -- D (master)(origin/master)
所以我在这里做一个假设,来自回购 A 中最后一次 master 提交的 TREE - 我们的历史重写创建的那个 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-id 是B 的SHA,$graft-id 是C 的SHA
rebase 可能会更简单一些(假设历史之间有一定程度的一致性),但会引入您最终修改 D 的树的可能性。如果您确实决定尝试变基,那将是
git rebase --onto repo-A/master B' master
其中B' 是 Repo B 根提交的 SHA ID。 (交替
git rebase --interactive --onto repo-A/master --root master
然后删除B' 的条目。)
这些选项中的任何一个都将重写提交C 和D。 (即使重新设置确保TREE 不变,提交仍会被替换。)您的开发人员必须将其视为上游变基(请参阅“从上游变基恢复”下的git rebase 文档)。为了缓解这种情况,我通常建议进行协调切换,开发人员检查他们拥有的所有东西,丢弃他们的克隆,然后你进行重写,他们从新的 repo 重新克隆。
如果你想避免重写,你可以使用第三个选项:git replace。众所周知,这有一些怪癖,并且需要正确设置每个克隆才能“查看”拼接历史。
因此,为了支持这一点,您只需标记 B(也许还有 B'):
git tag old-history repo-a/master
git tag new-root B'
(其中B' 是适当的 SHA 值 ID,或等效表达式)。
当有人克隆 repo 时,他们只会看到新的历史记录,但他们可以说
git replace new-root old-history
这将掩盖历史的突破。
一旦您完成了 reparent、rebase 或 replace - 您可以删除 repo-a 遥控器。