之间的区别。 git中的(点)和*(星号)通配符

时间:2018-11-27 21:46:51

标签: git wildcard

我有一个本地存储库,尝试放弃自上次提交以来的所有更改

git checkout HEAD -- *

命令。即使所做的更改位于某个子目录中,一切也都可以正常工作。但是,当我添加一些未跟踪的文件(使.gitignore中的掩码令人满意)时,在存储库的根目录中说“ Ignored.txt”,以上命令将失败,并显示消息

error: pathspec 'Ignored.txt' did not match any file(s) known to git

相反,

git checkout HEAD -- .

按预期工作。所以我想知道:

  

git中.*通配符之间有什么区别?

4 个答案:

答案 0 :(得分:4)

主要差异不是主要

因为它不在Git中,所以是否有所不同,如果有,有什么差异,取决于您使用的not-Git命令解释器。

Shell vs Git

在Unix-ish系统(包括Linux)上,是命令解释器(或“ shell”)为您扩展了*。各种外壳,例如bashzshfish以及tcshdash等(大多数名称以{{1}结尾) }),将sh解释为“当前工作目录中的大多数文件”。这种扩展称为globbing

这些shell 以任何方式解释和扩展*。这意味着:

.

使用三个参数git xyzzy -- . gitxyzzy调用--。但是:

.

使用七个参数调用git xyzzy -- * gitxyzy--ab,{{1} },c,如果当前工作目录中有名为deabc的文件。 / p>

Git并没有太多使用工作目录

Git主要对存储在存储库中的 commits 感兴趣。为了构建 new 提交,Git使用它的 index (也存储在存储库中)。索引实际上以特殊的,仅Git的压缩格式保存文件,与提交中的内容基本相同。

文件的索引副本是可写的,而任何文件的提交副本都是只读的。 (从技术上讲,Git只是将 objects 保留在其主对象数据库中,索引和提交副本都只是对这些对象的引用。您不能覆盖任何对象,但是可以添加 new < / em>对象并切换索引引用;您不能更改提交的引用。其结果是提交中的文件被冻结,而索引中的文件被解冻/可写。)

这种仅Git的内部文件格式对您,用户或计算机上的大多数程序都没有用。因此,这些文件必须进行扩展。它们被扩展到您的工作树中,该文件位于可能具有子目录(子文件夹)的目录(或文件夹)中。您可以将当前工作目录设置为任何这些工作树目录。然后,Shell将d扩展为工作树中此当前目录中找到的文件的名称。

Git主要使用您当前的工作目录查找Git实际文件所在的存储库数据库。工作树副本仅供您随意摆弄。

某些Git命令确实使用工作树

当然,e*都以各种方式使用工作树。但是请注意,由于工作树是供您的使用的,因此您可以在存储库中将不存在的文件放入其中。

文件可以在您的工作树中,但不能在您的索引中。 (实际上,该索引位于工作树和适当的存储库之间,并为Git提供了一个存放将进入 next 提交的文件的位置。)此状态(在您的工作树中,但不在索引中)被称为未跟踪

Git本质上不了解未跟踪的文件。但是它们在您的工作树中(根据定义),因此,如果这也是您当前的工作目录,并且您使用git checkout并让Shell将git add扩展为这些文件名,您将将当前不知道的文件名传递给Git。

如果您使用的命令是*,您将告诉Git:将此文件从工作树复制到索引中。这将在索引中创建文件,以便现在将在您进行的下一次提交中。 Git很好!

但是,如果您使用的命令是*,您将告诉Git:将此文件从索引复制到工作树中。 Git在索引中找不到文件,所以它会抱怨。 (它不会触摸工作树中的文件。)

请注意,未跟踪的文件可以(但不一定)也被忽略(这不是一个很好的术语,但这是Git使用的一个术语)。您通过在名为git add的文件中列出未跟踪文件的名称或名称模式来告诉Git不要抱怨。在这种情况下,git checkout会警告您忽略了一个未被跟踪和忽略的文件:即使您使用.gitignore,Git也不会将该文件复制到索引中。

Git 了解git add

如果使用git add *.,则shell根本不会扩展。 Git看到了git add .并知道这意味着“当前工作目录”,因此,如果合适,例如git checkout -- .,Git将读取当前工作目录。它可以将找到的文件与索引中已经存在的文件进行比较,并知道如何为.更新它们,包括添加(1)未跟踪的文件和( 2)在git add中列出。 (根据定义,已经在中的文件不会被忽略,因此它们也将从工作树更新到索引中。)

使用git add,Git可以直接在索引中查找,根本看不到未跟踪的文件。

特殊情况:“点文件”,根本没有文件和CMD.EXE

上面的内容非常简单,但是有几种特殊情况使它复杂化:

  • 大多数shell 在扩展.gitignore时都不匹配点文件(例如git checkout -- ..profile)。因此,如果当前目录中有文件.gitignore*,并且使用.gitattributes,则可能不会将这些文件的索引版本复制到工作树中。

  • 如果当前目录中根本没有文件,或者没有文件与.gitignore之类的glob模式匹配,则某些shell会抱怨并中止命令,而其他shell只会传递该模式-{{1 }}甚至git checkout -- *-运行的程序上。

  • DOS样式的CMD.EXE不会展开*.asdf

对于最后两种情况,Git本身确实参见*.asdf。现在, Git 有机会进行全局扩展,在Git的情况下,Git 确实匹配*文件,例如*和{{1} }。因此,如果当前工作目录没有非非点文件,但确实有*.,则运行:

.gitattributes

然后在这种情况下,Git会将.gitignore.gitattributes从索引复制到工作树。

答案 1 :(得分:3)

Git没有看到星号。它通过外壳扩展为当前目录中的所有文件名和目录名(不以点开头)。该点不是通配符,仅表示“当前目录”。所以

git checkout HEAD -- .

检出当前目录及其所有子目录,即git知道的一切。带有星号的git会看到

git checkout HEAD -- tracked-file1 tracked-file2 Ignored.txt

但是它不知道如何检出被忽略的文件:因此出现错误。

答案 2 :(得分:2)

首先要了解的是,.不是通配符。

.*都可以是路径规范(这就是在您使用的命令中使用它们的意思。)要了解路径规范的解释方式,您可以在git词汇表(https://git-scm.com/docs/gitglossary)中的路径规范下查看

但是另一个麻烦是,在您的“失败”示例中,git 不是接收到*的路径规范,因为您的外壳在将*传递给它之前将其展开git。因此,要完全理解此行为,您还可以参考外壳程序的文档,了解它如何预处理命令行。

通过*作为路径规范,而不会受到外壳程序的干扰,则可以对其进行转义。 (假设您使用的是bash或类似工具,则它看起来可能是

git checkout -- '*'

但同样,这取决于所使用的外壳。)

答案 3 :(得分:1)

那是文件混乱。 https://www.w3resource.com/linux-system-administration/file-globbing.php

.表示“当前目录”,而*表示“将其替换为所有可能的值”。通常情况是,bash(如果您使用的是bash)将使用*并实际上将其替换为您所在的所有可能值(如果您仅提供一个*作为参数),并且将(替换所有值之后)交给git。只有在没有匹配值的情况下(例如,您写“ blahblah *”并且那里没有与模式匹配的文件),bash才会放弃替换该值,并将“ *”移交给git。