Skip to content

Commit 42d5c03

Browse files
rseichtergitster
authored andcommitted
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 <[email protected]> Signed-off-by: Junio C Hamano <[email protected]>
1 parent 4f9b731 commit 42d5c03

File tree

12 files changed

+78
-45
lines changed

12 files changed

+78
-45
lines changed

Documentation/git-config.txt

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -9,9 +9,9 @@ git-config - Get and set repository or global options
99
SYNOPSIS
1010
--------
1111
[verse]
12-
'git config' [<file-option>] [--type=<type>] [--fixed-value] [--show-origin] [--show-scope] [-z|--null] <name> [<value> [<value-pattern>]]
13-
'git config' [<file-option>] [--type=<type>] --add <name> <value>
14-
'git config' [<file-option>] [--type=<type>] [--fixed-value] --replace-all <name> <value> [<value-pattern>]
12+
'git config' [<file-option>] [--type=<type>] [--comment=<value>] [--fixed-value] [--show-origin] [--show-scope] [-z|--null] <name> [<value> [<value-pattern>]]
13+
'git config' [<file-option>] [--type=<type>] [--comment=<value>] --add <name> <value>
14+
'git config' [<file-option>] [--type=<type>] [--comment=<value>] [--fixed-value] --replace-all <name> <value> [<value-pattern>]
1515
'git config' [<file-option>] [--type=<type>] [--show-origin] [--show-scope] [-z|--null] [--fixed-value] --get <name> [<value-pattern>]
1616
'git config' [<file-option>] [--type=<type>] [--show-origin] [--show-scope] [-z|--null] [--fixed-value] --get-all <name> [<value-pattern>]
1717
'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
8787
values. This is the same as providing '^$' as the `value-pattern`
8888
in `--replace-all`.
8989

90+
--comment <value>::
91+
Append a comment to new or modified lines. A '#' character will be
92+
unconditionally prepended to the value. The value must not contain
93+
linefeed characters (no multi-line comments are permitted).
94+
9095
--get::
9196
Get the value for a given key (optionally filtered by a regex
9297
matching the value). Returns error code 1 if the key was not

builtin/config.c

Lines changed: 15 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@ static struct config_options config_options;
4444
static int show_origin;
4545
static int show_scope;
4646
static int fixed_value;
47+
static const char *comment;
4748

4849
#define ACTION_GET (1<<0)
4950
#define ACTION_GET_ALL (1<<1)
@@ -173,6 +174,7 @@ static struct option builtin_config_options[] = {
173174
OPT_BOOL(0, "show-origin", &show_origin, N_("show origin of config (file, standard input, blob, command line)")),
174175
OPT_BOOL(0, "show-scope", &show_scope, N_("show scope of config (worktree, local, global, system, command)")),
175176
OPT_STRING(0, "default", &default_value, N_("value"), N_("with --get, use default value when missing entry")),
177+
OPT_STRING(0, "comment", &comment, N_("value"), N_("human-readable comment string (# will be prepended automatically)")),
176178
OPT_END(),
177179
};
178180

@@ -797,6 +799,12 @@ int cmd_config(int argc, const char **argv, const char *prefix)
797799
usage_builtin_config();
798800
}
799801

