在 Unicode 文件 (MS Windows) 上的 Mercurial 中获得可读的差异显示

Posted

技术标签:

【中文标题】在 Unicode 文件 (MS Windows) 上的 Mercurial 中获得可读的差异显示【英文标题】:Getting readable diff displays in Mercurial on Unicode files (MS Windows) 【发布时间】:2010-06-10 14:52:14 【问题描述】:

我正在尝试将一些 Windows PowerShell 脚本存储在 Mercurial 存储库中。 PowerShell 编辑器似乎喜欢将文件保存为 UTF-16 Unicode。这意味着有很多 \0 字节,这是 Mercurial 用来区分“文本”和“二进制”文件的。我知道这对 Mercurial 存储数据的方式没有影响,但这确实意味着它会显示二进制差异,这有点难以阅读。有没有办法告诉 Mercurial 这些真的是文本文件?大概我需要说服 Mercurial 对特定文件类型使用外部 Unicode 感知差异程序。

【问题讨论】:

具体来说,我的问题是使用 TortoiseHg 的“提交”工具中的“文本差异”页面,它通常显示所选文件中更改的漂亮摘要,但显示 UTF-16 的垃圾文件。 @orad:截至 2010 年 9 月 22 日,我还没有找到答案。 BOM.py 答案将起作用。只需将整个内容复制到一个文件中,然后编辑(或创建)您的 users\yourname\Mercurial.ini 文件并在“[extensions]”行下(添加它,如果没有这样的行),添加一个带有名称的行= 文件(如“bom = C:\path\to\the\bom.py”)。 【参考方案1】:

这可能与您无关;如果听起来不像,请阅读最后一段。

我不确定这是否是您需要的,但我需要 UTF-16LE 内容的差异,而不仅仅是“二进制文件不同”——当我在几个月前搜索它时发现讨论它的线程和错误; here's part of it。我现在找不到这个迷你扩展的原始来源(虽然它正在做那个补丁所做的事情),但我得到的是一个扩展,BOM.py

#!/usr/bin/env python

from mercurial import hg, util

import codecs

boms = [
    codecs.BOM_UTF8,
    codecs.BOM_UTF16_BE, codecs.BOM_UTF16_LE,
    codecs.BOM_UTF32_BE, codecs.BOM_UTF32_LE
    ]

def binary(s):
    if s:
        for bom in boms:
            if s.startswith(bom):
                return False
        return '\0' in s
    return False


def reposetup(ui, repo):
    util.binary = binary

这会像这样加载到 .hgrc(或您的用户\用户名\mercurial.ini)中:

[extensions]
bom = ~/.hgexts/BOM.py

注意路径在 Windows 和 Linux 之间会有所不同;在我的 Windows 副本上,我将路径设置为 \...\whatever(它位于驱动器号可以更改的 USB 磁盘上)。不幸的是,相对路径是相对于当前工作目录而不是存储库根目录或任何类似的东西,但如果你将它保存在 C: 驱动器上,你可以放完整路径。

在 Linux(我的主要开发环境)中,这很好用;在命令提示符(我仍然经常使用)中,它通常运行良好。我从未在 PowerShell 中尝试过,但我希望它比命令提示符更好,因为它支持命令行中的任意空字节。

我不确定这是否是您想要的;顺便说一句,您说过“二进制差异”,我怀疑您可能已经拥有此功能或正在做hg diff -a,这正在实现相同的目标。在这种情况下,我能想到的就是编写另一个扩展,它采用 UTF-16LE 并尝试将其解码为 UTF-8。我不确定这种扩展的语法,但我可能会尝试一下。

编辑: 现在通过 commands.py、cmdutil.py、patch.py​​ 和 mdiff.py 搜索了 mercurial 源,我看到二进制差异是使用 base85 编码完成的(patch.b85diff ) 而不是正常的差异。我没有意识到这一点,我认为它只是强迫它区分它。在那种情况下,也许这个文本毕竟相关的。我正在等待回复,看看是否有!

【讨论】:

当心!虽然此扩展适用于在命令行上进行差异化,但我在通过 qnew 创建 MQ 补丁时遇到了损坏问题。【参考方案2】:

我已通过使用 NotePad++ 创建一个新文件并将其保存为 PowerShell 文件(扩展名为 .ps1)来解决此问题。 NotePad++ 会将文件创建为纯文本 ANSI 文件。创建后,我可以在 PowerShell 编辑器中打开文件并根据需要进行任何更改,而无需编辑器修改文件编码。

免责声明:我刚才遇到了这个问题,所以我不确定是否有任何影响,但到目前为止,我的脚本似乎正常工作,我的差异也很好地显示出来。

【讨论】:

转换为 UTF-8 也适用于 Xcode 中的 .strings 文件(genstrings 默认生成 UTF-16LE)【参考方案3】:

如果我的另一个答案不能满足您的要求,我认为这个可以;虽然我还没有在 Windows 上测试过它,但它在 Linux 上运行良好。它做了一些可能令人讨厌的事情,用一个将 utf-16le 转换为 utf-8 的新函数包装 mercurial.mdiff.unidiff。这不会影响hg st,但会影响hg diff。一个潜在的陷阱是 BOM 也会从 UTF-16LE BOM 更改为 UTF-8 BOM。

不管怎样,我认为它可能对你有用,所以就在这里。

扩展文件utf16decodediff.py:

import codecs
from mercurial import mdiff

unidiff = mdiff.unidiff

def new_unidiff(a, ad, b, bd, fn1, fn2, r=None, opts=mdiff.defaultopts):
    """
    A simple wrapper around mercurial.mdiff.unidiff which first decodes
    UTF-16LE text.
    """

    if a.startswith(codecs.BOM_UTF16_LE):
        try:
            # Gets reencoded as utf-8 to be a str rather than a unicode; some
            # extensions may expect a str and may break if it's wrong.
            a = a.decode('utf-16le').encode('utf-8')
        except UnicodeDecodeError:
            pass

    if b.startswith(codecs.BOM_UTF16_LE):
        try:
            b = b.decode('utf-16le').encode('utf-8')
        except UnicodeDecodeError:
            pass

    return unidiff(a, ad, b, bd, fn1, fn2, r, opts)

mdiff.unidiff = new_unidiff

.hgrc:

[extensions]
utf16decodediff = ~/.hgexts/utf16decodediff.py

(或等效路径。)

【讨论】:

不幸的是,这种方法存在内存问题:文件被吞食(通过 mercurial,而不是此扩展名),因此如果内存紧张,您可能会用完。它需要您设置--config diff.nobinary=True(根据我的个人经验,我知道这会搞砸 mq 补丁,所以我不建议永久启用它)才能到达包装器。 我还推荐if isinstance(a, str):if isinstance(b, str),因为当一个版本缺少文件而另一个版本具有这些变量的差异可能是 NoneType 并导致扩展崩溃 mercurial

以上是关于在 Unicode 文件 (MS Windows) 上的 Mercurial 中获得可读的差异显示的主要内容,如果未能解决你的问题,请参考以下文章

如何通过 Java 从 MS Access 2007 数据库中读取 Unicode 字符?

在Windows上的python 2.7中列出具有Unicode名称的文件

能不能修改windows的写字板默认的文件编码为unicode?

如何使用 Perl 在 Windows 中创建 unicode 文件名

如何在 R Windows 中将 Unicode 字符串写入文本文件?

Unicode / UTF-8文本文件:Windows控制台上的乱码(试图显示希伯来语)