为什么这个干净的过滤器在第一次运行时被忽略了?

Why is this clean filter being ignored the first time it runs?

为了在提交时自动使 .sh 文件可执行,我制作了一个干净的过滤器并对其结果感到困惑。

首先,我运行git config filter.executable.clean 'chmod +x %f'。这会将以下节添加到我的测试存储库的 .git/config 文件中:

[filter "executable"]
        clean = chmod +x %f

接下来我创建了一个 .gitattributes 文件:

*.sh    filter=executable

让我们看看它的表现如何。当然,新创建的 .sh 文件不可执行:

$ touch foo.sh
$ ls -l foo.sh
-rw-r--r-- foo.sh

如果我们 add 我们的清洁过滤器应该 运行。但这会在我们的工作目录中产生一个可执行文件,在我们的暂存区中产生一个 不可执行的 文件,以及一个脏工作副本!

$ git add foo.sh
$ ls -l foo.sh  # As expected
-rwxr-xr-x foo.sh
$ git ls-files -s foo.sh  # Confusing
100644 foo.sh
$ git status  # Confusing
Changes to be committed:
  new file: foo.sh

Changes not staged for commit:
  modified: foo.sh

运行 git add 再次在暂存区生成一个可执行文件和一个干净的工作副本:

$ git add foo.sh
$ git ls-files -s foo.sh
10755 foo.sh
$ git status
Changes to be committed:
  new file: foo.sh

这是怎么回事?

Git 的过滤器必须 take their input from STDIN and send their output to STDOUT:

Upon checkout, when the smudge command is specified, the command is fed the blob object from its standard input, and its standard output is used to update the worktree file. Similarly, the clean command is used to convert the contents of worktree file upon checkin.

上面显示的过滤器不是这样的。它忽略 STDIN 并且不向 STDOUT 发送任何输出,因此第一个 git add 命令不会捕获其效果。暂存文件不可执行。

但是,过滤器 可以 运行。这具有在工作副本中的文件上添加执行位的副作用。 git status 看到文件的权限已更改。

第二个 git add 捕获可执行位,但不是直接因为我们的过滤器。它只是暂存它在工作副本中看到的权限更改。

我不知道有什么方法可以通过 STDIN 和 STDOUT 修改文件的权限。在干净和污迹过滤器中修改文件权限可能非常困难。