问题在于我们已经将文件从repo A移动到repo B,所有文件的历史记录都丢失了。但是现在一些开发人员已经开始在repo B上工作并推送了他们的更改。因此,现在我想将一些文件的历史记录从repo A合并/复制到repo B中,这仅适用于某些文件。是否可能实现?或者一旦文件的历史记录丢失就永远丢失了?
请帮忙解答。谢谢。
可以做到,但可能并不容易。首先要明确一点:没有“移动文件的历史”这种说法。只有移动提交,所以如果您想要代表文件子集的历史记录,则创建这些提交是第一个挑战。
最简单的方法是转移所有历史记录。(实际上,如果你将Repo B作为Repo A的浅克隆,那么你可以取消浅层克隆就完成了。但我猜你不是这样创建Repo B的...)
无论如何,由于您要从Repo A移动到Repo 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
如果仓库有很多提交记录,那可能需要一段时间。如果要删除的文件列表不是微不足道的,您可能希望将多个git rm
命令放在一个shell脚本中,并使用它作为--index-filter
参数,而不是像上面展示的那样内联。
无论哪种方式,希望您已经有了想要移植到Repo 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 最后一个 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。
如果历史记录之间存在一定的一致性,则变基可能会更简单,但会引入在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
不变,提交仍然被替换)。你的开发人员必须将其视为上游rebase(请参见“从上游rebase恢复”的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
远程仓库。
--index-filter
示例中的单引号在我的浏览器中看起来像反引号(`),花了我几次尝试才意识到它们应该是单引号(')。 - rbhitchcock