summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--cache-tree.c32
-rw-r--r--cache-tree.h3
-rw-r--r--diff-lib.c2
-rw-r--r--unpack-trees.c17
-rw-r--r--unpack-trees.h1
5 files changed, 55 insertions, 0 deletions
diff --git a/cache-tree.c b/cache-tree.c
index 5481e43340..16a65dfac1 100644
--- a/cache-tree.c
+++ b/cache-tree.c
@@ -631,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;
+}
diff --git a/cache-tree.h b/cache-tree.h
index eadcad8adc..3df641f593 100644
--- a/cache-tree.h
+++ b/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 {
@@ -42,4 +43,6 @@ int cache_tree_update(struct cache_tree *, struct cache_entry **, int, int, int)
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
diff --git a/diff-lib.c b/diff-lib.c
index a310fb2ad0..1cb97af22d 100644
--- a/diff-lib.c
+++ b/diff-lib.c
@@ -446,6 +446,7 @@ 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;
opts.merge = 1;
opts.fn = oneway_diff;
opts.unpack_data = revs;
@@ -502,6 +503,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 = 1;
opts.merge = 1;
opts.fn = oneway_diff;
opts.unpack_data = &revs;
diff --git a/unpack-trees.c b/unpack-trees.c
index aaacaf1015..8eb3ddb392 100644
--- a/unpack-trees.c
+++ b/unpack-trees.c
@@ -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;
diff --git a/unpack-trees.h b/unpack-trees.h
index 0d26f3d73e..1e0e2325f1 100644
--- a/unpack-trees.h
+++ b/unpack-trees.h
@@ -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;