Merge branch 'mw/alternates' into next

* mw/alternates:
  clone: don't clone the info/alternates file
  test case for transitive info/alternates
  Transitively read alternatives
  repack: honor -d even when no new pack was created
  clone: keep --reference even with -l -s
  repo-config: document what value_regexp does a bit more clearly.
  Release config lock if the regex is invalid
  core-tutorial.txt: escape asterisk
  Sparse fix for builtin-diff
  Fix users of prefix_path() to free() only when necessary
This commit is contained in:
Junio C Hamano 2006-05-07 16:06:45 -07:00
commit 7f498065e9
9 changed files with 307 additions and 92 deletions

View File

@ -971,7 +971,7 @@ $ git show-branch --topo-order master mybranch
The first two lines indicate that it is showing the two branches The first two lines indicate that it is showing the two branches
and the first line of the commit log message from their and the first line of the commit log message from their
top-of-the-tree commits, you are currently on `master` branch top-of-the-tree commits, you are currently on `master` branch
(notice the asterisk `*` character), and the first column for (notice the asterisk `\*` character), and the first column for
the later output lines is used to show commits contained in the the later output lines is used to show commits contained in the
`master` branch, and the second column for the `mybranch` `master` branch, and the second column for the `mybranch`
branch. Three commits are shown along with their log messages. branch. Three commits are shown along with their log messages.

View File

@ -23,10 +23,11 @@ You can query/set/replace/unset options with this command. The name is
actually the section and the key separated by a dot, and the value will be actually the section and the key separated by a dot, and the value will be
escaped. escaped.
If you want to set/unset an option which can occur on multiple lines, you If you want to set/unset an option which can occur on multiple
should provide a POSIX regex for the value. If you want to handle the lines lines, a POSIX regexp `value_regex` needs to be given. Only the
*not* matching the regex, just prepend a single exclamation mark in front existing values that match the regexp are updated or unset. If
(see EXAMPLES). you want to handle the lines that do *not* match the regex, just
prepend a single exclamation mark in front (see EXAMPLES).
The type specifier can be either '--int' or '--bool', which will make The type specifier can be either '--int' or '--bool', which will make
'git-repo-config' ensure that the variable(s) are of the given type and 'git-repo-config' ensure that the variable(s) are of the given type and

View File

