config: values of pathname type can be prefixed with :(optional)
Sometimes people want to specify additional configuration data
as "best effort" basis. Maybe commit.template configuration file points
at somewhere in ~/template/ but on a particular system, the file may not
exist and the user may be OK without using the template in such a case.
When the value given to a configuration variable whose type is
pathname wants to signal such an optional file, it can be marked by
prepending ":(optional)" in front of it. Such a setting that is
marked optional would avoid getting the command barf for a missing
file, as an optional configuration setting that names a missing
file is not even seen.
cf. <xmqq5ywehb69.fsf@gitster.g>
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 committedSep 28, 2025 at 17:29 UTC749d6d166d8e3ea0ae32ede25f9aa23aa3b5e42b
5 files changed+41-4
Documentation/config.adoc
+3-1
index cc769251be..7301ced836 100644--- a/Documentation/config.adoc+++ b/Documentation/config.adoc@@ -358,7 +358,9 @@ compiled without runtime prefix support, the compiled-in prefix will be substituted instead. In the unlikely event that a literal path needs to be specified that should _not_ be expanded, it needs to be prefixed by `./`, like so: `./%(prefix)/bin`.-+++If prefixed with `:(optional)`, the configuration variable is treated+as if it does not exist, if the named path does not exist. Variables ~~~~~~~~~
config.c
+14-2
index 97ffef4270..73fc74c8fa 100644--- a/config.c+++ b/config.c@@ -1279,11 +1279,23 @@ int git_config_string(char **dest, const char *var, const char *value) int git_config_pathname(char **dest, const char *var, const char *value) {+ int is_optional;+ char *path;+ if (!value) return config_error_nonbool(var);- *dest = interpolate_path(value, 0);- if (!*dest)++ is_optional = skip_prefix(value, ":(optional)", &value);+ path = interpolate_path(value, 0);+ if (!path) die(_("failed to expand user dir in: '%s'"), value);++ if (is_optional && is_missing_file(path)) {+ free(path);+ return 0;+ }++ *dest = path; return 0; }
t/t7500-commit-template-squash-signoff.sh
+8
index 4922543256..a85229e556 100755--- a/t/t7500-commit-template-squash-signoff.sh+++ b/t/t7500-commit-template-squash-signoff.sh@@ -46,6 +46,14 @@ test_expect_success 'nonexistent template file in config should return error' ' ) '+test_expect_success 'nonexistent optional template file in config' '+ test_config commit.template ":(optional)$PWD"/notexist &&+ GIT_EDITOR="echo hello >" git commit --allow-empty &&+ git cat-file commit HEAD | sed -e "1,/^$/d" >actual &&+ echo hello >expect &&+ test_cmp expect actual+'+ # From now on we'll use a template file that exists. TEMPLATE="$PWD"/template
wrapper.c
+13
index 2f00d2ac87..3d507d4204 100644--- a/wrapper.c+++ b/wrapper.c@@ -721,6 +721,19 @@ int xgethostname(char *buf, size_t len) return ret; }+int is_missing_file(const char *filename)+{+ struct stat st;++ if (stat(filename, &st) < 0) {+ if (errno == ENOENT)+ return 1;+ die_errno(_("could not stat %s"), filename);+ }++ return 0;+}+ int is_empty_or_missing_file(const char *filename) { struct stat st;
wrapper.h
+3-1
index 7df824e34a..44a8597ac3 100644--- a/wrapper.h+++ b/wrapper.h@@ -66,7 +66,9 @@ void write_file_buf(const char *path, const char *buf, size_t len); __attribute__((format (printf, 2, 3))) void write_file(const char *path, const char *fmt, ...);-/* Return 1 if the file is empty or does not exists, 0 otherwise. */+/* Return 1 if the file does not exist, 0 otherwise. */+int is_missing_file(const char *filename);+/* Return 1 if the file is empty or does not exist, 0 otherwise. */ int is_empty_or_missing_file(const char *filename); enum fsync_action {