config: add --comment option to add a comment

Introduce the ability to append comments to modifications
made using git-config. Example usage:

  git config --comment "changed via script" \
    --add safe.directory /home/alice/repo.git

based on the proposed patch, the output produced is:

  [safe]
    directory = /home/alice/repo.git #changed via script

Users need to be able to distinguish between config entries made
using automation and entries made by a human. Automation can add
comments containing a URL pointing to explanations for the change
made, avoiding questions from users as to why their config file
was changed by a third party.

The implementation ensures that a # character is unconditionally
prepended to the provided comment string, and that the comment
text is appended as a suffix to the changed key-value-pair in the
same line of text. Multi-line comments (i.e. comments containing
linefeed) are rejected as errors, causing Git to exit without
making changes.

Comments are aimed at humans who inspect or change their Git
config using a pager or editor. Comments are not meant to be
read or displayed by git-config at a later time.

Signed-off-by: Ralph Seichter <github@seichter.de>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
This commit is contained in:
Ralph Seichter
2024-03-12 21:47:00 +00:00
committed by Junio C Hamano
parent 4f9b731bde
commit 42d5c03394
12 changed files with 78 additions and 45 deletions

View File

@ -9,9 +9,9 @@ git-config - Get and set repository or global options
SYNOPSIS SYNOPSIS
-------- --------
[verse] [verse]
'git config' [<file-option>] [--type=<type>] [--fixed-value] [--show-origin] [--show-scope] [-z|--null] <name> [<value> [<value-pattern>]] 'git config' [<file-option>] [--type=<type>] [--comment=<value>] [--fixed-value] [--show-origin] [--show-scope] [-z|--null] <name> [<value> [<value-pattern>]]
'git config' [<file-option>] [--type=<type>] --add <name> <value> 'git config' [<file-option>] [--type=<type>] [--comment=<value>] --add <name> <value>
'git config' [<file-option>] [--type=<type>] [--fixed-value] --replace-all <name> <value> [<value-pattern>] 'git config' [<file-option>] [--type=<type>] [--comment=<value>] [--fixed-value] --replace-all <name> <value> [<value-pattern>]
'git config' [<file-option>] [--type=<type>] [--show-origin] [--show-scope] [-z|--null] [--fixed-value] --get <name> [<value-pattern>] 'git config' [<file-option>] [--type=<type>] [--show-origin] [--show-scope] [-z|--null] [--fixed-value] --get <name> [<value-pattern>]
'git config' [<file-option>] [--type=<type>] [--show-origin] [--show-scope] [-z|--null] [--fixed-value] --get-all <name> [<value-pattern>] 'git config' [<file-option>] [--type=<type>] [--show-origin] [--show-scope] [-z|--null] [--fixed-value] --get-all <name> [<value-pattern>]
'git config' [<file-option>] [--type=<type>] [--show-origin] [--show-scope] [-z|--null] [--fixed-value] [--name-only] --get-regexp <name-regex> [<value-pattern>] 'git config' [<file-option>] [--type=<type>] [--show-origin] [--show-scope] [-z|--null] [--fixed-value] [--name-only] --get-regexp <name-regex> [<value-pattern>]
@ -87,6 +87,11 @@ OPTIONS
values. This is the same as providing '^$' as the `value-pattern` values. This is the same as providing '^$' as the `value-pattern`
in `--replace-all`. in `--replace-all`.
--comment <value>::
Append a comment to new or modified lines. A '#' character will be
unconditionally prepended to the value. The value must not contain
linefeed characters (no multi-line comments are permitted).
--get:: --get::
Get the value for a given key (optionally filtered by a regex Get the value for a given key (optionally filtered by a regex
matching the value). Returns error code 1 if the key was not matching the value). Returns error code 1 if the key was not

View File

