@@ -53,6 +53,18 @@ since it will not expire `.graph` files that were in the previous
`commit-graph-chain` file. They will be deleted by a later run based on
the expiration delay.
+fetch::
+ The `fetch` job updates the object directory with the latest objects
+ from all registered remotes. For each remote, a `git fetch` command
+ is run. The refmap is custom to avoid updating local or remote
+ branches (those in `refs/heads` or `refs/remotes`). Instead, the
+ remote refs are stored in `refs/hidden/<remote>/`. Also, no tags are
+ updated.
++
+This means that foreground fetches are still required to update the
+remote refs, but the users is notified when the branches and tags are
+updated on the remote.
+
gc::
Cleanup unnecessary files and optimize the local repository. "GC"
stands for "garbage collection," but this task performs many
@@ -28,6 +28,7 @@
#include "blob.h"
#include "tree.h"
#include "promisor-remote.h"
+#include "remote.h"
#define FAILED_RUN "failed to run %s"
@@ -705,7 +706,7 @@ int cmd_gc(int argc, const char **argv, const char *prefix)
return 0;
}
-#define MAX_NUM_TASKS 2
+#define MAX_NUM_TASKS 3
static const char * const builtin_maintenance_usage[] = {
N_("git maintenance run [<options>]"),
@@ -788,6 +789,64 @@ static int maintenance_task_commit_graph(struct repository *r)
return 1;
}
+static int fetch_remote(struct repository *r, const char *remote)
+{
+ int result;
+ struct argv_array cmd = ARGV_ARRAY_INIT;
+ struct strbuf refmap = STRBUF_INIT;
+
+ argv_array_pushl(&cmd, "-C", r->worktree,
+ "fetch", remote, "--prune",
+ "--no-tags", "--refmap=", NULL);
+
+ strbuf_addf(&refmap, "+refs/heads/*:refs/hidden/%s/*", remote);
+ argv_array_push(&cmd, refmap.buf);
+
+ if (opts.quiet)
+ argv_array_push(&cmd, "--quiet");
+
+ result = run_command_v_opt(cmd.argv, RUN_GIT_CMD);
+
+ strbuf_release(&refmap);
+ return result;
+}
+
+static int fill_each_remote(struct remote *remote, void *cbdata)
+{
+ struct string_list *remotes = (struct string_list *)cbdata;
+
+ string_list_append(remotes, remote->name);
+ return 0;
+}
+
+static int maintenance_task_fetch(struct repository *r)
+{
+ int result = 0;
+ struct string_list_item *item;
+ struct string_list remotes = STRING_LIST_INIT_DUP;
+
+ if (for_each_remote(fill_each_remote, &remotes)) {
+ error(_("failed to fill remotes"));
+ result = 1;
+ goto cleanup;
+ }
+
+ /*
+ * Do not modify the result based on the success of the 'fetch'
+ * operation, as a loss of network could cause 'fetch' to fail
+ * quickly. We do not want that to stop the rest of our
+ * background operations.
+ */
+ for (item = remotes.items;
+ item && item < remotes.items + remotes.nr;
+ item++)
+ fetch_remote(r, item->string);
+
+cleanup:
+ string_list_clear(&remotes, 0);
+ return result;
+}
+
static int maintenance_task_gc(struct repository *r)
{
int result;
@@ -893,6 +952,10 @@ static void initialize_tasks(void)
for (i = 0; i < MAX_NUM_TASKS; i++)
tasks[i] = xcalloc(1, sizeof(struct maintenance_task));
+ tasks[num_tasks]->name = "fetch";
+ tasks[num_tasks]->fn = maintenance_task_fetch;
+ num_tasks++;
+
tasks[num_tasks]->name = "gc";
tasks[num_tasks]->fn = maintenance_task_gc;
tasks[num_tasks]->enabled = 1;
@@ -44,4 +44,28 @@ test_expect_success 'run --task duplicate' '
test_i18ngrep "cannot be selected multiple times" err
'
+test_expect_success 'run --task=fetch with no remotes' '
+ git maintenance run --task=fetch 2>err &&
+ test_must_be_empty err
+'
+
+test_expect_success 'fetch multiple remotes' '
+ git clone . clone1 &&
+ git clone . clone2 &&
+ git remote add remote1 "file://$(pwd)/clone1" &&
+ git remote add remote2 "file://$(pwd)/clone2" &&
+ git -C clone1 switch -c one &&
+ git -C clone2 switch -c two &&
+ test_commit -C clone1 one &&
+ test_commit -C clone2 two &&
+ GIT_TRACE2_EVENT="$(pwd)/run-fetch.txt" git maintenance run --task=fetch &&
+ grep ",\"fetch\",\"remote1\"" run-fetch.txt &&
+ grep ",\"fetch\",\"remote2\"" run-fetch.txt &&
+ test_path_is_missing .git/refs/remotes &&
+ test_cmp clone1/.git/refs/heads/one .git/refs/hidden/remote1/one &&
+ test_cmp clone2/.git/refs/heads/two .git/refs/hidden/remote2/two &&
+ git log hidden/remote1/one &&
+ git log hidden/remote2/two
+'
+
test_done