5

本周末我将使用fast-export将一个大型 Mercurial 项目转换为 Git 。我已经测试了几次,结果很好。

我们还想将我们的源代码编码(许多带有变音符号的德语注释/字符串文字)从 ISO-8859-1 转换为 UTF-8(repo 中的所有其他非 java 文件应保持原样),并且Git 迁移为我们提供了一个机会,因为无论如何每个人都需要再次克隆。但是,我没有找到一个好的方法。

  1. 我已经尝试过git filter-tree --tree-filter ...来自SO 的评论中的方法。然而,虽然这看起来很理想,但由于存储库的大小(大约 200000 个提交,18000 个代码文件),这将花费比我周末更多的时间。我已经尝试直接从具有 72 个内核的 linux VM 上的 64GB tmpfs 卷运行它(在一个高度优化的版本中,文件列表被分块并且子列表被并行转换(使用GNU 并行)),它仍然会需要几天...
  2. 或者,我尝试了一种简单的方法,即在任何活动分支上单独执行转换并提交更改。但是,结果并不令人满意,因为在合并或挑选预转换提交时,我几乎总是会遇到冲突。
  3. 现在我再次运行方法 1,但没有尝试重写所有分支的完整历史记录(--allas <rev-list>),而是仅从当前活动分支可访问的所有提交,并且无法通过某些过去的提交(希望)是所有当前的前身分支(branch-a branch-b branch-c --not old-tag-before-branch-a-b-c-forked-offas <rev-list>)。它仍在运行,但我担心我不能真正相信结果,因为这似乎是一个非常糟糕的主意。
  4. 我们可以像方法 2 一样使用正常提交来切换主分支中的编码,但这又会使从/到主控的樱桃采摘修复成为一场灾难。而且它会引入很多编码问题,因为开发人员在主分支和非转换分支之间切换时肯定会忘记更改他们的 IDE 设置。

所以现在,不知何故,我觉得最好的解决方案可能就是坚持 ISO-8859-1。

有人有想法吗?有人提到,也许reposurgeon基本上可以使用它的transcode操作来完成方法 1,其性能要好得多,git filter-tree --tree-filter ...但我不知道它是如何工作的。

4

3 回答 3

2

树过滤器git filter-branch本质上很慢。它的工作原理是将每个提交提取到临时目录中的完整树中,让您更改每个文件,然后找出您更改的内容并从您留下的每个文件中进行新的提交。

如果您通过快速导出/快速导入进行导出和导入,将是转换数据的时间:在将文件写入内存之前,您在内存中拥有文件的扩展数据,但不是文件系统形式出口/进口管道。此外,git fast-import它本身是一个 shell 脚本,因此在那里插入过滤很简单,并且hg-fast-export是一个 Python 程序,所以在那里插入过滤也很简单。显而易见的地方是这里:只需重新编码d

于 2018-06-08T14:38:19.997 回答
2

您可以考虑使用git filter-branch --index-filter— 而不是--tree-filter(这是默认设置)。这个想法是,--index-filter没有结帐步骤(即工作树在每次迭代中都没有(重新)填充)。

所以你可能会考虑编写一个过滤器来git filter-branch --index-filter 使用git ls-files——像这样:

  1. 调用git ls-files --cached --stage并遍历每个条目。

    仅考虑那些具有100644 文件模式的文件,即普通文件。

  2. 对于每个条目,运行类似

    sha1=`git show ":0:$filename" \
        | iconv -f iso8859-1 -t utf-8 \
        | git hash-object -t blob -w --stdin`
    git update-index --cacheinfo "10644,$sha1,$filename" --info-only
    
  3. 冲洗,重复。

我理解的另一种方法是从不同的角度解决问题:生成git fast-export 和消耗的流格式git fast-import是纯文本¹(只需将导出器的输出通过管道传输到less另一个寻呼机,然后自己查看)。

你可以使用你最喜欢的 PL 编写一个过滤器,它会解析流,重新编码任何data块。流的组织方式不使用 SHA-1 哈希,因此您可以随时重新编码。我理解的唯一明显的问题是,这些data块没有关于它们将在结果提交中代表哪个文件的信息(如果有的话),所以如果你的历史记录中有非文本文件,你可能需要根据每个数据 blob 的内容或通过记住它所看到的 blob 并在它看到commit将文件名分配给(其中一些)这些 blob 的记录后决定要重新编码哪些 blob 来使您的处理器更加复杂。


¹ 记录在git-fast-import(1)—run中git help fast-import

于 2018-06-08T17:22:09.083 回答
2

我遇到了完全相同的问题,解决方案基于@kostix 的答案,即使用--index-filter选项作为基础filter-branch,但是有一些额外的改进。

  1. 用于git diff --name-only --staged检测暂存区的内容
  2. 遍历此列表并过滤:
    1. git ls-files $filename,即它不是已删除的文件
    2. git show ":0:$filename" | file - --brief --mime-encodingis not的结果binary,即它是一个文本文件,也不是 UTF-8 编码的
  3. 对每个文件使用检测到的 mime 编码
  4. 使用 iconv 转换文件
  5. 检测文件模式git ls-files $filename --stage | cut -c 1-6

这是我的 bash 函数的外观:

changeencoding() {
    for filename in `git diff --name-only --staged`; do
        # Only if file is present, i.e., filter deletions
        if [ `git ls-files $filename` ]; then
            local encoding=`git show ":0:$filename" | file - --brief --mime-encoding`
            if [ "$encoding" != "binary" -a  "$encoding" != "utf-8" ]; then
                local sha1=`git show ":0:$filename" \
                    | iconv --from-code=$encoding --to-code=utf-8 \
                    | git hash-object -t blob -w --stdin`
                local mode=`git ls-files $filename --stage | cut -c 1-6`
                git update-index --cacheinfo "$mode,$sha1,$filename" --info-only
            fi
        fi
    done
}
于 2019-08-01T22:45:23.140 回答