aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJunio C Hamano <gitster@pobox.com>2025-12-22 14:57:48 +0900
committerJunio C Hamano <gitster@pobox.com>2025-12-22 14:57:48 +0900
commit5a8046ab33d383b758e7e283405697957e4691c0 (patch)
treefeb806a82b6c53d930598faba644c366e2d9b40a
parentc4a0c8845e2426375ad257b6c221a3a7d92ecfda (diff)
parent221a877d4785030e07d20977418609257fd606d8 (diff)
downloadgit-5a8046ab33d383b758e7e283405697957e4691c0.tar.xz
Merge branch 'ps/odb-alternates-object-sources'
Code refactoring around alternate object store. * ps/odb-alternates-object-sources: odb: write alternates via sources odb: read alternates via sources odb: drop forward declaration of `read_info_alternates()` odb: remove mutual recursion when parsing alternates odb: stop splitting alternate in `odb_add_to_alternates_file()` odb: move computation of normalized objdir into `alt_odb_usable()` odb: resolve relative alternative paths when parsing odb: refactor parsing of alternates to be self-contained
-rw-r--r--odb.c307
1 files changed, 158 insertions, 149 deletions
diff --git a/odb.c b/odb.c
index af13174425..7b5da2de32 100644
--- a/odb.c
+++ b/odb.c
@@ -89,17 +89,20 @@ int odb_mkstemp(struct object_database *odb,
/*
* Return non-zero iff the path is usable as an alternate object database.
*/
-static int alt_odb_usable(struct object_database *o, const char *path,
- const char *normalized_objdir)
+static bool odb_is_source_usable(struct object_database *o, const char *path)
{
int r;
+ struct strbuf normalized_objdir = STRBUF_INIT;
+ bool usable = false;
+
+ strbuf_realpath(&normalized_objdir, o->sources->path, 1);
/* Detect cases where alternate disappeared */
if (!is_directory(path)) {
error(_("object directory %s does not exist; "
"check .git/objects/info/alternates"),
path);
- return 0;
+ goto out;
}
/*
@@ -116,33 +119,104 @@ static int alt_odb_usable(struct object_database *o, const char *path,
kh_value(o->source_by_path, p) = o->sources;
}
- if (fspatheq(path, normalized_objdir))
- return 0;
+ if (fspatheq(path, normalized_objdir.buf))
+ goto out;
if (kh_get_odb_path_map(o->source_by_path, path) < kh_end(o->source_by_path))
- return 0;
+ goto out;
+
+ usable = true;
- return 1;
+out:
+ strbuf_release(&normalized_objdir);
+ return usable;
+}
+
+static void parse_alternates(const char *string,
+ int sep,
+ const char *relative_base,
+ struct strvec *out)
+{
+ struct strbuf pathbuf = STRBUF_INIT;
+ struct strbuf buf = STRBUF_INIT;
+
+ if (!string || !*string)
+ return;
+
+ while (*string) {
+ const char *end;
+
+ strbuf_reset(&buf);
+ strbuf_reset(&pathbuf);
+
+ if (*string == '#') {
+ /* comment; consume up to next separator */
+ end = strchrnul(string, sep);
+ } else if (*string == '"' && !unquote_c_style(&buf, string, &end)) {
+ /*
+ * quoted path; unquote_c_style has copied the
+ * data for us and set "end". Broken quoting (e.g.,
+ * an entry that doesn't end with a quote) falls
+ * back to the unquoted case below.
+ */
+ } else {
+ /* normal, unquoted path */
+ end = strchrnul(string, sep);
+ strbuf_add(&buf, string, end - string);
+ }
+
+ if (*end)
+ end++;
+ string = end;
+
+ if (!buf.len)
+ continue;
+
+ if (!is_absolute_path(buf.buf) && relative_base) {
+ strbuf_realpath(&pathbuf, relative_base, 1);
+ strbuf_addch(&pathbuf, '/');
+ }
+ strbuf_addbuf(&pathbuf, &buf);
+
+ strbuf_reset(&buf);
+ if (!strbuf_realpath(&buf, pathbuf.buf, 0)) {
+ error(_("unable to normalize alternate object path: %s"),
+ pathbuf.buf);
+ continue;
+ }
+
+ /*
+ * The trailing slash after the directory name is given by
+ * this function at the end. Remove duplicates.
+ */
+ while (buf.len && buf.buf[buf.len - 1] == '/')
+ strbuf_setlen(&buf, buf.len - 1);
+
+ strvec_push(out, buf.buf);
+ }
+
+ strbuf_release(&pathbuf);
+ strbuf_release(&buf);
+}
+
+static void odb_source_read_alternates(struct odb_source *source,
+ struct strvec *out)
+{
+ struct strbuf buf = STRBUF_INIT;
+ char *path;
+
+ path = xstrfmt("%s/info/alternates", source->path);
+ if (strbuf_read_file(&buf, path, 1024) < 0) {
+ warn_on_fopen_errors(path);
+ free(path);
+ return;
+ }
+ parse_alternates(buf.buf, '\n', source->path, out);
+
+ strbuf_release(&buf);
+ free(path);
}
-/*
- * Prepare alternate object database registry.
- *
- * The variable alt_odb_list points at the list of struct
- * odb_source. The elements on this list come from
- * non-empty elements from colon separated ALTERNATE_DB_ENVIRONMENT
- * environment variable, and $GIT_OBJECT_DIRECTORY/info/alternates,
- * whose contents is similar to that environment variable but can be
- * LF separated. Its base points at a statically allocated buffer that
- * contains "/the/directory/corresponding/to/.git/objects/...", while
- * its name points just after the slash at the end of ".git/objects/"
- * in the example above, and has enough space to hold all hex characters
- * of the object ID, an extra slash for the first level indirection, and
- * the terminating NUL.
- */
-static void read_info_alternates(struct object_database *odb,
- const char *relative_base,
- int depth);
static struct odb_source *odb_source_new(struct object_database *odb,
const char *path,
@@ -159,44 +233,19 @@ static struct odb_source *odb_source_new(struct object_database *odb,
return source;
}
-static struct odb_source *link_alt_odb_entry(struct object_database *odb,
- const char *dir,
- const char *relative_base,
- int depth)
+static struct odb_source *odb_add_alternate_recursively(struct object_database *odb,
+ const char *source,
+ int depth)
{
struct odb_source *alternate = NULL;
- struct strbuf pathbuf = STRBUF_INIT;
- struct strbuf tmp = STRBUF_INIT;
+ struct strvec sources = STRVEC_INIT;
khiter_t pos;
int ret;
- if (!is_absolute_path(dir) && relative_base) {
- strbuf_realpath(&pathbuf, relative_base, 1);
- strbuf_addch(&pathbuf, '/');
- }
- strbuf_addstr(&pathbuf, dir);
-
- if (!strbuf_realpath(&tmp, pathbuf.buf, 0)) {
- error(_("unable to normalize alternate object path: %s"),
- pathbuf.buf);
- goto error;
- }
- strbuf_swap(&pathbuf, &tmp);
-
- /*
- * The trailing slash after the directory name is given by
- * this function at the end. Remove duplicates.
- */
- while (pathbuf.len && pathbuf.buf[pathbuf.len - 1] == '/')
- strbuf_setlen(&pathbuf, pathbuf.len - 1);
-
- strbuf_reset(&tmp);
- strbuf_realpath(&tmp, odb->sources->path, 1);
-
- if (!alt_odb_usable(odb, pathbuf.buf, tmp.buf))
+ if (!odb_is_source_usable(odb, source))
goto error;
- alternate = odb_source_new(odb, pathbuf.buf, false);
+ alternate = odb_source_new(odb, source, false);
/* add the alternate entry */
*odb->sources_tail = alternate;
@@ -208,104 +257,42 @@ static struct odb_source *link_alt_odb_entry(struct object_database *odb,
kh_value(odb->source_by_path, pos) = alternate;
/* recursively add alternates */
- read_info_alternates(odb, alternate->path, depth + 1);
-
- error:
- strbuf_release(&tmp);
- strbuf_release(&pathbuf);
- return alternate;
-}
-
-static const char *parse_alt_odb_entry(const char *string,
- int sep,
- struct strbuf *out)
-{
- const char *end;
-
- strbuf_reset(out);
-
- if (*string == '#') {
- /* comment; consume up to next separator */
- end = strchrnul(string, sep);
- } else if (*string == '"' && !unquote_c_style(out, string, &end)) {
- /*
- * quoted path; unquote_c_style has copied the
- * data for us and set "end". Broken quoting (e.g.,
- * an entry that doesn't end with a quote) falls
- * back to the unquoted case below.
- */
- } else {
- /* normal, unquoted path */
- end = strchrnul(string, sep);
- strbuf_add(out, string, end - string);
- }
-
- if (*end)
- end++;
- return end;
-}
-
-static void link_alt_odb_entries(struct object_database *odb, const char *alt,
- int sep, const char *relative_base, int depth)
-{
- struct strbuf dir = STRBUF_INIT;
-
- if (!alt || !*alt)
- return;
-
- if (depth > 5) {
+ odb_source_read_alternates(alternate, &sources);
+ if (sources.nr && depth + 1 > 5) {
error(_("%s: ignoring alternate object stores, nesting too deep"),
- relative_base);
- return;
- }
-
- while (*alt) {
- alt = parse_alt_odb_entry(alt, sep, &dir);
- if (!dir.len)
- continue;
- link_alt_odb_entry(odb, dir.buf, relative_base, depth);
- }
- strbuf_release(&dir);
-}
-
-static void read_info_alternates(struct object_database *odb,
- const char *relative_base,
- int depth)
-{
- char *path;
- struct strbuf buf = STRBUF_INIT;
-
- path = xstrfmt("%s/info/alternates", relative_base);
- if (strbuf_read_file(&buf, path, 1024) < 0) {
- warn_on_fopen_errors(path);
- free(path);
- return;
+ source);
+ } else {
+ for (size_t i = 0; i < sources.nr; i++)
+ odb_add_alternate_recursively(odb, sources.v[i], depth + 1);
}
- link_alt_odb_entries(odb, buf.buf, '\n', relative_base, depth);
- strbuf_release(&buf);
- free(path);
+ error:
+ strvec_clear(&sources);
+ return alternate;
}
-void odb_add_to_alternates_file(struct object_database *odb,
- const char *dir)
+static int odb_source_write_alternate(struct odb_source *source,
+ const char *alternate)
{
struct lock_file lock = LOCK_INIT;
- char *alts = repo_git_path(odb->repo, "objects/info/alternates");
+ char *path = xstrfmt("%s/%s", source->path, "info/alternates");
FILE *in, *out;
int found = 0;
+ int ret;
- hold_lock_file_for_update(&lock, alts, LOCK_DIE_ON_ERROR);
+ hold_lock_file_for_update(&lock, path, LOCK_DIE_ON_ERROR);
out = fdopen_lock_file(&lock, "w");
- if (!out)
- die_errno(_("unable to fdopen alternates lockfile"));
+ if (!out) {
+ ret = error_errno(_("unable to fdopen alternates lockfile"));
+ goto out;
+ }
- in = fopen(alts, "r");
+ in = fopen(path, "r");
if (in) {
struct strbuf line = STRBUF_INIT;
while (strbuf_getline(&line, in) != EOF) {
- if (!strcmp(dir, line.buf)) {
+ if (!strcmp(alternate, line.buf)) {
found = 1;
break;
}
@@ -314,20 +301,36 @@ void odb_add_to_alternates_file(struct object_database *odb,
strbuf_release(&line);
fclose(in);
+ } else if (errno != ENOENT) {
+ ret = error_errno(_("unable to read alternates file"));
+ goto out;
}
- else if (errno != ENOENT)
- die_errno(_("unable to read alternates file"));
if (found) {
rollback_lock_file(&lock);
} else {
- fprintf_or_die(out, "%s\n", dir);
- if (commit_lock_file(&lock))
- die_errno(_("unable to move new alternates file into place"));
- if (odb->loaded_alternates)
- link_alt_odb_entries(odb, dir, '\n', NULL, 0);
+ 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;
+ }
}
- free(alts);
+
+ ret = 0;
+
+out:
+ free(path);
+ return ret;
+}
+
+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);
+ if (odb->loaded_alternates)
+ odb_add_alternate_recursively(odb, dir, 0);
}
struct odb_source *odb_add_to_alternates_memory(struct object_database *odb,
@@ -338,7 +341,7 @@ struct odb_source *odb_add_to_alternates_memory(struct object_database *odb,
* overwritten when they are.
*/
odb_prepare_alternates(odb);
- return link_alt_odb_entry(odb, dir, NULL, 0);
+ return odb_add_alternate_recursively(odb, dir, 0);
}
struct odb_source *odb_set_temporary_primary_source(struct object_database *odb,
@@ -609,13 +612,19 @@ int odb_for_each_alternate(struct object_database *odb,
void odb_prepare_alternates(struct object_database *odb)
{
+ struct strvec sources = STRVEC_INIT;
+
if (odb->loaded_alternates)
return;
- link_alt_odb_entries(odb, odb->alternate_db, PATH_SEP, NULL, 0);
+ parse_alternates(odb->alternate_db, PATH_SEP, NULL, &sources);
+ odb_source_read_alternates(odb->sources, &sources);
+ for (size_t i = 0; i < sources.nr; i++)
+ odb_add_alternate_recursively(odb, sources.v[i], 0);
- read_info_alternates(odb, odb->sources->path, 0);
odb->loaded_alternates = 1;
+
+ strvec_clear(&sources);
}
int odb_has_alternates(struct object_database *odb)