Skip to content

Commit a1e8af9

Browse files
committed
Merge branch 'jc/optional-path' into next
Configuration variables that take a pathname as a value (e.g. blame.ignorerevsfile) can be marked as optional by prefixing ":(optoinal)" before its value. * jc/optional-path: parseopt: values of pathname type can be prefixed with :(optional) config: values of pathname type can be prefixed with :(optional) t7500: fix GIT_EDITOR shell snippet t7500: make each piece more independent
2 parents 7bda692 + ccfcaf3 commit a1e8af9

File tree

7 files changed

+95
-25
lines changed

7 files changed

+95
-25
lines changed

Documentation/config.adoc

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -357,7 +357,9 @@ compiled without runtime prefix support, the compiled-in prefix will be
357357
substituted instead. In the unlikely event that a literal path needs to
358358
be specified that should _not_ be expanded, it needs to be prefixed by
359359
`./`, like so: `./%(prefix)/bin`.
360-
360+
+
361+
If prefixed with `:(optional)`, the configuration variable is treated
362+
as if it does not exist, if the named path does not exist.
361363

362364
Variables
363365
~~~~~~~~~

Documentation/gitcli.adoc

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -216,6 +216,20 @@ $ git describe --abbrev=10 HEAD # correct
216216
$ git describe --abbrev 10 HEAD # NOT WHAT YOU MEANT
217217
----------------------------
218218
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+
219233
NOTES ON FREQUENTLY CONFUSED OPTIONS
220234
------------------------------------
221235

config.c

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1278,11 +1278,23 @@ int git_config_string(char **dest, const char *var, const char *value)
12781278

12791279
int git_config_pathname(char **dest, const char *var, const char *value)
12801280
{
1281+
int is_optional;
1282+
char *path;
1283+
12811284
if (!value)
12821285
return config_error_nonbool(var);
1283-
*dest = interpolate_path(value, 0);
1284-
if (!*dest)
1286+
1287+
is_optional = skip_prefix(value, ":(optional)", &value);
1288+
path = interpolate_path(value, 0);
1289+
if (!path)
12851290
die(_("failed to expand user dir in: '%s'"), value);
1291+
1292+
if (is_optional && is_missing_file(path)) {
1293+
free(path);
1294+
return 0;
1295+
}
1296+
1297+
*dest = path;
12861298
return 0;
12871299
}
12881300

parse-options.c

Lines changed: 20 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -133,7 +133,6 @@ static enum parse_opt_result do_get_value(struct parse_opt_ctx_t *p,
133133
{
134134
const char *arg;
135135
const int unset = flags & OPT_UNSET;
136-
int err;
137136

138137
if (unset && p->opt)
139138
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,
209208
case OPTION_FILENAME:
210209
{
211210
const char *value;
212-
213-
FREE_AND_NULL(*(char **)opt->value);
214-
215-
err = 0;
211+
int is_optional;
216212

217213
if (unset)
218214
value = NULL;
219215
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;
223224

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;
227236
}
228237
case OPTION_CALLBACK:
229238
{

t/t7500-commit-template-squash-signoff.sh

Lines changed: 28 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -31,52 +31,70 @@ test_expect_success 'nonexistent template file should return error' '
3131
echo changes >> foo &&
3232
git add foo &&
3333
(
34-
GIT_EDITOR="echo hello >\"\$1\"" &&
34+
GIT_EDITOR="echo hello >" &&
3535
export GIT_EDITOR &&
3636
test_must_fail git commit --template "$PWD"/notexist
3737
)
3838
'
3939

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+
4050
test_expect_success 'nonexistent template file in config should return error' '
4151
test_config commit.template "$PWD"/notexist &&
4252
(
43-
GIT_EDITOR="echo hello >\"\$1\"" &&
53+
GIT_EDITOR="echo hello >" &&
4454
export GIT_EDITOR &&
45-
test_must_fail git commit
55+
test_must_fail git commit --allow-empty
4656
)
4757
'
4858

59+
test_expect_success 'nonexistent optional template file in config' '
60+
test_config commit.template ":(optional)$PWD"/notexist &&
61+
GIT_EDITOR="echo hello >" git commit --allow-empty &&
62+
git cat-file commit HEAD | sed -e "1,/^$/d" >actual &&
63+
echo hello >expect &&
64+
test_cmp expect actual
65+
'
66+
4967
# From now on we'll use a template file that exists.
5068
TEMPLATE="$PWD"/template
5169

5270
test_expect_success 'unedited template should not commit' '
53-
echo "template line" > "$TEMPLATE" &&
54-
test_must_fail git commit --template "$TEMPLATE"
71+
echo "template line" >"$TEMPLATE" &&
72+
test_must_fail git commit --allow-empty --template "$TEMPLATE"
5573
'
5674

5775
test_expect_success 'unedited template with comments should not commit' '
58-
echo "# comment in template" >> "$TEMPLATE" &&
59-
test_must_fail git commit --template "$TEMPLATE"
76+
echo "# comment in template" >>"$TEMPLATE" &&
77+
test_must_fail git commit --allow-empty --template "$TEMPLATE"
6078
'
6179

6280
test_expect_success 'a Signed-off-by line by itself should not commit' '
6381
(
6482
test_set_editor "$TEST_DIRECTORY"/t7500/add-signed-off &&
65-
test_must_fail git commit --template "$TEMPLATE"
83+
test_must_fail git commit --allow-empty --template "$TEMPLATE"
6684
)
6785
'
6886

6987
test_expect_success 'adding comments to a template should not commit' '
7088
(
7189
test_set_editor "$TEST_DIRECTORY"/t7500/add-comments &&
72-
test_must_fail git commit --template "$TEMPLATE"
90+
test_must_fail git commit --allow-empty --template "$TEMPLATE"
7391
)
7492
'
7593

7694
test_expect_success 'adding real content to a template should commit' '
7795
(
7896
test_set_editor "$TEST_DIRECTORY"/t7500/add-content &&
79-
git commit --template "$TEMPLATE"
97+
git commit --allow-empty --template "$TEMPLATE"
8098
) &&
8199
commit_msg_is "template linecommit message"
82100
'

wrapper.c

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -721,6 +721,19 @@ int xgethostname(char *buf, size_t len)
721721
return ret;
722722
}
723723

724+
int is_missing_file(const char *filename)
725+
{
726+
struct stat st;
727+
728+
if (stat(filename, &st) < 0) {
729+
if (errno == ENOENT)
730+
return 1;
731+
die_errno(_("could not stat %s"), filename);
732+
}
733+
734+
return 0;
735+
}
736+
724737
int is_empty_or_missing_file(const char *filename)
725738
{
726739
struct stat st;

wrapper.h

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -66,7 +66,9 @@ void write_file_buf(const char *path, const char *buf, size_t len);
6666
__attribute__((format (printf, 2, 3)))
6767
void write_file(const char *path, const char *fmt, ...);
6868

69-
/* Return 1 if the file is empty or does not exists, 0 otherwise. */
69+
/* Return 1 if the file does not exist, 0 otherwise. */
70+
int is_missing_file(const char *filename);
71+
/* Return 1 if the file is empty or does not exist, 0 otherwise. */
7072
int is_empty_or_missing_file(const char *filename);
7173

7274
enum fsync_action {

0 commit comments

Comments
 (0)