Git如何在制表符和空格之间进行转换,但仅限于某些情况下。

4
首先,我是一个 Git 的新手。就像,如果它在暂存区中击中我,我几乎分不清缓存和索引之间的区别。我的问题是这样的:
假设我想要开发一个项目,其代码风格需要使用空格进行缩进,但我喜欢使用制表符。看起来我可以使用 clean 和 smudge 功能,但有个问题。代码风格并没有一致地遵循,有些文件在同一行上混合使用了制表符和空格。因此,一种天真的方法会导致我只改变一行,但意外地创建了一个巨大的提交,将项目完全符合其自身标准。虽然这很好,但差异会变得不太有用,所以最终我会得到更多敌人。
那么问题是:是否有一种方式可以让它以这样的方式工作,即如果我不触及某个文件,它就不会出现在提交中?(即使我只更改了单个字符,我也愿意对我接触过的文件负责任。)
编辑:好的,我刚刚取消了昨天接受的答案。我非常确定这样做很粗鲁。我的借口是今天才测试它。由于显然已经有两个人误解了我的意思,所以让我明确一下我实际上做了什么,也许有人可以告诉我是否被困惑和/或混淆了。
$ ls -a
.  ..  t.txt
$ hd t.txt # file contains 3 bytes: a tab, a capital A, and a newline
00000000  09 41 0a                                          |.A.|
00000003
$ git init
Initialized empty Git repository in /home/marvy/test/.git/
$ git config --local git config --local user.name me
$ git config --local user.email me@example.com
$ git add t.txt
$ git commit
[master (root-commit) 959bf99] testing cleverness of git status
 1 file changed, 1 insertion(+)
 create mode 100644 t.txt
$ echo '*.txt filter=tabspace' > .git/info/attributes
$ cat .git/info/attributes
*.txt filter=tabspace
$ git config --local filter.tabspace.smudge unexpand
$ git config --local filter.tabspace.clean expand
$ rm t.txt
$ git checkout t.txt
$ git status
On branch master
Changes not staged for commit:
  (use "git add <file>..." to update what will be committed)
  (use "git checkout -- <file>..." to discard changes in working directory)

        modified:   t.txt

no changes added to commit (use "git add" and/or "git commit -a")
$ git help --stackoverflow

我们可以看到,git status报告t.txt已经被修改,即使我刚刚检出它。如果你运行git diff,它会声称我要将制表符转换为空格。我做错了什么吗?


没有回答你的问题,但我很好奇你为什么要费这么大劲。在编辑器还比较愚笨的时候,我也曾强烈偏爱制表符而非空格。但是自从IDE编辑器具备了语法感知能力,我很难分辨它们之间的区别。你使用的是哪个编辑器,使得制表符实际上更容易编辑呢? - Gene
请在您最喜爱的编辑器中尝试此操作。将光标定位于行“开头”处;也就是第一个非空格字符之前。现在按下键盘上的左箭头键。会发生什么? - Mark VY
好的。我很少或从不需要这样做。 - Gene
真的吗?在任何一个小时内,我可能会做这件事很多次,如果它能正常工作,我会更频繁地这样做。我想每个人都不一样 :) - Mark VY
3个回答

3
您可以使用 pre-commit hook 仅循环遍历编辑过的文件并将制表符替换为空格。类似下面这样的东西:
FILES=`git status -s -uno | egrep '^M' | sed 's/^M//'`

for FILE in $FILES
do
    (sed -i 's/[[:space:]]*$//' "$FILE" > /dev/null 2>&1 || sed -i '' -E 's/[[:space:]]*$//' "$FILE")
fi

