]> git.ipfire.org Git - thirdparty/git.git/commitdiff
bulk-checkin: remove global transaction state
authorJustin Tobler <jltobler@gmail.com>
Fri, 22 Aug 2025 21:34:58 +0000 (16:34 -0500)
committerJunio C Hamano <gitster@pobox.com>
Mon, 25 Aug 2025 16:48:13 +0000 (09:48 -0700)
Object database transactions in the bulk-checkin subsystem rely on
global state to track transaction status. Stop relying on global state
and instead store the transaction in the `struct object_database`.
Functions that operate on transactions are updated to now wire
transaction state.

Signed-off-by: Justin Tobler <jltobler@gmail.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
builtin/add.c
builtin/unpack-objects.c
builtin/update-index.c
bulk-checkin.c
bulk-checkin.h
cache-tree.c
object-file.c
odb.h
read-cache.c

index 0235854f8099c49328a00aadad4adbbbdfc0579b..740c7c45817828cadc130227569c2457e2f1743c 100644 (file)
@@ -389,6 +389,7 @@ int cmd_add(int argc,
        char *seen = NULL;
        char *ps_matched = NULL;
        struct lock_file lock_file = LOCK_INIT;
+       struct odb_transaction *transaction;
 
        repo_config(repo, add_config, NULL);
 
@@ -574,7 +575,7 @@ int cmd_add(int argc,
                string_list_clear(&only_match_skip_worktree, 0);
        }
 
-       begin_odb_transaction();
+       transaction = begin_odb_transaction(repo->objects);
 
        ps_matched = xcalloc(pathspec.nr, 1);
        if (add_renormalize)
@@ -593,7 +594,7 @@ int cmd_add(int argc,
 
        if (chmod_arg && pathspec.nr)
                exit_status |= chmod_pathspec(repo, &pathspec, chmod_arg[0], show_only);
-       end_odb_transaction();
+       end_odb_transaction(transaction);
 
 finish:
        if (write_locked_index(repo->index, &lock_file,
index 7ae7c82b6c05a6a6b09e77766ef7226c0c4ddcd4..28124b324d2641ae9a4b6619cd69aaafcf4c5442 100644 (file)
@@ -584,6 +584,7 @@ static void unpack_all(void)
 {
        int i;
        unsigned char *hdr = fill(sizeof(struct pack_header));
+       struct odb_transaction *transaction;
 
        if (get_be32(hdr) != PACK_SIGNATURE)
                die("bad pack file");
@@ -599,12 +600,12 @@ static void unpack_all(void)
                progress = start_progress(the_repository,
                                          _("Unpacking objects"), nr_objects);
        CALLOC_ARRAY(obj_list, nr_objects);
-       begin_odb_transaction();
+       transaction = begin_odb_transaction(the_repository->objects);
        for (i = 0; i < nr_objects; i++) {
                unpack_one(i);
                display_progress(progress, i + 1);
        }
-       end_odb_transaction();
+       end_odb_transaction(transaction);
        stop_progress(&progress);
 
        if (delta_list)
index 2380f3ccd68c8ce0ac4b221dbf84f18b9a430811..2ba2d29c959facc1538f592e024a26fbdc3367de 100644 (file)
@@ -77,7 +77,7 @@ static void report(const char *fmt, ...)
         * objects invisible while a transaction is active, so flush the
         * transaction here before reporting a change made by update-index.
         */
-       flush_odb_transaction();
+       flush_odb_transaction(the_repository->objects->transaction);
        va_start(vp, fmt);
        vprintf(fmt, vp);
        putchar('\n');
@@ -940,6 +940,7 @@ int cmd_update_index(int argc,
        strbuf_getline_fn getline_fn;
        int parseopt_state = PARSE_OPT_UNKNOWN;
        struct repository *r = the_repository;
+       struct odb_transaction *transaction;
        struct option options[] = {
                OPT_BIT('q', NULL, &refresh_args.flags,
                        N_("continue refresh even when index needs update"),
@@ -1130,7 +1131,7 @@ int cmd_update_index(int argc,
         * Allow the object layer to optimize adding multiple objects in
         * a batch.
         */
-       begin_odb_transaction();
+       transaction = begin_odb_transaction(the_repository->objects);
        while (ctx.argc) {
                if (parseopt_state != PARSE_OPT_DONE)
                        parseopt_state = parse_options_step(&ctx, options,
@@ -1213,7 +1214,7 @@ int cmd_update_index(int argc,
        /*
         * By now we have added all of the new objects
         */
-       end_odb_transaction();
+       end_odb_transaction(transaction);
 
        if (split_index > 0) {
                if (repo_config_get_split_index(the_repository) == 0)
index 82a73da79e8f0e0cdbdbe0cfb4e4dd0855476eac..53a20a2d92fd77a18fa7de3b46487228726ea9f6 100644 (file)
@@ -30,11 +30,13 @@ struct bulk_checkin_packfile {
        uint32_t nr_written;
 };
 
-static struct odb_transaction {
+struct odb_transaction {
+       struct object_database *odb;
+
        int nesting;
        struct tmp_objdir *objdir;
        struct bulk_checkin_packfile packfile;
-} transaction;
+};
 
 static void finish_tmp_packfile(struct strbuf *basename,
                                const char *pack_tmp_name,
@@ -98,12 +100,12 @@ clear_exit:
 /*
  * Cleanup after batch-mode fsync_object_files.
  */
-static void flush_batch_fsync(void)
+static void flush_batch_fsync(struct odb_transaction *transaction)
 {
        struct strbuf temp_path = STRBUF_INIT;
        struct tempfile *temp;
 
-       if (!transaction.objdir)
+       if (!transaction->objdir)
                return;
 
        /*
@@ -125,8 +127,8 @@ static void flush_batch_fsync(void)
         * Make the object files visible in the primary ODB after their data is
         * fully durable.
         */
-       tmp_objdir_migrate(transaction.objdir);
-       transaction.objdir = NULL;
+       tmp_objdir_migrate(transaction->objdir);
+       transaction->objdir = NULL;
 }
 
 static int already_written(struct bulk_checkin_packfile *state, struct object_id *oid)
@@ -325,7 +327,7 @@ static int deflate_blob_to_pack(struct bulk_checkin_packfile *state,
        return 0;
 }
 
-void prepare_loose_object_bulk_checkin(void)
+void prepare_loose_object_bulk_checkin(struct odb_transaction *transaction)
 {
        /*
         * We lazily create the temporary object directory
@@ -333,15 +335,16 @@ void prepare_loose_object_bulk_checkin(void)
         * callers may not know whether any objects will be
         * added at the time they call begin_odb_transaction.
         */
-       if (!transaction.nesting || transaction.objdir)
+       if (!transaction || transaction->objdir)
                return;
 
-       transaction.objdir = tmp_objdir_create(the_repository, "bulk-fsync");
-       if (transaction.objdir)
-               tmp_objdir_replace_primary_odb(transaction.objdir, 0);
+       transaction->objdir = tmp_objdir_create(the_repository, "bulk-fsync");
+       if (transaction->objdir)
+               tmp_objdir_replace_primary_odb(transaction->objdir, 0);
 }
 
-void fsync_loose_object_bulk_checkin(int fd, const char *filename)
+void fsync_loose_object_bulk_checkin(struct odb_transaction *transaction,
+                                    int fd, const char *filename)
 {
        /*
         * If we have an active ODB transaction, we issue a call that
@@ -350,7 +353,7 @@ void fsync_loose_object_bulk_checkin(int fd, const char *filename)
         * before renaming the objects to their final names as part of
         * flush_batch_fsync.
         */
-       if (!transaction.objdir ||
+       if (!transaction || !transaction->objdir ||
            git_fsync(fd, FSYNC_WRITEOUT_ONLY) < 0) {
                if (errno == ENOSYS)
                        warning(_("core.fsyncMethod = batch is unsupported on this platform"));
@@ -358,36 +361,57 @@ void fsync_loose_object_bulk_checkin(int fd, const char *filename)
        }
 }
 
-int index_blob_bulk_checkin(struct object_id *oid,
-                           int fd, size_t size,
+int index_blob_bulk_checkin(struct odb_transaction *transaction,
+                           struct object_id *oid, int fd, size_t size,
                            const char *path, unsigned flags)
 {
-       int status = deflate_blob_to_pack(&transaction.packfile, oid, fd, size,
-                                         path, flags);
-       if (!transaction.nesting)
-               flush_bulk_checkin_packfile(&transaction.packfile);
+       int status;
+
+       if (transaction) {
+               status = deflate_blob_to_pack(&transaction->packfile, oid, fd,
+                                             size, path, flags);
+       } else {
+               struct bulk_checkin_packfile state = { 0 };
+
+               status = deflate_blob_to_pack(&state, oid, fd, size, path, flags);
+               flush_bulk_checkin_packfile(&state);
+       }
+
        return status;
 }
 
-void begin_odb_transaction(void)
+struct odb_transaction *begin_odb_transaction(struct object_database *odb)
 {
-       transaction.nesting += 1;
+       if (!odb->transaction) {
+               CALLOC_ARRAY(odb->transaction, 1);
+               odb->transaction->odb = odb;
+       }
+
+       odb->transaction->nesting += 1;
+
+       return odb->transaction;
 }
 
-void flush_odb_transaction(void)
+void flush_odb_transaction(struct odb_transaction *transaction)
 {
-       flush_batch_fsync();
-       flush_bulk_checkin_packfile(&transaction.packfile);
+       if (!transaction)
+               return;
+
+       flush_batch_fsync(transaction);
+       flush_bulk_checkin_packfile(&transaction->packfile);
 }
 
-void end_odb_transaction(void)
+void end_odb_transaction(struct odb_transaction *transaction)
 {
-       transaction.nesting -= 1;
-       if (transaction.nesting < 0)
+       if (!transaction || transaction->nesting == 0)
                BUG("Unbalanced ODB transaction nesting");
 
-       if (transaction.nesting)
+       transaction->nesting -= 1;
+
+       if (transaction->nesting)
                return;
 
-       flush_odb_transaction();
+       flush_odb_transaction(transaction);
+       transaction->odb->transaction = NULL;
+       free(transaction);
 }
index 7246ea58dcf3481e026560f987138b1efe112390..16254ce6a704f6e66e5e83298306f0a14716e3f3 100644 (file)
@@ -5,9 +5,13 @@
 #define BULK_CHECKIN_H
 
 #include "object.h"
+#include "odb.h"
 
-void prepare_loose_object_bulk_checkin(void);
-void fsync_loose_object_bulk_checkin(int fd, const char *filename);
+struct odb_transaction;
+
+void prepare_loose_object_bulk_checkin(struct odb_transaction *transaction);
+void fsync_loose_object_bulk_checkin(struct odb_transaction *transaction,
+                                    int fd, const char *filename);
 
 /*
  * This creates one packfile per large blob unless bulk-checkin
@@ -24,8 +28,8 @@ void fsync_loose_object_bulk_checkin(int fd, const char *filename);
  * binary blobs, they generally do not want to get any conversion, and
  * callers should avoid this code path when filters are requested.
  */
-int index_blob_bulk_checkin(struct object_id *oid,
-                           int fd, size_t size,
+int index_blob_bulk_checkin(struct odb_transaction *transaction,
+                           struct object_id *oid, int fd, size_t size,
                            const char *path, unsigned flags);
 
 /*
@@ -35,20 +39,20 @@ int index_blob_bulk_checkin(struct object_id *oid,
  * and objects are only visible after the outermost transaction
  * is complete or the transaction is flushed.
  */
-void begin_odb_transaction(void);
+struct odb_transaction *begin_odb_transaction(struct object_database *odb);
 
 /*
  * Make any objects that are currently part of a pending object
  * database transaction visible. It is valid to call this function
  * even if no transaction is active.
  */
-void flush_odb_transaction(void);
+void flush_odb_transaction(struct odb_transaction *transaction);
 
 /*
  * Tell the object database to make any objects from the
  * current transaction visible if this is the final nested
  * transaction.
  */
-void end_odb_transaction(void);
+void end_odb_transaction(struct odb_transaction *transaction);
 
 #endif
index 66ef2becbe01a4bfe2d41de91d97389487c82f64..d225554eedd920dcadc2b5d0c61c2e2932f6e811 100644 (file)
@@ -474,6 +474,7 @@ static int update_one(struct cache_tree *it,
 
 int cache_tree_update(struct index_state *istate, int flags)
 {
+       struct odb_transaction *transaction;
        int skip, i;
 
        i = verify_cache(istate, flags);
@@ -489,10 +490,10 @@ int cache_tree_update(struct index_state *istate, int flags)
 
        trace_performance_enter();
        trace2_region_enter("cache_tree", "update", the_repository);
-       begin_odb_transaction();
+       transaction = begin_odb_transaction(the_repository->objects);
        i = update_one(istate->cache_tree, istate->cache, istate->cache_nr,
                       "", 0, &skip, flags);
-       end_odb_transaction();
+       end_odb_transaction(transaction);
        trace2_region_leave("cache_tree", "update", the_repository);
        trace_performance_leave("cache_tree_update");
        if (i < 0)
index 2bc36ab3ee8cbf2d83c4b3204a7c5df132b934d6..1740aa2b2e35f3161d1cae583743317e5f5d101c 100644 (file)
@@ -674,7 +674,7 @@ static void close_loose_object(struct odb_source *source,
                goto out;
 
        if (batch_fsync_enabled(FSYNC_COMPONENT_LOOSE_OBJECT))
-               fsync_loose_object_bulk_checkin(fd, filename);
+               fsync_loose_object_bulk_checkin(source->odb->transaction, fd, filename);
        else if (fsync_object_files > 0)
                fsync_or_die(fd, filename);
        else
@@ -852,7 +852,7 @@ static int write_loose_object(struct odb_source *source,
        static struct strbuf filename = STRBUF_INIT;
 
        if (batch_fsync_enabled(FSYNC_COMPONENT_LOOSE_OBJECT))
-               prepare_loose_object_bulk_checkin();
+               prepare_loose_object_bulk_checkin(source->odb->transaction);
 
        odb_loose_path(source, &filename, oid);
 
@@ -941,7 +941,7 @@ int stream_loose_object(struct odb_source *source,
        int hdrlen;
 
        if (batch_fsync_enabled(FSYNC_COMPONENT_LOOSE_OBJECT))
-               prepare_loose_object_bulk_checkin();
+               prepare_loose_object_bulk_checkin(source->odb->transaction);
 
        /* Since oid is not determined, save tmp file to odb path. */
        strbuf_addf(&filename, "%s/", source->path);
@@ -1263,8 +1263,9 @@ int index_fd(struct index_state *istate, struct object_id *oid,
                ret = index_core(istate, oid, fd, xsize_t(st->st_size),
                                 type, path, flags);
        else
-               ret = index_blob_bulk_checkin(oid, fd, xsize_t(st->st_size), path,
-                                            flags);
+               ret = index_blob_bulk_checkin(the_repository->objects->transaction,
+                                             oid, fd, xsize_t(st->st_size),
+                                             path, flags);
        close(fd);
        return ret;
 }
diff --git a/odb.h b/odb.h
index 3dfc66d75a3d20dd64d787d04e3ab4e5f9010e8a..a89b2143909920881f5d4916cf5ae81f95f24958 100644 (file)
--- a/odb.h
+++ b/odb.h
@@ -84,6 +84,7 @@ struct odb_source {
 
 struct packed_git;
 struct cached_object_entry;
+struct odb_transaction;
 
 /*
  * The object database encapsulates access to objects in a repository. It
@@ -94,6 +95,13 @@ struct object_database {
        /* Repository that owns this database. */
        struct repository *repo;
 
+       /*
+        * State of current current object database transaction. Only one
+        * transaction may be pending at a time. Is NULL when no transaction is
+        * configured.
+        */
+       struct odb_transaction *transaction;
+
        /*
         * Set of all object directories; the main directory is first (and
         * cannot be NULL after initialization). Subsequent directories are
index 06ad74db2286aee617b0f7bf5eb3c15cc252a3bf..229b8ef11c9a7449d8a1e843b93a0180f4687830 100644 (file)
@@ -3947,6 +3947,7 @@ int add_files_to_cache(struct repository *repo, const char *prefix,
                       const struct pathspec *pathspec, char *ps_matched,
                       int include_sparse, int flags)
 {
+       struct odb_transaction *transaction;
        struct update_callback_data data;
        struct rev_info rev;
 
@@ -3972,9 +3973,9 @@ 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.
         */
-       begin_odb_transaction();
+       transaction = begin_odb_transaction(repo->objects);
        run_diff_files(&rev, DIFF_RACY_IS_MODIFIED);
-       end_odb_transaction();
+       end_odb_transaction(transaction);
 
        release_revisions(&rev);
        return !!data.add_errors;