builtin/receive-pack: stage incoming objects via ODB transactions

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>
main
Justin Tobler 2026-07-10 11:37:22 -05:00 committed by Junio C Hamano
parent 6c242569c1
commit bdee7b3013
1 changed files with 35 additions and 33 deletions

View File

@ -37,7 +37,6 @@
#include "sigchain.h" #include "sigchain.h"
#include "string-list.h" #include "string-list.h"
#include "strvec.h" #include "strvec.h"
#include "tmp-objdir.h"
#include "trace.h" #include "trace.h"
#include "trace2.h" #include "trace2.h"
#include "version.h" #include "version.h"
@ -112,8 +111,6 @@ static enum {
} use_keepalive; } use_keepalive;
static int keepalive_in_sec = 5; static int keepalive_in_sec = 5;


static struct tmp_objdir *tmp_objdir;

static struct proc_receive_ref { static struct proc_receive_ref {
unsigned int want_add:1, unsigned int want_add:1,
want_delete: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, static int run_receive_hook(struct command *commands,
const char *hook_name, const char *hook_name,
int skip_broken, int skip_broken,
struct odb_transaction *transaction,
const struct string_list *push_options) const struct string_list *push_options)
{ {
struct run_hooks_opt opt = RUN_HOOKS_OPT_INIT; 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"); strvec_push(&opt.env, "GIT_PUSH_OPTION_COUNT");
} }


if (tmp_objdir) if (transaction)
strvec_pushv(&opt.env, tmp_objdir_env(tmp_objdir)); odb_transaction_env(transaction, &opt.env);


prepare_push_cert_sha1(&opt); 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, static void set_connectivity_errors(struct command *commands,
struct shallow_info *si) struct shallow_info *si,
struct odb_transaction *transaction)
{ {
struct command *cmd; struct command *cmd;


for (cmd = commands; cmd; cmd = cmd->next) { for (cmd = commands; cmd; cmd = cmd->next) {
struct command *singleton = cmd; struct command *singleton = cmd;
struct check_connected_options opt = CHECK_CONNECTED_INIT; struct check_connected_options opt = CHECK_CONNECTED_INIT;
struct strvec env = STRVEC_INIT;


if (shallow_update && si->shallow_ref[cmd->index]) if (shallow_update && si->shallow_ref[cmd->index])
/* to be checked in update_shallow_ref() */ /* to be checked in update_shallow_ref() */
continue; continue;


opt.env = tmp_objdir_env(tmp_objdir); odb_transaction_env(transaction, &env);
opt.env = env.v;

if (!check_connected(command_singleton_iterator, &singleton, if (!check_connected(command_singleton_iterator, &singleton,
&opt)) &opt))
continue; continue;


cmd->error_string = "missing necessary objects"; cmd->error_string = "missing necessary objects";

strvec_clear(&env);
} }
} }


@ -2027,6 +2031,7 @@ cleanup:
static void execute_commands(struct command *commands, static void execute_commands(struct command *commands,
const char *unpacker_error, const char *unpacker_error,
struct shallow_info *si, struct shallow_info *si,
struct odb_transaction *transaction,
const struct string_list *push_options) const struct string_list *push_options)
{ {
struct check_connected_options opt = CHECK_CONNECTED_INIT; struct check_connected_options opt = CHECK_CONNECTED_INIT;
@ -2043,6 +2048,8 @@ static void execute_commands(struct command *commands,
} }


if (!skip_connectivity_check) { if (!skip_connectivity_check) {
struct strvec env = STRVEC_INIT;

if (use_sideband) { if (use_sideband) {
memset(&muxer, 0, sizeof(muxer)); memset(&muxer, 0, sizeof(muxer));
muxer.proc = copy_to_sideband; muxer.proc = copy_to_sideband;
@ -2056,14 +2063,17 @@ static void execute_commands(struct command *commands,
data.si = si; data.si = si;
opt.err_fd = err_fd; opt.err_fd = err_fd;
opt.progress = err_fd && !quiet; 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"; opt.exclude_hidden_refs_section = "receive";


if (check_connected(iterate_receive_command_list, &data, &opt)) if (check_connected(iterate_receive_command_list, &data, &opt))
set_connectivity_errors(commands, si); set_connectivity_errors(commands, si, transaction);


if (use_sideband) if (use_sideband)
finish_async(&muxer); finish_async(&muxer);

strvec_clear(&env);
} }


reject_updates_to_hidden(commands); 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) { for (cmd = commands; cmd; cmd = cmd->next) {
if (!cmd->error_string) if (!cmd->error_string)
cmd->error_string = "pre-receive hook declined"; 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 * 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. * 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) { for (cmd = commands; cmd; cmd = cmd->next) {
if (!cmd->error_string) if (!cmd->error_string)
cmd->error_string = "unable to migrate objects to permanent storage"; cmd->error_string = "unable to migrate objects to permanent storage";
} }
return; return;
} }
tmp_objdir = NULL;


check_aliased_updates(commands); 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)); 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; struct pack_header hdr;
const char *hdr_err; 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); strvec_push(&child.args, alt_shallow_file);
} }


