Skip to content

Commit 0dd171f

Browse files
committed
Merge branch 'jk/rev-parse-end-of-options'
"git rev-parse" learned the "--end-of-options" to help scripts to safely take a parameter that is supposed to be a revision, e.g. "git rev-parse --verify -q --end-of-options $rev". * jk/rev-parse-end-of-options: rev-parse: handle --end-of-options rev-parse: put all options under the "-" check rev-parse: don't accept options after dashdash
2 parents 473c622 + 3a1f91c commit 0dd171f

File tree

4 files changed

+99
-47
lines changed

4 files changed

+99
-47
lines changed

Documentation/git-rev-parse.txt

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -109,6 +109,10 @@ names an existing object that is a commit-ish (i.e. a commit, or an
109109
annotated tag that points at a commit). To make sure that `$VAR`
110110
names an existing object of any type, `git rev-parse "$VAR^{object}"`
111111
can be used.
112+
+
113+
Note that if you are verifying a name from an untrusted source, it is
114+
wise to use `--end-of-options` so that the name argument is not mistaken
115+
for another option.
112116

113117
-q::
114118
--quiet::
@@ -446,15 +450,15 @@ $ git rev-parse --verify HEAD
446450
* Print the commit object name from the revision in the $REV shell variable:
447451
+
448452
------------
449-
$ git rev-parse --verify $REV^{commit}
453+
$ git rev-parse --verify --end-of-options $REV^{commit}
450454
------------
451455
+
452456
This will error out if $REV is empty or not a valid revision.
453457

454458
* Similar to above:
455459
+
456460
------------
457-
$ git rev-parse --default master --verify $REV
461+
$ git rev-parse --default master --verify --end-of-options $REV
458462
------------
459463
+
460464
but if $REV is empty, the commit object name from master will be printed.

builtin/rev-parse.c

Lines changed: 55 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -595,6 +595,7 @@ int cmd_rev_parse(int argc, const char **argv, const char *prefix)
595595
struct object_context unused;
596596
struct strbuf buf = STRBUF_INIT;
597597
const int hexsz = the_hash_algo->hexsz;
598+
int seen_end_of_options = 0;
598599

