]> git.ipfire.org Git - thirdparty/git.git/commitdiff
builtin/receive-pack: stage incoming objects via ODB transactions
authorJustin Tobler <jltobler@gmail.com>
Fri, 10 Jul 2026 16:37:22 +0000 (11:37 -0500)
committerJunio C Hamano <gitster@pobox.com>
Fri, 10 Jul 2026 20:21:53 +0000 (13:21 -0700)
Objects received by git-receive-pack(1) are quarantined in a temporary
"incoming" directory and migrated into the object database prior to the
reference updates. The quarantine is currently managed through
`tmp_objdir` directly. In a pluggable ODB future, how exactly an object
gets written to a transaction may vary for a given ODB source. Refactor
git-receive-pack(1) to use the ODB transaction interfaces to manage the
object staging area in a more agnostic manner accordingly.

Note that the ODB transaction is now responsible for managing the
primary and alternate ODBs for the repository. One small change as a
result is that the temporary directory is now applied as the primary ODB
in the main process instead of an alternate. This does not change
anything for git-receive-pack(1) though because it only needs access to
the newly written objects and doesn't care how exactly it is set up.

Signed-off-by: Justin Tobler <jltobler@gmail.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
builtin/receive-pack.c

index 50bc05c70c24e843380d03b615c16de671e66be1..8b8c20dc1aed76a0ec4b13593c7c6930a9965671 100644 (file)
@@ -37,7 +37,6 @@
 #include "sigchain.h"
 #include "string-list.h"
 #include "strvec.h"
-#include "tmp-objdir.h"
 #include "trace.h"
 #include "trace2.h"
 #include "version.h"
@@ -112,8 +111,6 @@ static enum {
 } use_keepalive;
 static int keepalive_in_sec = 5;
 
-static struct tmp_objdir *tmp_objdir;
-
 static struct proc_receive_ref {
        unsigned int want_add:1,
                     want_delete:1,
@@ -926,6 +923,7 @@ static void receive_hook_feed_state_free(void *data)
 static int run_receive_hook(struct command *commands,
                            const char *hook_name,
                            int skip_broken,
+                           struct odb_transaction *transaction,
                            const struct string_list *push_options)
 {
        struct run_hooks_opt opt = RUN_HOOKS_OPT_INIT;
@@ -959,8 +957,8 @@ static int run_receive_hook(struct command *commands,
                strvec_push(&opt.env, "GIT_PUSH_OPTION_COUNT");
        }
 
-       if (tmp_objdir)
-               strvec_pushv(&opt.env, tmp_objdir_env(tmp_objdir));
+       if (transaction)
+               odb_transaction_env(transaction, &opt.env);
 
        prepare_push_cert_sha1(&opt);
 
@@ -1789,24 +1787,30 @@ static const struct object_id *command_singleton_iterator(void *cb_data)
 }
 
 static void set_connectivity_errors(struct command *commands,
-                                   struct shallow_info *si)
+                                   struct shallow_info *si,
+                                   struct odb_transaction *transaction)
 {
        struct command *cmd;
 
        for (cmd = commands; cmd; cmd = cmd->next) {
                struct command *singleton = cmd;
                struct check_connected_options opt = CHECK_CONNECTED_INIT;
+               struct strvec env = STRVEC_INIT;
 
                if (shallow_update && si->shallow_ref[cmd->index])
                        /* to be checked in update_shallow_ref() */
                        continue;
 
-               opt.env = tmp_objdir_env(tmp_objdir);
+               odb_transaction_env(transaction, &env);
+               opt.env = env.v;
+
                if (!check_connected(command_singleton_iterator, &singleton,
                                     &opt))
                        continue;
 
                cmd->error_string = "missing necessary objects";
+
+               strvec_clear(&env);
        }
 }
 
