@@ -3006,6 +3006,7 @@ static ssize_t write_section(int fd, const char *key,
30063006}
30073007
30083008static ssize_t write_pair (int fd , const char * key , const char * value ,
3009+ const char * comment ,
30093010 const struct config_store_data * store )
30103011{
30113012 int i ;
@@ -3046,7 +3047,11 @@ static ssize_t write_pair(int fd, const char *key, const char *value,
30463047 strbuf_addch (& sb , value [i ]);
30473048 break ;
30483049 }
3049- strbuf_addf (& sb , "%s\n" , quote );
3050+
3051+ if (comment )
3052+ strbuf_addf (& sb , "%s%s\n" , quote , comment );
3053+ else
3054+ strbuf_addf (& sb , "%s\n" , quote );
30503055
30513056 ret = write_in_full (fd , sb .buf , sb .len );
30523057 strbuf_release (& sb );
@@ -3135,9 +3140,9 @@ static void maybe_remove_section(struct config_store_data *store,
31353140}
31363141
31373142int git_config_set_in_file_gently (const char * config_filename ,
3138- const char * key , const char * value )
3143+ const char * key , const char * comment , const char * value )
31393144{
3140- return git_config_set_multivar_in_file_gently (config_filename , key , value , NULL , 0 );
3145+ return git_config_set_multivar_in_file_gently (config_filename , key , value , NULL , comment , 0 );
31413146}
31423147
31433148void git_config_set_in_file (const char * config_filename ,
@@ -3158,7 +3163,7 @@ int repo_config_set_worktree_gently(struct repository *r,
31583163 if (r -> repository_format_worktree_config ) {
31593164 char * file = repo_git_path (r , "config.worktree" );
31603165 int ret = git_config_set_multivar_in_file_gently (
3161- file , key , value , NULL , 0 );
3166+ file , key , value , NULL , NULL , 0 );
31623167 free (file );
31633168 return ret ;
31643169 }
@@ -3172,6 +3177,62 @@ void git_config_set(const char *key, const char *value)
31723177 trace2_cmd_set_config (key , value );
31733178}
31743179
3180+ /*
3181+ * The ownership rule is that the caller will own the string
3182+ * if it receives a piece of memory different from what it passed
3183+ * as the parameter.
3184+ */
3185+ const char * git_config_prepare_comment_string (const char * comment )
3186+ {
3187+ size_t leading_blanks ;
3188+
3189+ if (!comment )
3190+ return NULL ;
3191+
3192+ if (strchr (comment , '\n' ))
3193+ die (_ ("no multi-line comment allowed: '%s'" ), comment );
3194+
3195+ /*
3196+ * If it begins with one or more leading whitespace characters
3197+ * followed by '#", the comment string is used as-is.
3198+ *
3199+ * If it begins with '#', a SP is inserted between the comment
3200+ * and the value the comment is about.
3201+ *
3202+ * Otherwise, the value is followed by a SP followed by '#'
3203+ * followed by SP and then the comment string comes.
3204+ */
3205+
3206+ leading_blanks = strspn (comment , " \t" );
3207+ if (leading_blanks && comment [leading_blanks ] == '#' )
3208+ ; /* use it as-is */
3209+ else if (comment [0 ] == '#' )
3210+ comment = xstrfmt (" %s" , comment );
3211+ else
3212+ comment = xstrfmt (" # %s" , comment );
3213+
3214+ return comment ;
3215+ }
3216+
3217+ static void validate_comment_string (const char * comment )
3218+ {
3219+ size_t leading_blanks ;
3220+
3221+ if (!comment )
3222+ return ;
3223+ /*
3224+ * The front-end must have massaged the comment string
3225+ * properly before calling us.
3226+ */
3227+ if (strchr (comment , '\n' ))
3228+ BUG ("multi-line comments are not permitted: '%s'" , comment );
3229+
3230+ leading_blanks = strspn (comment , " \t" );
3231+ if (!leading_blanks || comment [leading_blanks ] != '#' )
3232+ BUG ("comment must begin with one or more SP followed by '#': '%s'" ,
3233+ comment );
3234+ }
3235+
31753236/*
31763237 * If value==NULL, unset in (remove from) config,
31773238 * if value_pattern!=NULL, disregard key/value pairs where value does not match.
@@ -3200,6 +3261,7 @@ void git_config_set(const char *key, const char *value)
32003261int git_config_set_multivar_in_file_gently (const char * config_filename ,
32013262 const char * key , const char * value ,
32023263 const char * value_pattern ,
3264+ const char * comment ,
32033265 unsigned flags )
32043266{
32053267 int fd = -1 , in_fd = -1 ;
@@ -3210,6 +3272,8 @@ int git_config_set_multivar_in_file_gently(const char *config_filename,
32103272 size_t contents_sz ;
32113273 struct config_store_data store = CONFIG_STORE_INIT ;
32123274
3275+ validate_comment_string (comment );
3276+
32133277 /* parse-key returns negative; flip the sign to feed exit(3) */
32143278 ret = 0 - git_config_parse_key (key , & store .key , & store .baselen );
32153279 if (ret )
@@ -3250,7 +3314,7 @@ int git_config_set_multivar_in_file_gently(const char *config_filename,
32503314 free (store .key );
32513315 store .key = xstrdup (key );
32523316 if (write_section (fd , key , & store ) < 0 ||
3253- write_pair (fd , key , value , & store ) < 0 )
3317+ write_pair (fd , key , value , comment , & store ) < 0 )
32543318 goto write_err_out ;
32553319 } else {
32563320 struct stat st ;
@@ -3404,7 +3468,7 @@ int git_config_set_multivar_in_file_gently(const char *config_filename,
34043468 if (write_section (fd , key , & store ) < 0 )
34053469 goto write_err_out ;
34063470 }
3407- if (write_pair (fd , key , value , & store ) < 0 )
3471+ if (write_pair (fd , key , value , comment , & store ) < 0 )
34083472 goto write_err_out ;
34093473 }
34103474
@@ -3449,7 +3513,7 @@ void git_config_set_multivar_in_file(const char *config_filename,
34493513 const char * value_pattern , unsigned flags )
34503514{
34513515 if (!git_config_set_multivar_in_file_gently (config_filename , key , value ,
3452- value_pattern , flags ))
3516+ value_pattern , NULL , flags ))
34533517 return ;
34543518 if (value )
34553519 die (_ ("could not set '%s' to '%s'" ), key , value );
@@ -3472,7 +3536,7 @@ int repo_config_set_multivar_gently(struct repository *r, const char *key,
34723536 int res = git_config_set_multivar_in_file_gently (file ,
34733537 key , value ,
34743538 value_pattern ,
3475- flags );
3539+ NULL , flags );
34763540 free (file );
34773541 return res ;
34783542}
0 commit comments