Merge branch 'jc/optional-path'
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 independentmain
commit
44dee53a30
|
|
@ -357,7 +357,9 @@ compiled without runtime prefix support, the compiled-in prefix will be
|
||||||
substituted instead. In the unlikely event that a literal path needs to
|
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
|
be specified that should _not_ be expanded, it needs to be prefixed by
|
||||||
`./`, like so: `./%(prefix)/bin`.
|
`./`, 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
|
Variables
|
||||||
~~~~~~~~~
|
~~~~~~~~~
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
------------------------------------
|
------------------------------------
|
||||||
|
|
||||||
|
|
|
||||||
16
config.c
16
config.c
|
|
@ -1278,11 +1278,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 git_config_pathname(char **dest, const char *var, const char *value)
|
||||||
{
|
{
|
||||||
|
int is_optional;
|
||||||
|
char *path;
|
||||||
|
|
||||||
if (!value)
|
if (!value)
|
||||||
return config_error_nonbool(var);
|
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);
|
die(_("failed to expand user dir in: '%s'"), value);
|
||||||
|
|
||||||
|
if (is_optional && is_missing_file(path)) {
|
||||||
|
free(path);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
*dest = path;
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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,22 +208,32 @@ 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)
|
||||||
if (!err)
|
|
||||||
*(char **)opt->value = fix_filename(p->prefix, value);
|
|
||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
|
if (!value)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
is_optional = skip_prefix(value, ":(optional)", &value);
|
||||||
|
if (!value)
|
||||||
|
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:
|
||||||
{
|
{
|
||||||
const char *p_arg = NULL;
|
const char *p_arg = NULL;
|
||||||
|
|
|
||||||
|
|
@ -31,52 +31,70 @@ test_expect_success 'nonexistent template file should return error' '
|
||||||
echo changes >> foo &&
|
echo changes >> foo &&
|
||||||
git add foo &&
|
git add foo &&
|
||||||
(
|
(
|
||||||
GIT_EDITOR="echo hello >\"\$1\"" &&
|
GIT_EDITOR="echo hello >" &&
|
||||||
export GIT_EDITOR &&
|
export GIT_EDITOR &&
|
||||||
test_must_fail git commit --template "$PWD"/notexist
|
test_must_fail git commit --template "$PWD"/notexist
|
||||||
)
|
)
|
||||||
'
|
'
|
||||||
|
|
||||||
|
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 &&
|
||||||
(
|
(
|
||||||
GIT_EDITOR="echo hello >\"\$1\"" &&
|
GIT_EDITOR="echo hello >" &&
|
||||||
export GIT_EDITOR &&
|
export GIT_EDITOR &&
|
||||||
test_must_fail git commit
|
test_must_fail git commit --allow-empty
|
||||||
)
|
)
|
||||||
'
|
'
|
||||||
|
|
||||||
|
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.
|
# From now on we'll use a template file that exists.
|
||||||
TEMPLATE="$PWD"/template
|
TEMPLATE="$PWD"/template
|
||||||
|
|
||||||
test_expect_success 'unedited template should not commit' '
|
test_expect_success 'unedited template should not commit' '
|
||||||
echo "template line" > "$TEMPLATE" &&
|
echo "template line" >"$TEMPLATE" &&
|
||||||
test_must_fail git commit --template "$TEMPLATE"
|
test_must_fail git commit --allow-empty --template "$TEMPLATE"
|
||||||
'
|
'
|
||||||
|
|
||||||
test_expect_success 'unedited template with comments should not commit' '
|
test_expect_success 'unedited template with comments should not commit' '
|
||||||
echo "# comment in template" >> "$TEMPLATE" &&
|
echo "# comment in template" >>"$TEMPLATE" &&
|
||||||
test_must_fail git commit --template "$TEMPLATE"
|
test_must_fail git commit --allow-empty --template "$TEMPLATE"
|
||||||
'
|
'
|
||||||
|
|
||||||
test_expect_success 'a Signed-off-by line by itself should not commit' '
|
test_expect_success 'a Signed-off-by line by itself should not commit' '
|
||||||
(
|
(
|
||||||
test_set_editor "$TEST_DIRECTORY"/t7500/add-signed-off &&
|
test_set_editor "$TEST_DIRECTORY"/t7500/add-signed-off &&
|
||||||
test_must_fail git commit --template "$TEMPLATE"
|
test_must_fail git commit --allow-empty --template "$TEMPLATE"
|
||||||
)
|
)
|
||||||
'
|
'
|
||||||
|
|
||||||
test_expect_success 'adding comments to a template should not commit' '
|
test_expect_success 'adding comments to a template should not commit' '
|
||||||
(
|
(
|
||||||
test_set_editor "$TEST_DIRECTORY"/t7500/add-comments &&
|
test_set_editor "$TEST_DIRECTORY"/t7500/add-comments &&
|
||||||
test_must_fail git commit --template "$TEMPLATE"
|
test_must_fail git commit --allow-empty --template "$TEMPLATE"
|
||||||
)
|
)
|
||||||
'
|
'
|
||||||
|
|
||||||
test_expect_success 'adding real content to a template should commit' '
|
test_expect_success 'adding real content to a template should commit' '
|
||||||
(
|
(
|
||||||
test_set_editor "$TEST_DIRECTORY"/t7500/add-content &&
|
test_set_editor "$TEST_DIRECTORY"/t7500/add-content &&
|
||||||
git commit --template "$TEMPLATE"
|
git commit --allow-empty --template "$TEMPLATE"
|
||||||
) &&
|
) &&
|
||||||
commit_msg_is "template linecommit message"
|
commit_msg_is "template linecommit message"
|
||||||
'
|
'
|
||||||
|
|
|
||||||
13
wrapper.c
13
wrapper.c
|
|
@ -721,6 +721,19 @@ int xgethostname(char *buf, size_t len)
|
||||||
return ret;
|
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)
|
int is_empty_or_missing_file(const char *filename)
|
||||||
{
|
{
|
||||||
struct stat st;
|
struct stat st;
|
||||||
|
|
|
||||||
|
|
@ -66,7 +66,9 @@ void write_file_buf(const char *path, const char *buf, size_t len);
|
||||||
__attribute__((format (printf, 2, 3)))
|
__attribute__((format (printf, 2, 3)))
|
||||||
void write_file(const char *path, const char *fmt, ...);
|
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);
|
int is_empty_or_missing_file(const char *filename);
|
||||||
|
|
||||||
enum fsync_action {
|
enum fsync_action {
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue