diff --git a/diff-lib.c b/diff-lib.c index f759917d33ea174800b57e34c5f192ad43977f49..c9998f4c91087271c36703b6dbe8a67d872624c5 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 08bbd3e9070996b38f4d34cedf7640d93aa5808d..3bfb4a19d2cc758f5937199cc37b05307c87d85d 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 15fcecdecd9b033700902de44138eafdb66932eb..471f606a926242ab48957746dcc33f7ff0a41418 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 f1e650ac39c59dfed692a8c373ed9ffef83f45f6..9811d467da5a54eefd68ecfad4c68b67e5b3734f 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 685adb4b7723a510fc90f09c1a65a1382bff8c74..74cabc36ff09255819558c38b25a38a4e6871418 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);