如何将多个Git仓库合并而不破坏文件历史记录?

8
我们正试图迁移离开TFS。使用git-tfs工具,我们能够迁移现有仓库的部分内容,但在某些棘手的提交处崩溃。我们已经能够制作出一套拼凑起来的Git仓库,涵盖了大部分原始TFS提交。
目前有:
- 包含2009年至2011年更改的Git仓库 - 包含2011年至2016年更改的Git仓库 - 包含2016年至今的更改的Git仓库
期望达到的效果:
- 涵盖2009年至今的大型Git仓库 - 任何整个时间段内存在的文件都将拥有单个文件历史记录
有没有办法将它们拼接成一个单独的Git仓库?我们不关心保留SHAs(它们都是新的),但我们不能破坏文件历史记录。

据我所知,这是不可能的。你将会面临的问题是,2009-2011代码库的最后提交哈希值不会是2011-2016代码库的父级。可以将两个git代码库合并为一个,但通常这些代码库中有_不同的_文件,因此它们之间的父子关系并不重要。 - mkasberg
3个回答

7
编辑: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/grafts https://git.wiki.kernel.org/index.php/GraftPoint (您的文件中应该有2行)

如果您使用 git log 或任何Git GUI,则现在应该像您想要的那样查看历史记录。

如果您满意,则使用以下命令重写历史记录以使其最终确定:

git filter-branch

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

附:关于此主题的另一篇文档,但融合了嫁接和替换git功能:https://legacy-developer.atlassian.com/blog/2015/08/grafting-earlier-history-with-git/


Git拥有_一切_。谢谢! - Scott Stafford

3

Git没有文件历史。

Git存储提交,提交就是历史。它们是唯一的历史记录。(我说不是文件历史,因为它是提交历史。) 每个提交都有一个父提交,如果提交是合并,则有两个父提交(如果是八爪鱼合并,则可能有多个父提交)。

除了有一个父提交之外,每个提交都是所有在该提交中的文件的独立快照。这里没有历史记录: 只有一个快照。如果你想看看前一次提交和当前提交之间发生了什么,你可以让Git提取前一次提交(快照O代表“旧的”)和当前提交(快照N代表“新的”),然后运行diff O N。那就是改变了什么: 介于O和N之间的不同之处。

你可以要求Git合成一个文件历史记录,但它通过一种可怕的方法来实现: 它在遍历提交历史时查找一个特定的更改过的文件。当比较该提交与该提交的父提交时,它打印出更改该文件的提交。如果文件名更改——如果提交重命名了文件——并且你使用了--follow选项,Git会更改它正在查找的(单个)文件名,现在它正在前一个名称下查找。

如果你有一个由一系列提交组成的历史记录:

(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)

在一个代码库中,你可以使用git replace命令编写一个“替换”提交对象,该对象与我们想要替换的第二个根提交非常相似,除了一点:它的父提交是指向end的提交。
这个替换提交有效地将两个历史记录拼接在一起。
如果需要,可以为多个单独的提交链添加任意数量的拼接。然后,可以在这个代码库上运行git filter-branch命令,复制每个提交,但遵循替换的路径。这样就可以巩固嫁接的位置。例如,请参见What does git filter-branch with no arguments do?Rebase entire git branch onto orphan branch while keeping commit tree intact

0

根据Eric Lee的博客文章

  1. 创建一个新的空仓库New。
  2. 进行初始提交,因为我们需要在合并之前进行一次提交。
  3. 添加一个远程仓库OldA。
  4. 将OldA/master合并到New/master。
  5. 创建一个子目录OldA。
  6. 将所有文件移动到子目录OldA中。
  7. 提交所有文件移动操作。
  8. 重复3-6步骤来处理OldB。

我认为这种方法无法在不同的代码库中为特定文件保留连续的历史记录。最终,我将在每个子目录OldA /,OldB /等中拥有一个文件夹。 - Scott Stafford

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接