如何在不破坏文件历史记录的情况下合并多个 Git 存储库?

How do I combine several Git repositories without breaking file history?

我们正在尝试从 TFS 迁移。使用 git-tfs 工具,我们能够迁移现有仓库的部分内容,但它会在某些麻烦的签入时崩溃。我们已经能够制作一个拼凑而成的 Git 回购协议集,涵盖了大部分原始 TFS 提交。

目前有:

期望:

我们有什么办法可以将它们拼接成一个 Git 存储库吗?我们不关心保留 SHA(无论如何它们都是新的),但我们不能破坏文件历史记录。

基于Eric Lee's blog post

  1. Create a new empty repository New.
  2. Make an initial commit because we need one before we do a merge.
  3. Add a remote to old repository OldA.
  4. Merge OldA/master to New/master.
  5. Make a subdirectory OldA.
  6. Move all files into subdirectory OldA.
  7. Commit all of the file moves.
  8. Repeat 3-6 for OldB.

Git 没有 文件历史记录。

Git 存储提交,提交 历史记录。他们是那里唯一的历史。 (我说这不是文件历史,因为它是 commit 历史。)每个提交都有一个父提交,或者如果提交是合并,则有两个父(如果是章鱼,则可能超过两个)合并)。

除了拥有父级之外,每个提交都是该提交中所有文件的独立快照。这里没有历史:它只是一个快照。如果您想查看上一次提交和当前提交之间发生了什么,您可以 Git 提取上一次提交(快照 O for Old)和当前提交(快照 N for New) and 运行 diff O N.这就是改变的地方:ON.

之间的任何不同之处

您可以要求 Git 综合文件历史记录,但它通过一个可怕的 hack 来实现:它在每次提交中查找一个特定的更改文件,因为它会返回提交历史记录。与该提交的父级相比,它打印提交更改文件的提交。如果文件 name 更改——如果提交重命名文件——并且你使用了 --follow,Git 更改了哪个(单个)文件 name 正在找,所以现在在以前的名字下找

如果您的历史由一系列提交组成:

(history starts here, at a root commit)
  |
  v

  o--o--<branches and merges...>--o   <-- end

和第二个历史:

  o--o--<branches and merges...>--o   <-- end

  o--o--...--o   <-- end2
  ^
  |
(we want to replace this one)

在单个存储库中,您可以编写一个 "replacement" 提交对象(使用 git replace),就像我们 想要 的第二个根提交一样替换,除了一件事:作为其父提交,它具有 end 指向的提交。

这个替换提交有效地将两个历史拼接在一起。

根据需要为您想要添加的拼接重复此操作,为您在单个存储库中拥有的独立提交链添加任意数量的提交链。然后你可以 运行 git filter-branch 这个存储库,它复制每个提交,但遵循替换。这具有将移植物固定到位的效果。例如,参见 or

编辑:git 的最新版本现在扩展了 git replace 命令以使用 git replace --graft <commit> <parent> 更轻松地完成它(参见 https://git-scm.com/docs/git-replace#Documentation/git-replace.txt---graftltcommitgtltparentgt82308203


使用 git 的 'graft' 功能可以轻松做到这一点。这是一个与@torek 提到的 git replace 目标相同的功能,但在您的情况下更容易使用。

首先,导入同一个存储库中的所有历史记录。在最近的存储库中,为另外 2 个做:

  1. git remote add c:/path/toward/other/repository
  2. git fetch

然后按照帮助创建git嫁接文件.git/info/graftshttps://git.wiki.kernel.org/index.php/GraftPoint (你的文件中应该有 2 行)

如果您使用 git log 或任何 Git GUI,您现在应该可以看到您想要的历史记录。

如果您满意,则重写历史以使其具有权威性:

git filter-branch

您现在可以将您的历史推送到中央存储库或共享它。

Ps:关于该主题的另一篇文档,但熔化移植物并替换 git 特征:https://legacy-developer.atlassian.com/blog/2015/08/grafting-earlier-history-with-git/