802+
if (comment &&
803+
!(actions & (ACTION_ADD|ACTION_SET|ACTION_SET_ALL|ACTION_REPLACE_ALL))) {
804+
error(_("--comment is only applicable to add/set/replace operations"));
805+
usage_builtin_config();
806+
}
807+
800808
/* check usage of --fixed-value */
801809
if (fixed_value) {
802810
int allowed_usage = 0;
@@ -880,7 +888,7 @@ int cmd_config(int argc, const char **argv, const char *prefix)
880888
check_write();
881889
check_argc(argc, 2, 2);
882890
value = normalize_value(argv[0], argv[1], &default_kvi);
883-
ret = git_config_set_in_file_gently(given_config_source.file, argv[0], value);
891+
ret = git_config_set_in_file_gently(given_config_source.file, argv[0], comment, value);
884892
if (ret == CONFIG_NOTHING_SET)
885893
error(_("cannot overwrite multiple values with a single value\n"
886894
" 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)
891899
value = normalize_value(argv[0], argv[1], &default_kvi);
892900
ret = git_config_set_multivar_in_file_gently(given_config_source.file,
893901
argv[0], value, argv[2],
894-
flags);
902+
comment, flags);
895903
}
896904
else if (actions == ACTION_ADD) {
897905
check_write();
@@ -900,15 +908,15 @@ int cmd_config(int argc, const char **argv, const char *prefix)
900908
ret = git_config_set_multivar_in_file_gently(given_config_source.file,
901909
argv[0], value,
902910
CONFIG_REGEX_NONE,
903-
flags);
911+
comment, flags);
904912
}
905913
else if (actions == ACTION_REPLACE_ALL) {
906914
check_write();
907915
check_argc(argc, 2, 3);
908916
value = normalize_value(argv[0], argv[1], &default_kvi);
909917
ret = git_config_set_multivar_in_file_gently(given_config_source.file,
910918
argv[0], value, argv[2],
911-
flags | CONFIG_FLAGS_MULTI_REPLACE);
919+
comment, flags | CONFIG_FLAGS_MULTI_REPLACE);
912920
}
913921
else if (actions == ACTION_GET) {
914922
check_argc(argc, 1, 2);
@@ -936,17 +944,17 @@ int cmd_config(int argc, const char **argv, const char *prefix)
936944
if (argc == 2)
937945
return git_config_set_multivar_in_file_gently(given_config_source.file,
938946
argv[0], NULL, argv[1],
939-
flags);
947+
NULL, flags);
940948
else
941949
return git_config_set_in_file_gently(given_config_source.file,
942-
argv[0], NULL);
950+
argv[0], NULL, NULL);
943951
}
944952
else if (actions == ACTION_UNSET_ALL) {
945953
check_write();
946954
check_argc(argc, 1, 2);
947955
return git_config_set_multivar_in_file_gently(given_config_source.file,
948956
argv[0], NULL, argv[1],
949-
flags | CONFIG_FLAGS_MULTI_REPLACE);
957+
NULL, flags | CONFIG_FLAGS_MULTI_REPLACE);
950958
}
951959
else if (actions == ACTION_RENAME_SECTION) {
952960
check_write();

builtin/gc.c

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1553,7 +1553,7 @@ static int maintenance_register(int argc, const char **argv, const char *prefix)
15531553
die(_("$HOME not set"));
15541554
rc = git_config_set_multivar_in_file_gently(
15551555
config_file, "maintenance.repo", maintpath,
1556-
CONFIG_REGEX_NONE, 0);
1556+
CONFIG_REGEX_NONE, NULL, 0);
15571557
free(global_config_file);
15581558

15591559
if (rc)
@@ -1620,7 +1620,7 @@ static int maintenance_unregister(int argc, const char **argv, const char *prefi
16201620
if (!config_file)
16211621
die(_("$HOME not set"));
16221622
rc = git_config_set_multivar_in_file_gently(
1623-
config_file, key, NULL, maintpath,
1623+
config_file, key, NULL, maintpath, NULL,
16241624
CONFIG_FLAGS_MULTI_REPLACE | CONFIG_FLAGS_FIXED_VALUE);
16251625
free(global_config_file);
16261626

builtin/submodule--helper.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1283,7 +1283,7 @@ static void sync_submodule(const char *path, const char *prefix,
12831283
submodule_to_gitdir(&sb, path);
12841284
strbuf_addstr(&sb, "/config");
12851285

1286-
if (git_config_set_in_file_gently(sb.buf, remote_key, sub_origin_url))
1286+
if (git_config_set_in_file_gently(sb.buf, remote_key, NULL, sub_origin_url))
12871287
die(_("failed to update remote for submodule '%s'"),
12881288
path);
12891289

builtin/worktree.c

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -365,12 +365,12 @@ static void copy_filtered_worktree_config(const char *worktree_git_dir)
365365
if (!git_configset_get_bool(&cs, "core.bare", &bare) &&
366366
bare &&
367367
git_config_set_multivar_in_file_gently(
368-
to_file, "core.bare", NULL, "true", 0))
368+
to_file, "core.bare", NULL, "true", NULL, 0))
369369
error(_("failed to unset '%s' in '%s'"),
370370
"core.bare", to_file);
371371
if (!git_configset_get(&cs, "core.worktree") &&
372372
git_config_set_in_file_gently(to_file,
373-
"core.worktree", NULL))
373+
"core.worktree", NULL, NULL))
374374
error(_("failed to unset '%s' in '%s'"),
375375
"core.worktree", to_file);
376376

config.c

Lines changed: 17 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -3001,6 +3001,7 @@ static ssize_t write_section(int fd, const char *key,
30013001
}
30023002

