Git - 为啥在已删除文件上运行命令时需要双破折号?

Posted

技术标签:

【中文标题】Git - 为啥在已删除文件上运行命令时需要双破折号?【英文标题】:Git - why are double dashes needed when running a command on a deleted file?Git - 为什么在已删除文件上运行命令时需要双破折号? 【发布时间】:2013-04-05 02:07:16 【问题描述】:

考虑一个 git 存储库,其中一个文件曾经被删除。

git rm path/to/file
git commit -a -m"testing"

好的,现在我想查看文件的git log,但我收到了经典的错误消息:

git log path/to/file
fatal: ambiguous argument 'path/to/file': unknown revision or path not in the working tree.
Use '--' to separate paths from revisions

解决方法很简单——添加--:

git log -- path/to/file

但是……为什么?为什么需要这个?这里的理由是什么? git 不能做一个有根据的猜测,这可能曾经是一个文件吗?我理解“歧义”问题 - 但从来没有那个名字的标签。如果文件曾经被删除,并且没有标签存在,那么选择“文件解释”总是不错的选择。

另一方面,可以有一个与文件同名的标签,git log 处理得很好:

fatal: ambiguous argument 'path/to/file': both revision and filename
Use '--' to separate filenames from revisions

这种行为似乎不一致。谁能解释一下 git 的开发者的想法?

【问题讨论】:

【参考方案1】:

git log 可用于文件以及分支、标签等。

假设您有一个名为 a/b/c 的文件夹,您将使用

获取此文件夹的提交
git log a/b/c

没关系。

您还可以有一个名为d/e/f 的分支。您将使用

获得此分支的提交
git log d/e/f

那也不错。

如果无法明确确定git log 应该处理的项目,事情就会变得复杂。如果你很愚蠢并且也调用你的 branch a/b/c,git 不知道应该打印谁的日志:branch a/b/c 的日志或你的日志目录 a/b/c?因此,您必须多说一点您想要接收的信息:

显示分支的日志a/b/c:git log a/b/c -- 显示当前分支中文件夹a/b/c的日志:git log -- a/b/ca/b/c 分支中显示文件夹 a/b/c 的日志:git log a/b/c -- a/b/c

对于删除的文件,您会遇到类似的问题:工作副本中既没有名为path/to/file 的文件,也没有名为path/to/file 的分支。这就是为什么你必须指定你想要什么的原因。

当然,git 可以知道 一个名为 path/to/file 20.000 次修订的文件,但这需要(最坏的情况)搜索项目的整个历史记录,无论这样的文件是否存在或不是。

通过在--之后显式指定文件路径,你告诉git:

更加努力地搜索该文件,即使需要几个小时


结论(回答您的问题): 在您的情况下,-- 是必需的,否则git log 通常会运行得更慢。

【讨论】:

【参考方案2】:

为什么文件存在与不存在时的差异?我们来看看案例:

    版本和文件名都存在。显然是模棱两可的。 存在修订但文件名不存在。显然不是模棱两可。 版本不存在,文件名存在。显然不是模棱两可。 修订版不存在,文件名也不存在。模棱两可?

确实如此。

要知道某个文件是否曾经存在于某个路径意味着遍历历史、打开每个提交、打开每个树并遍历树以查看该路径当时是否存在。在具有大量提交和深度路径的大型存储库中......这可能会变得很昂贵,尤其是在慢速磁盘上。

但是!你说。这不正是git log filename 所做的吗?答案是肯定的,是的。不同之处在于,当我运行git log filename 并且已知文件存在时,git-log 知道我想花时间获取此历史记录。

相反,如果我运行 git log foo 并且 foo 不是当前存在的修订版或文件,那么它需要花时间遍历整个图表,只是为了告诉我 foo 是模棱两可的。

哎哟。

所以欢迎你说git log -- filename 告诉 git 你真的希望它走图。否则会拒绝。

旁注: git 只是stat(2)s 你在命令行上给出的参数来确定文件是否存在。它不会查看索引,也不会打开您的 HEAD 树。当然,它可以做这些事情,这将允许您使用git log filename,其中filename 是删除而不是暂存提交。这似乎是一个非常合理的变化。

【讨论】:

这几乎就是我要问的问题:) 我理解在编写命令行工具时需要--。但是如果文件曾经被删除,并且没有标签存在,那么选择“文件解释”总是不错的选择。 @Liosan:啊,我想我误解了你的问题......!【参考方案3】:

从来没有那个名字的标签。

你为什么这么说? git tag path/to/file 工作正常。

它看起来真的很简单:git rm 只接受路径名; git log 采用 ref 名称和路径名称,首先是 refnames,任何可能是路径名的东西也可能是 ref 名称——这并不是定义什么可能是 ref 名称的规则。

在小型项目中,git log 很容易确定它是否有效,它必须是某个源文件的路径,但此时你必须做出判断调用来平衡这里所有可能的错误的可能性和成本。您是否更有可能要求不再存在的文件的日志,或者您对现有路径名或现有引用进行了粗略处理? git log remote/ref 很常见。我认为 git 只是假设一个与当前不匹配的名称很可能是一个错字。

【讨论】:

我想你误解了我的意思。我知道我可以创建一个标签,但没有那个名称的标签。它不存在。我知道它可能可以,但事实并非如此。如果我尝试做git show path/to/file --(不使用你展示的git tag),那么我会得到fatal: bad revision 'path/to/file'

以上是关于Git - 为啥在已删除文件上运行命令时需要双破折号?的主要内容,如果未能解决你的问题,请参考以下文章

运行 npm 命令时 -- 做啥?

git上为啥删除不掉文件夹

为啥 git stash 删除我的 node_modules 文件夹?

为啥在已删除指针上调用非虚拟成员函数是未定义的行为?

为啥我看不到 git 命令的输出?

git 两个破折号意味着没有更多选项