Merge branch 'rp/maintenance-qol'

'git maintenance register' is taught to write configuration to an
arbitrary path, and 'git for-each-repo' is taught to expand tilde
characters in paths.

* rp/maintenance-qol:
  builtin/gc.c: fix use-after-free in maintenance_unregister()
  maintenance --unregister: fix uninit'd data use & -Wdeclaration-after-statement
  maintenance: add option to register in a specific config
  for-each-repo: interpolate repo path arguments
This commit is contained in:
Junio C Hamano 2022-11-23 11:22:23 +09:00
commit 56a64fcdc3
5 changed files with 69 additions and 22 deletions

View File

@ -50,13 +50,13 @@ stop::
the background maintenance is restarted later.
register::
Initialize Git config values so any scheduled maintenance will
start running on this repository. This adds the repository to the
`maintenance.repo` config variable in the current user's global
config and enables some recommended configuration values for
`maintenance.<task>.schedule`. The tasks that are enabled are safe
for running in the background without disrupting foreground
processes.
Initialize Git config values so any scheduled maintenance will start
running on this repository. This adds the repository to the
`maintenance.repo` config variable in the current user's global config,
or the config specified by --config-file option, and enables some
recommended configuration values for `maintenance.<task>.schedule`. The
tasks that are enabled are safe for running in the background without
disrupting foreground processes.
+
The `register` subcommand will also set the `maintenance.strategy` config
value to `incremental`, if this value is not previously set. The

View File

@ -14,13 +14,16 @@ static int run_command_on_repo(const char *path, int argc, const char ** argv)
{
int i;
struct child_process child = CHILD_PROCESS_INIT;
char *abspath = interpolate_path(path, 0);
child.git_cmd = 1;
strvec_pushl(&child.args, "-C", path, NULL);
strvec_pushl(&child.args, "-C", abspath, NULL);
for (i = 0; i < argc; i++)
strvec_push(&child.args, argv[i]);
free(abspath);
return run_command(&child);
}

View File

