diff options
-rw-r--r-- | Documentation/git-count-objects.txt | 5 | ||||
-rw-r--r-- | builtin/count-objects.c | 12 | ||||
-rw-r--r-- | builtin/fsck.c | 10 | ||||
-rw-r--r-- | builtin/submodule--helper.c | 11 | ||||
-rw-r--r-- | cache.h | 36 | ||||
-rw-r--r-- | sha1_file.c | 179 | ||||
-rw-r--r-- | sha1_name.c | 17 | ||||
-rw-r--r-- | strbuf.c | 20 | ||||
-rw-r--r-- | strbuf.h | 8 | ||||
-rw-r--r-- | submodule.c | 23 | ||||
-rwxr-xr-x | t/t5613-info-alternate.sh | 205 | ||||
-rw-r--r-- | transport.c | 4 |
12 files changed, 308 insertions, 222 deletions
diff --git a/Documentation/git-count-objects.txt b/Documentation/git-count-objects.txt index 2ff35683e5..cb9b4d2e46 100644 --- a/Documentation/git-count-objects.txt +++ b/Documentation/git-count-objects.txt @@ -38,6 +38,11 @@ objects nor valid packs + size-garbage: disk space consumed by garbage files, in KiB (unless -H is specified) ++ +alternate: absolute path of alternate object databases; may appear +multiple times, one line per path. Note that if the path contains +non-printable characters, it may be surrounded by double-quotes and +contain C-style backslashed escape sequences. -H:: --human-readable:: diff --git a/builtin/count-objects.c b/builtin/count-objects.c index ba9291944f..a04b4f2ef3 100644 --- a/builtin/count-objects.c +++ b/builtin/count-objects.c @@ -8,6 +8,7 @@ #include "dir.h" #include "builtin.h" #include "parse-options.h" +#include "quote.h" static unsigned long garbage; static off_t size_garbage; @@ -73,6 +74,14 @@ static int count_cruft(const char *basename, const char *path, void *data) return 0; } +static int print_alternate(struct alternate_object_database *alt, void *data) +{ + printf("alternate: "); + quote_c_style(alt->path, NULL, stdout, 0); + putchar('\n'); + return 0; +} + static char const * const count_objects_usage[] = { N_("git count-objects [-v] [-H | --human-readable]"), NULL @@ -88,6 +97,8 @@ int cmd_count_objects(int argc, const char **argv, const char *prefix) OPT_END(), }; + git_config(git_default_config, NULL); + argc = parse_options(argc, argv, prefix, opts, count_objects_usage, 0); /* we do not take arguments other than flags for now */ if (argc) @@ -140,6 +151,7 @@ int cmd_count_objects(int argc, const char **argv, const char *prefix) printf("prune-packable: %lu\n", packed_loose); printf("garbage: %lu\n", garbage); printf("size-garbage: %s\n", garbage_buf.buf); + foreach_alt_odb(print_alternate, NULL); strbuf_release(&loose_buf); strbuf_release(&pack_buf); strbuf_release(&garbage_buf); diff --git a/builtin/fsck.c b/builtin/fsck.c index 055dfdcf9e..f01b81eebf 100644 --- a/builtin/fsck.c +++ b/builtin/fsck.c @@ -644,14 +644,8 @@ int cmd_fsck(int argc, const char **argv, const char *prefix) fsck_object_dir(get_object_directory()); prepare_alt_odb(); - for (alt = alt_odb_list; alt; alt = alt->next) { - /* directory name, minus trailing slash */ - size_t namelen = alt->name - alt->base - 1; - struct strbuf name = STRBUF_INIT; - strbuf_add(&name, alt->base, namelen); - fsck_object_dir(name.buf); - strbuf_release(&name); - } + for (alt = alt_odb_list; alt; alt = alt->next) + fsck_object_dir(alt->path); } if (check_full) { diff --git a/builtin/submodule--helper.c b/builtin/submodule--helper.c index 444ec06c2a..6182eb3197 100644 --- a/builtin/submodule--helper.c +++ b/builtin/submodule--helper.c @@ -492,20 +492,16 @@ static int add_possible_reference_from_superproject( { struct submodule_alternate_setup *sas = sas_cb; - /* directory name, minus trailing slash */ - size_t namelen = alt->name - alt->base - 1; - struct strbuf name = STRBUF_INIT; - strbuf_add(&name, alt->base, namelen); - /* * If the alternate object store is another repository, try the * standard layout with .git/modules/<name>/objects */ - if (ends_with(name.buf, ".git/objects")) { + if (ends_with(alt->path, ".git/objects")) { char *sm_alternate; struct strbuf sb = STRBUF_INIT; struct strbuf err = STRBUF_INIT; - strbuf_add(&sb, name.buf, name.len - strlen("objects")); + strbuf_add(&sb, alt->path, strlen(alt->path) - strlen("objects")); + /* * We need to end the new path with '/' to mark it as a dir, * otherwise a submodule name containing '/' will be broken @@ -533,7 +529,6 @@ static int add_possible_reference_from_superproject( strbuf_release(&sb); } - strbuf_release(&name); return 0; } @@ -1390,16 +1390,46 @@ extern void remove_scheduled_dirs(void); extern struct alternate_object_database { struct alternate_object_database *next; - char *name; - char base[FLEX_ARRAY]; /* more */ + + /* see alt_scratch_buf() */ + struct strbuf scratch; + size_t base_len; + + char path[FLEX_ARRAY]; } *alt_odb_list; extern void prepare_alt_odb(void); extern void read_info_alternates(const char * relative_base, int depth); extern char *compute_alternate_path(const char *path, struct strbuf *err); -extern void add_to_alternates_file(const char *reference); typedef int alt_odb_fn(struct alternate_object_database *, void *); extern int foreach_alt_odb(alt_odb_fn, void*); +/* + * Allocate a "struct alternate_object_database" but do _not_ actually + * add it to the list of alternates. + */ +struct alternate_object_database *alloc_alt_odb(const char *dir); + +/* + * Add the directory to the on-disk alternates file; the new entry will also + * take effect in the current process. + */ +extern void add_to_alternates_file(const char *dir); + +/* + * Add the directory to the in-memory list of alternates (along with any + * recursive alternates it points to), but do not modify the on-disk alternates + * file. + */ +extern void add_to_alternates_memory(const char *dir); + +/* + * Returns a scratch strbuf pre-filled with the alternate object directory, + * including a trailing slash, which can be used to access paths in the + * alternate. Always use this over direct access to alt->scratch, as it + * cleans up any previous use of the scratch buffer. + */ +extern struct strbuf *alt_scratch_buf(struct alternate_object_database *alt); + struct pack_window { struct pack_window *next; unsigned char *base; diff --git a/sha1_file.c b/sha1_file.c index 309e87d987..266152de36 100644 --- a/sha1_file.c +++ b/sha1_file.c @@ -172,36 +172,42 @@ enum scld_error safe_create_leading_directories_const(const char *path) return result; } -static void fill_sha1_path(char *pathbuf, const unsigned char *sha1) +static void fill_sha1_path(struct strbuf *buf, const unsigned char *sha1) { int i; for (i = 0; i < 20; i++) { static char hex[] = "0123456789abcdef"; unsigned int val = sha1[i]; - char *pos = pathbuf + i*2 + (i > 0); - *pos++ = hex[val >> 4]; - *pos = hex[val & 0xf]; + strbuf_addch(buf, hex[val >> 4]); + strbuf_addch(buf, hex[val & 0xf]); + if (!i) + strbuf_addch(buf, '/'); } } const char *sha1_file_name(const unsigned char *sha1) { - static char buf[PATH_MAX]; - const char *objdir; - int len; + static struct strbuf buf = STRBUF_INIT; - objdir = get_object_directory(); - len = strlen(objdir); + strbuf_reset(&buf); + strbuf_addf(&buf, "%s/", get_object_directory()); - /* '/' + sha1(2) + '/' + sha1(38) + '\0' */ - if (len + 43 > PATH_MAX) - die("insanely long object directory %s", objdir); - memcpy(buf, objdir, len); - buf[len] = '/'; - buf[len+3] = '/'; - buf[len+42] = '\0'; - fill_sha1_path(buf + len + 1, sha1); - return buf; + fill_sha1_path(&buf, sha1); + return buf.buf; +} + +struct strbuf *alt_scratch_buf(struct alternate_object_database *alt) +{ + strbuf_setlen(&alt->scratch, alt->base_len); + return &alt->scratch; +} + +static const char *alt_sha1_path(struct alternate_object_database *alt, + const unsigned char *sha1) +{ + struct strbuf *buf = alt_scratch_buf(alt); + fill_sha1_path(buf, sha1); + return buf->buf; } /* @@ -235,6 +241,35 @@ struct alternate_object_database *alt_odb_list; static struct alternate_object_database **alt_odb_tail; /* + * Return non-zero iff the path is usable as an alternate object database. + */ +static int alt_odb_usable(struct strbuf *path, const char *normalized_objdir) +{ + struct alternate_object_database *alt; + + /* Detect cases where alternate disappeared */ + if (!is_directory(path->buf)) { + error("object directory %s does not exist; " + "check .git/objects/info/alternates.", + path->buf); + return 0; + } + + /* + * Prevent the common mistake of listing the same + * thing twice, or object directory itself. + */ + for (alt = alt_odb_list; alt; alt = alt->next) { + if (!fspathcmp(path->buf, alt->path)) + return 0; + } + if (!fspathcmp(path->buf, normalized_objdir)) + return 0; + + return 1; +} + +/* * Prepare alternate object database registry. * * The variable alt_odb_list points at the list of struct @@ -253,8 +288,6 @@ static int link_alt_odb_entry(const char *entry, const char *relative_base, int depth, const char *normalized_objdir) { struct alternate_object_database *ent; - struct alternate_object_database *alt; - size_t pfxlen, entlen; struct strbuf pathbuf = STRBUF_INIT; if (!is_absolute_path(entry) && relative_base) { @@ -263,49 +296,26 @@ static int link_alt_odb_entry(const char *entry, const char *relative_base, } strbuf_addstr(&pathbuf, entry); - normalize_path_copy(pathbuf.buf, pathbuf.buf); - - pfxlen = strlen(pathbuf.buf); + if (strbuf_normalize_path(&pathbuf) < 0) { + error("unable to normalize alternate object path: %s", + pathbuf.buf); + strbuf_release(&pathbuf); + return -1; + } /* * The trailing slash after the directory name is given by * this function at the end. Remove duplicates. */ - while (pfxlen && pathbuf.buf[pfxlen-1] == '/') - pfxlen -= 1; + while (pathbuf.len && pathbuf.buf[pathbuf.len - 1] == '/') + strbuf_setlen(&pathbuf, pathbuf.len - 1); - entlen = st_add(pfxlen, 43); /* '/' + 2 hex + '/' + 38 hex + NUL */ - ent = xmalloc(st_add(sizeof(*ent), entlen)); - memcpy(ent->base, pathbuf.buf, pfxlen); - strbuf_release(&pathbuf); - - ent->name = ent->base + pfxlen + 1; - ent->base[pfxlen + 3] = '/'; - ent->base[pfxlen] = ent->base[entlen-1] = 0; - - /* Detect cases where alternate disappeared */ - if (!is_directory(ent->base)) { - error("object directory %s does not exist; " - "check .git/objects/info/alternates.", - ent->base); - free(ent); + if (!alt_odb_usable(&pathbuf, normalized_objdir)) { + strbuf_release(&pathbuf); return -1; } - /* Prevent the common mistake of listing the same - * thing twice, or object directory itself. - */ - for (alt = alt_odb_list; alt; alt = alt->next) { - if (pfxlen == alt->name - alt->base - 1 && - !memcmp(ent->base, alt->base, pfxlen)) { - free(ent); - return -1; - } - } - if (!fspathcmp(ent->base, normalized_objdir)) { - free(ent); - return -1; - } + ent = alloc_alt_odb(pathbuf.buf); /* add the alternate entry */ *alt_odb_tail = ent; @@ -313,10 +323,9 @@ static int link_alt_odb_entry(const char *entry, const char *relative_base, ent->next = NULL; /* recursively add alternates */ - read_info_alternates(ent->base, depth + 1); - - ent->base[pfxlen] = '/'; + read_info_alternates(pathbuf.buf, depth + 1); + strbuf_release(&pathbuf); return 0; } @@ -335,7 +344,9 @@ static void link_alt_odb_entries(const char *alt, int len, int sep, } strbuf_add_absolute_path(&objdirbuf, get_object_directory()); - normalize_path_copy(objdirbuf.buf, objdirbuf.buf); + if (strbuf_normalize_path(&objdirbuf) < 0) + die("unable to normalize object directory: %s", + objdirbuf.buf); alt_copy = xmemdupz(alt, len); string_list_split_in_place(&entries, alt_copy, sep, -1); @@ -343,12 +354,7 @@ static void link_alt_odb_entries(const char *alt, int len, int sep, const char *entry = entries.items[i].string; if (entry[0] == '\0' || entry[0] == '#') continue; - if (!is_absolute_path(entry) && depth) { - error("%s: ignoring relative alternate object store %s", - relative_base, entry); - } else { - link_alt_odb_entry(entry, relative_base, depth, objdirbuf.buf); - } + link_alt_odb_entry(entry, relative_base, depth, objdirbuf.buf); } string_list_clear(&entries, 0); free(alt_copy); @@ -381,6 +387,18 @@ void read_info_alternates(const char * relative_base, int depth) munmap(map, mapsz); } +struct alternate_object_database *alloc_alt_odb(const char *dir) +{ + struct alternate_object_database *ent; + + FLEX_ALLOC_STR(ent, path, dir); + strbuf_init(&ent->scratch, 0); + strbuf_addf(&ent->scratch, "%s/", dir); + ent->base_len = ent->scratch.len; + + return ent; +} + void add_to_alternates_file(const char *reference) { struct lock_file *lock = xcalloc(1, sizeof(struct lock_file)); @@ -426,6 +444,17 @@ void add_to_alternates_file(const char *reference) free(alts); } +void add_to_alternates_memory(const char *reference) +{ + /* + * Make sure alternates are initialized, or else our entry may be + * overwritten when they are. + */ + prepare_alt_odb(); + + link_alt_odb_entries(reference, strlen(reference), '\n', NULL, 0); +} + /* * Compute the exact path an alternate is at and returns it. In case of * error NULL is returned and the human readable error is added to `err` @@ -566,8 +595,8 @@ static int check_and_freshen_nonlocal(const unsigned char *sha1, int freshen) struct alternate_object_database *alt; prepare_alt_odb(); for (alt = alt_odb_list; alt; alt = alt->next) { - fill_sha1_path(alt->name, sha1); - if (check_and_freshen_file(alt->base, freshen)) + const char *path = alt_sha1_path(alt, sha1); + if (check_and_freshen_file(path, freshen)) return 1; } return 0; @@ -1443,11 +1472,8 @@ void prepare_packed_git(void) return; prepare_packed_git_one(get_object_directory(), 1); prepare_alt_odb(); - for (alt = alt_odb_list; alt; alt = alt->next) { - alt->name[-1] = 0; - prepare_packed_git_one(alt->base, 0); - alt->name[-1] = '/'; - } + for (alt = alt_odb_list; alt; alt = alt->next) + prepare_packed_git_one(alt->path, 0); rearrange_packed_git(); prepare_packed_git_mru(); prepare_packed_git_run_once = 1; @@ -1565,8 +1591,8 @@ static int stat_sha1_file(const unsigned char *sha1, struct stat *st) prepare_alt_odb(); errno = ENOENT; for (alt = alt_odb_list; alt; alt = alt->next) { - fill_sha1_path(alt->name, sha1); - if (!lstat(alt->base, st)) + const char *path = alt_sha1_path(alt, sha1); + if (!lstat(path, st)) return 0; } @@ -1586,8 +1612,8 @@ static int open_sha1_file(const unsigned char *sha1) prepare_alt_odb(); for (alt = alt_odb_list; alt; alt = alt->next) { - fill_sha1_path(alt->name, sha1); - fd = git_open_noatime(alt->base); + const char *path = alt_sha1_path(alt, sha1); + fd = git_open_noatime(path); if (fd >= 0) return fd; if (most_interesting_errno == ENOENT) @@ -3648,8 +3674,7 @@ static int loose_from_alt_odb(struct alternate_object_database *alt, struct strbuf buf = STRBUF_INIT; int r; - /* copy base not including trailing '/' */ - strbuf_add(&buf, alt->base, alt->name - alt->base - 1); + strbuf_addstr(&buf, alt->path); r = for_each_loose_file_in_objdir_buf(&buf, data->cb, NULL, NULL, data->data); diff --git a/sha1_name.c b/sha1_name.c index 3b647fd7cf..4092836146 100644 --- a/sha1_name.c +++ b/sha1_name.c @@ -91,25 +91,18 @@ static void find_short_object_filename(struct disambiguate_state *ds) * alt->name/alt->base while iterating over the * object databases including our own. */ - const char *objdir = get_object_directory(); - size_t objdir_len = strlen(objdir); - fakeent = xmalloc(st_add3(sizeof(*fakeent), objdir_len, 43)); - memcpy(fakeent->base, objdir, objdir_len); - fakeent->name = fakeent->base + objdir_len + 1; - fakeent->name[-1] = '/'; + fakeent = alloc_alt_odb(get_object_directory()); } fakeent->next = alt_odb_list; xsnprintf(hex, sizeof(hex), "%.2s", ds->hex_pfx); for (alt = fakeent; alt && !ds->ambiguous; alt = alt->next) { + struct strbuf *buf = alt_scratch_buf(alt); struct dirent *de; DIR *dir; - /* - * every alt_odb struct has 42 extra bytes after the base - * for exactly this purpose - */ - xsnprintf(alt->name, 42, "%.2s/", ds->hex_pfx); - dir = opendir(alt->base); + + strbuf_addf(buf, "%.2s/", ds->hex_pfx); + dir = opendir(buf->buf); if (!dir) continue; @@ -870,3 +870,23 @@ void strbuf_stripspace(struct strbuf *sb, int skip_comments) strbuf_setlen(sb, j); } + +int strbuf_normalize_path(struct strbuf *src) +{ + struct strbuf dst = STRBUF_INIT; + + strbuf_grow(&dst, src->len); + if (normalize_path_copy(dst.buf, src->buf) < 0) { + strbuf_release(&dst); + return -1; + } + + /* + * normalize_path does not tell us the new length, so we have to + * compute it by looking for the new NUL it placed + */ + strbuf_setlen(&dst, strlen(dst.buf)); + strbuf_swap(src, &dst); + strbuf_release(&dst); + return 0; +} @@ -443,6 +443,14 @@ extern int strbuf_getcwd(struct strbuf *sb); */ extern void strbuf_add_absolute_path(struct strbuf *sb, const char *path); + +/** + * Normalize in-place the path contained in the strbuf. See + * normalize_path_copy() for details. If an error occurs, the contents of "sb" + * are left untouched, and -1 is returned. + */ +extern int strbuf_normalize_path(struct strbuf *sb); + /** * Strip whitespace from a buffer. The second parameter controls if * comments are considered contents to be removed or not. diff --git a/submodule.c b/submodule.c index 2de06a3351..733332035b 100644 --- a/submodule.c +++ b/submodule.c @@ -123,9 +123,7 @@ void stage_updated_gitmodules(void) static int add_submodule_odb(const char *path) { struct strbuf objects_directory = STRBUF_INIT; - struct alternate_object_database *alt_odb; int ret = 0; - size_t alloc; ret = strbuf_git_path_submodule(&objects_directory, path, "objects/"); if (ret) @@ -134,26 +132,7 @@ static int add_submodule_odb(const char *path) ret = -1; goto done; } - /* avoid adding it twice */ - prepare_alt_odb(); - for (alt_odb = alt_odb_list; alt_odb; alt_odb = alt_odb->next) - if (alt_odb->name - alt_odb->base == objects_directory.len && - !strncmp(alt_odb->base, objects_directory.buf, - objects_directory.len)) - goto done; - - alloc = st_add(objects_directory.len, 42); /* for "12/345..." sha1 */ - alt_odb = xmalloc(st_add(sizeof(*alt_odb), alloc)); - alt_odb->next = alt_odb_list; - xsnprintf(alt_odb->base, alloc, "%s", objects_directory.buf); - alt_odb->name = alt_odb->base + objects_directory.len; - alt_odb->name[2] = '/'; - alt_odb->name[40] = '\0'; - alt_odb->name[41] = '\0'; - alt_odb_list = alt_odb; - - /* add possible alternates from the submodule */ - read_info_alternates(objects_directory.buf, 0); + add_to_alternates_memory(objects_directory.buf); done: strbuf_release(&objects_directory); return ret; diff --git a/t/t5613-info-alternate.sh b/t/t5613-info-alternate.sh index 9cd2626dba..895f46bb91 100755 --- a/t/t5613-info-alternate.sh +++ b/t/t5613-info-alternate.sh @@ -6,107 +6,134 @@ test_description='test transitive info/alternate entries' . ./test-lib.sh -# test that a file is not reachable in the current repository -# but that it is after creating a info/alternate entry -reachable_via() { - alternate="$1" - file="$2" - if git cat-file -e "HEAD:$file"; then return 1; fi - echo "$alternate" >> .git/objects/info/alternate - git cat-file -e "HEAD:$file" -} - -test_valid_repo() { - git fsck --full > fsck.log && - test_line_count = 0 fsck.log -} - -base_dir=$(pwd) - -test_expect_success 'preparing first repository' \ -'test_create_repo A && cd A && -echo "Hello World" > file1 && -git add file1 && -git commit -m "Initial commit" file1 && -git repack -a -d && -git prune' - -cd "$base_dir" - -test_expect_success 'preparing second repository' \ -'git clone -l -s A B && cd B && -echo "foo bar" > file2 && -git add file2 && -git commit -m "next commit" file2 && -git repack -a -d -l && -git prune' - -cd "$base_dir" - -test_expect_success 'preparing third repository' \ -'git clone -l -s B C && cd C && -echo "Goodbye, cruel world" > file3 && -git add file3 && -git commit -m "one more" file3 && -git repack -a -d -l && -git prune' - -cd "$base_dir" - -test_expect_success 'creating too deep nesting' \ -'git clone -l -s C D && -git clone -l -s D E && -git clone -l -s E F && -git clone -l -s F G && -git clone --bare -l -s G H' - -test_expect_success 'invalidity of deepest repository' \ -'cd H && { - test_valid_repo - test $? -ne 0 -}' - -cd "$base_dir" +test_expect_success 'preparing first repository' ' + test_create_repo A && ( + cd A && + echo "Hello World" > file1 && + git add file1 && + git commit -m "Initial commit" file1 && + git repack -a -d && + git prune + ) +' -test_expect_success 'validity of third repository' \ -'cd C && -test_valid_repo' +test_expect_success 'preparing second repository' ' + git clone -l -s A B && ( + cd B && + echo "foo bar" > file2 && + git add file2 && + git commit -m "next commit" file2 && + git repack -a -d -l && + git prune + ) +' -cd "$base_dir" +test_expect_success 'preparing third repository' ' + git clone -l -s B C && ( + cd C && + echo "Goodbye, cruel world" > file3 && + git add file3 && + git commit -m "one more" file3 && + git repack -a -d -l && + git prune + ) +' -test_expect_success 'validity of fourth repository' \ -'cd D && -test_valid_repo' +test_expect_success 'count-objects shows the alternates' ' + cat >expect <<-EOF && + alternate: $(pwd)/B/.git/objects + alternate: $(pwd)/A/.git/objects + EOF + git -C C count-objects -v >actual && + grep ^alternate: actual >actual.alternates && + test_cmp expect actual.alternates +' -cd "$base_dir" +# Note: These tests depend on the hard-coded value of 5 as the maximum depth +# we will follow recursion. We start the depth at 0 and count links, not +# repositories. This means that in a chain like: +# +# A --> B --> C --> D --> E --> F --> G --> H +# 0 1 2 3 4 5 6 +# +# we are OK at "G", but break at "H", even though "H" is actually the 8th +# repository, not the 6th, which you might expect. Counting the links allows +# N+1 repositories, and counting from 0 to 5 inclusive allows 6 links. +# +# Note also that we must use "--bare -l" to make the link to H. The "-l" +# ensures we do not do a connectivity check, and the "--bare" makes sure +# we do not try to checkout the result (which needs objects), either of +# which would cause the clone to fail. +test_expect_success 'creating too deep nesting' ' + git clone -l -s C D && + git clone -l -s D E && + git clone -l -s E F && + git clone -l -s F G && + git clone --bare -l -s G H +' -test_expect_success 'breaking of loops' \ -'echo "$base_dir"/B/.git/objects >> "$base_dir"/A/.git/objects/info/alternates&& -cd C && -test_valid_repo' +test_expect_success 'validity of seventh repository' ' + git -C G fsck +' -cd "$base_dir" +test_expect_success 'invalidity of eighth repository' ' + test_must_fail git -C H fsck +' -test_expect_success 'that info/alternates is necessary' \ -'cd C && -rm -f .git/objects/info/alternates && -! (test_valid_repo)' +test_expect_success 'breaking of loops' ' + echo "$(pwd)"/B/.git/objects >>A/.git/objects/info/alternates && + git -C C fsck +' -cd "$base_dir" +test_expect_success 'that info/alternates is necessary' ' + rm -f C/.git/objects/info/alternates && + test_must_fail git -C C fsck +' -test_expect_success 'that relative alternate is possible for current dir' \ -'cd C && -echo "../../../B/.git/objects" > .git/objects/info/alternates && -test_valid_repo' +test_expect_success 'that relative alternate is possible for current dir' ' + echo "../../../B/.git/objects" >C/.git/objects/info/alternates && + git fsck +' -cd "$base_dir" +test_expect_success 'that relative alternate is recursive' ' + git -C D fsck +' -test_expect_success \ - 'that relative alternate is only possible for current dir' ' - cd D && - ! (test_valid_repo) +# we can reach "A" from our new repo both directly, and via "C". +# The deep/subdir is there to make sure we are not doing a stupid +# pure-text comparison of the alternate names. +test_expect_success 'relative duplicates are eliminated' ' + mkdir -p deep/subdir && + git init --bare deep/subdir/duplicate.git && + cat >deep/subdir/duplicate.git/objects/info/alternates <<-\EOF && + ../../../../C/.git/objects + ../../../../A/.git/objects + EOF + cat >expect <<-EOF && + alternate: $(pwd)/C/.git/objects + alternate: $(pwd)/B/.git/objects + alternate: $(pwd)/A/.git/objects + EOF + git -C deep/subdir/duplicate.git count-objects -v >actual && + grep ^alternate: actual >actual.alternates && + test_cmp expect actual.alternates ' -cd "$base_dir" +test_expect_success CASE_INSENSITIVE_FS 'dup finding can be case-insensitive' ' + git init --bare insensitive.git && + # the previous entry for "A" will have used uppercase + cat >insensitive.git/objects/info/alternates <<-\EOF && + ../../C/.git/objects + ../../a/.git/objects + EOF + cat >expect <<-EOF && + alternate: $(pwd)/C/.git/objects + alternate: $(pwd)/B/.git/objects + alternate: $(pwd)/A/.git/objects + EOF + git -C insensitive.git count-objects -v >actual && + grep ^alternate: actual >actual.alternates && + test_cmp expect actual.alternates +' test_done diff --git a/transport.c b/transport.c index a85801042b..079499dbaf 100644 --- a/transport.c +++ b/transport.c @@ -1096,9 +1096,7 @@ static int refs_from_alternate_cb(struct alternate_object_database *e, const struct ref *extra; struct alternate_refs_data *cb = data; - e->name[-1] = '\0'; - other = xstrdup(real_path(e->base)); - e->name[-1] = '/'; + other = xstrdup(real_path(e->path)); len = strlen(other); while (other[len-1] == '/') |