【问题标题】:In Git, what does `--` (dash dash) mean?在 Git 中,`--`(破折号)是什么意思?
【发布时间】:2014-03-30 21:21:13
【问题描述】:

在阅读 Git 命令的手册页时,您经常会看到可选的 --(破折号)。根据我的经验,-- 不是必需的,也没有什么区别。你什么时候需要它?鉴于它出现在这么多命令中,它一般意味着什么?

【问题讨论】:

  • 不是重复的。这个问题要求对所有 git 命令中的双破折号有一个概念性的理解。链接的问题仅询问git checkout

标签: git


【解决方案1】:

git 中的双破折号-- 对不同的命令有不同的含义,但通常它将选项与参数分开。

git 中,-- 的含义取决于您使用的子命令。它通常将子命令参数(如git checkout 中的分支名称)与修订或文件名分开。有时它是完全可选的,仅用于防止不寻常的文件名被解释为程序选项。

举例

  • git checkout。要查看“提交”(在手册中称为“tree-ish”,因为您实际上可以指定一系列对象类型),请使用

    git checkout <commit>

    要将检出细化为仅一两个文件,请使用-- 将“tree-ish”参数与您希望检出的“文件名”分开。

  • git commit。要提交“索引”中的任何内容(即,您通过git add 暂存的内容,只需发出git commit 命令。

    git commit [-m 消息]

    要忽略您通过git add 添加的任何内容并在特定文件中提交更改,请使用 git commit -- <filename>

  • git add。要提交名称以--- 开头的文件,您必须告诉 git add 停止读取参数,并开始读取文件名; -- 就是这样做的。

    git add -- -sample.txt

  • git log。要查看仅限于影响文件的提交的提交历史记录,请使用

    git log -- filename

如果你需要了解它的具体含义,你需要查看你使用的任何 git 命令的手册页。

【讨论】:

  • 有趣。我是从git-scm.com/docs/git-diff 来到这里的,在列出的所有示例中,缺少 git-diff :) 明白了,谢谢。
【解决方案2】:

这个问题要求对所有 git 命令中的双破折号有一个概念性的理解。

表示选项结束的双破折号已被认为对 Git 来说“不够”。

使用 Git 2.24(2019 年第三季度),命令行解析器学习了“--end-of-options”表示法:

脚本编写者首先在命令行上硬编码一组选项并强制命令将最终用户输入视为非选项的标准约定是使用“--”作为分隔符,但这不适用于使用“--”作为 revs 和 pathspec 之间的分隔符的命令。

参见Jeff King (peff)commit 67feca3commit 51b4594commit 19e8789(2019 年 8 月 6 日)。
(由 Junio C Hamano -- gitster -- 合并到 commit 4a12f89,2019 年 9 月 9 日)

revision: 允许--end-of-options 结束选项解析

目前没有可靠的方法告诉 Git 特定选项是修订版,而不是选项。
所以如果你有一个分支“refs/heads/--foo”,你不能只说:

git rev-list --foo

你可以说:

git rev-list refs/heads/--foo

但是,如果您不知道引用名称,特别是如果您是一个脚本,它会从其他地方传递一个值。
在大多数程序中,您可以使用“--”来结束选项解析,如下所示:

some-prog -- "$revision"

但这不适用于修订解析器,因为“--”在那里已经很有意义:它将修订与路径规范分开。
所以我们需要一些其他的标记来区分选项和修订。

此补丁引入了“--end-of-options”,用于此目的:

git rev-list --oneline --end-of-options "$revision"

无论“$revision”中有什么内容都可以工作(好吧,如果你说“--”它可能会失败,但它不会做一些危险的事情,比如触发一个意外的选项)。

这个名字很冗长,但这可能是件好事;这适用于可读性比简洁更重要的脚本调用。

另一种选择是引入一个显式选项来标记修订,例如:

git rev-list --oneline --revision="$revision"

这比这次提交稍微更多提供信息(因为它甚至可以使诸如“--”之类的愚蠢内容变得明确)。但是使用像“--”这样的分隔符的模式在 git 和其他命令中已经很成熟了,它使一些脚本任务更简单,比如:

git rev-list --end-of-options "$@"

parse-options: 允许--end-of-options 作为“--”的同义词

修订选项解析器最近了解到--end-of-options,但这对所有调用者来说还不够。
其中一些,例如git-log,使用parse_options() 选择一些选项,然后将剩余部分提供给setup_revisions()
对于这些情况,我们需要阻止parse_options() 在看到--end-of-options 时找到更多选项,并在argv 中保留该选项,以便setup_revisions() 也可以看到它。

让我们像处理“--”一样处理这个问题。我们甚至可以搭载 处理PARSE_OPT_KEEP_DASHDASH,因为任何想要 保留一个会想保留另一个。

例子:

git update-ref refs/heads/--source HEAD &&\
git log --end-of-options --source

在 Git 2.30(2021 年第一季度)中,“git rev-parse(man) 学习了“--end-of-options”来帮助脚本安全地获取应该是修订版的参数,例如“git rev-parse --verify -q --end-of-options $rev(man)”。

