Git fetch 不能解决 git 结帐时的路径规范错误 -- *



在以前的项目中,我经常运行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. 我读了另一篇StackOverflow帖子,说git fetch可以解决这个路径规范错误。我试过了,它从远程打印了一个新的功能分支:

    $ 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现在显示我有一棵干净的工作树。

我的路径规范错误的根本原因到底是什么,我该如何解决它?我可以同时签出特定文件,但我只是对这个错误感到好奇。

恐怕链接的问题 Git:无法结帐分支 - 错误:路径规范"..."不匹配 Git 已知的任何文件都是一团糟:正如您所指出的,它有很多答案,几乎没有解释。 同时,ElpieKay的评论有正确的答案:node_modules是一个文件或目录,你让你的Git忽略了,所以当你要求你的Git更新它时,它说:嗯? 现在更新什么?

长篇描述

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

  • 从一些现有的远程跟踪名称(如origin/develop)自动创建一个新分支(例如develop的新分支名称);或
  • 签出现有分支(具有给定名称的分支,如master);或
  • 查看现有的历史提交(例如,通过标签名称(如v1.2)或原始哈希 ID),从而产生 Git 所谓的分离 HEAD。

所有这些操作都以某种方式移动了 Git 的HEAD概念。 特殊名称HEAD(全大写字母 - 这在Linux系统上通常是必需的,而Windows用户通常可以通过以小写形式键入head)是Git如何记住您所在的分支(如果有的话)。 因此,上述三种git checkout改变您所在的分支,或者 - 对于听起来很可怕,但实际上内部很正常,1分离的 HEAD 案例,即缺少任何分支。


例如,1Git 在冲突或交互式变基时使用这种分离的 HEAD 状态。 将其用于正常开发不是一个好主意,但对于临时工作或这些内部到 Git 状态来说,这很好。 只需完成你的变基,或者任何导致暂时分离的 HEAD 的东西,Git 就会重新连接 HEAD,你可以继续走动,你的头牢牢地重新连接。:-)


但是git checkout可以做更多根本不涉及改变HEAD的事情,这就是git checkout --的目的。 在这里,Git 可以:

  • 将索引中的一个或多个文件提取到工作树中:git checkout --filename(s),或
  • 从特定提交中提取一个或多个文件,首先将其写入索引,然后写入工作树:git checkoutcommit-specifier--filename(s)

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

git checkout可以做更多的事情,但让我们停止这三组明显非常不同的操作:(1)将HEAD更改为不同的分支,(2)将HEAD更改为在特定提交时分离,以及(3)根本不更改HEAD,只需从索引或特定提交中获取一个文件或多个文件即可。

Git 在git checkout文档中使用各种语法标记来表达这些不同的操作。 引用这一点——我将引用那里的整个可怕的七项清单——我们看到:

git 结帐 [-q] [-f] [-m] [<branch>] git 结帐 [-q] [-f] [-m]--detach[<branch>]git 结帐 [-q] [-f] [-m] [--detach]


<commit>

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

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

按名称签出分支(或签出提交并分离 HEAD)

从第一行开始:

git 结帐[<branch>]

(我省略了简化它的选项)。 这将在尖括号中显示分支名称,这意味着您应该填写一个。 它也在方括号中,所以你可以省略它,但如果你把它省略,它意味着"留在当前的分支上",这是一件愚蠢的事情。 像这样将单词放在尖括号中branch将其标记为某些人所说的元变量,即您应该填写的内容,元变量的名称告诉您这里会发生什么类型:分支名称!

这是切换到某个现有分支,或从某个现有的远程跟踪名称操作创建新分支。 您指定的分支名称是要切换到或自动创建的分支名称。 Git 将查找具有该名称的现有分支,如果找不到,将查看所有远程跟踪名称(origin/masterorigin/develop等),以查看这些名称之一是否可以origin/删除并成为您请求的名称。

(第二行引用与第一行类似,但插入了--detach。 第三行与前两行类似,但不是<branch>而是说<commit>. 在第二个命令行中,--detach是必需的,在第三个命令行中,它仍然是可选的。<commit>元变量意味着您可以使用任何命名提交的内容,而不仅仅是分支名称。 这些是产生分离 HEAD 的变体:它们以与切换分支相同的方式签出提交,但它们在此过程中会切断您的 HEAD,因此您没有分支。 从本质上讲,如果你给git checkout一个参数来命名一个提交,但不是分支名称,Git 只是假设你的意思--detach。 如果要在使用分支名称时分离,则必须添加--detach。 最后一件事不是大多数人想做的事情,但手册页无论如何都涵盖了它。

链接的问题及其答案主要是关于从远程跟踪名称创建新分支也就是说,他们回答了这个问题:

如果我说git checkout feature-X我得到一个错误,但随后我运行git fetch,然后再次执行git checkout feature-X,它有效。 为什么?

这里的答案是,你第一次跑git checkout develop,你没有origin/feature-X,但是git fetch跑完之后,你确实origin/feature-X。 反过来,这是因为git fetch创造了它,因为其他人origin上创造了feature-X,这是最近一段时间。git fetch让你的 Git 在origin调用 Git,并获取其所有分支和提交的列表。 您的 Git 会加载您尚未拥有的所有新提交,并创建或更新所有origin/*名称,现在您有origin/feature-X.

签出特定文件

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

git 结帐[<tree-ish>] [--]<pathspec>...

显示了两个元变量。 第一个拼写为<tree-ish>,这是 Git 的简写:你可以在这里使用分支名称,或者提交哈希,或者我可以用来查找我称之为树对象的任何内容。Git 有很多方法可以指定提交哈希 ID,这意味着涵盖所有这些方法,以及一些你不太可能遇到的更奇怪的极端情况。 第一个元变量是可选的,如果你省略它,Git 将从 Git 的索引中签出文件(我们在这里没有真正描述,我不会详细介绍,因为这已经很长了)。

第二个元变量是<pathspec>。 请注意,它不是可选的! 这是 Git 语言的简写,用于文件名,或带有*的模式,或许多其他内容中的任何一个,这些内容也太长而无法在这里讨论。...部分意味着您可以列出其中的多个。 这些路径规范命名您希望 Git 在<tree-ish>中找到的一个或多个特定文件(您命名的提交,如果您命名了提交)。

哪里出了问题

当你写:

git checkout -- *

您的 shell(可能是bash)会扩展*以匹配当前工作目录中的所有文件。阿拉伯数字因此,如果你有文件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>论据。 这意味着"当前目录中 Git 已知的所有文件",因此这将执行您想要的操作。 你也可以写:

git checkout -- '*'

它使用引号来保护*免受bash(或您正在使用的任何外壳)的影响。 然后Git将看到*,Git 将*扩展到"当前目录中Git已知的所有文件"。 但是写.更容易。


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

相关内容

最新更新