@@ -1958,9 +1958,51 @@ int git_configset_get_pathname(struct config_set *set, const char *key, char **d
19581958struct 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
19652007static const char * comment_key_name (unsigned id )
19662008{
@@ -1976,10 +2018,10 @@ static const char* comment_key_name(unsigned id)
19762018}
19772019
19782020static 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+
19962166struct 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
20482228static 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
20902271static void git_config_check_init (struct repository * repo )
0 commit comments