Skip to content

Commit 29fdf28

Browse files
committed
commit: print advice when core.commentString=auto
Support for this config setting was deprecated in the last commit so print some advice to help the user update their config settings when they are using this setting. The advice message explains that the setting is deprecated and will be removed in future. It also shows the commands that the user needs to run to either unset core.commentChar and core.commentString completely or to change the current setting to a fixed comment string. In order to generate this advice we need to parse the config with a callback that records each file where either of the keys is set and whether a key occurs more that once in a given file. This lets us generate the list of commands to remove all the keys and also tells us which key the "auto" setting comes from. The hard coding of some filenames in add_config_scope_arg() is unfortunate but as this temporary code that will be removed when Git 3.0 is released I decided it wasn't worth adding functions to get the name of the local and worktree config files. As we want the user to update their config we do not provide a way for this advice to be disabled other than changing the value of core.commentChar or core.commentString. Signed-off-by: Phillip Wood <[email protected]> y Conflicts: y config.c y t/t3404-rebase-interactive.sh y t/t7502-commit-porcelain.sh y Conflicts: y config.c y Conflicts: y config.c y Conflicts: y config.c
1 parent ea90dca commit 29fdf28

File tree

3 files changed

+232
-10
lines changed

3 files changed

+232
-10
lines changed

config.c

Lines changed: 187 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1958,9 +1958,51 @@ int git_configset_get_pathname(struct config_set *set, const char *key, char **d
19581958
struct comment_char_config {
19591959
unsigned last_key_id;
19601960
bool auto_set;
1961+
bool auto_set_in_file;
1962+
struct strintmap key_flags;
1963+
size_t alloc, nr;
1964+
struct comment_char_config_item {
1965+
unsigned key_id;
1966+
char *path;
1967+
enum config_scope scope;
1968+
} *item;
19611969
};
19621970

1963-
#define COMMENT_CHAR_CFG_INIT { 0 }
1971+
#define COMMENT_CHAR_CFG_INIT { \
1972+
.key_flags = STRINTMAP_INIT, \
1973+
}
1974+
1975+
static void comment_char_config_release(struct comment_char_config *config)
1976+
{
1977+
strintmap_clear(&config->key_flags);
1978+
for (size_t i = 0; i < config->nr; i++)
1979+
free(config->item[i].path);
1980+
free(config->item);
1981+
}
1982+
1983+
/* Used to track whether the key occurs more than once in a given file */
1984+
#define KEY_SEEN_ONCE 1u
1985+
#define KEY_SEEN_TWICE 2u
1986+
#define COMMENT_KEY_SHIFT(id) (2 * (id))
1987+
#define COMMENT_KEY_MASK(id) (3u << COMMENT_KEY_SHIFT(id))
1988+
1989+
static void set_comment_key_flags(struct comment_char_config *config,
1990+
const char *path, unsigned id, unsigned value)
1991+
{
1992+
unsigned old = strintmap_get(&config->key_flags, path);
1993+
unsigned new = (old & ~COMMENT_KEY_MASK(id)) |
1994+
value << COMMENT_KEY_SHIFT(id);
1995+
1996+
strintmap_set(&config->key_flags, path, new);
1997+
}
1998+
1999+
static unsigned get_comment_key_flags(struct comment_char_config *config,
2000+
const char *path, unsigned id)
2001+
{
2002+
unsigned value = strintmap_get(&config->key_flags, path);
2003+
2004+
return (value & COMMENT_KEY_MASK(id)) >> COMMENT_KEY_SHIFT(id);
2005+
}
19642006

19652007
static const char* comment_key_name(unsigned id)
19662008
{
@@ -1976,10 +2018,10 @@ static const char* comment_key_name(unsigned id)
19762018
}
19772019

