Merge branch 'jc/cache-tree'

* jc/cache-tree:
  Avoid "diff-index --cached" optimization under --find-copies-harder
  Optimize "diff-index --cached" using cache-tree
  t4007: modernize the style
  cache-tree.c::cache_tree_find(): simplify internal API
  write-tree --ignore-cache-tree
This commit is contained in:
Junio C Hamano 2009-06-20 21:47:30 -07:00
commit c28a17f270
7 changed files with 116 additions and 43 deletions

View File

@ -13,7 +13,7 @@ static const char write_tree_usage[] =
int cmd_write_tree(int argc, const char **argv, const char *unused_prefix) int cmd_write_tree(int argc, const char **argv, const char *unused_prefix)
{ {
int missing_ok = 0, ret; int flags = 0, ret;
const char *prefix = NULL; const char *prefix = NULL;
unsigned char sha1[20]; unsigned char sha1[20];
const char *me = "git-write-tree"; const char *me = "git-write-tree";
@ -22,9 +22,15 @@ int cmd_write_tree(int argc, const char **argv, const char *unused_prefix)
while (1 < argc) { while (1 < argc) {
const char *arg = argv[1]; const char *arg = argv[1];
if (!strcmp(arg, "--missing-ok")) if (!strcmp(arg, "--missing-ok"))
missing_ok = 1; flags |= WRITE_TREE_MISSING_OK;
else if (!prefixcmp(arg, "--prefix=")) else if (!prefixcmp(arg, "--prefix="))
prefix = arg + 9; prefix = arg + 9;
else if (!prefixcmp(arg, "--ignore-cache-tree"))
/*
* This is only useful for debugging, so I
* do not bother documenting it.
*/
flags |= WRITE_TREE_IGNORE_CACHE_TREE;
else else
usage(write_tree_usage); usage(write_tree_usage);
argc--; argv++; argc--; argv++;
@ -33,7 +39,7 @@ int cmd_write_tree(int argc, const char **argv, const char *unused_prefix)
if (argc > 2) if (argc > 2)
die("too many options"); die("too many options");
ret = write_cache_as_tree(sha1, missing_ok, prefix); ret = write_cache_as_tree(sha1, flags, prefix);
switch (ret) { switch (ret) {
case 0: case 0:
printf("%s\n", sha1_to_hex(sha1)); printf("%s\n", sha1_to_hex(sha1));

View File

@ -514,6 +514,8 @@ struct cache_tree *cache_tree_read(const char *buffer, unsigned long size)
static struct cache_tree *cache_tree_find(struct cache_tree *it, const char *path) static struct cache_tree *cache_tree_find(struct cache_tree *it, const char *path)
{ {
if (!it)
return NULL;
while (*path) { while (*path) {
const char *slash; const char *slash;
struct cache_tree_sub *sub; struct cache_tree_sub *sub;
@ -538,28 +540,32 @@ static struct cache_tree *cache_tree_find(struct cache_tree *it, const char *pat
return it; return it;
} }
int write_cache_as_tree(unsigned char *sha1, int missing_ok, const char *prefix) int write_cache_as_tree(unsigned char *sha1, int flags, const char *prefix)
{ {
int entries, was_valid, newfd; int entries, was_valid, newfd;
struct lock_file *lock_file;
/* /*
* We can't free this memory, it becomes part of a linked list * We can't free this memory, it becomes part of a linked list
* parsed atexit() * parsed atexit()
*/ */
struct lock_file *lock_file = xcalloc(1, sizeof(struct lock_file)); lock_file = xcalloc(1, sizeof(struct lock_file));
newfd = hold_locked_index(lock_file, 1); newfd = hold_locked_index(lock_file, 1);
entries = read_cache(); entries = read_cache();
if (entries < 0) if (entries < 0)
return WRITE_TREE_UNREADABLE_INDEX; return WRITE_TREE_UNREADABLE_INDEX;
if (flags & WRITE_TREE_IGNORE_CACHE_TREE)
cache_tree_free(&(active_cache_tree));
if (!active_cache_tree) if (!active_cache_tree)
active_cache_tree = cache_tree(); active_cache_tree = cache_tree();
was_valid = cache_tree_fully_valid(active_cache_tree); was_valid = cache_tree_fully_valid(active_cache_tree);
if (!was_valid) { if (!was_valid) {
int missing_ok = flags & WRITE_TREE_MISSING_OK;
if (cache_tree_update(active_cache_tree, if (cache_tree_update(active_cache_tree,
active_cache, active_nr, active_cache, active_nr,
missing_ok, 0) < 0) missing_ok, 0) < 0)
@ -625,3 +631,35 @@ void prime_cache_tree(struct cache_tree **it, struct tree *tree)
*it = cache_tree(); *it = cache_tree();
prime_cache_tree_rec(*it, tree); prime_cache_tree_rec(*it, tree);
} }
/*
* find the cache_tree that corresponds to the current level without
* exploding the full path into textual form. The root of the
* cache tree is given as "root", and our current level is "info".
* (1) When at root level, info->prev is NULL, so it is "root" itself.
* (2) Otherwise, find the cache_tree that corresponds to one level
* above us, and find ourselves in there.
*/
static struct cache_tree *find_cache_tree_from_traversal(struct cache_tree *root,
struct traverse_info *info)
{
struct cache_tree *our_parent;
if (!info->prev)
return root;
our_parent = find_cache_tree_from_traversal(root, info->prev);
return cache_tree_find(our_parent, info->name.path);
}
int cache_tree_matches_traversal(struct cache_tree *root,
struct name_entry *ent,
struct traverse_info *info)
{
struct cache_tree *it;
it = find_cache_tree_from_traversal(root, info);
it = cache_tree_find(it, ent->path);
if (it && it->entry_count > 0 && !hashcmp(ent->sha1, it->sha1))
return it->entry_count;
return 0;
}

View File

@ -2,6 +2,7 @@
#define CACHE_TREE_H #define CACHE_TREE_H
#include "tree.h" #include "tree.h"
#include "tree-walk.h"
struct cache_tree; struct cache_tree;
struct cache_tree_sub { struct cache_tree_sub {
@ -30,11 +31,18 @@ struct cache_tree *cache_tree_read(const char *buffer, unsigned long size);
int cache_tree_fully_valid(struct cache_tree *); int cache_tree_fully_valid(struct cache_tree *);
int cache_tree_update(struct cache_tree *, struct cache_entry **, int, int, int); int cache_tree_update(struct cache_tree *, struct cache_entry **, int, int, int);
/* bitmasks to write_cache_as_tree flags */
#define WRITE_TREE_MISSING_OK 1
#define WRITE_TREE_IGNORE_CACHE_TREE 2
/* error return codes */
#define WRITE_TREE_UNREADABLE_INDEX (-1) #define WRITE_TREE_UNREADABLE_INDEX (-1)
#define WRITE_TREE_UNMERGED_INDEX (-2) #define WRITE_TREE_UNMERGED_INDEX (-2)
#define WRITE_TREE_PREFIX_ERROR (-3) #define WRITE_TREE_PREFIX_ERROR (-3)
int write_cache_as_tree(unsigned char *sha1, int missing_ok, const char *prefix); int write_cache_as_tree(unsigned char *sha1, int flags, const char *prefix);
void prime_cache_tree(struct cache_tree **, struct tree *); void prime_cache_tree(struct cache_tree **, struct tree *);
extern int cache_tree_matches_traversal(struct cache_tree *, struct name_entry *ent, struct traverse_info *info);
#endif #endif

View File

@ -446,6 +446,8 @@ int run_diff_index(struct rev_info *revs, int cached)
memset(&opts, 0, sizeof(opts)); memset(&opts, 0, sizeof(opts));
opts.head_idx = 1; opts.head_idx = 1;
opts.index_only = cached; opts.index_only = cached;
opts.diff_index_cached = (cached &&
!DIFF_OPT_TST(&revs->diffopt, FIND_COPIES_HARDER));
opts.merge = 1; opts.merge = 1;
opts.fn = oneway_diff; opts.fn = oneway_diff;
opts.unpack_data = revs; opts.unpack_data = revs;
@ -502,6 +504,7 @@ int do_diff_cache(const unsigned char *tree_sha1, struct diff_options *opt)
memset(&opts, 0, sizeof(opts)); memset(&opts, 0, sizeof(opts));
opts.head_idx = 1; opts.head_idx = 1;
opts.index_only = 1; opts.index_only = 1;
opts.diff_index_cached = !DIFF_OPT_TST(opt, FIND_COPIES_HARDER);
opts.merge = 1; opts.merge = 1;
opts.fn = oneway_diff; opts.fn = oneway_diff;
opts.unpack_data = &revs; opts.unpack_data = &revs;

View File

@ -9,32 +9,36 @@ test_description='Rename interaction with pathspec.
. ./test-lib.sh . ./test-lib.sh
. "$TEST_DIRECTORY"/diff-lib.sh ;# test-lib chdir's into trash . "$TEST_DIRECTORY"/diff-lib.sh ;# test-lib chdir's into trash
test_expect_success \ test_expect_success 'prepare reference tree' '
'prepare reference tree' \ mkdir path0 path1 &&
'mkdir path0 path1 && cp "$TEST_DIRECTORY"/../COPYING path0/COPYING &&
cp "$TEST_DIRECTORY"/../COPYING path0/COPYING && git update-index --add path0/COPYING &&
git update-index --add path0/COPYING && tree=$(git write-tree) &&
tree=$(git write-tree) && echo $tree
echo $tree' '
test_expect_success \ test_expect_success 'prepare work tree' '
'prepare work tree' \ cp path0/COPYING path1/COPYING &&
'cp path0/COPYING path1/COPYING && git update-index --add --remove path0/COPYING path1/COPYING
git update-index --add --remove path0/COPYING path1/COPYING' '
# In the tree, there is only path0/COPYING. In the cache, path0 and # In the tree, there is only path0/COPYING. In the cache, path0 and
# path1 both have COPYING and the latter is a copy of path0/COPYING. # path1 both have COPYING and the latter is a copy of path0/COPYING.
# Comparing the full tree with cache should tell us so. # Comparing the full tree with cache should tell us so.
git diff-index -C --find-copies-harder $tree >current
cat >expected <<\EOF cat >expected <<\EOF
:100644 100644 6ff87c4664981e4397625791c8ea3bbb5f2279a3 6ff87c4664981e4397625791c8ea3bbb5f2279a3 C100 path0/COPYING path1/COPYING :100644 100644 6ff87c4664981e4397625791c8ea3bbb5f2279a3 6ff87c4664981e4397625791c8ea3bbb5f2279a3 C100 path0/COPYING path1/COPYING
EOF EOF
test_expect_success \ test_expect_success 'copy detection' '
'validate the result (#1)' \ git diff-index -C --find-copies-harder $tree >current &&
'compare_diff_raw current expected' compare_diff_raw current expected
'
test_expect_success 'copy detection, cached' '
git diff-index -C --find-copies-harder --cached $tree >current &&
compare_diff_raw current expected
'
# In the tree, there is only path0/COPYING. In the cache, path0 and # In the tree, there is only path0/COPYING. In the cache, path0 and
# path1 both have COPYING and the latter is a copy of path0/COPYING. # path1 both have COPYING and the latter is a copy of path0/COPYING.
@ -42,49 +46,45 @@ test_expect_success \
# path1/COPYING suddenly appearing from nowhere, not detected as # path1/COPYING suddenly appearing from nowhere, not detected as
# a copy from path0/COPYING. # a copy from path0/COPYING.
git diff-index -C $tree path1 >current
cat >expected <<\EOF cat >expected <<\EOF
:000000 100644 0000000000000000000000000000000000000000 6ff87c4664981e4397625791c8ea3bbb5f2279a3 A path1/COPYING :000000 100644 0000000000000000000000000000000000000000 6ff87c4664981e4397625791c8ea3bbb5f2279a3 A path1/COPYING
EOF EOF
test_expect_success \ test_expect_success 'copy, limited to a subtree' '
'validate the result (#2)' \ git diff-index -C --find-copies-harder $tree path1 >current &&
'compare_diff_raw current expected' compare_diff_raw current expected
'
test_expect_success \
'tweak work tree' \
'rm -f path0/COPYING &&
git update-index --remove path0/COPYING'
test_expect_success 'tweak work tree' '
rm -f path0/COPYING &&
git update-index --remove path0/COPYING
'
# In the tree, there is only path0/COPYING. In the cache, path0 does # In the tree, there is only path0/COPYING. In the cache, path0 does
# not have COPYING anymore and path1 has COPYING which is a copy of # not have COPYING anymore and path1 has COPYING which is a copy of
# path0/COPYING. Showing the full tree with cache should tell us about # path0/COPYING. Showing the full tree with cache should tell us about
# the rename. # the rename.
git diff-index -C $tree >current
cat >expected <<\EOF cat >expected <<\EOF
:100644 100644 6ff87c4664981e4397625791c8ea3bbb5f2279a3 6ff87c4664981e4397625791c8ea3bbb5f2279a3 R100 path0/COPYING path1/COPYING :100644 100644 6ff87c4664981e4397625791c8ea3bbb5f2279a3 6ff87c4664981e4397625791c8ea3bbb5f2279a3 R100 path0/COPYING path1/COPYING
EOF EOF
test_expect_success \ test_expect_success 'rename detection' '
'validate the result (#3)' \ git diff-index -C --find-copies-harder $tree >current &&
'compare_diff_raw current expected' compare_diff_raw current expected
'
# In the tree, there is only path0/COPYING. In the cache, path0 does # In the tree, there is only path0/COPYING. In the cache, path0 does
# not have COPYING anymore and path1 has COPYING which is a copy of # not have COPYING anymore and path1 has COPYING which is a copy of
# path0/COPYING. When we say we care only about path1, we should just # path0/COPYING. When we say we care only about path1, we should just
# see path1/COPYING appearing from nowhere. # see path1/COPYING appearing from nowhere.
git diff-index -C $tree path1 >current
cat >expected <<\EOF cat >expected <<\EOF
:000000 100644 0000000000000000000000000000000000000000 6ff87c4664981e4397625791c8ea3bbb5f2279a3 A path1/COPYING :000000 100644 0000000000000000000000000000000000000000 6ff87c4664981e4397625791c8ea3bbb5f2279a3 A path1/COPYING
EOF EOF
test_expect_success \ test_expect_success 'rename, limited to a subtree' '
'validate the result (#4)' \ git diff-index -C --find-copies-harder $tree path1 >current &&
'compare_diff_raw current expected' compare_diff_raw current expected
'
test_done test_done

View File

@ -326,6 +326,23 @@ static int unpack_callback(int n, unsigned long mask, unsigned long dirmask, str
if (src[0]) if (src[0])
conflicts |= 1; conflicts |= 1;
} }
/* special case: "diff-index --cached" looking at a tree */
if (o->diff_index_cached &&
n == 1 && dirmask == 1 && S_ISDIR(names->mode)) {
int matches;
matches = cache_tree_matches_traversal(o->src_index->cache_tree,
names, info);
/*
* Everything under the name matches. Adjust o->pos to
* skip the entire hierarchy.
*/
if (matches) {
o->pos += matches;
return mask;
}
}
if (traverse_trees_recursive(n, dirmask, conflicts, if (traverse_trees_recursive(n, dirmask, conflicts,
names, info) < 0) names, info) < 0)
return -1; return -1;

View File

@ -27,6 +27,7 @@ struct unpack_trees_options {
aggressive:1, aggressive:1,
skip_unmerged:1, skip_unmerged:1,
initial_checkout:1, initial_checkout:1,
diff_index_cached:1,
gently:1; gently:1;
const char *prefix; const char *prefix;
int pos; int pos;