@ -1480,13 +1480,15 @@ static char *get_maintpath(void)
}
static char const * const builtin_maintenance_register_usage[] = {
"git maintenance register",
"git maintenance register [--config-file <path>]",
NULL
};
static int maintenance_register(int argc, const char **argv, const char *prefix)
{
char *config_file = NULL;
struct option options[] = {
OPT_STRING(0, "config-file", &config_file, N_("file"), N_("use given config file")),
OPT_END(),
};
int found = 0;
@ -1523,12 +1525,16 @@ static int maintenance_register(int argc, const char **argv, const char *prefix)
if (!found) {
int rc;
char *user_config, *xdg_config;
git_global_config(&user_config, &xdg_config);
if (!user_config)
die(_("$HOME not set"));
char *user_config = NULL, *xdg_config = NULL;
if (!config_file) {
git_global_config(&user_config, &xdg_config);
config_file = user_config;
if (!user_config)
die(_("$HOME not set"));
}
rc = git_config_set_multivar_in_file_gently(
user_config, "maintenance.repo", maintpath,
config_file, "maintenance.repo", maintpath,
CONFIG_REGEX_NONE, 0);
free(user_config);
free(xdg_config);
@ -1543,14 +1549,16 @@ static int maintenance_register(int argc, const char **argv, const char *prefix)
}
static char const * const builtin_maintenance_unregister_usage[] = {
"git maintenance unregister [--force]",
"git maintenance unregister [--config-file <path>] [--force]",
NULL
};
static int maintenance_unregister(int argc, const char **argv, const char *prefix)
{
int force = 0;
char *config_file = NULL;
struct option options[] = {
OPT_STRING(0, "config-file", &config_file, N_("file"), N_("use given config file")),
OPT__FORCE(&force,
N_("return success even if repository was not registered"),
PARSE_OPT_NOCOMPLETE),
@ -1561,6 +1569,7 @@ static int maintenance_unregister(int argc, const char **argv, const char *prefi
int found = 0;
struct string_list_item *item;
const struct string_list *list;
struct config_set cs = { { 0 } };
argc = parse_options(argc, argv, prefix, options,
builtin_maintenance_unregister_usage, 0);
@ -1568,7 +1577,13 @@ static int maintenance_unregister(int argc, const char **argv, const char *prefi
usage_with_options(builtin_maintenance_unregister_usage,
options);
list = git_config_get_value_multi(key);
if (config_file) {
git_configset_init(&cs);
git_configset_add_file(&cs, config_file);
list = git_configset_get_value_multi(&cs, key);
} else {
list = git_config_get_value_multi(key);
}
if (list) {
for_each_string_list_item(item, list) {
if (!strcmp(maintpath, item->string)) {
@ -1580,12 +1595,15 @@ static int maintenance_unregister(int argc, const char **argv, const char *prefi
if (found) {
int rc;
char *user_config, *xdg_config;
git_global_config(&user_config, &xdg_config);
if (!user_config)
die(_("$HOME not set"));
char *user_config = NULL, *xdg_config = NULL;
if (!config_file) {
git_global_config(&user_config, &xdg_config);
config_file = user_config;
if (!user_config)
die(_("$HOME not set"));
}
rc = git_config_set_multivar_in_file_gently(
user_config, key, NULL, maintpath,
config_file, key, NULL, maintpath,
CONFIG_FLAGS_MULTI_REPLACE | CONFIG_FLAGS_FIXED_VALUE);
free(user_config);
free(xdg_config);
@ -1598,6 +1616,7 @@ static int maintenance_unregister(int argc, const char **argv, const char *prefi
die(_("repository '%s' is not registered"), maintpath);
}
git_configset_clear(&cs);
free(maintpath);
return 0;
}

View File

@ -8,9 +8,11 @@ test_expect_success 'run based on configured value' '
git init one &&
git init two &&
git init three &&
git init ~/four &&
git -C two commit --allow-empty -m "DID NOT RUN" &&
git config run.key "$TRASH_DIRECTORY/one" &&
git config --add run.key "$TRASH_DIRECTORY/three" &&
git config --add run.key "~/four" &&
git for-each-repo --config=run.key commit --allow-empty -m "ran" &&
git -C one log -1 --pretty=format:%s >message &&
grep ran message &&
@ -18,12 +20,16 @@ test_expect_success 'run based on configured value' '
! grep ran message &&
git -C three log -1 --pretty=format:%s >message &&
grep ran message &&
git -C ~/four log -1 --pretty=format:%s >message &&
grep ran message &&
git for-each-repo --config=run.key -- commit --allow-empty -m "ran again" &&
git -C one log -1 --pretty=format:%s >message &&
grep again message &&
git -C two log -1 --pretty=format:%s >message &&
! grep again message &&
git -C three log -1 --pretty=format:%s >message &&
grep again message &&
git -C ~/four log -1 --pretty=format:%s >message &&
grep again message
'

View File

@ -500,9 +500,28 @@ test_expect_success 'register and unregister' '
git config --global --get-all maintenance.repo >actual &&
test_cmp before actual &&
git config --file ./other --add maintenance.repo /existing1 &&
git config --file ./other --add maintenance.repo /existing2 &&
git config --file ./other --get-all maintenance.repo >before &&
git maintenance register --config-file ./other &&
test_cmp_config false maintenance.auto &&
git config --file ./other --get-all maintenance.repo >between &&
cp before expect &&
pwd >>expect &&
test_cmp expect between &&
git maintenance unregister --config-file ./other &&
git config --file ./other --get-all maintenance.repo >actual &&
test_cmp before actual &&
test_must_fail git maintenance unregister 2>err &&
grep "is not registered" err &&
git maintenance unregister --force
git maintenance unregister --force &&
test_must_fail git maintenance unregister --config-file ./other 2>err &&
grep "is not registered" err &&
git maintenance unregister --config-file ./other --force
'
test_expect_success !MINGW 'register and unregister with regex metacharacters' '