从一个巨大的Subversion存储库迁移到许多小型Git存储库

时间:2014-08-04 16:16:12

标签: git git-svn

我正在进行Git to Subversion迁移。我们在一个Subversion存储库中有以下布局:

Common
|-- Module A
|-- Module B
|-- Module C
`-- Module D

我们有几个不同的需求,到目前为止我发现的一切都没有用。

首先,我们只想迁移一个模块,但也要维护根文件夹。例如,我想将模块A迁移到Git存储库名称module-a,如下所示:

module-a
`-- Module A

这是为了维护包含路径的一致性。选项1是将模块A迁移到Git存储库的根目录,然后使用git mv重新创建该文件夹,但这种方式会丢失日志历史记录。我知道git log --follow已修复此问题,但我们需要移动大量存储库,许多用户界面不会公开或支持--follow标记。

选项2是检查根并使用--ignore-paths选项,但这似乎引入了Subversion历史记录的所有,这对于拥有整个历史记录来说有点愚蠢只是存储库的一小部分。

关于如何实现上述目标的任何想法? Subversion的一个子目录到一个新的Git存储库中,保留了该目录的目录和历史记录。

1 个答案:

答案 0 :(得分:0)

买者

你想要做的事情会略微减少对粮食的影响。本机解决方案会将模块A到D分解为单独的存储库,其中每个存储库都包含Module [ABCD]目录下的内容。第五个“超级存储库”会使用git submodule将它们绑定在一起,并且具有名为Module A和朋友的目录。

答案

我认为你有充分的理由做你想做的事。从选项1导入的第一部分开始,而不是最后的git mv提交,返回并使用git filter-branch重写您的历史记录,其文档包含一个示例调用,用于将所有文件推送到一个级别在目录树中。

根据您的情况,第一个存储库的调整将变为

git filter-branch --index-filter \
        'git ls-files -s | sed "s-\t\"*-&Module A/-" |
                GIT_INDEX_FILE="$GIT_INDEX_FILE.new" \
                        git update-index --index-info &&
         mv "$GIT_INDEX_FILE.new" "$GIT_INDEX_FILE"' -- --all

将重写的分支与refs/original/refs/heads下的备份进行比较。要删除备份,请使用(假设您的更改发生在master分支上)

git update-ref -d refs/original/refs/heads/master

冲洗并重复其他储存库。

步行通过

使用占位符数据创建示例存储库。

$ git init mymoda; cd mymoda
Initialized empty Git repository in /tmp/mymoda/.git/

$ touch foo.m; git add foo.m; git commit -m 'foo.m'
[master (root-commit) f321c95] foo.m
 0 files changed, 0 insertions(+), 0 deletions(-)
 create mode 100644 foo.m

$ mkdir bar; touch bar/bar.m; git add bar; git commit -m 'bar.m'
[master 608df91] bar.m
 0 files changed, 0 insertions(+), 0 deletions(-)
 create mode 100644 bar/bar.m

$ touch "`echo -e "dont\ttab\nme\\bro"`"; git add .; git commit -m weird
[master 524e78f] weird
 0 files changed, 0 insertions(+), 0 deletions(-)
 create mode 100644 "dont\ttab\nme\bro

创建一个包含内容的分支。

$ git checkout -b branch
Switched to a new branch 'branch'

$ touch "`echo -e "me\tneither"`"; git add .; git commit -m also\ weird
[branch 75b4c3c] also weird
 0 files changed, 0 insertions(+), 0 deletions(-)
 create mode 100644 "me\tneither"

了解我们到目前为止的情况。请注意,git lola是一个非标准但非常有用的别名。

$ git lola --name-status
* 75b4c3c (HEAD, branch) also weird
| A     "me\tneither"
* 524e78f (master) weird
| A     "dont\ttab\nme\bro"
* 608df91 bar.m
| A     bar/bar.m
* f321c95 foo.m
  A     foo.m

假装一切都在Module A之下。

$ git filter-branch --index-filter \
    'git ls-files -s | sed "s-\t\"*-&Module A/-" | \
      GIT_INDEX_FILE="$GIT_INDEX_FILE.new" \
        git update-index --index-info && \
     mv "$GIT_INDEX_FILE.new" "$GIT_INDEX_FILE"' -- --all
Rewrite 75b4c3cdf18f360ea29e7cf4bb6382b65b2ed536 (4/4)
Ref 'refs/heads/branch' was rewritten
Ref 'refs/heads/master' was rewritten

现在看起来像这样。

$ git lola --name-status
* cf45854 (HEAD, branch) also weird
| A     "Module A/me\tneither"
* 58bc1ee (master) weird
| A     "Module A/dont\ttab\nme\bro"
* 4a1fe56 bar.m
| A     Module A/bar/bar.m
* e3e9eb2 foo.m
  A     Module A/foo.m
* 75b4c3c (refs/original/refs/heads/branch) also weird
| A     "me\tneither"
* 524e78f (refs/original/refs/heads/master) weird
| A     "dont\ttab\nme\bro"
* 608df91 bar.m
| A     bar/bar.m
* f321c95 foo.m
  A     foo.m

查看并删除备份。

$ git update-ref -d refs/original/refs/heads/master
$ git update-ref -d refs/original/refs/heads/branch

最终结果:

$ git lola --name-status
* cf45854 (HEAD, branch) also weird
| A     "Module A/me\tneither"
* 58bc1ee (master) weird
| A     "Module A/dont\ttab\nme\bro"
* 4a1fe56 bar.m
| A     Module A/bar/bar.m
* e3e9eb2 foo.m
  A     Module A/foo.m

低级别说明

这涉及相当多的改变。其核心是git update-index,用于调整路径前缀。文档的“Using --index-info” section定义了git update-index接受的格式,第三种情况是您感兴趣的格式。

  

3。 模式 SP sha1 SP 阶段 TAB 路径

将文件向下推一级是一个简单的事情,即将字符串前缀附加到馈送到git update-index的路径字段的开头,这就是sed过滤器的用途。用英文写的

  • 替代(s
  • 使用-作为搜索模式和替换的分隔符
  • 搜索ASCII TAB\t),然后搜索零或多个双引号(\"*
  • 替换搜索匹配的所有内容(&),然后是新的父目录,后跟斜杠(Module A/

请注意,必须引用整个sed脚本,因为它包含一个空格,即Module A,这需要在搜索模式中转义双引号。

这可以满足您的需求,因为TAB引入了git ls-files -s输出的路径字段,并标记了新父文件夹名称的插入点。关于零或多个双引号的位是允许包含TABLF\的奇怪路径,这些路径将被转义和引用。在这种情况下,sed脚本会引用未修改的引用。

成功编写新索引后,mv命令将其交换到适当位置,并使用git filter-branch机制将其拼接到您的历史记录中。