Skip to content

Commit c28a17f

Browse files
committed
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
2 parents deded16 + a0919ce commit c28a17f

File tree

7 files changed

+117
-44
lines changed

7 files changed

+117
-44
lines changed

builtin-write-tree.c

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ static const char write_tree_usage[] =
1313

1414
int cmd_write_tree(int argc, const char **argv, const char *unused_prefix)
1515
{
16-
int missing_ok = 0, ret;
16+
int flags = 0, ret;
1717
const char *prefix = NULL;
1818
unsigned char sha1[20];
1919
const char *me = "git-write-tree";
@@ -22,9 +22,15 @@ int cmd_write_tree(int argc, const char **argv, const char *unused_prefix)
2222
while (1 < argc) {
2323
const char *arg = argv[1];
2424
if (!strcmp(arg, "--missing-ok"))
25-
missing_ok = 1;
25+
flags |= WRITE_TREE_MISSING_OK;
2626
else if (!prefixcmp(arg, "--prefix="))
2727
prefix = arg + 9;
28+
else if (!prefixcmp(arg, "--ignore-cache-tree"))
29+
/*
30+
* This is only useful for debugging, so I
31+
* do not bother documenting it.
32+
*/
33+
flags |= WRITE_TREE_IGNORE_CACHE_TREE;
2834
else
2935
usage(write_tree_usage);
3036
argc--; argv++;
@@ -33,7 +39,7 @@ int cmd_write_tree(int argc, const char **argv, const char *unused_prefix)
3339
if (argc > 2)
3440
die("too many options");
3541

36-
ret = write_cache_as_tree(sha1, missing_ok, prefix);
42+
ret = write_cache_as_tree(sha1, flags, prefix);
3743
switch (ret) {
3844
case 0:
3945
printf("%s\n", sha1_to_hex(sha1));

cache-tree.c

Lines changed: 41 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -514,6 +514,8 @@ struct cache_tree *cache_tree_read(const char *buffer, unsigned long size)
514514

515515
static struct cache_tree *cache_tree_find(struct cache_tree *it, const char *path)
516516
{
517+
if (!it)
518+
return NULL;
517519
while (*path) {
518520
const char *slash;
519521
struct cache_tree_sub *sub;
@@ -538,28 +540,32 @@ static struct cache_tree *cache_tree_find(struct cache_tree *it, const char *pat
538540
return it;
539541
}
540542

541-
int write_cache_as_tree(unsigned char *sha1, int missing_ok, const char *prefix)
543+
int write_cache_as_tree(unsigned char *sha1, int flags, const char *prefix)
542544
{
543545
int entries, was_valid, newfd;
546+
struct lock_file *lock_file;
544547

545548
/*
546549
* We can't free this memory, it becomes part of a linked list
547550
* parsed atexit()
548551
*/
549-
struct lock_file *lock_file = xcalloc(1, sizeof(struct lock_file));
552+
lock_file = xcalloc(1, sizeof(struct lock_file));
550553

551554
newfd = hold_locked_index(lock_file, 1);
552555

553556
entries = read_cache();
554557
if (entries < 0)
555558
return WRITE_TREE_UNREADABLE_INDEX;
559+
if (flags & WRITE_TREE_IGNORE_CACHE_TREE)
560+
cache_tree_free(&(active_cache_tree));
556561

557562
if (!active_cache_tree)
558563
active_cache_tree = cache_tree();
559564

560565
was_valid = cache_tree_fully_valid(active_cache_tree);
561-
562566
if (!was_valid) {
567+
int missing_ok = flags & WRITE_TREE_MISSING_OK;
568+
563569
if (cache_tree_update(active_cache_tree,
564570
active_cache, active_nr,
565571
missing_ok, 0) < 0)
@@ -625,3 +631,35 @@ void prime_cache_tree(struct cache_tree **it, struct tree *tree)
625631
*it = cache_tree();
626632
prime_cache_tree_rec(*it, tree);
627633
}
634+
635+
/*
636+
* find the cache_tree that corresponds to the current level without
637+
* exploding the full path into textual form. The root of the
638+
* cache tree is given as "root", and our current level is "info".
639+
* (1) When at root level, info->prev is NULL, so it is "root" itself.
640+
* (2) Otherwise, find the cache_tree that corresponds to one level
641+
* above us, and find ourselves in there.
642+
*/
643+
static struct cache_tree *find_cache_tree_from_traversal(struct cache_tree *root,
644+
struct traverse_info *info)
645+
{
646+
struct cache_tree *our_parent;
647+
648+
if (!info->prev)
649+
return root;
650+
our_parent = find_cache_tree_from_traversal(root, info->prev);
651+
return cache_tree_find(our_parent, info->name.path);
652+
}
653+
654+
int cache_tree_matches_traversal(struct cache_tree *root,
655+
struct name_entry *ent,
656+
struct traverse_info *info)
657+
{
658+
struct cache_tree *it;
659+
660+
it = find_cache_tree_from_traversal(root, info);
661+
it = cache_tree_find(it, ent->path);
662+
if (it && it->entry_count > 0 && !hashcmp(ent->sha1, it->sha1))
663+
return it->entry_count;
664+
return 0;
665+
}

cache-tree.h

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
#define CACHE_TREE_H
33

44
#include "tree.h"
5+
#include "tree-walk.h"
56

67
struct cache_tree;
78
struct cache_tree_sub {
@@ -30,11 +31,18 @@ struct cache_tree *cache_tree_read(const char *buffer, unsigned long size);
3031
int cache_tree_fully_valid(struct cache_tree *);
3132
int cache_tree_update(struct cache_tree *, struct cache_entry **, int, int, int);
3233

34+
/* bitmasks to write_cache_as_tree flags */
35+
#define WRITE_TREE_MISSING_OK 1
36+
#define WRITE_TREE_IGNORE_CACHE_TREE 2
37+
38+
/* error return codes */
3339
#define WRITE_TREE_UNREADABLE_INDEX (-1)
3440
#define WRITE_TREE_UNMERGED_INDEX (-2)
3541
#define WRITE_TREE_PREFIX_ERROR (-3)
3642

37-
int write_cache_as_tree(unsigned char *sha1, int missing_ok, const char *prefix);
43+
int write_cache_as_tree(unsigned char *sha1, int flags, const char *prefix);
3844
void prime_cache_tree(struct cache_tree **, struct tree *);
3945

46+
extern int cache_tree_matches_traversal(struct cache_tree *, struct name_entry *ent, struct traverse_info *info);
47+
4048
#endif

diff-lib.c

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -446,6 +446,8 @@ int run_diff_index(struct rev_info *revs, int cached)
446446
memset(&opts, 0, sizeof(opts));
447447
opts.head_idx = 1;
448448
opts.index_only = cached;
449+
opts.diff_index_cached = (cached &&
450+
!DIFF_OPT_TST(&revs->diffopt, FIND_COPIES_HARDER));
449451
opts.merge = 1;
450452
opts.fn = oneway_diff;
451453
opts.unpack_data = revs;
@@ -502,6 +504,7 @@ int do_diff_cache(const unsigned char *tree_sha1, struct diff_options *opt)
502504
memset(&opts, 0, sizeof(opts));
503505
opts.head_idx = 1;
504506
opts.index_only = 1;
507+
opts.diff_index_cached = !DIFF_OPT_TST(opt, FIND_COPIES_HARDER);
505508
opts.merge = 1;
506509
opts.fn = oneway_diff;
507510
opts.unpack_data = &revs;

t/t4007-rename-3.sh

Lines changed: 37 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -9,82 +9,82 @@ test_description='Rename interaction with pathspec.
99
. ./test-lib.sh
1010
. "$TEST_DIRECTORY"/diff-lib.sh ;# test-lib chdir's into trash
1111

12-
test_expect_success \
13-
'prepare reference tree' \
14-
'mkdir path0 path1 &&
15-
cp "$TEST_DIRECTORY"/../COPYING path0/COPYING &&
16-
git update-index --add path0/COPYING &&
17-
tree=$(git write-tree) &&
18-
echo $tree'
19-
20-
test_expect_success \
21-
'prepare work tree' \
22-
'cp path0/COPYING path1/COPYING &&
23-
git update-index --add --remove path0/COPYING path1/COPYING'
12+
test_expect_success 'prepare reference tree' '
13+
mkdir path0 path1 &&
14+
cp "$TEST_DIRECTORY"/../COPYING path0/COPYING &&
15+
git update-index --add path0/COPYING &&
16+
tree=$(git write-tree) &&
17+
echo $tree
18+
'
19+
20+
test_expect_success 'prepare work tree' '
21+
cp path0/COPYING path1/COPYING &&
22+
git update-index --add --remove path0/COPYING path1/COPYING
23+
'
2424

2525
# In the tree, there is only path0/COPYING. In the cache, path0 and
2626
# path1 both have COPYING and the latter is a copy of path0/COPYING.
2727
# Comparing the full tree with cache should tell us so.
2828

29-
git diff-index -C --find-copies-harder $tree >current
30-
3129
cat >expected <<\EOF
3230
:100644 100644 6ff87c4664981e4397625791c8ea3bbb5f2279a3 6ff87c4664981e4397625791c8ea3bbb5f2279a3 C100 path0/COPYING path1/COPYING
3331
EOF
3432

35-
test_expect_success \
36-
'validate the result (#1)' \
37-
'compare_diff_raw current expected'
33+
test_expect_success 'copy detection' '
34+
git diff-index -C --find-copies-harder $tree >current &&
35+
compare_diff_raw current expected
36+
'
37+
38+
test_expect_success 'copy detection, cached' '
39+
git diff-index -C --find-copies-harder --cached $tree >current &&
40+
compare_diff_raw current expected
41+
'
3842

3943
# In the tree, there is only path0/COPYING. In the cache, path0 and
4044
# path1 both have COPYING and the latter is a copy of path0/COPYING.
4145
# However when we say we care only about path1, we should just see
4246
# path1/COPYING suddenly appearing from nowhere, not detected as
4347
# a copy from path0/COPYING.
4448

45-
git diff-index -C $tree path1 >current
46-
4749
cat >expected <<\EOF
4850
:000000 100644 0000000000000000000000000000000000000000 6ff87c4664981e4397625791c8ea3bbb5f2279a3 A path1/COPYING
4951
EOF
5052

51-
test_expect_success \
52-
'validate the result (#2)' \
53-
'compare_diff_raw current expected'
54-
55-
test_expect_success \
56-
'tweak work tree' \
57-
'rm -f path0/COPYING &&
58-
git update-index --remove path0/COPYING'
53+
test_expect_success 'copy, limited to a subtree' '
54+
git diff-index -C --find-copies-harder $tree path1 >current &&
55+
compare_diff_raw current expected
56+
'
5957

58+
test_expect_success 'tweak work tree' '
59+
rm -f path0/COPYING &&
60+
git update-index --remove path0/COPYING
61+
'
6062
# In the tree, there is only path0/COPYING. In the cache, path0 does
6163
# not have COPYING anymore and path1 has COPYING which is a copy of
6264
# path0/COPYING. Showing the full tree with cache should tell us about
6365
# the rename.
6466

65-
git diff-index -C $tree >current
66-
6767
cat >expected <<\EOF
6868
:100644 100644 6ff87c4664981e4397625791c8ea3bbb5f2279a3 6ff87c4664981e4397625791c8ea3bbb5f2279a3 R100 path0/COPYING path1/COPYING
6969
EOF
7070

71-
test_expect_success \
72-
'validate the result (#3)' \
73-
'compare_diff_raw current expected'
71+
test_expect_success 'rename detection' '
72+
git diff-index -C --find-copies-harder $tree >current &&
73+
compare_diff_raw current expected
74+
'
7475

7576
# In the tree, there is only path0/COPYING. In the cache, path0 does
7677
# not have COPYING anymore and path1 has COPYING which is a copy of
7778
# path0/COPYING. When we say we care only about path1, we should just
7879
# see path1/COPYING appearing from nowhere.
7980

80-
git diff-index -C $tree path1 >current
81-
8281
cat >expected <<\EOF
8382
:000000 100644 0000000000000000000000000000000000000000 6ff87c4664981e4397625791c8ea3bbb5f2279a3 A path1/COPYING
8483
EOF
8584

86-
test_expect_success \
87-
'validate the result (#4)' \
88-
'compare_diff_raw current expected'
85+
test_expect_success 'rename, limited to a subtree' '
86+
git diff-index -C --find-copies-harder $tree path1 >current &&
87+
compare_diff_raw current expected
88+
'
8989

9090
test_done

unpack-trees.c

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -326,6 +326,23 @@ static int unpack_callback(int n, unsigned long mask, unsigned long dirmask, str
326326
if (src[0])
327327
conflicts |= 1;
328328
}
329+
330+
/* special case: "diff-index --cached" looking at a tree */
331+
if (o->diff_index_cached &&
332+
n == 1 && dirmask == 1 && S_ISDIR(names->mode)) {
333+
int matches;
334+
matches = cache_tree_matches_traversal(o->src_index->cache_tree,
335+
names, info);
336+
/*
337+
* Everything under the name matches. Adjust o->pos to
338+
* skip the entire hierarchy.
339+
*/
340+
if (matches) {
341+
o->pos += matches;
342+
return mask;
343+
}
344+
}
345+
329346
if (traverse_trees_recursive(n, dirmask, conflicts,
330347
names, info) < 0)
331348
return -1;

unpack-trees.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ struct unpack_trees_options {
2727
aggressive:1,
2828
skip_unmerged:1,
2929
initial_checkout:1,
30+
diff_index_cached:1,
3031
gently:1;
3132
const char *prefix;
3233
int pos;

0 commit comments

Comments
 (0)