From patchwork Wed Aug 19 17:16:42 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Philippe Blain via GitGitGadget X-Patchwork-Id: 11724691 Return-Path: Received: from mail.kernel.org (pdx-korg-mail-1.web.codeaurora.org [172.30.200.123]) by pdx-korg-patchwork-2.web.codeaurora.org (Postfix) with ESMTP id 260EF618 for ; Wed, 19 Aug 2020 17:17:05 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by mail.kernel.org (Postfix) with ESMTP id 0DB5020888 for ; Wed, 19 Aug 2020 17:17:05 +0000 (UTC) Authentication-Results: mail.kernel.org; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b="Sigo3c3G" Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1726698AbgHSRRD (ORCPT ); Wed, 19 Aug 2020 13:17:03 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:48138 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1726466AbgHSRQ5 (ORCPT ); Wed, 19 Aug 2020 13:16:57 -0400 Received: from mail-wm1-x341.google.com (mail-wm1-x341.google.com [IPv6:2a00:1450:4864:20::341]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 89A4AC061383 for ; Wed, 19 Aug 2020 10:16:56 -0700 (PDT) Received: by mail-wm1-x341.google.com with SMTP id p14so2832502wmg.1 for ; Wed, 19 Aug 2020 10:16:56 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20161025; h=message-id:in-reply-to:references:from:date:subject:fcc :content-transfer-encoding:mime-version:to:cc; bh=XENtOmnJI6XILr8KzgGQwXoUMksy7K+nexUxsFsywUU=; b=Sigo3c3GLSxjTN+L9jBePeikh+jRmH4C5wT1wf9o5JMtbFhp/2ZypFbBzpiTwwIKCq ++8QNOpKzb9JES1XaD+DKjGeI6cBi3Uae5l98rV2/cs0+5vw+CYHcPSvdgK/JC8w9wPU DDQAJz6px18pOj4N7yvQ+9CGH8c5VqHbHqvD03BYnngywfb92cQS2jFxDP1H38Z2oi4G Dc6sZembki15c19gIXaSjBGnRPItg+fYEFs40BhzqabN0prjzK32CHAI8ZCUX7k05rN4 ZS5yRjtq6C3FThD6Qz+9qvkT6jXFDosm6jee2+zS/ILyvJWZM8E/1WQa9GQef46gdrGJ guFg== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:message-id:in-reply-to:references:from:date :subject:fcc:content-transfer-encoding:mime-version:to:cc; bh=XENtOmnJI6XILr8KzgGQwXoUMksy7K+nexUxsFsywUU=; b=D3YVqaro19GtUAw39lMs41bQmmg93F1T7T6uMOlgTZ0kuuN5eLYYI+/qHoYYKURzbb dNBwEDW2dKqZUuRHLRgB9Z6AThmPefFghHOX0P1Q5E9JT9M5FFwt8NdZsze3E8DOZl2O 0dK4/4V5PUxwUW1N3poXp2MOd74VFceLVtb2QUnQZG3pd4xwuGc7ny+XoEmtYya/EVX8 cmUPIL/WSyr4JvLQU52mrAB+7tvK+ZZQdQvXJC/q7LAkk+YEUarSeURnnawCz2hqF/Hv Br9/KL4Ls6SBuCT+CfdlgG/NNZ0g+RcmzU+v9pnt2XgCsAd0ugNbJjxcH4W0J9xepCvT XD1g== X-Gm-Message-State: AOAM533iKD5Pz98FtJz7VQp1VVV7lPq/nBH0CxLs05TIGB3WDdQAhQk4 njRdbz5yGSc7juG6Vb1GRQcBkWLlwAY= X-Google-Smtp-Source: ABdhPJysMYJWLNS6NlIxh/zCbKKN3SLeOhIf7Pk72i2VfSinAyf4JiqJWSf01L+CHeiPgkFvN0fH0A== X-Received: by 2002:a1c:9942:: with SMTP id b63mr5502726wme.12.1597857415061; Wed, 19 Aug 2020 10:16:55 -0700 (PDT) Received: from [127.0.0.1] ([13.74.141.28]) by smtp.gmail.com with ESMTPSA id c194sm6900765wme.8.2020.08.19.10.16.54 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Wed, 19 Aug 2020 10:16:54 -0700 (PDT) Message-Id: <90de25d1287595fdedc9dcd194a2e0ac120d4aad.1597857409.git.gitgitgadget@gmail.com> In-Reply-To: References: From: "Derrick Stolee via GitGitGadget" Date: Wed, 19 Aug 2020 17:16:42 +0000 Subject: [PATCH 1/7] maintenance: optionally skip --auto process Fcc: Sent MIME-Version: 1.0 To: git@vger.kernel.org Cc: sandals@crustytoothpaste.net, steadmon@google.com, jrnieder@gmail.com, peff@peff.net, congdanhqx@gmail.com, phillip.wood123@gmail.com, emilyshaffer@google.com, sluongng@gmail.com, jonathantanmy@google.com, Derrick Stolee , Derrick Stolee Sender: git-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: git@vger.kernel.org From: Derrick Stolee Some commands run 'git maintenance run --auto --[no-]quiet' after doing their normal work, as a way to keep repositories clean as they are used. Currently, users who do not want this maintenance to occur would set the 'gc.auto' config option to 0 to avoid the 'gc' task from running. However, this does not stop the extra process invocation. On Windows, this extra process invocation can be more expensive than necessary. Allow users to drop this extra process by setting 'maintenance.auto' to 'false'. Signed-off-by: Derrick Stolee --- Documentation/config/maintenance.txt | 5 +++++ run-command.c | 8 ++++++++ t/t7900-maintenance.sh | 13 +++++++++++++ 3 files changed, 26 insertions(+) diff --git a/Documentation/config/maintenance.txt b/Documentation/config/maintenance.txt index a0706d8f09..06db758172 100644 --- a/Documentation/config/maintenance.txt +++ b/Documentation/config/maintenance.txt @@ -1,3 +1,8 @@ +maintenance.auto:: + This boolean config option controls whether some commands run + `git maintenance run --auto` after doing their normal work. Defaults + to true. + maintenance..enabled:: This boolean config option controls whether the maintenance task with name `` is run when no `--task` option is specified to diff --git a/run-command.c b/run-command.c index 2ee59acdc8..9c9d5d7f98 100644 --- a/run-command.c +++ b/run-command.c @@ -7,6 +7,7 @@ #include "strbuf.h" #include "string-list.h" #include "quote.h" +#include "config.h" void child_process_init(struct child_process *child) { @@ -1868,8 +1869,15 @@ int run_processes_parallel_tr2(int n, get_next_task_fn get_next_task, int run_auto_maintenance(int quiet) { + int enabled; struct child_process maint = CHILD_PROCESS_INIT; + if (!git_config_get_bool("maintenance.auto", &enabled) && + !enabled) { + fprintf(stderr, "enabled: %d\n", enabled); + return 0; + } + maint.git_cmd = 1; strvec_pushl(&maint.args, "maintenance", "run", "--auto", NULL); strvec_push(&maint.args, quiet ? "--quiet" : "--no-quiet"); diff --git a/t/t7900-maintenance.sh b/t/t7900-maintenance.sh index 6f878b0141..e0ba19e1ff 100755 --- a/t/t7900-maintenance.sh +++ b/t/t7900-maintenance.sh @@ -26,6 +26,19 @@ test_expect_success 'run [--auto|--quiet]' ' test_subcommand git gc --no-quiet .enabled' ' git config maintenance.gc.enabled false && git config maintenance.commit-graph.enabled true && From patchwork Wed Aug 19 17:16:44 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Philippe Blain via GitGitGadget X-Patchwork-Id: 11724697 Return-Path: Received: from mail.kernel.org (pdx-korg-mail-1.web.codeaurora.org [172.30.200.123]) by pdx-korg-patchwork-2.web.codeaurora.org (Postfix) with ESMTP id 0379513A4 for ; Wed, 19 Aug 2020 17:17:28 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by mail.kernel.org (Postfix) with ESMTP id D3226207FF for ; Wed, 19 Aug 2020 17:17:27 +0000 (UTC) Authentication-Results: mail.kernel.org; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b="kYwOYt4s" Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1726795AbgHSRRZ (ORCPT ); Wed, 19 Aug 2020 13:17:25 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:48174 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1726209AbgHSRRL (ORCPT ); Wed, 19 Aug 2020 13:17:11 -0400 Received: from mail-wm1-x333.google.com (mail-wm1-x333.google.com [IPv6:2a00:1450:4864:20::333]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 05EA1C061342 for ; Wed, 19 Aug 2020 10:17:10 -0700 (PDT) Received: by mail-wm1-x333.google.com with SMTP id k20so2930811wmi.5 for ; Wed, 19 Aug 2020 10:17:09 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20161025; h=message-id:in-reply-to:references:from:date:subject:fcc :content-transfer-encoding:mime-version:to:cc; bh=UVTECwSWY++dvwKBt2Xi+XO49gvvSGwJiw/Y9rQyIw0=; b=kYwOYt4s/6paeq2WkwoaiA4zwIF5Y71x2S0H8KuL30Mlgg8wdlKTLzMf2w8lMxTy3u 2ha+5tBEDvbujjy4hK7LHCuHvtgI2ohPD8ySQ/5WrPVw3SS4UV6Ue9rk3pTgRia7489q bXC7zBJB9NKW8q8blUpUKVGy7ygGHhAaTepqmu39btsLrCfQjrnA7/kMTXYiYfqzF7aq DED2NagqpDXo2Bp7cYoq+OT6aU6N0S8kZNuMSMNeMZggZc3G/y+KJlA0mG5ItaQnnu9g ym+Q2rTE0CXe7KOyVkC5XMq7VjWb6bf15WDNLOXWHVTL3tuRiggq5+OGoW5sYuMbfbAu sDtA== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:message-id:in-reply-to:references:from:date :subject:fcc:content-transfer-encoding:mime-version:to:cc; bh=UVTECwSWY++dvwKBt2Xi+XO49gvvSGwJiw/Y9rQyIw0=; b=bRwt8D9ibuPYQs2cJ0qvd2lT2jQ0JgcBjy+XbsP1C4t0XNk7azA5o5X2CRKdIh9JNk kiZQbj1PMqCJrmFjM4at8z9ZzqStGAJrtUmC7iRIbZM3i5feztbZoyIo9GiPn8JrkINs jMrdSpqaWGzaIA189ojdN0BLQnqLbvExTst5dnCglMds27QJeomj99xWN/jmeDrnp+aW Yt4zcwQIYScC9XODpwH89wsEzFMuc3rgFnWzdo7S5JsuTRsbIqFwsWWjICwabC0yRIpf L3l03amYYbIJEmByoMtPLQ65ncJ6R3rKqLthPwGb5dvY+84Qsyt0U2E3CBhwoviDeo8A XEtA== X-Gm-Message-State: AOAM5318bOss0TFkoA9HxWqEN66zunFl6g3SblOzbecR0MHG+MQlZEVC vVziKa1mvsQdp3m49EhO/8hJuZ1UcXo= X-Google-Smtp-Source: ABdhPJwEFkn6cYY0nqkVPH3wuCazGVdV9H3waRSCO6805oV+sIlHgk0Ghoyf1VYvOSebU6gPZ6M6Tg== X-Received: by 2002:a7b:cf22:: with SMTP id m2mr6095661wmg.46.1597857428266; Wed, 19 Aug 2020 10:17:08 -0700 (PDT) Received: from [127.0.0.1] ([13.74.141.28]) by smtp.gmail.com with ESMTPSA id r21sm42332118wrc.2.2020.08.19.10.17.07 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Wed, 19 Aug 2020 10:17:07 -0700 (PDT) Message-Id: <4473c93b118a0e0cdb205d1758aaaa2d8bf5139a.1597857412.git.gitgitgadget@gmail.com> In-Reply-To: References: From: "Derrick Stolee via GitGitGadget" Date: Wed, 19 Aug 2020 17:16:44 +0000 Subject: [PATCH 3/7] maintenance: add --scheduled option and config Fcc: Sent MIME-Version: 1.0 To: git@vger.kernel.org Cc: sandals@crustytoothpaste.net, steadmon@google.com, jrnieder@gmail.com, peff@peff.net, congdanhqx@gmail.com, phillip.wood123@gmail.com, emilyshaffer@google.com, sluongng@gmail.com, jonathantanmy@google.com, Derrick Stolee , Derrick Stolee Sender: git-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: git@vger.kernel.org From: Derrick Stolee A user may want to run certain maintenance tasks based on frequency, not conditions given in the repository. For example, the user may want to perform a 'prefetch' task every hour, or 'gc' task every day. To assist, update the 'git maintenance run --scheduled' command to check the config for the last run of that task and add a number of seconds. The task would then run only if the current time is beyond that minimum timestamp. Add a '--scheduled' option to 'git maintenance run' to only run tasks that have had enough time pass since their last run. This is done for each enabled task by checking if the current timestamp is at least as large as the sum of 'maintenance..lastRun' and 'maintenance..schedule' in the Git config. This second value is new to this commit, storing a number of seconds intended between runs. A user could then set up an hourly maintenance run with the following cron table: 0 * * * * git -C maintenance run --scheduled Then, the user could configure the repository with the following config values: maintenance.prefetch.schedule 3000 maintenance.gc.schedule 86000 These numbers are slightly lower than one hour and one day (in seconds). The cron schedule will enforce the hourly run rate, but we can use these schedules to ensure the 'gc' task runs once a day. The error is given because the *.lastRun config option is specified at the _start_ of the task run. Otherwise, a slow task run could shift the "daily" job of 'gc' from a 10:00pm run to 11:00pm run, or later. Signed-off-by: Derrick Stolee --- Documentation/config/maintenance.txt | 9 +++++ Documentation/git-maintenance.txt | 13 +++++++- builtin/gc.c | 50 +++++++++++++++++++++++++++- t/t7900-maintenance.sh | 20 +++++++++++ 4 files changed, 90 insertions(+), 2 deletions(-) diff --git a/Documentation/config/maintenance.txt b/Documentation/config/maintenance.txt index 8dd34169da..caacacd322 100644 --- a/Documentation/config/maintenance.txt +++ b/Documentation/config/maintenance.txt @@ -15,6 +15,15 @@ maintenance..lastRun:: `` is run. It stores a timestamp representing the most-recent run of the ``. +maintenance..schedule:: + This config option controls whether or not the given `` runs + during a `git maintenance run --scheduled` command. If the option + is an integer value `S`, then the `` is run when the current + time is `S` seconds after the timestamp stored in + `maintenance..lastRun`. If the option has no value or a + non-integer value, then the task will never run with the `--scheduled` + option. + maintenance.commit-graph.auto:: This integer config option controls how often the `commit-graph` task should be run as part of `git maintenance run --auto`. If zero, then diff --git a/Documentation/git-maintenance.txt b/Documentation/git-maintenance.txt index 95a333f000..e8004e7b11 100644 --- a/Documentation/git-maintenance.txt +++ b/Documentation/git-maintenance.txt @@ -110,7 +110,18 @@ OPTIONS only if certain thresholds are met. For example, the `gc` task runs when the number of loose objects exceeds the number stored in the `gc.auto` config setting, or when the number of pack-files - exceeds the `gc.autoPackLimit` config setting. + exceeds the `gc.autoPackLimit` config setting. Not compatible with + the `--scheduled` option. + +--scheduled:: + When combined with the `run` subcommand, run maintenance tasks + only if certain time conditions are met, as specified by the + `maintenance..schedule` config value for each ``. + This config value specifies a number of seconds since the last + time that task ran, according to the `maintenance..lastRun` + config value. The tasks that are tested are those provided by + the `--task=` option(s) or those with + `maintenance..enabled` set to true. --quiet:: Do not report progress or other information over `stderr`. diff --git a/builtin/gc.c b/builtin/gc.c index 707c144fb9..352948529d 100644 --- a/builtin/gc.c +++ b/builtin/gc.c @@ -711,6 +711,7 @@ static const char * const builtin_maintenance_usage[] = { struct maintenance_opts { int auto_flag; + int scheduled; int quiet; }; @@ -1226,7 +1227,8 @@ struct maintenance_task { const char *name; maintenance_task_fn *fn; maintenance_auto_fn *auto_condition; - unsigned enabled:1; + unsigned enabled:1, + scheduled:1; /* -1 if not selected. */ int selected_order; @@ -1337,6 +1339,9 @@ static int maintenance_run(struct maintenance_opts *opts) !tasks[i].auto_condition())) continue; + if (opts->scheduled && !tasks[i].scheduled) + continue; + update_last_run(&tasks[i]); trace2_region_enter("maintenance", tasks[i].name, r); @@ -1351,6 +1356,29 @@ static int maintenance_run(struct maintenance_opts *opts) return result; } +static void fill_schedule_info(struct maintenance_task *task, + const char *config_name, + timestamp_t schedule_delay) +{ + timestamp_t now = approxidate("now"); + char *value = NULL; + struct strbuf last_run = STRBUF_INIT; + int64_t previous_run; + + strbuf_addf(&last_run, "maintenance.%s.lastrun", task->name); + + if (git_config_get_string(last_run.buf, &value)) + task->scheduled = 1; + else { + previous_run = git_config_int64(last_run.buf, value); + if (now >= previous_run + schedule_delay) + task->scheduled = 1; + } + + free(value); + strbuf_release(&last_run); +} + static void initialize_task_config(void) { int i; @@ -1359,6 +1387,7 @@ static void initialize_task_config(void) for (i = 0; i < TASK__COUNT; i++) { int config_value; + char *config_str; strbuf_setlen(&config_name, 0); strbuf_addf(&config_name, "maintenance.%s.enabled", @@ -1366,6 +1395,20 @@ static void initialize_task_config(void) if (!git_config_get_bool(config_name.buf, &config_value)) tasks[i].enabled = config_value; + + strbuf_setlen(&config_name, 0); + strbuf_addf(&config_name, "maintenance.%s.schedule", + tasks[i].name); + + if (!git_config_get_string(config_name.buf, &config_str)) { + timestamp_t schedule_delay = git_config_int64( + config_name.buf, + config_str); + fill_schedule_info(&tasks[i], + config_name.buf, + schedule_delay); + free(config_str); + } } strbuf_release(&config_name); @@ -1409,6 +1452,8 @@ int cmd_maintenance(int argc, const char **argv, const char *prefix) struct option builtin_maintenance_options[] = { OPT_BOOL(0, "auto", &opts.auto_flag, N_("run tasks based on the state of the repository")), + OPT_BOOL(0, "scheduled", &opts.scheduled, + N_("run tasks based on time intervals")), OPT_BOOL(0, "quiet", &opts.quiet, N_("do not report progress or other information over stderr")), OPT_CALLBACK_F(0, "task", NULL, N_("task"), @@ -1434,6 +1479,9 @@ int cmd_maintenance(int argc, const char **argv, const char *prefix) builtin_maintenance_usage, PARSE_OPT_KEEP_UNKNOWN); + if (opts.auto_flag + opts.scheduled > 1) + die(_("use at most one of the --auto and --scheduled options")); + if (argc != 1) usage_with_options(builtin_maintenance_usage, builtin_maintenance_options); diff --git a/t/t7900-maintenance.sh b/t/t7900-maintenance.sh index a985ce3674..3e0c5f1ca8 100755 --- a/t/t7900-maintenance.sh +++ b/t/t7900-maintenance.sh @@ -264,6 +264,11 @@ test_expect_success 'maintenance.incremental-repack.auto' ' done ' +test_expect_success '--auto and --scheduled incompatible' ' + test_must_fail git maintenance run --auto --scheduled 2>err && + test_i18ngrep "at most one" err +' + test_expect_success 'tasks update maintenance..lastRun' ' git config --unset maintenance.commit-graph.lastrun && GIT_TRACE2_EVENT="$(pwd)/run.txt" \ @@ -274,4 +279,19 @@ test_expect_success 'tasks update maintenance..lastRun' ' test_cmp_config 1595000000 maintenance.commit-graph.lastrun ' +test_expect_success '--scheduled with specific time' ' + git config maintenance.commit-graph.schedule 100 && + GIT_TRACE2_EVENT="$(pwd)/too-soon.txt" \ + GIT_TEST_DATE_NOW=1595000099 \ + git maintenance run --scheduled 2>/dev/null && + test_subcommand ! git commit-graph write --split --reachable \ + --no-progress /dev/null && + test_subcommand git commit-graph write --split --reachable \ + --no-progress X-Patchwork-Id: 11724695 Return-Path: Received: from mail.kernel.org (pdx-korg-mail-1.web.codeaurora.org [172.30.200.123]) by pdx-korg-patchwork-2.web.codeaurora.org (Postfix) with ESMTP id A3BCF618 for ; Wed, 19 Aug 2020 17:17:25 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by mail.kernel.org (Postfix) with ESMTP id 850A0207FF for ; Wed, 19 Aug 2020 17:17:25 +0000 (UTC) Authentication-Results: mail.kernel.org; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b="Tydl6E4z" Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1726772AbgHSRRX (ORCPT ); Wed, 19 Aug 2020 13:17:23 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:48182 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1726435AbgHSRRM (ORCPT ); Wed, 19 Aug 2020 13:17:12 -0400 Received: from mail-wm1-x341.google.com (mail-wm1-x341.google.com [IPv6:2a00:1450:4864:20::341]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 4F675C061343 for ; Wed, 19 Aug 2020 10:17:11 -0700 (PDT) Received: by mail-wm1-x341.google.com with SMTP id c80so2833699wme.0 for ; Wed, 19 Aug 2020 10:17:11 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20161025; h=message-id:in-reply-to:references:from:date:subject:fcc :content-transfer-encoding:mime-version:to:cc; bh=kIYShZeyv1j5IAEm0LbwEt/d2VNJnMKN8E/TZynKQSc=; b=Tydl6E4zuiAj8a4h5dWmM4KPRAkwXJa6xdbERQiGUsx+0qFhyNSCIKkJAABKSvWCJK nMh7MxSHKdwQy8WyeEHpbTYW/o+I395hXGPeazJ6pjd+juS9JT6+P9MmBMs5KvB6H75B V2q36VVTKBUFOEWCCLy39tuCM8LvWNab+U8Nl+akB9IGyWXzUFvNfZF3kUUbc13DGTWc 0Ym5/S4hl4FHXJN7SfWQBmtmmwlorqP69FFfeY0esSG14hc81eA5mOnII/9WSBSb0W7p ONNta4qD4d7eHa3T8kiqkEYv2pMoQ4D1RnEG1POCQoonhm3V8Eakq2HEaersLWoA2KyP lzsg== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:message-id:in-reply-to:references:from:date :subject:fcc:content-transfer-encoding:mime-version:to:cc; bh=kIYShZeyv1j5IAEm0LbwEt/d2VNJnMKN8E/TZynKQSc=; b=rnrR+w89uhQSJMBBLGsWOlTMe2+/hXdjcsvGeOzVUbfs209ZZO5UwWZYr9/lgwCZMB /vXbs6KQ+zaLtBiHSIRXwWtDkwMVR5xrNvLZQos6739Go84Au/NcMKzdCVlS0pxt6xUv eRXzoL3eUp6B2ia9PqkTWeDuQE52dEHCF3/B6I7a1fVc8OIWTtSBquAXvMOXJyHfi0Pn dxpwUKADBxSxPtuAoyn6T7wL9bSBkRKlcMjLUe0JSpdZ4NyN/pqF+VgIDbPm13dPrhRU txFKU4AFgi/JfpJQStTDTepacqD+nzMipCyHwpd0Hi5iSjf5sOWeZ9cpZOAwdKsEHA8Q nDEA== X-Gm-Message-State: AOAM533PCCT12Ialrl57+mARluptEkSfvcYnJDX6GDH8XdiqBwJUt7gW ebKri1rRIDCm2RCMbI+e+56onsNkO+w= X-Google-Smtp-Source: ABdhPJzBaZioq0EURRH6MMkOTuZxrONSmS0EEvb4nNzR/ANkc6hk3P0l6JnJSldI3xXIwUpuu0nsnw== X-Received: by 2002:a7b:c0cb:: with SMTP id s11mr5792166wmh.89.1597857428957; Wed, 19 Aug 2020 10:17:08 -0700 (PDT) Received: from [127.0.0.1] ([13.74.141.28]) by smtp.gmail.com with ESMTPSA id b8sm41378206wrv.4.2020.08.19.10.17.08 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Wed, 19 Aug 2020 10:17:08 -0700 (PDT) Message-Id: In-Reply-To: References: From: "Derrick Stolee via GitGitGadget" Date: Wed, 19 Aug 2020 17:16:45 +0000 Subject: [PATCH 4/7] for-each-repo: run subcommands on configured repos Fcc: Sent MIME-Version: 1.0 To: git@vger.kernel.org Cc: sandals@crustytoothpaste.net, steadmon@google.com, jrnieder@gmail.com, peff@peff.net, congdanhqx@gmail.com, phillip.wood123@gmail.com, emilyshaffer@google.com, sluongng@gmail.com, jonathantanmy@google.com, Derrick Stolee , Derrick Stolee Sender: git-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: git@vger.kernel.org From: Derrick Stolee It can be helpful to store a list of repositories in global or system config and then iterate Git commands on that list. Create a new builtin that makes this process simple for experts. We will use this builtin to run scheduled maintenance on all configured repositories in a future change. The test is very simple, but does highlight that the "--" argument is optional. Signed-off-by: Derrick Stolee --- .gitignore | 1 + Documentation/git-for-each-repo.txt | 45 ++++++++++++++++++++++ Makefile | 1 + builtin.h | 1 + builtin/for-each-repo.c | 58 +++++++++++++++++++++++++++++ git.c | 1 + t/t0068-for-each-repo.sh | 30 +++++++++++++++ 7 files changed, 137 insertions(+) create mode 100644 Documentation/git-for-each-repo.txt create mode 100644 builtin/for-each-repo.c create mode 100755 t/t0068-for-each-repo.sh diff --git a/.gitignore b/.gitignore index a5808fa30d..5eb2a2be71 100644 --- a/.gitignore +++ b/.gitignore @@ -67,6 +67,7 @@ /git-filter-branch /git-fmt-merge-msg /git-for-each-ref +/git-for-each-repo /git-format-patch /git-fsck /git-fsck-objects diff --git a/Documentation/git-for-each-repo.txt b/Documentation/git-for-each-repo.txt new file mode 100644 index 0000000000..83b06db410 --- /dev/null +++ b/Documentation/git-for-each-repo.txt @@ -0,0 +1,45 @@ +git-for-each-repo(1) +==================== + +NAME +---- +git-for-each-repo - Run a Git command on a list of repositories + + +SYNOPSIS +-------- +[verse] +'git for-each-repo' --config= [--] + + +DESCRIPTION +----------- +Run a Git commands on a list of repositories. The arguments after the +known options or `--` indicator are used as the arguments for the Git +command. + +For example, we could run maintenance on each of a list of repositories +stored in a `maintenance.repo` config variable using + +------------- +git for-each-repo --config=maintenance.repo maintenance run +------------- + +This will run `git -C maintenance run` for each value `` +in the multi-valued config variable `maintenance.repo`. + + +OPTIONS +------- +--config=:: + Use the given config variable as a multi-valued list storing + absolute path names. Iterate on that list of paths to run + the given arguments. ++ +These config values are loaded from system, global, and local Git config, +as available. If `git for-each-repo` is run in a directory that is not a +Git repository, then only the system and global config is used. + +GIT +--- +Part of the linkgit:git[1] suite diff --git a/Makefile b/Makefile index 65f8cfb236..7c588ff036 100644 --- a/Makefile +++ b/Makefile @@ -1071,6 +1071,7 @@ BUILTIN_OBJS += builtin/fetch-pack.o BUILTIN_OBJS += builtin/fetch.o BUILTIN_OBJS += builtin/fmt-merge-msg.o BUILTIN_OBJS += builtin/for-each-ref.o +BUILTIN_OBJS += builtin/for-each-repo.o BUILTIN_OBJS += builtin/fsck.o BUILTIN_OBJS += builtin/gc.o BUILTIN_OBJS += builtin/get-tar-commit-id.o diff --git a/builtin.h b/builtin.h index 17c1c0ce49..ff7c6e5aa9 100644 --- a/builtin.h +++ b/builtin.h @@ -150,6 +150,7 @@ int cmd_fetch(int argc, const char **argv, const char *prefix); int cmd_fetch_pack(int argc, const char **argv, const char *prefix); int cmd_fmt_merge_msg(int argc, const char **argv, const char *prefix); int cmd_for_each_ref(int argc, const char **argv, const char *prefix); +int cmd_for_each_repo(int argc, const char **argv, const char *prefix); int cmd_format_patch(int argc, const char **argv, const char *prefix); int cmd_fsck(int argc, const char **argv, const char *prefix); int cmd_gc(int argc, const char **argv, const char *prefix); diff --git a/builtin/for-each-repo.c b/builtin/for-each-repo.c new file mode 100644 index 0000000000..5bba623ff1 --- /dev/null +++ b/builtin/for-each-repo.c @@ -0,0 +1,58 @@ +#include "cache.h" +#include "config.h" +#include "builtin.h" +#include "parse-options.h" +#include "run-command.h" +#include "string-list.h" + +static const char * const for_each_repo_usage[] = { + N_("git for-each-repo --config= "), + NULL +}; + +static int run_command_on_repo(const char *path, + void *cbdata) +{ + int i; + struct child_process child = CHILD_PROCESS_INIT; + struct strvec *args = (struct strvec *)cbdata; + + child.git_cmd = 1; + strvec_pushl(&child.args, "-C", path, NULL); + + for (i = 0; i < args->nr; i++) + strvec_push(&child.args, args->v[i]); + + return run_command(&child); +} + +int cmd_for_each_repo(int argc, const char **argv, const char *prefix) +{ + static const char *config_key = NULL; + int i, result = 0; + const struct string_list *values; + struct strvec args = STRVEC_INIT; + + const struct option options[] = { + OPT_STRING(0, "config", &config_key, N_("config"), + N_("config key storing a list of repository paths")), + OPT_END() + }; + + argc = parse_options(argc, argv, prefix, options, for_each_repo_usage, + PARSE_OPT_STOP_AT_NON_OPTION); + + if (!config_key) + die(_("missing --config=")); + + for (i = 0; i < argc; i++) + strvec_push(&args, argv[i]); + + values = repo_config_get_value_multi(the_repository, + config_key); + + for (i = 0; !result && i < values->nr; i++) + result = run_command_on_repo(values->items[i].string, &args); + + return result; +} diff --git a/git.c b/git.c index 24f250d29a..1cab64b5d1 100644 --- a/git.c +++ b/git.c @@ -511,6 +511,7 @@ static struct cmd_struct commands[] = { { "fetch-pack", cmd_fetch_pack, RUN_SETUP | NO_PARSEOPT }, { "fmt-merge-msg", cmd_fmt_merge_msg, RUN_SETUP }, { "for-each-ref", cmd_for_each_ref, RUN_SETUP }, + { "for-each-repo", cmd_for_each_repo, RUN_SETUP_GENTLY }, { "format-patch", cmd_format_patch, RUN_SETUP }, { "fsck", cmd_fsck, RUN_SETUP }, { "fsck-objects", cmd_fsck, RUN_SETUP }, diff --git a/t/t0068-for-each-repo.sh b/t/t0068-for-each-repo.sh new file mode 100755 index 0000000000..136b4ec839 --- /dev/null +++ b/t/t0068-for-each-repo.sh @@ -0,0 +1,30 @@ +#!/bin/sh + +test_description='git for-each-repo builtin' + +. ./test-lib.sh + +test_expect_success 'run based on configured value' ' + git init one && + git init two && + git init three && + 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 for-each-repo --config=run.key commit --allow-empty -m "ran" && + git -C one log -1 --pretty=format:%s >message && + grep ran message && + git -C two log -1 --pretty=format:%s >message && + ! grep ran message && + git -C three 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 +' + +test_done From patchwork Wed Aug 19 17:16:46 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Philippe Blain via GitGitGadget X-Patchwork-Id: 11724699 Return-Path: Received: from mail.kernel.org (pdx-korg-mail-1.web.codeaurora.org [172.30.200.123]) by pdx-korg-patchwork-2.web.codeaurora.org (Postfix) with ESMTP id C1B8A618 for ; Wed, 19 Aug 2020 17:17:40 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by mail.kernel.org (Postfix) with ESMTP id 9E5DB207FF for ; Wed, 19 Aug 2020 17:17:40 +0000 (UTC) Authentication-Results: mail.kernel.org; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b="rgW2EgiV" Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1726815AbgHSRR3 (ORCPT ); Wed, 19 Aug 2020 13:17:29 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:48184 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1726701AbgHSRRM (ORCPT ); Wed, 19 Aug 2020 13:17:12 -0400 Received: from mail-wr1-x443.google.com (mail-wr1-x443.google.com [IPv6:2a00:1450:4864:20::443]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 64261C061344 for ; Wed, 19 Aug 2020 10:17:11 -0700 (PDT) Received: by mail-wr1-x443.google.com with SMTP id r2so22289346wrs.8 for ; Wed, 19 Aug 2020 10:17:11 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20161025; h=message-id:in-reply-to:references:from:date:subject:fcc :content-transfer-encoding:mime-version:to:cc; bh=8w+O16rls3OYS+PPfEjmild1DTwYxx/bT7BABEf6eVk=; b=rgW2EgiVWXLh3j/QMRosilC0r73Fhzf1GoKu+4r7d0a9P5Sq0o/CzEWEi5jFg5M0cO 8ANUgDHoGH27wxXOXh20eg843qGg9ptCmxb4Apmir6c2X8bMzTV/df6hFomdKUsu3hgm fZiJ55mZlTEaP4ViGa8uS6HbRcvHy0JWEZ9JRFzHd6Ndfk1ZxPBMcZw2l6sp3AxLIlLC 4mZhtVTX94MuYibfoGcqeEh3AIxDSeByNjhVUJqYoHbVXBuUyU/7m6Nb/kv7zHh25FJ7 FfJzv1QiIkO2Pjwrcm/wVGmKDTNME8z3XpWZZTwe9T8FBXlA6X+coZxckDjlXSJFeN0o NwiA== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:message-id:in-reply-to:references:from:date :subject:fcc:content-transfer-encoding:mime-version:to:cc; bh=8w+O16rls3OYS+PPfEjmild1DTwYxx/bT7BABEf6eVk=; b=l7rnMllkx7vi9uHtAC5IReBYA8pUb6MZxv7oc9CXf3MjhdRpJTDfHkj1qsZ+bVS4uw T3bGCt5n0nHURd0fw12mUTjSu1quVCpP4mOkDdd0MA6FN60Al20jPJqUkngd2gBN9wYW H32wV5hJIbwdqXeFlRKpM8KQi9Sr2BpoO65d7RNWfDkzSuX4cRP2JCZUyBGCwS7JIeLv an64x8nYAl1UqrRQLx7dDGaTLMLAljV/pFjy1ssU+xxY3i1xaYyCmyD7nli7SjE7XXZb tR6BXQF2pfV01xxDSGoafWRlMmMwsGtaHYKKr4ArJwWpQY343t97Io4Js7eoxy044HLY 2FhA== X-Gm-Message-State: AOAM532YJKzuo/5WyS21ukKkzp+uY+Yorl7DzEGpdwz/nEH26aAeZQ6q 5R3jABvQiCgfkb9gWGgOPLtLYbmC4i0= X-Google-Smtp-Source: ABdhPJygNltHiSQ4xyqHwYUxv4tU1iachGX5uy9wTcKbZJCn3kyJ+nLV8EOf6p/+OGtFnckQfpo6vA== X-Received: by 2002:adf:ba05:: with SMTP id o5mr25840090wrg.7.1597857429678; Wed, 19 Aug 2020 10:17:09 -0700 (PDT) Received: from [127.0.0.1] ([13.74.141.28]) by smtp.gmail.com with ESMTPSA id l1sm44095023wrb.12.2020.08.19.10.17.09 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Wed, 19 Aug 2020 10:17:09 -0700 (PDT) Message-Id: In-Reply-To: References: From: "Derrick Stolee via GitGitGadget" Date: Wed, 19 Aug 2020 17:16:46 +0000 Subject: [PATCH 5/7] maintenance: add [un]register subcommands Fcc: Sent MIME-Version: 1.0 To: git@vger.kernel.org Cc: sandals@crustytoothpaste.net, steadmon@google.com, jrnieder@gmail.com, peff@peff.net, congdanhqx@gmail.com, phillip.wood123@gmail.com, emilyshaffer@google.com, sluongng@gmail.com, jonathantanmy@google.com, Derrick Stolee , Derrick Stolee Sender: git-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: git@vger.kernel.org From: Derrick Stolee In preparation for launching background maintenance from the 'git maintenance' builtin, create register/unregister subcommands. These commands update the new 'maintenance.repos' config option in the global config so the background maintenance job knows which repositories to maintain. These commands allow users to add a repository to the background maintenance list without disrupting the actual maintenance mechanism. For example, a user can run 'git maintenance register' when no background maintenance is running and it will not start the background maintenance. A later update to start running background maintenance will then pick up this repository automatically. The opposite example is that a user can run 'git maintenance unregister' to remove the current repository from background maintenance without halting maintenance for other repositories. Signed-off-by: Derrick Stolee --- Documentation/git-maintenance.txt | 14 ++++++++ builtin/gc.c | 55 ++++++++++++++++++++++++++++++- t/t7900-maintenance.sh | 17 +++++++++- 3 files changed, 84 insertions(+), 2 deletions(-) diff --git a/Documentation/git-maintenance.txt b/Documentation/git-maintenance.txt index e8004e7b11..ac6fcae678 100644 --- a/Documentation/git-maintenance.txt +++ b/Documentation/git-maintenance.txt @@ -29,6 +29,15 @@ Git repository. SUBCOMMANDS ----------- +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..schedule`. The tasks that are enabled are safe + for running in the background without disrupting foreground + processes. + run:: Run one or more maintenance tasks. If one or more `--task` options are specified, then those tasks are run in that order. Otherwise, @@ -36,6 +45,11 @@ run:: config options are true. By default, only `maintenance.gc.enabled` is true. +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. + TASKS ----- diff --git a/builtin/gc.c b/builtin/gc.c index 352948529d..eb8a0a52ab 100644 --- a/builtin/gc.c +++ b/builtin/gc.c @@ -705,7 +705,7 @@ int cmd_gc(int argc, const char **argv, const char *prefix) } static const char * const builtin_maintenance_usage[] = { - N_("git maintenance run []"), + N_("git maintenance []"), NULL }; @@ -1445,6 +1445,55 @@ static int task_option_parse(const struct option *opt, return 0; } +static int maintenance_register(void) +{ + struct child_process config_set = CHILD_PROCESS_INIT; + struct child_process config_get = CHILD_PROCESS_INIT; + + /* There is no current repository, so skip registering it */ + if (!the_repository || !the_repository->gitdir) + return 0; + + config_get.git_cmd = 1; + strvec_pushl(&config_get.args, "config", "--global", "--get", "maintenance.repo", + the_repository->worktree ? the_repository->worktree + : the_repository->gitdir, + NULL); + config_get.out = -1; + + if (start_command(&config_get)) + return error(_("failed to run 'git config'")); + + /* We already have this value in our config! */ + if (!finish_command(&config_get)) + return 0; + + config_set.git_cmd = 1; + strvec_pushl(&config_set.args, "config", "--add", "--global", "maintenance.repo", + the_repository->worktree ? the_repository->worktree + : the_repository->gitdir, + NULL); + + return run_command(&config_set); +} + +static int maintenance_unregister(void) +{ + struct child_process config_unset = CHILD_PROCESS_INIT; + + if (!the_repository || !the_repository->gitdir) + return error(_("no current repository to unregister")); + + config_unset.git_cmd = 1; + strvec_pushl(&config_unset.args, "config", "--global", "--unset", + "maintenance.repo", + the_repository->worktree ? the_repository->worktree + : the_repository->gitdir, + NULL); + + return run_command(&config_unset); +} + int cmd_maintenance(int argc, const char **argv, const char *prefix) { int i; @@ -1486,8 +1535,12 @@ int cmd_maintenance(int argc, const char **argv, const char *prefix) usage_with_options(builtin_maintenance_usage, builtin_maintenance_options); + if (!strcmp(argv[0], "register")) + return maintenance_register(); if (!strcmp(argv[0], "run")) return maintenance_run(&opts); + if (!strcmp(argv[0], "unregister")) + return maintenance_unregister(); die(_("invalid subcommand: %s"), argv[0]); } diff --git a/t/t7900-maintenance.sh b/t/t7900-maintenance.sh index 3e0c5f1ca8..b20ee2d542 100755 --- a/t/t7900-maintenance.sh +++ b/t/t7900-maintenance.sh @@ -9,7 +9,7 @@ GIT_TEST_MULTI_PACK_INDEX=0 test_expect_success 'help text' ' test_expect_code 129 git maintenance -h 2>err && - test_i18ngrep "usage: git maintenance run" err && + test_i18ngrep "usage: git maintenance " err && test_expect_code 128 git maintenance barf 2>err && test_i18ngrep "invalid subcommand: barf" err ' @@ -294,4 +294,19 @@ test_expect_success '--scheduled with specific time' ' test_cmp_config 1595000100 maintenance.commit-graph.lastrun ' +test_expect_success 'register and unregister' ' + test_when_finished git config --global --unset-all maintenance.repo && + git config --global --add maintenance.repo /existing1 && + git config --global --add maintenance.repo /existing2 && + git config --global --get-all maintenance.repo >before && + git maintenance register && + git config --global --get-all maintenance.repo >actual && + cp before after && + pwd >>after && + test_cmp after actual && + git maintenance unregister && + git config --global --get-all maintenance.repo >actual && + test_cmp before actual +' + test_done From patchwork Wed Aug 19 17:16:47 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Philippe Blain via GitGitGadget X-Patchwork-Id: 11724701 Return-Path: Received: from mail.kernel.org (pdx-korg-mail-1.web.codeaurora.org [172.30.200.123]) by pdx-korg-patchwork-2.web.codeaurora.org (Postfix) with ESMTP id 08227618 for ; Wed, 19 Aug 2020 17:17:43 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by mail.kernel.org (Postfix) with ESMTP id C8DAC20888 for ; Wed, 19 Aug 2020 17:17:42 +0000 (UTC) Authentication-Results: mail.kernel.org; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b="u0d3P7I6" Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1726751AbgHSRRk (ORCPT ); Wed, 19 Aug 2020 13:17:40 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:48190 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1726702AbgHSRRN (ORCPT ); Wed, 19 Aug 2020 13:17:13 -0400 Received: from mail-wr1-x444.google.com (mail-wr1-x444.google.com [IPv6:2a00:1450:4864:20::444]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 6F2A6C061345 for ; Wed, 19 Aug 2020 10:17:12 -0700 (PDT) Received: by mail-wr1-x444.google.com with SMTP id c15so22249121wrs.11 for ; Wed, 19 Aug 2020 10:17:12 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20161025; h=message-id:in-reply-to:references:from:date:subject:fcc :content-transfer-encoding:mime-version:to:cc; bh=r78Zz5GOXpIfgrcbYR+uGwBIYEgibxMJpGe9vnKEtPI=; b=u0d3P7I60MUKUu85YW5tARKcdguNa0TBEmMckBMm1+Jn9z2zEU3Z3oXqcAHfiiuZ/Z 30T15AHNWmFyDL1h6l3TGu8o6F+Nprpfff2xOra53tZcceYq6mKJgstAN/TI1yQKmJgo sjrWON+9sTyh3bIQPjG/nV5dKa+ibwVKnfT4YQwGIiSOOcLaZns0erDUnnW/Xuimq0a9 nJfIIivdqRr/JQOEKSXbQ11d6cB3W9tFe9ULulwG799cuWi/9CleRqnPQOfWqddIc7AI xZSdIhUjdLmYnz/IgmOwrLdvvf31adfgZ1td6UEZBL6rPAg6YGwQcNds9HK3Q6WSTCAT Jz0g== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:message-id:in-reply-to:references:from:date :subject:fcc:content-transfer-encoding:mime-version:to:cc; bh=r78Zz5GOXpIfgrcbYR+uGwBIYEgibxMJpGe9vnKEtPI=; b=VaQ5Uuo3MrjpQDLqemmPFou/cRmd0h7q6QEnD6RTFSmsLjmGuAg9JGOFmw/4WvSzxu TOBR7DVAkSw1eVRpTcIffH7LgtiGFtz3P1ELrPra9H4iAkvKfIBYIJ9x5VyyN44fYbQX s+pSUnjwMZVgikq4GDWLnlL7q1dqPYUqixjZtWrN2Dzx7AaTye64pmmWKpRDL57A0UlV nuqtZDWczlr9J9jONuzxayFiVE1AI1qa+iZtnLpI1M/HHKmy+vy6Y7Uun9/SDY+VtFbi 9D8b+Nv1JagufMKguaO+morW5EDdY7gh1pT2VcfWiOHY2wNbNfrfJToajDz4iN4DI/rG WdUg== X-Gm-Message-State: AOAM531hBk5G+1PTBJbWo+vegvh7AlZBIPiIJsKfiJcJYqFF8t8tbtke jE38ghhBbvBXSw3cQm+NGcf3JdubfNU= X-Google-Smtp-Source: ABdhPJxTJqk1qVvPse0QsynljuXAJt9a7kF7V+mevcFZAIJzhIZjIJb01e9iPYEDx4Q3BHeX0+5TNg== X-Received: by 2002:adf:f488:: with SMTP id l8mr4910302wro.123.1597857430582; Wed, 19 Aug 2020 10:17:10 -0700 (PDT) Received: from [127.0.0.1] ([13.74.141.28]) by smtp.gmail.com with ESMTPSA id n18sm41631210wrp.58.2020.08.19.10.17.09 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Wed, 19 Aug 2020 10:17:10 -0700 (PDT) Message-Id: <2442071fd0581a221802e348182ed04267ef3044.1597857412.git.gitgitgadget@gmail.com> In-Reply-To: References: From: "Derrick Stolee via GitGitGadget" Date: Wed, 19 Aug 2020 17:16:47 +0000 Subject: [PATCH 6/7] maintenance: add start/stop subcommands Fcc: Sent MIME-Version: 1.0 To: git@vger.kernel.org Cc: sandals@crustytoothpaste.net, steadmon@google.com, jrnieder@gmail.com, peff@peff.net, congdanhqx@gmail.com, phillip.wood123@gmail.com, emilyshaffer@google.com, sluongng@gmail.com, jonathantanmy@google.com, Derrick Stolee , Derrick Stolee Sender: git-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: git@vger.kernel.org From: Derrick Stolee Add new subcommands to 'git maintenance' that start or stop background maintenance using 'cron', when available. This integration is as simple as I could make it, barring some implementation complications. For now, the background maintenance is scheduled to run hourly via the following cron table row (ignore line breaks): 0 * * * * $p/git --exec-path=$p for-each-repo --config=maintenance.repo maintenance run --scheduled Future extensions may want to add more complex schedules or some form of logging. For now, hourly runs seem frequent enough to satisfy the needs of tasks like 'prefetch' without being so frequent that users would complain about many no-op commands. Here, "$p" is a placeholder for the path to the current Git executable. This is critical for systems with multiple versions of Git. Specifically, macOS has a system version at '/usr/bin/git' while the version that users can install resides at '/usr/local/bin/git' (symlinked to '/usr/local/libexec/git-core/git'). This will also use your locally-built version if you build and run this in your development environment without installing first. The GIT_TEST_CRONTAB environment variable is not intended for users to edit, but instead as a way to mock the 'crontab [-l]' command. This variable is set in test-lib.sh to avoid a future test from accidentally running anything with the cron integration from modifying the user's schedule. We use GIT_TEST_CRONTAB='test-tool crontab ' in our tests to check how the schedule is modified in 'git maintenance (start|stop)' commands. Signed-off-by: Derrick Stolee --- Documentation/git-maintenance.txt | 11 +++ Makefile | 1 + builtin/gc.c | 117 ++++++++++++++++++++++++++++++ t/helper/test-crontab.c | 35 +++++++++ t/helper/test-tool.c | 1 + t/helper/test-tool.h | 1 + t/t7900-maintenance.sh | 30 ++++++++ t/test-lib.sh | 6 ++ 8 files changed, 202 insertions(+) create mode 100644 t/helper/test-crontab.c diff --git a/Documentation/git-maintenance.txt b/Documentation/git-maintenance.txt index ac6fcae678..600272caa5 100644 --- a/Documentation/git-maintenance.txt +++ b/Documentation/git-maintenance.txt @@ -45,6 +45,17 @@ run:: config options are true. By default, only `maintenance.gc.enabled` is true. +start:: + Start running maintenance on the current repository. This performs + the same config updates as the `register` subcommand, then updates + the background scheduler to run `git maintenance run --scheduled` + on an hourly basis. + +stop:: + Halt the background maintenance schedule. The current repository + is not removed from the list of maintained repositories, in case + the background maintenance is restarted later. + unregister:: Remove the current repository from background maintenance. This only removes the repository from the configured list. It does not diff --git a/Makefile b/Makefile index 7c588ff036..c39b39bd7d 100644 --- a/Makefile +++ b/Makefile @@ -690,6 +690,7 @@ TEST_BUILTINS_OBJS += test-advise.o TEST_BUILTINS_OBJS += test-bloom.o TEST_BUILTINS_OBJS += test-chmtime.o TEST_BUILTINS_OBJS += test-config.o +TEST_BUILTINS_OBJS += test-crontab.o TEST_BUILTINS_OBJS += test-ctype.o TEST_BUILTINS_OBJS += test-date.o TEST_BUILTINS_OBJS += test-delta.o diff --git a/builtin/gc.c b/builtin/gc.c index eb8a0a52ab..b09287f0fc 100644 --- a/builtin/gc.c +++ b/builtin/gc.c @@ -32,6 +32,7 @@ #include "remote.h" #include "midx.h" #include "object-store.h" +#include "exec-cmd.h" #define FAILED_RUN "failed to run %s" @@ -1494,6 +1495,118 @@ static int maintenance_unregister(void) return run_command(&config_unset); } +#define BEGIN_LINE "# BEGIN GIT MAINTENANCE SCHEDULE" +#define END_LINE "# END GIT MAINTENANCE SCHEDULE" + +static int update_background_schedule(int run_maintenance) +{ + int result = 0; + int in_old_region = 0; + struct child_process crontab_list = CHILD_PROCESS_INIT; + struct child_process crontab_edit = CHILD_PROCESS_INIT; + FILE *cron_list, *cron_in; + const char *crontab_name; + struct strbuf line = STRBUF_INIT; + struct lock_file lk; + char *lock_path = xstrfmt("%s/schedule", the_repository->objects->odb->path); + + if (hold_lock_file_for_update(&lk, lock_path, LOCK_NO_DEREF) < 0) + return error(_("another process is scheduling background maintenance")); + + crontab_name = getenv("GIT_TEST_CRONTAB"); + if (!crontab_name) + crontab_name = "crontab"; + + strvec_split(&crontab_list.args, crontab_name); + strvec_push(&crontab_list.args, "-l"); + crontab_list.in = -1; + crontab_list.out = dup(lk.tempfile->fd); + crontab_list.git_cmd = 0; + + if (start_command(&crontab_list)) { + result = error(_("failed to run 'crontab -l'; your system might not support 'cron'")); + goto cleanup; + } + + /* Ignore exit code, as an empty crontab will return error. */ + finish_command(&crontab_list); + + /* + * Read from the .lock file, filtering out the old + * schedule while appending the new schedule. + */ + cron_list = fdopen(lk.tempfile->fd, "r"); + rewind(cron_list); + + strvec_split(&crontab_edit.args, crontab_name); + crontab_edit.in = -1; + crontab_edit.git_cmd = 0; + + if (start_command(&crontab_edit)) { + result = error(_("failed to run 'crontab'; your system might not support 'cron'")); + goto cleanup; + } + + cron_in = fdopen(crontab_edit.in, "w"); + if (!cron_in) { + result = error(_("failed to open stdin of 'crontab'")); + goto done_editing; + } + + while (!strbuf_getline_lf(&line, cron_list)) { + if (!in_old_region && !strcmp(line.buf, BEGIN_LINE)) + in_old_region = 1; + if (in_old_region) + continue; + fprintf(cron_in, "%s\n", line.buf); + if (in_old_region && !strcmp(line.buf, END_LINE)) + in_old_region = 0; + } + + if (run_maintenance) { + const char *exec_path = git_exec_path(); + + fprintf(cron_in, "\n%s\n", BEGIN_LINE); + fprintf(cron_in, "# The following schedule was created by Git\n"); + fprintf(cron_in, "# Any edits made in this region might be\n"); + fprintf(cron_in, "# replaced in the future by a Git command.\n\n"); + + fprintf(cron_in, + "0 * * * * \"%s/git\" --exec-path=\"%s\" for-each-repo --config=maintenance.repo maintenance run --scheduled\n", + exec_path, exec_path); + + fprintf(cron_in, "\n%s\n", END_LINE); + } + + fflush(cron_in); + fclose(cron_in); + close(crontab_edit.in); + +done_editing: + if (finish_command(&crontab_edit)) { + result = error(_("'crontab' died")); + goto cleanup; + } + fclose(cron_list); + +cleanup: + rollback_lock_file(&lk); + return result; +} + +static int maintenance_start(void) +{ + if (maintenance_register()) + warning(_("failed to add repo to global config")); + + return update_background_schedule(1); +} + +static int maintenance_stop(void) +{ + return update_background_schedule(0); +} + int cmd_maintenance(int argc, const char **argv, const char *prefix) { int i; @@ -1539,6 +1652,10 @@ int cmd_maintenance(int argc, const char **argv, const char *prefix) return maintenance_register(); if (!strcmp(argv[0], "run")) return maintenance_run(&opts); + if (!strcmp(argv[0], "start")) + return maintenance_start(); + if (!strcmp(argv[0], "stop")) + return maintenance_stop(); if (!strcmp(argv[0], "unregister")) return maintenance_unregister(); diff --git a/t/helper/test-crontab.c b/t/helper/test-crontab.c new file mode 100644 index 0000000000..f5db6319c6 --- /dev/null +++ b/t/helper/test-crontab.c @@ -0,0 +1,35 @@ +#include "test-tool.h" +#include "cache.h" + +/* + * Usage: test-tool cron [-l] + * + * If -l is specified, then write the contents of to stdou. + * Otherwise, write from stdin into . + */ +int cmd__crontab(int argc, const char **argv) +{ + char a; + FILE *from, *to; + + if (argc == 3 && !strcmp(argv[2], "-l")) { + from = fopen(argv[1], "r"); + if (!from) + return 0; + to = stdout; + } else if (argc == 2) { + from = stdin; + to = fopen(argv[1], "w"); + } else + return error("unknown arguments"); + + while ((a = fgetc(from)) != EOF) + fputc(a, to); + + if (argc == 3) + fclose(from); + else + fclose(to); + + return 0; +} diff --git a/t/helper/test-tool.c b/t/helper/test-tool.c index 590b2efca7..432b49d948 100644 --- a/t/helper/test-tool.c +++ b/t/helper/test-tool.c @@ -18,6 +18,7 @@ static struct test_cmd cmds[] = { { "bloom", cmd__bloom }, { "chmtime", cmd__chmtime }, { "config", cmd__config }, + { "crontab", cmd__crontab }, { "ctype", cmd__ctype }, { "date", cmd__date }, { "delta", cmd__delta }, diff --git a/t/helper/test-tool.h b/t/helper/test-tool.h index ddc8e990e9..7c3281e071 100644 --- a/t/helper/test-tool.h +++ b/t/helper/test-tool.h @@ -8,6 +8,7 @@ int cmd__advise_if_enabled(int argc, const char **argv); int cmd__bloom(int argc, const char **argv); int cmd__chmtime(int argc, const char **argv); int cmd__config(int argc, const char **argv); +int cmd__crontab(int argc, const char **argv); int cmd__ctype(int argc, const char **argv); int cmd__date(int argc, const char **argv); int cmd__delta(int argc, const char **argv); diff --git a/t/t7900-maintenance.sh b/t/t7900-maintenance.sh index b20ee2d542..6491031be8 100755 --- a/t/t7900-maintenance.sh +++ b/t/t7900-maintenance.sh @@ -309,4 +309,34 @@ test_expect_success 'register and unregister' ' test_cmp before actual ' +test_expect_success 'start from empty cron table' ' + GIT_TEST_CRONTAB="test-tool crontab cron.txt" git maintenance start && + + # start registers the repo + git config --get --global maintenance.repo "$(pwd)" && + + grep "for-each-repo --config=maintenance.repo maintenance run --scheduled" cron.txt +' + +test_expect_success 'stop from existing schedule' ' + GIT_TEST_CRONTAB="test-tool crontab cron.txt" git maintenance stop && + + # stop does not unregister the repo + git config --get --global maintenance.repo "$(pwd)" && + + # The newline is preserved + echo >empty && + test_cmp empty cron.txt && + + # Operation is idempotent + GIT_TEST_CRONTAB="test-tool crontab cron.txt" git maintenance stop && + test_cmp empty cron.txt +' + +test_expect_success 'start preserves existing schedule' ' + echo "Important information!" >cron.txt && + GIT_TEST_CRONTAB="test-tool crontab cron.txt" git maintenance start && + grep "Important information!" cron.txt +' + test_done diff --git a/t/test-lib.sh b/t/test-lib.sh index ef31f40037..4a60d1ed76 100644 --- a/t/test-lib.sh +++ b/t/test-lib.sh @@ -1702,3 +1702,9 @@ test_lazy_prereq SHA1 ' test_lazy_prereq REBASE_P ' test -z "$GIT_TEST_SKIP_REBASE_P" ' + +# Ensure that no test accidentally triggers a Git command +# that runs 'crontab', affecting a user's cron schedule. +# Tests that verify the cron integration must set this locally +# to avoid errors. +GIT_TEST_CRONTAB="exit 1" From patchwork Wed Aug 19 17:16:48 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Philippe Blain via GitGitGadget X-Patchwork-Id: 11724703 Return-Path: Received: from mail.kernel.org (pdx-korg-mail-1.web.codeaurora.org [172.30.200.123]) by pdx-korg-patchwork-2.web.codeaurora.org (Postfix) with ESMTP id 1FB9013A4 for ; Wed, 19 Aug 2020 17:17:45 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by mail.kernel.org (Postfix) with ESMTP id F18F320888 for ; Wed, 19 Aug 2020 17:17:44 +0000 (UTC) Authentication-Results: mail.kernel.org; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b="gMxK/FdF" Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1726792AbgHSRRm (ORCPT ); Wed, 19 Aug 2020 13:17:42 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:48192 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1726703AbgHSRRN (ORCPT ); Wed, 19 Aug 2020 13:17:13 -0400 Received: from mail-wr1-x443.google.com (mail-wr1-x443.google.com [IPv6:2a00:1450:4864:20::443]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id D6714C061346 for ; Wed, 19 Aug 2020 10:17:12 -0700 (PDT) Received: by mail-wr1-x443.google.com with SMTP id p20so22291848wrf.0 for ; Wed, 19 Aug 2020 10:17:12 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20161025; h=message-id:in-reply-to:references:from:date:subject:fcc :content-transfer-encoding:mime-version:to:cc; bh=pH+4E0BOt9rRfHDMb5OXdDsL4Wzt5LX8RegcuNrPwMg=; b=gMxK/FdFmxo37qKRq0//S1m0hpxmL1ONr0Riv3c3Yg16w9gm41RCGhUF9+e4HHif1N Nc4LsLls3XB9NUrrbdXkkYj6FT0YbCaRlID6vKz0TWqi1S4bnOuDw14+pARvNFSVY2H8 es5FAv+T5R6ZgS6xURWaNDiwShcmtEhv7GgsWNSvaU+aGKGF1UU9wBo0QSPspkrlQWGA ZvxouwdYk1WrSamrq+YZENfgoGhgl6Uz7Ce9cPJ+X8ZPhUoYtEXQ/U+K3d3G1FVR6EgV i/ElDdXUPYxbl1OaeshOfKCNZUhjRKwIPMLj27kfuH47K7aHlMFEAush0FSmk8HrVv5i newg== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:message-id:in-reply-to:references:from:date :subject:fcc:content-transfer-encoding:mime-version:to:cc; bh=pH+4E0BOt9rRfHDMb5OXdDsL4Wzt5LX8RegcuNrPwMg=; b=hDheUDrgh0iP2nxYrpbEohmf47Jg8C/RHSS6Hq3GYJXIsQ11ColKHqwHoYyBLQPXp2 Nick/q6Ds13RS1XPTLihn0j3mla9L4kzonMEPt22r0m+amZzXFJ0OpY3as7OzanVyTzj J18tU3hoEULS/Jawvm+p3xVQJDHFSu/LTmGkAlxEOtazXkjJuzr9cwxSuMmzowEx8q1v e1+tv0q21bMzaZQ/sCkGOpMnVulWSsNKI0mubseVIneF1DioE5fOz+ra7a+aUizn1owK zwqJkkuaMBRjfBO1L3BlGUGKSgbofQrKfrXLZtl5GJgHdBF7t8cLDHtruxGf3zVJSzrZ jqVw== X-Gm-Message-State: AOAM5327YzhXL9X8aeFkIVKaIAkjZuuxBGJKF8ig8V9T7Pl3SYbkT6pA jNtXTxn9DiIrTv+w35xuLdEN/bXMu4M= X-Google-Smtp-Source: ABdhPJyHYyru7jlIbJO4QMS4Pc1VGm///kbZmzDLfxVPvnzw85ANpUoZmxhvvLzD2x0LnlAZmuvBLQ== X-Received: by 2002:a5d:49c9:: with SMTP id t9mr12292638wrs.302.1597857431383; Wed, 19 Aug 2020 10:17:11 -0700 (PDT) Received: from [127.0.0.1] ([13.74.141.28]) by smtp.gmail.com with ESMTPSA id j4sm6335847wmi.48.2020.08.19.10.17.10 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Wed, 19 Aug 2020 10:17:10 -0700 (PDT) Message-Id: <40b1a0546ccea737df608aaffe90edf56377b4ce.1597857412.git.gitgitgadget@gmail.com> In-Reply-To: References: From: "Derrick Stolee via GitGitGadget" Date: Wed, 19 Aug 2020 17:16:48 +0000 Subject: [PATCH 7/7] maintenance: recommended schedule in register/start Fcc: Sent MIME-Version: 1.0 To: git@vger.kernel.org Cc: sandals@crustytoothpaste.net, steadmon@google.com, jrnieder@gmail.com, peff@peff.net, congdanhqx@gmail.com, phillip.wood123@gmail.com, emilyshaffer@google.com, sluongng@gmail.com, jonathantanmy@google.com, Derrick Stolee , Derrick Stolee Sender: git-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: git@vger.kernel.org From: Derrick Stolee The 'git maintenance (register|start)' subcommands add the current repository to the global Git config so maintenance will operate on that repository. It does not specify what maintenance should occur or how often. If a user sets any 'maintenance..scheduled' config value, then they have chosen a specific schedule for themselves and Git should respect that. However, in an effort to recommend a good schedule for repositories of all sizes, set new config values for recommended tasks that are safe to run in the background while users run foreground Git commands. These commands are generally everything but the 'gc' task. Author's Note: I feel we should do _something_ to recommend a good schedule to users, but I'm not 100% set on this schedule. This is the schedule we use in Scalar and VFS for Git for very large repositories using the GVFS protocol. While the schedule works in that environment, it is possible that "normal" Git repositories could benefit from something more obvious (such as running 'gc' once a day). However, this patch gives us a place to start a conversation on what we should recommend. For my purposes, Scalar will set these config values so we can always differ from core Git's recommendations. Signed-off-by: Derrick Stolee --- Documentation/git-maintenance.txt | 6 +++++ builtin/gc.c | 44 +++++++++++++++++++++++++++++++ t/t7900-maintenance.sh | 5 ++++ 3 files changed, 55 insertions(+) diff --git a/Documentation/git-maintenance.txt b/Documentation/git-maintenance.txt index 600272caa5..a4b46ea329 100644 --- a/Documentation/git-maintenance.txt +++ b/Documentation/git-maintenance.txt @@ -37,6 +37,12 @@ register:: `maintenance..schedule`. The tasks that are enabled are safe for running in the background without disrupting foreground processes. ++ +If your repository has no 'maintenance..schedule' configuration +values set, then Git will set configuration values to some recommended +settings. These settings disable foreground maintenance while performing +maintenance tasks in the background that will not interrupt foreground Git +operations. run:: Run one or more maintenance tasks. If one or more `--task` options diff --git a/builtin/gc.c b/builtin/gc.c index b09287f0fc..080d58735c 100644 --- a/builtin/gc.c +++ b/builtin/gc.c @@ -1446,6 +1446,47 @@ static int task_option_parse(const struct option *opt, return 0; } +static int has_schedule_config(void) +{ + int i, found = 0; + struct strbuf config_name = STRBUF_INIT; + size_t prefix; + + strbuf_addstr(&config_name, "maintenance."); + prefix = config_name.len; + + for (i = 0; !found && i < TASK__COUNT; i++) { + int value; + + strbuf_setlen(&config_name, prefix); + strbuf_addf(&config_name, "%s.schedule", tasks[i].name); + + if (!git_config_get_int(config_name.buf, &value)) + found = 1; + } + + strbuf_release(&config_name); + return found; +} + +static void set_recommended_schedule(void) +{ + git_config_set("maintenance.auto", "false"); + git_config_set("maintenance.gc.enabled", "false"); + + git_config_set("maintenance.prefetch.enabled", "true"); + git_config_set("maintenance.prefetch.schedule", "3500"); + + git_config_set("maintenance.commit-graph.enabled", "true"); + git_config_set("maintenance.commit-graph.schedule", "3500"); + + git_config_set("maintenance.loose-objects.enabled", "true"); + git_config_set("maintenance.loose-objects.schedule", "86000"); + + git_config_set("maintenance.incremental-repack.enabled", "true"); + git_config_set("maintenance.incremental-repack.schedule", "86000"); +} + static int maintenance_register(void) { struct child_process config_set = CHILD_PROCESS_INIT; @@ -1455,6 +1496,9 @@ static int maintenance_register(void) if (!the_repository || !the_repository->gitdir) return 0; + if (has_schedule_config()) + set_recommended_schedule(); + config_get.git_cmd = 1; strvec_pushl(&config_get.args, "config", "--global", "--get", "maintenance.repo", the_repository->worktree ? the_repository->worktree diff --git a/t/t7900-maintenance.sh b/t/t7900-maintenance.sh index 6491031be8..7417e5858a 100755 --- a/t/t7900-maintenance.sh +++ b/t/t7900-maintenance.sh @@ -300,6 +300,11 @@ test_expect_success 'register and unregister' ' git config --global --add maintenance.repo /existing2 && git config --global --get-all maintenance.repo >before && git maintenance register && + test_cmp_config false maintenance.auto && + test_cmp_config false maintenance.gc.enabled && + test_cmp_config true maintenance.prefetch.enabled && + test_cmp_config 3500 maintenance.commit-graph.schedule && + test_cmp_config 86000 maintenance.incremental-repack.schedule && git config --global --get-all maintenance.repo >actual && cp before after && pwd >>after &&