30033003
static ssize_t write_pair(int fd, const char *key, const char *value,
3004+
const char *comment,
30043005
const struct config_store_data *store)
30053006
{
30063007
int i;
@@ -3041,7 +3042,14 @@ static ssize_t write_pair(int fd, const char *key, const char *value,
30413042
strbuf_addch(&sb, value[i]);
30423043
break;
30433044
}
3044-
strbuf_addf(&sb, "%s\n", quote);
3045+
3046+
if (comment) {
3047+
if (strchr(comment, '\n'))
3048+
die(_("multi-line comments are not permitted: '%s'"), comment);
3049+
else
3050+
strbuf_addf(&sb, "%s #%s\n", quote, comment);
3051+
} else
3052+
strbuf_addf(&sb, "%s\n", quote);
30453053

30463054
ret = write_in_full(fd, sb.buf, sb.len);
30473055
strbuf_release(&sb);
@@ -3130,9 +3138,9 @@ static void maybe_remove_section(struct config_store_data *store,
31303138
}
31313139

31323140
int git_config_set_in_file_gently(const char *config_filename,
3133-
const char *key, const char *value)
3141+
const char *key, const char *comment, const char *value)
31343142
{
3135-
return git_config_set_multivar_in_file_gently(config_filename, key, value, NULL, 0);
3143+
return git_config_set_multivar_in_file_gently(config_filename, key, value, NULL, comment, 0);
31363144
}
31373145

31383146
void git_config_set_in_file(const char *config_filename,
@@ -3153,7 +3161,7 @@ int repo_config_set_worktree_gently(struct repository *r,
31533161
if (r->repository_format_worktree_config) {
31543162
char *file = repo_git_path(r, "config.worktree");
31553163
int ret = git_config_set_multivar_in_file_gently(
3156-
file, key, value, NULL, 0);
3164+
file, key, value, NULL, NULL, 0);
31573165
free(file);
31583166
return ret;
31593167
}
@@ -3195,6 +3203,7 @@ void git_config_set(const char *key, const char *value)
31953203
int git_config_set_multivar_in_file_gently(const char *config_filename,
31963204
const char *key, const char *value,
31973205
const char *value_pattern,
3206+
const char *comment,
31983207
unsigned flags)
31993208
{
32003209
int fd = -1, in_fd = -1;
@@ -3245,7 +3254,7 @@ int git_config_set_multivar_in_file_gently(const char *config_filename,
32453254
free(store.key);
32463255
store.key = xstrdup(key);
32473256
if (write_section(fd, key, &store) < 0 ||
3248-
write_pair(fd, key, value, &store) < 0)
3257+
write_pair(fd, key, value, comment, &store) < 0)
32493258
goto write_err_out;
32503259
} else {
32513260
struct stat st;
@@ -3399,7 +3408,7 @@ int git_config_set_multivar_in_file_gently(const char *config_filename,
33993408
if (write_section(fd, key, &store) < 0)
34003409
goto write_err_out;
34013410
}
3402-
if (write_pair(fd, key, value, &store) < 0)
3411+
if (write_pair(fd, key, value, comment, &store) < 0)
34033412
goto write_err_out;
34043413
}
34053414

@@ -3444,7 +3453,7 @@ void git_config_set_multivar_in_file(const char *config_filename,
34443453
const char *value_pattern, unsigned flags)
34453454
{
34463455
if (!git_config_set_multivar_in_file_gently(config_filename, key, value,
3447-
value_pattern, flags))
3456+
value_pattern, NULL, flags))
34483457
return;
34493458
if (value)
34503459
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,
34673476
int res = git_config_set_multivar_in_file_gently(file,
34683477
key, value,
34693478
value_pattern,
3470-
flags);
3479+
NULL, flags);
34713480
free(file);
34723481
return res;
34733482
}

config.h

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -290,7 +290,7 @@ int git_config_pathname(const char **, const char *, const char *);
290290

291291
int git_config_expiry_date(timestamp_t *, const char *, const char *);
292292
int git_config_color(char *, const char *, const char *);
293-
int git_config_set_in_file_gently(const char *, const char *, const char *);
293+
int git_config_set_in_file_gently(const char *, const char *, const char *, const char *);
294294

