From 3a1f91cfd9633a4f59e0534fa5ba076e031a78ed Mon Sep 17 00:00:00 2001 From: Jeff King Date: Tue, 10 Nov 2020 16:40:19 -0500 Subject: rev-parse: handle --end-of-options We taught rev-list a new way to separate options from revisions in 19e8789b23 (revision: allow --end-of-options to end option parsing, 2019-08-06), but rev-parse uses its own parser. It should know about --end-of-options not only for consistency, but because it may be presented with similarly ambiguous cases. E.g., if a caller does: git rev-parse "$rev" -- "$path" to parse an untrusted input, then it will get confused if $rev contains an option-like string like "--local-env-vars". Or even "--not-real", which we'd keep as an option to pass along to rev-list. Or even more importantly: git rev-parse --verify "$rev" can be confused by options, even though its purpose is safely parsing untrusted input. On the plus side, it will always fail the --verify part, as it will not have parsed a revision, so the caller will generally "fail closed" rather than continue to use the untrusted string. But it will still trigger whatever option was in "$rev"; this should be mostly harmless, since rev-parse options are all read-only, but I didn't carefully audit all paths. This patch lets callers write: git rev-parse --end-of-options "$rev" -- "$path" and: git rev-parse --verify --end-of-options "$rev" which will both treat "$rev" always as a revision parameter. The latter is a bit clunky. It would be nicer if we had defined "--verify" to require that its next argument be the revision. But we have not historically done so, and: git rev-parse --verify -q "$rev" does currently work. I added a test here to confirm that we didn't break that. A few implementation notes: - We don't document --end-of-options explicitly in commands, but rather in gitcli(7). So I didn't give it its own section in git-rev-parse(1). But I did call it out specifically in the --verify section, and include it in the examples, which should show best practices. - We don't have to re-indent the main option-parsing block, because we can combine our "did we see end of options" check with "does it start with a dash". The exception is the pre-setup options, which need their own block. - We do however have to pull the "--" parsing out of the "does it start with dash" block, because we want to parse it even if we've seen --end-of-options. - We'll leave "--end-of-options" in the output. This is probably not technically necessary, as a careful caller will do: git rev-parse --end-of-options $revs -- $paths and anything in $revs will be resolved to an object id. However, it does help a slightly less careful caller like: git rev-parse --end-of-options $revs_or_paths where a path "--foo" will remain in the output as long as it also exists on disk. In that case, it's helpful to retain --end-of-options to get passed along to rev-list, s it would otherwise see just "--foo". Signed-off-by: Jeff King Signed-off-by: Junio C Hamano --- t/t1503-rev-parse-verify.sh | 13 +++++++++++++ t/t1506-rev-parse-diagnosis.sh | 16 ++++++++++++++++ 2 files changed, 29 insertions(+) (limited to 't') diff --git a/t/t1503-rev-parse-verify.sh b/t/t1503-rev-parse-verify.sh index 492edffa9c..dc9fe3cbf1 100755 --- a/t/t1503-rev-parse-verify.sh +++ b/t/t1503-rev-parse-verify.sh @@ -144,4 +144,17 @@ test_expect_success SYMLINKS 'ref resolution not confused by broken symlinks' ' test_must_fail git rev-parse --verify broken ' +test_expect_success 'options can appear after --verify' ' + git rev-parse --verify HEAD >expect && + git rev-parse --verify -q HEAD >actual && + test_cmp expect actual +' + +test_expect_success 'verify respects --end-of-options' ' + git update-ref refs/heads/-tricky HEAD && + git rev-parse --verify HEAD >expect && + git rev-parse --verify --end-of-options -tricky >actual && + test_cmp expect actual +' + test_done diff --git a/t/t1506-rev-parse-diagnosis.sh b/t/t1506-rev-parse-diagnosis.sh index 2ed5d50059..e2ae15a2cf 100755 --- a/t/t1506-rev-parse-diagnosis.sh +++ b/t/t1506-rev-parse-diagnosis.sh @@ -263,4 +263,20 @@ test_expect_success 'arg after dashdash not interpreted as option' ' test_cmp expect actual ' +test_expect_success 'arg after end-of-options not interpreted as option' ' + test_must_fail git rev-parse --end-of-options --not-real -- 2>err && + test_i18ngrep bad.revision.*--not-real err +' + +test_expect_success 'end-of-options still allows --' ' + cat >expect <<-EOF && + --end-of-options + $(git rev-parse --verify HEAD) + -- + path + EOF + git rev-parse --end-of-options HEAD -- path >actual && + test_cmp expect actual +' + test_done -- cgit v1.2.3