Skip to content

Commit

Permalink
Merge branch 'ds/scalar-unregister-idempotent'
Browse files Browse the repository at this point in the history
"scalar unregister" in a repository that is already been
unregistered reported an error.

* ds/scalar-unregister-idempotent:
  string-list: document iterator behavior on NULL input
  gc: replace config subprocesses with API calls
  scalar: make 'unregister' idempotent
  maintenance: add 'unregister --force'
  • Loading branch information
gitster committed Oct 10, 2022
2 parents dc6dd55 + d151f0c commit 4b4d97c
Show file tree
Hide file tree
Showing 6 changed files with 96 additions and 36 deletions.
6 changes: 5 additions & 1 deletion Documentation/git-maintenance.txt
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ SYNOPSIS
[verse]
'git maintenance' run [<options>]
'git maintenance' start [--scheduler=<scheduler>]
'git maintenance' (stop|register|unregister)
'git maintenance' (stop|register|unregister) [<options>]


DESCRIPTION
Expand Down Expand Up @@ -79,6 +79,10 @@ unregister::
Remove the current repository from background maintenance. This
only removes the repository from the configured list. It does not
stop the background maintenance processes from running.
+
The `unregister` subcommand will report an error if the current repository
is not already registered. Use the `--force` option to return success even
when the current repository is not registered.

TASKS
-----
Expand Down
98 changes: 67 additions & 31 deletions builtin/gc.c
Original file line number Diff line number Diff line change
Expand Up @@ -1470,11 +1470,12 @@ static int maintenance_register(int argc, const char **argv, const char *prefix)
struct option options[] = {
OPT_END(),
};
int rc;
int found = 0;
const char *key = "maintenance.repo";
char *config_value;
struct child_process config_set = CHILD_PROCESS_INIT;
struct child_process config_get = CHILD_PROCESS_INIT;
char *maintpath = get_maintpath();
struct string_list_item *item;
const struct string_list *list;

argc = parse_options(argc, argv, prefix, options,
builtin_maintenance_register_usage, 0);
Expand All @@ -1491,60 +1492,95 @@ static int maintenance_register(int argc, const char **argv, const char *prefix)
else
git_config_set("maintenance.strategy", "incremental");

config_get.git_cmd = 1;
strvec_pushl(&config_get.args, "config", "--global", "--get",
"--fixed-value", "maintenance.repo", maintpath, NULL);
config_get.out = -1;

