diff --git a/Documentation/git-config.txt b/Documentation/git-config.txt index af374ee2e0..e4f2e07926 100644 --- a/Documentation/git-config.txt +++ b/Documentation/git-config.txt @@ -89,9 +89,15 @@ OPTIONS --comment :: Append a comment at the end of new or modified lines. - Unless __ begins with "#", a string "# " (hash - followed by a space) is prepended to it. The __ must not - contain linefeed characters (no multi-line comments are permitted). + + If __ begins with one or more whitespaces followed + by "#", it is used as-is. If it begins with "#", a space is + prepended before it is used. Otherwise, a string " # " (a + space followed by a hash followed by a space) is prepended + to it. And the resulting string is placed immediately after + the value defined for the variable. The __ must + not contain linefeed characters (no multi-line comments are + permitted). --get:: Get the value for a given key (optionally filtered by a regex diff --git a/builtin/config.c b/builtin/config.c index e859a659f4..0015620dde 100644 --- a/builtin/config.c +++ b/builtin/config.c @@ -841,12 +841,7 @@ int cmd_config(int argc, const char **argv, const char *prefix) flags |= CONFIG_FLAGS_FIXED_VALUE; } - if (comment) { - if (strchr(comment, '\n')) - die(_("no multi-line comment allowed: '%s'"), comment); - if (comment[0] != '#') - comment = xstrfmt("# %s", comment); - } + comment = git_config_prepare_comment_string(comment); if (actions & PAGING_ACTIONS) setup_auto_pager("config", 1); diff --git a/config.c b/config.c index 15019cb9a5..f1d4263a68 100644 --- a/config.c +++ b/config.c @@ -3044,7 +3044,7 @@ static ssize_t write_pair(int fd, const char *key, const char *value, } if (comment) - strbuf_addf(&sb, "%s %s\n", quote, comment); + strbuf_addf(&sb, "%s%s\n", quote, comment); else strbuf_addf(&sb, "%s\n", quote); @@ -3172,6 +3172,62 @@ void git_config_set(const char *key, const char *value) trace2_cmd_set_config(key, value); } +/* + * The ownership rule is that the caller will own the string + * if it receives a piece of memory different from what it passed + * as the parameter. + */ +const char *git_config_prepare_comment_string(const char *comment) +{ + size_t leading_blanks; + + if (!comment) + return NULL; + + if (strchr(comment, '\n')) + die(_("no multi-line comment allowed: '%s'"), comment); + + /* + * If it begins with one or more leading whitespace characters + * followed by '#", the comment string is used as-is. + * + * If it begins with '#', a SP is inserted between the comment + * and the value the comment is about. + * + * Otherwise, the value is followed by a SP followed by '#' + * followed by SP and then the comment string comes. + */ + + leading_blanks = strspn(comment, " \t"); + if (leading_blanks && comment[leading_blanks] == '#') + ; /* use it as-is */ + else if (comment[0] == '#') + comment = xstrfmt(" %s", comment); + else + comment = xstrfmt(" # %s", comment); + + return comment; +} + +static void validate_comment_string(const char *comment) +{ + size_t leading_blanks; + + if (!comment) + return; + /* + * The front-end must have massaged the comment string + * properly before calling us. + */ + if (strchr(comment, '\n')) + BUG("multi-line comments are not permitted: '%s'", comment); + + leading_blanks = strspn(comment, " \t"); + if (!leading_blanks || comment[leading_blanks] != '#') + BUG("comment must begin with one or more SP followed by '#': '%s'", + comment); +} + /* * If value==NULL, unset in (remove from) config, * if value_pattern!=NULL, disregard key/value pairs where value does not match. @@ -3211,16 +3267,7 @@ int git_config_set_multivar_in_file_gently(const char *config_filename, size_t contents_sz; struct config_store_data store = CONFIG_STORE_INIT; - if (comment) { - /* - * The front-end must have massaged the comment string - * properly before calling us. - */ - if (strchr(comment, '\n')) - BUG("multi-line comments are not permitted: '%s'", comment); - if (comment[0] != '#') - BUG("comment should begin with '#': '%s'", comment); - } + validate_comment_string(comment); /* parse-key returns negative; flip the sign to feed exit(3) */ ret = 0 - git_config_parse_key(key, &store.key, &store.baselen); diff --git a/config.h b/config.h index a85a827169..f4966e3749 100644 --- a/config.h +++ b/config.h @@ -338,6 +338,8 @@ 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 git_config_set_multivar_in_file_gently(const char *, const char *, const char *, const char *, const char *, unsigned); +const char *git_config_prepare_comment_string(const char *); + /** * takes four parameters: * diff --git a/t/t1300-config.sh b/t/t1300-config.sh index d5dfb45877..cc050b3c20 100755 --- a/t/t1300-config.sh +++ b/t/t1300-config.sh @@ -74,6 +74,8 @@ cat > expect << EOF penguin = gentoo # Pygoscelis papua disposition = peckish # find fish foo = bar #abc + spsp = value # and comment + htsp = value # and comment [Sections] WhatEver = Second EOF @@ -82,6 +84,10 @@ 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 && + + git config --comment="and comment" section.spsp value && + git config --comment=" # and comment" section.htsp value && + test_cmp expect .git/config '