summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--diff-lib.c1
-rw-r--r--diff.c17
-rw-r--r--diff.h1
-rwxr-xr-xt/t1012-read-tree-df.sh8
-rw-r--r--unpack-trees.c120
5 files changed, 140 insertions, 7 deletions
diff --git a/diff-lib.c b/diff-lib.c
index f759917d33..c9998f4c91 100644
--- a/diff-lib.c
+++ b/diff-lib.c
@@ -425,6 +425,7 @@ int run_diff_index(struct rev_info *revs, int cached)
exit(128);
diff_set_mnemonic_prefix(&revs->diffopt, "c/", cached ? "i/" : "w/");
+ diffcore_fix_diff_index(&revs->diffopt);
diffcore_std(&revs->diffopt);
diff_flush(&revs->diffopt);
return 0;
diff --git a/diff.c b/diff.c
index 08bbd3e907..3bfb4a19d2 100644
--- a/diff.c
+++ b/diff.c
@@ -3628,6 +3628,23 @@ static void diffcore_skip_stat_unmatch(struct diff_options *diffopt)
*q = outq;
}
+static int diffnamecmp(const void *a_, const void *b_)
+{
+ const struct diff_filepair *a = *((const struct diff_filepair **)a_);
+ const struct diff_filepair *b = *((const struct diff_filepair **)b_);
+ const char *name_a, *name_b;
+
+ name_a = a->one ? a->one->path : a->two->path;
+ name_b = b->one ? b->one->path : b->two->path;
+ return strcmp(name_a, name_b);
+}
+
+void diffcore_fix_diff_index(struct diff_options *options)
+{
+ struct diff_queue_struct *q = &diff_queued_diff;
+ qsort(q->queue, q->nr, sizeof(q->queue[0]), diffnamecmp);
+}
+
void diffcore_std(struct diff_options *options)
{
if (options->skip_stat_unmatch)
diff --git a/diff.h b/diff.h
index 15fcecdecd..471f606a92 100644
--- a/diff.h
+++ b/diff.h
@@ -208,6 +208,7 @@ extern int diff_setup_done(struct diff_options *);
#define DIFF_PICKAXE_REGEX 2
extern void diffcore_std(struct diff_options *);
+extern void diffcore_fix_diff_index(struct diff_options *);
#define COMMON_DIFF_OPTIONS_HELP \
"\ncommon diff options:\n" \
diff --git a/t/t1012-read-tree-df.sh b/t/t1012-read-tree-df.sh
index f1e650ac39..9811d467da 100755
--- a/t/t1012-read-tree-df.sh
+++ b/t/t1012-read-tree-df.sh
@@ -51,7 +51,7 @@ test_expect_success setup '
:
'
-test_expect_failure '3-way (1)' '
+test_expect_success '3-way (1)' '
settree A-000 &&
git read-tree -m -u O-000 A-000 B-000 &&
checkindex <<-EOF
@@ -63,7 +63,7 @@ test_expect_failure '3-way (1)' '
EOF
'
-test_expect_failure '3-way (2)' '
+test_expect_success '3-way (2)' '
settree A-001 &&
git read-tree -m -u O-000 A-001 B-000 &&
checkindex <<-EOF
@@ -76,7 +76,7 @@ test_expect_failure '3-way (2)' '
EOF
'
-test_expect_failure '3-way (3)' '
+test_expect_success '3-way (3)' '
settree A-010 &&
git read-tree -m -u O-010 A-010 B-010 &&
checkindex <<-EOF
@@ -90,7 +90,7 @@ test_expect_failure '3-way (3)' '
EOF
'
-test_expect_failure '2-way (1)' '
+test_expect_success '2-way (1)' '
settree O-020 &&
git read-tree -m -u O-020 A-020 &&
checkindex <<-EOF
diff --git a/unpack-trees.c b/unpack-trees.c
index 685adb4b77..74cabc36ff 100644
--- a/unpack-trees.c
+++ b/unpack-trees.c
@@ -231,9 +231,37 @@ static int unpack_index_entry(struct cache_entry *ce,
return ret;
}
+static int find_cache_pos(struct traverse_info *, const struct name_entry *);
+
+static void restore_cache_bottom(struct traverse_info *info, int bottom)
+{
+ struct unpack_trees_options *o = info->data;
+
+ if (o->diff_index_cached)
+ return;
+ o->cache_bottom = bottom;
+}
+
+static int switch_cache_bottom(struct traverse_info *info)
+{
+ struct unpack_trees_options *o = info->data;
+ int ret, pos;
+
+ if (o->diff_index_cached)
+ return 0;
+ ret = o->cache_bottom;
+ pos = find_cache_pos(info->prev, &info->name);
+
+ if (pos < -1)
+ o->cache_bottom = -2 - pos;
+ else if (pos < 0)
+ o->cache_bottom = o->src_index->cache_nr;
+ return ret;
+}
+
static int traverse_trees_recursive(int n, unsigned long dirmask, unsigned long df_conflicts, struct name_entry *names, struct traverse_info *info)
{
- int i;
+ int i, ret, bottom;
struct tree_desc t[MAX_UNPACK_TREES];
struct traverse_info newinfo;
struct name_entry *p;
@@ -254,7 +282,11 @@ static int traverse_trees_recursive(int n, unsigned long dirmask, unsigned long
sha1 = names[i].sha1;
fill_tree_descriptor(t+i, sha1);
}
- return traverse_trees(n, t, &newinfo);
+
+ bottom = switch_cache_bottom(&newinfo);
+ ret = traverse_trees(n, t, &newinfo);
+ restore_cache_bottom(&newinfo, bottom);
+ return ret;
}
/*
@@ -393,6 +425,82 @@ static int unpack_failed(struct unpack_trees_options *o, const char *message)
return -1;
}
+/* NEEDSWORK: give this a better name and share with tree-walk.c */
+static int name_compare(const char *a, int a_len,
+ const char *b, int b_len)
+{
+ int len = (a_len < b_len) ? a_len : b_len;
+ int cmp = memcmp(a, b, len);
+ if (cmp)
+ return cmp;
+ return (a_len - b_len);
+}
+
+/*
+ * The tree traversal is looking at name p. If we have a matching entry,
+ * return it. If name p is a directory in the index, do not return
+ * anything, as we will want to match it when the traversal descends into
+ * the directory.
+ */
+static int find_cache_pos(struct traverse_info *info,
+ const struct name_entry *p)
+{
+ int pos;
+ struct unpack_trees_options *o = info->data;
+ struct index_state *index = o->src_index;
+ int pfxlen = info->pathlen;
+ int p_len = tree_entry_len(p->path, p->sha1);
+
+ for (pos = o->cache_bottom; pos < index->cache_nr; pos++) {
+ struct cache_entry *ce = index->cache[pos];
+ const char *ce_name, *ce_slash;
+ int cmp, ce_len;
+
+ if (!ce_in_traverse_path(ce, info))
+ continue;
+ if (ce->ce_flags & CE_UNPACKED)
+ continue;
+ ce_name = ce->name + pfxlen;
+ ce_slash = strchr(ce_name, '/');
+ if (ce_slash)
+ ce_len = ce_slash - ce_name;
+ else
+ ce_len = ce_namelen(ce) - pfxlen;
+ cmp = name_compare(p->path, p_len, ce_name, ce_len);
+ /*
+ * Exact match; if we have a directory we need to
+ * delay returning it.
+ */
+ if (!cmp)
+ return ce_slash ? -2 - pos : pos;
+ if (0 < cmp)
+ continue; /* keep looking */
+ /*
+ * ce_name sorts after p->path; could it be that we
+ * have files under p->path directory in the index?
+ * E.g. ce_name == "t-i", and p->path == "t"; we may
+ * have "t/a" in the index.
+ */
+ if (p_len < ce_len && !memcmp(ce_name, p->path, p_len) &&
+ ce_name[p_len] < '/')
+ continue; /* keep looking */
+ break;
+ }
+ return -1;
+}
+
+static struct cache_entry *find_cache_entry(struct traverse_info *info,
+ const struct name_entry *p)
+{
+ int pos = find_cache_pos(info, p);
+ struct unpack_trees_options *o = info->data;
+
+ if (0 <= pos)
+ return o->src_index->cache[pos];
+ else
+ return NULL;
+}
+
static int unpack_callback(int n, unsigned long mask, unsigned long dirmask, struct name_entry *names, struct traverse_info *info)
{
struct cache_entry *src[MAX_UNPACK_TREES + 1] = { NULL, };
@@ -406,8 +514,14 @@ static int unpack_callback(int n, unsigned long mask, unsigned long dirmask, str
/* Are we supposed to look at the index too? */
if (o->merge) {
while (1) {
- struct cache_entry *ce = next_cache_entry(o);
int cmp;
+ struct cache_entry *ce;
+
+ if (o->diff_index_cached)
+ ce = next_cache_entry(o);
+ else
+ ce = find_cache_entry(info, p);
+
if (!ce)
break;
cmp = compare_entry(ce, info, p);