@@ -2027,6 +2031,7 @@ cleanup:
 static void execute_commands(struct command *commands,
                             const char *unpacker_error,
                             struct shallow_info *si,
+                            struct odb_transaction *transaction,
                             const struct string_list *push_options)
 {
        struct check_connected_options opt = CHECK_CONNECTED_INIT;
@@ -2043,6 +2048,8 @@ static void execute_commands(struct command *commands,
        }
 
        if (!skip_connectivity_check) {
+               struct strvec env = STRVEC_INIT;
+
                if (use_sideband) {
                        memset(&muxer, 0, sizeof(muxer));
                        muxer.proc = copy_to_sideband;
@@ -2056,14 +2063,17 @@ static void execute_commands(struct command *commands,
                data.si = si;
                opt.err_fd = err_fd;
                opt.progress = err_fd && !quiet;
-               opt.env = tmp_objdir_env(tmp_objdir);
+               odb_transaction_env(transaction, &env);
+               opt.env = env.v;
                opt.exclude_hidden_refs_section = "receive";
 
                if (check_connected(iterate_receive_command_list, &data, &opt))
-                       set_connectivity_errors(commands, si);
+                       set_connectivity_errors(commands, si, transaction);
 
                if (use_sideband)
                        finish_async(&muxer);
+
+               strvec_clear(&env);
        }
 
        reject_updates_to_hidden(commands);
@@ -2084,7 +2094,7 @@ static void execute_commands(struct command *commands,
                }
        }
 
-       if (run_receive_hook(commands, "pre-receive", 0, push_options)) {
+       if (run_receive_hook(commands, "pre-receive", 0, transaction, push_options)) {
                for (cmd = commands; cmd; cmd = cmd->next) {
                        if (!cmd->error_string)
                                cmd->error_string = "pre-receive hook declined";
@@ -2105,14 +2115,13 @@ static void execute_commands(struct command *commands,
         * Now we'll start writing out refs, which means the objects need
         * to be in their final positions so that other processes can see them.
         */
-       if (tmp_objdir_migrate(tmp_objdir) < 0) {
+       if (odb_transaction_commit(transaction)) {
                for (cmd = commands; cmd; cmd = cmd->next) {
                        if (!cmd->error_string)
                                cmd->error_string = "unable to migrate objects to permanent storage";
                }
                return;
        }
-       tmp_objdir = NULL;
 
        check_aliased_updates(commands);
 
@@ -2325,7 +2334,8 @@ static void push_header_arg(struct strvec *args, struct pack_header *hdr)
                     ntohl(hdr->hdr_version), ntohl(hdr->hdr_entries));
 }
 
-static const char *unpack(int err_fd, struct shallow_info *si)
+static const char *unpack(int err_fd, struct shallow_info *si,
+                         struct odb_transaction *transaction)
 {
        struct pack_header hdr;
        const char *hdr_err;
@@ -2350,20 +2360,7 @@ static const char *unpack(int err_fd, struct shallow_info *si)
                strvec_push(&child.args, alt_shallow_file);
        }
 
-       tmp_objdir = tmp_objdir_create(the_repository, "incoming");
-       if (!tmp_objdir) {
-               if (err_fd > 0)
-                       close(err_fd);
-               return "unable to create temporary object directory";
-       }
-       strvec_pushv(&child.env, tmp_objdir_env(tmp_objdir));
-
-       /*
-        * Normally we just pass the tmp_objdir environment to the child
-        * processes that do the heavy lifting, but we may need to see these
-        * objects ourselves to set up shallow information.
-        */
-       tmp_objdir_add_as_alternate(tmp_objdir);
+       odb_transaction_env(transaction, &child.env);
 
        if (ntohl(hdr.hdr_entries) < unpack_limit) {
                strvec_push(&child.args, "unpack-objects");
@@ -2430,13 +2427,14 @@ static const char *unpack(int err_fd, struct shallow_info *si)
        return NULL;
 }
 
-static const char *unpack_with_sideband(struct shallow_info *si)
+static const char *unpack_with_sideband(struct shallow_info *si,
+                                       struct odb_transaction *transaction)
 {
        struct async muxer;
        const char *ret;
 
        if (!use_sideband)
-               return unpack(0, si);
+               return unpack(0, si, transaction);
 
        use_keepalive = KEEPALIVE_AFTER_NUL;
        memset(&muxer, 0, sizeof(muxer));
@@ -2445,7 +2443,7 @@ static const char *unpack_with_sideband(struct shallow_info *si)
        if (start_async(&muxer))
                return NULL;
 
-       ret = unpack(muxer.in, si);
+       ret = unpack(muxer.in, si, transaction);
 
        finish_async(&muxer);
        return ret;
@@ -2622,6 +2620,7 @@ int cmd_receive_pack(int argc,
        struct oid_array ref = OID_ARRAY_INIT;
        struct shallow_info si;
        struct packet_reader reader;
+       struct odb_transaction *transaction = NULL;
 
        struct option options[] = {
                OPT__QUIET(&quiet, N_("quiet")),
@@ -2706,11 +2705,14 @@ int cmd_receive_pack(int argc,
                if (!si.nr_ours && !si.nr_theirs)
                        shallow_update = 0;
                if (!delete_only(commands)) {
-                       unpack_status = unpack_with_sideband(&si);
+                       if (odb_transaction_begin(the_repository->objects, &transaction, ODB_TRANSACTION_RECEIVE))
+                               unpack_status = "unable to start object transaction";
+                       else
+                               unpack_status = unpack_with_sideband(&si, transaction);
                        update_shallow_info(commands, &si, &ref);
                }
                use_keepalive = KEEPALIVE_ALWAYS;
-               execute_commands(commands, unpack_status, &si,
+               execute_commands(commands, unpack_status, &si, transaction,
                                 &push_options);
                delete_tempfile(&pack_lockfile);
                sigchain_push(SIGPIPE, SIG_IGN);
@@ -2719,7 +2721,7 @@ int cmd_receive_pack(int argc,
                else if (report_status)
                        report(commands, unpack_status);
                sigchain_pop(SIGPIPE);
-               run_receive_hook(commands, "post-receive", 1,
+               run_receive_hook(commands, "post-receive", 1, NULL,
                                 &push_options);
                run_update_post_hook(commands);
                free_commands(commands);