revision: allow --end-of-options to end option parsing

There's currently no robust way to tell Git that a particular option is
meant to be a revision, and not an option. So if you have a branch
"refs/heads/--foo", you cannot just say:

  git rev-list --foo

You can say:

  git rev-list refs/heads/--foo

But that breaks down if you don't know the refname, and in particular if
you're a script passing along a value from elsewhere. In most programs,
you can use "--" to end option parsing, like this:

  some-prog -- "$revision"

But that doesn't work for the revision parser, because "--" is already
meaningful there: it separates revisions from pathspecs. So we need some
other marker to separate options from revisions.

This patch introduces "--end-of-options", which serves that purpose:

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

will work regardless of what's in "$revision" (well, if you say "--" it
may fail, but it won't do something dangerous, like triggering an
unexpected option).

The name is verbose, but that's probably a good thing; this is meant to
be used for scripted invocations where readability is more important
than terseness.

One alternative would be to introduce an explicit option to mark a
revision, like:

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

That's slightly _more_ informative than this commit (because it makes
even something silly like "--" unambiguous). But the pattern of using a
separator like "--" is well established in git and in other commands,
and it makes some scripting tasks simpler like:

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

There's no documentation in this patch, because it will make sense to
describe the feature once it is available everywhere (and support will
be added in further patches).

Signed-off-by: Jeff King <peff@peff.net>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
This commit is contained in:
Jeff King 2019-08-06 10:39:58 -04:00 committed by Junio C Hamano
parent 7c20df84bd
commit 19e8789b23
2 changed files with 15 additions and 1 deletions

View File

@ -2523,6 +2523,7 @@ int setup_revisions(int argc, const char **argv, struct rev_info *revs, struct s
int i, flags, left, seen_dashdash, got_rev_arg = 0, revarg_opt;
struct argv_array prune_data = ARGV_ARRAY_INIT;
const char *submodule = NULL;
int seen_end_of_options = 0;
if (opt)
submodule = opt->submodule;
@ -2552,7 +2553,7 @@ int setup_revisions(int argc, const char **argv, struct rev_info *revs, struct s
revarg_opt |= REVARG_CANNOT_BE_FILENAME;
for (left = i = 1; i < argc; i++) {
const char *arg = argv[i];
if (*arg == '-') {
if (!seen_end_of_options && *arg == '-') {
int opts;
opts = handle_revision_pseudo_opt(submodule,
@ -2574,6 +2575,11 @@ int setup_revisions(int argc, const char **argv, struct rev_info *revs, struct s
continue;
}
if (!strcmp(arg, "--end-of-options")) {
seen_end_of_options = 1;
continue;
}
opts = handle_revision_opt(revs, argc - i, argv + i,
&left, argv, opt);
if (opts > 0) {

View File

@ -140,4 +140,12 @@ test_expect_success '--header shows a NUL after each commit' '
test_cmp expect actual
'
test_expect_success 'rev-list --end-of-options' '
git update-ref refs/heads/--output=yikes HEAD &&
git rev-list --end-of-options --output=yikes >actual &&
test_path_is_missing yikes &&
git rev-list HEAD >expect &&
test_cmp expect actual
'
test_done