Browse Source

Merge branch 'mw/alternates'

* mw/alternates:
  clone: don't clone the info/alternates file
  test case for transitive info/alternates
  Transitively read alternatives
maint
Junio C Hamano 19 years ago
parent
commit
2c49009dbe
  1. 6
      git-clone.sh
  2. 128
      sha1_file.c
  3. 105
      t/t5710-info-alternate.sh

6
git-clone.sh

@ -261,11 +261,7 @@ yes,yes)
;; ;;
yes) yes)
mkdir -p "$GIT_DIR/objects/info" mkdir -p "$GIT_DIR/objects/info"
{ echo "$repo/objects" >> "$GIT_DIR/objects/info/alternates"
test -f "$repo/objects/info/alternates" &&
cat "$repo/objects/info/alternates";
echo "$repo/objects"
} >>"$GIT_DIR/objects/info/alternates"
;; ;;
esac esac
git-ls-remote "$repo" >"$GIT_DIR/CLONE_HEAD" git-ls-remote "$repo" >"$GIT_DIR/CLONE_HEAD"

128
sha1_file.c

@ -217,6 +217,8 @@ char *sha1_pack_index_name(const unsigned char *sha1)
struct alternate_object_database *alt_odb_list; struct alternate_object_database *alt_odb_list;
static struct alternate_object_database **alt_odb_tail; static struct alternate_object_database **alt_odb_tail;


static void read_info_alternates(const char * alternates, int depth);