tmp_objdir = tmp_objdir_create(the_repository, "incoming"); odb_transaction_env(transaction, &child.env);
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);


if (ntohl(hdr.hdr_entries) < unpack_limit) { if (ntohl(hdr.hdr_entries) < unpack_limit) {
strvec_push(&child.args, "unpack-objects"); strvec_push(&child.args, "unpack-objects");
@ -2430,13 +2427,14 @@ static const char *unpack(int err_fd, struct shallow_info *si)
return NULL; 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; struct async muxer;
const char *ret; const char *ret;


if (!use_sideband) if (!use_sideband)
return unpack(0, si); return unpack(0, si, transaction);


use_keepalive = KEEPALIVE_AFTER_NUL; use_keepalive = KEEPALIVE_AFTER_NUL;
memset(&muxer, 0, sizeof(muxer)); memset(&muxer, 0, sizeof(muxer));
@ -2445,7 +2443,7 @@ static const char *unpack_with_sideband(struct shallow_info *si)
if (start_async(&muxer)) if (start_async(&muxer))
return NULL; return NULL;


ret = unpack(muxer.in, si); ret = unpack(muxer.in, si, transaction);


finish_async(&muxer); finish_async(&muxer);
return ret; return ret;
@ -2622,6 +2620,7 @@ int cmd_receive_pack(int argc,
struct oid_array ref = OID_ARRAY_INIT; struct oid_array ref = OID_ARRAY_INIT;
struct shallow_info si; struct shallow_info si;
struct packet_reader reader; struct packet_reader reader;
struct odb_transaction *transaction = NULL;


struct option options[] = { struct option options[] = {
OPT__QUIET(&quiet, N_("quiet")), OPT__QUIET(&quiet, N_("quiet")),
@ -2706,11 +2705,14 @@ int cmd_receive_pack(int argc,
if (!si.nr_ours && !si.nr_theirs) if (!si.nr_ours && !si.nr_theirs)
shallow_update = 0; shallow_update = 0;
if (!delete_only(commands)) { 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); update_shallow_info(commands, &si, &ref);
} }
use_keepalive = KEEPALIVE_ALWAYS; use_keepalive = KEEPALIVE_ALWAYS;
execute_commands(commands, unpack_status, &si, execute_commands(commands, unpack_status, &si, transaction,
&push_options); &push_options);
delete_tempfile(&pack_lockfile); delete_tempfile(&pack_lockfile);
sigchain_push(SIGPIPE, SIG_IGN); sigchain_push(SIGPIPE, SIG_IGN);
@ -2719,7 +2721,7 @@ int cmd_receive_pack(int argc,
else if (report_status) else if (report_status)
report(commands, unpack_status); report(commands, unpack_status);
sigchain_pop(SIGPIPE); sigchain_pop(SIGPIPE);
run_receive_hook(commands, "post-receive", 1, run_receive_hook(commands, "post-receive", 1, NULL,
&push_options); &push_options);
run_update_post_hook(commands); run_update_post_hook(commands);
free_commands(commands); free_commands(commands);