@@ -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
@@ -178,6 +182,25 @@ static int option_parse_type(const struct option *opt, const char *arg,
178
182
return 0 ;
179
183
}
180
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
+
181
204
static struct option builtin_config_options [] = {
182
205
OPT_GROUP (N_ ("Config file location" )),
183
206
OPT_BOOL (0 , "global" , & use_global_config , N_ ("use global config file" )),
@@ -197,6 +220,18 @@ static struct option builtin_config_options[] = {
197
220
OPT_BIT (0 , "unset-all" , & actions , N_ ("remove all matches: name [value-regex]" ), ACTION_UNSET_ALL ),
198
221
OPT_BIT (0 , "rename-section" , & actions , N_ ("rename section: old-name new-name" ), ACTION_RENAME_SECTION ),
199
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 },
200
235
OPT_BIT ('l' , "list" , & actions , N_ ("list all" ), ACTION_LIST ),
201
236
OPT_BIT ('e' , "edit" , & actions , N_ ("open an editor" ), ACTION_EDIT ),
202
237
OPT_BIT (0 , "get-color" , & actions , N_ ("find the color configured: slot [default]" ), ACTION_GET_COLOR ),
@@ -426,6 +461,84 @@ static int get_value(const char *key_, const char *regex_)
426
461
return ret ;
427
462
}
428
463
464
+ struct move_config_cb {
465
+ struct string_list keys ;
466
+ const char * key ;
467
+ regex_t key_re ;
468
+ };
469
+
470
+ static int collect_move_config (const char * key , const char * value , void * cb )
471
+ {
472
+ struct move_config_cb * data = cb ;
473
+
474
+ switch (actions ) {
475
+ case ACTION_MOVE :
476
+ if (strcasecmp (data -> key , key ))
477
+ return 0 ;
478
+ break ;
479
+ case ACTION_MOVE_REGEXP :
480
+ if (regexec (& data -> key_re , key , 0 , NULL , 0 ))
481
+ return 0 ;
482
+ break ;
483
+ case ACTION_MOVE_GLOB :
484
+ if (wildmatch (data -> key , key , WM_CASEFOLD ))
485
+ return 0 ;
486
+ break ;
487
+ default :
488
+ BUG ("action %d cannot get here" , actions );
489
+ }
490
+
491
+ string_list_append (& data -> keys , key )-> util = xstrdup (value );
492
+ return 0 ;
493
+ }
494
+
495
+ static int move_config (const char * key )
496
+ {
497
+ struct move_config_cb cb ;
498
+ int i , ret = 0 ;
499
+
500
+ config_options .respect_includes = 0 ;
501
+ if (!move_source .file && !move_source .use_stdin && !move_source .blob )
502
+ die (_ ("unknown config source" ));
503
+
504
+ string_list_init (& cb .keys , 1 );
505
+ cb .key = key ;
506
+ if (actions == ACTION_MOVE_REGEXP &&
507
+ regcomp (& cb .key_re , key , REG_EXTENDED | REG_ICASE ))
508
+ die (_ ("invalid key pattern: %s" ), key );
509
+
510
+ config_with_options (collect_move_config , & cb ,
511
+ & move_source , & config_options );
512
+
513
+ for (i = 0 ; i < cb .keys .nr && !ret ; i ++ ) {
514
+ const char * key = cb .keys .items [i ].string ;
515
+ const char * value = cb .keys .items [i ].util ;
516
+ const char * dest = given_config_source .file ;
517
+
518
+ ret = git_config_set_multivar_in_file_gently (
519
+ dest , key , value , CONFIG_REGEX_NONE , 0 );
520
+ }
521
+
522
+ /*
523
+ * OK all keys have been copied successfully, time to delete
524
+ * old ones
525
+ */
526
+ if (!ret && move_source .file ) {
527
+ for (i = 0 ; i < cb .keys .nr ; i ++ ) {
528
+ const char * key = cb .keys .items [i ].string ;
529
+ const char * src = move_source .file ;
530
+
531
+ git_config_set_multivar_in_file_gently (
532
+ src , key , NULL , NULL , 1 );
533
+ }
534
+ }
535
+
536
+ string_list_clear (& cb .keys , 1 );
537
+ if (actions == ACTION_MOVE_REGEXP )
538
+ regfree (& cb .key_re );
539
+ return ret ;
540
+ }
541
+
429
542
static char * normalize_value (const char * key , const char * value )
430
543
{
431
544
if (!value )
@@ -862,6 +975,13 @@ int cmd_config(int argc, const char **argv, const char *prefix)
862
975
color_stdout_is_tty = git_config_bool ("command line" , argv [1 ]);
863
976
return get_colorbool (argv [0 ], argc == 2 );
864
977
}
978
+ else if (actions == ACTION_MOVE ||
979
+ actions == ACTION_MOVE_REGEXP ||
980
+ actions == ACTION_MOVE_GLOB ) {
981
+ check_write ();
982
+ check_argc (argc , 1 , 1 );
983
+ return move_config (argv [0 ]);
984
+ }
865
985
866
986
return 0 ;
867
987
}
0 commit comments