参见Jeff King (peff)commit 3a1f91ccommit 9033addcommit e05e2ae(2020 年 11 月 10 日)。
(由 Junio C Hamano -- gitster -- 合并于 commit 0dd171f,2020 年 11 月 21 日)

rev-parse:处理--end-of-options

签字人:杰夫·金

我们在19e8789b23(“revision:allow --end-of-options to end option parsing”,2019-08-06,Git v2.24.0 中教授了 rev-list 一种将选项与修订区分开的新方法-rc0 -- mergebatch #2 中列出),但rev-parse 使用自己的解析器。
它应该知道--end-of-options 不仅是为了保持一致性,而且因为它可能会出现类似的模棱两可的情况。例如,如果调用者这样做:

git rev-parse "$rev" -- "$path"  

解析不受信任的输入,如果$rev 包含类似选项的字符串,如“--local-env-vars”,它会感到困惑。
甚至是“--not-real”,我们会保留它作为传递给 rev-list 的选项。

或者更重要的是:

git rev-parse --verify "$rev"  

可能会被选项混淆,即使它的目的是安全地解析不受信任的输入。
从好的方面来说,它总是会失败 --verify 部分,因为它不会解析修订,所以调用者通常会“失败关闭”而不是继续使用不受信任的字符串。
但它仍然会触发“$rev”中的任何选项;这应该是无害的,因为 rev-parse 选项都是只读的,但我没有仔细审核所有路径。

这个补丁可以让调用者写:

git rev-parse --end-of-options "$rev" -- "$path"  

和:

git rev-parse --verify --end-of-options "$rev"  

它们都将“$rev”始终视为修订参数。
后者有点笨拙。如果我们将“--verify”定义为要求其下一个参数是修订版会更好。
但我们从来没有这样做过,并且:

git rev-parse --verify -q "$rev"  

目前有效。我在这里添加了一个测试来确认我们没有破坏它。

一些实现说明:

  • 我们不必重新缩进主选项解析块,因为我们可以将“我们是否看到选项结尾”检查与“是否以破折号开头”结合起来。一个例外是预设置选项,它们需要自己的块。

  • 但是,我们必须将“--”解析从“是否以破折号开头”块中提取出来,因为即使我们已经看到了--end-of-options,我们也想解析它。

  • 我们将在输出中保留“--end-of-options”。这在技术上可能不是必需的,因为细心的调用者会这样做:

    git rev-parse --end-of-options $revs -- $paths

$revs 中的任何内容都将解析为对象 ID。
但是,它确实可以帮助稍微不那么小心的调用者,例如:

git rev-parse --end-of-options $revs_or_paths  

只要路径“--foo”也存在于磁盘上,它就会保留在输出中。
在这种情况下,保留 --end-of-options 以传递给 rev-list 会很有帮助,否则它只会看到“--foo”。

git rev-parse 现在包含在其man page 中:

请注意,如果您正在验证来自不受信任来源的名称,则它是 明智地使用--end-of-options,这样名称参数就不会被弄错 换一个选择。

$ git rev-parse --verify --end-of-options $REV^{commit}
$ git rev-parse --default master --verify --end-of-options $REV

在 Git 2.31(2021 年第一季度)中,“git mktag(man) 在编写标签对象之前使用自己的规则验证其输入——它已更新为共享逻辑与git fsck"。

也就是说它也支持--end-of-options

commit 06ce791(2021 年 1 月 6 日)、commit 2aa9425commit 3f390a3commit 9a1a3a4commit acfc013commit 1f3299fcommit acf9de4commit 40ef015commit dfe3948、@986 987654354@,commit 30f882ccommit ca9a1edcommit 47c95e7commit 3b9e4ddcommit 5c2303ecommit 317c176commit 0d35ccbcommit b5ca549commit b5ca549commit aba5377commit aba5377,@9876254(1) 987654365@、commit f59b61d(2020 年 12 月 23 日)Ævar Arnfjörð Bjarmason (avar)
(由 Junio C Hamano -- gitster -- 合并到 commit c7d6d41,2021 年 1 月 25 日)

mktag: 转换为解析选项

签字人:Ævar Arnfjörð Bjarmason

将“mktag”命令转换为使用parse-options.h,而不是其自己的临时 argc 处理。
这在实践中并不重要,因为它不支持任何选项,但在我们的代码库中删除了另一个特殊情况,并使其在将来更容易添加选项。

它确实略微改善了希望以一致方式执行 git 命令的程序的情况,例如始终使用--end-of-options
例如
"gitaly" 就是这样做的,并且有一个不支持 --end-of-options 的内置黑名单。
这是它和其他类似程序支持的一种不太特殊的情况。

【讨论】:

  • +1:感谢这份关于 Git 选项解析的远比我们认为的我们需要了解的概要。对我来说,这一切归结为:“最好不要将--foo 用作分支、标签或文件名”。但是很高兴能够编写能够安全地处理此类 repos 的强大脚本。
猜你喜欢
  • 2019-02-14
  • 2015-02-11
  • 1970-01-01
  • 1970-01-01
  • 2012-03-25
  • 1970-01-01
  • 2013-04-23
  • 1970-01-01
相关资源
最近更新 更多