bulk-checkin: remove global transaction state

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>

Justin Tobler committed Aug 22, 2025 at 16:34 UTC b3361447256bb92a1dbdda910a33cfb1d6fc8f88
9 files changed +94 -52
builtin/add.c
+3 -2
@@ -389,6 +389,7 @@ int cmd_add(int argc,
389 char *seen = NULL;
390 char *ps_matched = NULL;
391 struct lock_file lock_file = LOCK_INIT;
392 + struct odb_transaction *transaction;
393
394 repo_config(repo, add_config, NULL);
395
@@ -574,7 +575,7 @@ int cmd_add(int argc,
575 string_list_clear(&only_match_skip_worktree, 0);
576 }
577
577 - begin_odb_transaction();
578 + transaction = begin_odb_transaction(repo->objects);
579
580 ps_matched = xcalloc(pathspec.nr, 1);
581 if (add_renormalize)
@@ -593,7 +594,7 @@ int cmd_add(int argc,
594
595 if (chmod_arg && pathspec.nr)
596 exit_status |= chmod_pathspec(repo, &pathspec, chmod_arg[0], show_only);
596 - end_odb_transaction();
597 + end_odb_transaction(transaction);
598
599 finish:
600 if (write_locked_index(repo->index, &lock_file,
builtin/unpack-objects.c
+3 -2
@@ -584,6 +584,7 @@ static void unpack_all(void)
584 {
585 int i;
586 unsigned char *hdr = fill(sizeof(struct pack_header));
587 + struct odb_transaction *transaction;
588
589 if (get_be32(hdr) != PACK_SIGNATURE)
590 die("bad pack file");
@@ -599,12 +600,12 @@ static void unpack_all(void)
600 progress = start_progress(the_repository,
601 _("Unpacking objects"), nr_objects);
602 CALLOC_ARRAY(obj_list, nr_objects);
602 - begin_odb_transaction();
603 + transaction = begin_odb_transaction(the_repository->objects);
604 for (i = 0; i < nr_objects; i++) {
605 unpack_one(i);
606 display_progress(progress, i + 1);
607 }
607 - end_odb_transaction();
608 + end_odb_transaction(transaction);
609 stop_progress(&progress);
610
611 if (delta_list)
builtin/update-index.c
+4 -3
@@ -77,7 +77,7 @@ static void report(const char *fmt, ...)
77 * objects invisible while a transaction is active, so flush the
78 * transaction here before reporting a change made by update-index.
79 */
80 - flush_odb_transaction();
80 + flush_odb_transaction(the_repository->objects->transaction);
81 va_start(vp, fmt);
82 vprintf(fmt, vp);
83 putchar('\n');
@@ -940,6 +940,7 @@ int cmd_update_index(int argc,
940 strbuf_getline_fn getline_fn;
941 int parseopt_state = PARSE_OPT_UNKNOWN;
942 struct repository *r = the_repository;
943 + struct odb_transaction *transaction;
944 struct option options[] = {
945 OPT_BIT('q', NULL, &refresh_args.flags,
946 N_("continue refresh even when index needs update"),
@@ -1130,7 +1131,7 @@ int cmd_update_index(int argc,
1131 * Allow the object layer to optimize adding multiple objects in
1132 * a batch.
1133 */
1133 - begin_odb_transaction();
1134 + transaction = begin_odb_transaction(the_repository->objects);
1135 while (ctx.argc) {
1136 if (parseopt_state != PARSE_OPT_DONE)
1137 parseopt_state = parse_options_step(&ctx, options,
@@ -1213,7 +1214,7 @@ int cmd_update_index(int argc,
1214 /*
1215 * By now we have added all of the new objects
1216 */
1216 - end_odb_transaction();
1217 + end_odb_transaction(transaction);
1218
1219 if (split_index > 0) {
1220 if (repo_config_get_split_index(the_repository) == 0)
bulk-checkin.c
+53 -29
@@ -30,11 +30,13 @@ struct bulk_checkin_packfile {
30 uint32_t nr_written;
31 };
32
33 -static struct odb_transaction {
33 +struct odb_transaction {
34 + struct object_database *odb;
35 +
36 int nesting;
37 struct tmp_objdir *objdir;
38 struct bulk_checkin_packfile packfile;
37 -} transaction;
39 +};
40
41 static void finish_tmp_packfile(struct strbuf *basename,
42 const char *pack_tmp_name,
@@ -98,12 +100,12 @@ clear_exit:
100 /*
101 * Cleanup after batch-mode fsync_object_files.
102 */
101 -static void flush_batch_fsync(void)
103 +static void flush_batch_fsync(struct odb_transaction *transaction)
104 {
105 struct strbuf temp_path = STRBUF_INIT;
106 struct tempfile *temp;
107
106 - if (!transaction.objdir)
108 + if (!transaction->objdir)
109 return;
110
111 /*
@@ -125,8 +127,8 @@ static void flush_batch_fsync(void)
127 * Make the object files visible in the primary ODB after their data is
128 * fully durable.
129 */
128 - tmp_objdir_migrate(transaction.objdir);
129 - transaction.objdir = NULL;
130 + tmp_objdir_migrate(transaction->objdir);
131 + transaction->objdir = NULL;
132 }
133
134 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,
327 return 0;
328 }
329
328 -void prepare_loose_object_bulk_checkin(void)
330 +void prepare_loose_object_bulk_checkin(struct odb_transaction *transaction)
331 {
332 /*
333 * We lazily create the temporary object directory
@@ -333,15 +335,16 @@ void prepare_loose_object_bulk_checkin(void)
335 * callers may not know whether any objects will be
336 * added at the time they call begin_odb_transaction.
337 */
336 - if (!transaction.nesting || transaction.objdir)
338 + if (!transaction || transaction->objdir)
339 return;
340
339 - transaction.objdir = tmp_objdir_create(the_repository, "bulk-fsync");
340 - if (transaction.objdir)
341 - tmp_objdir_replace_primary_odb(transaction.objdir, 0);
341 + transaction->objdir = tmp_objdir_create(the_repository, "bulk-fsync");
342 + if (transaction->objdir)
343 + tmp_objdir_replace_primary_odb(transaction->objdir, 0);
344 }
345
344 -void fsync_loose_object_bulk_checkin(int fd, const char *filename)
346 +void fsync_loose_object_bulk_checkin(struct odb_transaction *transaction,
347 + int fd, const char *filename)
348 {
349 /*
350 * 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)
353 * before renaming the objects to their final names as part of
354 * flush_batch_fsync.
355 */
353 - if (!transaction.objdir ||
356 + if (!transaction || !transaction->objdir ||
357 git_fsync(fd, FSYNC_WRITEOUT_ONLY) < 0) {
358 if (errno == ENOSYS)
359 warning(_("core.fsyncMethod = batch is unsupported on this platform"));
@@ -358,36 +361,57 @@ void fsync_loose_object_bulk_checkin(int fd, const char *filename)
361 }
362 }
363
361 -int index_blob_bulk_checkin(struct object_id *oid,
362 - int fd, size_t size,
364 +int index_blob_bulk_checkin(struct odb_transaction *transaction,
365 + struct object_id *oid, int fd, size_t size,
366 const char *path, unsigned flags)
367 {
365 - int status = deflate_blob_to_pack(&transaction.packfile, oid, fd, size,
366 - path, flags);
367 - if (!transaction.nesting)
368 - flush_bulk_checkin_packfile(&transaction.packfile);
368 + int status;
369 +
370 + if (transaction) {
371 + status = deflate_blob_to_pack(&transaction->packfile, oid, fd,
372 + size, path, flags);
373 + } else {
374 + struct bulk_checkin_packfile state = { 0 };
375 +
376 + status = deflate_blob_to_pack(&state, oid, fd, size, path, flags);
377 + flush_bulk_checkin_packfile(&state);
378 + }
379 +
380 return status;
381 }
382
372 -void begin_odb_transaction(void)
383 +struct odb_transaction *begin_odb_transaction(struct object_database *odb)
384 {
374 - transaction.nesting += 1;
385 + if (!odb->transaction) {
386 + CALLOC_ARRAY(odb->transaction, 1);
387 + odb->transaction->odb = odb;
388 + }
389 +
390 + odb->transaction->nesting += 1;
391 +
392 + return odb->transaction;
393 }
394
377 -void flush_odb_transaction(void)
395 +void flush_odb_transaction(struct odb_transaction *transaction)
396 {
379 - flush_batch_fsync();
380 - flush_bulk_checkin_packfile(&transaction.packfile);
397 + if (!transaction)
398 + return;
399 +
400 + flush_batch_fsync(transaction);
401 + flush_bulk_checkin_packfile(&transaction->packfile);
402 }
403
383 -void end_odb_transaction(void)
404 +void end_odb_transaction(struct odb_transaction *transaction)
405 {
385 - transaction.nesting -= 1;
386 - if (transaction.nesting < 0)
406 + if (!transaction || transaction->nesting == 0)
407 BUG("Unbalanced ODB transaction nesting");
408
389 - if (transaction.nesting)
409 + transaction->nesting -= 1;
410 +
411 + if (transaction->nesting)
412 return;
413
392 - flush_odb_transaction();
414 + flush_odb_transaction(transaction);
415 + transaction->odb->transaction = NULL;
416 + free(transaction);
417 }
bulk-checkin.h
+11 -7
@@ -5,9 +5,13 @@
5 #define BULK_CHECKIN_H
6
7 #include "object.h"
8 +#include "odb.h"
9
9 -void prepare_loose_object_bulk_checkin(void);
10 -void fsync_loose_object_bulk_checkin(int fd, const char *filename);
10 +struct odb_transaction;
11 +
12 +void prepare_loose_object_bulk_checkin(struct odb_transaction *transaction);
13 +void fsync_loose_object_bulk_checkin(struct odb_transaction *transaction,
14 + int fd, const char *filename);
15
16 /*
17 * 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);
28 * binary blobs, they generally do not want to get any conversion, and
29 * callers should avoid this code path when filters are requested.
30 */
27 -int index_blob_bulk_checkin(struct object_id *oid,
28 - int fd, size_t size,
31 +int index_blob_bulk_checkin(struct odb_transaction *transaction,
32 + struct object_id *oid, int fd, size_t size,
33 const char *path, unsigned flags);
34
35 /*
@@ -35,20 +39,20 @@ int index_blob_bulk_checkin(struct object_id *oid,
39 * and objects are only visible after the outermost transaction
40 * is complete or the transaction is flushed.
41 */
38 -void begin_odb_transaction(void);
42 +struct odb_transaction *begin_odb_transaction(struct object_database *odb);
43
44 /*
45 * Make any objects that are currently part of a pending object
46 * database transaction visible. It is valid to call this function
47 * even if no transaction is active.
48 */
45 -void flush_odb_transaction(void);
49 +void flush_odb_transaction(struct odb_transaction *transaction);
50
51 /*
52 * Tell the object database to make any objects from the
53 * current transaction visible if this is the final nested
54 * transaction.
55 */
52 -void end_odb_transaction(void);
56 +void end_odb_transaction(struct odb_transaction *transaction);
57
58 #endif
cache-tree.c
+3 -2
@@ -474,6 +474,7 @@ static int update_one(struct cache_tree *it,
474
475 int cache_tree_update(struct index_state *istate, int flags)
476 {
477 + struct odb_transaction *transaction;
478 int skip, i;
479
480 i = verify_cache(istate, flags);
@@ -489,10 +490,10 @@ int cache_tree_update(struct index_state *istate, int flags)
490
491 trace_performance_enter();
492 trace2_region_enter("cache_tree", "update", the_repository);
492 - begin_odb_transaction();
493 + transaction = begin_odb_transaction(the_repository->objects);
494 i = update_one(istate->cache_tree, istate->cache, istate->cache_nr,
495 "", 0, &skip, flags);
495 - end_odb_transaction();
496 + end_odb_transaction(transaction);
497 trace2_region_leave("cache_tree", "update", the_repository);
498 trace_performance_leave("cache_tree_update");
499 if (i < 0)
object-file.c
+6 -5
@@ -674,7 +674,7 @@ static void close_loose_object(struct odb_source *source,
674 goto out;
675
676 if (batch_fsync_enabled(FSYNC_COMPONENT_LOOSE_OBJECT))
677 - fsync_loose_object_bulk_checkin(fd, filename);
677 + fsync_loose_object_bulk_checkin(source->odb->transaction, fd, filename);
678 else if (fsync_object_files > 0)
679 fsync_or_die(fd, filename);
680 else
@@ -852,7 +852,7 @@ static int write_loose_object(struct odb_source *source,
852 static struct strbuf filename = STRBUF_INIT;
853
854 if (batch_fsync_enabled(FSYNC_COMPONENT_LOOSE_OBJECT))
855 - prepare_loose_object_bulk_checkin();
855 + prepare_loose_object_bulk_checkin(source->odb->transaction);
856
857 odb_loose_path(source, &filename, oid);
858
@@ -941,7 +941,7 @@ int stream_loose_object(struct odb_source *source,
941 int hdrlen;
942
943 if (batch_fsync_enabled(FSYNC_COMPONENT_LOOSE_OBJECT))
944 - prepare_loose_object_bulk_checkin();
944 + prepare_loose_object_bulk_checkin(source->odb->transaction);
945
946 /* Since oid is not determined, save tmp file to odb path. */
947 strbuf_addf(&filename, "%s/", source->path);
@@ -1263,8 +1263,9 @@ int index_fd(struct index_state *istate, struct object_id *oid,
1263 ret = index_core(istate, oid, fd, xsize_t(st->st_size),
1264 type, path, flags);
1265 else
1266 - ret = index_blob_bulk_checkin(oid, fd, xsize_t(st->st_size), path,
1267 - flags);
1266 + ret = index_blob_bulk_checkin(the_repository->objects->transaction,
1267 + oid, fd, xsize_t(st->st_size),
1268 + path, flags);
1269 close(fd);
1270 return ret;
1271 }
odb.h
+8
@@ -84,6 +84,7 @@ struct odb_source {
84
85 struct packed_git;
86 struct cached_object_entry;
87 +struct odb_transaction;
88
89 /*
90 * The object database encapsulates access to objects in a repository. It
@@ -94,6 +95,13 @@ struct object_database {
95 /* Repository that owns this database. */
96 struct repository *repo;
97
98 + /*
99 + * State of current current object database transaction. Only one
100 + * transaction may be pending at a time. Is NULL when no transaction is
101 + * configured.
102 + */
103 + struct odb_transaction *transaction;
104 +
105 /*
106 * Set of all object directories; the main directory is first (and
107 * cannot be NULL after initialization). Subsequent directories are
read-cache.c
+3 -2
@@ -3947,6 +3947,7 @@ int add_files_to_cache(struct repository *repo, const char *prefix,
3947 const struct pathspec *pathspec, char *ps_matched,
3948 int include_sparse, int flags)
3949 {
3950 + struct odb_transaction *transaction;
3951 struct update_callback_data data;
3952 struct rev_info rev;
3953
@@ -3972,9 +3973,9 @@ int add_files_to_cache(struct repository *repo, const char *prefix,
3973 * This function is invoked from commands other than 'add', which
3974 * may not have their own transaction active.
3975 */
3975 - begin_odb_transaction();
3976 + transaction = begin_odb_transaction(repo->objects);
3977 run_diff_files(&rev, DIFF_RACY_IS_MODIFIED);
3977 - end_odb_transaction();
3978 + end_odb_transaction(transaction);
3979
3980 release_revisions(&rev);
3981 return !!data.add_errors;