Merge branch 'jt/receive-pack-use-odb-transactions'
'git receive-pack' has been refactored to use ODB transaction interfaces instead of directly managing 'tmp_objdir' for staging incoming objects, bringing it closer to being ODB backend agnostic. * jt/receive-pack-use-odb-transactions: builtin/receive-pack: stage incoming objects via ODB transactions builtin/receive-pack: drop redundant tmpdir env odb/transaction: introduce ODB transaction flags odb/transaction: add transaction env interface odb/transaction: propagate commit errors odb/transaction: propagate begin errors object-file: propagate files transaction errors object-file: drop check for inflight transactions object-file: embed transaction flush logic in commit function object-file: rename files transaction fsync function object-file: rename files transaction prepare functionmain
commit
55f961a555
|
|
@ -581,7 +581,7 @@ int cmd_add(int argc,
|
|||
string_list_clear(&only_match_skip_worktree, 0);
|
||||
}
|
||||
|
||||
transaction = odb_transaction_begin(repo->objects);
|
||||
odb_transaction_begin_or_die(repo->objects, &transaction, 0);
|
||||
|
||||
ps_matched = xcalloc(pathspec.nr, 1);
|
||||
if (add_renormalize)
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
||||
|
|
@ -1363,7 +1361,6 @@ static int update_shallow_ref(struct command *cmd, struct shallow_info *si)
|
|||
!delayed_reachability_test(si, i))
|
||||
oid_array_append(&extra, &si->shallow->oid[i]);
|
||||
|
||||
opt.env = tmp_objdir_env(tmp_objdir);
|
||||
setup_alternate_shallow(&shallow_lock, &opt.shallow_file, &extra);
|
||||
if (check_connected(command_singleton_iterator, cmd, &opt)) {
|
||||
rollback_shallow_file(the_repository, &shallow_lock);
|
||||
|
|
@ -1790,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);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -2028,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;
|
||||
|
|
@ -2044,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;
|
||||
|
|
@ -2057,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);
|
||||
|
|
@ -2085,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";
|
||||
|
|
@ -2106,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);
|
||||
|
||||
|
|
@ -2326,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;
|
||||
|
|
@ -2351,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");
|
||||
|
|
@ -2431,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));
|
||||
|
|
@ -2446,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;
|
||||
|
|
@ -2623,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")),
|
||||
|
|
@ -2707,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);
|
||||
|
|
@ -2720,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);
|
||||
|
|
|
|||
|
|
@ -598,7 +598,7 @@ static void unpack_all(void)
|
|||
progress = start_progress(the_repository,
|
||||
_("Unpacking objects"), nr_objects);
|
||||
CALLOC_ARRAY(obj_list, nr_objects);
|
||||
transaction = odb_transaction_begin(the_repository->objects);
|
||||
odb_transaction_begin_or_die(the_repository->objects, &transaction, 0);
|
||||
for (i = 0; i < nr_objects; i++) {
|
||||
unpack_one(i);
|
||||
display_progress(progress, i + 1);
|
||||
|
|
|
|||
|
|
@ -1124,7 +1124,7 @@ int cmd_update_index(int argc,
|
|||
* Allow the object layer to optimize adding multiple objects in
|
||||
* a batch.
|
||||
*/
|
||||
transaction = odb_transaction_begin(the_repository->objects);
|
||||
odb_transaction_begin_or_die(the_repository->objects, &transaction, 0);
|
||||
while (ctx.argc) {
|
||||
if (parseopt_state != PARSE_OPT_DONE)
|
||||
parseopt_state = parse_options_step(&ctx, options,
|
||||
|
|
|
|||
|
|
@ -516,6 +516,7 @@ static int update_one(struct cache_tree *it,
|
|||
|
||||
int cache_tree_update(struct index_state *istate, int flags)
|
||||
{
|
||||
int inflight = !!the_repository->objects->transaction;
|
||||
struct odb_transaction *transaction;
|
||||
int skip, i;
|
||||
|
||||
|
|
@ -532,10 +533,12 @@ int cache_tree_update(struct index_state *istate, int flags)
|
|||
|
||||
trace_performance_enter();
|
||||
trace2_region_enter("cache_tree", "update", istate->repo);
|
||||
transaction = odb_transaction_begin(the_repository->objects);
|
||||
if (!inflight)
|
||||
odb_transaction_begin_or_die(the_repository->objects, &transaction, 0);
|
||||
i = update_one(istate->cache_tree, istate->cache, istate->cache_nr,
|
||||
"", 0, &skip, flags);
|
||||
odb_transaction_commit(transaction);
|
||||
if (!inflight)
|
||||
odb_transaction_commit(transaction);
|
||||
trace2_region_leave("cache_tree", "update", istate->repo);
|
||||
trace_performance_leave("cache_tree_update");
|
||||
if (i < 0)
|
||||
|
|
|
|||
161
object-file.c
161
object-file.c
|
|
@ -27,6 +27,7 @@
|
|||
#include "path.h"
|
||||
#include "read-cache-ll.h"
|
||||
#include "setup.h"
|
||||
#include "strvec.h"
|
||||
#include "tempfile.h"
|
||||
#include "tmp-objdir.h"
|
||||
|
||||
|
|
@ -494,9 +495,10 @@ struct odb_transaction_files {
|
|||
|
||||
struct tmp_objdir *objdir;
|
||||
struct transaction_packfile packfile;
|
||||
const char *prefix;
|
||||
};
|
||||
|
||||
static void prepare_loose_object_transaction(struct odb_transaction *base)
|
||||
static int odb_transaction_files_prepare(struct odb_transaction *base)
|
||||
{
|
||||
struct odb_transaction_files *transaction =
|
||||
container_of_or_null(base, struct odb_transaction_files, base);
|
||||
|
|
@ -508,19 +510,28 @@ static void prepare_loose_object_transaction(struct odb_transaction *base)
|
|||
* added at the time they call odb_transaction_files_begin.
|
||||
*/
|
||||
if (!transaction || transaction->objdir)
|
||||
return;
|
||||
return 0;
|
||||
|
||||
transaction->objdir = tmp_objdir_create(base->source->odb->repo, "bulk-fsync");
|
||||
if (transaction->objdir)
|
||||
tmp_objdir_replace_primary_odb(transaction->objdir, 0);
|
||||
transaction->objdir = tmp_objdir_create(base->source->odb->repo, transaction->prefix);
|
||||
if (!transaction->objdir)
|
||||
return error(_("unable to create temporary object directory"));
|
||||
|
||||
tmp_objdir_replace_primary_odb(transaction->objdir, 0);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void fsync_loose_object_transaction(struct odb_transaction *base,
|
||||
int fd, const char *filename)
|
||||
static void odb_transaction_files_fsync(struct odb_transaction *base,
|
||||
int fd, const char *filename)
|
||||
{
|
||||
struct odb_transaction_files *transaction =
|
||||
container_of_or_null(base, struct odb_transaction_files, base);
|
||||
|
||||
if (!transaction || !transaction->objdir) {
|
||||
fsync_or_die(fd, filename);
|
||||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
* If we have an active ODB transaction, we issue a call that
|
||||
* cleans the filesystem page cache but avoids a hardware flush
|
||||
|
|
@ -528,49 +539,13 @@ static void fsync_loose_object_transaction(struct odb_transaction *base,
|
|||
* before renaming the objects to their final names as part of
|
||||
* flush_batch_fsync.
|
||||
*/
|
||||
if (!transaction || !transaction->objdir ||
|
||||
git_fsync(fd, FSYNC_WRITEOUT_ONLY) < 0) {
|
||||
if (git_fsync(fd, FSYNC_WRITEOUT_ONLY) < 0) {
|
||||
if (errno == ENOSYS)
|
||||
warning(_("core.fsyncMethod = batch is unsupported on this platform"));
|
||||
fsync_or_die(fd, filename);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Cleanup after batch-mode fsync_object_files.
|
||||
*/
|
||||
static void flush_loose_object_transaction(struct odb_transaction_files *transaction)
|
||||
{
|
||||
struct strbuf temp_path = STRBUF_INIT;
|
||||
struct tempfile *temp;
|
||||
|
||||
if (!transaction->objdir)
|
||||
return;
|
||||
|
||||
/*
|
||||
* Issue a full hardware flush against a temporary file to ensure
|
||||
* that all objects are durable before any renames occur. The code in
|
||||
* fsync_loose_object_transaction has already issued a writeout
|
||||
* request, but it has not flushed any writeback cache in the storage
|
||||
* hardware or any filesystem logs. This fsync call acts as a barrier
|
||||
* to ensure that the data in each new object file is durable before
|
||||
* the final name is visible.
|
||||
*/
|
||||
strbuf_addf(&temp_path, "%s/bulk_fsync_XXXXXX",
|
||||
repo_get_object_directory(transaction->base.source->odb->repo));
|
||||
temp = xmks_tempfile(temp_path.buf);
|
||||
fsync_or_die(get_tempfile_fd(temp), get_tempfile_path(temp));
|
||||
delete_tempfile(&temp);
|
||||
strbuf_release(&temp_path);
|
||||
|
||||
/*
|
||||
* Make the object files visible in the primary ODB after their data is
|
||||
* fully durable.
|
||||
*/
|
||||
tmp_objdir_migrate(transaction->objdir);
|
||||
transaction->objdir = NULL;
|
||||
}
|
||||
|
||||
/* Finalize a file on disk, and close it. */
|
||||
static void close_loose_object(struct odb_source_loose *loose,
|
||||
int fd, const char *filename)
|
||||
|
|
@ -579,7 +554,7 @@ static void close_loose_object(struct odb_source_loose *loose,
|
|||
goto out;
|
||||
|
||||
if (batch_fsync_enabled(FSYNC_COMPONENT_LOOSE_OBJECT))
|
||||
fsync_loose_object_transaction(loose->base.odb->transaction, fd, filename);
|
||||
odb_transaction_files_fsync(loose->base.odb->transaction, fd, filename);
|
||||
else if (fsync_object_files > 0)
|
||||
fsync_or_die(fd, filename);
|
||||
else
|
||||
|
|
@ -758,7 +733,7 @@ int write_loose_object(struct odb_source_loose *loose,
|
|||
static struct strbuf filename = STRBUF_INIT;
|
||||
|
||||
if (batch_fsync_enabled(FSYNC_COMPONENT_LOOSE_OBJECT))
|
||||
prepare_loose_object_transaction(loose->base.odb->transaction);
|
||||
odb_transaction_files_prepare(loose->base.odb->transaction);
|
||||
|
||||
odb_loose_path(loose, &filename, oid);
|
||||
|
||||
|
|
@ -822,7 +797,7 @@ int odb_source_loose_write_stream(struct odb_source_loose *loose,
|
|||
int hdrlen;
|
||||
|
||||
if (batch_fsync_enabled(FSYNC_COMPONENT_LOOSE_OBJECT))
|
||||
prepare_loose_object_transaction(loose->base.odb->transaction);
|
||||
odb_transaction_files_prepare(loose->base.odb->transaction);
|
||||
|
||||
/* Since oid is not determined, save tmp file to odb path. */
|
||||
strbuf_addf(&filename, "%s/", loose->base.path);
|
||||
|
|
@ -1380,13 +1355,17 @@ int index_fd(struct index_state *istate, struct object_id *oid,
|
|||
|
||||
if (flags & INDEX_WRITE_OBJECT) {
|
||||
struct object_database *odb = the_repository->objects;
|
||||
struct odb_transaction *transaction = odb_transaction_begin(odb);
|
||||
struct odb_transaction *transaction = odb->transaction;
|
||||
int inflight = !!transaction;
|
||||
|
||||
ret = odb_transaction_write_object_stream(odb->transaction,
|
||||
if (!inflight)
|
||||
odb_transaction_begin_or_die(odb, &transaction, 0);
|
||||
ret = odb_transaction_write_object_stream(transaction,
|
||||
&stream,
|
||||
xsize_t(st->st_size),
|
||||
oid);
|
||||
odb_transaction_commit(transaction);
|
||||
if (!inflight)
|
||||
odb_transaction_commit(transaction);
|
||||
} else {
|
||||
ret = hash_blob_stream(&stream,
|
||||
the_repository->hash_algo, oid,
|
||||
|
|
@ -1671,27 +1650,93 @@ out:
|
|||
return ret;
|
||||
}
|
||||
|
||||
static void odb_transaction_files_commit(struct odb_transaction *base)
|
||||
static int odb_transaction_files_commit(struct odb_transaction *base)
|
||||
{
|
||||
struct odb_transaction_files *transaction =
|
||||
container_of(base, struct odb_transaction_files, base);
|
||||
|
||||
flush_loose_object_transaction(transaction);
|
||||
if (transaction->objdir) {
|
||||
struct strbuf temp_path = STRBUF_INIT;
|
||||
struct tempfile *temp;
|
||||
|
||||
/*
|
||||
* Issue a full hardware flush against a temporary file to ensure
|
||||
* that all objects are durable before any renames occur. The code in
|
||||
* odb_transaction_files_fsync has already issued a writeout
|
||||
* request, but it has not flushed any writeback cache in the storage
|
||||
* hardware or any filesystem logs. This fsync call acts as a barrier
|
||||
* to ensure that the data in each new object file is durable before
|
||||
* the final name is visible.
|
||||
*/
|
||||
strbuf_addf(&temp_path, "%s/bulk_fsync_XXXXXX",
|
||||
repo_get_object_directory(transaction->base.source->odb->repo));
|
||||
temp = xmks_tempfile(temp_path.buf);
|
||||
fsync_or_die(get_tempfile_fd(temp), get_tempfile_path(temp));
|
||||
delete_tempfile(&temp);
|
||||
strbuf_release(&temp_path);
|
||||
|
||||
/*
|
||||
* Make the object files visible in the primary ODB after their data is
|
||||
* fully durable.
|
||||
*/
|
||||
if (tmp_objdir_migrate(transaction->objdir))
|
||||
return error(_("unable to migrate temporary objects"));
|
||||
|
||||
transaction->objdir = NULL;
|
||||
}
|
||||
|
||||
flush_packfile_transaction(transaction);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
struct odb_transaction *odb_transaction_files_begin(struct odb_source *source)
|
||||
static int odb_transaction_files_env(struct odb_transaction *base,
|
||||
struct strvec *env)
|
||||
{
|
||||
struct odb_transaction_files *transaction =
|
||||
container_of(base, struct odb_transaction_files, base);
|
||||
int ret;
|
||||
|
||||
ret = odb_transaction_files_prepare(&transaction->base);
|
||||
if (!ret)
|
||||
strvec_pushv(env, tmp_objdir_env(transaction->objdir));
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
int odb_transaction_files_begin(struct odb_source *source,
|
||||
struct odb_transaction **out,
|
||||
enum odb_transaction_flags flags)
|
||||
{
|
||||
struct odb_transaction_files *transaction;
|
||||
struct object_database *odb = source->odb;
|
||||
|
||||
if (odb->transaction)
|
||||
return NULL;
|
||||
|
||||
transaction = xcalloc(1, sizeof(*transaction));
|
||||
transaction->base.source = source;
|
||||
transaction->base.commit = odb_transaction_files_commit;
|
||||
transaction->base.write_object_stream = odb_transaction_files_write_object_stream;
|
||||
transaction->base.env = odb_transaction_files_env;
|
||||
|
||||
return &transaction->base;
|
||||
transaction->prefix = "bulk-fsync";
|
||||
if (flags & ODB_TRANSACTION_RECEIVE) {
|
||||
/*
|
||||
* ODB transactions for git-receive-pack(1) eagerly create a
|
||||
* temporary directory and use a different temporary directory
|
||||
* prefix.
|
||||
*
|
||||
* NEEDSWORK: This transaction flag is only used by the "files"
|
||||
* backend to special case temporary directory set up and
|
||||
* handling. Ideally transaction users should not have to care
|
||||
* though. To avoid this, we could eagerly create the temporary
|
||||
* directory and use the same prefix name for all transactions.
|
||||
*/
|
||||
transaction->prefix = "incoming";
|
||||
if (odb_transaction_files_prepare(&transaction->base)) {
|
||||
free(transaction);
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
*out = &transaction->base;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@
|
|||
#include "object.h"
|
||||
#include "odb.h"
|
||||
#include "odb/source-loose.h"
|
||||
#include "odb/transaction.h"
|
||||
|
||||
/* The maximum size for an object header. */
|
||||
#define MAX_HEADER_LEN 32
|
||||
|
|
@ -194,9 +195,10 @@ struct odb_transaction;
|
|||
/*
|
||||
* Tell the object database to optimize for adding
|
||||
* multiple objects. odb_transaction_files_commit must be called
|
||||
* to make new objects visible. If a transaction is already
|
||||
* pending, NULL is returned.
|
||||
* to make new objects visible.
|
||||
*/
|
||||
struct odb_transaction *odb_transaction_files_begin(struct odb_source *source);
|
||||
int odb_transaction_files_begin(struct odb_source *source,
|
||||
struct odb_transaction **out,
|
||||
enum odb_transaction_flags flags);
|
||||
|
||||
#endif /* OBJECT_FILE_H */
|
||||
|
|
|
|||
|
|
@ -181,13 +181,10 @@ static int odb_source_files_write_object_stream(struct odb_source *source,
|
|||
}
|
||||
|
||||
static int odb_source_files_begin_transaction(struct odb_source *source,
|
||||
struct odb_transaction **out)
|
||||
struct odb_transaction **out,
|
||||
enum odb_transaction_flags flags)
|
||||
{
|
||||
struct odb_transaction *tx = odb_transaction_files_begin(source);
|
||||
if (!tx)
|
||||
return -1;
|
||||
*out = tx;
|
||||
return 0;
|
||||
return odb_transaction_files_begin(source, out, flags);
|
||||
}
|
||||
|
||||
static int odb_source_files_read_alternates(struct odb_source *source,
|
||||
|
|
|
|||
|
|
@ -305,7 +305,8 @@ static int odb_source_inmemory_freshen_object(struct odb_source *source,
|
|||
}
|
||||
|
||||
static int odb_source_inmemory_begin_transaction(struct odb_source *source UNUSED,
|
||||
struct odb_transaction **out UNUSED)
|
||||
struct odb_transaction **out UNUSED,
|
||||
enum odb_transaction_flags flags UNUSED)
|
||||
{
|
||||
return error("in-memory source does not support transactions");
|
||||
}
|
||||
|
|
|
|||
|
|
@ -638,7 +638,8 @@ static int odb_source_loose_write_object_stream(struct odb_source *source,
|
|||
}
|
||||
|
||||
static int odb_source_loose_begin_transaction(struct odb_source *source UNUSED,
|
||||
struct odb_transaction **out UNUSED)
|
||||
struct odb_transaction **out UNUSED,
|
||||
enum odb_transaction_flags flags UNUSED)
|
||||
{
|
||||
/* TODO: this is a known omission that we'll want to address eventually. */
|
||||
return error("loose source does not support transactions");
|
||||
|
|
|
|||
|
|
@ -545,7 +545,8 @@ static int odb_source_packed_write_object_stream(struct odb_source *source UNUSE
|
|||
}
|
||||
|
||||
static int odb_source_packed_begin_transaction(struct odb_source *source UNUSED,
|
||||
struct odb_transaction **out UNUSED)
|
||||
struct odb_transaction **out UNUSED,
|
||||
enum odb_transaction_flags flags UNUSED)
|
||||
{
|
||||
return error("packed backend cannot begin transactions");
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@
|
|||
|
||||
#include "object.h"
|
||||
#include "odb.h"
|
||||
#include "odb/transaction.h"
|
||||
|
||||
enum odb_source_type {
|
||||
/*
|
||||
|
|
@ -232,7 +233,8 @@ struct odb_source {
|
|||
* negative error code otherwise.
|
||||
*/
|
||||
int (*begin_transaction)(struct odb_source *source,
|
||||
struct odb_transaction **out);
|
||||
struct odb_transaction **out,
|
||||
enum odb_transaction_flags flags);
|
||||
|
||||
/*
|
||||
* This callback is expected to read the list of alternate object
|
||||
|
|
@ -472,9 +474,10 @@ static inline int odb_source_write_alternate(struct odb_source *source,
|
|||
* Returns 0 on success, a negative error code otherwise.
|
||||
*/
|
||||
static inline int odb_source_begin_transaction(struct odb_source *source,
|
||||
struct odb_transaction **out)
|
||||
struct odb_transaction **out,
|
||||
enum odb_transaction_flags flags)
|
||||
{
|
||||
return source->begin_transaction(source, out);
|
||||
return source->begin_transaction(source, out, flags);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
|
|
|||
|
|
@ -1,30 +1,41 @@
|
|||
#include "git-compat-util.h"
|
||||
#include "gettext.h"
|
||||
#include "odb/source.h"
|
||||
#include "odb/transaction.h"
|
||||
|
||||
struct odb_transaction *odb_transaction_begin(struct object_database *odb)
|
||||
int odb_transaction_begin(struct object_database *odb,
|
||||
struct odb_transaction **out,
|
||||
enum odb_transaction_flags flags)
|
||||
{
|
||||
int ret;
|
||||
|
||||
if (odb->transaction)
|
||||
return NULL;
|
||||
return error(_("object database transaction already pending"));
|
||||
|
||||
odb_source_begin_transaction(odb->sources, &odb->transaction);
|
||||
ret = odb_source_begin_transaction(odb->sources, out, flags);
|
||||
if (!ret)
|
||||
odb->transaction = *out;
|
||||
|
||||
return odb->transaction;
|
||||
return ret;
|
||||
}
|
||||
|
||||
void odb_transaction_commit(struct odb_transaction *transaction)
|
||||
int odb_transaction_commit(struct odb_transaction *transaction)
|
||||
{
|
||||
int ret;
|
||||
|
||||
if (!transaction)
|
||||
return;
|
||||
return 0;
|
||||
|
||||
/*
|
||||
* Ensure the transaction ending matches the pending transaction.
|
||||
*/
|
||||
ASSERT(transaction == transaction->source->odb->transaction);
|
||||
|
||||
transaction->commit(transaction);
|
||||
ret = transaction->commit(transaction);
|
||||
transaction->source->odb->transaction = NULL;
|
||||
free(transaction);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
int odb_transaction_write_object_stream(struct odb_transaction *transaction,
|
||||
|
|
@ -33,3 +44,11 @@ int odb_transaction_write_object_stream(struct odb_transaction *transaction,
|
|||
{
|
||||
return transaction->write_object_stream(transaction, stream, len, oid);
|
||||
}
|
||||
|
||||
int odb_transaction_env(struct odb_transaction *transaction, struct strvec *env)
|
||||
{
|
||||
if (!transaction)
|
||||
return 0;
|
||||
|
||||
return transaction->env(transaction, env);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,8 +1,8 @@
|
|||
#ifndef ODB_TRANSACTION_H
|
||||
#define ODB_TRANSACTION_H
|
||||
|
||||
#include "gettext.h"
|
||||
#include "odb.h"
|
||||
#include "odb/source.h"
|
||||
|
||||
/*
|
||||
* A transaction may be started for an object database prior to writing new
|
||||
|
|
@ -16,8 +16,11 @@ struct odb_transaction {
|
|||
/* The ODB source the transaction is opened against. */
|
||||
struct odb_source *source;
|
||||
|
||||
/* The ODB source specific callback invoked to commit a transaction. */
|
||||
void (*commit)(struct odb_transaction *transaction);
|
||||
/*
|
||||
* The ODB source specific callback invoked to commit a transaction.
|
||||
* Returns 0 on success, a negative error code otherwise.
|
||||
*/
|
||||
int (*commit)(struct odb_transaction *transaction);
|
||||
|
||||
/*
|
||||
* This callback is expected to write the given object stream into
|
||||
|
|
@ -30,20 +33,47 @@ struct odb_transaction {
|
|||
int (*write_object_stream)(struct odb_transaction *transaction,
|
||||
struct odb_write_stream *stream, size_t len,
|
||||
struct object_id *oid);
|
||||
|
||||
/*
|
||||
* This callback is expected to populate the provided strvec with the
|
||||
* environment variables that a child process should inherit so that its
|
||||
* object writes participate in the transaction. Returns 0 on success, a
|
||||
* negative error code otherwise.
|
||||
*/
|
||||
int (*env)(struct odb_transaction *transaction, struct strvec *env);
|
||||
};
|
||||
|
||||
/* Flags used to configure an ODB transaction. */
|
||||
enum odb_transaction_flags {
|
||||
/* Configures the transaction for use with git-receive-pack(1). */
|
||||
ODB_TRANSACTION_RECEIVE = (1 << 0),
|
||||
};
|
||||
|
||||
/*
|
||||
* Starts an ODB transaction. Subsequent objects are written to the transaction
|
||||
* and not committed until odb_transaction_commit() is invoked on the
|
||||
* transaction. If the ODB already has a pending transaction, NULL is returned.
|
||||
* Starts an ODB transaction and returns it via `out`. Subsequent objects are
|
||||
* written to the transaction and not committed until odb_transaction_commit()
|
||||
* is invoked on the transaction. Returns 0 on success and a negative value on
|
||||
* error. Note that it is considered an error to start a new transaction if the
|
||||
* ODB already has an inflight transaction pending.
|
||||
*/
|
||||
struct odb_transaction *odb_transaction_begin(struct object_database *odb);
|
||||
int odb_transaction_begin(struct object_database *odb,
|
||||
struct odb_transaction **out,
|
||||
enum odb_transaction_flags flags);
|
||||
|
||||
static inline void odb_transaction_begin_or_die(struct object_database *odb,
|
||||
struct odb_transaction **out,
|
||||
enum odb_transaction_flags flags)
|
||||
{
|
||||
if (odb_transaction_begin(odb, out, flags))
|
||||
die(_("failed to start ODB transaction"));
|
||||
}
|
||||
|
||||
/*
|
||||
* Commits an ODB transaction making the written objects visible. If the
|
||||
* specified transaction is NULL, the function is a no-op.
|
||||
* Commits an ODB transaction making the written objects visible. Returns 0 on
|
||||
* success, a negative error code otherwise. Note that, if the specified
|
||||
* transaction is NULL, the function is a no-op and no error is returned.
|
||||
*/
|
||||
void odb_transaction_commit(struct odb_transaction *transaction);
|
||||
int odb_transaction_commit(struct odb_transaction *transaction);
|
||||
|
||||
/*
|
||||
* Writes the object in the provided stream into the transaction. The resulting
|
||||
|
|
@ -54,4 +84,13 @@ int odb_transaction_write_object_stream(struct odb_transaction *transaction,
|
|||
struct odb_write_stream *stream,
|
||||
size_t len, struct object_id *oid);
|
||||
|
||||
/*
|
||||
* Populates the provided strvec with the environment variables that a child
|
||||
* process should inherit so that its object writes participate in the
|
||||
* transaction, suitable for using via child_process.env. Returns 0 on success,
|
||||
* a negative error code otherwise. Note that, if the specified transaction is
|
||||
* NULL, the function is a no-op and no error is returned.
|
||||
*/
|
||||
int odb_transaction_env(struct odb_transaction *transaction, struct strvec *env);
|
||||
|
||||
#endif
|
||||
|
|
|
|||
|
|
@ -4015,6 +4015,7 @@ int add_files_to_cache(struct repository *repo, const char *prefix,
|
|||
const struct pathspec *pathspec, char *ps_matched,
|
||||
int include_sparse, int flags, int ignored_too )
|
||||
{
|
||||
int inflight = !!repo->objects->transaction;
|
||||
struct odb_transaction *transaction;
|
||||
struct update_callback_data data;
|
||||
struct rev_info rev;
|
||||
|
|
@ -4045,9 +4046,11 @@ int add_files_to_cache(struct repository *repo, const char *prefix,
|
|||
* This function is invoked from commands other than 'add', which
|
||||
* may not have their own transaction active.
|
||||
*/
|
||||
transaction = odb_transaction_begin(repo->objects);
|
||||
if (!inflight)
|
||||
odb_transaction_begin_or_die(repo->objects, &transaction, 0);
|
||||
run_diff_files(&rev, DIFF_RACY_IS_MODIFIED);
|
||||
odb_transaction_commit(transaction);
|
||||
if (!inflight)
|
||||
odb_transaction_commit(transaction);
|
||||
|
||||
release_revisions(&rev);
|
||||
return !!data.add_errors;
|
||||
|
|
|
|||
Loading…
Reference in New Issue