Merge branch 'ps/odb-alternates-at-creation' into next

The setup of alternates has been deferred to object database
creation time during clone, which drops the unused ad-hoc alternate
writing API, simplifying the object database backend interface.

* ps/odb-alternates-at-creation:
  odb/source: remove the ability to write alternates
  builtin/clone: write alternates via `odb_create_on_disk()`
  odb/source: support writing alternates when creating the database
  builtin/clone: move setup of alternates for non-shared local clones
  builtin/clone: move setup of alternates for shared local clones
  builtin/clone: refactor handling of "--reference{,-if-able}"
  builtin/clone: move around `setup_reference()`
  builtin/clone: defer setup of the object database
  setup: split up concerns of `init_db()`
next
Junio C Hamano 2026-09-15 11:09:25 -07:00
commit 4acaa6a8aa
12 changed files with 263 additions and 221 deletions

View File

@ -154,24 +154,26 @@ static char *get_repo_path(const char *repo, int *is_bundle)
return canon;
}

static int add_one_reference(struct string_list_item *item, void *cb_data)
struct collect_alternates_data {
struct strvec *alternates;
bool required;
};

static int collect_one_alternate(struct string_list_item *item, void *cb_data)
{
struct collect_alternates_data *data = cb_data;
struct strbuf err = STRBUF_INIT;
int *required = cb_data;
char *ref_git = compute_alternate_path(item->string, &err);

if (!ref_git) {
if (*required)
if (data->required)
die("%s", err.buf);
else
fprintf(stderr,
_("info: Could not add alternate for '%s': %s\n"),
item->string, err.buf);
} else {
struct strbuf sb = STRBUF_INIT;
strbuf_addf(&sb, "%s/objects", ref_git);
odb_add_to_alternates_file(the_repository->objects, sb.buf);
strbuf_release(&sb);
strvec_pushf(data->alternates, "%s/objects", ref_git);
}

strbuf_release(&err);
@ -179,17 +181,7 @@ static int add_one_reference(struct string_list_item *item, void *cb_data)
return 0;
}

static void setup_reference(void)
{
int required = 1;
for_each_string_list(&option_required_reference,
add_one_reference, &required);
required = 0;
for_each_string_list(&option_optional_reference,
add_one_reference, &required);
}

static void copy_alternates(struct strbuf *src, const char *src_repo)
static void read_alternates(struct strvec *alternates, const char *src_repo)
{
/*
* Read from the source objects/info/alternates file
@ -203,29 +195,70 @@ static void copy_alternates(struct strbuf *src, const char *src_repo)
* to turn entries with paths relative to the original
* absolute, so that they can be used in the new repository.
*/
FILE *in = xfopen(src->buf, "r");
FILE *in;
struct strbuf path = STRBUF_INIT;
struct strbuf line = STRBUF_INIT;

strbuf_addf(&path, "%s/objects/info/alternates", src_repo);

in = fopen(path.buf, "r");
if (!in) {
if (errno == ENOENT)
goto out;
die_errno("could not read alternates file '%s'", path.buf);
}

while (strbuf_getline(&line, in) != EOF) {
char *abs_path;
if (!line.len || line.buf[0] == '#')
continue;
if (is_absolute_path(line.buf)) {
odb_add_to_alternates_file(the_repository->objects,
line.buf);
strvec_push(alternates, line.buf);
continue;
}
abs_path = mkpathdup("%s/objects/%s", src_repo, line.buf);
if (!normalize_path_copy(abs_path, abs_path))
odb_add_to_alternates_file(the_repository->objects,
abs_path);
strvec_push(alternates, abs_path);
else
warning("skipping invalid relative alternate: %s/%s",
src_repo, line.buf);
free(abs_path);
}

out:
strbuf_release(&path);
strbuf_release(&line);
fclose(in);
if (in)
fclose(in);
}

