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

时间:2017-05-19 19:34:30

标签: git git-rebase git-filter-branch git-history-graph

我有一个在svn存储库中有超过3年历史的项目。它被迁移到git,但是这样做的人,只需要使用最后一个版本并抛弃所有这3年的历史。

现在该项目在一个存储库中有最近3-4个月的历史记录,并且已将其他3年的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中运行。

1 个答案:

答案 0 :(得分:2)

首先要做的事情是:您需要一个具有所有可用历史记录的回购。

使用最近的历史记录复制repo。添加具有旧历史记录的仓库作为远程。我推荐这个克隆是一个&#34;镜像&#34;并且你用这个替换你的原始仓库来完成。但是,你也可以离开--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'

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

重写最近的历史记录

IMO最好的方式如果你可以协调所有开发人员切换到一个新的回购(意味着你安排一个时间,他们都同意推动所有杰出的工作,所以他们放弃他们克隆;然后你进行转换;然后它们都重新克隆)是(有效地)将最近的历史记录变换为旧历史记录。

如果真的只有一个分支,那么你可以逐字地使用rebase

git rebase --onto D' D master

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

在最近的历史中,你更有可能有一些分支和合并;在这种情况下,rebase操作将很快开始成为一个问题。另一方面,您可以利用DD'具有相同树的事实 - 因此rebase和re-parent或多或少相等。

因此,您可以使用git filter-branch--parent-filter进行重写。根据{{​​3}}文档中的示例,您可以执行类似

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

(再次将DD'替换为提交的SHA ID。

这会创建&#34;备份&#34;请注意您需要清理。最后你会得到

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

事实是FF'取代,这导致需要进行严格的切换(或多或少)。

现在,如果你在第1步做了一个镜像克隆,你可以考虑擦除reflog,删除遥控器,然后运行gc,然后这是一个新的可立即使用的源回购。

如果你做了一个普通的克隆,那么你需要push -f所有的refs到原点,这可能会在原始仓库上留下一些混乱。

使用&#34;替换提交&#34;

另一个选项并不能创造一个严格的切换,但它会让你头疼不已,永远无法应对。您可以使用git replace。在您的合并仓库中

git replace `D` `D'`

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

有一些已知的故障。可能有未知的故障。默认情况下,&#34;替换refs&#34;这使得所有工作都没有共享,所以你必须故意推送和获取它们。