19782020
static void comment_char_callback(const char *key, const char *value,
1979-
const struct config_context *ctx UNUSED,
1980-
void *data)
2021+
const struct config_context *ctx, void *data)
19812022
{
19822023
struct comment_char_config *config = data;
2024+
const struct key_value_info *kvi = ctx->kvi;
19832025
unsigned key_id;
19842026

19852027
if (!strcmp(key, "core.commentchar"))
@@ -1991,8 +2033,136 @@ static void comment_char_callback(const char *key, const char *value,
19912033

19922034
config->last_key_id = key_id;
19932035
config->auto_set = value && !strcmp(value, "auto");
2036+
if (kvi->origin_type != CONFIG_ORIGIN_FILE) {
2037+
return;
2038+
} else if (get_comment_key_flags(config, kvi->filename, key_id)) {
2039+
set_comment_key_flags(config, kvi->filename, key_id,
2040+
KEY_SEEN_TWICE);
2041+
} else {
2042+
struct comment_char_config_item *item;
2043+
2044+
ALLOC_GROW_BY(config->item, config->nr, 1, config->alloc);
2045+
item = &config->item[config->nr - 1];
2046+
item->key_id = key_id;
2047+
item->scope = kvi->scope;
2048+
item->path = xstrdup(kvi->filename);
2049+
set_comment_key_flags(config, kvi->filename, key_id,
2050+
KEY_SEEN_ONCE);
2051+
}
2052+
config->auto_set_in_file = config->auto_set;
19942053
}
19952054

2055+
static void add_config_scope_arg(struct repository *repo, struct strbuf *buf,
2056+
struct comment_char_config_item *item)
2057+
{
2058+
char *global_config = git_global_config();
2059+
char *system_config = git_system_config();
2060+
2061+
if (item->scope == CONFIG_SCOPE_SYSTEM && access(item->path, W_OK)) {
2062+
/*
2063+
* If the user cannot write to the system config recommend
2064+
* setting the global config instead.
2065+
*/
2066+
strbuf_addstr(buf, "--global ");
2067+
} else if (fspatheq(item->path, system_config)) {
2068+
strbuf_addstr(buf, "--system ");
2069+
} else if (fspatheq(item->path, global_config)) {
2070+
strbuf_addstr(buf, "--global ");
2071+
} else if (fspatheq(item->path,
2072+
mkpath("%s/config",
2073+
repo_get_git_dir(repo)))) {
2074+
; /* --local is the default */
2075+
} else if (fspatheq(item->path,
2076+
mkpath("%s/config.worktree",
2077+
repo_get_common_dir(repo)))) {
2078+
strbuf_addstr(buf, "--worktree ");
2079+
} else {
2080+
const char *path = item->path;
2081+
const char *home = getenv("HOME");
2082+
2083+
strbuf_addstr(buf, "--file ");
2084+
if (home && !fspathncmp(path, home, strlen(home))) {
2085+
path += strlen(home);
2086+
if (!fspathncmp(path, "/", 1))
2087+
path++;
2088+
strbuf_addstr(buf, "~/");
2089+
}
2090+
sq_quote_buf_pretty(buf, path);
2091+
strbuf_addch(buf, ' ');
2092+
}
2093+
2094+
free(global_config);
2095+
free(system_config);
2096+
}
2097+
2098+
static bool can_unset_comment_char_config(struct comment_char_config *config)
2099+
{
2100+
for (size_t i = 0; i < config->nr; i++) {
2101+
struct comment_char_config_item *item = &config->item[i];
2102+
2103+
if (item->scope == CONFIG_SCOPE_SYSTEM &&
2104+
access(item->path, W_OK))
2105+
return false;
2106+
}
2107+
2108+
return true;
2109+
}
2110+
2111+
static void add_unset_auto_comment_char_advice(struct repository *repo,
2112+
struct comment_char_config *config)
2113+
{
2114+
struct strbuf buf = STRBUF_INIT;
2115+
2116+
if (!can_unset_comment_char_config(config))
2117+
return;
2118+
2119+
for (size_t i = 0; i < config->nr; i++) {
2120+
struct comment_char_config_item *item = &config->item[i];
2121+
2122+
strbuf_addstr(&buf, " git config unset ");
2123+
add_config_scope_arg(repo, &buf, item);
2124+
if (get_comment_key_flags(config, item->path, item->key_id) == KEY_SEEN_TWICE)
2125+
strbuf_addstr(&buf, "--all ");
2126+
strbuf_addf(&buf, "%s\n", comment_key_name(item->key_id));
2127+
}
2128+
advise(_("\nTo use the default comment string (#) please run\n\n%s"),
2129+
buf.buf);
2130+
strbuf_release(&buf);
2131+
}
2132+
2133+
static void add_comment_char_advice(struct repository *repo,
2134+
struct comment_char_config *config)
2135+
{
2136+
struct strbuf buf = STRBUF_INIT;
2137+
struct comment_char_config_item *item;
2138+
/* TRANSLATORS this is a place holder for the value of core.commentString */
2139+
const char *placeholder = _("<comment string>");
2140+
2141+
/*
2142+
* If auto is set in the last file that we saw advise the user how to
2143+
* update their config.
2144+
*/
2145+
if (!config->auto_set_in_file)
2146+
return;
2147+
2148+
add_unset_auto_comment_char_advice(repo, config);
2149+
item = &config->item[config->nr - 1];
2150+
strbuf_reset(&buf);
2151+
strbuf_addstr(&buf, " git config set ");
2152+
add_config_scope_arg(repo, &buf, item);
2153+
strbuf_addf(&buf, "%s %s\n", comment_key_name(item->key_id),
2154+
placeholder);
2155+
advise(_("\nTo set a custom comment string please run\n\n"
2156+
"%s\nwhere '%s' is the string you wish to use.\n"),
2157+
buf.buf, placeholder);
2158+
strbuf_release(&buf);
2159+
}
2160+
2161+
#undef KEY_SEEN_ONCE
2162+
#undef KEY_SEEN_TWICE
2163+
#undef COMMENT_KEY_SHIFT
2164+
#undef COMMENT_KEY_MASK
2165+
19962166
struct repo_config {
19972167
struct repository *repo;
19982168
struct comment_char_config comment_char_config;
@@ -2003,18 +2173,26 @@ struct repo_config {
20032173
.repo = repo_, \
20042174
};
20052175

2176+
static void repo_config_release(struct repo_config *config)
2177+
{
2178+
comment_char_config_release(&config->comment_char_config);
2179+
}
2180+
20062181
#ifdef WITH_BREAKING_CHANGES
2007-
static void check_auto_comment_char_config(struct comment_char_config *config)
2182+
static void check_auto_comment_char_config(struct repository *repo,
2183+
struct comment_char_config *config)
20082184
{
20092185
if (!config->auto_set)
20102186
return;
20112187

20122188
die_message(_("Support for '%s=auto' has been removed in Git 3.0"),
20132189
comment_key_name(config->last_key_id));
2190+
add_comment_char_advice(repo, config);
20142191
die(NULL);
20152192
}
20162193
#else
2017-
static void check_auto_comment_char_config(struct comment_char_config *config)
2194+
static void check_auto_comment_char_config(struct repository *repo,
2195+
struct comment_char_config *config)
20182196
{
20192197
extern bool warn_on_auto_comment_char;
20202198
const char *DEPRECATED_CONFIG_ENV =
@@ -2034,6 +2212,7 @@ static void check_auto_comment_char_config(struct comment_char_config *config)
20342212

20352213
warning(_("Support for '%s=auto' is deprecated and will be removed in "
20362214
"Git 3.0"), comment_key_name(config->last_key_id));
2215+
add_comment_char_advice(repo, config);
20372216
}
20382217
#endif /* WITH_BREAKING_CHANGES */
20392218

@@ -2042,7 +2221,8 @@ static void check_deprecated_config(struct repo_config *config)
20422221
if (!config->repo->check_deprecated_config)
20432222
return;
20442223

2045-
check_auto_comment_char_config(&config->comment_char_config);
2224+
check_auto_comment_char_config(config->repo,
2225+
&config->comment_char_config);
20462226
}
20472227

20482228
static int repo_config_callback(const char *key, const char *value,
@@ -2085,6 +2265,7 @@ static void repo_read_config(struct repository *repo)
20852265
*/
20862266
die(_("unknown error occurred while reading the configuration files"));
20872267
check_deprecated_config(&config);
2268+
repo_config_release(&config);
20882269
}
20892270

20902271
static void git_config_check_init(struct repository *repo)

t/t3404-rebase-interactive.sh

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1186,9 +1186,19 @@ test_expect_success !WITH_BREAKING_CHANGES 'rebase -i respects core.commentchar=
11861186
test_set_editor "$(pwd)/copy-edit-script.sh" &&
11871187
git rebase -i HEAD^ 2>err
11881188
) &&
1189-
sed -n "s/^warning: //p" err >actual &&
1189+
sed -n "s/^hint: *\$//p; s/^hint: //p; s/^warning: //p" err >actual &&
11901190
cat >expect <<-EOF &&
11911191
Support for ${SQ}core.commentChar=auto${SQ} is deprecated and will be removed in Git 3.0
1192+
1193+
To use the default comment string (#) please run
1194+
1195+
git config unset core.commentChar
1196+
1197+
To set a custom comment string please run
1198+
1199+
git config set core.commentChar <comment string>
1200+
1201+
where ${SQ}<comment string>${SQ} is the string you wish to use.
11921202
EOF
11931203
test_cmp expect actual &&
11941204
test -z "$(grep -ve "^#" -e "^\$" -e "^pick" edit-script)"

t/t7502-commit-porcelain.sh

Lines changed: 34 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -958,10 +958,31 @@ test_expect_success 'commit --status with custom comment character' '
958958

959959
test_expect_success !WITH_BREAKING_CHANGES 'switch core.commentchar' '
960960
test_commit "#foo" foo &&
961-
GIT_EDITOR=.git/FAKE_EDITOR git -c core.commentChar=auto commit --amend 2>err &&
962-
sed -n "s/^warning: //p" err >actual &&
961+
cat >config-include <<-\EOF &&
962+
[core]
963+
commentString=:
964+
commentString=%
965+
commentChar=auto
966+
EOF
967+
test_when_finished "rm config-include" &&
968+
test_config include.path "$(pwd)/config-include" &&
969+
test_config core.commentChar ! &&
970+
GIT_EDITOR=.git/FAKE_EDITOR git commit --amend 2>err &&
971+
sed -n "s/^hint: *\$//p; s/^hint: //p; s/^warning: //p" err >actual &&
963972
cat >expect <<-EOF &&
964973
Support for ${SQ}core.commentChar=auto${SQ} is deprecated and will be removed in Git 3.0
974+
975+
To use the default comment string (#) please run
976+
977+
git config unset core.commentChar
978+
git config unset --file ~/config-include --all core.commentString
979+
git config unset --file ~/config-include core.commentChar
980+
981+
To set a custom comment string please run
982+
983+
git config set --file ~/config-include core.commentChar <comment string>
984+
985+
where ${SQ}<comment string>${SQ} is the string you wish to use.
965986
EOF
966987
test_cmp expect actual &&
967988
test_grep "^; Changes to be committed:" .git/COMMIT_EDITMSG
@@ -990,9 +1011,19 @@ EOF
9901011
test_expect_success WITH_BREAKING_CHANGES 'core.commentChar=auto is rejected' '
9911012
test_config core.commentChar auto &&
9921013
test_must_fail git rev-parse --git-dir 2>err &&
993-
sed -n "s/^fatal: //p" err >actual &&
1014+
sed -n "s/^hint: *\$//p; s/^hint: //p; s/^fatal: //p" err >actual &&
9941015
cat >expect <<-EOF &&
9951016
Support for ${SQ}core.commentChar=auto${SQ} has been removed in Git 3.0
1017+
1018+
To use the default comment string (#) please run
1019+
1020+
git config unset core.commentChar
1021+
1022+
To set a custom comment string please run
1023+
1024+
git config set core.commentChar <comment string>
1025+
1026+
where ${SQ}<comment string>${SQ} is the string you wish to use.
9961027
EOF
9971028
test_cmp expect actual
9981029
'

0 commit comments

Comments
 (0)