295295
/**
296296
* 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 *);
336336
int git_config_set_multivar_gently(const char *, const char *, const char *, unsigned);
337337
void git_config_set_multivar(const char *, const char *, const char *, unsigned);
338338
int repo_config_set_multivar_gently(struct repository *, const char *, const char *, const char *, unsigned);
339-
int git_config_set_multivar_in_file_gently(const char *, const char *, const char *, const char *, unsigned);
339+
int git_config_set_multivar_in_file_gently(const char *, const char *, const char *, const char *, const char *, unsigned);
340340

341341
/**
342342
* takes four parameters:

sequencer.c

Lines changed: 14 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -3462,54 +3462,54 @@ static int save_opts(struct replay_opts *opts)
34623462

34633463
if (opts->no_commit)
34643464
res |= git_config_set_in_file_gently(opts_file,
3465-
"options.no-commit", "true");
3465+
"options.no-commit", NULL, "true");
34663466
if (opts->edit >= 0)
3467-
res |= git_config_set_in_file_gently(opts_file, "options.edit",
3467+
res |= git_config_set_in_file_gently(opts_file, "options.edit", NULL,
34683468
opts->edit ? "true" : "false");
34693469
if (opts->allow_empty)
34703470
res |= git_config_set_in_file_gently(opts_file,
3471-
"options.allow-empty", "true");
3471+
"options.allow-empty", NULL, "true");
34723472
if (opts->allow_empty_message)
34733473
res |= git_config_set_in_file_gently(opts_file,
3474-
"options.allow-empty-message", "true");
3474+
"options.allow-empty-message", NULL, "true");
34753475
if (opts->keep_redundant_commits)
34763476
res |= git_config_set_in_file_gently(opts_file,
3477-
"options.keep-redundant-commits", "true");
3477+
"options.keep-redundant-commits", NULL, "true");
34783478
if (opts->signoff)
34793479
res |= git_config_set_in_file_gently(opts_file,
3480-
"options.signoff", "true");
3480+
"options.signoff", NULL, "true");
34813481
if (opts->record_origin)
34823482
res |= git_config_set_in_file_gently(opts_file,
3483-
"options.record-origin", "true");
3483+
"options.record-origin", NULL, "true");
34843484
if (opts->allow_ff)
34853485
res |= git_config_set_in_file_gently(opts_file,
3486-
"options.allow-ff", "true");
3486+
"options.allow-ff", NULL, "true");
34873487
if (opts->mainline) {
34883488
struct strbuf buf = STRBUF_INIT;
34893489
strbuf_addf(&buf, "%d", opts->mainline);
34903490
res |= git_config_set_in_file_gently(opts_file,
3491-
"options.mainline", buf.buf);
3491+
"options.mainline", NULL, buf.buf);
34923492
strbuf_release(&buf);
34933493
}
34943494
if (opts->strategy)
34953495
res |= git_config_set_in_file_gently(opts_file,
3496-
"options.strategy", opts->strategy);
3496+
"options.strategy", NULL, opts->strategy);
34973497
if (opts->gpg_sign)
34983498
res |= git_config_set_in_file_gently(opts_file,
3499-
"options.gpg-sign", opts->gpg_sign);
3499+
"options.gpg-sign", NULL, opts->gpg_sign);
35003500
for (size_t i = 0; i < opts->xopts.nr; i++)
35013501
res |= git_config_set_multivar_in_file_gently(opts_file,
35023502
"options.strategy-option",
3503-
opts->xopts.v[i], "^$", 0);
3503+
opts->xopts.v[i], "^$", NULL, 0);
35043504
if (opts->allow_rerere_auto)
35053505
res |= git_config_set_in_file_gently(opts_file,
3506-
"options.allow-rerere-auto",
3506+
"options.allow-rerere-auto", NULL,
35073507
opts->allow_rerere_auto == RERERE_AUTOUPDATE ?
35083508
"true" : "false");
35093509

35103510
if (opts->explicit_cleanup)
35113511
res |= git_config_set_in_file_gently(opts_file,
3512-
"options.default-msg-cleanup",
3512+
"options.default-msg-cleanup", NULL,
35133513
describe_cleanup_mode(opts->default_msg_cleanup));
35143514
return res;
35153515
}

submodule-config.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -978,7 +978,7 @@ int config_set_in_gitmodules_file_gently(const char *key, const char *value)
978978
{
979979
int ret;
980980

981-
ret = git_config_set_in_file_gently(GITMODULES_FILE, key, value);
981+
ret = git_config_set_in_file_gently(GITMODULES_FILE, key, NULL, value);
982982
if (ret < 0)
983983
/* Maybe the user already did that, don't error out here */
984984
warning(_("Could not update .gitmodules entry %s"), key);

submodule.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2046,7 +2046,7 @@ void submodule_unset_core_worktree(const struct submodule *sub)
20462046
submodule_name_to_gitdir(&config_path, the_repository, sub->name);
20472047
strbuf_addstr(&config_path, "/config");
20482048

2049-
if (git_config_set_in_file_gently(config_path.buf, "core.worktree", NULL))
2049+
if (git_config_set_in_file_gently(config_path.buf, "core.worktree", NULL, NULL))
20502050
warning(_("Could not unset core.worktree setting in submodule '%s'"),
20512051
sub->path);
20522052

0 commit comments

Comments
 (0)