{"thread":{"id":"19455","subject":"[PATCH 0/5] Speeding up \"git status\" and \"git diff --cached\"","startedAt":"2009-05-23T19:24:33Z","lastAt":"2009-05-25T10:59:17Z","messageCount":7,"participants":["Junio C Hamano","Jeff King"],"isPatch":true,"patchVersion":1,"patchTotal":5},"messages":[{"id":"114524","messageId":"1243106678-6343-1-git-send-email-gitster@pobox.com","threadId":"19455","inReplyTo":null,"subject":"[PATCH 0/5] Speeding up \"git status\" and \"git diff --cached\"","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2009-05-23T19:24:33Z","receivedAt":"2009-05-23T19:24:33Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Here is a refined version of an earlier patch series to use cache-tree\ninformation to optimize comparison between the index with an existing\ncommit.\n\nRecently there was a discussion to auto-commit any change to the index\nthat are going to be discarded to some kind of \"trash\" branch; I am not\npersonally interested in writing a patch to do so, but this will also help\nsuch a feature by making it cheaper to see if there are differences worth\nsaving.\n\nJunio C Hamano (5):\n  write-tree --ignore-cache-tree\n  cache-tree.c::cache_tree_find(): simplify inernal API\n  t4007: modernize the style\n  Optimize \"diff-index --cached\" using cache-tree\n  Avoid \"diff-index --cached\" optimization under --find-copies-harder\n\n builtin-write-tree.c |   12 ++++++--\n cache-tree.c         |   44 +++++++++++++++++++++++++++--\n cache-tree.h         |   10 ++++++-\n diff-lib.c           |    3 ++\n t/t4007-rename-3.sh  |   74 +++++++++++++++++++++++++-------------------------\n unpack-trees.c       |   17 +++++++++++\n unpack-trees.h       |    1 +\n 7 files changed, 117 insertions(+), 44 deletions(-)\n"},{"id":"114526","messageId":"1243106678-6343-2-git-send-email-gitster@pobox.com","threadId":"19455","inReplyTo":"1243106678-6343-1-git-send-email-gitster@pobox.com","subject":"[PATCH 1/5] write-tree --ignore-cache-tree","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2009-05-23T19:24:34Z","receivedAt":"2009-05-23T19:24:34Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"This allows you to discard the cache-tree information before writing the\ntree out of the index (i.e. it always recomputes the tree object names for\nall the subtrees).\n\nThis is only useful as a debug option, so I did not bother documenting it.\n\nSigned-off-by: Junio C Hamano <gitster@pobox.com>\n---\n builtin-write-tree.c |   12 +++++++++---\n cache-tree.c         |   10 +++++++---\n cache-tree.h         |    7 ++++++-\n 3 files changed, 22 insertions(+), 7 deletions(-)\n\ndiff --git a/builtin-write-tree.c b/builtin-write-tree.c\nindex 9d64050..3a24ce8 100644\n--- a/builtin-write-tree.c\n+++ b/builtin-write-tree.c\n@@ -13,7 +13,7 @@ static const char write_tree_usage[] =\n \n int cmd_write_tree(int argc, const char **argv, const char *unused_prefix)\n {\n-\tint missing_ok = 0, ret;\n+\tint flags = 0, ret;\n \tconst char *prefix = NULL;\n \tunsigned char sha1[20];\n \tconst char *me = \"git-write-tree\";\n@@ -22,9 +22,15 @@ int cmd_write_tree(int argc, const char **argv, const char *unused_prefix)\n \twhile (1 < argc) {\n \t\tconst char *arg = argv[1];\n \t\tif (!strcmp(arg, \"--missing-ok\"))\n-\t\t\tmissing_ok = 1;\n+\t\t\tflags |= WRITE_TREE_MISSING_OK;\n \t\telse if (!prefixcmp(arg, \"--prefix=\"))\n \t\t\tprefix = arg + 9;\n+\t\telse if (!prefixcmp(arg, \"--ignore-cache-tree\"))\n+\t\t\t/*\n+\t\t\t * This is only useful for debugging, so I\n+\t\t\t * do not bother documenting it.\n+\t\t\t */\n+\t\t\tflags |= WRITE_TREE_IGNORE_CACHE_TREE;\n \t\telse\n \t\t\tusage(write_tree_usage);\n \t\targc--; argv++;\n@@ -33,7 +39,7 @@ int cmd_write_tree(int argc, const char **argv, const char *unused_prefix)\n \tif (argc > 2)\n \t\tdie(\"too many options\");\n \n-\tret = write_cache_as_tree(sha1, missing_ok, prefix);\n+\tret = write_cache_as_tree(sha1, flags, prefix);\n \tswitch (ret) {\n \tcase 0:\n \t\tprintf(\"%s\\n\", sha1_to_hex(sha1));\ndiff --git a/cache-tree.c b/cache-tree.c\nindex 37bf35e..6dd8411 100644\n--- a/cache-tree.c\n+++ b/cache-tree.c\n@@ -538,28 +538,32 @@ static struct cache_tree *cache_tree_find(struct cache_tree *it, const char *pat\n \treturn it;\n }\n \n-int write_cache_as_tree(unsigned char *sha1, int missing_ok, const char *prefix)\n+int write_cache_as_tree(unsigned char *sha1, int flags, const char *prefix)\n {\n \tint entries, was_valid, newfd;\n+\tstruct lock_file *lock_file;\n \n \t/*\n \t * We can't free this memory, it becomes part of a linked list\n \t * parsed atexit()\n \t */\n-\tstruct lock_file *lock_file = xcalloc(1, sizeof(struct lock_file));\n+\tlock_file = xcalloc(1, sizeof(struct lock_file));\n \n \tnewfd = hold_locked_index(lock_file, 1);\n \n \tentries = read_cache();\n \tif (entries < 0)\n \t\treturn WRITE_TREE_UNREADABLE_INDEX;\n+\tif (flags & WRITE_TREE_IGNORE_CACHE_TREE)\n+\t\tcache_tree_free(&(active_cache_tree));\n \n \tif (!active_cache_tree)\n \t\tactive_cache_tree = cache_tree();\n \n \twas_valid = cache_tree_fully_valid(active_cache_tree);\n-\n \tif (!was_valid) {\n+\t\tint missing_ok = flags & WRITE_TREE_MISSING_OK;\n+\n \t\tif (cache_tree_update(active_cache_tree,\n \t\t\t\t      active_cache, active_nr,\n \t\t\t\t      missing_ok, 0) < 0)\ndiff --git a/cache-tree.h b/cache-tree.h\nindex e958835..eadcad8 100644\n--- a/cache-tree.h\n+++ b/cache-tree.h\n@@ -30,11 +30,16 @@ struct cache_tree *cache_tree_read(const char *buffer, unsigned long size);\n int cache_tree_fully_valid(struct cache_tree *);\n int cache_tree_update(struct cache_tree *, struct cache_entry **, int, int, int);\n \n+/* bitmasks to write_cache_as_tree flags */\n+#define WRITE_TREE_MISSING_OK 1\n+#define WRITE_TREE_IGNORE_CACHE_TREE 2\n+\n+/* error return codes */\n #define WRITE_TREE_UNREADABLE_INDEX (-1)\n #define WRITE_TREE_UNMERGED_INDEX (-2)\n #define WRITE_TREE_PREFIX_ERROR (-3)\n \n-int write_cache_as_tree(unsigned char *sha1, int missing_ok, const char *prefix);\n+int write_cache_as_tree(unsigned char *sha1, int flags, const char *prefix);\n void prime_cache_tree(struct cache_tree **, struct tree *);\n \n #endif\n-- \n1.6.3.1.145.gb74d77\n"},{"id":"114525","messageId":"1243106678-6343-3-git-send-email-gitster@pobox.com","threadId":"19455","inReplyTo":"1243106678-6343-2-git-send-email-gitster@pobox.com","subject":"[PATCH 2/5] cache-tree.c::cache_tree_find(): simplify inernal API","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2009-05-23T19:24:35Z","receivedAt":"2009-05-23T19:24:35Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Earlier cache_tree_find() needs to be called with a valid cache_tree,\nbut repeated look-up may find an invalid or missing cache_tree in between.\nHelp simplify the callers by returning NULL to mean \"nothing appropriate\nfound\" when the input is NULL.\n\nSigned-off-by: Junio C Hamano <gitster@pobox.com>\n---\n cache-tree.c |    2 ++\n 1 files changed, 2 insertions(+), 0 deletions(-)\n\ndiff --git a/cache-tree.c b/cache-tree.c\nindex 6dd8411..5481e43 100644\n--- a/cache-tree.c\n+++ b/cache-tree.c\n@@ -514,6 +514,8 @@ struct cache_tree *cache_tree_read(const char *buffer, unsigned long size)\n \n static struct cache_tree *cache_tree_find(struct cache_tree *it, const char *path)\n {\n+\tif (!it)\n+\t\treturn NULL;\n \twhile (*path) {\n \t\tconst char *slash;\n \t\tstruct cache_tree_sub *sub;\n-- \n1.6.3.1.145.gb74d77\n"},{"id":"114529","messageId":"1243106678-6343-4-git-send-email-gitster@pobox.com","threadId":"19455","inReplyTo":"1243106678-6343-3-git-send-email-gitster@pobox.com","subject":"[PATCH 3/5] t4007: modernize the style","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2009-05-23T19:24:36Z","receivedAt":"2009-05-23T19:24:36Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"This is one of the oldest scripts; update it to match more modern style.\nNotably, we should:\n\n - Put the test title on the same line as the \"test_expect_success\", and\n   end the line with a single-quote to begin the body of the test which is\n   one multi-line string; and\n\n - Run as many commands inside test_expect_success, not outside, to catch\n   unexpected breakages.\n\nSigned-off-by: Junio C Hamano <gitster@pobox.com>\n---\n t/t4007-rename-3.sh |   69 +++++++++++++++++++++++---------------------------\n 1 files changed, 32 insertions(+), 37 deletions(-)\n\ndiff --git a/t/t4007-rename-3.sh b/t/t4007-rename-3.sh\nindex 42072d7..25e7a83 100755\n--- a/t/t4007-rename-3.sh\n+++ b/t/t4007-rename-3.sh\n@@ -9,32 +9,31 @@ test_description='Rename interaction with pathspec.\n . ./test-lib.sh\n . \"$TEST_DIRECTORY\"/diff-lib.sh ;# test-lib chdir's into trash\n \n-test_expect_success \\\n-    'prepare reference tree' \\\n-    'mkdir path0 path1 &&\n-     cp \"$TEST_DIRECTORY\"/../COPYING path0/COPYING &&\n-     git update-index --add path0/COPYING &&\n-    tree=$(git write-tree) &&\n-    echo $tree'\n-\n-test_expect_success \\\n-    'prepare work tree' \\\n-    'cp path0/COPYING path1/COPYING &&\n-     git update-index --add --remove path0/COPYING path1/COPYING'\n+test_expect_success 'prepare reference tree' '\n+\tmkdir path0 path1 &&\n+\tcp \"$TEST_DIRECTORY\"/../COPYING path0/COPYING &&\n+\tgit update-index --add path0/COPYING &&\n+\ttree=$(git write-tree) &&\n+\techo $tree\n+'\n+\n+test_expect_success 'prepare work tree' '\n+\tcp path0/COPYING path1/COPYING &&\n+\tgit update-index --add --remove path0/COPYING path1/COPYING\n+'\n \n # In the tree, there is only path0/COPYING.  In the cache, path0 and\n # path1 both have COPYING and the latter is a copy of path0/COPYING.\n # Comparing the full tree with cache should tell us so.\n \n-git diff-index -C --find-copies-harder $tree >current\n-\n cat >expected <<\\EOF\n :100644 100644 6ff87c4664981e4397625791c8ea3bbb5f2279a3 6ff87c4664981e4397625791c8ea3bbb5f2279a3 C100\tpath0/COPYING\tpath1/COPYING\n EOF\n \n-test_expect_success \\\n-    'validate the result (#1)' \\\n-    'compare_diff_raw current expected'\n+test_expect_success 'copy detection' '\n+\tgit diff-index -C --find-copies-harder $tree >current &&\n+\tcompare_diff_raw current expected\n+'\n \n # In the tree, there is only path0/COPYING.  In the cache, path0 and\n # path1 both have COPYING and the latter is a copy of path0/COPYING.\n@@ -42,49 +41,45 @@ test_expect_success \\\n # path1/COPYING suddenly appearing from nowhere, not detected as\n # a copy from path0/COPYING.\n \n-git diff-index -C $tree path1 >current\n-\n cat >expected <<\\EOF\n :000000 100644 0000000000000000000000000000000000000000 6ff87c4664981e4397625791c8ea3bbb5f2279a3 A\tpath1/COPYING\n EOF\n \n-test_expect_success \\\n-    'validate the result (#2)' \\\n-    'compare_diff_raw current expected'\n-\n-test_expect_success \\\n-    'tweak work tree' \\\n-    'rm -f path0/COPYING &&\n-     git update-index --remove path0/COPYING'\n+test_expect_success 'copy, limited to a subtree' '\n+\tgit diff-index -C --find-copies-harder $tree path1 >current &&\n+\tcompare_diff_raw current expected\n+'\n \n+test_expect_success 'tweak work tree' '\n+\trm -f path0/COPYING &&\n+\tgit update-index --remove path0/COPYING\n+'\n # In the tree, there is only path0/COPYING.  In the cache, path0 does\n # not have COPYING anymore and path1 has COPYING which is a copy of\n # path0/COPYING.  Showing the full tree with cache should tell us about\n # the rename.\n \n-git diff-index -C $tree >current\n-\n cat >expected <<\\EOF\n :100644 100644 6ff87c4664981e4397625791c8ea3bbb5f2279a3 6ff87c4664981e4397625791c8ea3bbb5f2279a3 R100\tpath0/COPYING\tpath1/COPYING\n EOF\n \n-test_expect_success \\\n-    'validate the result (#3)' \\\n-    'compare_diff_raw current expected'\n+test_expect_success 'rename detection' '\n+\tgit diff-index -C --find-copies-harder $tree >current &&\n+\tcompare_diff_raw current expected\n+'\n \n # In the tree, there is only path0/COPYING.  In the cache, path0 does\n # not have COPYING anymore and path1 has COPYING which is a copy of\n # path0/COPYING.  When we say we care only about path1, we should just\n # see path1/COPYING appearing from nowhere.\n \n-git diff-index -C $tree path1 >current\n-\n cat >expected <<\\EOF\n :000000 100644 0000000000000000000000000000000000000000 6ff87c4664981e4397625791c8ea3bbb5f2279a3 A\tpath1/COPYING\n EOF\n \n-test_expect_success \\\n-    'validate the result (#4)' \\\n-    'compare_diff_raw current expected'\n+test_expect_success 'rename, limited to a subtree' '\n+\tgit diff-index -C --find-copies-harder $tree path1 >current &&\n+\tcompare_diff_raw current expected\n+'\n \n test_done\n-- \n1.6.3.1.145.gb74d77\n"},{"id":"114527","messageId":"1243106678-6343-5-git-send-email-gitster@pobox.com","threadId":"19455","inReplyTo":"1243106678-6343-4-git-send-email-gitster@pobox.com","subject":"[PATCH 4/5] Optimize \"diff-index --cached\" using cache-tree","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2009-05-23T19:24:37Z","receivedAt":"2009-05-23T19:24:37Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"When running \"diff-index --cached\" after making a change to only a small\nportion of the index, there is no point unpacking unchanged subtrees into\nthe index recursively, only to find that all entries match anyway.  Tweak\nunpack_trees() logic that is used to read in the tree object to catch the\ncase where the tree entry we are looking at matches the index as a whole\nby looking at the cache-tree.\n\nAs an exercise, after modifying a few paths in the kernel tree, here are\na few numbers on my Athlon 64X2 3800+:\n\n    (without patch, hot cache)\n    $ /usr/bin/time git diff --cached --raw\n    :100644 100644 b57e1f5... e69de29... M  Makefile\n    :100644 000000 8c86b72... 0000000... D  arch/x86/Makefile\n    :000000 100644 0000000... e69de29... A  arche\n    0.07user 0.02system 0:00.09elapsed 102%CPU (0avgtext+0avgdata 0maxresident)k\n    0inputs+0outputs (0major+9407minor)pagefaults 0swaps\n\n    (with patch, hot cache)\n    $ /usr/bin/time ../git.git/git-diff --cached --raw\n    :100644 100644 b57e1f5... e69de29... M  Makefile\n    :100644 000000 8c86b72... 0000000... D  arch/x86/Makefile\n    :000000 100644 0000000... e69de29... A  arche\n    0.02user 0.00system 0:00.02elapsed 103%CPU (0avgtext+0avgdata 0maxresident)k\n    0inputs+0outputs (0major+2446minor)pagefaults 0swaps\n\nCold cache numbers are very impressive, but it does not matter very much\nin practice:\n\n    (without patch, cold cache)\n    $ su root sh -c 'echo 3 >/proc/sys/vm/drop_caches'\n    $ /usr/bin/time git diff --cached --raw\n    :100644 100644 b57e1f5... e69de29... M  Makefile\n    :100644 000000 8c86b72... 0000000... D  arch/x86/Makefile\n    :000000 100644 0000000... e69de29... A  arche\n    0.06user 0.17system 0:10.26elapsed 2%CPU (0avgtext+0avgdata 0maxresident)k\n    247032inputs+0outputs (1172major+8237minor)pagefaults 0swaps\n\n    (with patch, cold cache)\n    $ su root sh -c 'echo 3 >/proc/sys/vm/drop_caches'\n    $ /usr/bin/time ../git.git/git-diff --cached --raw\n    :100644 100644 b57e1f5... e69de29... M  Makefile\n    :100644 000000 8c86b72... 0000000... D  arch/x86/Makefile\n    :000000 100644 0000000... e69de29... A  arche\n    0.02user 0.01system 0:01.01elapsed 3%CPU (0avgtext+0avgdata 0maxresident)k\n    18440inputs+0outputs (79major+2369minor)pagefaults 0swaps\n\nThis of course helps \"git status\" as well.\n\n    (without patch, hot cache)\n    $ /usr/bin/time ../git.git/git-status >/dev/null\n    0.17user 0.18system 0:00.35elapsed 100%CPU (0avgtext+0avgdata 0maxresident)k\n    0inputs+5336outputs (0major+10970minor)pagefaults 0swaps\n\n    (with patch, hot cache)\n    $ /usr/bin/time ../git.git/git-status >/dev/null\n    0.10user 0.16system 0:00.27elapsed 99%CPU (0avgtext+0avgdata 0maxresident)k\n    0inputs+5336outputs (0major+3921minor)pagefaults 0swaps\n\nSigned-off-by: Junio C Hamano <gitster@pobox.com>\n---\n cache-tree.c   |   32 ++++++++++++++++++++++++++++++++\n cache-tree.h   |    3 +++\n diff-lib.c     |    2 ++\n unpack-trees.c |   17 +++++++++++++++++\n unpack-trees.h |    1 +\n 5 files changed, 55 insertions(+), 0 deletions(-)\n\ndiff --git a/cache-tree.c b/cache-tree.c\nindex 5481e43..16a65df 100644\n--- a/cache-tree.c\n+++ b/cache-tree.c\n@@ -631,3 +631,35 @@ void prime_cache_tree(struct cache_tree **it, struct tree *tree)\n \t*it = cache_tree();\n \tprime_cache_tree_rec(*it, tree);\n }\n+\n+/*\n+ * find the cache_tree that corresponds to the current level without\n+ * exploding the full path into textual form.  The root of the\n+ * cache tree is given as \"root\", and our current level is \"info\".\n+ * (1) When at root level, info->prev is NULL, so it is \"root\" itself.\n+ * (2) Otherwise, find the cache_tree that corresponds to one level\n+ *     above us, and find ourselves in there.\n+ */\n+static struct cache_tree *find_cache_tree_from_traversal(struct cache_tree *root,\n+\t\t\t\t\t\t\t struct traverse_info *info)\n+{\n+\tstruct cache_tree *our_parent;\n+\n+\tif (!info->prev)\n+\t\treturn root;\n+\tour_parent = find_cache_tree_from_traversal(root, info->prev);\n+\treturn cache_tree_find(our_parent, info->name.path);\n+}\n+\n+int cache_tree_matches_traversal(struct cache_tree *root,\n+\t\t\t\t struct name_entry *ent,\n+\t\t\t\t struct traverse_info *info)\n+{\n+\tstruct cache_tree *it;\n+\n+\tit = find_cache_tree_from_traversal(root, info);\n+\tit = cache_tree_find(it, ent->path);\n+\tif (it && it->entry_count > 0 && !hashcmp(ent->sha1, it->sha1))\n+\t\treturn it->entry_count;\n+\treturn 0;\n+}\ndiff --git a/cache-tree.h b/cache-tree.h\nindex eadcad8..3df641f 100644\n--- a/cache-tree.h\n+++ b/cache-tree.h\n@@ -2,6 +2,7 @@\n #define CACHE_TREE_H\n \n #include \"tree.h\"\n+#include \"tree-walk.h\"\n \n struct cache_tree;\n struct cache_tree_sub {\n@@ -42,4 +43,6 @@ int cache_tree_update(struct cache_tree *, struct cache_entry **, int, int, int)\n int write_cache_as_tree(unsigned char *sha1, int flags, const char *prefix);\n void prime_cache_tree(struct cache_tree **, struct tree *);\n \n+extern int cache_tree_matches_traversal(struct cache_tree *, struct name_entry *ent, struct traverse_info *info);\n+\n #endif\ndiff --git a/diff-lib.c b/diff-lib.c\nindex a310fb2..1cb97af 100644\n--- a/diff-lib.c\n+++ b/diff-lib.c\n@@ -446,6 +446,7 @@ int run_diff_index(struct rev_info *revs, int cached)\n \tmemset(&opts, 0, sizeof(opts));\n \topts.head_idx = 1;\n \topts.index_only = cached;\n+\topts.diff_index_cached = cached;\n \topts.merge = 1;\n \topts.fn = oneway_diff;\n \topts.unpack_data = revs;\n@@ -502,6 +503,7 @@ int do_diff_cache(const unsigned char *tree_sha1, struct diff_options *opt)\n \tmemset(&opts, 0, sizeof(opts));\n \topts.head_idx = 1;\n \topts.index_only = 1;\n+\topts.diff_index_cached = 1;\n \topts.merge = 1;\n \topts.fn = oneway_diff;\n \topts.unpack_data = &revs;\ndiff --git a/unpack-trees.c b/unpack-trees.c\nindex aaacaf1..8eb3ddb 100644\n--- a/unpack-trees.c\n+++ b/unpack-trees.c\n@@ -326,6 +326,23 @@ static int unpack_callback(int n, unsigned long mask, unsigned long dirmask, str\n \t\t\tif (src[0])\n \t\t\t\tconflicts |= 1;\n \t\t}\n+\n+\t\t/* special case: \"diff-index --cached\" looking at a tree */\n+\t\tif (o->diff_index_cached &&\n+\t\t    n == 1 && dirmask == 1 && S_ISDIR(names->mode)) {\n+\t\t\tint matches;\n+\t\t\tmatches = cache_tree_matches_traversal(o->src_index->cache_tree,\n+\t\t\t\t\t\t\t       names, info);\n+\t\t\t/*\n+\t\t\t * Everything under the name matches.  Adjust o->pos to\n+\t\t\t * skip the entire hierarchy.\n+\t\t\t */\n+\t\t\tif (matches) {\n+\t\t\t\to->pos += matches;\n+\t\t\t\treturn mask;\n+\t\t\t}\n+\t\t}\n+\n \t\tif (traverse_trees_recursive(n, dirmask, conflicts,\n \t\t\t\t\t     names, info) < 0)\n \t\t\treturn -1;\ndiff --git a/unpack-trees.h b/unpack-trees.h\nindex 0d26f3d..1e0e232 100644\n--- a/unpack-trees.h\n+++ b/unpack-trees.h\n@@ -27,6 +27,7 @@ struct unpack_trees_options {\n \t\t     aggressive:1,\n \t\t     skip_unmerged:1,\n \t\t     initial_checkout:1,\n+\t\t     diff_index_cached:1,\n \t\t     gently:1;\n \tconst char *prefix;\n \tint pos;\n-- \n1.6.3.1.145.gb74d77\n"},{"id":"114528","messageId":"1243106678-6343-6-git-send-email-gitster@pobox.com","threadId":"19455","inReplyTo":"1243106678-6343-5-git-send-email-gitster@pobox.com","subject":"[PATCH 5/5] Avoid \"diff-index --cached\" optimization under --find-copies-harder","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2009-05-23T19:24:38Z","receivedAt":"2009-05-23T19:24:38Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"When find-copies-harder is in effect, the diff frontends are expected to\nfeed all paths, not just changed paths, to the diffcore, so that copy\nsources can be picked up.  In such a case, not descending into subtrees\nusing the cache-tree information is simply wrong.\n\nSigned-off-by: Junio C Hamano <gitster@pobox.com>\n---\n diff-lib.c          |    5 +++--\n t/t4007-rename-3.sh |    5 +++++\n 2 files changed, 8 insertions(+), 2 deletions(-)\n\ndiff --git a/diff-lib.c b/diff-lib.c\nindex 1cb97af..ae75eac 100644\n--- a/diff-lib.c\n+++ b/diff-lib.c\n@@ -446,7 +446,8 @@ int run_diff_index(struct rev_info *revs, int cached)\n \tmemset(&opts, 0, sizeof(opts));\n \topts.head_idx = 1;\n \topts.index_only = cached;\n-\topts.diff_index_cached = cached;\n+\topts.diff_index_cached = (cached &&\n+\t\t\t\t  !DIFF_OPT_TST(&revs->diffopt, FIND_COPIES_HARDER));\n \topts.merge = 1;\n \topts.fn = oneway_diff;\n \topts.unpack_data = revs;\n@@ -503,7 +504,7 @@ int do_diff_cache(const unsigned char *tree_sha1, struct diff_options *opt)\n \tmemset(&opts, 0, sizeof(opts));\n \topts.head_idx = 1;\n \topts.index_only = 1;\n-\topts.diff_index_cached = 1;\n+\topts.diff_index_cached = !DIFF_OPT_TST(opt, FIND_COPIES_HARDER);\n \topts.merge = 1;\n \topts.fn = oneway_diff;\n \topts.unpack_data = &revs;\ndiff --git a/t/t4007-rename-3.sh b/t/t4007-rename-3.sh\nindex 25e7a83..11502b7 100755\n--- a/t/t4007-rename-3.sh\n+++ b/t/t4007-rename-3.sh\n@@ -35,6 +35,11 @@ test_expect_success 'copy detection' '\n \tcompare_diff_raw current expected\n '\n \n+test_expect_success 'copy detection, cached' '\n+\tgit diff-index -C --find-copies-harder --cached $tree >current &&\n+\tcompare_diff_raw current expected\n+'\n+\n # In the tree, there is only path0/COPYING.  In the cache, path0 and\n # path1 both have COPYING and the latter is a copy of path0/COPYING.\n # However when we say we care only about path1, we should just see\n-- \n1.6.3.1.145.gb74d77\n"},{"id":"114656","messageId":"20090525105917.GA27070@coredump.intra.peff.net","threadId":"19455","inReplyTo":"1243106678-6343-1-git-send-email-gitster@pobox.com","subject":"Re: [PATCH 0/5] Speeding up \"git status\" and \"git diff --cached\"","fromName":"Jeff King","fromEmail":"peff@peff.net","sentAt":"2009-05-25T10:59:17Z","receivedAt":"2009-05-25T10:59:17Z","isPatch":true,"sender":{"key":"peff@peff.net","avatar":"https://avatars.githubusercontent.com/u/45925?v=4"},"body":"On Sat, May 23, 2009 at 12:24:33PM -0700, Junio C Hamano wrote:\n\n> Here is a refined version of an earlier patch series to use cache-tree\n> information to optimize comparison between the index with an existing\n> commit.\n\nI just read through the series, and it looks sane to me. My only concern\nwould be that we are forgetting some option that needs to disable the\noptimization (in the same way that find-copies-harder) does, but I\ncouldn't think of one. And that is, after all, what cooking it in next\nis for. :)\n\nBut I am not terribly familiar with the cache-trees corner of the code,\nso take my endorsement with a grain of salt.\n\nThere is a minor typo in the subject of 2/5.\n\n-Peff\n"}]}