Mercurial中的不区分大小写的差异比较

7
我正在使用Mercurial(特别是在Windows上的TortoiseHg)来对VBA代码进行版本控制。任何尝试过这样做的人都知道,每当项目中某个变量的声明发生更改时(无论作用域如何),VBA都会更改该变量的大小写。这使得版本控制成为一场噩梦。
我想在执行差异比较时忽略源代码中的大小写更改。有什么最简单的方法可以做到这一点?(一些我可能错过的差异选项、外部差异工具或其他东西?)
注意:我不是在谈论“不区分大小写的文件名”(是的,我在和你说话,Google…)

我们又见面了,我的朋友!你是否找到了解决方案,或者一个可以进行不区分大小写差异比较的版本控制系统? - RubberDuck
1
@RubberDuck:你激励了我,让我终于发布了我过去几年一直在使用的方法。虽然我还没有完全解决这个问题,但我已经消除了很多痛苦。基本上,我会忽略那些只有大小写变化的文件。如果文件有合法的更改,我仍然会看到大小写的变化,但这比什么都没有要好得多(多了很多)。 - mwolfe02
3个回答

7

您可以使用Extdiff扩展进行屏幕上显示时的差异比较。

  [extensions]
  hgext.extdiff =

  [extdiff]
  # add new command that runs GNU diff(1) in case-insensitive mode
  cmd.mydiff = diff
  opts.mydiff = -i

然后你可以在命令行中运行hg mydiff。当然,这需要安装一个diff二进制文件,可以是gnu或其他。

但是,这并不像你希望的那样有用,因为在内部,Mercurial不能忽略大小写——它正在获取文件内容的加密哈希值,而这些不允许有任何错误的余地。因此,如果你设置好了,你将执行hg mydiff,看不到任何更改,然后执行hg commit,会看到到处都是更改。

所以你可以使其在屏幕上正常工作,但不能从根本上解决问题。

一种选项是找到一个Visual Basic代码清理工具,类似于C语言中的indent,它可以规范化变量大小写,并在Mercurial提交挂钩中运行该工具。然后,源代码库中的所有代码将保持一致,并且可以准确地跨版本进行差异比较。


似乎只有某种代码清理工具是我的唯一选择。谢谢你的帮助。 - mwolfe02

2
如果您可以接受将代码全部小写,那么您可以使用编码/解码钩子来实现。它的工作方式如下:
[encode]
*.vba = tr A-Z a-z

这将在每次提交时将文件内容进行小写编码。差异也是基于文件的编码(存储库版本)计算的。
考虑一个包含以下内容的文件:
hello

将其在您的工作副本中更改为
Hello World

将会给出一个差异比较。
% hg diff
diff --git a/a.txt b/a.txt
--- a/a.txt
+++ b/a.txt
@@ -1,1 +1,1 @@
-hello
+hello world

请注意大写字母"H"和"W"被忽略了。
我不太了解VBA代码,所以我不能百分之百确定这个解决方案适用于您。但我希望它可以作为一个起点。
一个缺点是您需要为所有存储库设置此编码规则。reposettings扩展程序可以在这里帮助您。

这在我的情况下并不适用,但这是一个很好的提示,我会尝试记住它。谢谢你的帮助。 - mwolfe02

2
这是我选择的解决方案。虽然远非理想,但比我考虑过的其他替代方案要好得多。
我创建了一个Autohotkey脚本,它执行以下操作:
- 将检测到更改的MS Access文件恢复为存储库中的.orig文件 - 读取.orig文件(带有更改的文件) - 读取现有文件(已经在存储库中的文件) - 将两个文件的文本转换为小写 - 比较文件的小写内容 - 如果文件仍然不同,则还原.orig文件,以便可以将其提交到存储库 - 如果文件相同(即它们只在大小写上不同),则删除.orig文件,因为我们不关心这些更改
对于我们关心的实际更改的文件,我仍然看到所做的大小写更改。如果这导致很多噪音,我会在允许不区分大小写比较的比较工具中打开该文件(例如kdiff)。
这不是完美的解决方案,但对我来说消除了约90%的挫败感。
这是我的脚本。请注意,脚本包括另一个Autohotkey脚本ConsoleApp.ahk,它提供了一个名为ConsoleApp_RunWait()的函数。这是一个第三方脚本,与64位AHK不再很好地配合使用,因此我不会将其作为我的答案的一部分包含在内。任何执行命令行并将输出作为字符串返回的AHK函数都足够了。
; This script checks an MS Access source directory and reverts all files whose only modifications are to the 
; case of the characters within the file.

#Include %A_ScriptDir%\ConsoleApp.ahk
#NoEnv  ; Recommended for performance and compatibility with future AutoHotkey releases.
SendMode Input  ; Recommended for new scripts due to its superior speed and reliability.
SetWorkingDir %A_ScriptDir%  ; Ensures a consistent starting directory.

; Allow for custom path to hg (support for moving to TortoiseHg 2.0)
IniRead hg, %A_ScriptDir%\LocalSettings\Settings.cfg, TortoiseHg, hg_path, hg

if 0 < 1  ; The left side of a non-expression if-statement is always the name of a variable.
{
    MsgBox Usage:`n`HgIgnoreCase DirectoryWithFilesToScrub
    ExitApp
}

SrcDir = %1%
StringReplace SrcDir, SrcDir, ", , All

StringRight test, SrcDir, 1 ; add trailing slash if necessary
ifnotequal test, \
    SrcDir = %SrcDir%\

RestoreOriginals(SrcDir)
RevertCaseChangeModifiedFiles(SrcDir)

RevertCaseChangeModifiedFiles(SrcDir) {
global hg
    includes =  -I "*.form" -I "*.bas" -I "*.report" -I "*.table"
    cmdline = %hg% revert --all %includes%

    ;Don't revert items that have been removed completely
    Loop 3
    {
        Result := ConsoleApp_RunWait(hg . " status -nrd " . includes, SrcDir)
        If (Result)
            Break
    }
    Loop parse, Result, `n, `r
    {
        if (A_LoopField)
            cmdline = %cmdline% -X "%A_LoopField%"
    }
    Result =
    ;msgbox %cmdline%
    ;revert all modified forms, reports, and code modules
    Loop 3
    {

        Result := ConsoleApp_RunWait(cmdline, SrcDir)
        If (Result)
            Break
    }
    ;MsgBox %Result%

    Loop parse, Result, `n, `r
    {
        StringLeft FileStatus, A_LoopField, 9
        If (FileStatus = "reverting")
        {
            StringMid FName, A_LoopField, 11
            FullPath = %SrcDir%%FName%
            ToolTip Checking %FullPath%
            RestoreIfNotEqual(FullPath, FullPath . ".orig")
        }
    }
    ToolTip
}

RestoreIfNotEqual(FName, FNameOrig) {
    FileRead File1, %FName%
    FileRead File2, %FNameOrig%

    StringLower File1, File1
    StringLower File2, File2
    ;MsgBox %FName%`n%FNameOrig%
    If (File1 = File2)
        FileDelete %FNameOrig%
    Else
        FileMove %FNameOrig%, %FName%, 1
}

RestoreOriginals(SrcDir) {
    Loop %SrcDir%*.orig
    {
        ;MsgBox %A_LoopFileLongPath%`n%NewName%
        NewName := SubStr(A_LoopFileLongPath, 1, -5)
        FileMove %A_LoopFileLongPath%, %NewName%, 1
    }
    while FileExist(SrcDir . "*.orig")
        Sleep 10
}

感谢确认这是一个有用的方法。我也在考虑类似的东西。 - RubberDuck

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接