maintenance: fix incorrect `maintenance.repo` path with bare repository

The periodic maintenance tasks configured by `git maintenance start` invoke `git for-each-repo` to run `git maintenance run` on each path specified by the multi-value global configuration variable `maintenance.repo`. Because `git for-each-repo` will likely be run outside of the repositories which require periodic maintenance, it is mandatory that the repository paths specified by `maintenance.repo` are absolute. Unfortunately, however, `git maintenance register` does nothing to ensure that the paths it assigns to `maintenance.repo` are indeed absolute, and may in fact -- especially in the case of a bare repository -- assign a relative path to `maintenance.repo` instead. Fix this problem by converting all paths to absolute before assigning them to `maintenance.repo`. While at it, also fix `git maintenance unregister` to convert paths to absolute, as well, in order to ensure that it can correctly remove from `maintenance.repo` a path assigned via `git maintenance register`. Reported-by: Clement Moyroud <clement.moyroud@gmail.com> Signed-off-by: Eric Sunshine <sunshine@sunshineco.com> Signed-off-by: Junio C Hamano <gitster@pobox.com>

Eric Sunshine committed Feb 23, 2021 at 02:31 UTC 26c7974376fad730ba5bc1926afe7d26ba8d91e0
2 files changed +46 -17
builtin/gc.c
+33 -17
@@ -1461,11 +1461,23 @@ static int maintenance_run(int argc, const char **argv, const char *prefix)
1461 return maintenance_run_tasks(&opts);
1462 }
1463
1464 +static char *get_maintpath(void)
1465 +{
1466 + struct strbuf sb = STRBUF_INIT;
1467 + const char *p = the_repository->worktree ?
1468 + the_repository->worktree : the_repository->gitdir;
1469 +
1470 + strbuf_realpath(&sb, p, 1);
1471 + return strbuf_detach(&sb, NULL);
1472 +}
1473 +
1474 static int maintenance_register(void)
1475 {
1476 + int rc;
1477 char *config_value;
1478 struct child_process config_set = CHILD_PROCESS_INIT;
1479 struct child_process config_get = CHILD_PROCESS_INIT;
1480 + char *maintpath = get_maintpath();
1481
1482 /* Disable foreground maintenance */
1483 git_config_set("maintenance.auto", "false");
@@ -1478,40 +1490,44 @@ static int maintenance_register(void)
1490
1491 config_get.git_cmd = 1;
1492 strvec_pushl(&config_get.args, "config", "--global", "--get",
1481 - "--fixed-value", "maintenance.repo",
1482 - the_repository->worktree ? the_repository->worktree
1483 - : the_repository->gitdir,
1484 - NULL);
1493 + "--fixed-value", "maintenance.repo", maintpath, NULL);
1494 config_get.out = -1;
1495
1487 - if (start_command(&config_get))
1488 - return error(_("failed to run 'git config'"));
1496 + if (start_command(&config_get)) {
1497 + rc = error(_("failed to run 'git config'"));
1498 + goto done;
1499 + }
1500
1501 /* We already have this value in our config! */
1491 - if (!finish_command(&config_get))
1492 - return 0;
1502 + if (!finish_command(&config_get)) {
1503 + rc = 0;
1504 + goto done;
1505 + }
1506
1507 config_set.git_cmd = 1;
1508 strvec_pushl(&config_set.args, "config", "--add", "--global", "maintenance.repo",
1496 - the_repository->worktree ? the_repository->worktree
1497 - : the_repository->gitdir,
1498 - NULL);
1509 + maintpath, NULL);
1510 +
1511 + rc = run_command(&config_set);
1512
1500 - return run_command(&config_set);
1513 +done:
1514 + free(maintpath);
1515 + return rc;
1516 }
1517
1518 static int maintenance_unregister(void)
1519 {
1520 + int rc;
1521 struct child_process config_unset = CHILD_PROCESS_INIT;
1522 + char *maintpath = get_maintpath();
1523
1524 config_unset.git_cmd = 1;
1525 strvec_pushl(&config_unset.args, "config", "--global", "--unset",
1509 - "--fixed-value", "maintenance.repo",
1510 - the_repository->worktree ? the_repository->worktree
1511 - : the_repository->gitdir,
1512 - NULL);
1526 + "--fixed-value", "maintenance.repo", maintpath, NULL);
1527
1514 - return run_command(&config_unset);
1528 + rc = run_command(&config_unset);
1529 + free(maintpath);
1530 + return rc;
1531 }
1532
1533 static const char *get_frequency(enum schedule_priority schedule)
t/t7900-maintenance.sh
+13
@@ -632,4 +632,17 @@ test_expect_success 'fails when running outside of a repository' '
632 nongit test_must_fail git maintenance unregister
633 '
634
635 +test_expect_success 'register and unregister bare repo' '
636 + test_when_finished "git config --global --unset-all maintenance.repo || :" &&
637 + test_might_fail git config --global --unset-all maintenance.repo &&
638 + git init --bare barerepo &&
639 + (
640 + cd barerepo &&
641 + git maintenance register &&
642 + git config --get --global --fixed-value maintenance.repo "$(pwd)" &&
643 + git maintenance unregister &&
644 + test_must_fail git config --global --get-all maintenance.repo
645 + )
646 +'
647 +
648 test_done