88
99#include "git-compat-util.h"
1010#include "abspath.h"
11+ #include "advice.h"
1112#include "date.h"
1213#include "branch.h"
1314#include "config.h"
@@ -1955,9 +1956,51 @@ int git_configset_get_pathname(struct config_set *set, const char *key, char **d
19551956struct comment_char_config {
19561957 unsigned last_key_id ;
19571958 bool auto_set ;
1959+ bool auto_set_in_file ;
1960+ struct strintmap key_flags ;
1961+ size_t alloc , nr ;
1962+ struct comment_char_config_item {
1963+ unsigned key_id ;
1964+ char * path ;
1965+ enum config_scope scope ;
1966+ } * item ;
19581967};
19591968
1960- #define COMMENT_CHAR_CFG_INIT { 0 }
1969+ #define COMMENT_CHAR_CFG_INIT { \
1970+ .key_flags = STRINTMAP_INIT, \
1971+ }
1972+
1973+ static void comment_char_config_release (struct comment_char_config * config )
1974+ {
1975+ strintmap_clear (& config -> key_flags );
1976+ for (size_t i = 0 ; i < config -> nr ; i ++ )
1977+ free (config -> item [i ].path );
1978+ free (config -> item );
1979+ }
1980+
1981+ /* Used to track whether the key occurs more than once in a given file */
1982+ #define KEY_SEEN_ONCE 1u
1983+ #define KEY_SEEN_TWICE 2u
1984+ #define COMMENT_KEY_SHIFT (id ) (2 * (id))
1985+ #define COMMENT_KEY_MASK (id ) (3u << COMMENT_KEY_SHIFT(id))
1986+
1987+ static void set_comment_key_flags (struct comment_char_config * config ,
1988+ const char * path , unsigned id , unsigned value )
1989+ {
1990+ unsigned old = strintmap_get (& config -> key_flags , path );
1991+ unsigned new = (old & ~COMMENT_KEY_MASK (id )) |
1992+ value << COMMENT_KEY_SHIFT (id );
1993+
1994+ strintmap_set (& config -> key_flags , path , new );
1995+ }
1996+
1997+ static unsigned get_comment_key_flags (struct comment_char_config * config ,
1998+ const char * path , unsigned id )
1999+ {
2000+ unsigned value = strintmap_get (& config -> key_flags , path );
2001+
2002+ return (value & COMMENT_KEY_MASK (id )) >> COMMENT_KEY_SHIFT (id );
2003+ }
19612004
19622005static const char * comment_key_name (unsigned id )
19632006{
@@ -1973,10 +2016,10 @@ static const char *comment_key_name(unsigned id)
19732016}
19742017
19752018static void comment_char_callback (const char * key , const char * value ,
1976- const struct config_context * ctx UNUSED ,
1977- void * data )
2019+ const struct config_context * ctx , void * data )
19782020{
19792021 struct comment_char_config * config = data ;
2022+ const struct key_value_info * kvi = ctx -> kvi ;
19802023 unsigned key_id ;
19812024
19822025 if (!strcmp (key , "core.commentchar" ))
@@ -1988,8 +2031,136 @@ static void comment_char_callback(const char *key, const char *value,
19882031
19892032 config -> last_key_id = key_id ;
19902033 config -> auto_set = value && !strcmp (value , "auto" );
2034+ if (kvi -> origin_type != CONFIG_ORIGIN_FILE ) {
2035+ return ;
2036+ } else if (get_comment_key_flags (config , kvi -> filename , key_id )) {
2037+ set_comment_key_flags (config , kvi -> filename , key_id ,
2038+ KEY_SEEN_TWICE );
2039+ } else {
2040+ struct comment_char_config_item * item ;
2041+
2042+ ALLOC_GROW_BY (config -> item , config -> nr , 1 , config -> alloc );
2043+ item = & config -> item [config -> nr - 1 ];
2044+ item -> key_id = key_id ;
2045+ item -> scope = kvi -> scope ;
2046+ item -> path = xstrdup (kvi -> filename );
2047+ set_comment_key_flags (config , kvi -> filename , key_id ,
2048+ KEY_SEEN_ONCE );
2049+ }
2050+ config -> auto_set_in_file = config -> auto_set ;
19912051}
19922052
2053+ static void add_config_scope_arg (struct repository * repo , struct strbuf * buf ,
2054+ struct comment_char_config_item * item )
2055+ {
2056+ char * global_config = git_global_config ();
2057+ char * system_config = git_system_config ();
2058+
2059+ if (item -> scope == CONFIG_SCOPE_SYSTEM && access (item -> path , W_OK )) {
2060+ /*
2061+ * If the user cannot write to the system config recommend
2062+ * setting the global config instead.
2063+ */
2064+ strbuf_addstr (buf , "--global " );
2065+ } else if (fspatheq (item -> path , system_config )) {
2066+ strbuf_addstr (buf , "--system " );
2067+ } else if (fspatheq (item -> path , global_config )) {
2068+ strbuf_addstr (buf , "--global " );
2069+ } else if (fspatheq (item -> path ,
2070+ mkpath ("%s/config" ,
2071+ repo_get_git_dir (repo )))) {
2072+ ; /* --local is the default */
2073+ } else if (fspatheq (item -> path ,
2074+ mkpath ("%s/config.worktree" ,
2075+ repo_get_common_dir (repo )))) {
2076+ strbuf_addstr (buf , "--worktree " );
2077+ } else {
2078+ const char * path = item -> path ;
2079+ const char * home = getenv ("HOME" );
2080+
2081+ strbuf_addstr (buf , "--file " );
2082+ if (home && !fspathncmp (path , home , strlen (home ))) {
2083+ path += strlen (home );
2084+ if (!fspathncmp (path , "/" , 1 ))
2085+ path ++ ;
2086+ strbuf_addstr (buf , "~/" );
2087+ }
2088+ sq_quote_buf_pretty (buf , path );
2089+ strbuf_addch (buf , ' ' );
2090+ }
2091+
2092+ free (global_config );
2093+ free (system_config );
2094+ }
2095+
2096+ static bool can_unset_comment_char_config (struct comment_char_config * config )
2097+ {
2098+ for (size_t i = 0 ; i < config -> nr ; i ++ ) {
2099+ struct comment_char_config_item * item = & config -> item [i ];
2100+
2101+ if (item -> scope == CONFIG_SCOPE_SYSTEM &&
2102+ access (item -> path , W_OK ))
2103+ return false;
2104+ }
2105+
2106+ return true;
2107+ }
2108+
2109+ static void add_unset_auto_comment_char_advice (struct repository * repo ,
2110+ struct comment_char_config * config )
2111+ {
2112+ struct strbuf buf = STRBUF_INIT ;
2113+
2114+ if (!can_unset_comment_char_config (config ))
2115+ return ;
2116+
2117+ for (size_t i = 0 ; i < config -> nr ; i ++ ) {
2118+ struct comment_char_config_item * item = & config -> item [i ];
2119+
2120+ strbuf_addstr (& buf , " git config unset " );
2121+ add_config_scope_arg (repo , & buf , item );
2122+ if (get_comment_key_flags (config , item -> path , item -> key_id ) == KEY_SEEN_TWICE )
2123+ strbuf_addstr (& buf , "--all " );
2124+ strbuf_addf (& buf , "%s\n" , comment_key_name (item -> key_id ));
2125+ }
2126+ advise (_ ("\nTo use the default comment string (#) please run\n\n%s" ),
2127+ buf .buf );
2128+ strbuf_release (& buf );
2129+ }
2130+
2131+ static void add_comment_char_advice (struct repository * repo ,
2132+ struct comment_char_config * config )
2133+ {
2134+ struct strbuf buf = STRBUF_INIT ;
2135+ struct comment_char_config_item * item ;
2136+ /* TRANSLATORS this is a place holder for the value of core.commentString */
2137+ const char * placeholder = _ ("<comment string>" );
2138+
2139+ /*
2140+ * If auto is set in the last file that we saw advise the user how to
2141+ * update their config.
2142+ */
2143+ if (!config -> auto_set_in_file )
2144+ return ;
2145+
2146+ add_unset_auto_comment_char_advice (repo , config );
2147+ item = & config -> item [config -> nr - 1 ];
2148+ strbuf_reset (& buf );
2149+ strbuf_addstr (& buf , " git config set " );
2150+ add_config_scope_arg (repo , & buf , item );
2151+ strbuf_addf (& buf , "%s %s\n" , comment_key_name (item -> key_id ),
2152+ placeholder );
2153+ advise (_ ("\nTo set a custom comment string please run\n\n"
2154+ "%s\nwhere '%s' is the string you wish to use.\n" ),
2155+ buf .buf , placeholder );
2156+ strbuf_release (& buf );
2157+ }
2158+
2159+ #undef KEY_SEEN_ONCE
2160+ #undef KEY_SEEN_TWICE
2161+ #undef COMMENT_KEY_SHIFT
2162+ #undef COMMENT_KEY_MASK
2163+
19932164struct repo_config {
19942165 struct repository * repo ;
19952166 struct comment_char_config comment_char_config ;
@@ -2000,18 +2171,26 @@ struct repo_config {
20002171 .repo = repo_, \
20012172 };
20022173
2174+ static void repo_config_release (struct repo_config * config )
2175+ {
2176+ comment_char_config_release (& config -> comment_char_config );
2177+ }
2178+
20032179#ifdef WITH_BREAKING_CHANGES
2004- static void check_auto_comment_char_config (struct comment_char_config * config )
2180+ static void check_auto_comment_char_config (struct repository * repo ,
2181+ struct comment_char_config * config )
20052182{
20062183 if (!config -> auto_set )
20072184 return ;
20082185
20092186 die_message (_ ("Support for '%s=auto' has been removed in Git 3.0" ),
20102187 comment_key_name (config -> last_key_id ));
2188+ add_comment_char_advice (repo , config );
20112189 die (NULL );
20122190}
20132191#else
2014- static void check_auto_comment_char_config (struct comment_char_config * config )
2192+ static void check_auto_comment_char_config (struct repository * repo ,
2193+ struct comment_char_config * config )
20152194{
20162195 extern bool warn_on_auto_comment_char ;
20172196 const char * DEPRECATED_CONFIG_ENV =
@@ -2031,6 +2210,7 @@ static void check_auto_comment_char_config(struct comment_char_config *config)
20312210
20322211 warning (_ ("Support for '%s=auto' is deprecated and will be removed in "
20332212 "Git 3.0" ), comment_key_name (config -> last_key_id ));
2213+ add_comment_char_advice (repo , config );
20342214}
20352215#endif /* WITH_BREAKING_CHANGES */
20362216
@@ -2039,7 +2219,8 @@ static void check_deprecated_config(struct repo_config *config)
20392219 if (!config -> repo -> check_deprecated_config )
20402220 return ;
20412221
2042- check_auto_comment_char_config (& config -> comment_char_config );
2222+ check_auto_comment_char_config (config -> repo ,
2223+ & config -> comment_char_config );
20432224}
20442225
20452226static int repo_config_callback (const char * key , const char * value ,
@@ -2082,6 +2263,7 @@ static void repo_read_config(struct repository *repo)
20822263 */
20832264 die (_ ("unknown error occurred while reading the configuration files" ));
20842265 check_deprecated_config (& config );
2266+ repo_config_release (& config );
20852267}
20862268
20872269static void git_config_check_init (struct repository * repo )
0 commit comments