From 002a7cfc731b9a4b77b580e82cba9aeea5b692cf Mon Sep 17 00:00:00 2001 From: Patrick Steinhardt Date: Thu, 10 Sep 2026 17:09:16 +0200 Subject: [PATCH] builtin/clone: move setup of alternates for non-shared local clones Similar as in the preceding commit, move the setup of alternates for local clones with "--no-shared" into `collect_alternates()`. With this step, the complete setup of alternates is now handled by that function. Note that besides moving stuff around, it also fixes a bug: previously, we did not know to resolve the referenced repository's common directory. Consequently, when referencing a worktree we failed to resolve alternates. But as `collect_alternates()` already knows to resolve the commondir for "--local" we can simply reuse this resolved path for our purpose. Add two tests, the first one of which exercises this bug to avoid future regressions. The second test ensures that we properly handle relative alternates for a referenced worktree. Signed-off-by: Patrick Steinhardt Signed-off-by: Junio C Hamano --- builtin/clone.c | 34 +++++++++++++++++++++++----------- t/t5604-clone-reference.sh | 25 +++++++++++++++++++++++++ 2 files changed, 48 insertions(+), 11 deletions(-) diff --git a/builtin/clone.c b/builtin/clone.c index d397fd36b2..17353a8e1f 100644 --- a/builtin/clone.c +++ b/builtin/clone.c @@ -181,7 +181,7 @@ static int collect_one_alternate(struct string_list_item *item, void *cb_data) return 0; } -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 @@ -195,29 +195,41 @@ 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, @@ -242,6 +254,8 @@ static void collect_alternates(struct strvec *alternates, 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); } @@ -320,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); diff --git a/t/t5604-clone-reference.sh b/t/t5604-clone-reference.sh index 39a0c318df..9e4b98fdb8 100755 --- a/t/t5604-clone-reference.sh +++ b/t/t5604-clone-reference.sh @@ -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