将根提交的父级更改为指向另一个提交(连接两个独立的git存储库)

5
我有一个项目在svn仓库中已经有三年的历史记录。它被迁移到了git,但是负责人只拿了最新版本,抛弃了过去三年的历史记录。
现在这个项目在一个仓库里只有最近3-4个月的历史记录,我已经将另外三年的svn历史记录导入到了一个新的git仓库中。
有没有办法将第二个仓库的根提交连接到第一个仓库的最后一个提交?
就像这样:
  *   2017-04-21 - last commit on master
  |   
  *   2017-03-20 - merge branch Y into master
  |\  
  | * 2017-03-19 - commit on branch Y
  | | 
  * | 2017-03-18 - merge branch X into master
 /| * 2017-02-17 - commit on another new branch Y
* |/  2017-02-16 - commit on branch X
| *   2017-02-15 - commit on master branch
* |   2017-01-14 - commit on new branch X
 \|   
  *   2017-01-13 - first commit on new repository
  |   
  *   2017-01-12 - init new git project with the last version of the code in svn repository
  .   
  .   
There is no relationship between the two different repositories yet, this is what I wanna
do. I want to connect the root commit of 2nd repository with the last commit of the first
one.
  .
  .   
  *   2017-01-09 - commit
  |   
  *   2017-01-08 - commit
  |   
  *   2017-01-07 - merge
 /|   
* |   2016-01-06 - 2nd commit the other branch
| *   2016-01-05 - commit on trunk
* |   2016-01-04 - commit on new branch
 \|   
  *   2015-01-03 - first commit
  |   
  *   2015-01-02 - beggining of the project

更新:

我刚刚得知需要进行git rebase,但是该如何操作呢?请注意提交日期,就像它是SHA-1代码一样...答案是使用git filter-branch命令,并加上--parent-filter选项,而不是使用git rebase

更新2:

我尝试了命令git filter-branch --parent-filter 'test $GIT_COMMIT = 443aec8880e898710796a1c4fb4decea1ca5ff66 && echo "-p 98e2b95e07b84ad1e40c3231e66840ea910e9d66" || cat' HEAD,但它没有起作用:

PS D:\git\rebase-test\rep2cc> git filter-branch --parent-filter 'test $GIT_COMMIT = 443aec8880e898710796a1c4fb4decea1ca5ff66 && echo "-p 98e2b95e07b84ad1e40c3231e66840ea910e9d66" || cat' HEAD
fatal: ambiguous argument '98e2b95e07b84ad1e40c3231e66840ea910e9d66 || cat': unknown revision or path not in the working tree.
Use '--' to separate paths from revisions, like this:
'git <command> [<revision>...] -- [<file>...]'

更新3:

在Windows CMD或PowerShell上无法运行,但在Windows上的Git Bash中可以运行。


2
你考虑过在同一个仓库中获取两个历史记录,然后将其中一个历史记录重新基于另一个历史记录进行变基吗?这当然会重写你正在变基的历史记录中的所有提交。 - Lasse V. Karlsen
1
Lasse做得对。只需使用正确的svn克隆设置新的存储库,添加一个与此存储库相关的远程仓库,然后获取并将在从svn克隆后的git上完成的历史记录cherry-pick到正确克隆的svn存储库中。 - eftshift0
我是git的新手,我刚学会了一个我一直在寻找的魔术词汇——“rebase”。 - lmcarreiro
我需要做什么?假设示例中的提交日期是 SHA-1... git rebase 2017-01-09 2017-04-21 - lmcarreiro
1个回答

6

首先,您需要一个包含所有可用历史记录的单一仓库。

克隆具有最新历史记录的存储库。将具有旧历史记录的存储库添加为远程存储库。我建议此克隆是一个"镜像",并且您最后将使用此克隆替换原始存储库。但是,您也可以留下--mirror,并通过将所有引用推回原点(可能是强制推送,具体取决于您使用的方法)来完成。

git clone --mirror url/of/current/repo
cd repo
git remote add history url/of/historical/repo
git fetch history

接下来你需要做的是确定你将在哪里拼接历史记录。我觉得描述这个术语有点模糊...你要找到对应于最近的SVN修订版本,两个历史记录都有提交的两个提交。例如,你的SVN仓库包含版本1、2、3和4。现在你有

Recent-History Repo

C --- D --- E --- F <--(master)

Old-History Repo

A --- B --- C' --- D'

其中 A 代表版本1,B 代表版本2,CC' 代表版本3,DD' 代表版本4。 EF 是原始迁移后创建的工作。因此,您希望将其父提交为 D(在此示例中为 E)拼接到 D' 上。

现在,我可以想到两种方法,每种方法都有利弊。

重写最近的历史记录

在我看来,如果您可以协调所有开发人员切换到一个新的存储库(意味着您安排一个时间,他们都同意将所有未解决的工作推送,因此丢弃他们的克隆;然后进行转换;然后他们都重新克隆),则最好的方式是(实际上)将最近的历史记录基于旧历史记录进行变基。

如果确实只有一个分支,则可以直接使用 rebase。

git rebase --onto D' D master

(其中DD'被替换为提交的SHA ID)。

很可能您在最近的历史记录中有一些分支和合并; 在这种情况下,变基操作很快就会成为一个问题。另一方面,您可以利用D具有与D'相同的树的事实-因此,变基和重新父代几乎是等效的。

因此,您可以使用带有--parent-filtergit filter-branch进行重写。 基于文档中的示例https://git-scm.com/docs/git-filter-branch,您可以执行以下操作:

git filter-branch --parent-filter 'test $GIT_COMMIT = D && echo "-p D'" || cat' HEAD

(其中再次使用DD'替换为提交的SHA ID)。

这将创建需要清理的“备份”引用。最终你会得到

A --- B --- C' --- D' --- E' --- F' <--(master)

事实上,F被替换为F',这就创建了一个需要进行硬切换的需求(或多或少)。

如果您在步骤1时制作了镜像克隆,那么您可以考虑清除reflog、删除远程,并运行gc,然后这是一个新的可用的原始存储库。

如果您进行了常规克隆,则需要将所有引用push -f到原始存储库,这可能会在原始存储库中留下一些混乱。

使用“替换提交”

另一种选择不会创建硬切换,但会让您永远面临小问题。您可以使用git replace。在您的组合存储库中

git replace `D` `D'`

默认情况下,当生成日志输出或其他内容时,如果git找到D,它将在输出中替换为D'(及其历史记录)。

已知存在一些小问题,可能还有未知的问题。默认情况下,“replacement refs”并不是共享的,因此您需要有意地推送和获取它们。


我尝试了git replace,但它并不是我想要的。而且我的历史记录中有很多分支和合并,所以像你说的那样,rebase操作确实成为了一个问题。 - lmcarreiro
这第三个选项,我不明白... PP' 是什么?而且这个命令 git filter-branch --parent-filter 'test $GIT_COMMIT = D && echo "-p D'" || cat' HEAD 在 Windows 上能用吗? - lmcarreiro
抱歉,“PP'”应该是DD',就像其他地方一样;我已经进行了更新。在git bash shell中,该命令将适用于Windows操作系统; 只需记得使用正确的提交引用(例如SHA id)替换DD'即可。 - Mark Adelsberger

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