Git - 为什么在对已删除的文件运行命令时需要双破折号?

11

考虑一个 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的开发者当初的想法?

3个回答

11

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

假设你有一个名为 a/b/c 的文件夹,你可以使用以下命令获取该文件夹的提交记录:

git log a/b/c

没问题。

你还可以有一个名为d/e/f的分支。你可以使用以下命令获取这个分支的提交记录:

git log d/e/f

这也是可以的。

如果无法清楚确定git log所在的项目,事情就会变得复杂起来。 如果你愚蠢地将你的分支命名为a/b/c,那么git就不知道应该打印谁的日志:是分支a/b/c的日志还是你目录a/b/c的日志?因此,您必须多提供一些有关要接收的信息:

  • 显示分支a/b/c的日志:
    git log a/b/c --
  • 在当前分支中显示文件夹a/b/c的日志:
    git log -- a/b/c
  • a/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通常会变慢。


3
为什么文件存在和不存在时会产生不同?让我们看看各种情况:
  1. 修订版本和文件名都存在。显然是有歧义的。
  2. 修订版本存在而文件名不存在。显然是没有歧义的。
  3. 修订版本不存在而文件名存在。显然是没有歧义的。
  4. 修订版本和文件名都不存在。这是有歧义的吗?
确实是有歧义的。
要知道某个路径下是否曾经存在过文件,就意味着要遍历历史记录,打开每个提交,打开每个树并遍历树来查看是否存在此路径。在一个具有许多提交和深层路径的大型存储库上... 这可能会变得很昂贵,特别是在慢速磁盘上。
但是!你说的没错。git log filename 不就是做这件事吗?答案是肯定的。区别在于当我运行 git log filename 并且已知该文件存在时,git-log 就知道我想花时间获取此历史记录。
如果我运行 git log foo 而 foo 不是一个修订版本或当前存在的文件,则需要花费时间遍历整个图形,以告诉我 foo 是有歧义的。
糟糕了。
因此,您可以使用 git log -- filename 告诉 Git 您确实希望它遍历图形。否则,它将拒绝。
顺便说一下: Git 仅通过 stat(2) 命令检查您在命令行上输入的参数以确定文件是否存在。它不查看索引,也不打开 HEAD 树。当然,它可以进行这些操作,从而允许您在 filename 是未提交的删除时使用 git log filename。这似乎是一个非常合理的更改。

这几乎就是我要问的 :) 我理解在编写命令行工具时需要使用 --。但如果文件曾经被删除,且没有标签存在,则选择“文件解释”始终是一个好选择。 - Liosan
@Liosan:啊,我想我误解了你的问题...! - Edward Thomson

2

这个标签从来没有叫过这个名字。

为什么这么说?git tag path/to/file完全没问题。

它真的就像看起来那么简单:git rm只接收路径名;git log接收引用名和路径名,首先是引用名,而任何可能是路径名的东西也可以是引用名——这不是一个什么能当作引用名的规则,而是定义。

在小项目中,git log很容易决定如果它有效,则必须曾经是源文件的路径,但是到了这一步,您必须进行判断,权衡所有可能出错的成本。你是要请求不存在的文件的日志,还是输入了错误的路径或者引用名?git log remote/ref非常常见。我想git默认匹配不到任何当前的名称时最有可能是打错了。


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

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接