@@ -26,6 +26,7 @@ static char term = '\n';
26
26
27
27
static int use_global_config , use_system_config , use_local_config ;
28
28
static int use_worktree_config ;
29
+ static struct git_config_source move_source ;
29
30
static struct git_config_source given_config_source ;
30
31
static int actions , type ;
31
32
static char * default_value ;
@@ -50,6 +51,9 @@ static int show_origin;
50
51
#define ACTION_GET_COLOR (1<<13)
51
52
#define ACTION_GET_COLORBOOL (1<<14)
52
53
#define ACTION_GET_URLMATCH (1<<15)
54
+ #define ACTION_MOVE (1<<16)
55
+ #define ACTION_MOVE_REGEXP (1<<17)
56
+ #define ACTION_MOVE_GLOB (1<<18)
53
57
54
58
/*
55
59
* The actions "ACTION_LIST | ACTION_GET_*" which may produce more than
@@ -71,6 +75,64 @@ static int show_origin;
71
75
72
76
static NORETURN void usage_builtin_config (void );
73
77
78
+ static void set_config_source_file (void )
79
+ {
80
+ int nongit = !startup_info -> have_repository ;
81
+
82
+ if (use_global_config + use_system_config + use_local_config +
83
+ use_worktree_config +
84
+ !!given_config_source .file + !!given_config_source .blob > 1 )
85
+ die (_ ("only one config file at a time" ));
86
+
87
+ if (use_local_config && nongit )
88
+ die (_ ("--local can only be used inside a git repository" ));
89
+
90
+ if (given_config_source .blob && nongit )
91
+ die (_ ("--blob can only be used inside a git repository" ));
92
+
93
+ if (given_config_source .file &&
94
+ !strcmp (given_config_source .file , "-" )) {
95
+ given_config_source .file = NULL ;
96
+ given_config_source .use_stdin = 1 ;
97
+ }
98
+
99
+ if (use_global_config ) {
100
+ char * user_config = expand_user_path ("~/.gitconfig" , 0 );
101
+ char * xdg_config = xdg_config_home ("config" );
102
+
103
+ if (!user_config )
104
+ /*
105
+ * It is unknown if HOME/.gitconfig exists, so
106
+ * we do not know if we should write to XDG
107
+ * location; error out even if XDG_CONFIG_HOME
108
+ * is set and points at a sane location.
109
+ */
110
+ die (_ ("$HOME not set" ));
111
+
112
+ if (access_or_warn (user_config , R_OK , 0 ) &&
113
+ xdg_config && !access_or_warn (xdg_config , R_OK , 0 )) {
114
+ given_config_source .file = xdg_config ;
115
+ free (user_config );
116
+ } else {
117
+ given_config_source .file = user_config ;
118
+ free (xdg_config );
119
+ }
120
+ }
121
+ else if (use_system_config )
122
+ given_config_source .file = git_etc_gitconfig ();
123
+ else if (use_local_config )
124
+ given_config_source .file = git_pathdup ("config" );
125
+ else if (use_worktree_config ) {
126
+ given_config_source .file = get_worktree_config (the_repository );
127
+ if (!given_config_source .file )
128
+ die (_ ("--worktree cannot be used with multiple "
129
+ "working trees unless the config\n"
130
+ "extension worktreeConfig is enabled. "
131
+ "Please read \"CONFIGURATION FILE\"\n"
132
+ "section in \"git help worktree\" for details" ));
133
+ }
134
+ }
135
+
74
136
static int option_parse_type (const struct option * opt , const char * arg ,
75
137
int unset )
76
138
{
@@ -120,13 +182,32 @@ static int option_parse_type(const struct option *opt, const char *arg,
120
182
return 0 ;
121
183
}
122
184
185
+ static int option_move_cb (const struct option * opt ,
186
+ const char * arg , int unset )
187
+ {
188
+ BUG_ON_OPT_NEG (unset );
189
+ BUG_ON_OPT_ARG (arg );
190
+
191
+ set_config_source_file ();
192
+ memcpy (& move_source , & given_config_source , sizeof (move_source ));
193
+
194
+ memset (& given_config_source , 0 , sizeof (given_config_source ));
195
+ use_global_config = 0 ;
196
+ use_system_config = 0 ;
197
+ use_local_config = 0 ;
198
+ use_worktree_config = 0 ;
199
+
200
+ actions = opt -> defval ;
201
+ return 0 ;
202
+ }
203
+
123
204
static struct option builtin_config_options [] = {
124
205
OPT_GROUP (N_ ("Config file location" )),
125
206
OPT_BOOL (0 , "global" , & use_global_config , N_ ("use global config file" )),
126
207
OPT_BOOL (0 , "system" , & use_system_config , N_ ("use system config file" )),
127
208
OPT_BOOL (0 , "local" , & use_local_config , N_ ("use repository config file" )),
128
209
OPT_BOOL (0 , "worktree" , & use_worktree_config , N_ ("use per-worktree config file" )),
129
- OPT_STRING ('f' , "file" , & given_config_source .file , N_ ( "file" ) , N_ ("use given config file" )),
210
+ OPT_FILENAME ('f' , "file" , & given_config_source .file , N_ ("use given config file" )),
130
211
OPT_STRING (0 , "blob" , & given_config_source .blob , N_ ("blob-id" ), N_ ("read config from given blob object" )),
131
212
OPT_GROUP (N_ ("Action" )),
132
213
OPT_BIT (0 , "get" , & actions , N_ ("get value: name [value-regex]" ), ACTION_GET ),
@@ -139,6 +220,18 @@ static struct option builtin_config_options[] = {
139
220
OPT_BIT (0 , "unset-all" , & actions , N_ ("remove all matches: name [value-regex]" ), ACTION_UNSET_ALL ),
140
221
OPT_BIT (0 , "rename-section" , & actions , N_ ("rename section: old-name new-name" ), ACTION_RENAME_SECTION ),
141
222
OPT_BIT (0 , "remove-section" , & actions , N_ ("remove a section: name" ), ACTION_REMOVE_SECTION ),
223
+ { OPTION_CALLBACK , 0 , "move-to" , NULL , NULL ,
224
+ N_ ("move a variable to a different config file" ),
225
+ PARSE_OPT_NONEG | PARSE_OPT_NOARG ,
226
+ option_move_cb , ACTION_MOVE },
227
+ { OPTION_CALLBACK , 0 , "move-regexp-to" , NULL , NULL ,
228
+ N_ ("move matching variables to a different config file" ),
229
+ PARSE_OPT_NONEG | PARSE_OPT_NOARG ,
230
+ option_move_cb , ACTION_MOVE_REGEXP },
231
+ { OPTION_CALLBACK , 0 , "move-glob-to" , NULL , NULL ,
232
+ N_ ("move matching variables to a different config file" ),
233
+ PARSE_OPT_NONEG | PARSE_OPT_NOARG ,
234
+ option_move_cb , ACTION_MOVE_GLOB },
142
235
OPT_BIT ('l' , "list" , & actions , N_ ("list all" ), ACTION_LIST ),
143
236
OPT_BIT ('e' , "edit" , & actions , N_ ("open an editor" ), ACTION_EDIT ),
144
237
OPT_BIT (0 , "get-color" , & actions , N_ ("find the color configured: slot [default]" ), ACTION_GET_COLOR ),
@@ -369,6 +462,84 @@ static int get_value(const char *key_, const char *regex_)
369
462
return ret ;
370
463
}
371
464
465
+ struct move_config_cb {
466
+ struct string_list keys ;
467
+ const char * key ;
468
+ regex_t key_re ;
469
+ };
470
+
471
+ static int collect_move_config (const char * key , const char * value , void * cb )
472
+ {
473
+ struct move_config_cb * data = cb ;
474
+
475
+ switch (actions ) {
476
+ case ACTION_MOVE :
477
+ if (strcasecmp (data -> key , key ))
478
+ return 0 ;
479
+ break ;
480
+ case ACTION_MOVE_REGEXP :
481
+ if (regexec (& data -> key_re , key , 0 , NULL , 0 ))
482
+ return 0 ;
483
+ break ;
484
+ case ACTION_MOVE_GLOB :
485
+ if (wildmatch (data -> key , key , WM_CASEFOLD ))
486
+ return 0 ;
487
+ break ;
488
+ default :
489
+ BUG ("action %d cannot get here" , actions );
490
+ }
491
+
492
+ string_list_append (& data -> keys , key )-> util = xstrdup (value );
493
+ return 0 ;
494
+ }
495
+
496
+ static int move_config (const char * key )
497
+ {
498
+ struct move_config_cb cb ;
499
+ int i , ret = 0 ;
500
+
501
+ config_options .respect_includes = 0 ;
502
+ if (!move_source .file && !move_source .use_stdin && !move_source .blob )
503
+ die (_ ("unknown config source" ));
504
+
505
+ string_list_init (& cb .keys , 1 );
506
+ cb .key = key ;
507
+ if (actions == ACTION_MOVE_REGEXP &&
508
+ regcomp (& cb .key_re , key , REG_EXTENDED | REG_ICASE ))
509
+ die (_ ("invalid key pattern: %s" ), key );
510
+
511
+ config_with_options (collect_move_config , & cb ,
512
+ & move_source , & config_options );
513
+
514
+ for (i = 0 ; i < cb .keys .nr && !ret ; i ++ ) {
515
+ const char * key = cb .keys .items [i ].string ;
516
+ const char * value = cb .keys .items [i ].util ;
517
+ const char * dest = given_config_source .file ;
518
+
519
+ ret = git_config_set_multivar_in_file_gently (
520
+ dest , key , value , CONFIG_REGEX_NONE , 0 );
521
+ }
522
+
523
+ /*
524
+ * OK all keys have been copied successfully, time to delete
525
+ * old ones
526
+ */
527
+ if (!ret && move_source .file ) {
528
+ for (i = 0 ; i < cb .keys .nr ; i ++ ) {
529
+ const char * key = cb .keys .items [i ].string ;
530
+ const char * src = move_source .file ;
531
+
532
+ git_config_set_multivar_in_file_gently (
533
+ src , key , NULL , NULL , 1 );
534
+ }
535
+ }
536
+
537
+ string_list_clear (& cb .keys , 1 );
538
+ if (actions == ACTION_MOVE_REGEXP )
539
+ regfree (& cb .key_re );
540
+ return ret ;
541
+ }
542
+
372
543
static char * normalize_value (const char * key , const char * value )
373
544
{
374
545
if (!value )
@@ -605,69 +776,7 @@ int cmd_config(int argc, const char **argv, const char *prefix)
605
776
builtin_config_usage ,
606
777
PARSE_OPT_STOP_AT_NON_OPTION );
607
778
608
- if (use_global_config + use_system_config + use_local_config +
609
- use_worktree_config +
610
- !!given_config_source .file + !!given_config_source .blob > 1 ) {
611
- error (_ ("only one config file at a time" ));
612
- usage_builtin_config ();
613
- }
614
-
615
- if (use_local_config && nongit )
616
- die (_ ("--local can only be used inside a git repository" ));
617
-
618
- if (given_config_source .blob && nongit )
619
- die (_ ("--blob can only be used inside a git repository" ));
620
-
621
- if (given_config_source .file &&
622
- !strcmp (given_config_source .file , "-" )) {
623
- given_config_source .file = NULL ;
624
- given_config_source .use_stdin = 1 ;
625
- }
626
-
627
- if (use_global_config ) {
628
- char * user_config = expand_user_path ("~/.gitconfig" , 0 );
629
- char * xdg_config = xdg_config_home ("config" );
630
-
631
- if (!user_config )
632
- /*
633
- * It is unknown if HOME/.gitconfig exists, so
634
- * we do not know if we should write to XDG
635
- * location; error out even if XDG_CONFIG_HOME
636
- * is set and points at a sane location.
637
- */
638
- die (_ ("$HOME not set" ));
639
-
640
- if (access_or_warn (user_config , R_OK , 0 ) &&
641
- xdg_config && !access_or_warn (xdg_config , R_OK , 0 )) {
642
- given_config_source .file = xdg_config ;
643
- free (user_config );
644
- } else {
645
- given_config_source .file = user_config ;
646
- free (xdg_config );
647
- }
648
- }
649
- else if (use_system_config )
650
- given_config_source .file = git_etc_gitconfig ();
651
- else if (use_local_config )
652
- given_config_source .file = git_pathdup ("config" );
653
- else if (use_worktree_config ) {
654
- struct worktree * * worktrees = get_worktrees (0 );
655
- if (repository_format_worktree_config )
656
- given_config_source .file = git_pathdup ("config.worktree" );
657
- else if (worktrees [0 ] && worktrees [1 ])
658
- die (_ ("--worktree cannot be used with multiple "
659
- "working trees unless the config\n"
660
- "extension worktreeConfig is enabled. "
661
- "Please read \"CONFIGURATION FILE\"\n"
662
- "section in \"git help worktree\" for details" ));
663
- else
664
- given_config_source .file = git_pathdup ("config" );
665
- free_worktrees (worktrees );
666
- } else if (given_config_source .file ) {
667
- if (!is_absolute_path (given_config_source .file ) && prefix )
668
- given_config_source .file =
669
- prefix_filename (prefix , given_config_source .file );
670
- }
779
+ set_config_source_file ();
671
780
672
781
if (respect_includes_opt == -1 )
673
782
config_options .respect_includes = !given_config_source .file ;
@@ -867,6 +976,13 @@ int cmd_config(int argc, const char **argv, const char *prefix)
867
976
color_stdout_is_tty = git_config_bool ("command line" , argv [1 ]);
868
977
return get_colorbool (argv [0 ], argc == 2 );
869
978
}
979
+ else if (actions == ACTION_MOVE ||
980
+ actions == ACTION_MOVE_REGEXP ||
981
+ actions == ACTION_MOVE_GLOB ) {
982
+ check_write ();
983
+ check_argc (argc , 1 , 1 );
984
+ return move_config (argv [0 ]);
985
+ }
870
986
871
987
return 0 ;
872
988
}
0 commit comments