如何让Git忽略空格和制表符?

29

我有一个小的脚本项目,包含在名为"Droid XX-XX-XX"的目录中的五个不同的源文件。每当我创建了源目录的新备份副本时,我会在X中放入日期。因此,有大约15个来自不同日期的版本。我想从最早的版本开始将它们添加到我的全新的Git存储库中。

但是我遇到了几个问题。

  1. 其中一个问题是一些文件使用制表符进行缩进,而其他文件使用空格--但是即使唯一的区别是选项卡与空格问题,Git也将整行视为不同。如何让Git忽略缩进格式?

  2. 另一个问题是某些文件名没有空格,而其他文件名中有单词之间的空格--但是Git将它们视为不同的文件。更糟糕的是,有时文件名会因为没有真正的原因而被更改为不同的名称(比如“PatrolPlan”更改为只是“Patrol”)。当我要添加新的文件组时,如何告诉Git即使文件名不同,它实际上只是某个旧文件的新版本?或者更好的是,我可以设置它在发生这种情况时自动检测吗?

  3. 最后一个问题是,在开发的某些时刻,我们将两个源文件合并成一个,或者将一个源文件拆分为两个--但是Git不会自动检测到相似之处并推断出发生了什么。如何告诉Git发生了什么?或者更好的是,我可以设置它在两个源文件合并或一个源文件拆分时自动检测吗?

我意识到问题(2)和(3)高度相关。感谢任何协助!


1
我必须反对“没有真正原因而改变...”。每件事都有原因。如果是“没有合理可控的原因”,比如另一个团队拒绝让你的生活更轻松,或者某些第三方软件表现得很难懂,那么这就没关系了。 - Kelvin
没有任何真正的原因,我只是这么做了...我知道,因为我自己也这样做过,当时我甚至想,“我没有任何真正的理由去这样做。” - CommaToast
凯尔文,我很困惑。你一直在提到“团队”——你在说什么?没有团队啊。 - CommaToast
1
@CommaToast 不要太字面理解“团队”这个词 :) 。想象一下你克隆了自己并需要向克隆体解释。有时候和自己辩论可以帮助写出更易于维护的代码。 - Kelvin
@Kelvin 我明白你的意思。但在这种情况下,我只是想重新命名一个文件。那是10年前,当我还是个新手开发者时,我并不知道自己在做什么,也不知道重命名文件可能会有任何影响。但在git中,它确实很重要,尽管本不应如此。 - CommaToast
显示剩余2条评论
3个回答

46

听起来你需要更多地控制和标准化开发过程。提交更改的人应该是修改文件的同一人,或者至少提交者应该清楚地知道发生了什么变化。

仔细检查 git diff 的输出,并使用 -w 标志忽略空格。还有显示行内差异的选项。请参见下面的行内差异

请注意,当提交时不能告诉 git 跳过空格更改。我建议使用 GitX(我喜欢“brotherbard”分支),它允许您在提交之前交互式地丢弃 hunk。

提交时使用描述性消息。例如,如果文件被拆分,请明确说明。使您的提交小。如果发现自己编写了长的提交消息,请将提交分成较小的部分。这样当您长时间后检查日志时,会更容易理解更改内容。

行内差异

Git 有一些能力在单个行中显示“单词”的差异。最简单的方法就是使用 git diff --color-words

但是,我喜欢使用 diff.wordRegex 配置自定义“单词”的含义。我也喜欢纯文本的单词差异格式,因为它更清楚地显示了差异的位置(除了使用颜色外,还在变化周围插入括号)。

命令:

git diff --word-diff=plain

在我的配置文件中还有这个:

[diff]
        wordRegex = [[:alnum:]_]+|[^[:alnum:]_[:space:]]+

这个正则表达式将以下内容视为“单词”:

  • 连续的字母数字和下划线字符串
  • 连续的非字母数字、非下划线和非空格字符字符串(用于检测运算符)

您必须使用最新版本的git才能使用wordRegex。查看您的git-config手册页面是否列出了该选项。

更新

