odb/transaction: introduce ODB transaction flags

The temporary directory used by git-receive-pack(1) to write objects is managed slightly differently than how it is done via ODB transactions: - The temporary directory is eagerly created upfront, instead of waiting for the first object write. - The prefix name of the temporary directory is "incoming" instead of "bulk-fsync". In a subsequent commit, git-receive-pack(1) will use ODB transactions instead of `tmp_objdir` directly. To provide a means to configure the same transaction behavior, introduce `enum odb_transaction_flags` and the ODB_TRANSACTION_RECEIVE flag intended as a signal for ODB transactions using the "files" backend to be set up for git-receive-pack(1). Transaction call sites are updated accordingly to provide the required flag parameter. Signed-off-by: Justin Tobler <jltobler@gmail.com> Signed-off-by: Junio C Hamano <gitster@pobox.com>

Justin Tobler committed Jul 10, 2026 at 11:37 UTC 48d730a16a6f02ca952f653cf70d8c0471137112
13 files changed +61 -22
builtin/add.c
+1 -1
@@ -581,7 +581,7 @@ int cmd_add(int argc,
581 string_list_clear(&only_match_skip_worktree, 0);
582 }
583
584 - odb_transaction_begin_or_die(repo->objects, &transaction);
584 + odb_transaction_begin_or_die(repo->objects, &transaction, 0);
585
586 ps_matched = xcalloc(pathspec.nr, 1);
587 if (add_renormalize)
builtin/unpack-objects.c
+1 -1
@@ -596,7 +596,7 @@ static void unpack_all(void)
596 progress = start_progress(the_repository,
597 _("Unpacking objects"), nr_objects);
598 CALLOC_ARRAY(obj_list, nr_objects);
599 - odb_transaction_begin_or_die(the_repository->objects, &transaction);
599 + odb_transaction_begin_or_die(the_repository->objects, &transaction, 0);
600 for (i = 0; i < nr_objects; i++) {
601 unpack_one(i);
602 display_progress(progress, i + 1);
builtin/update-index.c
+1 -1
@@ -1124,7 +1124,7 @@ int cmd_update_index(int argc,
1124 * Allow the object layer to optimize adding multiple objects in
1125 * a batch.
1126 */
1127 - odb_transaction_begin_or_die(the_repository->objects, &transaction);
1127 + odb_transaction_begin_or_die(the_repository->objects, &transaction, 0);
1128 while (ctx.argc) {
1129 if (parseopt_state != PARSE_OPT_DONE)
1130 parseopt_state = parse_options_step(&ctx, options,
cache-tree.c
+1 -1
@@ -492,7 +492,7 @@ int cache_tree_update(struct index_state *istate, int flags)
492 trace_performance_enter();
493 trace2_region_enter("cache_tree", "update", istate->repo);
494 if (!inflight)
495 - odb_transaction_begin_or_die(the_repository->objects, &transaction);
495 + odb_transaction_begin_or_die(the_repository->objects, &transaction, 0);
496 i = update_one(istate->cache_tree, istate->cache, istate->cache_nr,
497 "", 0, &skip, flags);
498 if (!inflight)
object-file.c
+26 -3
@@ -498,6 +498,7 @@ struct odb_transaction_files {
498
499 struct tmp_objdir *objdir;
500 struct transaction_packfile packfile;
501 + const char *prefix;
502 };
503
504 static int odb_transaction_files_prepare(struct odb_transaction *base)
@@ -514,7 +515,7 @@ static int odb_transaction_files_prepare(struct odb_transaction *base)
515 if (!transaction || transaction->objdir)
516 return 0;
517
517 - transaction->objdir = tmp_objdir_create(base->source->odb->repo, "bulk-fsync");
518 + transaction->objdir = tmp_objdir_create(base->source->odb->repo, transaction->prefix);
519 if (!transaction->objdir)
520 return error(_("unable to create temporary object directory"));
521
@@ -1357,7 +1358,7 @@ int index_fd(struct index_state *istate, struct object_id *oid,
1358 int inflight = !!transaction;
1359
1360 if (!inflight)
1360 - odb_transaction_begin_or_die(odb, &transaction);
1361 + odb_transaction_begin_or_die(odb, &transaction, 0);
1362 ret = odb_transaction_write_object_stream(transaction,
1363 &stream,
1364 xsize_t(st->st_size),
@@ -1701,7 +1702,8 @@ static int odb_transaction_files_env(struct odb_transaction *base,
1702 }
1703
1704 int odb_transaction_files_begin(struct odb_source *source,
1704 - struct odb_transaction **out)
1705 + struct odb_transaction **out,
1706 + enum odb_transaction_flags flags)
1707 {
1708 struct odb_transaction_files *transaction;
1709
@@ -1710,6 +1712,27 @@ int odb_transaction_files_begin(struct odb_source *source,
1712 transaction->base.commit = odb_transaction_files_commit;
1713 transaction->base.write_object_stream = odb_transaction_files_write_object_stream;
1714 transaction->base.env = odb_transaction_files_env;
1715 +
1716 + transaction->prefix = "bulk-fsync";
1717 + if (flags & ODB_TRANSACTION_RECEIVE) {
1718 + /*
1719 + * ODB transactions for git-receive-pack(1) eagerly create a
1720 + * temporary directory and use a different temporary directory
1721 + * prefix.
1722 + *
1723 + * NEEDSWORK: This transaction flag is only used by the "files"
1724 + * backend to special case temporary directory set up and
1725 + * handling. Ideally transaction users should not have to care
1726 + * though. To avoid this, we could eagerly create the temporary
1727 + * directory and use the same prefix name for all transactions.
1728 + */
1729 + transaction->prefix = "incoming";
1730 + if (odb_transaction_files_prepare(&transaction->base)) {
1731 + free(transaction);
1732 + return -1;
1733 + }
1734 + }
1735 +
1736 *out = &transaction->base;
1737
1738 return 0;
object-file.h
+3 -1
@@ -5,6 +5,7 @@
5 #include "object.h"
6 #include "odb.h"
7 #include "odb/source-loose.h"
8 +#include "odb/transaction.h"
9
10 /* The maximum size for an object header. */
11 #define MAX_HEADER_LEN 32
@@ -197,6 +198,7 @@ struct odb_transaction;
198 * to make new objects visible.
199 */
200 int odb_transaction_files_begin(struct odb_source *source,
200 - struct odb_transaction **out);
201 + struct odb_transaction **out,
202 + enum odb_transaction_flags flags);
203
204 #endif /* OBJECT_FILE_H */
odb/source-files.c
+3 -2
@@ -180,9 +180,10 @@ static int odb_source_files_write_object_stream(struct odb_source *source,
180 }
181
182 static int odb_source_files_begin_transaction(struct odb_source *source,
183 - struct odb_transaction **out)
183 + struct odb_transaction **out,
184 + enum odb_transaction_flags flags)
185 {
185 - return odb_transaction_files_begin(source, out);
186 + return odb_transaction_files_begin(source, out, flags);
187 }
188
189 static int odb_source_files_read_alternates(struct odb_source *source,
odb/source-inmemory.c
+2 -1
@@ -304,7 +304,8 @@ static int odb_source_inmemory_freshen_object(struct odb_source *source,
304 }
305
306 static int odb_source_inmemory_begin_transaction(struct odb_source *source UNUSED,
307 - struct odb_transaction **out UNUSED)
307 + struct odb_transaction **out UNUSED,
308 + enum odb_transaction_flags flags UNUSED)
309 {
310 return error("in-memory source does not support transactions");
311 }
odb/source-loose.c
+2 -1
@@ -646,7 +646,8 @@ static int odb_source_loose_write_object_stream(struct odb_source *source,
646 }
647
648 static int odb_source_loose_begin_transaction(struct odb_source *source UNUSED,
649 - struct odb_transaction **out UNUSED)
649 + struct odb_transaction **out UNUSED,
650 + enum odb_transaction_flags flags UNUSED)
651 {
652 /* TODO: this is a known omission that we'll want to address eventually. */
653 return error("loose source does not support transactions");
odb/source.h
+6 -3
@@ -3,6 +3,7 @@
3
4 #include "object.h"
5 #include "odb.h"
6 +#include "odb/transaction.h"
7
8 enum odb_source_type {
9 /*
@@ -228,7 +229,8 @@ struct odb_source {
229 * negative error code otherwise.
230 */
231 int (*begin_transaction)(struct odb_source *source,
231 - struct odb_transaction **out);
232 + struct odb_transaction **out,
233 + enum odb_transaction_flags flags);
234
235 /*
236 * This callback is expected to read the list of alternate object
@@ -467,9 +469,10 @@ static inline int odb_source_write_alternate(struct odb_source *source,
469 * Returns 0 on success, a negative error code otherwise.
470 */
471 static inline int odb_source_begin_transaction(struct odb_source *source,
470 - struct odb_transaction **out)
472 + struct odb_transaction **out,
473 + enum odb_transaction_flags flags)
474 {
472 - return source->begin_transaction(source, out);
475 + return source->begin_transaction(source, out, flags);
476 }
477
478 #endif
odb/transaction.c
+3 -2
@@ -4,14 +4,15 @@
4 #include "odb/transaction.h"
5
6 int odb_transaction_begin(struct object_database *odb,
7 - struct odb_transaction **out)
7 + struct odb_transaction **out,
8 + enum odb_transaction_flags flags)
9 {
10 int ret;
11
12 if (odb->transaction)
13 return error(_("object database transaction already pending"));
14
14 - ret = odb_source_begin_transaction(odb->sources, out);
15 + ret = odb_source_begin_transaction(odb->sources, out, flags);
16 if (!ret)
17 odb->transaction = *out;
18
odb/transaction.h
+11 -4
@@ -3,7 +3,6 @@
3
4 #include "gettext.h"
5 #include "odb.h"
6 -#include "odb/source.h"
6
7 /*
8 * A transaction may be started for an object database prior to writing new
@@ -44,6 +43,12 @@ struct odb_transaction {
43 int (*env)(struct odb_transaction *transaction, struct strvec *env);
44 };
45
46 +/* Flags used to configure an ODB transaction. */
47 +enum odb_transaction_flags {
48 + /* Configures the transaction for use with git-receive-pack(1). */
49 + ODB_TRANSACTION_RECEIVE = (1 << 0),
50 +};
51 +
52 /*
53 * Starts an ODB transaction and returns it via `out`. Subsequent objects are
54 * written to the transaction and not committed until odb_transaction_commit()
@@ -52,12 +57,14 @@ struct odb_transaction {
57 * ODB already has an inflight transaction pending.
58 */
59 int odb_transaction_begin(struct object_database *odb,
55 - struct odb_transaction **out);
60 + struct odb_transaction **out,
61 + enum odb_transaction_flags flags);
62
63 static inline void odb_transaction_begin_or_die(struct object_database *odb,
58 - struct odb_transaction **out)
64 + struct odb_transaction **out,
65 + enum odb_transaction_flags flags)
66 {
60 - if (odb_transaction_begin(odb, out))
67 + if (odb_transaction_begin(odb, out, flags))
68 die(_("failed to start ODB transaction"));
69 }
70
read-cache.c
+1 -1
@@ -4044,7 +4044,7 @@ int add_files_to_cache(struct repository *repo, const char *prefix,
4044 * may not have their own transaction active.
4045 */
4046 if (!inflight)
4047 - odb_transaction_begin_or_die(repo->objects, &transaction);
4047 + odb_transaction_begin_or_die(repo->objects, &transaction, 0);
4048 run_diff_files(&rev, DIFF_RACY_IS_MODIFIED);
4049 if (!inflight)
4050 odb_transaction_commit(transaction);