这个能行吗? "git status" 足够聪明以意识到文件没有改变,即使它们在磁盘和存储库中看起来不同吗?如果是这样,那么最初的“天真”解决方案也可以工作吗?(另外:我在使用Windows,所以没有 sed。虽然想起来我已经安装了Cygwin。嗯……) - Mark VY
1
@MarkVY “git status” 聪明到足以意识到文件没有改变,即使它们在磁盘和仓库中看起来不同吗?是的。是的,smudge 和 clean 可以工作,并且作为 bash 脚本执行,即使在 Windows 上也是如此。而且,您有 sed 和其他 200 多个 Unix 命令,可以直接从 CMD 会话中执行(无需 bash 或 cygwin):您只需要从 Git 安装中添加 usr/bin,如 https://stackoverflow.com/a/44510644/6309 中所述。 - VonC
好的,太棒了!谢谢你! - Mark VY

2

最好的方法是使用带有 smudge-clean 的脚本。

smudge-clean

Smudge/Clean 是过滤器,会在文件通过暂存区时运行,并通过执行给定的脚本修改文件。

使用 smudge 和 clean 只会触及您通过暂存区传递的文件。


例如(Unix 示例): 如果您不熟悉 uexpand/unuexpand,请在此处阅读有关它的信息 这里

~/.gitconfig

# filters to convert between tabs to spaces
[filter "tabspace"]
    smudge = unexpand --tabs=2 --first-only
    clean = expand --tabs=2 --initial

~/.gitattributes

*.txt  filter=tabspace

现在,每当您添加/检出文件时,它们将根据您的配置进行转换。
您还可以查看GitHub上的此项目

似乎我表达得非常不清楚,因为每个人都试图回答我没有问的问题。您提倡的设置将导致 git status 声称我修改了从未触及的文件,因为这些文件不符合官方项目编码风格,而过滤器会尝试使它们符合规范。我的问题的整个重点是如何避免这种情况!我知道我说我是一个 Git 新手,但这并不意味着我无助到不能阅读文档。请给我一点信任 :) - Mark VY
享受您的自动赏金 :) 尽管如此,我仍然不接受这个答案。这是一个好答案,只是不是我的问题的答案 :) - Mark VY

1

设置代码库:

  1. 获取代码库。
  2. 合并到您的本地主分支。

进行大改动:

  1. 如果需要,可以更改整个代码库。做你想做的任何事情。进行一行更改以改变所有内容。但是只将自己触及的文件添加到“暂存区”。换句话说,您必须手动添加这些文件:git add FilesYouWantToAdd.txt和其他您负责的文件。不要将您不负责的文件添加到暂存区中。

    git commit -m 'alter spaces to tabs' // 仅添加您负责的文件。

    git push

完成。

放弃其他更改:

因为其余文件中没有实质性更改,所以可以简单地放弃这些更改。

git reset HEAD .

实际上很简单。希望这可以帮到你。

编辑: git modified


当文件被修改时,git状态将显示它已被修改。但是,如果文件没有被修改,那么git状态仍然可以声称它已被修改,因为存储库中的文件版本与在工作目录中运行清洁过滤器产生的版本不匹配。对吗? - Mark VY
我定义“变更”为文件发生任何不同的事情并进行保存。例如,如果您删除一个空格并添加一个制表符-即使您只更改了一个字符-并保存文档,这也将构成Git中的变更。我的理解是,clean过滤器实际上正在修改所有文件。因此,您必须手动将您独自工作的所有文件添加到暂存区中,而且只有您知道这些文件。如果它们都在同一个文件夹中,那么很容易-只需将该文件夹添加到暂存区即可。希望这讲得清楚吗? - BenKoshy
是的,这很有道理,但这不是我想要的。我想要一种魔法,让git区分“由我修改”和“由我设置的过滤器修改”。 - Mark VY
1
可能更容易的方法是创建一个文本文件,您可以将其中包含的所有需要添加的文件不断地复制粘贴到命令行中,例如:git add file1.txt file2.txt等。这可能是最简单的方法 - 假设您没有处理太多文件。 - BenKoshy
这太不方便了,我宁愿放弃过滤器。问题不在于太少还是太多,而在于有时我自己都不记得我改了什么,所以我使用git status作为提醒。但我会记住这个想法,在其他情况下可能会有用。 - Mark VY
显示剩余2条评论

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