зеркало из https://github.com/microsoft/git.git
Merge branch 'jk/alt-odb-cleanup'
Codepaths involved in interacting alternate object store have been cleaned up. * jk/alt-odb-cleanup: alternates: use fspathcmp to detect duplicates sha1_file: always allow relative paths to alternates count-objects: report alternates via verbose mode fill_sha1_file: write into a strbuf alternates: store scratch buffer as strbuf fill_sha1_file: write "boring" characters alternates: use a separate scratch space alternates: encapsulate alt->base munging alternates: provide helper for allocating alternate alternates: provide helper for adding to alternates list link_alt_odb_entry: refactor string handling link_alt_odb_entry: handle normalize_path errors t5613: clarify "too deep" recursion tests t5613: do not chdir in main process t5613: whitespace/style cleanups t5613: use test_must_fail t5613: drop test_valid_repo function t5613: drop reachable_via function
This commit is contained in:
Коммит
dec040192f
|
@ -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::
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
36
cache.h
36
cache.h
|
@ -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;
|
||||
|
|
179
sha1_file.c
179
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;
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -234,6 +240,35 @@ char *sha1_pack_index_name(const unsigned char *sha1)
|
|||
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.
|
||||
*
|
||||
|
@ -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);
|
||||
|
|
17
sha1_name.c
17
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;
|
||||
|
||||
|
|
20
strbuf.c
20
strbuf.c
|
@ -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;
|
||||
}
|
||||
|
|
8
strbuf.h
8
strbuf.h
|
@ -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.
|
||||
|
|
23
submodule.c
23
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;
|
||||
|
|
|
@ -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 'validity of third repository' \
|
||||
'cd C &&
|
||||
test_valid_repo'
|
||||
|
||||
cd "$base_dir"
|
||||
|
||||
test_expect_success 'validity of fourth repository' \
|
||||
'cd D &&
|
||||
test_valid_repo'
|
||||
|
||||
cd "$base_dir"
|
||||
|
||||
test_expect_success 'breaking of loops' \
|
||||
'echo "$base_dir"/B/.git/objects >> "$base_dir"/A/.git/objects/info/alternates&&
|
||||
cd C &&
|
||||
test_valid_repo'
|
||||
|
||||
cd "$base_dir"
|
||||
|
||||
test_expect_success 'that info/alternates is necessary' \
|
||||
'cd C &&
|
||||
rm -f .git/objects/info/alternates &&
|
||||
! (test_valid_repo)'
|
||||
|
||||
cd "$base_dir"
|
||||
|
||||
test_expect_success 'that relative alternate is possible for current dir' \
|
||||
'cd C &&
|
||||
echo "../../../B/.git/objects" > .git/objects/info/alternates &&
|
||||
test_valid_repo'
|
||||
|
||||
cd "$base_dir"
|
||||
|
||||
test_expect_success \
|
||||
'that relative alternate is only possible for current dir' '
|
||||
cd D &&
|
||||
! (test_valid_repo)
|
||||
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
|
||||
)
|
||||
'
|
||||
|
||||
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 '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
|
||||
'
|
||||
|
||||
# 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 'validity of seventh repository' '
|
||||
git -C G fsck
|
||||
'
|
||||
|
||||
test_expect_success 'invalidity of eighth repository' '
|
||||
test_must_fail git -C H fsck
|
||||
'
|
||||
|
||||
test_expect_success 'breaking of loops' '
|
||||
echo "$(pwd)"/B/.git/objects >>A/.git/objects/info/alternates &&
|
||||
git -C C fsck
|
||||
'
|
||||
|
||||
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' '
|
||||
echo "../../../B/.git/objects" >C/.git/objects/info/alternates &&
|
||||
git fsck
|
||||
'
|
||||
|
||||
test_expect_success 'that relative alternate is recursive' '
|
||||
git -C D fsck
|
||||
'
|
||||
|
||||
# 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
|
||||
'
|
||||
|
||||
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
|
||||
|
|
|
@ -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] == '/')
|
||||
|
|
Загрузка…
Ссылка в новой задаче