odb/source: support writing alternates when creating the database

Add the ability to write alternates when creating the object database.
This change allows us to remove the `write_alternates()` callback in a
subsequent patch.

Signed-off-by: Patrick Steinhardt <ps@pks.im>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
jch
Patrick Steinhardt 2026-08-31 12:02:10 +02:00 committed by Junio C Hamano
parent 2e26876b48
commit d540449b15
3 changed files with 91 additions and 6 deletions

View File

@ -18,6 +18,7 @@
#include "run-command.h"
#include "strbuf.h"
#include "string-list.h"
#include "strmap.h"
#include "strvec.h"
#include "tree.h"
#include "write-or-die.h"
@ -51,9 +52,14 @@ static void odb_source_files_close(struct odb_source *source)
odb_source_close(&files->packed->base);
}

static int odb_source_files_create_on_disk(struct odb_source *source)
static int odb_source_files_create_on_disk(struct odb_source *source,
const struct odb_create_on_disk_options *opts)
{
struct lock_file alternates_lock = LOCK_INIT;
struct strbuf path = STRBUF_INIT;
struct strset seen = STRSET_INIT;
struct strbuf line = STRBUF_INIT;
int ret;

safe_create_dir(source->odb->repo, source->path, 1);

@ -64,8 +70,74 @@ static int odb_source_files_create_on_disk(struct odb_source *source)
strbuf_addf(&path, "%s/info", source->path);
safe_create_dir(source->odb->repo, path.buf, 1);

if (opts->alternates && opts->alternates->nr) {
FILE *alternates, *orig;

strbuf_reset(&path);
strbuf_addf(&path, "%s/info/alternates", source->path);

repo_hold_lock_file_for_update(source->odb->repo, &alternates_lock,
path.buf, LOCK_DIE_ON_ERROR);

alternates = fdopen_lock_file(&alternates_lock, "w");
if (!alternates) {
ret = error_errno(_("unable to fdopen alternates lockfile"));
goto out;
}

/*
* The alternates file may already exist, e.g. when it has been
* seeded from a template directory. Read any preexisting
* entries so that we don't end up writing duplicates.
*/
orig = fopen(path.buf, "r");
if (orig) {
while (strbuf_getline(&line, orig) != EOF) {
strset_add(&seen, line.buf);
fprintf(alternates, "%s\n", line.buf);
}

if (ferror(orig)) {
ret = error_errno(_("unable to read alternates file"));
fclose(orig);
goto out;
}

fclose(orig);
} else if (errno != ENOENT) {
ret = error_errno(_("unable to read alternates file"));
goto out;
}

for (size_t i = 0; i < opts->alternates->nr; i++) {
const char *alternate = opts->alternates->v[i];
if (!strset_add(&seen, alternate))
continue;
fprintf(alternates, "%s\n", alternate);
}

if (ferror(alternates)) {
ret = error_errno(_("unable to write alternates file"));
goto out;
}

if (commit_lock_file(&alternates_lock)) {
ret = error_errno(_("unable to commit alternates file"));
goto out;
}
}

/* Reprepare the object database to activate alternates. */
odb_reprepare(source->odb);

ret = 0;

out:
rollback_lock_file(&alternates_lock);
strbuf_release(&line);
strbuf_release(&path);
return 0;
strset_clear(&seen);
return ret;
}

static void odb_source_files_prepare(struct odb_source *source,

View File

@ -36,6 +36,15 @@ struct object_id;
struct odb_stream;
struct strvec;

struct odb_create_on_disk_options {
/*
* Alternates that shall be written into the newly created object
* database. Whether or not this option can be handled is specific to
* the backend.
*/
const struct strvec *alternates;
};

/*
* The source is the part of the object database that stores the actual
* objects. It thus encapsulates the logic to read and write the specific
@ -106,7 +115,8 @@ struct odb_source {
* This callback may be NULL in case the source does not need any
* on-disk setup.
*/
int (*create_on_disk)(struct odb_source *source);
int (*create_on_disk)(struct odb_source *source,
const struct odb_create_on_disk_options *opts);

/*
* This callback is expected to prepare the source so that it becomes
@ -356,11 +366,12 @@ static inline void odb_source_close(struct odb_source *source)
* Create on-disk data structures that are required for this source to operate
* correctly. Returns 0 on success, a negative error code otherwise.
*/
static inline int odb_source_create_on_disk(struct odb_source *source)
static inline int odb_source_create_on_disk(struct odb_source *source,
const struct odb_create_on_disk_options *opts)
{
if (!source->create_on_disk)
return 0;
return source->create_on_disk(source);
return source->create_on_disk(source, opts);
}

/*

View File

@ -2649,6 +2649,8 @@ static int create_default_files(struct repository *repo,

void create_object_database(struct repository *repo)
{
struct odb_create_on_disk_options opts = { 0 };

/*
* Create the "objects" directory in the common directory. This is done
* so that the repository can be discovered regardless of the backend
@ -2668,7 +2670,7 @@ void create_object_database(struct repository *repo)

repo->objects = odb_new(repo, ODB_NEW_HONOR_ENV);

if (odb_source_create_on_disk(repo->objects->sources) < 0)
if (odb_source_create_on_disk(repo->objects->sources, &opts) < 0)
die(_("failed creating object database"));
}