@ -84,8 +84,7 @@ static void stuff_change(struct diff_options *opt,
if (opt->reverse_diff) { if (opt->reverse_diff) {
unsigned tmp; unsigned tmp;
const const unsigned char *tmp_u;
const unsigned char *tmp_u;
const char *tmp_c; const char *tmp_c;
tmp = old_mode; old_mode = new_mode; new_mode = tmp; tmp = old_mode; old_mode = new_mode; new_mode = tmp;
tmp_u = old_sha1; old_sha1 = new_sha1; new_sha1 = tmp_u; tmp_u = old_sha1; old_sha1 = new_sha1; new_sha1 = tmp_u;

View File

@ -516,6 +516,8 @@ int git_config_set_multivar(const char* key, const char* value,
fprintf(stderr, "Invalid pattern: %s\n", fprintf(stderr, "Invalid pattern: %s\n",
value_regex); value_regex);
free(store.value_regex); free(store.value_regex);
close(fd);
unlink(lock_file);
ret = 6; ret = 6;
goto out_free; goto out_free;
} }

View File

@ -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"

View File

@ -48,15 +48,15 @@ name=$(git-rev-list --objects --all $rev_list 2>&1 |
exit 1 exit 1
if [ -z "$name" ]; then if [ -z "$name" ]; then
echo Nothing new to pack. echo Nothing new to pack.
exit 0 else
echo "Pack pack-$name created."
mkdir -p "$PACKDIR" || exit
mv .tmp-pack-$name.pack "$PACKDIR/pack-$name.pack" &&
mv .tmp-pack-$name.idx "$PACKDIR/pack-$name.idx" ||
exit
fi fi
echo "Pack pack-$name created."
mkdir -p "$PACKDIR" || exit
mv .tmp-pack-$name.pack "$PACKDIR/pack-$name.pack" &&
mv .tmp-pack-$name.idx "$PACKDIR/pack-$name.idx" ||
exit
if test "$remove_redundant" = t if test "$remove_redundant" = t
then then

View File

@ -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,13 +234,84 @@ 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 int link_alt_odb_entry(const char * entry, int len, const char * relative_base, int depth)
{
struct stat st;
const char *objdir = get_object_directory();
struct alternate_object_database *ent;
struct alternate_object_database *alt;
/* 43 = 40-byte + 2 '/' + terminating NUL */
int pfxlen = len;
int entlen = pfxlen + 43;
int base_len = -1;
if (*entry != '/' && relative_base) {
/* Relative alt-odb */
if (base_len < 0)
base_len = strlen(relative_base) + 1;
entlen += base_len;
pfxlen += base_len;
}
ent = xmalloc(sizeof(*ent) + entlen);
if (*entry != '/' && relative_base) {
memcpy(ent->base, relative_base, base_len - 1);
ent->base[base_len - 1] = '/';
memcpy(ent->base + base_len, entry, len);
}
else
memcpy(ent->base, entry, pfxlen);
ent->name = ent->base + pfxlen + 1;
ent->base[pfxlen + 3] = '/';
ent->base[pfxlen] = ent->base[entlen-1] = 0;
/* Detect cases where alternate disappeared */
if (stat(ent->base, &st) || !S_ISDIR(st.st_mode)) {
error("object directory %s does not exist; "
"check .git/objects/info/alternates.",
ent->base);
free(ent);
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 (!memcmp(ent->base, alt->base, pfxlen)) {
free(ent);
return -1;
}
}
if (!memcmp(ent->base, objdir, pfxlen)) {
free(ent);
return -1;
}
/* add the alternate entry */
*alt_odb_tail = ent;
alt_odb_tail = &(ent->next);
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, static void link_alt_odb_entries(const char *alt, const char *ep, int sep,
const char *relative_base) const char *relative_base, int depth)
{ {
const char *cp, *last; const char *cp, *last;
struct alternate_object_database *ent;
const char *objdir = get_object_directory(); if (depth > 5) {
int base_len = -1; error("%s: ignoring alternate object stores, nesting too deep.",
relative_base);
return;
}
last = alt; last = alt;
while (last < ep) { while (last < ep) {
@ -249,60 +322,15 @@ static void link_alt_odb_entries(const char *alt, const char *ep, int sep,
last = cp + 1; last = cp + 1;
continue; continue;
} }
for ( ; cp < ep && *cp != sep; cp++) while (cp < ep && *cp != sep)
; cp++;
if (last != cp) { if (last != cp) {
struct stat st; if ((*last != '/') && depth) {
struct alternate_object_database *alt; error("%s: ignoring relative alternate object store %s",
/* 43 = 40-byte + 2 '/' + terminating NUL */ relative_base, last);
int pfxlen = cp - last; } else {
int entlen = pfxlen + 43; link_alt_odb_entry(last, cp - last,
relative_base, depth);
if (*last != '/' && relative_base) {
/* Relative alt-odb */
if (base_len < 0)
base_len = strlen(relative_base) + 1;
entlen += base_len;
pfxlen += base_len;
}
ent = xmalloc(sizeof(*ent) + entlen);
if (*last != '/' && relative_base) {
memcpy(ent->base, relative_base, base_len - 1);
ent->base[base_len - 1] = '/';
memcpy(ent->base + base_len,
last, cp - last);
}
else
memcpy(ent->base, last, pfxlen);
ent->name = ent->base + pfxlen + 1;
ent->base[pfxlen + 3] = '/';
ent->base[pfxlen] = ent->base[entlen-1] = 0;
/* Detect cases where alternate disappeared */
if (stat(ent->base, &st) || !S_ISDIR(st.st_mode)) {
error("object directory %s does not exist; "
"check .git/objects/info/alternates.",
ent->base);
goto bad;
}
ent->base[pfxlen] = '/';
/* Prevent the common mistake of listing the same
* thing twice, or object directory itself.
*/
for (alt = alt_odb_list; alt; alt = alt->next)
if (!memcmp(ent->base, alt->base, pfxlen))
goto bad;
if (!memcmp(ent->base, objdir, pfxlen)) {
bad:
free(ent);
}
else {
*alt_odb_tail = ent;
alt_odb_tail = &(ent->next);
ent->next = NULL;
} }
} }
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); sprintf(path, "%s/info/alternates", relative_base);
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());
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);

78
t/t5700-clone-reference.sh Executable file
View File

@ -0,0 +1,78 @@
#!/bin/sh
#
# Copyright (C) 2006 Martin Waitz <tali@admingilde.org>
#
test_description='test clone --reference'
. ./test-lib.sh
base_dir=`pwd`
test_expect_success 'preparing first repository' \
'test_create_repo A && cd A &&
echo first > file1 &&
git add file1 &&
git commit -m initial'
cd "$base_dir"
test_expect_success 'preparing second repository' \
'git clone A B && cd B &&
echo second > file2 &&
git add file2 &&
git commit -m addition &&
git repack -a -d &&
git prune'
cd "$base_dir"
test_expect_success 'cloning with reference' \
'git clone -l -s --reference B A C'
cd "$base_dir"
test_expect_success 'existance of info/alternates' \
'test `wc -l <C/.git/objects/info/alternates` = 2'
cd "$base_dir"
test_expect_success 'pulling from reference' \
'cd C &&
git pull ../B'
cd "$base_dir"
test_expect_success 'that reference gets used' \
'cd C &&
echo "0 objects, 0 kilobytes" > expected &&
git count-objects > current &&
diff expected current'
cd "$base_dir"
test_expect_success 'updating origin' \
'cd A &&
echo third > file3 &&
git add file3 &&
git commit -m update &&
git repack -a -d &&
git prune'
cd "$base_dir"
test_expect_success 'pulling changes from origin' \
'cd C &&
git pull origin'
cd "$base_dir"
# the 2 local objects are commit and tree from the merge
test_expect_success 'that alternate to origin gets used' \
'cd C &&
echo "2 objects" > expected &&
git count-objects | cut -d, -f1 > current &&
diff expected current'
cd "$base_dir"
test_done

105
t/t5710-info-alternate.sh Executable file
View File

@ -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