599600
if (argc > 1 && !strcmp("--parseopt", argv[1]))
600601
return cmd_parseopt(argc - 1, argv + 1, prefix);
@@ -622,21 +623,29 @@ int cmd_rev_parse(int argc, const char **argv, const char *prefix)
622623
for (i = 1; i < argc; i++) {
623624
const char *arg = argv[i];
624625

625-
if (!strcmp(arg, "--local-env-vars")) {
626-
int i;
627-
for (i = 0; local_repo_env[i]; i++)
628-
printf("%s\n", local_repo_env[i]);
626+
if (as_is) {
627+
if (show_file(arg, output_prefix) && as_is < 2)
628+
verify_filename(prefix, arg, 0);
629629
continue;
630630
}
631-
if (!strcmp(arg, "--resolve-git-dir")) {
632-
const char *gitdir = argv[++i];
633-
if (!gitdir)
634-
die("--resolve-git-dir requires an argument");
635-
gitdir = resolve_gitdir(gitdir);
636-
if (!gitdir)
637-
die("not a gitdir '%s'", argv[i]);
638-
puts(gitdir);
639-
continue;
631+
632+
if (!seen_end_of_options) {
633+
if (!strcmp(arg, "--local-env-vars")) {
634+
int i;
635+
for (i = 0; local_repo_env[i]; i++)
636+
printf("%s\n", local_repo_env[i]);
637+
continue;
638+
}
639+
if (!strcmp(arg, "--resolve-git-dir")) {
640+
const char *gitdir = argv[++i];
641+
if (!gitdir)
642+
die("--resolve-git-dir requires an argument");
643+
gitdir = resolve_gitdir(gitdir);
644+
if (!gitdir)
645+
die("not a gitdir '%s'", argv[i]);
646+
puts(gitdir);
647+
continue;
648+
}
640649
}
641650

642651
/* The rest of the options require a git repository. */
@@ -646,41 +655,36 @@ int cmd_rev_parse(int argc, const char **argv, const char *prefix)
646655
did_repo_setup = 1;
647656
}
648657

649-
if (!strcmp(arg, "--git-path")) {
650-
if (!argv[i + 1])
651-
die("--git-path requires an argument");
652-
strbuf_reset(&buf);
653-
puts(relative_path(git_path("%s", argv[i + 1]),
654-
prefix, &buf));
655-
i++;
656-
continue;
657-
}
658-
if (as_is) {
659-
if (show_file(arg, output_prefix) && as_is < 2)
660-
verify_filename(prefix, arg, 0);
661-
continue;
662-
}
663-
if (!strcmp(arg,"-n")) {
664-
if (++i >= argc)
665-
die("-n requires an argument");
666-
if ((filter & DO_FLAGS) && (filter & DO_REVS)) {
667-
show(arg);
668-
show(argv[i]);
669-
}
670-
continue;
671-
}
672-
if (starts_with(arg, "-n")) {
673-
if ((filter & DO_FLAGS) && (filter & DO_REVS))
674-
show(arg);
658+
if (!strcmp(arg, "--")) {
659+
as_is = 2;
660+
/* Pass on the "--" if we show anything but files.. */
661+
if (filter & (DO_FLAGS | DO_REVS))
662+
show_file(arg, 0);
675663
continue;
676664
}
677665

678-
if (*arg == '-') {
679-
if (!strcmp(arg, "--")) {
680-
as_is = 2;
681-
/* Pass on the "--" if we show anything but files.. */
682-
if (filter & (DO_FLAGS | DO_REVS))
683-
show_file(arg, 0);
666+
if (!seen_end_of_options && *arg == '-') {
667+
if (!strcmp(arg, "--git-path")) {
668+
if (!argv[i + 1])
669+
die("--git-path requires an argument");
670+
strbuf_reset(&buf);
671+
puts(relative_path(git_path("%s", argv[i + 1]),
672+
prefix, &buf));
673+
i++;
674+
continue;
675+
}
676+
if (!strcmp(arg,"-n")) {
677+
if (++i >= argc)
678+
die("-n requires an argument");
679+
if ((filter & DO_FLAGS) && (filter & DO_REVS)) {
680+
show(arg);
681+
show(argv[i]);
682+
}
683+
continue;
684+
}
685+
if (starts_with(arg, "-n")) {
686+
if ((filter & DO_FLAGS) && (filter & DO_REVS))
687+
show(arg);
684688
continue;
685689
}
686690
if (!strcmp(arg, "--default")) {
@@ -937,6 +941,12 @@ int cmd_rev_parse(int argc, const char **argv, const char *prefix)
937941
puts(the_hash_algo->name);
938942
continue;
939943
}
944+
if (!strcmp(arg, "--end-of-options")) {
945+
seen_end_of_options = 1;
946+
if (filter & (DO_FLAGS | DO_REVS))
947+
show_file(arg, 0);
948+
continue;
949+
}
940950
if (show_flag(arg) && verify)
941951
die_no_single_rev(quiet);
942952
continue;

t/t1503-rev-parse-verify.sh

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -144,4 +144,17 @@ test_expect_success SYMLINKS 'ref resolution not confused by broken symlinks' '
144144
test_must_fail git rev-parse --verify broken
145145
'
146146

147+
test_expect_success 'options can appear after --verify' '
148+
git rev-parse --verify HEAD >expect &&
149+
git rev-parse --verify -q HEAD >actual &&
150+
test_cmp expect actual
151+
'
152+
153+
test_expect_success 'verify respects --end-of-options' '
154+
git update-ref refs/heads/-tricky HEAD &&
155+
git rev-parse --verify HEAD >expect &&
156+
git rev-parse --verify --end-of-options -tricky >actual &&
157+
test_cmp expect actual
158+
'
159+
147160
test_done

t/t1506-rev-parse-diagnosis.sh

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -254,4 +254,29 @@ test_expect_success 'escaped char does not trigger wildcard rule' '
254254
test_must_fail git rev-parse "foo\\*bar"
255255
'
256256

257+
test_expect_success 'arg after dashdash not interpreted as option' '
258+
cat >expect <<-\EOF &&
259+
--
260+
--local-env-vars
261+
EOF
262+
git rev-parse -- --local-env-vars >actual &&
263+
test_cmp expect actual
264+
'
265+
266+
test_expect_success 'arg after end-of-options not interpreted as option' '
267+
test_must_fail git rev-parse --end-of-options --not-real -- 2>err &&
268+
test_i18ngrep bad.revision.*--not-real err
269+
'
270+
271+
test_expect_success 'end-of-options still allows --' '
272+
cat >expect <<-EOF &&
273+
--end-of-options
274+
$(git rev-parse --verify HEAD)
275+
--
276+
path
277+
EOF
278+
git rev-parse --end-of-options HEAD -- path >actual &&
279+
test_cmp expect actual
280+
'
281+
257282
test_done

0 commit comments

Comments
 (0)