Git fork

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>

+44 -11
+14
Documentation/gitcli.adoc
··· 216 216 $ git describe --abbrev 10 HEAD # NOT WHAT YOU MEANT 217 217 ---------------------------- 218 218 219 + 220 + Magic filename options 221 + ~~~~~~~~~~~~~~~~~~~~~~ 222 + Options that take a filename allow a prefix `:(optional)`. For example: 223 + 224 + ---------------------------- 225 + git commit -F :(optional)COMMIT_EDITMSG 226 + # if COMMIT_EDITMSG does not exist, equivalent to 227 + git commit 228 + ---------------------------- 229 + 230 + Like with configuration values, if the named file is missing Git behaves as if 231 + the option was not given at all. See "Values" in linkgit:git-config[1]. 232 + 219 233 NOTES ON FREQUENTLY CONFUSED OPTIONS 220 234 ------------------------------------ 221 235
+20 -11
parse-options.c
··· 133 133 { 134 134 const char *arg; 135 135 const int unset = flags & OPT_UNSET; 136 - int err; 137 136 138 137 if (unset && p->opt) 139 138 return error(_("%s takes no value"), optname(opt, flags)); ··· 209 208 case OPTION_FILENAME: 210 209 { 211 210 const char *value; 212 - 213 - FREE_AND_NULL(*(char **)opt->value); 214 - 215 - err = 0; 211 + int is_optional; 216 212 217 213 if (unset) 218 214 value = NULL; 219 215 else if (opt->flags & PARSE_OPT_OPTARG && !p->opt) 220 - value = (const char *) opt->defval; 221 - else 222 - err = get_arg(p, opt, flags, &value); 216 + value = (char *)opt->defval; 217 + else { 218 + int err = get_arg(p, opt, flags, &value); 219 + if (err) 220 + return err; 221 + } 222 + if (!value) 223 + return 0; 223 224 224 - if (!err) 225 - *(char **)opt->value = fix_filename(p->prefix, value); 226 - return err; 225 + is_optional = skip_prefix(value, ":(optional)", &value); 226 + if (!value) 227 + is_optional = 0; 228 + value = fix_filename(p->prefix, value); 229 + if (is_optional && is_empty_or_missing_file(value)) { 230 + free((char *)value); 231 + } else { 232 + FREE_AND_NULL(*(char **)opt->value); 233 + *(const char **)opt->value = value; 234 + } 235 + return 0; 227 236 } 228 237 case OPTION_CALLBACK: 229 238 {
+10
t/t7500-commit-template-squash-signoff.sh
··· 37 37 ) 38 38 ' 39 39 40 + test_expect_success 'nonexistent optional template file on command line' ' 41 + echo changes >> foo && 42 + git add foo && 43 + ( 44 + GIT_EDITOR="echo hello >\"\$1\"" && 45 + export GIT_EDITOR && 46 + git commit --template ":(optional)$PWD/notexist" 47 + ) 48 + ' 49 + 40 50 test_expect_success 'nonexistent template file in config should return error' ' 41 51 test_config commit.template "$PWD"/notexist && 42 52 (