parseopt: values of pathname type can be prefixed with :(optional)

In the previous step, we introduced an optional filename that can be
given to a configuration variable, and nullify the fact that such a
configuration setting even existed if the named path is missing or
empty.

Let's do the same for command line options that name a pathname.

Signed-off-by: Junio C Hamano <gitster@pobox.com>
Signed-off-by: Taylor Blau <me@ttaylorr.com>
Signed-off-by: D. Ben Knoble <ben.knoble+github@gmail.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
Junio C Hamano 2025-09-28 17:29:16 -04:00
parent f744457cca
commit ce61a885c9
3 changed files with 44 additions and 11 deletions

View File

@ -216,6 +216,20 @@ $ git describe --abbrev=10 HEAD # correct
$ git describe --abbrev 10 HEAD # NOT WHAT YOU MEANT $ git describe --abbrev 10 HEAD # NOT WHAT YOU MEANT
---------------------------- ----------------------------



Magic filename options
~~~~~~~~~~~~~~~~~~~~~~
Options that take a filename allow a prefix `:(optional)`. For example:

----------------------------
git commit -F :(optional)COMMIT_EDITMSG
# if COMMIT_EDITMSG does not exist, equivalent to
git commit
----------------------------

Like with configuration values, if the named file is missing Git behaves as if
the option was not given at all. See "Values" in linkgit:git-config[1].

NOTES ON FREQUENTLY CONFUSED OPTIONS NOTES ON FREQUENTLY CONFUSED OPTIONS
------------------------------------ ------------------------------------



View File

@ -133,7 +133,6 @@ static enum parse_opt_result do_get_value(struct parse_opt_ctx_t *p,
{ {
const char *arg; const char *arg;
const int unset = flags & OPT_UNSET; const int unset = flags & OPT_UNSET;
int err;


if (unset && p->opt) if (unset && p->opt)
return error(_("%s takes no value"), optname(opt, flags)); return error(_("%s takes no value"), optname(opt, flags));
@ -209,21 +208,31 @@ static enum parse_opt_result do_get_value(struct parse_opt_ctx_t *p,
case OPTION_FILENAME: case OPTION_FILENAME:
{ {
const char *value; const char *value;

int is_optional;
FREE_AND_NULL(*(char **)opt->value);

err = 0;


if (unset) if (unset)
value = NULL; value = NULL;
else if (opt->flags & PARSE_OPT_OPTARG && !p->opt) else if (opt->flags & PARSE_OPT_OPTARG && !p->opt)
value = (const char *) opt->defval; value = (char *)opt->defval;
else else {
err = get_arg(p, opt, flags, &value); int err = get_arg(p, opt, flags, &value);
if (err)
return err;
}
if (!value)
return 0;


if (!err) is_optional = skip_prefix(value, ":(optional)", &value);
*(char **)opt->value = fix_filename(p->prefix, value); if (!value)
return err; is_optional = 0;
value = fix_filename(p->prefix, value);
if (is_optional && is_empty_or_missing_file(value)) {
free((char *)value);
} else {
FREE_AND_NULL(*(char **)opt->value);
*(const char **)opt->value = value;
}
return 0;
} }
case OPTION_CALLBACK: case OPTION_CALLBACK:
{ {

View File

@ -37,6 +37,16 @@ test_expect_success 'nonexistent template file should return error' '
) )
' '


test_expect_success 'nonexistent optional template file on command line' '
echo changes >> foo &&
git add foo &&
(
GIT_EDITOR="echo hello >\"\$1\"" &&
export GIT_EDITOR &&
git commit --template ":(optional)$PWD/notexist"
)
'

test_expect_success 'nonexistent template file in config should return error' ' test_expect_success 'nonexistent template file in config should return error' '
test_config commit.template "$PWD"/notexist && test_config commit.template "$PWD"/notexist &&
( (