将根提交父级更改为指向另一个提交(连接两个独立的git存储库) [英] Change the root commit parent to point to another commit (connecting two independent git repositories)

查看:63
本文介绍了将根提交父级更改为指向另一个提交(连接两个独立的git存储库)的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我在svn信息库中有一个拥有3年以上历史的项目.它已迁移到git,但是执行此操作的人只是选择了最新版本并扔掉了所有这3年的历史.

I have a project that has more than 3 years of history in the svn repository. It was migrated to git, but the guy who did this, just take the last version and throw out all these 3 years of history.

现在,该项目在一个存储库中具有最近3-4个月的历史记录,而我将另外3年的svn历史记录导入了新的git存储库中.

Now the project has the last 3-4 months of history in one repository, and I've imported the other 3 years of svn history into a new git repository.

是否有某种方法可以将第二个存储库的根提交连接到第一个存储库的最后一个提交中?

Is there some way to connect the root commit of the second repository into the last commit of the first one?

是这样的:

  *   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.

I just learn that I need to do a git rebase, but how? Please, let's consider the commit dates like it was the SHA-1 codes... The answer was to use git filter-branch with --parent-filter option, not a git rebase.

更新2:

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

I tried the command git filter-branch --parent-filter 'test $GIT_COMMIT = 443aec8880e898710796a1c4fb4decea1ca5ff66 && echo "-p 98e2b95e07b84ad1e40c3231e66840ea910e9d66" || cat' HEAD and it didn't work:

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中运行.

It didn't work on Windows CMD or PowerShell, but it did work in Git Bash on windows.

推荐答案

首先,您需要一个具有所有可用历史记录的仓库.

First things first: you need a single repo that has all the available history.

使用最近的历史记录克隆存储库.将具有旧历史记录的仓库添加为远程仓库.我建议将此克隆作为镜像",并通过用此克隆替换原始回购来完成.但是,也可以将--mirror保留为关闭状态,然后通过将所有引用推回原点来完成(可能根据所使用的方法用力推动).

Make a clone of the repo with the recent history. Add the repo with the old history as a remote. I recommend this clone be a "mirror" and that you finish by replacing your origin repo with this one. But alternately you can leave --mirror off, and you'll finish by pushing (possibly force-pushing depending on which approach you use) all refs back to origin.

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.现在您拥有

The next thing you need to do is figure out where you'll be splicing the history. The terminology to describe this is a bit fuzzy I think... what you want is to find the two commits that correspond to the most recent SVN revision for which both histories have a commit. For example your SVN repo contained versions 1, 2, 3, and 4. Now you have

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'上.

where A represents version 1, B represents version 2, C and C' represent version 3, and D and D' represent version 4. E and F are work created after the original migration. So you want to splice the commits whose parent is D (E in this example) onto D'.

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

Now, I can think of two approaches, each with pros and cons.

重写最近的历史记录

IMO的最佳方法,如果您可以协调所有开发人员的过渡到新的回购(这意味着您安排一个时间,让他们都同意推动所有出色的工作,因此他们会放弃他们的工作.克隆;然后进行转换;然后全部重新克隆)是(有效地)将最近的历史重新建立到旧的历史上.

IMO the best way if you can coordinate a cut-over of all developers to a new repo (meaning you arrange a time when they all agree that all outstanding work is pushed, so they discard their clones; then you do the conversion; then they all re-clone) is to (effectively) rebase the recent history onto the old history.

如果确实只有一个分支,那么您可以直接使用rebase

If there is really just a single branch, then you can literally use rebase

git rebase --onto D' D master

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

(where D and D' are replaced with the SHA ID of the commits).

在最近的历史中,您更有可能拥有一些分支机构和合并机构;在那种情况下,变基操作将很快开始成为问题.另一方面,您可以利用以下事实:DD'具有相同的树-因此,重定基数和重定父级或多或少是等效的.

More likely you have some branches and merges in the recent history; in that case a rebase operation will start becoming a problem very quickly. On the other hand, you can take advantage of the fact that D has the same tree as D' -- so a rebase and a re-parent are more or less equivalent.

因此您可以将git filter-branch--parent-filter一起使用来进行重写.根据 https://git-scm.com/docs/上的文档中的示例git-filter-branch 你会做类似的事情

So you can use git filter-branch with a --parent-filter to do the rewrite. Based on the examples in the docs at https://git-scm.com/docs/git-filter-branch you would do something like

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

(其中DD'再次用提交的SHA ID代替).

(where again D and D' are replaced with the SHA ID of the commits).

这将创建您需要清理的备份"引用.最后,您会得到

This creates "backup" refs that you'll need to clean up. In the end you'll get

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

事实是FF'取代,这导致(或多或少)需要硬转换.

It's the fact that F was replace by F' which creates the need for a hard cut-over (more or less).

现在,如果您在第1步进行了镜像克隆,则可以考虑清除reflog,删除远程服务器并运行gc,然后这是一个新的随时可用的原始存储库.

Now if you made a mirror clone back at step 1, you can consider wiping the reflog, dropping the remotes, and running gc, and then this is a new ready-to-use origin repo.

如果您进行了常规克隆,则需要push -f所有对原点的引用,这可能会在原点回购上留下一些混乱.

If you made a regular clone, then you'll need to push -f all the refs to the origin, and this will likely leave behind some clutter on the origin repo.

使用替换提交"

另一种选择不会产生硬转换,但会给您带来很多麻烦,需要您永远解决.您可以使用git replace.在您的组合仓库中

The other option doesn't create a hard cut-over, but it leaves you with small headaches to deal with forever. You can use git replace. In your combined repo

git replace `D` `D'`

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

By default, when generating log output or whatever, if git finds D, it will substitute D' (and its history) in the output.

有一些已知的故障.可能存在未知的故障.默认情况下,不共享使所有这些工作都起作用的替换引用",因此您必须有意识地推送和获取它们.

There are some known glitches. There may be unknown glitches. And by default the "replacement refs" that make this all work aren't shared, so you have to push and fetch them deliberately.

这篇关于将根提交父级更改为指向另一个提交(连接两个独立的git存储库)的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