如果您使用git mv重命名文件(这比使用其他工具或操作系统重命名更可取),则可以看到git检测到重命名。我强烈建议将重命名单独提交,而不是与文件内容的任何编辑一起提交。这是因为git实际上并没有存储重命名的信息-它使用启发式方法基于文件有多少变化来猜测是否是同一个文件。在重命名提交期间越少更改,效果越好。

如果您稍微更改了文件内容,您可以使用-C参数来git diffgit log,以尝试更努力地检测副本和重命名。添加百分比(例如-C75%)可以使git在差异方面更加宽容。百分比表示内容必须相似到什么程度才能被认为是匹配的。


1
谢谢...等我回家后,我会尝试使用-w标志。 - CommaToast
1
我发现Atlassian SourceTree(Git的图形化用户界面客户端)比命令行上的任何东西更好地处理所有这些事情。唯一的缺点是你不能通过正则表达式来筛选代码块,但同样,你在命令行上也无法高效地完成这个任务。 - CommaToast
另外……这是一个回到我还不知道git的项目之前的事情,当时只是使用文件夹的副本来跟踪版本,然后将其追溯地添加到git中,以便有一些关于它的进展和更改历史记录。仅供参考。 - CommaToast
自从我发布这个答案以来,我也开始使用SourceTree。但对于某些事情来说,使用命令行是无可替代的。我完全理解并不是每个人都有愿望或时间去了解这些技巧(我可能只知道<5%),但有时它能够带来回报。 - Kelvin
@Kelvin 是的,我同意。对于某些事情,例如 git diff --stat 等,我仍然使用 Git 命令行。 - CommaToast
显示剩余2条评论

4

现在我对Git有了更深入的了解,可以回答自己的问题。

  1. 最好使用正则表达式进行全局搜索替换,以标准化不同版本项目文件之间的空格,这样当它们被顺序提交时,空格变化就不需要提交。话虽如此,Atlassian SourceTree的diff工具允许您隐藏空格更改,因此至少您不会看到这些更改。

  2. 处理文件名更改的关键是制作一个只更改文件名的提交(不要暂存任何其他更改)。然后再制作一个修改其内容的提交。这样,普通的diff工具不需要进行大量启发式和深度挖掘就可以理解发生了什么。问题在于,如果一个文件发生了太多变化,比如名称和大部分内容都发生了变化,那么大多数diff工具将把它视为摘要删除和新文件。(如正确答案所述)

  3. 这是一个更困难的问题,没有真正好的解决方法。如果您将一个文件拆分成两个,或者合并两个文件,那么它在diff中就会很丑陋。尽量不要在拆分时同时进行大量更改,这样拆分将是一件事情,而随后的更改将是另一件事情。


2
  1. 你无法让git忽略制表符/空格,因为git会为每个文件创建哈希值,如果哈希值不同,则认为这是不同的文件。

  2. Git将树(目录)与文件视为同一内容;如果它们的内容发生变化,则它们就是不同的树。

我认为这些更改并不需要担心;这在任何开发过程中都会发生。我认为对于你来说,最好的方法是使用git重新进行开发。换句话说,从初始版本开始,然后进行必要的更改(就像最初做的那样),git会记住你正在做什么。

可选:如果您想记录更改的日期/时间大致与最初的日期/时间相同,则可以使用--date命令行选项向git commit告诉git何时进行这些更改。


我一直在重放开发过程,通过打开文件、选择全部、复制,然后将内容粘贴到现有副本中。但假设我粘贴的文件中包含空格而不是制表符——Git会显示整行不同,即使对于肉眼来说,它看起来完全相同。 - CommaToast
是的 - 这个文件不同 - 即使字符计数也不同。在执行 diff 时,你可以让 git 忽略空格,但它仍将由于空格字符的不同而认为文件不同。 - trojanfoe
我真的在寻找一种方法,不需要手动将空格替换为制表符,就可以将新文件内容粘贴到旧文件中并保存(请参见我在Kelvin的答案上的长评论)。这是我迄今为止找到的唯一一种可靠地忽略空格的正确查看差异的方法。我在BBEdit中使用“显示不可见字符”和“显示空格”来查看它们,然后很明显要找到并替换两个文件中的空格区域以标准化它们,并在将新版本粘贴到旧版本文本之前进行保存。我觉得我不应该这样做... -w不太强大... - CommaToast

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