static void collect_alternates(struct strvec *alternates,
const char *local_source_repo)
{
if (option_required_reference.nr || option_optional_reference.nr) {
struct collect_alternates_data data = {
.alternates = alternates,
.required = true,
};

for_each_string_list(&option_required_reference,
collect_one_alternate, &data);
data.required = false;
for_each_string_list(&option_optional_reference,
collect_one_alternate, &data);
}

if (local_source_repo) {
struct strbuf commondir = STRBUF_INIT;

get_common_dir(&commondir, local_source_repo);
if (option_shared)
strvec_pushf(alternates, "%s/objects", commondir.buf);
else
read_alternates(alternates, commondir.buf);

strbuf_release(&commondir);
}
}

static void mkdir_if_missing(const char *pathname, mode_t mode)
@ -301,11 +334,9 @@ static void copy_or_link_directory(struct strbuf *src, struct strbuf *dest,
continue;
}

/* Files that cannot be copied bit-for-bit... */
if (!fspathcmp(iter->relative_path, "info/alternates")) {
copy_alternates(src, src_repo);
/* Alternates were already handled earlier. */
if (!fspathcmp(iter->relative_path, "info/alternates"))
continue;
}

if (unlink(dest->buf) && errno != ENOENT)
die_errno(_("failed to unlink '%s'"), dest->buf);
@ -349,13 +380,7 @@ static void copy_or_link_directory(struct strbuf *src, struct strbuf *dest,

static void clone_local(const char *src_repo, const char *dest_repo)
{
if (option_shared) {
struct strbuf alt = STRBUF_INIT;
get_common_dir(&alt, src_repo);
strbuf_addstr(&alt, "/objects");
odb_add_to_alternates_file(the_repository->objects, alt.buf);
strbuf_release(&alt);
} else {
if (!option_shared) {
struct strbuf src = STRBUF_INIT;
struct strbuf dest = STRBUF_INIT;
get_common_dir(&src, src_repo);
@ -999,6 +1024,7 @@ int cmd_clone(int argc,
N_("uri"), N_("a URI for downloading bundles before fetching from origin remote")),
OPT_END()
};
struct strvec alternates = STRVEC_INIT;

const char * const builtin_clone_usage[] = {
N_("git clone [<options>] [--] <repo> [<dir>]"),
@ -1185,10 +1211,9 @@ int cmd_clone(int argc,
* repository, and reference backends may persist that information into
* their on-disk data structures.
*/
init_db(the_repository, git_dir, real_git_dir, work_tree, option_template,
GIT_HASH_UNKNOWN, ref_storage_format, NULL,
do_not_override_repo_unix_permissions,
INIT_DB_QUIET | INIT_DB_SKIP_REFDB);
create_repository(the_repository, git_dir, real_git_dir, work_tree,
option_template, GIT_HASH_UNKNOWN, ref_storage_format,
do_not_override_repo_unix_permissions, NULL);

if (real_git_dir) {
free((char *)git_dir);
@ -1311,9 +1336,6 @@ int cmd_clone(int argc,
strbuf_reset(&key);
}

if (option_required_reference.nr || option_optional_reference.nr)
setup_reference();

remote = remote_get_early(remote_name);

if (!option_rev)
@ -1342,6 +1364,9 @@ int cmd_clone(int argc,
if (option_local > 0 && !is_local)
warning(_("--local is ignored"));

collect_alternates(&alternates, is_local ? path : NULL);
create_object_database(the_repository, &alternates);

transport = transport_get(remote, path ? path : remote->url.v[0]);
transport_set_verbosity(transport, option_verbosity, option_progress);
transport->family = family;
@ -1445,6 +1470,7 @@ int cmd_clone(int argc,
initialize_repository_version(the_repository, hash_algo, the_repository->ref_storage_format, 1);
repo_set_hash_algo(the_repository, hash_algo);
create_reference_database(the_repository, NULL, 1);
startup_info->have_repository = 1;

/*
* Before fetching from the remote, download and install bundle
@ -1637,6 +1663,7 @@ int cmd_clone(int argc,
string_list_clear(&option_not, 0);
string_list_clear(&option_config, 0);
string_list_clear(&server_options, 0);
strvec_clear(&alternates);

free(remote_name);
strbuf_release(&reflog_msg);

View File

@ -80,7 +80,7 @@ int cmd_init_db(int argc,
char *work_tree = NULL;
const char *template_dir = NULL;
char *template_dir_to_free = NULL;
unsigned int flags = 0;
int quiet = 0;
int bare = startup_info->force_bare_repository ? 1 : -1;
const char *object_format = NULL;
const char *ref_format = NULL;
@ -102,7 +102,7 @@ int cmd_init_db(int argc,
.flags = PARSE_OPT_OPTARG | PARSE_OPT_NONEG,
.callback = shared_callback
},
OPT_BIT('q', "quiet", &flags, N_("be quiet"), INIT_DB_QUIET),
OPT_BOOL('q', "quiet", &quiet, N_("be quiet")),
OPT_STRING(0, "separate-git-dir", &real_git_dir, N_("gitdir"),
N_("separate git dir from working tree")),
OPT_STRING('b', "initial-branch", &initial_branch, N_("name"),
@ -113,7 +113,7 @@ int cmd_init_db(int argc,
N_("specify the reference format to use")),
OPT_END()
};
int ret;
int reinit;

argc = parse_options(argc, argv, prefix, init_db_options, init_db_usage, 0);

@ -247,14 +247,30 @@ int cmd_init_db(int argc,
die(_("--separate-git-dir incompatible with bare repository"));
}

flags |= INIT_DB_EXIST_OK;
ret = init_db(the_repository, git_dir, real_git_dir, work_tree,
template_dir, hash_algo, ref_storage_format, initial_branch,
init_shared_repository, flags);
create_repository(the_repository, git_dir, real_git_dir, work_tree,
template_dir, hash_algo, ref_storage_format,
init_shared_repository, &reinit);
create_reference_database(the_repository, initial_branch, quiet);
create_object_database(the_repository, NULL);

if (!quiet) {
int len = strlen(git_dir);

if (reinit)
printf(repo_settings_get_shared_repository(the_repository)
? _("Reinitialized existing shared Git repository in %s%s\n")
: _("Reinitialized existing Git repository in %s%s\n"),
git_dir, len && git_dir[len-1] != '/' ? "/" : "");
else
printf(repo_settings_get_shared_repository(the_repository)
? _("Initialized empty shared Git repository in %s%s\n")
: _("Initialized empty Git repository in %s%s\n"),
git_dir, len && git_dir[len-1] != '/' ? "/" : "");
}

free(template_dir_to_free);
free(real_git_dir_to_free);
free(work_tree);
free(git_dir);
return ret;
return 0;
}

9
odb.c
View File

@ -238,15 +238,6 @@ static struct odb_source *odb_add_alternate_recursively(struct object_database *
return alternate;
}

void odb_add_to_alternates_file(struct object_database *odb,
const char *dir)
{
int ret = odb_source_write_alternate(odb->sources, dir);
if (ret < 0)
die(NULL);
odb_add_alternate_recursively(odb, dir, 0);
}

struct odb_source *odb_set_temporary_primary_source(struct object_database *odb,
const char *dir, int will_destroy,
struct odb_source **prev_source)

7
odb.h
View File

@ -284,13 +284,6 @@ int odb_mkstemp(struct object_database *odb,
*/
int odb_has_alternates(struct object_database *odb);

/*
* Add the directory to the on-disk alternates file; the new entry will also
* take effect in the current process.
*/
void odb_add_to_alternates_file(struct object_database *odb,
const char *dir);

/*
* Read an object from the database. Returns the object data and assigns object
* type and size to the `type` and `size` pointers, if these pointers are

View File

@ -19,6 +19,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,
@ -251,59 +323,6 @@ static int odb_source_files_read_alternates(struct odb_source *source,
return 0;
}

static int odb_source_files_write_alternate(struct odb_source *source,
const char *alternate)
{
struct lock_file lock = LOCK_INIT;
char *path = xstrfmt("%s/%s", source->path, "info/alternates");
FILE *in, *out;
int found = 0;
int ret;

repo_hold_lock_file_for_update(source->odb->repo, &lock, path,
LOCK_DIE_ON_ERROR);
out = fdopen_lock_file(&lock, "w");
if (!out) {
ret = error_errno(_("unable to fdopen alternates lockfile"));
goto out;
}

in = fopen(path, "r");
if (in) {
struct strbuf line = STRBUF_INIT;

while (strbuf_getline(&line, in) != EOF) {
if (!strcmp(alternate, line.buf)) {
found = 1;
break;
}
fprintf_or_die(out, "%s\n", line.buf);
}

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

if (found) {
rollback_lock_file(&lock);
} else {
fprintf_or_die(out, "%s\n", alternate);
if (commit_lock_file(&lock)) {
ret = error_errno(_("unable to move new alternates file into place"));
goto out;
}
}

ret = 0;

out:
free(path);
return ret;
}

static int too_many_loose_objects(struct odb_source_files *files, int limit)
{
unsigned long loose_count;
@ -950,7 +969,6 @@ struct odb_source_files *odb_source_files_new(struct object_database *odb,
files->base.write_object_stream = odb_source_files_write_object_stream;
files->base.begin_transaction = odb_source_files_begin_transaction;
files->base.read_alternates = odb_source_files_read_alternates;
files->base.write_alternate = odb_source_files_write_alternate;
files->base.optimize = odb_source_files_optimize;
files->base.optimize_required = odb_source_files_optimize_required;
files->base.generate_pack = odb_source_files_generate_pack;

View File

@ -328,12 +328,6 @@ static int odb_source_inmemory_read_alternates(struct odb_source *source UNUSED,
return 0;
}

static int odb_source_inmemory_write_alternate(struct odb_source *source UNUSED,
const char *alternate UNUSED)
{
return error("in-memory source does not support alternates");
}

static void odb_source_inmemory_close(struct odb_source *source UNUSED)
{
}
@ -397,7 +391,6 @@ struct odb_source_inmemory *odb_source_inmemory_new(struct object_database *odb)
source->base.freshen_object = odb_source_inmemory_freshen_object;
source->base.begin_transaction = odb_source_inmemory_begin_transaction;
source->base.read_alternates = odb_source_inmemory_read_alternates;
source->base.write_alternate = odb_source_inmemory_write_alternate;

return source;
}

View File

@ -991,12 +991,6 @@ static int odb_source_loose_read_alternates(struct odb_source *source UNUSED,
return 0;
}

static int odb_source_loose_write_alternate(struct odb_source *source UNUSED,
const char *alternate UNUSED)
{
return error("loose source does not support alternates");
}

static void odb_source_loose_clear_cache(struct odb_source_loose *loose)
{
oidtree_clear(loose->cache);
@ -1152,7 +1146,6 @@ struct odb_source_loose *odb_source_loose_new(struct object_database *odb,
loose->base.write_object_stream = odb_source_loose_write_object_stream;
loose->base.begin_transaction = odb_source_loose_begin_transaction;
loose->base.read_alternates = odb_source_loose_read_alternates;
loose->base.write_alternate = odb_source_loose_write_alternate;

if (!is_absolute_path(loose->base.path))
chdir_notify_register(odb_source_loose_reparent, loose);

View File

@ -693,12 +693,6 @@ static int odb_source_packed_read_alternates(struct odb_source *source UNUSED,
return 0;
}

static int odb_source_packed_write_alternate(struct odb_source *source UNUSED,
const char *alternate UNUSED)
{
return error("packed backend cannot write alternates");
}

void (*report_garbage)(unsigned seen_bits, const char *path);

static void report_helper(const struct string_list *list,
@ -1026,7 +1020,6 @@ struct odb_source_packed *odb_source_packed_new(struct object_database *odb,
packed->base.write_object_stream = odb_source_packed_write_object_stream;
packed->base.begin_transaction = odb_source_packed_begin_transaction;
packed->base.read_alternates = odb_source_packed_read_alternates;
packed->base.write_alternate = odb_source_packed_write_alternate;

if (!is_absolute_path(path))
chdir_notify_register(odb_source_packed_reparent, packed);

View File

@ -37,6 +37,15 @@ struct odb_stream;
struct strbuf;
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
@ -107,7 +116,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
@ -281,19 +291,6 @@ struct odb_source {
int (*read_alternates)(struct odb_source *source,
struct strvec *out);

/*
* This callback is expected to persist the singular alternate passed
* to it into its list of alternates. Any pre-existing alternates are
* expected to remain active. Subsequent calls to `read_alternates` are
* thus expected to yield the pre-existing list of alternates plus the
* newly added alternate appended to its end.
*
* The callback is expected to return 0 on success, a negative error
* code otherwise.
*/
int (*write_alternate)(struct odb_source *source,
const char *alternate);

/*
* This callback is expected to optimize the object database source.
* Returns 0 on success, a negative error code otherwise.
@ -389,11 +386,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);
}

/*
@ -545,19 +543,6 @@ static inline int odb_source_read_alternates(struct odb_source *source,
return source->read_alternates(source, out);
}

/*
* Write and persist a new alternate object database source for the given
* source. Any preexisting alternates are expected to stay valid, and the new
* alternate shall be appended to the end of the list.
*
* Returns 0 on success, a negative error code otherwise.
*/
static inline int odb_source_write_alternate(struct odb_source *source,
const char *alternate)
{
return source->write_alternate(source, alternate);
}

/*
* Create a new transaction that can be used to write objects into a temporary
* staging area. The objects will only be persisted when the transaction is

61
setup.c
View File

@ -2663,8 +2663,13 @@ static int create_default_files(struct repository *repo,
return reinit;
}

static void create_object_database(struct repository *repo)
void create_object_database(struct repository *repo,
const struct strvec *alternates)
{
struct odb_create_on_disk_options opts = {
.alternates = alternates,
};

/*
* Create the "objects" directory in the common directory. This is done
* so that the repository can be discovered regardless of the backend
@ -2684,7 +2689,7 @@ static 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"));
}

@ -2838,17 +2843,17 @@ static void repository_format_configure(struct repository_format *repo_fmt,
}
}

int init_db(struct repository *repo,
const char *git_dir,
const char *real_git_dir,
const char *worktree,
const char *template_dir, int hash,
enum ref_storage_format ref_storage_format,
const char *initial_branch,
int init_shared_repository, unsigned int flags)
void create_repository(struct repository *repo,
const char *git_dir,
const char *real_git_dir,
const char *worktree,
const char *template_dir,
int hash,
enum ref_storage_format ref_storage_format,
int init_shared_repository,
int *reinit_ok)
{
int reinit;
int exist_ok = flags & INIT_DB_EXIST_OK;
int reinit_ignored;
char *original_git_dir = real_pathdup(git_dir, 1);
struct repository_format repo_fmt = REPOSITORY_FORMAT_INIT;
struct strbuf err = STRBUF_INIT;
@ -2856,10 +2861,10 @@ int init_db(struct repository *repo,
if (real_git_dir) {
struct stat st;

if (!exist_ok && !stat(git_dir, &st))
if (!reinit_ok && !stat(git_dir, &st))
die(_("%s already exists"), git_dir);

if (!exist_ok && !stat(real_git_dir, &st))
if (!reinit_ok && !stat(real_git_dir, &st))
die(_("%s already exists"), real_git_dir);

apply_and_export_relative_gitdir(repo, real_git_dir, 1);
@ -2893,8 +2898,10 @@ int init_db(struct repository *repo,

safe_create_dir(repo, git_dir, 0);

reinit = create_default_files(repo, template_dir, original_git_dir,
&repo_fmt, init_shared_repository);
if (!reinit_ok)
reinit_ok = &reinit_ignored;
*reinit_ok = create_default_files(repo, template_dir, original_git_dir,
&repo_fmt, init_shared_repository);

if (repo_settings_get_shared_repository(repo)) {
char buf[10];
@ -2917,29 +2924,7 @@ int init_db(struct repository *repo,
repo_config_set(repo, "receive.denyNonFastforwards", "true");
}

if (!(flags & INIT_DB_SKIP_REFDB))
create_reference_database(repo, initial_branch, flags & INIT_DB_QUIET);
create_object_database(repo);

startup_info->have_repository = 1;

if (!(flags & INIT_DB_QUIET)) {
int len = strlen(git_dir);

if (reinit)
printf(repo_settings_get_shared_repository(repo)
? _("Reinitialized existing shared Git repository in %s%s\n")
: _("Reinitialized existing Git repository in %s%s\n"),
git_dir, len && git_dir[len-1] != '/' ? "/" : "");
else
printf(repo_settings_get_shared_repository(repo)
? _("Initialized empty shared Git repository in %s%s\n")
: _("Initialized empty Git repository in %s%s\n"),
git_dir, len && git_dir[len-1] != '/' ? "/" : "");
}

clear_repository_format(&repo_fmt);
strbuf_release(&err);
free(original_git_dir);
return 0;
}

45
setup.h
View File

@ -257,24 +257,47 @@ int apply_repository_format(struct repository *repo,

const char *get_template_dir(const char *option_template);

#define INIT_DB_QUIET (1 << 0)
#define INIT_DB_EXIST_OK (1 << 1)
#define INIT_DB_SKIP_REFDB (1 << 2)
/*
* Create the repository by creating the necessary directory structures,
* setting up the configuration and configuring the repository's format. If
* `template_dir` is set, copy over templates from that directory. Furthermore,
* if and only if `reinit_ok` is a non-NULL pointer, then the function may
* reinitialize a preexisting repository. In that case, the pointer will be set
* to `1` in case the repo was reinitialized and `0` if it didn't exist yet.
*
* Note that this function does not create the reference and object databases.
*/
void create_repository(struct repository *repo,
const char *git_dir,
const char *real_git_dir,
const char *worktree,
const char *template_dir,
int hash_algo,
enum ref_storage_format ref_storage_format,
int init_shared_repository,
int *reinit_ok);

int init_db(struct repository *repo,
const char *git_dir,
const char *real_git_dir,
const char *worktree,
const char *template_dir, int hash_algo,
enum ref_storage_format ref_storage_format,
const char *initial_branch, int init_shared_repository,
unsigned int flags);
void initialize_repository_version(struct repository *repo,
int hash_algo,
enum ref_storage_format ref_storage_format,
int reinit);

/*
* Create the reference database for the repository. The repository and its ref
* storage format must have already been configured properly before calling
* this function. When set, `initial_branch` overrides the default branch that
* HEAD will point to.
*/
void create_reference_database(struct repository *repo, const char *initial_branch, int quiet);

/*
* Create the object database for the repository. The repository must have
* already been configured properly before calling this function. When set,
* `alternates` is the list of alternates that should be written into the
* object database.
*/
void create_object_database(struct repository *repo, const struct strvec *alternates);

/*
* NOTE NOTE NOTE!!
*

View File

@ -383,4 +383,29 @@ test_expect_success 'dissociate from repo with commit graph' '
git clone --no-local --reference graph.git --dissociate orig clone
'

test_expect_success 'local clone from linked worktree carries over alternates' '
rm -fr base derived derived-wt dst expect &&
git init base &&
test_commit -C base one &&
git clone --shared base derived &&
git -C derived worktree add ../derived-wt &&
git clone derived-wt dst &&
echo "$(pwd)/base/.git/objects" >expect &&
test_cmp expect dst/.git/objects/info/alternates &&
git -C dst fsck
'

test_expect_success 'local clone from linked worktree resolves relative alternates' '
rm -fr base derived derived-wt dst expect &&
git init base &&
test_commit -C base one &&
git clone --shared base derived &&
echo "../../../base/.git/objects" >derived/.git/objects/info/alternates &&
git -C derived worktree add ../derived-wt &&
git clone derived-wt dst &&
echo "$(pwd)/base/.git/objects" >expect &&
test_cmp expect dst/.git/objects/info/alternates &&
git -C dst fsck
'

test_done