将两个不同的git仓库合并在一起,而不会丢失任何历史记录

4
在我的当前项目中,我遇到了一个有趣的 git 问题:
自项目开始以来,更改一直存储在客户端远程服务器上的 SVN 存储库中。在开发进展中,我开始并行使用本地 git 存储库,以便更轻松地测试新功能而不破坏当前版本。可惜的是,git-svn 无法工作,这会让我的生活更加轻松。
现在我的客户转移到了另一个服务器,并在此过程中将 SVN 存储库移动到 git(使用 git2svn)。
虽然我总体上对此感到高兴,但是我现在有两个问题:
1.由于我正在处理一个较大的功能,因此我已经七天没有提交到 SVN。我在本地功能分支上工作并将其备份到本地服务器,但现在是远程 git 存储库的 SVN 副本与我的本地版本相比已经过时了。
2.由于远程 repo 是由 git2svn 创建的,它与我的 repo 完全不同(消息:警告:Repo 没有共同的提交),这使得标准合并变得不可能。
现在我的期望目标是:
1.合并两个存储库,使我的当前版本成为检查的版本,并再次推送到 origin。
2.保留两个存储库的历史记录(来自 SVN 的旧记录以及来自我的 git 的最近 7 天的记录)。
我尝试过的方法:
我尝试克隆远程 git(从 svn),并将我的本地 repo 合并到其中。我有 153 个冲突,"changed in both versions"。接受 "theirs"(即我最新的开发)会丢失文件的历史记录(它只是从我的平行 git repo 初始化开始)。
我的想法是,我可以为最近 7 天的每个提交创建一个补丁,并使用相应的提交消息(即 "手动合并")将其提交到新存储库中。在编写脚本之前,我想询问是否有内置的方法来执行此操作。
先感谢您!
更新:我尝试了许多解决方案,但每个解决方案都让我失望。基本问题是我现在有两个分支,没有一个共同的提交,但具有相同的树结构。这导致合并冲突,例如 "added in both branches",因为 git 不知道“myFile.txt”和“myFile.txt”是相同的并且可以合并。相反,我必须手动合并那一周中更改的 150 个文件,这是我无法也不会做的。
到目前为止,我最好的方法是为更改创建一个补丁,并将其应用于 "new" 存储库。但由于树不匹配,我还没有找到一个不会失败的补丁。我还需要找到正确的提交来开始补丁信息。
解决方案:
TL;DR:没有完美的解决方案,只能在两种选择中取其一。
1. 你可以使用这里描述的合并方法,但它会用你的side-git(即你的其他repo)替换SVN的单个文件历史记录(如果你的其他repo只有2个月,你就无法查找为什么这个文件3个月前被更改)。但是积极的一面是,你仍然可以通过git log在全局历史记录中合并你的更改。
2. 我最终采用的另一种选择是将“新”(即SVN)repo中的所有文件复制并替换为我的git repo的当前状态(通过cp -vru,但省略.git文件夹和所有生成的文件)。这让我失去了一周的历史记录,但仍然让我回溯到项目开始之前的过去,这是我喜欢的。 为了减轻这种损失的痛苦,我使用git log --date=short --pretty="format:%cd - %s" --name-status创建了一个详细的提交消息摘要,这至少给了我一个机会返回到这个条目并查看带有描述的消息。 但是,这对于文件删除是不起作用的,而且你也不知道哪个更改实际上属于巨大的提交消息的哪个部分。

我不明白为什么你不能按照poke的建议去做,即(1)检出mine存储库的分支mine-master,然后(2)运行git rebase master mine-mastermine的工作重放在master中保留的工作之上。 - JJD
2个回答

3
我刚刚用我的一个代码库进行了测试。我创建了一个新克隆,代表了您客户的新代码库。然后我将旧版本的内容复制到一个新文件夹中,并在其中初始化了一个代码库。我添加了这些内容,并进行了一些提交。
这样我就有了两个没有共同提交的代码库。
现在,在新的代码库中,我将我的副本(以下简称为mine)作为远程添加并获取其内容。然后我检出了一个新分支到mine的主分支:
git checkout -b new mine/master

因此,我可以从这个分支访问我单独存储库的所有历史记录。接下来,我使用递归合并策略将更新后的主分支合并到我的分支中,但对于冲突情况,我们(新的更改)优先考虑:

git merge master -s recursive -Xours

这将自动合并所有内容,如果存在冲突,则会通过使用我们的版本来解决冲突,从而有效地放弃主分支中的更改。

因此,您应该最终获得一个合并的分支,并且两个存储库的所有历史记录仍然存在。


感谢您的详细描述和努力。我确实最终得到了一个合并的存储库,但是:对于大多数文件,SVN历史记录都丢失了,并被较新的Git历史记录所替代。 - Stefan Hoth
不,历史记录并没有丢失(您仍然可以在日志等中访问它)。您的意思是您编辑的文件中的更改也会丢失。那是因为您只是使用您的版本来处理任何冲突的文件。如果您不想这样,您将需要手动修复合并冲突。 - poke
噢,好的,我明白了。但是我该如何看到似乎已经丢失的“其他”历史记录呢?我应该用什么命令? - Stefan Hoth
你可以使用 git log 作为最简单的例子;或者使用 gitk 在图形浏览器中查看。 - poke
虽然我希望能够保存每个文件的历史记录,但我很高兴已经将我的更改合并到新的存储库中,而不会丢失在此期间所做的工作信息。感谢您的帮助。 - Stefan Hoth
1
你有单独的文件历史记录,但需要理解 Git 的历史不一定是线性的。通常情况下,它会不断分叉和合并。你可以使用 git log -- <path> 命令查看单个文件的历史记录(即影响该文件的全局历史的部分)。此外,请注意 Git 不跟踪单个文件,而总是在提交时跟踪整个存储库的内容,因此从技术上讲,Git 没有单独的文件。 - poke

1

如果我理解正确,您的情况是这样的:

remote repo:
A - B

your repo
C - D - E - F

其中提交 A 实际上与 C 相同,提交 B 与 D 相同(就包含的文件而言)。我相信可能有效的方法就是在最新的远程提交上进行简单的变基工作,比如:

git rebase --onto B D

这应该将您的提交从 D 到您的 HEAD(例如 F),并将它们作为补丁应用于 B,从而产生以下结果:

A - B - E' - F'

这样,您的本地更改应该与此相同,并且可以轻松地推送到远程存储库。


rebase 的参数应该是第一个不想包含的提交。我认为在上面的图表中, myfeaturebranch 应该指向提交 F,但你想要的是它的祖先—— D(对应于 newgitrepo/master,或图表中的 B)。 - che
似乎不起作用。无论我尝试哪个方向进行变基,我最终都会失去其他历史记录。 - Stefan Hoth
问题是由于异步提交,它们永远无法完全同步。因为在使用SVN时,我进行了基于问题的提交(功能提交),而对于git,我只是进行了“批量提交”(git -am "Catch up with SVN")。现在我感到很抱歉,但当时git只是在中间起到了一些作用。:( - Stefan Hoth
@JJD 感谢你的提议,但我现在不在城里。附注:o_O - Stefan Hoth
显示剩余4条评论

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