@ -44,6 +44,7 @@ static struct config_options config_options;
static int show_origin; static int show_origin;
static int show_scope; static int show_scope;
static int fixed_value; static int fixed_value;
static const char *comment;
#define ACTION_GET (1<<0) #define ACTION_GET (1<<0)
#define ACTION_GET_ALL (1<<1) #define ACTION_GET_ALL (1<<1)
@ -173,6 +174,7 @@ static struct option builtin_config_options[] = {
OPT_BOOL(0, "show-origin", &show_origin, N_("show origin of config (file, standard input, blob, command line)")), OPT_BOOL(0, "show-origin", &show_origin, N_("show origin of config (file, standard input, blob, command line)")),
OPT_BOOL(0, "show-scope", &show_scope, N_("show scope of config (worktree, local, global, system, command)")), OPT_BOOL(0, "show-scope", &show_scope, N_("show scope of config (worktree, local, global, system, command)")),
OPT_STRING(0, "default", &default_value, N_("value"), N_("with --get, use default value when missing entry")), OPT_STRING(0, "default", &default_value, N_("value"), N_("with --get, use default value when missing entry")),
OPT_STRING(0, "comment", &comment, N_("value"), N_("human-readable comment string (# will be prepended automatically)")),
OPT_END(), OPT_END(),
}; };
@ -797,6 +799,12 @@ int cmd_config(int argc, const char **argv, const char *prefix)
usage_builtin_config(); usage_builtin_config();
} }
if (comment &&
!(actions & (ACTION_ADD|ACTION_SET|ACTION_SET_ALL|ACTION_REPLACE_ALL))) {
error(_("--comment is only applicable to add/set/replace operations"));
usage_builtin_config();
}
/* check usage of --fixed-value */ /* check usage of --fixed-value */
if (fixed_value) { if (fixed_value) {
int allowed_usage = 0; int allowed_usage = 0;
@ -880,7 +888,7 @@ int cmd_config(int argc, const char **argv, const char *prefix)
check_write(); check_write();
check_argc(argc, 2, 2); check_argc(argc, 2, 2);
value = normalize_value(argv[0], argv[1], &default_kvi); value = normalize_value(argv[0], argv[1], &default_kvi);
ret = git_config_set_in_file_gently(given_config_source.file, argv[0], value); ret = git_config_set_in_file_gently(given_config_source.file, argv[0], comment, value);
if (ret == CONFIG_NOTHING_SET) if (ret == CONFIG_NOTHING_SET)
error(_("cannot overwrite multiple values with a single value\n" error(_("cannot overwrite multiple values with a single value\n"
" Use a regexp, --add or --replace-all to change %s."), argv[0]); " Use a regexp, --add or --replace-all to change %s."), argv[0]);
@ -891,7 +899,7 @@ int cmd_config(int argc, const char **argv, const char *prefix)
value = normalize_value(argv[0], argv[1], &default_kvi); value = normalize_value(argv[0], argv[1], &default_kvi);
ret = git_config_set_multivar_in_file_gently(given_config_source.file, ret = git_config_set_multivar_in_file_gently(given_config_source.file,
argv[0], value, argv[2], argv[0], value, argv[2],
flags); comment, flags);
} }
else if (actions == ACTION_ADD) { else if (actions == ACTION_ADD) {
check_write(); check_write();
@ -900,7 +908,7 @@ int cmd_config(int argc, const char **argv, const char *prefix)
ret = git_config_set_multivar_in_file_gently(given_config_source.file, ret = git_config_set_multivar_in_file_gently(given_config_source.file,
argv[0], value, argv[0], value,
CONFIG_REGEX_NONE, CONFIG_REGEX_NONE,
flags); comment, flags);
} }
else if (actions == ACTION_REPLACE_ALL) { else if (actions == ACTION_REPLACE_ALL) {
check_write(); check_write();
@ -908,7 +916,7 @@ int cmd_config(int argc, const char **argv, const char *prefix)
value = normalize_value(argv[0], argv[1], &default_kvi); value = normalize_value(argv[0], argv[1], &default_kvi);
ret = git_config_set_multivar_in_file_gently(given_config_source.file, ret = git_config_set_multivar_in_file_gently(given_config_source.file,
argv[0], value, argv[2], argv[0], value, argv[2],
flags | CONFIG_FLAGS_MULTI_REPLACE); comment, flags | CONFIG_FLAGS_MULTI_REPLACE);
} }
else if (actions == ACTION_GET) { else if (actions == ACTION_GET) {
check_argc(argc, 1, 2); check_argc(argc, 1, 2);
@ -936,17 +944,17 @@ int cmd_config(int argc, const char **argv, const char *prefix)
if (argc == 2) if (argc == 2)
return git_config_set_multivar_in_file_gently(given_config_source.file, return git_config_set_multivar_in_file_gently(given_config_source.file,
argv[0], NULL, argv[1], argv[0], NULL, argv[1],
flags); NULL, flags);
else else
return git_config_set_in_file_gently(given_config_source.file, return git_config_set_in_file_gently(given_config_source.file,
argv[0], NULL); argv[0], NULL, NULL);
} }
else if (actions == ACTION_UNSET_ALL) { else if (actions == ACTION_UNSET_ALL) {
check_write(); check_write();
check_argc(argc, 1, 2); check_argc(argc, 1, 2);
return git_config_set_multivar_in_file_gently(given_config_source.file, return git_config_set_multivar_in_file_gently(given_config_source.file,
argv[0], NULL, argv[1], argv[0], NULL, argv[1],
flags | CONFIG_FLAGS_MULTI_REPLACE); NULL, flags | CONFIG_FLAGS_MULTI_REPLACE);
} }
else if (actions == ACTION_RENAME_SECTION) { else if (actions == ACTION_RENAME_SECTION) {
check_write(); check_write();

View File

@ -1553,7 +1553,7 @@ static int maintenance_register(int argc, const char **argv, const char *prefix)
die(_("$HOME not set")); die(_("$HOME not set"));
rc = git_config_set_multivar_in_file_gently( rc = git_config_set_multivar_in_file_gently(
config_file, "maintenance.repo", maintpath, config_file, "maintenance.repo", maintpath,
CONFIG_REGEX_NONE, 0); CONFIG_REGEX_NONE, NULL, 0);
free(global_config_file); free(global_config_file);
if (rc) if (rc)
@ -1620,7 +1620,7 @@ static int maintenance_unregister(int argc, const char **argv, const char *prefi
if (!config_file) if (!config_file)
die(_("$HOME not set")); die(_("$HOME not set"));
rc = git_config_set_multivar_in_file_gently( rc = git_config_set_multivar_in_file_gently(
config_file, key, NULL, maintpath, config_file, key, NULL, maintpath, NULL,
CONFIG_FLAGS_MULTI_REPLACE | CONFIG_FLAGS_FIXED_VALUE); CONFIG_FLAGS_MULTI_REPLACE | CONFIG_FLAGS_FIXED_VALUE);
free(global_config_file); free(global_config_file);

View File

@ -1283,7 +1283,7 @@ static void sync_submodule(const char *path, const char *prefix,
submodule_to_gitdir(&sb, path); submodule_to_gitdir(&sb, path);
strbuf_addstr(&sb, "/config"); strbuf_addstr(&sb, "/config");
if (git_config_set_in_file_gently(sb.buf, remote_key, sub_origin_url)) if (git_config_set_in_file_gently(sb.buf, remote_key, NULL, sub_origin_url))
die(_("failed to update remote for submodule '%s'"), die(_("failed to update remote for submodule '%s'"),
path); path);

View File

@ -365,12 +365,12 @@ static void copy_filtered_worktree_config(const char *worktree_git_dir)
if (!git_configset_get_bool(&cs, "core.bare", &bare) && if (!git_configset_get_bool(&cs, "core.bare", &bare) &&
bare && bare &&
git_config_set_multivar_in_file_gently( git_config_set_multivar_in_file_gently(
to_file, "core.bare", NULL, "true", 0)) to_file, "core.bare", NULL, "true", NULL, 0))
error(_("failed to unset '%s' in '%s'"), error(_("failed to unset '%s' in '%s'"),
"core.bare", to_file); "core.bare", to_file);
if (!git_configset_get(&cs, "core.worktree") && if (!git_configset_get(&cs, "core.worktree") &&
git_config_set_in_file_gently(to_file, git_config_set_in_file_gently(to_file,
"core.worktree", NULL)) "core.worktree", NULL, NULL))
error(_("failed to unset '%s' in '%s'"), error(_("failed to unset '%s' in '%s'"),
"core.worktree", to_file); "core.worktree", to_file);

View File

@ -3001,6 +3001,7 @@ static ssize_t write_section(int fd, const char *key,
} }
static ssize_t write_pair(int fd, const char *key, const char *value, static ssize_t write_pair(int fd, const char *key, const char *value,
const char *comment,
const struct config_store_data *store) const struct config_store_data *store)
{ {
int i; int i;
@ -3041,7 +3042,14 @@ static ssize_t write_pair(int fd, const char *key, const char *value,
strbuf_addch(&sb, value[i]); strbuf_addch(&sb, value[i]);
break; break;
} }
strbuf_addf(&sb, "%s\n", quote);
if (comment) {
if (strchr(comment, '\n'))
die(_("multi-line comments are not permitted: '%s'"), comment);
else
strbuf_addf(&sb, "%s #%s\n", quote, comment);
} else
strbuf_addf(&sb, "%s\n", quote);
ret = write_in_full(fd, sb.buf, sb.len); ret = write_in_full(fd, sb.buf, sb.len);
strbuf_release(&sb); strbuf_release(&sb);
@ -3130,9 +3138,9 @@ static void maybe_remove_section(struct config_store_data *store,
} }
int git_config_set_in_file_gently(const char *config_filename, int git_config_set_in_file_gently(const char *config_filename,
const char *key, const char *value) const char *key, const char *comment, const char *value)
{ {
return git_config_set_multivar_in_file_gently(config_filename, key, value, NULL, 0); return git_config_set_multivar_in_file_gently(config_filename, key, value, NULL, comment, 0);
} }
void git_config_set_in_file(const char *config_filename, void git_config_set_in_file(const char *config_filename,
@ -3153,7 +3161,7 @@ int repo_config_set_worktree_gently(struct repository *r,
if (r->repository_format_worktree_config) { if (r->repository_format_worktree_config) {
char *file = repo_git_path(r, "config.worktree"); char *file = repo_git_path(r, "config.worktree");
int ret = git_config_set_multivar_in_file_gently( int ret = git_config_set_multivar_in_file_gently(
file, key, value, NULL, 0); file, key, value, NULL, NULL, 0);
free(file); free(file);
return ret; return ret;
} }
@ -3195,6 +3203,7 @@ void git_config_set(const char *key, const char *value)
int git_config_set_multivar_in_file_gently(const char *config_filename, int git_config_set_multivar_in_file_gently(const char *config_filename,
const char *key, const char *value, const char *key, const char *value,
const char *value_pattern, const char *value_pattern,
const char *comment,
unsigned flags) unsigned flags)
{ {
int fd = -1, in_fd = -1; int fd = -1, in_fd = -1;
@ -3245,7 +3254,7 @@ int git_config_set_multivar_in_file_gently(const char *config_filename,
free(store.key); free(store.key);
store.key = xstrdup(key); store.key = xstrdup(key);
if (write_section(fd, key, &store) < 0 || if (write_section(fd, key, &store) < 0 ||
write_pair(fd, key, value, &store) < 0) write_pair(fd, key, value, comment, &store) < 0)
goto write_err_out; goto write_err_out;
} else { } else {
struct stat st; struct stat st;
@ -3399,7 +3408,7 @@ int git_config_set_multivar_in_file_gently(const char *config_filename,
if (write_section(fd, key, &store) < 0) if (write_section(fd, key, &store) < 0)
goto write_err_out; goto write_err_out;
} }
if (write_pair(fd, key, value, &store) < 0) if (write_pair(fd, key, value, comment, &store) < 0)
goto write_err_out; goto write_err_out;
} }
@ -3444,7 +3453,7 @@ void git_config_set_multivar_in_file(const char *config_filename,
const char *value_pattern, unsigned flags) const char *value_pattern, unsigned flags)
{ {
if (!git_config_set_multivar_in_file_gently(config_filename, key, value, if (!git_config_set_multivar_in_file_gently(config_filename, key, value,
value_pattern, flags)) value_pattern, NULL, flags))
return; return;
if (value) if (value)
die(_("could not set '%s' to '%s'"), key, value); die(_("could not set '%s' to '%s'"), key, value);
@ -3467,7 +3476,7 @@ int repo_config_set_multivar_gently(struct repository *r, const char *key,
int res = git_config_set_multivar_in_file_gently(file, int res = git_config_set_multivar_in_file_gently(file,
key, value, key, value,
value_pattern, value_pattern,
flags); NULL, flags);
free(file); free(file);
return res; return res;
} }

View File

@ -290,7 +290,7 @@ int git_config_pathname(const char **, const char *, const char *);
int git_config_expiry_date(timestamp_t *, const char *, const char *); int git_config_expiry_date(timestamp_t *, const char *, const char *);
int git_config_color(char *, const char *, const char *); int git_config_color(char *, const char *, const char *);
int git_config_set_in_file_gently(const char *, const char *, const char *); int git_config_set_in_file_gently(const char *, const char *, const char *, const char *);
/** /**
* write config values to a specific config file, takes a key/value pair as * write config values to a specific config file, takes a key/value pair as
@ -336,7 +336,7 @@ int git_config_parse_key(const char *, char **, size_t *);
int git_config_set_multivar_gently(const char *, const char *, const char *, unsigned); int git_config_set_multivar_gently(const char *, const char *, const char *, unsigned);
void git_config_set_multivar(const char *, const char *, const char *, unsigned); void git_config_set_multivar(const char *, const char *, const char *, unsigned);
int repo_config_set_multivar_gently(struct repository *, const char *, const char *, const char *, unsigned); int repo_config_set_multivar_gently(struct repository *, const char *, const char *, const char *, unsigned);
int git_config_set_multivar_in_file_gently(const char *, const char *, const char *, const char *, unsigned); int git_config_set_multivar_in_file_gently(const char *, const char *, const char *, const char *, const char *, unsigned);
/** /**
* takes four parameters: * takes four parameters:

View File

@ -3462,54 +3462,54 @@ static int save_opts(struct replay_opts *opts)
if (opts->no_commit) if (opts->no_commit)
res |= git_config_set_in_file_gently(opts_file, res |= git_config_set_in_file_gently(opts_file,
"options.no-commit", "true"); "options.no-commit", NULL, "true");
if (opts->edit >= 0) if (opts->edit >= 0)
res |= git_config_set_in_file_gently(opts_file, "options.edit", res |= git_config_set_in_file_gently(opts_file, "options.edit", NULL,
opts->edit ? "true" : "false"); opts->edit ? "true" : "false");
if (opts->allow_empty) if (opts->allow_empty)
res |= git_config_set_in_file_gently(opts_file, res |= git_config_set_in_file_gently(opts_file,
"options.allow-empty", "true"); "options.allow-empty", NULL, "true");
if (opts->allow_empty_message) if (opts->allow_empty_message)
res |= git_config_set_in_file_gently(opts_file, res |= git_config_set_in_file_gently(opts_file,
"options.allow-empty-message", "true"); "options.allow-empty-message", NULL, "true");
if (opts->keep_redundant_commits) if (opts->keep_redundant_commits)
res |= git_config_set_in_file_gently(opts_file, res |= git_config_set_in_file_gently(opts_file,
"options.keep-redundant-commits", "true"); "options.keep-redundant-commits", NULL, "true");
if (opts->signoff) if (opts->signoff)
res |= git_config_set_in_file_gently(opts_file, res |= git_config_set_in_file_gently(opts_file,
"options.signoff", "true"); "options.signoff", NULL, "true");
if (opts->record_origin) if (opts->record_origin)
res |= git_config_set_in_file_gently(opts_file, res |= git_config_set_in_file_gently(opts_file,
"options.record-origin", "true"); "options.record-origin", NULL, "true");
if (opts->allow_ff) if (opts->allow_ff)
res |= git_config_set_in_file_gently(opts_file, res |= git_config_set_in_file_gently(opts_file,
"options.allow-ff", "true"); "options.allow-ff", NULL, "true");
if (opts->mainline) { if (opts->mainline) {
struct strbuf buf = STRBUF_INIT; struct strbuf buf = STRBUF_INIT;
strbuf_addf(&buf, "%d", opts->mainline); strbuf_addf(&buf, "%d", opts->mainline);
res |= git_config_set_in_file_gently(opts_file, res |= git_config_set_in_file_gently(opts_file,
"options.mainline", buf.buf); "options.mainline", NULL, buf.buf);
strbuf_release(&buf); strbuf_release(&buf);
} }
if (opts->strategy) if (opts->strategy)
res |= git_config_set_in_file_gently(opts_file, res |= git_config_set_in_file_gently(opts_file,
"options.strategy", opts->strategy); "options.strategy", NULL, opts->strategy);
if (opts->gpg_sign) if (opts->gpg_sign)
res |= git_config_set_in_file_gently(opts_file, res |= git_config_set_in_file_gently(opts_file,
"options.gpg-sign", opts->gpg_sign); "options.gpg-sign", NULL, opts->gpg_sign);
for (size_t i = 0; i < opts->xopts.nr; i++) for (size_t i = 0; i < opts->xopts.nr; i++)
res |= git_config_set_multivar_in_file_gently(opts_file, res |= git_config_set_multivar_in_file_gently(opts_file,
"options.strategy-option", "options.strategy-option",
opts->xopts.v[i], "^$", 0); opts->xopts.v[i], "^$", NULL, 0);
if (opts->allow_rerere_auto) if (opts->allow_rerere_auto)
res |= git_config_set_in_file_gently(opts_file, res |= git_config_set_in_file_gently(opts_file,
"options.allow-rerere-auto", "options.allow-rerere-auto", NULL,
opts->allow_rerere_auto == RERERE_AUTOUPDATE ? opts->allow_rerere_auto == RERERE_AUTOUPDATE ?
"true" : "false"); "true" : "false");
if (opts->explicit_cleanup) if (opts->explicit_cleanup)
res |= git_config_set_in_file_gently(opts_file, res |= git_config_set_in_file_gently(opts_file,
"options.default-msg-cleanup", "options.default-msg-cleanup", NULL,
describe_cleanup_mode(opts->default_msg_cleanup)); describe_cleanup_mode(opts->default_msg_cleanup));
return res; return res;
} }

View File

@ -978,7 +978,7 @@ int config_set_in_gitmodules_file_gently(const char *key, const char *value)
{ {
int ret; int ret;
ret = git_config_set_in_file_gently(GITMODULES_FILE, key, value); ret = git_config_set_in_file_gently(GITMODULES_FILE, key, NULL, value);
if (ret < 0) if (ret < 0)
/* Maybe the user already did that, don't error out here */ /* Maybe the user already did that, don't error out here */
warning(_("Could not update .gitmodules entry %s"), key); warning(_("Could not update .gitmodules entry %s"), key);

View File

@ -2046,7 +2046,7 @@ void submodule_unset_core_worktree(const struct submodule *sub)
submodule_name_to_gitdir(&config_path, the_repository, sub->name); submodule_name_to_gitdir(&config_path, the_repository, sub->name);
strbuf_addstr(&config_path, "/config"); strbuf_addstr(&config_path, "/config");
if (git_config_set_in_file_gently(config_path.buf, "core.worktree", NULL)) if (git_config_set_in_file_gently(config_path.buf, "core.worktree", NULL, NULL))
warning(_("Could not unset core.worktree setting in submodule '%s'"), warning(_("Could not unset core.worktree setting in submodule '%s'"),
sub->path); sub->path);

View File

@ -69,13 +69,24 @@ test_expect_success 'replace with non-match (actually matching)' '
cat > expect << EOF cat > expect << EOF
[section] [section]
penguin = very blue
Movie = BadPhysics Movie = BadPhysics
UPPERCASE = true UPPERCASE = true
penguin = kingpin penguin = gentoo #Pygoscelis papua
disposition = peckish #find fish
foo = bar ## abc
[Sections] [Sections]
WhatEver = Second WhatEver = Second
EOF EOF
test_expect_success 'append comments' '
git config --replace-all --comment="Pygoscelis papua" section.penguin gentoo &&
git config --comment="find fish" section.disposition peckish &&
git config --comment="# abc" section.foo bar &&
test_cmp expect .git/config
'
test_expect_success 'Prohibited LF in comment' '
test_must_fail git config --comment="a${LF}b" section.k v
'
test_expect_success 'non-match result' 'test_cmp expect .git/config' test_expect_success 'non-match result' 'test_cmp expect .git/config'

View File

@ -807,9 +807,9 @@ int should_prune_worktree(const char *id, struct strbuf *reason, char **wtpath,
static int move_config_setting(const char *key, const char *value, static int move_config_setting(const char *key, const char *value,
const char *from_file, const char *to_file) const char *from_file, const char *to_file)
{ {
if (git_config_set_in_file_gently(to_file, key, value)) if (git_config_set_in_file_gently(to_file, key, NULL, value))
return error(_("unable to set %s in '%s'"), key, to_file); return error(_("unable to set %s in '%s'"), key, to_file);
if (git_config_set_in_file_gently(from_file, key, NULL)) if (git_config_set_in_file_gently(from_file, key, NULL, NULL))
return error(_("unable to unset %s in '%s'"), key, from_file); return error(_("unable to unset %s in '%s'"), key, from_file);
return 0; return 0;
} }