Skip to content

Commit 6f11fd5

Browse files
pcloudsgitster
authored andcommitted
config: add --move-to
This option can be used to move one or multiple variables from one place to another, e.g. to move some aliases from repo config to user config, or from repo config to per-worktree config. This will be useful for moving config variables around when extensions.worktreeConfig is enabled. E.g. git config --local --move-to --worktree core.worktree git config --local --move-glob-to --worktree 'submodule.*.*' The implementation is definitely not the best. We could for example lock both source and destination files before doing any update, and perhaps edit these files just once instead of once per key. But it adds a lot more complication to config update code. Let's stay with something simple for now. It's not worse than scripting using "git config". Optimization could be done later. Signed-off-by: Nguyễn Thái Ngọc Duy <[email protected]> Signed-off-by: Junio C Hamano <[email protected]>
1 parent a12c1ff commit 6f11fd5

File tree

4 files changed

+196
-6
lines changed

4 files changed

+196
-6
lines changed

Documentation/git-config.txt

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,9 @@ SYNOPSIS
2020
'git config' [<file-option>] --unset-all name [value_regex]
2121
'git config' [<file-option>] --rename-section old_name new_name
2222
'git config' [<file-option>] --remove-section name
23+
'git config' [<file-option>] --move-to name
24+
'git config' [<file-option>] --move-regexp-to name-regexp
25+
'git config' [<file-option>] --move-glob-to name-glob
2326
'git config' [<file-option>] [--show-origin] [-z|--null] [--name-only] -l | --list
2427
'git config' [<file-option>] --get-color name [default]
2528
'git config' [<file-option>] --get-colorbool name [stdout-is-tty]
@@ -161,6 +164,15 @@ See also <<FILES>>.
161164
--unset-all::
162165
Remove all lines matching the key from config file.
163166

167+
--move-to::
168+
--move-regexp-to::
169+
--move-glob-to::
170+
Move a config variable (or multiple variables matching the
171+
given regular expression or glob pattern) to a new file. Any
172+
option about the config file location after `--move-to` or
173+
`--move-to-regexp` specifies the move destination. Existing
174+
config of the same name remains.
175+
164176
-l::
165177
--list::
166178
List all variables set in config file, along with their values.

