зеркало из https://github.com/microsoft/git.git
Merge branch 'dt/cache-tree-repair'
Add a few more places in "commit" and "checkout" that make sure that the cache-tree is fully populated in the index. * dt/cache-tree-repair: cache-tree: do not try to use an invalidated subtree info to build a tree cache-tree: Write updated cache-tree after commit cache-tree: subdirectory tests test-dump-cache-tree: invalid trees are not errors cache-tree: create/update cache-tree on checkout
This commit is contained in:
Коммит
3fd13cbcd5
|
@ -552,6 +552,12 @@ static int merge_working_tree(const struct checkout_opts *opts,
|
|||
}
|
||||
}
|
||||
|
||||
if (!active_cache_tree)
|
||||
active_cache_tree = cache_tree();
|
||||
|
||||
if (!cache_tree_fully_valid(active_cache_tree))
|
||||
cache_tree_update(&the_index, WRITE_TREE_SILENT | WRITE_TREE_REPAIR);
|
||||
|
||||
if (write_locked_index(&the_index, lock_file, COMMIT_LOCK))
|
||||
die(_("unable to write new index file"));
|
||||
|
||||
|
|
|
@ -353,6 +353,13 @@ static char *prepare_index(int argc, const char **argv, const char *prefix,
|
|||
|
||||
discard_cache();
|
||||
read_cache_from(index_lock.filename);
|
||||
if (update_main_cache_tree(WRITE_TREE_SILENT) == 0) {
|
||||
if (reopen_lock_file(&index_lock) < 0)
|
||||
die(_("unable to write index file"));
|
||||
if (write_locked_index(&the_index, &index_lock, CLOSE_LOCK))
|
||||
die(_("unable to update temporary index"));
|
||||
} else
|
||||
warning(_("Failed to update main cache tree"));
|
||||
|
||||
commit_style = COMMIT_NORMAL;
|
||||
return index_lock.filename;
|
||||
|
@ -393,8 +400,12 @@ static char *prepare_index(int argc, const char **argv, const char *prefix,
|
|||
if (!only && !pathspec.nr) {
|
||||
hold_locked_index(&index_lock, 1);
|
||||
refresh_cache_or_die(refresh_flags);
|
||||
if (active_cache_changed) {
|
||||
if (active_cache_changed
|
||||
|| !cache_tree_fully_valid(active_cache_tree)) {
|
||||
update_main_cache_tree(WRITE_TREE_SILENT);
|
||||
active_cache_changed = 1;
|
||||
}
|
||||
if (active_cache_changed) {
|
||||
if (write_locked_index(&the_index, &index_lock,
|
||||
COMMIT_LOCK))
|
||||
die(_("unable to write new_index file"));
|
||||
|
@ -444,6 +455,7 @@ static char *prepare_index(int argc, const char **argv, const char *prefix,
|
|||
hold_locked_index(&index_lock, 1);
|
||||
add_remove_files(&partial);
|
||||
refresh_cache(REFRESH_QUIET);
|
||||
update_main_cache_tree(WRITE_TREE_SILENT);
|
||||
if (write_locked_index(&the_index, &index_lock, CLOSE_LOCK))
|
||||
die(_("unable to write new_index file"));
|
||||
|
||||
|
|
19
cache-tree.c
19
cache-tree.c
|
@ -246,9 +246,12 @@ static int update_one(struct cache_tree *it,
|
|||
struct strbuf buffer;
|
||||
int missing_ok = flags & WRITE_TREE_MISSING_OK;
|
||||
int dryrun = flags & WRITE_TREE_DRY_RUN;
|
||||
int repair = flags & WRITE_TREE_REPAIR;
|
||||
int to_invalidate = 0;
|
||||
int i;
|
||||
|
||||
assert(!(dryrun && repair));
|
||||
|
||||
*skip_count = 0;
|
||||
|
||||
if (0 <= it->entry_count && has_sha1_file(it->sha1))
|
||||
|
@ -320,6 +323,7 @@ static int update_one(struct cache_tree *it,
|
|||
int pathlen, entlen;
|
||||
const unsigned char *sha1;
|
||||
unsigned mode;
|
||||
int expected_missing = 0;
|
||||
|
||||
path = ce->name;
|
||||
pathlen = ce_namelen(ce);
|
||||
|
@ -336,8 +340,10 @@ static int update_one(struct cache_tree *it,
|
|||
i += sub->count;
|
||||
sha1 = sub->cache_tree->sha1;
|
||||
mode = S_IFDIR;
|
||||
if (sub->cache_tree->entry_count < 0)
|
||||
if (sub->cache_tree->entry_count < 0) {
|
||||
to_invalidate = 1;
|
||||
expected_missing = 1;
|
||||
}
|
||||
}
|
||||
else {
|
||||
sha1 = ce->sha1;
|
||||
|
@ -347,6 +353,8 @@ static int update_one(struct cache_tree *it,
|
|||
}
|
||||
if (mode != S_IFGITLINK && !missing_ok && !has_sha1_file(sha1)) {
|
||||
strbuf_release(&buffer);
|
||||
if (expected_missing)
|
||||
return -1;
|
||||
return error("invalid object %06o %s for '%.*s'",
|
||||
mode, sha1_to_hex(sha1), entlen+baselen, path);
|
||||
}
|
||||
|
@ -381,7 +389,14 @@ static int update_one(struct cache_tree *it,
|
|||
#endif
|
||||
}
|
||||
|
||||
if (dryrun)
|
||||
if (repair) {
|
||||
unsigned char sha1[20];
|
||||
hash_sha1_file(buffer.buf, buffer.len, tree_type, sha1);
|
||||
if (has_sha1_file(sha1))
|
||||
hashcpy(it->sha1, sha1);
|
||||
else
|
||||
to_invalidate = 1;
|
||||
} else if (dryrun)
|
||||
hash_sha1_file(buffer.buf, buffer.len, tree_type, it->sha1);
|
||||
else if (write_sha1_file(buffer.buf, buffer.len, tree_type, it->sha1)) {
|
||||
strbuf_release(&buffer);
|
||||
|
|
|
@ -39,6 +39,7 @@ int update_main_cache_tree(int);
|
|||
#define WRITE_TREE_IGNORE_CACHE_TREE 2
|
||||
#define WRITE_TREE_DRY_RUN 4
|
||||
#define WRITE_TREE_SILENT 8
|
||||
#define WRITE_TREE_REPAIR 16
|
||||
|
||||
/* error return codes */
|
||||
#define WRITE_TREE_UNREADABLE_INDEX (-1)
|
||||
|
|
|
@ -8,7 +8,7 @@ cache-tree extension.
|
|||
. ./test-lib.sh
|
||||
|
||||
cmp_cache_tree () {
|
||||
test-dump-cache-tree >actual &&
|
||||
test-dump-cache-tree | sed -e '/#(ref)/d' >actual &&
|
||||
sed "s/$_x40/SHA/" <actual >filtered &&
|
||||
test_cmp "$1" filtered
|
||||
}
|
||||
|
@ -16,15 +16,40 @@ cmp_cache_tree () {
|
|||
# We don't bother with actually checking the SHA1:
|
||||
# test-dump-cache-tree already verifies that all existing data is
|
||||
# correct.
|
||||
test_shallow_cache_tree () {
|
||||
printf "SHA (%d entries, 0 subtrees)\n" $(git ls-files|wc -l) >expect &&
|
||||
generate_expected_cache_tree_rec () {
|
||||
dir="$1${1:+/}" &&
|
||||
parent="$2" &&
|
||||
# ls-files might have foo/bar, foo/bar/baz, and foo/bar/quux
|
||||
# We want to count only foo because it's the only direct child
|
||||
subtrees=$(git ls-files|grep /|cut -d / -f 1|uniq) &&
|
||||
subtree_count=$(echo "$subtrees"|awk '$1 {++c} END {print c}') &&
|
||||
entries=$(git ls-files|wc -l) &&
|
||||
printf "SHA $dir (%d entries, %d subtrees)\n" "$entries" "$subtree_count" &&
|
||||
for subtree in $subtrees
|
||||
do
|
||||
cd "$subtree"
|
||||
generate_expected_cache_tree_rec "$dir$subtree" "$dir" || return 1
|
||||
cd ..
|
||||
done &&
|
||||
dir=$parent
|
||||
}
|
||||
|
||||
generate_expected_cache_tree () {
|
||||
(
|
||||
generate_expected_cache_tree_rec
|
||||
)
|
||||
}
|
||||
|
||||
test_cache_tree () {
|
||||
generate_expected_cache_tree >expect &&
|
||||
cmp_cache_tree expect
|
||||
}
|
||||
|
||||
test_invalid_cache_tree () {
|
||||
echo "invalid (0 subtrees)" >expect &&
|
||||
printf "SHA #(ref) (%d entries, 0 subtrees)\n" $(git ls-files|wc -l) >>expect &&
|
||||
cmp_cache_tree expect
|
||||
printf "invalid %s ()\n" "" "$@" >expect &&
|
||||
test-dump-cache-tree |
|
||||
sed -n -e "s/[0-9]* subtrees//" -e '/#(ref)/d' -e '/^invalid /p' >actual &&
|
||||
test_cmp expect actual
|
||||
}
|
||||
|
||||
test_no_cache_tree () {
|
||||
|
@ -32,26 +57,59 @@ test_no_cache_tree () {
|
|||
cmp_cache_tree expect
|
||||
}
|
||||
|
||||
test_expect_failure 'initial commit has cache-tree' '
|
||||
test_expect_success 'initial commit has cache-tree' '
|
||||
test_commit foo &&
|
||||
test_shallow_cache_tree
|
||||
test_cache_tree
|
||||
'
|
||||
|
||||
test_expect_success 'read-tree HEAD establishes cache-tree' '
|
||||
git read-tree HEAD &&
|
||||
test_shallow_cache_tree
|
||||
test_cache_tree
|
||||
'
|
||||
|
||||
test_expect_success 'git-add invalidates cache-tree' '
|
||||
test_when_finished "git reset --hard; git read-tree HEAD" &&
|
||||
echo "I changed this file" > foo &&
|
||||
echo "I changed this file" >foo &&
|
||||
git add foo &&
|
||||
test_invalid_cache_tree
|
||||
'
|
||||
|
||||
test_expect_success 'git-add in subdir invalidates cache-tree' '
|
||||
test_when_finished "git reset --hard; git read-tree HEAD" &&
|
||||
mkdir dirx &&
|
||||
echo "I changed this file" >dirx/foo &&
|
||||
git add dirx/foo &&
|
||||
test_invalid_cache_tree
|
||||
'
|
||||
|
||||
cat >before <<\EOF
|
||||
SHA (3 entries, 2 subtrees)
|
||||
SHA dir1/ (1 entries, 0 subtrees)
|
||||
SHA dir2/ (1 entries, 0 subtrees)
|
||||
EOF
|
||||
|
||||
cat >expect <<\EOF
|
||||
invalid (2 subtrees)
|
||||
invalid dir1/ (0 subtrees)
|
||||
SHA dir2/ (1 entries, 0 subtrees)
|
||||
EOF
|
||||
|
||||
test_expect_success 'git-add in subdir does not invalidate sibling cache-tree' '
|
||||
git tag no-children &&
|
||||
test_when_finished "git reset --hard no-children; git read-tree HEAD" &&
|
||||
mkdir dir1 dir2 &&
|
||||
test_commit dir1/a &&
|
||||
test_commit dir2/b &&
|
||||
echo "I changed this file" >dir1/a &&
|
||||
cmp_cache_tree before &&
|
||||
echo "I changed this file" >dir1/a &&
|
||||
git add dir1/a &&
|
||||
cmp_cache_tree expect
|
||||
'
|
||||
|
||||
test_expect_success 'update-index invalidates cache-tree' '
|
||||
test_when_finished "git reset --hard; git read-tree HEAD" &&
|
||||
echo "I changed this file" > foo &&
|
||||
echo "I changed this file" >foo &&
|
||||
git update-index --add foo &&
|
||||
test_invalid_cache_tree
|
||||
'
|
||||
|
@ -59,7 +117,7 @@ test_expect_success 'update-index invalidates cache-tree' '
|
|||
test_expect_success 'write-tree establishes cache-tree' '
|
||||
test-scrap-cache-tree &&
|
||||
git write-tree &&
|
||||
test_shallow_cache_tree
|
||||
test_cache_tree
|
||||
'
|
||||
|
||||
test_expect_success 'test-scrap-cache-tree works' '
|
||||
|
@ -70,24 +128,94 @@ test_expect_success 'test-scrap-cache-tree works' '
|
|||
|
||||
test_expect_success 'second commit has cache-tree' '
|
||||
test_commit bar &&
|
||||
test_shallow_cache_tree
|
||||
test_cache_tree
|
||||
'
|
||||
|
||||
test_expect_success 'commit --interactive gives cache-tree on partial commit' '
|
||||
cat <<-\EOT >foo.c &&
|
||||
int foo()
|
||||
{
|
||||
return 42;
|
||||
}
|
||||
int bar()
|
||||
{
|
||||
return 42;
|
||||
}
|
||||
EOT
|
||||
git add foo.c &&
|
||||
test_invalid_cache_tree &&
|
||||
git commit -m "add a file" &&
|
||||
test_cache_tree &&
|
||||
cat <<-\EOT >foo.c &&
|
||||
int foo()
|
||||
{
|
||||
return 43;
|
||||
}
|
||||
int bar()
|
||||
{
|
||||
return 44;
|
||||
}
|
||||
EOT
|
||||
(echo p; echo 1; echo; echo s; echo n; echo y; echo q) |
|
||||
git commit --interactive -m foo &&
|
||||
test_cache_tree
|
||||
'
|
||||
|
||||
test_expect_success 'commit in child dir has cache-tree' '
|
||||
mkdir dir &&
|
||||
>dir/child.t &&
|
||||
git add dir/child.t &&
|
||||
git commit -m dir/child.t &&
|
||||
test_cache_tree
|
||||
'
|
||||
|
||||
test_expect_success 'reset --hard gives cache-tree' '
|
||||
test-scrap-cache-tree &&
|
||||
git reset --hard &&
|
||||
test_shallow_cache_tree
|
||||
test_cache_tree
|
||||
'
|
||||
|
||||
test_expect_success 'reset --hard without index gives cache-tree' '
|
||||
rm -f .git/index &&
|
||||
git reset --hard &&
|
||||
test_shallow_cache_tree
|
||||
test_cache_tree
|
||||
'
|
||||
|
||||
test_expect_failure 'checkout gives cache-tree' '
|
||||
test_expect_success 'checkout gives cache-tree' '
|
||||
git tag current &&
|
||||
git checkout HEAD^ &&
|
||||
test_shallow_cache_tree
|
||||
test_cache_tree
|
||||
'
|
||||
|
||||
test_expect_success 'checkout -b gives cache-tree' '
|
||||
git checkout current &&
|
||||
git checkout -b prev HEAD^ &&
|
||||
test_cache_tree
|
||||
'
|
||||
|
||||
test_expect_success 'checkout -B gives cache-tree' '
|
||||
git checkout current &&
|
||||
git checkout -B prev HEAD^ &&
|
||||
test_cache_tree
|
||||
'
|
||||
|
||||
test_expect_success 'partial commit gives cache-tree' '
|
||||
git checkout -b partial no-children &&
|
||||
test_commit one &&
|
||||
test_commit two &&
|
||||
echo "some change" >one.t &&
|
||||
git add one.t &&
|
||||
echo "some other change" >two.t &&
|
||||
git commit two.t -m partial &&
|
||||
test_cache_tree
|
||||
'
|
||||
|
||||
test_expect_success 'no phantom error when switching trees' '
|
||||
mkdir newdir &&
|
||||
>newdir/one &&
|
||||
git add newdir/one &&
|
||||
git checkout 2>errors &&
|
||||
! test -s errors
|
||||
'
|
||||
|
||||
test_done
|
||||
|
|
|
@ -26,16 +26,16 @@ static int dump_cache_tree(struct cache_tree *it,
|
|||
return 0;
|
||||
|
||||
if (it->entry_count < 0) {
|
||||
/* invalid */
|
||||
dump_one(it, pfx, "");
|
||||
dump_one(ref, pfx, "#(ref) ");
|
||||
if (it->subtree_nr != ref->subtree_nr)
|
||||
errs = 1;
|
||||
}
|
||||
else {
|
||||
dump_one(it, pfx, "");
|
||||
if (hashcmp(it->sha1, ref->sha1) ||
|
||||
ref->entry_count != it->entry_count ||
|
||||
ref->subtree_nr != it->subtree_nr) {
|
||||
/* claims to be valid but is lying */
|
||||
dump_one(ref, pfx, "#(ref) ");
|
||||
errs = 1;
|
||||
}
|
||||
|
|
Загрузка…
Ссылка в новой задаче