Git 提取无法解决 git 结帐时的路径规范错误 -- *

Git fetch does not resolve pathspec error on git checkout -- *

在以前的项目中,我经常 运行 git checkout -- * 放弃工作目录中的所有更改。

  1. 在我当前的项目中,我得到以下信息:

    $ git checkout -- *
    error: pathspec 'node_modules' did not match any file(s) known to git.
    
  2. 然后我做一个git status:

    $ git status
    On branch <feature_branch>
    Your branch is ahead of 'origin/<feature_branch>' by 2 commits.
      (use "git push" to publish your local commits)
    
    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:   <relative_file_path_in_current_app_dir>
    
    no changes added to commit (use "git add" and/or "git commit -a")
    
  3. 我读了另一个 Whosebug postgit fetch 可以解决这个 pathspec 错误。我试过了,它从远程拉出一个新的功能分支:

    $ git fetch
    remote: Counting objects: 67, done.
    remote: Compressing objects: 100% (67/67), done.
    remote: Total 67 (delta 55), reused 0 (delta 0)
    Unpacking objects: 100% (67/67), done.
    From https://bitbucket.org/<repo_name>
       b42b31e05..e7f3858ad  <unrelated_bug_branch>       -> origin/<unrelated_bug_branch>
       36b2cd4e9..ac87583fd  develop                      -> origin/develop
       61945b8ef..22a63fd7e  <unrelated_feature_1_branch> -> origin/<unrelated_feature_branch>
       322a39980..1f8752f2c  <unrelated_feature_2_branch> -> origin/<unrelated_feature_2_branch>
     * [new branch]          <unrelated_feature_3_branch> -> origin/<unrelated_feature_3_branch>
       5fe02b8b3..a27140571  <unrelated_feature_4_branch> -> origin/<unrelated_feature_4_branch>
    
  4. 我再做一个git status,和上面#2一样。

  5. 我可以定位特定文件,这会起作用:

    $ git checkout -- <relative_file_path_in_current_app_dir>
    
  6. git status 现在显示我有一个干净的工作树。

我的 pathspec 错误的根本原因到底是什么,我该如何解决?我可以同时检查特定文件,但我只是对这个错误感到好奇。

恐怕链接问题 Git: cannot checkout branch - error: pathspec '...' did not match any file(s) known to git is a mess: as you noted, it has a lot of answers, and little explication. Meanwhile 的答案是正确的:node_modules 是您要 Git 忽略的文件或目录,所以当您询问 Git 更新它,它说:嗯?现在更新什么?

详细说明

问题的根源在于Git 将太多东西塞进一个命令。 git checkout 命令可以:

  • 自动创建一个新的 b运行ch(一个新的 b运行ch name 例如 develop)从一些现有的远程-跟踪名称如 origin/develop;或
  • 查看现有的 b运行ch(具有给定 name 的 b运行ch,例如 master);或
  • 检查现有的历史提交(通过 标签名称 例如 v1.2,或通过原始哈希 ID),导致 Git 调用一个 分离的 HEAD.

所有这些行为都在某种程度上改变了您 Git 对 HEAD 的看法。特殊名称 HEAD(全是大写字母——这在 Linux 系统上通常是必需的,而 Windows 用户通常可以通过输入小写的 head 来逃脱)是你的 Git 记得 你在哪个 b运行ch,如果有的话。因此,上述三种 git checkout 改变 你在哪个 b运行ch,或者——听起来很可怕,但实际上在内部很正常,1 分离HEAD case,缺少-any-b运行ch.


1Git 例如,当您处于冲突或交互式变基时,使用此分离的 HEAD 状态。将它用于正常开发不是一个好主意,但它可以用于临时工作,或者用于这些 internal-to-Git 状态。只需完成你的变基,或任何导致 HEAD 暂时分离的东西,Git 将重新连接 HEAD,你可以继续四处走动,头部牢固地重新连接。 :-)


但是 git checkout 可以做更多 涉及改变你的 HEAD 的事情,这就是 git checkout --是为了。这里,Git可以:

  • 从您的 index 中提取一个或多个文件到您的 work-tree: git checkout -- <em>文件名</em>,或
  • 从特定提交中提取一个或多个文件,首先将其写入索引,然后写入工作树:git checkout <em>commit-specifier</em> -- <em>文件名</em>.

-- 将提交说明符(例如 masterdevelop1f3a907 或其他)与文件名分开。如果文件名为 master-- 通常 需要 git checkout master 会将您的 HEAD 切换为 master,而不是检查名为 master 文件 。它有时是可选的:如果你有一个名为 master 的文件,但你想从 develop 的提示中获取副本,写 git checkout develop master 可以让 Git 清楚这一点(即使它让普通人感到困惑)。

git checkout 可以做更多的事情,但让我们就这三组明显不同的操作停止:(1) 将 HEAD 更改为不同的 b运行ch,(2)更改 HEAD 以在特定提交时分离 ,以及(3)根本不更改 HEAD,只需从索引或特定提交.

中获取一个或多个文件

Git用the git checkout documentation中的各种语法标记来表达这些不同的动作。引用其中的内容——我将引用其中包含的整个可怕的七项列表——我们看到:

