mirror of
https://github.com/git/git.git
synced 2024-11-24 10:26:17 +08:00
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:
commit
c28a17f270
@ -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));
|
||||
|
44
cache-tree.c
44
cache-tree.c
@ -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;
|
||||
}
|
||||
|
10
cache-tree.h
10
cache-tree.h
@ -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
|
||||
|
@ -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;
|
||||
|
@ -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 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 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
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
|
Loading…
Reference in New Issue
Block a user