]> git.ipfire.org Git - thirdparty/git.git/commitdiff
odb: prepare `struct odb_transaction` to become generic
authorJustin Tobler <jltobler@gmail.com>
Tue, 3 Feb 2026 00:10:01 +0000 (18:10 -0600)
committerJunio C Hamano <gitster@pobox.com>
Tue, 3 Feb 2026 01:14:03 +0000 (17:14 -0800)
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>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
object-file.c
object-file.h
odb.c
odb.h

index 7b34a2b274c8ce32902ca151d1ee0d19829243cb..d7e153c1b9ad04202b3be90a7d8f0c0d3343bc71 100644 (file)
@@ -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 @@ clear_exit:
  * 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 @@ out:
        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;
index b4a3341a890dc4b7d3d7fe49cecead9add4cfdc7..a62d0de39459b1452282df1510da604f6b801130 100644 (file)
@@ -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 a5e6fd01a930aff3c6c3f640cf5bc7a60dc2ec94..349b4218a591cfb73e75c89199d2cb2edf775cef 100644 (file)
--- 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 bab07755f4ec95ae3ad67caef291f2f03baf332a..83d3a37805980243e6488f4d164a360c80594bb6 100644 (file)
--- 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