为什么 'Git add *' 保留一些文件未暂存?

Why does 'Git add *' leave some files unstaged?

我在我的 Django 项目中初始化了一个新的 git 存储库。当我 运行 git 时,所有文件(包括 子目录 pipfile 中的文件都被暂存添加 * 除了 .__atomic-writegxd2tirf 文件。为什么排除这个?

git add * 仅阶段名称不以 "." 开头的问题。 另一种方法可以是 运行 git add . for all files OR 运行 git add 对于以 "."(点) 开头的文件名。

现在您已经使用几个不同的命令(特别是 git addecho 体验过这种情况),是时候概括一下了。

在类 Unix 系统上(包括 Linux、macOS 终端 windows 等),或在 Windows 上使用 bash 作为命令解释器时,您正在处理一种称为 shell 的编程语言。有不同的 shell 编程语言(bash、dash、sh、zsh、fish 等——由于历史原因,大多数语言的名称中往往带有 sh),但它们都倾向于共享某些属性。其中之一是它们如何 扩展 命令行参数以传递给命令。

$ mkdir t && cd t && touch .a .b c d e
$ ls -A
.a      .b      c       d       e
$ echo *
c d e

这个临时目录中有五个文件1(除了ls -A 省略的标准两个... 之外。但是 echo * 只扩展到其中三个。那是因为 * 扩展故意 忽略 名称 2 .[=107 开头的任何文件=].

一些 shell 有打开或关闭此功能的选项:例如,在 bash 中,您可以设置 dotglob 选项。 (用户可能不应该 fiddle 过多地使用这些选项,以免他们在使用其他人的 shell 或其他一些 shell 时感到惊讶,但知道它们存在是件好事。)

Shell 编程语言大多是围绕 运行 其他程序 的思想构建的。因此,当 shell 产生提示时,例如 $> 或任何 shell 的标准提示,然后您输入一系列白色-space 分隔的词,shell 将这些词分解并使用其中一个作为命令名称,其余的作为命令的参数,运行 是该命令。因此,输入:

git add *

有 shell 运行 git 命令,参数为 add*。但是 * 本身被 shell 特殊对待 ,扩展为文件名,不包括以点开头的文件名。所以 git 命令只看到参数:

add
c
d
e

(每行一个参数)。 git 的第一个参数是子命令,在本例中为 add,其余参数由子命令解释。3

自从 * 被 shell 扩展后,Git 从未 看到 * 并且从未有过自己的机会用 * 做自己的事情。但是,您可以保护 * 不受 shell:

的影响
*

在这里,shell 将双引号视为一种保护。这使得 shell 无法解释 *。 shell did 自己解释双引号,所以 echo 没有看到双引号。它只看到星号 *.

$ echo '"*"'
"*"

这次我使用了单引号作为外层。这些还告诉 shell 保护内容,即不要解释双引号 星号。所以这次 echo 看到了双引号和星号。

shell 的引用规则因 shell 的不同而不同,这可能会变得非常复杂。 None 这与 Git 本身有任何关系:shell 正在做相同类型的解释,而不管哪个命令是 运行.

如果您设法将文字星号传递给 Git,Git 将对星号进行自己的解释,并且其解释不同来自 shell。所以 git add '*' 表现不同。

如果您使用 Windows 和 CMD.EXE,请注意 CMD.EXE 本身不如 shell 可编程,并且具有更简单的命令行规则。在某些方面,这使得它的使用更容易、更可预测。这是它的优势,也是它的劣势:典型 Unix shells 的可编程性使得使用它们编写整个系统成为可能。


1在 Unix 的世界里,即使是目录(或文件夹,如果你喜欢这个词)也是一种文件。有时人们使用 file 这个词来表示文件和目录,但由于我已经排除了 ...,它们是目录文件,所以我绕过了需要提及——除了这个脚注!

2在类 Unix 系统中的分层文件系统中,文件不一定只有一个名称。在这里,单词 name 实际上是指在某个包含目录中找到的 component name。例如,Git 中的文件名可能是 path/to/.file。然而,在 Unix 文件系统级别,这是一个名为 path 的目录,其中包含一个名为 to 的目录,其中包含一个组件名称为 .file 的文件。对于 shell 文件名末尾有一个点,所以 echo path/to/* 不会匹配它。

3这种顶级命令与子命令的模式最近变得越来越普遍,但只有部分 Unix 系统以这种方式工作。特别是在 Python 中,您可以使用 argparse 来模拟此类行为。