1515#include "date.h"
1616#include "branch.h"
1717#include "config.h"
18+ #include "dir.h"
1819#include "parse.h"
1920#include "convert.h"
2021#include "environment.h"
@@ -2470,10 +2471,276 @@ int git_configset_get_pathname(struct config_set *set, const char *key, char **d
24702471 return 1 ;
24712472}
24722473
2474+ struct comment_char_config {
2475+ unsigned last_key_id ;
2476+ bool auto_set ;
2477+ bool auto_set_in_file ;
2478+ struct strintmap key_flags ;
2479+ size_t alloc , nr ;
2480+ struct comment_char_config_item {
2481+ unsigned key_id ;
2482+ char * path ;
2483+ enum config_scope scope ;
2484+ } * item ;
2485+ struct repository * repo ;
2486+ };
2487+
2488+ #define COMMENT_CHAR_CFG_INIT { \
2489+ .key_flags = STRINTMAP_INIT, \
2490+ }
2491+
2492+ static void comment_char_config_release (struct comment_char_config * config )
2493+ {
2494+ strintmap_clear (& config -> key_flags );
2495+ for (size_t i = 0 ; i < config -> nr ; i ++ )
2496+ free (config -> item [i ].path );
2497+ free (config -> item );
2498+ }
2499+
2500+ /* Used to track whether the key occurs more than once in a given file */
2501+ #define KEY_SEEN_ONCE 1u
2502+ #define KEY_SEEN_TWICE 2u
2503+ #define COMMENT_KEY_SHIFT (id ) (2 * (id))
2504+ #define COMMENT_KEY_MASK (id ) (3u << COMMENT_KEY_SHIFT(id))
2505+
2506+ static void set_comment_key_flags (struct comment_char_config * config ,
2507+ const char * path , unsigned id , unsigned value )
2508+ {
2509+ unsigned old = strintmap_get (& config -> key_flags , path );
2510+ unsigned new = (old & ~COMMENT_KEY_MASK (id )) |
2511+ value << COMMENT_KEY_SHIFT (id );
2512+
2513+ strintmap_set (& config -> key_flags , path , new );
2514+ }
2515+
2516+ static unsigned get_comment_key_flags (struct comment_char_config * config ,
2517+ const char * path , unsigned id )
2518+ {
2519+ unsigned value = strintmap_get (& config -> key_flags , path );
2520+
2521+ return (value & COMMENT_KEY_MASK (id )) >> COMMENT_KEY_SHIFT (id );
2522+ }
2523+
2524+ static const char * comment_key_name (unsigned id )
2525+ {
2526+ static const char * name [] = {
2527+ "core.commentChar" ,
2528+ "core.commentString" ,
2529+ };
2530+
2531+ if (id >= ARRAY_SIZE (name ))
2532+ BUG ("invalid comment key id" );
2533+
2534+ return name [id ];
2535+ }
2536+
2537+ static void comment_char_callback (const char * key , const char * value ,
2538+ const struct config_context * ctx , void * data )
2539+ {
2540+ struct comment_char_config * config = data ;
2541+ const struct key_value_info * kvi = ctx -> kvi ;
2542+ unsigned key_id ;
2543+
2544+ if (!strcmp (key , "core.commentchar" ))
2545+ key_id = 0 ;
2546+ else if (!strcmp (key , "core.commentstring" ))
2547+ key_id = 1 ;
2548+ else
2549+ return ;
2550+
2551+ config -> last_key_id = key_id ;
2552+ config -> auto_set = value && !strcmp (value , "auto" );
2553+ if (kvi -> origin_type != CONFIG_ORIGIN_FILE ) {
2554+ return ;
2555+ } else if (get_comment_key_flags (config , kvi -> filename , key_id )) {
2556+ set_comment_key_flags (config , kvi -> filename , key_id ,
2557+ KEY_SEEN_TWICE );
2558+ } else {
2559+ struct comment_char_config_item * item ;
2560+
2561+ ALLOC_GROW_BY (config -> item , config -> nr , 1 , config -> alloc );
2562+ item = & config -> item [config -> nr - 1 ];
2563+ item -> key_id = key_id ;
2564+ item -> scope = kvi -> scope ;
2565+ item -> path = xstrdup (kvi -> filename );
2566+ set_comment_key_flags (config , kvi -> filename , key_id ,
2567+ KEY_SEEN_ONCE );
2568+ }
2569+ config -> auto_set_in_file = config -> auto_set ;
2570+ }
2571+
2572+ static void add_config_scope_arg (struct strbuf * buf ,
2573+ struct comment_char_config_item * item )
2574+ {
2575+ char * global_config = git_global_config ();
2576+ char * system_config = git_system_config ();
2577+
2578+ if (item -> scope == CONFIG_SCOPE_SYSTEM && access (item -> path , W_OK )) {
2579+ /*
2580+ * If the user cannot write to the system config recommend
2581+ * setting the global config instead.
2582+ */
2583+ strbuf_addstr (buf , "--global " );
2584+ } else if (fspatheq (item -> path , system_config )) {
2585+ strbuf_addstr (buf , "--system " );
2586+ } else if (fspatheq (item -> path , global_config )) {
2587+ strbuf_addstr (buf , "--global " );
2588+ } else if (fspatheq (item -> path ,
2589+ mkpath ("%s/config" ,
2590+ repo_get_git_dir (the_repository )))) {
2591+ ; /* --local is the default */
2592+ } else if (fspatheq (item -> path ,
2593+ mkpath ("%s/config.worktree" ,
2594+ repo_get_common_dir (the_repository )))) {
2595+ strbuf_addstr (buf , "--worktree " );
2596+ } else {
2597+ const char * path = item -> path ;
2598+ const char * home = getenv ("HOME" );
2599+
2600+ strbuf_addstr (buf , "--file " );
2601+ if (home && !fspathncmp (path , home , strlen (home ))) {
2602+ path += strlen (home );
2603+ if (!fspathncmp (path , "/" , 1 ))
2604+ path ++ ;
2605+ strbuf_addstr (buf , "~/" );
2606+ }
2607+ sq_quote_buf_pretty (buf , path );
2608+ strbuf_addch (buf , ' ' );
2609+ }
2610+
2611+ free (global_config );
2612+ free (system_config );
2613+ }
2614+
2615+ static bool can_unset_comment_char_config (struct comment_char_config * config )
2616+ {
2617+ for (size_t i = 0 ; i < config -> nr ; i ++ ) {
2618+ struct comment_char_config_item * item = & config -> item [i ];
2619+
2620+ if (item -> scope == CONFIG_SCOPE_SYSTEM &&
2621+ access (item -> path , W_OK ))
2622+ return false;
2623+ }
2624+
2625+ return true;
2626+ }
2627+
2628+ static void add_unset_auto_comment_char_advice (struct comment_char_config * config )
2629+ {
2630+ struct strbuf buf = STRBUF_INIT ;
2631+
2632+ if (!can_unset_comment_char_config (config ))
2633+ return ;
2634+
2635+ for (size_t i = 0 ; i < config -> nr ; i ++ ) {
2636+ struct comment_char_config_item * item = & config -> item [i ];
2637+
2638+ strbuf_addstr (& buf , " git config unset " );
2639+ add_config_scope_arg (& buf , item );
2640+ if (get_comment_key_flags (config , item -> path , item -> key_id ) == KEY_SEEN_TWICE )
2641+ strbuf_addstr (& buf , "--all " );
2642+ strbuf_addf (& buf , "%s\n" , comment_key_name (item -> key_id ));
2643+ }
2644+ advise (_ ("\nTo use the default comment string (#) please run\n\n%s" ),
2645+ buf .buf );
2646+ strbuf_release (& buf );
2647+ }
2648+
2649+ static void add_comment_char_advice (struct comment_char_config * config )
2650+ {
2651+ struct strbuf buf = STRBUF_INIT ;
2652+ struct comment_char_config_item * item ;
2653+ /* TRANSLATORS this is a place holder for the value of core.commentString */
2654+ const char * placeholder = _ ("<comment string>" );
2655+
2656+ /*
2657+ * If auto is set in the last file that we saw advise the user how to
2658+ * update their config.
2659+ */
2660+ if (!config -> auto_set_in_file )
2661+ return ;
2662+
2663+ add_unset_auto_comment_char_advice (config );
2664+ item = & config -> item [config -> nr - 1 ];
2665+ strbuf_reset (& buf );
2666+ strbuf_addstr (& buf , " git config set " );
2667+ add_config_scope_arg (& buf , item );
2668+ strbuf_addf (& buf , "%s %s\n" , comment_key_name (item -> key_id ),
2669+ placeholder );
2670+ advise (_ ("\nTo set a custom comment string please run\n\n"
2671+ "%s\nwhere '%s' is the string you wish to use.\n" ),
2672+ buf .buf , placeholder );
2673+ strbuf_release (& buf );
2674+ }
2675+
2676+ #undef KEY_SEEN_ONCE
2677+ #undef KEY_SEEN_TWICE
2678+ #undef COMMENT_KEY_SHIFT
2679+ #undef COMMENT_KEY_MASK
2680+
2681+ struct repo_config {
2682+ struct repository * repo ;
2683+ struct comment_char_config comment_char_config ;
2684+ };
2685+
2686+ #define REPO_CONFIG_INIT (repo_ ) { \
2687+ .comment_char_config = COMMENT_CHAR_CFG_INIT, \
2688+ .repo = repo_, \
2689+ };
2690+
2691+ static void repo_config_release (struct repo_config * config )
2692+ {
2693+ comment_char_config_release (& config -> comment_char_config );
2694+ }
2695+
2696+ static void check_auto_comment_str_config (struct comment_char_config * config )
2697+ {
2698+ if (!config -> auto_set )
2699+ return ;
2700+
2701+ #ifndef WITH_BREAKING_CHANGES
2702+ # define DEPRECATED_CONFIG_ENV "GIT_DEPRECATED_CONFIG_WARNING_GIVEN"
2703+ if (!warn_on_auto_comment_char ||
2704+ git_env_bool (DEPRECATED_CONFIG_ENV , 0 ))
2705+ return ;
2706+
2707+ setenv (DEPRECATED_CONFIG_ENV , "true" , 1 );
2708+ warning (_ ("Support for '%s=auto' is deprecated and will be removed in "
2709+ "Git 3.0" ), comment_key_name (config -> last_key_id ));
2710+ # undef DEPRECATED_CONFIG_ENV
2711+ #else
2712+ die_message (_ ("Support for '%s=auto' has been removed in Git 3.0" ),
2713+ comment_key_name (config -> last_key_id ));
2714+ #endif /* WITH_BREAKING_CHANGES */
2715+ add_comment_char_advice (config );
2716+ #ifdef WITH_BREAKING_CHANGES
2717+ die (NULL );
2718+ #endif /* WITH_BREAKING_CHANGES */
2719+ }
2720+
2721+ static void check_deprecated_config (struct repo_config * config )
2722+ {
2723+ #ifdef WITH_BREAKING_CHANGES
2724+ if (!config -> repo -> check_deprecated_config )
2725+ return ;
2726+ #endif /* WITH_BREAKING_CHANGES */
2727+ check_auto_comment_str_config (& config -> comment_char_config );
2728+ }
2729+
2730+ static int repo_config_callback (const char * key , const char * value ,
2731+ const struct config_context * ctx , void * data )
2732+ {
2733+ struct repo_config * config = data ;
2734+
2735+ comment_char_callback (key , value , ctx , & config -> comment_char_config );
2736+ return config_set_callback (key , value , ctx , config -> repo -> config );
2737+ }
2738+
24732739/* Functions use to read configuration from a repository */
24742740static void repo_read_config (struct repository * repo )
24752741{
24762742 struct config_options opts = { 0 };
2743+ struct repo_config config = REPO_CONFIG_INIT (repo );
24772744
24782745 opts .respect_includes = 1 ;
24792746 opts .commondir = repo -> commondir ;
@@ -2485,8 +2752,8 @@ static void repo_read_config(struct repository *repo)
24852752 git_configset_clear (repo -> config );
24862753
24872754 git_configset_init (repo -> config );
2488- if (config_with_options (config_set_callback , repo -> config , NULL ,
2489- repo , & opts ) < 0 )
2755+ if (config_with_options (repo_config_callback , & config , NULL , repo ,
2756+ & opts ) < 0 )
24902757 /*
24912758 * config_with_options() normally returns only
24922759 * zero, as most errors are fatal, and
@@ -2499,6 +2766,8 @@ static void repo_read_config(struct repository *repo)
24992766 * immediately.
25002767 */
25012768 die (_ ("unknown error occurred while reading the configuration files" ));
2769+ check_deprecated_config (& config );
2770+ repo_config_release (& config );
25022771}
25032772
25042773static void git_config_check_init (struct repository * repo )
@@ -3185,6 +3454,11 @@ int repo_config_set_multivar_in_file_gently(struct repository *r,
31853454 char * contents = NULL ;
31863455 size_t contents_sz ;
31873456 struct config_store_data store = CONFIG_STORE_INIT ;
3457+ #ifdef WITH_BREAKING_CHANGES
3458+ bool saved_check_deprecated_config = r -> check_deprecated_config ;
3459+
3460+ r -> check_deprecated_config = false;
3461+ #endif /* WITH_BREAKING_CHANGES */
31883462
31893463 validate_comment_string (comment );
31903464
@@ -3415,6 +3689,9 @@ int repo_config_set_multivar_in_file_gently(struct repository *r,
34153689 if (in_fd >= 0 )
34163690 close (in_fd );
34173691 config_store_data_clear (& store );
3692+ #ifdef WITH_BREAKING_CHANGES
3693+ r -> check_deprecated_config = saved_check_deprecated_config ;
3694+ #endif /* WITH_BREAKING_CHANGES */
34183695 return ret ;
34193696
34203697write_err_out :
0 commit comments