Merge branch 'jc/cache-tree'
authorJunio C Hamano <gitster@pobox.com>
Sun, 21 Jun 2009 04:47:30 +0000 (21:47 -0700)
committerJunio C Hamano <gitster@pobox.com>
Sun, 21 Jun 2009 04:47:30 +0000 (21:47 -0700)
* 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

builtin-write-tree.c
cache-tree.c
cache-tree.h
diff-lib.c
t/t4007-rename-3.sh
unpack-trees.c
unpack-trees.h
index 9d640508dd8eb62201b286490b7f83486470d611..3a24ce8157be7c4209d831027693653b8d65ca13 100644 (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 missing_ok = 0, ret;
+       int flags = 0, ret;
        const char *prefix = NULL;
        unsigned char sha1[20];
        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) {
                const char *arg = argv[1];
                if (!strcmp(arg, "--missing-ok"))
-                       missing_ok = 1;
+                       flags |= WRITE_TREE_MISSING_OK;
                else if (!prefixcmp(arg, "--prefix="))
                        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
                        usage(write_tree_usage);
                argc--; argv++;
@@ -33,7 +39,7 @@ int cmd_write_tree(int argc, const char **argv, const char *unused_prefix)
        if (argc > 2)
                die("too many options");
 
-       ret = write_cache_as_tree(sha1, missing_ok, prefix);
+       ret = write_cache_as_tree(sha1, flags, prefix);
        switch (ret) {
        case 0:
                printf("%s\n", sha1_to_hex(sha1));
index 37bf35e636f0966662416f0693dbea5c1cc73311..16a65dfac11eccc68acb4a0ed472761f3f84f9c4 100644 (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)
 {
+       if (!it)
+               return NULL;
        while (*path) {
                const char *slash;
                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;
 }
 
-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;
+       struct lock_file *lock_file;
 
        /*
         * We can't free this memory, it becomes part of a linked list
         * 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);
 
        entries = read_cache();
        if (entries < 0)
                return WRITE_TREE_UNREADABLE_INDEX;
+       if (flags & WRITE_TREE_IGNORE_CACHE_TREE)
+               cache_tree_free(&(active_cache_tree));
 
        if (!active_cache_tree)
                active_cache_tree = cache_tree();
 
        was_valid = cache_tree_fully_valid(active_cache_tree);
-
        if (!was_valid) {
+               int missing_ok = flags & WRITE_TREE_MISSING_OK;
+
                if (cache_tree_update(active_cache_tree,
                                      active_cache, active_nr,
                                      missing_ok, 0) < 0)
@@ -625,3 +631,35 @@ void prime_cache_tree(struct cache_tree **it, struct tree *tree)
        *it = cache_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;
+}
index e95883523633a51f833683e96af1738da2868238..3df641f59311f43aa951a2cdfa9f110b97b13a45 100644 (file)
@@ -2,6 +2,7 @@
 #define CACHE_TREE_H
 
 #include "tree.h"
+#include "tree-walk.h"
 
 struct cache_tree;
 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_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_UNMERGED_INDEX (-2)
 #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 *);
 
+extern int cache_tree_matches_traversal(struct cache_tree *, struct name_entry *ent, struct traverse_info *info);
+
 #endif
index 0aba6cda3c01e17b07bb7235b0395ba50256aedd..ad2a4cde74e0c2ec6367a7128aa0732f131f6bdf 100644 (file)
@@ -446,6 +446,8 @@ int run_diff_index(struct rev_info *revs, int cached)
        memset(&opts, 0, sizeof(opts));
        opts.head_idx = 1;
        opts.index_only = cached;
+       opts.diff_index_cached = (cached &&
+                                 !DIFF_OPT_TST(&revs->diffopt, FIND_COPIES_HARDER));
        opts.merge = 1;
        opts.fn = oneway_diff;
        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));
        opts.head_idx = 1;
        opts.index_only = 1;
+       opts.diff_index_cached = !DIFF_OPT_TST(opt, FIND_COPIES_HARDER);
        opts.merge = 1;
        opts.fn = oneway_diff;
        opts.unpack_data = &revs;
index 42072d724ef67c51e6a9adcf9bf8e3ca1757e053..11502b750997aba0ecfb6aa5b07c9aad1c45d592 100755 (executable)
@@ -9,32 +9,36 @@ test_description='Rename interaction with pathspec.
 . ./test-lib.sh
 . "$TEST_DIRECTORY"/diff-lib.sh ;# test-lib chdir's into trash
 
-test_expect_success \
-    'prepare reference tree' \
-    'mkdir path0 path1 &&
-     cp "$TEST_DIRECTORY"/../COPYING path0/COPYING &&
-     git update-index --add path0/COPYING &&
-    tree=$(git write-tree) &&
-    echo $tree'
-
-test_expect_success \
-    'prepare work tree' \
-    'cp path0/COPYING path1/COPYING &&
-     git update-index --add --remove path0/COPYING path1/COPYING'
+test_expect_success 'prepare reference tree' '
+       mkdir path0 path1 &&
+       cp "$TEST_DIRECTORY"/../COPYING path0/COPYING &&
+       git update-index --add path0/COPYING &&
+       tree=$(git write-tree) &&
+       echo $tree
+'
+
+test_expect_success 'prepare work tree' '
+       cp 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
 # path1 both have COPYING and the latter is a copy of path0/COPYING.
 # Comparing the full tree with cache should tell us so.
 
-git diff-index -C --find-copies-harder $tree >current
-
 cat >expected <<\EOF
 :100644 100644 6ff87c4664981e4397625791c8ea3bbb5f2279a3 6ff87c4664981e4397625791c8ea3bbb5f2279a3 C100  path0/COPYING   path1/COPYING
 EOF
 
-test_expect_success \
-    'validate the result (#1)' \
-    'compare_diff_raw current expected'
+test_expect_success 'copy detection' '
+       git diff-index -C --find-copies-harder $tree >current &&
+       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
 # 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
 # a copy from path0/COPYING.
 
-git diff-index -C $tree path1 >current
-
 cat >expected <<\EOF
 :000000 100644 0000000000000000000000000000000000000000 6ff87c4664981e4397625791c8ea3bbb5f2279a3 A     path1/COPYING
 EOF
 
-test_expect_success \
-    'validate the result (#2)' \
-    'compare_diff_raw current expected'
-
-test_expect_success \
-    'tweak work tree' \
-    'rm -f path0/COPYING &&
-     git update-index --remove path0/COPYING'
+test_expect_success 'copy, limited to a subtree' '
+       git diff-index -C --find-copies-harder $tree path1 >current &&
+       compare_diff_raw current expected
+'
 
+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
 # 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
 # the rename.
 
-git diff-index -C $tree >current
-
 cat >expected <<\EOF
 :100644 100644 6ff87c4664981e4397625791c8ea3bbb5f2279a3 6ff87c4664981e4397625791c8ea3bbb5f2279a3 R100  path0/COPYING   path1/COPYING
 EOF
 
-test_expect_success \
-    'validate the result (#3)' \
-    'compare_diff_raw current expected'
+test_expect_success 'rename detection' '
+       git diff-index -C --find-copies-harder $tree >current &&
+       compare_diff_raw current expected
+'
 
 # 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
 # path0/COPYING.  When we say we care only about path1, we should just
 # see path1/COPYING appearing from nowhere.
 
-git diff-index -C $tree path1 >current
-
 cat >expected <<\EOF
 :000000 100644 0000000000000000000000000000000000000000 6ff87c4664981e4397625791c8ea3bbb5f2279a3 A     path1/COPYING
 EOF
 
-test_expect_success \
-    'validate the result (#4)' \
-    'compare_diff_raw current expected'
+test_expect_success 'rename, limited to a subtree' '
+       git diff-index -C --find-copies-harder $tree path1 >current &&
+       compare_diff_raw current expected
+'
 
 test_done
index aaacaf1015ccf1f353151982a3018ad349663d76..8eb3ddb392499a41eaf6587b3ad717bc8a6ba041 100644 (file)
@@ -326,6 +326,23 @@ static int unpack_callback(int n, unsigned long mask, unsigned long dirmask, str
                        if (src[0])
                                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,
                                             names, info) < 0)
                        return -1;
index 0d26f3d73e773230972db86f34a5147ada881e8b..1e0e2325f150e943dd22b16c1392c136570732bc 100644 (file)
@@ -27,6 +27,7 @@ struct unpack_trees_options {
                     aggressive:1,
                     skip_unmerged:1,
                     initial_checkout:1,
+                    diff_index_cached:1,
                     gently:1;
        const char *prefix;
        int pos;