如何在不破坏文件历史记录的情况下合并多个 Git 存储库?
How do I combine several Git repositories without breaking file history?
我们正在尝试从 TFS 迁移。使用 git-tfs 工具,我们能够迁移现有仓库的部分内容,但它会在某些麻烦的签入时崩溃。我们已经能够制作一个拼凑而成的 Git 回购协议集,涵盖了大部分原始 TFS 提交。
目前有:
- Git 回购从 2009 年到 2011 年有变化
- Git 回购从 2011 年到 2016 年有变化
- Git 回购从 2016 年到当前
期望:
- 涵盖 2009 年到当前的大 Git 回购协议
- 任何一直存在的文件都会有一个文件历史
我们有什么办法可以将它们拼接成一个 Git 存储库吗?我们不关心保留 SHA(无论如何它们都是新的),但我们不能破坏文件历史记录。
- Create a new empty repository New.
- Make an initial commit because we need one before we do a merge.
- Add a remote to old repository OldA.
- Merge OldA/master to New/master.
- Make a subdirectory OldA.
- Move all files into subdirectory OldA.
- Commit all of the file moves.
- Repeat 3-6 for OldB.
Git 没有 文件历史记录。
Git 存储提交,提交 是 历史记录。他们是那里唯一的历史。 (我说这不是文件历史,因为它是 commit 历史。)每个提交都有一个父提交,或者如果提交是合并,则有两个父(如果是章鱼,则可能超过两个)合并)。
除了拥有父级之外,每个提交都是该提交中所有文件的独立快照。这里没有历史:它只是一个快照。如果您想查看上一次提交和当前提交之间发生了什么,您可以 Git 提取上一次提交(快照 O for Old)和当前提交(快照 N for New) and 运行 diff O N.这就是改变的地方:O 和 N.
之间的任何不同之处
您可以要求 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 个做:
git remote add c:/path/toward/other/repository
git fetch
然后按照帮助创建git嫁接文件.git/info/grafts
:
https://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/
我们正在尝试从 TFS 迁移。使用 git-tfs 工具,我们能够迁移现有仓库的部分内容,但它会在某些麻烦的签入时崩溃。我们已经能够制作一个拼凑而成的 Git 回购协议集,涵盖了大部分原始 TFS 提交。
目前有:
- Git 回购从 2009 年到 2011 年有变化
- Git 回购从 2011 年到 2016 年有变化
- Git 回购从 2016 年到当前
期望:
- 涵盖 2009 年到当前的大 Git 回购协议
- 任何一直存在的文件都会有一个文件历史
我们有什么办法可以将它们拼接成一个 Git 存储库吗?我们不关心保留 SHA(无论如何它们都是新的),但我们不能破坏文件历史记录。
- Create a new empty repository New.
- Make an initial commit because we need one before we do a merge.
- Add a remote to old repository OldA.
- Merge OldA/master to New/master.
- Make a subdirectory OldA.
- Move all files into subdirectory OldA.
- Commit all of the file moves.
- Repeat 3-6 for OldB.
Git 没有 文件历史记录。
Git 存储提交,提交 是 历史记录。他们是那里唯一的历史。 (我说这不是文件历史,因为它是 commit 历史。)每个提交都有一个父提交,或者如果提交是合并,则有两个父(如果是章鱼,则可能超过两个)合并)。
除了拥有父级之外,每个提交都是该提交中所有文件的独立快照。这里没有历史:它只是一个快照。如果您想查看上一次提交和当前提交之间发生了什么,您可以 Git 提取上一次提交(快照 O for Old)和当前提交(快照 N for New) and 运行 diff O N.这就是改变的地方:O 和 N.
之间的任何不同之处您可以要求 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
这个存储库,它复制每个提交,但遵循替换。这具有将移植物固定到位的效果。例如,参见
编辑: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 个做:
git remote add c:/path/toward/other/repository
git fetch
然后按照帮助创建git嫁接文件.git/info/grafts
:
https://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/