git checkout [-q] [-f] [-m] [<branch>]
git checkout [-q] [-f] [-m] --detach [<branch>]
git checkout [-q] [-f] [-m] [--detach] <commit>
git checkout [-q] [-f] [-m] [[-b | -B | --orphan] <new_branch> [<start_point>]
git checkout [-f | --ours | --theirs | -m | --conflict=<style>] [<tree-ish>] [--] <paths>...
git checkout [<tree-ish>] [--] <pathspec>...
git checkout (-p | --patch) [<tree-ish>] [--] [<paths>...]

其中三个是我们在此答案中讨论的操作(其他四个是 更多 git checkout 可以做的事情,其中​​一些可能应该是不同的 Git 命令)。让我们来看看它们。

按名称签出 b运行ch(或签出提交并分离 HEAD)

从第一行开始:

git checkout [<branch>]

(我省略了简化它的选项)。这在尖括号中显示了 b运行ch 名称,这意味着您应该填写一个。它也在方括号中,因此您可以将其省略,但是如果您 do把它去掉,它意味着 "stay on the current branch",这是一种愚蠢的做法。像这样将单词 branch 放在尖括号中,将其标记为某些人所说的 元变量 ,即您应该使用 填写的内容name 的元变量告诉你什么种东西 去这里:b运行ch names!

这是切换到一些现有的 b运行ch,或者从一些现有的远程跟踪名称 操作创建一个新的 b运行ch。您提供的 b运行ch 名称是要切换到或自动创建的 b运行ch 名称。 Git 将查找具有该名称的现有 b运行ch,如果找不到,将查看您所有的 remote-tracking 名称——您的 origin/masterorigin/develop,依此类推——查看 这些 名称中的一个是否可以 origin/ 删除并成为您要求的名称。

(第二行与第一行类似,但插入了 --detach。第三行与前两行类似,但不是 <branch>,而是 <commit>。在第二个命令行中,--detach 是必需的,在第三个中,它又是可选的。<commit> 元变量意味着您可以使用任何命名提交的内容,而不仅仅是 b运行ch 名称。这些是产生分离 HEAD 的变体:它们检查提交的方式与切换 b运行ches 的方式相同,但它们在此过程中切断了你的 HEAD,所以你在 no b运行ch。本质上,如果你给 git checkout 一个命名提交但不是 b运行ch 名称的参数,Git 只是假设你 meant --detach。如果你想在使用 b运行ch 名称时分离,你必须添加 --detach。这最后一件事不是什么大多数人都想这样做,但手册页还是涵盖了它。)

链接的问题及其答案主要是关于从远程跟踪名称创建新的 b运行ch。 即他们回答问题:

If I say git checkout feature-X I get an error, but then I run git fetch, and do git checkout feature-X again, and it works. Why?

这里的答案是,你第一次 运行 git checkout develop,你 没有 origin/feature-X,但在 git fetch 完成了,你 做了 origin/feature-X。反过来,这是因为 git fetch 创建了它,因为 其他人 origin 上创建了 feature-X,时间相对较近。 git fetch 让您的 Git 在 origin 调用 Git 并获取其所有 b运行 项和提交的列表。你的 Git 加载了他们所有你还没有的新提交,并创建或更新了你所有的 origin/* 名称,现在你有 origin/feature-X.

签出特定文件

倒数第二个引用的语法行:

git checkout [<tree-ish>] [--] <pathspec>...

显示两个元变量。第一个拼写为 <tree-ish>,即 Git shorthand 用于: 您可以在此处使用 b运行ch 名称,或提交哈希,或任何其他内容我可以用它来找到我所说的树对象。 Git 有 很多 方法来指定提交哈希 ID,这意味着涵盖所有这些,加上一些你不太可能遇到的更奇怪的极端情况。这第一个元变量是可选的,如果你省略它,Git 将从 Git 的 index 中检出文件(我们还没有真正描述过在这里,我不会详细介绍,因为这已经很长了)。

第二个元变量是<pathspec>。注意是不是可选的!这是 Git-speak shorthand 表示 文件名,或带有 * 的模式,或许多其他太长的东西中的任何一个进入此处。 ... 部分意味着您可以列出多个。这些 pathspecs 命名您希望 Git 在 <tree-ish> 中找到的 特定文件或文件 (您指定的提交,如果你命名的话)。

哪里出了问题

当你写:

git checkout -- *

您的 shell—可能 bash—扩展 * 以匹配您当前工作目录中的所有文件。2 所以如果你有文件 READMEhellonode_modules 等等,Git 看到的是:

git checkout -- README hello node_modules ...

没有 <tree-ish>,因此 Git 在您的索引中查找名为 READMEhellonode_modules 等的文件。

如果它没有找到其中之一——也没有找到node_modules——Git抱怨:

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

什么都不做。

如果您改为使用 .,您的 shell 运行:

git checkout -- .

和 Git 将 . 视为 <pathspec> 参数。这意味着 "all the files known to Git in the current directory",所以这将执行您想要的操作。你也可以这样写:

git checkout -- '*'

它使用引号来保护 * 免受 bash(或您正在使用的任何 shell)的影响。然后 Git 将看到 * 并且 Git* 扩展为 "all the files known to Git in the current directory"。但是写 . 更容易。


2请注意 Windows,CMD.EXE 不会 展开 * ,而不是将其传递给 Git,让 Git 扩展 *,此错误永远不会发生!