Documentation/git-worktree.txt

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -252,13 +252,17 @@ rev-parse --git-path config.worktree`. You can add or update
252252
configuration in this file with `git config --worktree`. Older Git
253253
versions will refuse to access repositories with this extension.
254254

255-
Note that in this file, the exception for `core.bare` and `core.worktree`
256-
is gone. If you have them in $GIT_DIR/config before, you must move
257-
them to the `config.worktree` of the main working tree. You may also
258-
take this opportunity to review and move other configuration that you
259-
do not want to share to all working trees:
255+
Note that in this file, the exception for `core.bare` and
256+
`core.worktree` is gone. If you have them in $GIT_DIR/config before,
257+
you must move them to the `config.worktree` of the main working tree.
260258

261-
- `core.worktree` and `core.bare` should never be shared
259+
------------
260+
$ git config --local --move-to --worktree core.bare
261+
$ git config --local --move-to --worktree core.worktree
262+
------------
263+
264+
You may also take this opportunity to review and move other
265+
configuration that you do not want to share to all working trees:
262266

263267
- `core.sparseCheckout` is recommended per working tree, unless you
264268
are sure you always use sparse checkout for all working trees.

builtin/config.c

Lines changed: 120 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ static char term = '\n';
2626

2727
static int use_global_config, use_system_config, use_local_config;
2828
static int use_worktree_config;
29+
static struct git_config_source move_source;
2930
static struct git_config_source given_config_source;
3031
static int actions, type;
3132
static char *default_value;
@@ -50,6 +51,9 @@ static int show_origin;
5051
#define ACTION_GET_COLOR (1<<13)
5152
#define ACTION_GET_COLORBOOL (1<<14)
5253
#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)
5357

5458
/*
5559
* 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,
178182
return 0;
179183
}
180184

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+
181204
static struct option builtin_config_options[] = {
182205
OPT_GROUP(N_("Config file location")),
183206
OPT_BOOL(0, "global", &use_global_config, N_("use global config file")),
@@ -197,6 +220,18 @@ static struct option builtin_config_options[] = {
197220
OPT_BIT(0, "unset-all", &actions, N_("remove all matches: name [value-regex]"), ACTION_UNSET_ALL),
198221
OPT_BIT(0, "rename-section", &actions, N_("rename section: old-name new-name"), ACTION_RENAME_SECTION),
199222
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 },
200235
OPT_BIT('l', "list", &actions, N_("list all"), ACTION_LIST),
201236
OPT_BIT('e', "edit", &actions, N_("open an editor"), ACTION_EDIT),
202237
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_)
426461
return ret;
427462
}
428463

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+
429542
static char *normalize_value(const char *key, const char *value)
430543
{
431544
if (!value)
@@ -862,6 +975,13 @@ int cmd_config(int argc, const char **argv, const char *prefix)
862975
color_stdout_is_tty = git_config_bool("command line", argv[1]);
863976
return get_colorbool(argv[0], argc == 2);
864977
}
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+
}
865985

866986
return 0;
867987
}

t/t1300-config.sh

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1844,4 +1844,58 @@ test_expect_success '--replace-all does not invent newlines' '
18441844
test_cmp expect .git/config
18451845
'
18461846

1847+
test_expect_success '--move-to moves keys' '
1848+
test_when_finished rm -f dest &&
1849+
git config single.foo bar &&
1850+
git config multi.foo bar1 &&
1851+
git config --add multi.foo bar2 &&
1852+
git config --local --move-to -f dest SINGLE.foo &&
1853+
! git config single.foo &&
1854+
test_cmp_config bar -f dest single.foo &&
1855+
git config --local --move-to -f dest multi.FOO &&
1856+
! git config multi.foo &&
1857+
git config -f dest --get-all multi.foo | sort >actual &&
1858+
cat >expected <<-\EOF &&
1859+
bar1
1860+
bar2
1861+
EOF
1862+
test_cmp expected actual
1863+
'
1864+
1865+
test_expect_success '--move-regexp-to moves keys' '
1866+
test_when_finished rm -f dest &&
1867+
git config single.foo bar &&
1868+
git config multi.foo bar1 &&
1869+
git config --add multi.foo bar2 &&
1870+
git config --local --move-regexp-to -f dest "S.*OO" &&
1871+
! git config single.foo &&
1872+
test_cmp_config bar -f dest single.foo &&
1873+
git config --local --move-regexp-to -f dest ^multi &&
1874+
! git config multi.foo &&
1875+
git config -f dest --get-all multi.foo | sort >actual &&
1876+
cat >expected <<-\EOF &&
1877+
bar1
1878+
bar2
1879+
EOF
1880+
test_cmp expected actual
1881+
'
1882+
1883+
test_expect_success '--move-glob-to moves keys' '
1884+
test_when_finished rm -f dest &&
1885+
git config single.foo bar &&
1886+
git config multi.foo bar1 &&
1887+
git config --add multi.foo bar2 &&
1888+
git config --local --move-glob-to -f dest "SINGLE.*" &&
1889+
! git config single.foo &&
1890+
test_cmp_config bar -f dest single.foo &&
1891+
git config --local --move-glob-to -f dest "m*.foo" &&
1892+
! git config multi.foo &&
1893+
git config -f dest --get-all multi.foo | sort >actual &&
1894+
cat >expected <<-\EOF &&
1895+
bar1
1896+
bar2
1897+
EOF
1898+
test_cmp expected actual
1899+
'
1900+
18471901
test_done

0 commit comments

Comments
 (0)