为什么 '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 add
和 echo
体验过这种情况),是时候概括一下了。
在类 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
来模拟此类行为。
我在我的 Django 项目中初始化了一个新的 git 存储库。当我 运行 git 时,所有文件(包括 子目录 和 pipfile 中的文件都被暂存添加 * 除了 .__atomic-writegxd2tirf 文件。为什么排除这个?
git add * 仅阶段名称不以 "." 开头的问题。 另一种方法可以是 运行 git add . for all files OR 运行 git add 对于以 "."(点) 开头的文件名。
现在您已经使用几个不同的命令(特别是 git add
和 echo
体验过这种情况),是时候概括一下了。
在类 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
来模拟此类行为。