if (start_command(&config_get)) {
rc = error(_("failed to run 'git config'"));
goto done;
list = git_config_get_value_multi(key);
if (list) {
for_each_string_list_item(item, list) {
if (!strcmp(maintpath, item->string)) {
found = 1;
break;
}
}
}

/* We already have this value in our config! */
if (!finish_command(&config_get)) {
rc = 0;
goto done;
if (!found) {
int rc;
char *user_config, *xdg_config;
git_global_config(&user_config, &xdg_config);
if (!user_config)
die(_("$HOME not set"));
rc = git_config_set_multivar_in_file_gently(
user_config, "maintenance.repo", maintpath,
CONFIG_REGEX_NONE, 0);
free(user_config);
free(xdg_config);

if (rc)
die(_("unable to add '%s' value of '%s'"),
key, maintpath);
}

config_set.git_cmd = 1;
strvec_pushl(&config_set.args, "config", "--add", "--global", "maintenance.repo",
maintpath, NULL);

rc = run_command(&config_set);

done:
free(maintpath);
return rc;
return 0;
}

static char const * const builtin_maintenance_unregister_usage[] = {
"git maintenance unregister",
"git maintenance unregister [--force]",
NULL
};

static int maintenance_unregister(int argc, const char **argv, const char *prefix)
{
int force = 0;
struct option options[] = {
OPT__FORCE(&force,
N_("return success even if repository was not registered"),
PARSE_OPT_NOCOMPLETE),
OPT_END(),
};
int rc;
struct child_process config_unset = CHILD_PROCESS_INIT;
const char *key = "maintenance.repo";
char *maintpath = get_maintpath();
int found = 0;
struct string_list_item *item;
const struct string_list *list;

argc = parse_options(argc, argv, prefix, options,
builtin_maintenance_unregister_usage, 0);
if (argc)
usage_with_options(builtin_maintenance_unregister_usage,
options);

config_unset.git_cmd = 1;
strvec_pushl(&config_unset.args, "config", "--global", "--unset",
"--fixed-value", "maintenance.repo", maintpath, NULL);
list = git_config_get_value_multi(key);
if (list) {
for_each_string_list_item(item, list) {
if (!strcmp(maintpath, item->string)) {
found = 1;
break;
}
}
}

if (found) {
int rc;
char *user_config, *xdg_config;
git_global_config(&user_config, &xdg_config);
if (!user_config)
die(_("$HOME not set"));
rc = git_config_set_multivar_in_file_gently(
user_config, key, NULL, maintpath,
CONFIG_FLAGS_MULTI_REPLACE | CONFIG_FLAGS_FIXED_VALUE);
free(user_config);
free(xdg_config);

if (rc &&
(!force || rc == CONFIG_NOTHING_SET))
die(_("unable to unset '%s' value of '%s'"),
key, maintpath);
} else if (!force) {
die(_("repository '%s' is not registered"), maintpath);
}

rc = run_command(&config_unset);
free(maintpath);
return rc;
return 0;
}

static const char *get_frequency(enum schedule_priority schedule)
Expand Down
5 changes: 4 additions & 1 deletion scalar.c
Original file line number Diff line number Diff line change
Expand Up @@ -207,7 +207,10 @@ static int set_recommended_config(int reconfigure)

static int toggle_maintenance(int enable)
{
return run_git("maintenance", enable ? "start" : "unregister", NULL);
return run_git("maintenance",
enable ? "start" : "unregister",
enable ? NULL : "--force",
NULL);
}

static int add_or_remove_enlistment(int add)
Expand Down
7 changes: 6 additions & 1 deletion string-list.h
Original file line number Diff line number Diff line change
Expand Up @@ -141,7 +141,12 @@ void string_list_clear_func(struct string_list *list, string_list_clear_func_t c
int for_each_string_list(struct string_list *list,
string_list_each_func_t func, void *cb_data);

/** Iterate over each item, as a macro. */
/**
* Iterate over each item, as a macro.
*
* Be sure that 'list' is non-NULL. The macro cannot perform NULL
* checks due to -Werror=address errors.
*/
#define for_each_string_list_item(item,list) \
for (item = (list)->items; \
item && item < (list)->items + (list)->nr; \
Expand Down
11 changes: 10 additions & 1 deletion t/t7900-maintenance.sh
Original file line number Diff line number Diff line change
Expand Up @@ -480,6 +480,11 @@ test_expect_success 'maintenance.strategy inheritance' '

test_expect_success 'register and unregister' '
test_when_finished git config --global --unset-all maintenance.repo &&
test_must_fail git maintenance unregister 2>err &&
grep "is not registered" err &&
git maintenance unregister --force &&
git config --global --add maintenance.repo /existing1 &&
git config --global --add maintenance.repo /existing2 &&
git config --global --get-all maintenance.repo >before &&
Expand All @@ -493,7 +498,11 @@ test_expect_success 'register and unregister' '
git maintenance unregister &&
git config --global --get-all maintenance.repo >actual &&
test_cmp before actual
test_cmp before actual &&
test_must_fail git maintenance unregister 2>err &&
grep "is not registered" err &&
git maintenance unregister --force
'

test_expect_success !MINGW 'register and unregister with regex metacharacters' '
Expand Down
5 changes: 4 additions & 1 deletion t/t9210-scalar.sh
Original file line number Diff line number Diff line change
Expand Up @@ -116,7 +116,10 @@ test_expect_success 'scalar unregister' '
test_must_fail git config --get --global --fixed-value \
maintenance.repo "$(pwd)/vanish/src" &&
scalar list >scalar.repos &&
! grep -F "$(pwd)/vanish/src" scalar.repos
! grep -F "$(pwd)/vanish/src" scalar.repos &&
# scalar unregister should be idempotent
scalar unregister vanish
'

test_expect_success 'set up repository to clone' '
Expand Down

0 comments on commit 4b4d97c

Please sign in to comment.