From: Justin Tobler <jltobler@gmail.com>
To: git@vger.kernel.org
Cc: ps@pks.im, gitster@pobox.com, Justin Tobler <jltobler@gmail.com>
Subject: [PATCH v2 3/4] odb: prepare `struct odb_transaction` to become generic
Date: Mon, 2 Feb 2026 18:10:01 -0600 [thread overview]
Message-ID: <20260203001002.2500198-4-jltobler@gmail.com> (raw)
In-Reply-To: <20260203001002.2500198-1-jltobler@gmail.com>
An ODB transaction handles how objects are stored temporarily and
eventually committed. Due to object storage being implemented
differently for a given ODB source, the ODB transactions must be
implemented in a manner specific to the source the objects are being
written to. To provide generic transactions, `struct odb_transaction` is
updated to store a commit callback that can be configured to support a
specific ODB source. For now `struct odb_transaction_files` is the
only transaction type and what is always returned when starting a
transaction.
Signed-off-by: Justin Tobler <jltobler@gmail.com>
---
object-file.c | 80 ++++++++++++++++++++++++++++-----------------------
object-file.h | 6 ----
odb.c | 5 +++-
odb.h | 17 +++++++++++
4 files changed, 65 insertions(+), 43 deletions(-)
diff --git a/object-file.c b/object-file.c
index 7b34a2b274..d7e153c1b9 100644
--- a/object-file.c
+++ b/object-file.c
@@ -710,15 +710,17 @@ struct transaction_packfile {
uint32_t nr_written;
};
-struct odb_transaction {
- struct odb_source *source;
+struct odb_transaction_files {
+ struct odb_transaction base;
struct tmp_objdir *objdir;
struct transaction_packfile packfile;
};
-static void prepare_loose_object_transaction(struct odb_transaction *transaction)
+static void prepare_loose_object_transaction(struct odb_transaction *base)
{
+ struct odb_transaction_files *transaction = (struct odb_transaction_files *)base;
+
/*
* We lazily create the temporary object directory
* the first time an object might be added, since
@@ -728,14 +730,16 @@ static void prepare_loose_object_transaction(struct odb_transaction *transaction
if (!transaction || transaction->objdir)
return;
- transaction->objdir = tmp_objdir_create(transaction->source->odb->repo, "bulk-fsync");
+ transaction->objdir = tmp_objdir_create(base->source->odb->repo, "bulk-fsync");
if (transaction->objdir)
tmp_objdir_replace_primary_odb(transaction->objdir, 0);
}
-static void fsync_loose_object_transaction(struct odb_transaction *transaction,
+static void fsync_loose_object_transaction(struct odb_transaction *base,
int fd, const char *filename)
{
+ struct odb_transaction_files *transaction = (struct odb_transaction_files *)base;
+
/*
* If we have an active ODB transaction, we issue a call that
* cleans the filesystem page cache but avoids a hardware flush
@@ -754,7 +758,7 @@ static void fsync_loose_object_transaction(struct odb_transaction *transaction,
/*
* Cleanup after batch-mode fsync_object_files.
*/
-static void flush_loose_object_transaction(struct odb_transaction *transaction)
+static void flush_loose_object_transaction(struct odb_transaction_files *transaction)
{
struct strbuf temp_path = STRBUF_INIT;
struct tempfile *temp;
@@ -772,7 +776,7 @@ static void flush_loose_object_transaction(struct odb_transaction *transaction)
* the final name is visible.
*/
strbuf_addf(&temp_path, "%s/bulk_fsync_XXXXXX",
- repo_get_object_directory(transaction->source->odb->repo));
+ 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);
@@ -1340,11 +1344,11 @@ static int index_core(struct index_state *istate,
return ret;
}
-static int already_written(struct odb_transaction *transaction,
+static int already_written(struct odb_transaction_files *transaction,
struct object_id *oid)
{
/* The object may already exist in the repository */
- if (odb_has_object(transaction->source->odb, oid,
+ if (odb_has_object(transaction->base.source->odb, oid,
HAS_OBJECT_RECHECK_PACKED | HAS_OBJECT_FETCH_PROMISOR))
return 1;
@@ -1358,14 +1362,14 @@ static int already_written(struct odb_transaction *transaction,
}
/* Lazily create backing packfile for the state */
-static void prepare_packfile_transaction(struct odb_transaction *transaction,
+static void prepare_packfile_transaction(struct odb_transaction_files *transaction,
unsigned flags)
{
struct transaction_packfile *state = &transaction->packfile;
if (!(flags & INDEX_WRITE_OBJECT) || state->f)
return;
- state->f = create_tmp_packfile(transaction->source->odb->repo,
+ state->f = create_tmp_packfile(transaction->base.source->odb->repo,
&state->pack_tmp_name);
reset_pack_idx_option(&state->pack_idx_opts);
@@ -1466,10 +1470,10 @@ static int stream_blob_to_pack(struct transaction_packfile *state,
return 0;
}
-static void flush_packfile_transaction(struct odb_transaction *transaction)
+static void flush_packfile_transaction(struct odb_transaction_files *transaction)
{
struct transaction_packfile *state = &transaction->packfile;
- struct repository *repo = transaction->source->odb->repo;
+ struct repository *repo = transaction->base.source->odb->repo;
unsigned char hash[GIT_MAX_RAWSZ];
struct strbuf packname = STRBUF_INIT;
char *idx_tmp_name = NULL;
@@ -1494,7 +1498,7 @@ static void flush_packfile_transaction(struct odb_transaction *transaction)
}
strbuf_addf(&packname, "%s/pack/pack-%s.",
- repo_get_object_directory(transaction->source->odb->repo),
+ repo_get_object_directory(transaction->base.source->odb->repo),
hash_to_hex_algop(hash, repo->hash_algo));
stage_tmp_packfiles(repo, &packname, state->pack_tmp_name,
@@ -1534,7 +1538,7 @@ static void flush_packfile_transaction(struct odb_transaction *transaction)
* binary blobs, they generally do not want to get any conversion, and
* callers should avoid this code path when filters are requested.
*/
-static int index_blob_packfile_transaction(struct odb_transaction *transaction,
+static int index_blob_packfile_transaction(struct odb_transaction_files *transaction,
struct object_id *result_oid, int fd,
size_t size, const char *path,
unsigned flags)
@@ -1553,7 +1557,7 @@ static int index_blob_packfile_transaction(struct odb_transaction *transaction,
header_len = format_object_header((char *)obuf, sizeof(obuf),
OBJ_BLOB, size);
- transaction->source->odb->repo->hash_algo->init_fn(&ctx);
+ transaction->base.source->odb->repo->hash_algo->init_fn(&ctx);
git_hash_update(&ctx, obuf, header_len);
/* Note: idx is non-NULL when we are writing */
@@ -1629,10 +1633,11 @@ 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 {
+ struct object_database *odb = the_repository->objects;
struct odb_transaction *transaction;
- transaction = odb_transaction_begin(the_repository->objects);
- ret = index_blob_packfile_transaction(the_repository->objects->transaction,
+ transaction = odb_transaction_begin(odb);
+ ret = index_blob_packfile_transaction((struct odb_transaction_files *)odb->transaction,
oid, fd,
xsize_t(st->st_size),
path, flags);
@@ -1985,35 +1990,38 @@ int read_loose_object(struct repository *repo,
return ret;
}
-struct odb_transaction *odb_transaction_files_begin(struct odb_source *source)
+static void odb_transaction_files_commit(struct odb_transaction *base)
{
- struct object_database *odb = source->odb;
-
- if (odb->transaction)
- return NULL;
-
- CALLOC_ARRAY(odb->transaction, 1);
- odb->transaction->source = source;
-
- return odb->transaction;
-}
-
-void odb_transaction_files_commit(struct odb_transaction *transaction)
-{
- if (!transaction)
- return;
+ struct odb_transaction_files *transaction = (struct odb_transaction_files *)base;
/*
* Ensure the transaction ending matches the pending transaction.
*/
- ASSERT(transaction == transaction->source->odb->transaction);
+ ASSERT(base == base->source->odb->transaction);
flush_loose_object_transaction(transaction);
flush_packfile_transaction(transaction);
- transaction->source->odb->transaction = NULL;
+ base->source->odb->transaction = NULL;
free(transaction);
}
+struct odb_transaction *odb_transaction_files_begin(struct odb_source *source)
+{
+ 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;
+
+ odb->transaction = &transaction->base;
+
+ return &transaction->base;
+}
+
struct odb_source_loose *odb_source_loose_new(struct odb_source *source)
{
struct odb_source_loose *loose;
diff --git a/object-file.h b/object-file.h
index b4a3341a89..a62d0de394 100644
--- a/object-file.h
+++ b/object-file.h
@@ -208,10 +208,4 @@ struct odb_transaction;
*/
struct odb_transaction *odb_transaction_files_begin(struct odb_source *source);
-/*
- * Tell the object database to make any objects from the
- * current transaction visible.
- */
-void odb_transaction_files_commit(struct odb_transaction *transaction);
-
#endif /* OBJECT_FILE_H */
diff --git a/odb.c b/odb.c
index a5e6fd01a9..349b4218a5 100644
--- a/odb.c
+++ b/odb.c
@@ -1158,5 +1158,8 @@ struct odb_transaction *odb_transaction_begin(struct object_database *odb)
void odb_transaction_commit(struct odb_transaction *transaction)
{
- odb_transaction_files_commit(transaction);
+ if (!transaction)
+ return;
+
+ transaction->commit(transaction);
}
diff --git a/odb.h b/odb.h
index bab07755f4..83d3a37805 100644
--- a/odb.h
+++ b/odb.h
@@ -77,7 +77,24 @@ struct odb_source {
struct packed_git;
struct packfile_store;
struct cached_object_entry;
+
+/*
+ * A transaction may be started for an object database prior to writing new
+ * objects via odb_transaction_begin(). These objects are not committed until
+ * odb_transaction_commit() is invoked. Only a single transaction may be pending
+ * at a time.
+ *
+ * Each ODB source is expected to implement its own transaction handling.
+ */
struct odb_transaction;
+typedef void (*odb_transaction_commit_fn)(struct odb_transaction *transaction);
+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. */
+ odb_transaction_commit_fn commit;
+};
/*
* The object database encapsulates access to objects in a repository. It
--
2.52.0.373.g68cb7f9e92
next prev parent reply other threads:[~2026-02-03 0:10 UTC|newest]
Thread overview: 32+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-01-28 23:45 [PATCH 0/4] odb: support ODB source specific transaction handling Justin Tobler
2026-01-28 23:45 ` [PATCH 1/4] odb: store ODB source in `struct odb_transaction` Justin Tobler
2026-01-29 11:24 ` Patrick Steinhardt
2026-01-29 19:25 ` Junio C Hamano
2026-01-29 20:12 ` Justin Tobler
2026-01-29 20:28 ` Junio C Hamano
2026-01-29 21:54 ` Justin Tobler
2026-01-29 19:30 ` Justin Tobler
2026-01-28 23:45 ` [PATCH 2/4] object-file: rename transaction functions Justin Tobler
2026-01-28 23:45 ` [PATCH 3/4] odb: prepare `struct odb_transaction` to support more sources Justin Tobler
2026-01-29 11:24 ` Patrick Steinhardt
2026-01-29 19:41 ` Justin Tobler
2026-01-28 23:45 ` [PATCH 4/4] odb: transparently handle common transaction behavior Justin Tobler
2026-01-29 11:24 ` Patrick Steinhardt
2026-02-03 0:09 ` [PATCH v2 0/4] odb: support ODB source specific transaction handling Justin Tobler
2026-02-03 0:09 ` [PATCH v2 1/4] odb: store ODB source in `struct odb_transaction` Justin Tobler
2026-02-03 0:10 ` [PATCH v2 2/4] object-file: rename transaction functions Justin Tobler
2026-02-03 0:10 ` Justin Tobler [this message]
2026-02-03 15:54 ` [PATCH v2 3/4] odb: prepare `struct odb_transaction` to become generic Toon Claes
2026-02-03 16:46 ` Justin Tobler
2026-02-03 22:54 ` Junio C Hamano
2026-02-04 6:26 ` Patrick Steinhardt
2026-02-04 17:15 ` Justin Tobler
2026-02-04 10:31 ` Karthik Nayak
2026-02-04 17:38 ` Justin Tobler
2026-02-05 11:20 ` Karthik Nayak
2026-02-03 0:10 ` [PATCH v2 4/4] odb: transparently handle common transaction behavior Justin Tobler
2026-02-04 10:34 ` Karthik Nayak
2026-02-04 17:50 ` Justin Tobler
2026-02-05 11:22 ` Karthik Nayak
2026-02-03 1:16 ` [PATCH v2 0/4] odb: support ODB source specific transaction handling Junio C Hamano
2026-02-04 6:25 ` Patrick Steinhardt
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20260203001002.2500198-4-jltobler@gmail.com \
--to=jltobler@gmail.com \
--cc=git@vger.kernel.org \
--cc=gitster@pobox.com \
--cc=ps@pks.im \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox