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>
This commit is contained in:
Justin Tobler
2026-07-10 11:37:20 -05:00
committed by Junio C Hamano
parent 28fbc06769
commit 48d730a16a
13 changed files with 61 additions and 22 deletions

View File

@@ -581,7 +581,7 @@ int cmd_add(int argc,
string_list_clear(&only_match_skip_worktree, 0);
}
odb_transaction_begin_or_die(repo->objects, &transaction);
odb_transaction_begin_or_die(repo->objects, &transaction, 0);
ps_matched = xcalloc(pathspec.nr, 1);
if (add_renormalize)

View File

@@ -596,7 +596,7 @@ static void unpack_all(void)
progress = start_progress(the_repository,
_("Unpacking objects"), nr_objects);
CALLOC_ARRAY(obj_list, nr_objects);
odb_transaction_begin_or_die(the_repository->objects, &transaction);
odb_transaction_begin_or_die(the_repository->objects, &transaction, 0);
for (i = 0; i < nr_objects; i++) {
unpack_one(i);
display_progress(progress, i + 1);

View File

@@ -1124,7 +1124,7 @@ int cmd_update_index(int argc,
* Allow the object layer to optimize adding multiple objects in
* a batch.
*/
odb_transaction_begin_or_die(the_repository->objects, &transaction);
odb_transaction_begin_or_die(the_repository->objects, &transaction, 0);
while (ctx.argc) {
if (parseopt_state != PARSE_OPT_DONE)
parseopt_state = parse_options_step(&ctx, options,

View File

@@ -492,7 +492,7 @@ int cache_tree_update(struct index_state *istate, int flags)
trace_performance_enter();
trace2_region_enter("cache_tree", "update", istate->repo);
if (!inflight)
odb_transaction_begin_or_die(the_repository->objects, &transaction);
odb_transaction_begin_or_die(the_repository->objects, &transaction, 0);
i = update_one(istate->cache_tree, istate->cache, istate->cache_nr,
"", 0, &skip, flags);
if (!inflight)

View File

@@ -498,6 +498,7 @@ struct odb_transaction_files {
struct tmp_objdir *objdir;
struct transaction_packfile packfile;
const char *prefix;
};
static int odb_transaction_files_prepare(struct odb_transaction *base)
@@ -514,7 +515,7 @@ static int odb_transaction_files_prepare(struct odb_transaction *base)
if (!transaction || transaction->objdir)
return 0;
transaction->objdir = tmp_objdir_create(base->source->odb->repo, "bulk-fsync");
transaction->objdir = tmp_objdir_create(base->source->odb->repo, transaction->prefix);
if (!transaction->objdir)
return error(_("unable to create temporary object directory"));
@@ -1357,7 +1358,7 @@ int index_fd(struct index_state *istate, struct object_id *oid,
int inflight = !!transaction;
if (!inflight)
odb_transaction_begin_or_die(odb, &transaction);
odb_transaction_begin_or_die(odb, &transaction, 0);
ret = odb_transaction_write_object_stream(transaction,
&stream,
xsize_t(st->st_size),
@@ -1701,7 +1702,8 @@ static int odb_transaction_files_env(struct odb_transaction *base,
}
int odb_transaction_files_begin(struct odb_source *source,
struct odb_transaction **out)
struct odb_transaction **out,
enum odb_transaction_flags flags)
{
struct odb_transaction_files *transaction;
@@ -1710,6 +1712,27 @@ int odb_transaction_files_begin(struct odb_source *source,
transaction->base.commit = odb_transaction_files_commit;
transaction->base.write_object_stream = odb_transaction_files_write_object_stream;
transaction->base.env = odb_transaction_files_env;
transaction->prefix = "bulk-fsync";
if (flags & ODB_TRANSACTION_RECEIVE) {
/*
* ODB transactions for git-receive-pack(1) eagerly create a
* temporary directory and use a different temporary directory
* prefix.
*
* NEEDSWORK: This transaction flag is only used by the "files"
* backend to special case temporary directory set up and
* handling. Ideally transaction users should not have to care
* though. To avoid this, we could eagerly create the temporary
* directory and use the same prefix name for all transactions.
*/
transaction->prefix = "incoming";
if (odb_transaction_files_prepare(&transaction->base)) {
free(transaction);
return -1;
}
}
*out = &transaction->base;
return 0;

View File

@@ -5,6 +5,7 @@
#include "object.h"
#include "odb.h"
#include "odb/source-loose.h"
#include "odb/transaction.h"
/* The maximum size for an object header. */
#define MAX_HEADER_LEN 32
@@ -197,6 +198,7 @@ struct odb_transaction;
* to make new objects visible.
*/
int odb_transaction_files_begin(struct odb_source *source,
struct odb_transaction **out);
struct odb_transaction **out,
enum odb_transaction_flags flags);
#endif /* OBJECT_FILE_H */

View File

@@ -180,9 +180,10 @@ static int odb_source_files_write_object_stream(struct odb_source *source,
}
static int odb_source_files_begin_transaction(struct odb_source *source,
struct odb_transaction **out)
struct odb_transaction **out,
enum odb_transaction_flags flags)
{
return odb_transaction_files_begin(source, out);
return odb_transaction_files_begin(source, out, flags);
}
static int odb_source_files_read_alternates(struct odb_source *source,

View File

@@ -304,7 +304,8 @@ static int odb_source_inmemory_freshen_object(struct odb_source *source,
}
static int odb_source_inmemory_begin_transaction(struct odb_source *source UNUSED,
struct odb_transaction **out UNUSED)
struct odb_transaction **out UNUSED,
enum odb_transaction_flags flags UNUSED)
{
return error("in-memory source does not support transactions");
}

View File

@@ -646,7 +646,8 @@ static int odb_source_loose_write_object_stream(struct odb_source *source,
}
static int odb_source_loose_begin_transaction(struct odb_source *source UNUSED,
struct odb_transaction **out UNUSED)
struct odb_transaction **out UNUSED,
enum odb_transaction_flags flags UNUSED)
{
/* TODO: this is a known omission that we'll want to address eventually. */
return error("loose source does not support transactions");

View File

@@ -3,6 +3,7 @@
#include "object.h"
#include "odb.h"
#include "odb/transaction.h"
enum odb_source_type {
/*
@@ -228,7 +229,8 @@ struct odb_source {
* negative error code otherwise.
*/
int (*begin_transaction)(struct odb_source *source,
struct odb_transaction **out);
struct odb_transaction **out,
enum odb_transaction_flags flags);
/*
* 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,
* Returns 0 on success, a negative error code otherwise.
*/
static inline int odb_source_begin_transaction(struct odb_source *source,
struct odb_transaction **out)
struct odb_transaction **out,
enum odb_transaction_flags flags)
{
return source->begin_transaction(source, out);
return source->begin_transaction(source, out, flags);
}
#endif

View File

@@ -4,14 +4,15 @@
#include "odb/transaction.h"
int odb_transaction_begin(struct object_database *odb,
struct odb_transaction **out)
struct odb_transaction **out,
enum odb_transaction_flags flags)
{
int ret;
if (odb->transaction)
return error(_("object database transaction already pending"));
ret = odb_source_begin_transaction(odb->sources, out);
ret = odb_source_begin_transaction(odb->sources, out, flags);
if (!ret)
odb->transaction = *out;

View File

@@ -3,7 +3,6 @@
#include "gettext.h"
#include "odb.h"
#include "odb/source.h"
/*
* A transaction may be started for an object database prior to writing new
@@ -44,6 +43,12 @@ struct odb_transaction {
int (*env)(struct odb_transaction *transaction, struct strvec *env);
};
/* Flags used to configure an ODB transaction. */
enum odb_transaction_flags {
/* Configures the transaction for use with git-receive-pack(1). */
ODB_TRANSACTION_RECEIVE = (1 << 0),
};
/*
* Starts an ODB transaction and returns it via `out`. Subsequent objects are
* written to the transaction and not committed until odb_transaction_commit()
@@ -52,12 +57,14 @@ struct odb_transaction {
* ODB already has an inflight transaction pending.
*/
int odb_transaction_begin(struct object_database *odb,
struct odb_transaction **out);
struct odb_transaction **out,
enum odb_transaction_flags flags);
static inline void odb_transaction_begin_or_die(struct object_database *odb,
struct odb_transaction **out)
struct odb_transaction **out,
enum odb_transaction_flags flags)
{
if (odb_transaction_begin(odb, out))
if (odb_transaction_begin(odb, out, flags))
die(_("failed to start ODB transaction"));
}

View File

@@ -4044,7 +4044,7 @@ int add_files_to_cache(struct repository *repo, const char *prefix,
* may not have their own transaction active.
*/
if (!inflight)
odb_transaction_begin_or_die(repo->objects, &transaction);
odb_transaction_begin_or_die(repo->objects, &transaction, 0);
run_diff_files(&rev, DIFF_RACY_IS_MODIFIED);
if (!inflight)
odb_transaction_commit(transaction);