/* /*
* Prepare alternate object database registry. * Prepare alternate object database registry.
* *
@ -232,33 +234,18 @@ static struct alternate_object_database **alt_odb_tail;
* SHA1, an extra slash for the first level indirection, and the * SHA1, an extra slash for the first level indirection, and the
* terminating NUL. * terminating NUL.
*/ */
static void link_alt_odb_entries(const char *alt, const char *ep, int sep, static int link_alt_odb_entry(const char * entry, int len, const char * relative_base, int depth)
const char *relative_base)
{ {
const char *cp, *last;
struct alternate_object_database *ent;
const char *objdir = get_object_directory();
int base_len = -1;

last = alt;
while (last < ep) {
cp = last;
if (cp < ep && *cp == '#') {
while (cp < ep && *cp != sep)
cp++;
last = cp + 1;
continue;
}
for ( ; cp < ep && *cp != sep; cp++)
;
if (last != cp) {
struct stat st; struct stat st;
const char *objdir = get_object_directory();
struct alternate_object_database *ent;
struct alternate_object_database *alt; struct alternate_object_database *alt;
/* 43 = 40-byte + 2 '/' + terminating NUL */ /* 43 = 40-byte + 2 '/' + terminating NUL */
int pfxlen = cp - last; int pfxlen = len;
int entlen = pfxlen + 43; int entlen = pfxlen + 43;
int base_len = -1;


if (*last != '/' && relative_base) { if (*entry != '/' && relative_base) {
/* Relative alt-odb */ /* Relative alt-odb */
if (base_len < 0) if (base_len < 0)
base_len = strlen(relative_base) + 1; base_len = strlen(relative_base) + 1;
@ -267,14 +254,13 @@ static void link_alt_odb_entries(const char *alt, const char *ep, int sep,
} }
ent = xmalloc(sizeof(*ent) + entlen); ent = xmalloc(sizeof(*ent) + entlen);


if (*last != '/' && relative_base) { if (*entry != '/' && relative_base) {
memcpy(ent->base, relative_base, base_len - 1); memcpy(ent->base, relative_base, base_len - 1);
ent->base[base_len - 1] = '/'; ent->base[base_len - 1] = '/';
memcpy(ent->base + base_len, memcpy(ent->base + base_len, entry, len);
last, cp - last);
} }
else else
memcpy(ent->base, last, pfxlen); memcpy(ent->base, entry, pfxlen);


ent->name = ent->base + pfxlen + 1; ent->name = ent->base + pfxlen + 1;
ent->base[pfxlen + 3] = '/'; ent->base[pfxlen + 3] = '/';
@ -285,24 +271,66 @@ static void link_alt_odb_entries(const char *alt, const char *ep, int sep,
error("object directory %s does not exist; " error("object directory %s does not exist; "
"check .git/objects/info/alternates.", "check .git/objects/info/alternates.",
ent->base); ent->base);
goto bad; free(ent);
return -1;
} }
ent->base[pfxlen] = '/';


/* Prevent the common mistake of listing the same /* Prevent the common mistake of listing the same
* thing twice, or object directory itself. * thing twice, or object directory itself.
*/ */
for (alt = alt_odb_list; alt; alt = alt->next) for (alt = alt_odb_list; alt; alt = alt->next) {
if (!memcmp(ent->base, alt->base, pfxlen)) if (!memcmp(ent->base, alt->base, pfxlen)) {
goto bad; free(ent);
return -1;
}
}
if (!memcmp(ent->base, objdir, pfxlen)) { if (!memcmp(ent->base, objdir, pfxlen)) {
bad:
free(ent); free(ent);
return -1;
} }
else {
/* add the alternate entry */
*alt_odb_tail = ent; *alt_odb_tail = ent;
alt_odb_tail = &(ent->next); alt_odb_tail = &(ent->next);
ent->next = NULL; ent->next = NULL;

/* recursively add alternates */
read_info_alternates(ent->base, depth + 1);

ent->base[pfxlen] = '/';

return 0;
}

static void link_alt_odb_entries(const char *alt, const char *ep, int sep,
const char *relative_base, int depth)
{
const char *cp, *last;

if (depth > 5) {
error("%s: ignoring alternate object stores, nesting too deep.",
relative_base);
return;
}

last = alt;
while (last < ep) {
cp = last;
if (cp < ep && *cp == '#') {
while (cp < ep && *cp != sep)
cp++;
last = cp + 1;
continue;
}
while (cp < ep && *cp != sep)
cp++;
if (last != cp) {
if ((*last != '/') && depth) {
error("%s: ignoring relative alternate object store %s",
relative_base, last);
} else {
link_alt_odb_entry(last, cp - last,
relative_base, depth);
} }
} }
while (cp < ep && *cp == sep) while (cp < ep && *cp == sep)
@ -311,23 +339,14 @@ static void link_alt_odb_entries(const char *alt, const char *ep, int sep,
} }
} }


void prepare_alt_odb(void) static void read_info_alternates(const char * relative_base, int depth)
{ {
char path[PATH_MAX];
char *map; char *map;
int fd;
struct stat st; struct stat st;
char *alt; char path[PATH_MAX];

int fd;
alt = getenv(ALTERNATE_DB_ENVIRONMENT);
if (!alt) alt = "";

if (alt_odb_tail)
return;
alt_odb_tail = &alt_odb_list;
link_alt_odb_entries(alt, alt + strlen(alt), ':', NULL);


sprintf(path, "%s/info/alternates", get_object_directory()); sprintf(path, "%s/info/alternates", relative_base);
fd = open(path, O_RDONLY); fd = open(path, O_RDONLY);
if (fd < 0) if (fd < 0)
return; return;
@ -340,11 +359,26 @@ void prepare_alt_odb(void)
if (map == MAP_FAILED) if (map == MAP_FAILED)
return; return;


link_alt_odb_entries(map, map + st.st_size, '\n', link_alt_odb_entries(map, map + st.st_size, '\n', relative_base, depth);
get_object_directory());
munmap(map, st.st_size); munmap(map, st.st_size);
} }


void prepare_alt_odb(void)
{
char *alt;

alt = getenv(ALTERNATE_DB_ENVIRONMENT);
if (!alt) alt = "";

if (alt_odb_tail)
return;
alt_odb_tail = &alt_odb_list;
link_alt_odb_entries(alt, alt + strlen(alt), ':', NULL, 0);

read_info_alternates(get_object_directory(), 0);
}

static char *find_sha1_file(const unsigned char *sha1, struct stat *st) static char *find_sha1_file(const unsigned char *sha1, struct stat *st)
{ {
char *name = sha1_file_name(sha1); char *name = sha1_file_name(sha1);

105
t/t5710-info-alternate.sh

@ -0,0 +1,105 @@
#!/bin/sh
#
# Copyright (C) 2006 Martin Waitz <tali@admingilde.org>
#

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-objects --full > fsck.log &&
test `wc -l < fsck.log` = 0
}

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_failure '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 &&
test_valid_repo'

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_failure 'that info/alternates is neccessary' \
'cd C &&
rm .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_failure 'that relative alternate is only possible for current dir' \
'cd D &&
test_valid_repo'

cd "$base_dir"

test_done

Loading…
Cancel
Save