@@ -9,7 +9,7 @@ git-run-job - Run a maintenance job. Intended for background operation.
SYNOPSIS
--------
[verse]
-'git run-job (commit-graph|fetch)'
+'git run-job (commit-graph|fetch|loose-objects)'
DESCRIPTION
@@ -59,6 +59,18 @@ 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.
+'loose-objects'::
+
+The `loose-objects` job cleans up loose objects and places them into
+pack-files. In order to prevent race conditions with concurrent Git
+commands, it follows a two-step process. First, it deletes any loose
+objects that already exist in a pack-file; concurrent Git processes will
+examine the pack-file for the object data instead of the loose object.
+Second, it creates a new pack-file (starting with "loose-") containing
+a batch of loose objects. The batch size is limited to 50 thousand
+objects to prevent the job from taking too long on a repository with
+many loose objects.
+
GIT
---
Part of the linkgit:git[1] suite
@@ -7,7 +7,7 @@
#include "run-command.h"
static char const * const builtin_run_job_usage[] = {
- N_("git run-job (commit-graph|fetch)"),
+ N_("git run-job (commit-graph|fetch|loose-objects)"),
NULL
};
@@ -145,6 +145,99 @@ static int run_fetch_job(void)
return result;
}
+static int prune_packed(void)
+{
+ struct argv_array cmd = ARGV_ARRAY_INIT;
+ argv_array_pushl(&cmd, "prune-packed", NULL);
+ return run_command_v_opt(cmd.argv, RUN_GIT_CMD);
+}
+
+struct write_loose_object_data {
+ FILE *in;
+ int count;
+ int batch_size;
+};
+
+static int loose_object_exists(const struct object_id *oid,
+ const char *path,
+ void *data)
+{
+ return 1;
+}
+
+static int write_loose_object_to_stdin(const struct object_id *oid,
+ const char *path,
+ void *data)
+{
+ struct write_loose_object_data *d = (struct write_loose_object_data *)data;
+
+ fprintf(d->in, "%s\n", oid_to_hex(oid));
+
+ return ++(d->count) > d->batch_size;
+}
+
+static int pack_loose(void)
+{
+ int result = 0;
+ struct write_loose_object_data data;
+ struct strbuf prefix = STRBUF_INIT;
+ struct child_process *pack_proc;
+
+ /*
+ * Do not start pack-objects process
+ * if there are no loose objects.
+ */
+ if (!for_each_loose_file_in_objdir(the_repository->objects->odb->path,
+ loose_object_exists,
+ NULL, NULL, NULL))
+ return 0;
+
+ pack_proc = xmalloc(sizeof(*pack_proc));
+
+ child_process_init(pack_proc);
+
+ strbuf_addstr(&prefix, the_repository->objects->odb->path);
+ strbuf_addstr(&prefix, "/pack/loose");
+
+ argv_array_pushl(&pack_proc->args, "git", "pack-objects",
+ "--quiet", prefix.buf, NULL);
+
+ pack_proc->in = -1;
+
+ if (start_command(pack_proc)) {
+ error(_("failed to start 'git pack-objects' process"));
+ result = 1;
+ goto cleanup;
+ }
+
+ data.in = xfdopen(pack_proc->in, "w");
+ data.count = 0;
+ data.batch_size = 50000;
+
+ for_each_loose_file_in_objdir(the_repository->objects->odb->path,
+ write_loose_object_to_stdin,
+ NULL,
+ NULL,
+ &data);
+
+ fclose(data.in);
+
+ if (finish_command(pack_proc)) {
+ error(_("failed to finish 'git pack-objects' process"));
+ result = 1;
+ }
+
+cleanup:
+ strbuf_release(&prefix);
+ free(pack_proc);
+ return result;
+}
+
+static int run_loose_objects_job(void)
+{
+ return prune_packed() || pack_loose();
+}
+
int cmd_run_job(int argc, const char **argv, const char *prefix)
{
static struct option builtin_run_job_options[] = {
@@ -166,6 +259,8 @@ int cmd_run_job(int argc, const char **argv, const char *prefix)
return run_commit_graph_job();
if (!strcmp(argv[0], "fetch"))
return run_fetch_job();
+ if (!strcmp(argv[0], "loose-objects"))
+ return run_loose_objects_job();
}
usage_with_options(builtin_run_job_usage,
@@ -66,4 +66,31 @@ test_expect_success 'fetch job' '
test_line_count = 2 $chain
'
+test_expect_success 'loose-objects job' '
+ ls client/.git/objects >obj-dir-before &&
+ test_file_not_empty obj-dir-before &&
+ ls client/.git/objects/pack/*.pack >packs-before &&
+ test_line_count = 1 packs-before &&
+
+ # The first run creates a pack-file
+ # but does not delete loose objects.
+ git -C client run-job loose-objects &&
+ ls client/.git/objects >obj-dir-between &&
+ test_cmp obj-dir-before obj-dir-between &&
+ ls client/.git/objects/pack/*.pack >packs-between &&
+ test_line_count = 2 packs-between &&
+
+ # The second run deletes loose objects
+ # but does not create a pack-file.
+ git -C client run-job loose-objects &&
+ ls client/.git/objects >obj-dir-after &&
+ cat >expect <<-\EOF &&
+ info
+ pack
+ EOF
+ test_cmp expect obj-dir-after &&
+ ls client/.git/objects/pack/*.pack >packs-after &&
+ test_cmp packs-between packs-after
+'
+
test_done