diff options
author | Junio C Hamano <gitster@pobox.com> | 2008-03-11 22:13:44 -0700 |
---|---|---|
committer | Junio C Hamano <gitster@pobox.com> | 2008-03-11 22:13:44 -0700 |
commit | b85997d14d1873a4baa3d04966cd5947e6134acd (patch) | |
tree | b21c76c4bc0d3fbb4221283334065216e17beff1 | |
parent | Merge branch 'maint' (diff) | |
parent | unpack_trees(): fix diff-index regression. (diff) | |
download | tgif-b85997d14d1873a4baa3d04966cd5947e6134acd.tar.xz |
Merge branch 'lt/unpack-trees'
* lt/unpack-trees:
unpack_trees(): fix diff-index regression.
traverse_trees_recursive(): propagate merge errors up
unpack_trees(): minor memory leak fix in unused destination index
Make 'unpack_trees()' have a separate source and destination index
Make 'unpack_trees()' take the index to work on as an argument
Add 'const' where appropriate to index handling functions
Fix tree-walking compare_entry() in the presense of --prefix
Move 'unpack_trees()' over to 'traverse_trees()' interface
Make 'traverse_trees()' traverse conflicting DF entries in parallel
Add return value to 'traverse_tree()' callback
Make 'traverse_tree()' use linked structure rather than 'const char *base'
Add 'df_name_compare()' helper function
-rw-r--r-- | builtin-checkout.c | 9 | ||||
-rw-r--r-- | builtin-commit.c | 2 | ||||
-rw-r--r-- | builtin-merge-recursive.c | 2 | ||||
-rw-r--r-- | builtin-read-tree.c | 23 | ||||
-rw-r--r-- | cache.h | 11 | ||||
-rw-r--r-- | diff-lib.c | 55 | ||||
-rw-r--r-- | hash.c | 6 | ||||
-rw-r--r-- | hash.h | 4 | ||||
-rw-r--r-- | merge-tree.c | 58 | ||||
-rw-r--r-- | read-cache.c | 47 | ||||
-rwxr-xr-x | t/t1005-read-tree-reset.sh | 2 | ||||
-rw-r--r-- | tree-walk.c | 62 | ||||
-rw-r--r-- | tree-walk.h | 23 | ||||
-rw-r--r-- | unpack-trees.c | 636 | ||||
-rw-r--r-- | unpack-trees.h | 15 |
15 files changed, 503 insertions, 452 deletions
diff --git a/builtin-checkout.c b/builtin-checkout.c index 6b08016228..7deb504837 100644 --- a/builtin-checkout.c +++ b/builtin-checkout.c @@ -152,6 +152,7 @@ static int reset_to_new(struct tree *tree, int quiet) { struct unpack_trees_options opts; struct tree_desc tree_desc; + memset(&opts, 0, sizeof(opts)); opts.head_idx = -1; opts.update = 1; @@ -159,6 +160,8 @@ static int reset_to_new(struct tree *tree, int quiet) opts.merge = 1; opts.fn = oneway_merge; opts.verbose_update = !quiet; + opts.src_index = &the_index; + opts.dst_index = &the_index; parse_tree(tree); init_tree_desc(&tree_desc, tree->buffer, tree->size); if (unpack_trees(1, &tree_desc, &opts)) @@ -170,6 +173,7 @@ static void reset_clean_to_new(struct tree *tree, int quiet) { struct unpack_trees_options opts; struct tree_desc tree_desc; + memset(&opts, 0, sizeof(opts)); opts.head_idx = -1; opts.skip_unmerged = 1; @@ -177,6 +181,8 @@ static void reset_clean_to_new(struct tree *tree, int quiet) opts.merge = 1; opts.fn = oneway_merge; opts.verbose_update = !quiet; + opts.src_index = &the_index; + opts.dst_index = &the_index; parse_tree(tree); init_tree_desc(&tree_desc, tree->buffer, tree->size); if (unpack_trees(1, &tree_desc, &opts)) @@ -224,8 +230,11 @@ static int merge_working_tree(struct checkout_opts *opts, struct tree_desc trees[2]; struct tree *tree; struct unpack_trees_options topts; + memset(&topts, 0, sizeof(topts)); topts.head_idx = -1; + topts.src_index = &the_index; + topts.dst_index = &the_index; refresh_cache(REFRESH_QUIET); diff --git a/builtin-commit.c b/builtin-commit.c index f49c22e642..660a3458f7 100644 --- a/builtin-commit.c +++ b/builtin-commit.c @@ -198,6 +198,8 @@ static void create_base_index(void) opts.head_idx = 1; opts.index_only = 1; opts.merge = 1; + opts.src_index = &the_index; + opts.dst_index = &the_index; opts.fn = oneway_merge; tree = parse_tree_indirect(head_sha1); diff --git a/builtin-merge-recursive.c b/builtin-merge-recursive.c index 5a0a7b5297..5c7fbb2599 100644 --- a/builtin-merge-recursive.c +++ b/builtin-merge-recursive.c @@ -213,6 +213,8 @@ static int git_merge_trees(int index_only, opts.merge = 1; opts.head_idx = 2; opts.fn = threeway_merge; + opts.src_index = &the_index; + opts.dst_index = &the_index; init_tree_desc_from_tree(t+0, common); init_tree_desc_from_tree(t+1, head); diff --git a/builtin-read-tree.c b/builtin-read-tree.c index 0138f5a917..160456dad1 100644 --- a/builtin-read-tree.c +++ b/builtin-read-tree.c @@ -102,6 +102,8 @@ int cmd_read_tree(int argc, const char **argv, const char *unused_prefix) memset(&opts, 0, sizeof(opts)); opts.head_idx = -1; + opts.src_index = &the_index; + opts.dst_index = &the_index; git_config(git_default_config); @@ -220,27 +222,6 @@ int cmd_read_tree(int argc, const char **argv, const char *unused_prefix) if ((opts.dir && !opts.update)) die("--exclude-per-directory is meaningless unless -u"); - if (opts.prefix) { - int pfxlen = strlen(opts.prefix); - int pos; - if (opts.prefix[pfxlen-1] != '/') - die("prefix must end with /"); - if (stage != 2) - die("binding merge takes only one tree"); - pos = cache_name_pos(opts.prefix, pfxlen); - if (0 <= pos) - die("corrupt index file"); - pos = -pos-1; - if (pos < active_nr && - !strncmp(active_cache[pos]->name, opts.prefix, pfxlen)) - die("subdirectory '%s' already exists.", opts.prefix); - pos = cache_name_pos(opts.prefix, pfxlen-1); - if (0 <= pos) - die("file '%.*s' already exists.", - pfxlen-1, opts.prefix); - opts.pos = -1 - pos; - } - if (opts.merge) { if (stage < 2) die("just how do you expect me to merge %d trees?", stage-1); @@ -346,12 +346,12 @@ extern void verify_non_filename(const char *prefix, const char *name); /* Initialize and use the cache information */ extern int read_index(struct index_state *); extern int read_index_from(struct index_state *, const char *path); -extern int write_index(struct index_state *, int newfd); +extern int write_index(const struct index_state *, int newfd); extern int discard_index(struct index_state *); -extern int unmerged_index(struct index_state *); +extern int unmerged_index(const struct index_state *); extern int verify_path(const char *path); extern int index_name_exists(struct index_state *istate, const char *name, int namelen); -extern int index_name_pos(struct index_state *, const char *name, int namelen); +extern int index_name_pos(const struct index_state *, const char *name, int namelen); #define ADD_CACHE_OK_TO_ADD 1 /* Ok to add */ #define ADD_CACHE_OK_TO_REPLACE 2 /* Ok to replace file/directory */ #define ADD_CACHE_SKIP_DFCHECK 4 /* Ok to skip DF conflict checks */ @@ -368,8 +368,8 @@ extern int ce_same_name(struct cache_entry *a, struct cache_entry *b); #define CE_MATCH_IGNORE_VALID 01 /* do not check the contents but report dirty on racily-clean entries */ #define CE_MATCH_RACY_IS_DIRTY 02 -extern int ie_match_stat(struct index_state *, struct cache_entry *, struct stat *, unsigned int); -extern int ie_modified(struct index_state *, struct cache_entry *, struct stat *, unsigned int); +extern int ie_match_stat(const struct index_state *, struct cache_entry *, struct stat *, unsigned int); +extern int ie_modified(const struct index_state *, struct cache_entry *, struct stat *, unsigned int); extern int ce_path_match(const struct cache_entry *ce, const char **pathspec); extern int index_fd(unsigned char *sha1, int fd, struct stat *st, int write_object, enum object_type type, const char *path); @@ -536,6 +536,7 @@ extern int create_symref(const char *ref, const char *refs_heads_master, const c extern int validate_headref(const char *ref); extern int base_name_compare(const char *name1, int len1, int mode1, const char *name2, int len2, int mode2); +extern int df_name_compare(const char *name1, int len1, int mode1, const char *name2, int len2, int mode2); extern int cache_name_compare(const char *name1, int len1, const char *name2, int len2); extern void *read_object_with_reference(const unsigned char *sha1, diff --git a/diff-lib.c b/diff-lib.c index 4581b594d0..52dbac34a4 100644 --- a/diff-lib.c +++ b/diff-lib.c @@ -600,8 +600,7 @@ static void mark_merge_entries(void) */ static void do_oneway_diff(struct unpack_trees_options *o, struct cache_entry *idx, - struct cache_entry *tree, - int idx_pos, int idx_nr) + struct cache_entry *tree) { struct rev_info *revs = o->unpack_data; int match_missing, cached; @@ -642,32 +641,19 @@ static void do_oneway_diff(struct unpack_trees_options *o, show_modified(revs, tree, idx, 1, cached, match_missing); } -/* - * Count how many index entries go with the first one - */ -static inline int count_skip(const struct cache_entry *src, int pos) +static inline void skip_same_name(struct cache_entry *ce, struct unpack_trees_options *o) { - int skip = 1; - - /* We can only have multiple entries if the first one is not stage-0 */ - if (ce_stage(src)) { - struct cache_entry **p = active_cache + pos; - int namelen = ce_namelen(src); - - for (;;) { - const struct cache_entry *ce; - pos++; - if (pos >= active_nr) - break; - ce = *++p; - if (ce_namelen(ce) != namelen) - break; - if (memcmp(ce->name, src->name, namelen)) - break; - skip++; - } + int len = ce_namelen(ce); + const struct index_state *index = o->src_index; + + while (o->pos < index->cache_nr) { + struct cache_entry *next = index->cache[o->pos]; + if (len != ce_namelen(next)) + break; + if (memcmp(ce->name, next->name, len)) + break; + o->pos++; } - return skip; } /* @@ -685,17 +671,14 @@ static inline int count_skip(const struct cache_entry *src, int pos) * the fairly complex unpack_trees() semantic requirements, including * the skipping, the path matching, the type conflict cases etc. */ -static int oneway_diff(struct cache_entry **src, - struct unpack_trees_options *o, - int index_pos) +static int oneway_diff(struct cache_entry **src, struct unpack_trees_options *o) { - int skip = 0; struct cache_entry *idx = src[0]; struct cache_entry *tree = src[1]; struct rev_info *revs = o->unpack_data; - if (index_pos >= 0) - skip = count_skip(idx, index_pos); + if (idx && ce_stage(idx)) + skip_same_name(idx, o); /* * Unpack-trees generates a DF/conflict entry if @@ -707,9 +690,9 @@ static int oneway_diff(struct cache_entry **src, tree = NULL; if (ce_path_match(idx ? idx : tree, revs->prune_data)) - do_oneway_diff(o, idx, tree, index_pos, skip); + do_oneway_diff(o, idx, tree); - return skip; + return 0; } int run_diff_index(struct rev_info *revs, int cached) @@ -734,6 +717,8 @@ int run_diff_index(struct rev_info *revs, int cached) opts.merge = 1; opts.fn = oneway_diff; opts.unpack_data = revs; + opts.src_index = &the_index; + opts.dst_index = NULL; init_tree_desc(&t, tree->buffer, tree->size); if (unpack_trees(1, &t, &opts)) @@ -787,6 +772,8 @@ int do_diff_cache(const unsigned char *tree_sha1, struct diff_options *opt) opts.merge = 1; opts.fn = oneway_diff; opts.unpack_data = &revs; + opts.src_index = &the_index; + opts.dst_index = &the_index; init_tree_desc(&t, tree->buffer, tree->size); if (unpack_trees(1, &t, &opts)) @@ -9,7 +9,7 @@ * the existing entry, or the empty slot if none existed. The caller * can then look at the (*ptr) to see whether it existed or not. */ -static struct hash_table_entry *lookup_hash_entry(unsigned int hash, struct hash_table *table) +static struct hash_table_entry *lookup_hash_entry(unsigned int hash, const struct hash_table *table) { unsigned int size = table->size, nr = hash % size; struct hash_table_entry *array = table->array; @@ -66,7 +66,7 @@ static void grow_hash_table(struct hash_table *table) free(old_array); } -void *lookup_hash(unsigned int hash, struct hash_table *table) +void *lookup_hash(unsigned int hash, const struct hash_table *table) { if (!table->array) return NULL; @@ -81,7 +81,7 @@ void **insert_hash(unsigned int hash, void *ptr, struct hash_table *table) return insert_hash_entry(hash, ptr, table); } -int for_each_hash(struct hash_table *table, int (*fn)(void *)) +int for_each_hash(const struct hash_table *table, int (*fn)(void *)) { int sum = 0; unsigned int i; @@ -28,9 +28,9 @@ struct hash_table { struct hash_table_entry *array; }; -extern void *lookup_hash(unsigned int hash, struct hash_table *table); +extern void *lookup_hash(unsigned int hash, const struct hash_table *table); extern void **insert_hash(unsigned int hash, void *ptr, struct hash_table *table); -extern int for_each_hash(struct hash_table *table, int (*fn)(void *)); +extern int for_each_hash(const struct hash_table *table, int (*fn)(void *)); extern void free_hash(struct hash_table *table); static inline void init_hash(struct hash_table *table) diff --git a/merge-tree.c b/merge-tree.c index e08324686c..02fc10f7e6 100644 --- a/merge-tree.c +++ b/merge-tree.c @@ -168,7 +168,13 @@ static struct merge_list *create_entry(unsigned stage, unsigned mode, const unsi return res; } -static void resolve(const char *base, struct name_entry *branch1, struct name_entry *result) +static char *traverse_path(const struct traverse_info *info, const struct name_entry *n) +{ + char *path = xmalloc(traverse_path_len(info, n) + 1); + return make_traverse_path(path, info, n); +} + +static void resolve(const struct traverse_info *info, struct name_entry *branch1, struct name_entry *result) { struct merge_list *orig, *final; const char *path; @@ -177,7 +183,7 @@ static void resolve(const char *base, struct name_entry *branch1, struct name_en if (!branch1) return; - path = xstrdup(mkpath("%s%s", base, result->path)); + path = traverse_path(info, result); orig = create_entry(2, branch1->mode, branch1->sha1, path); final = create_entry(0, result->mode, result->sha1, path); @@ -186,9 +192,8 @@ static void resolve(const char *base, struct name_entry *branch1, struct name_en add_merge_entry(final); } -static int unresolved_directory(const char *base, struct name_entry n[3]) +static int unresolved_directory(const struct traverse_info *info, struct name_entry n[3]) { - int baselen, pathlen; char *newbase; struct name_entry *p; struct tree_desc t[3]; @@ -204,13 +209,7 @@ static int unresolved_directory(const char *base, struct name_entry n[3]) } if (!S_ISDIR(p->mode)) return 0; - baselen = strlen(base); - pathlen = tree_entry_len(p->path, p->sha1); - newbase = xmalloc(baselen + pathlen + 2); - memcpy(newbase, base, baselen); - memcpy(newbase + baselen, p->path, pathlen); - memcpy(newbase + baselen + pathlen, "/", 2); - + newbase = traverse_path(info, p); buf0 = fill_tree_descriptor(t+0, n[0].sha1); buf1 = fill_tree_descriptor(t+1, n[1].sha1); buf2 = fill_tree_descriptor(t+2, n[2].sha1); @@ -224,7 +223,7 @@ static int unresolved_directory(const char *base, struct name_entry n[3]) } -static struct merge_list *link_entry(unsigned stage, const char *base, struct name_entry *n, struct merge_list *entry) +static struct merge_list *link_entry(unsigned stage, const struct traverse_info *info, struct name_entry *n, struct merge_list *entry) { const char *path; struct merge_list *link; @@ -234,17 +233,17 @@ static struct merge_list *link_entry(unsigned stage, const char *base, struct na if (entry) path = entry->path; else - path = xstrdup(mkpath("%s%s", base, n->path)); + path = traverse_path(info, n); link = create_entry(stage, n->mode, n->sha1, path); link->link = entry; return link; } -static void unresolved(const char *base, struct name_entry n[3]) +static void unresolved(const struct traverse_info *info, struct name_entry n[3]) { struct merge_list *entry = NULL; - if (unresolved_directory(base, n)) + if (unresolved_directory(info, n)) return; /* @@ -252,9 +251,9 @@ static void unresolved(const char *base, struct name_entry n[3]) * list has the stages in order - link_entry adds new * links at the front. */ - entry = link_entry(3, base, n + 2, entry); - entry = link_entry(2, base, n + 1, entry); - entry = link_entry(1, base, n + 0, entry); + entry = link_entry(3, info, n + 2, entry); + entry = link_entry(2, info, n + 1, entry); + entry = link_entry(1, info, n + 0, entry); add_merge_entry(entry); } @@ -288,36 +287,41 @@ static void unresolved(const char *base, struct name_entry n[3]) * The successful merge rules are the same as for the three-way merge * in git-read-tree. */ -static void threeway_callback(int n, unsigned long mask, struct name_entry *entry, const char *base) +static int threeway_callback(int n, unsigned long mask, unsigned long dirmask, struct name_entry *entry, struct traverse_info *info) { /* Same in both? */ if (same_entry(entry+1, entry+2)) { if (entry[0].sha1) { - resolve(base, NULL, entry+1); - return; + resolve(info, NULL, entry+1); + return mask; } } if (same_entry(entry+0, entry+1)) { if (entry[2].sha1 && !S_ISDIR(entry[2].mode)) { - resolve(base, entry+1, entry+2); - return; + resolve(info, entry+1, entry+2); + return mask; } } if (same_entry(entry+0, entry+2)) { if (entry[1].sha1 && !S_ISDIR(entry[1].mode)) { - resolve(base, NULL, entry+1); - return; + resolve(info, NULL, entry+1); + return mask; } } - unresolved(base, entry); + unresolved(info, entry); + return mask; } static void merge_trees(struct tree_desc t[3], const char *base) { - traverse_trees(3, t, base, threeway_callback); + struct traverse_info info; + + setup_traverse_info(&info, base); + info.fn = threeway_callback; + traverse_trees(3, t, &info); } static void *get_tree_descriptor(struct tree_desc *desc, const char *rev) diff --git a/read-cache.c b/read-cache.c index 657f0c5894..a92b25b59b 100644 --- a/read-cache.c +++ b/read-cache.c @@ -255,13 +255,13 @@ static int ce_match_stat_basic(struct cache_entry *ce, struct stat *st) return changed; } -static int is_racy_timestamp(struct index_state *istate, struct cache_entry *ce) +static int is_racy_timestamp(const struct index_state *istate, struct cache_entry *ce) { return (istate->timestamp && ((unsigned int)istate->timestamp) <= ce->ce_mtime); } -int ie_match_stat(struct index_state *istate, +int ie_match_stat(const struct index_state *istate, struct cache_entry *ce, struct stat *st, unsigned int options) { @@ -304,7 +304,7 @@ int ie_match_stat(struct index_state *istate, return changed; } -int ie_modified(struct index_state *istate, +int ie_modified(const struct index_state *istate, struct cache_entry *ce, struct stat *st, unsigned int options) { int changed, changed_fs; @@ -351,6 +351,41 @@ int base_name_compare(const char *name1, int len1, int mode1, return (c1 < c2) ? -1 : (c1 > c2) ? 1 : 0; } +/* + * df_name_compare() is identical to base_name_compare(), except it + * compares conflicting directory/file entries as equal. Note that + * while a directory name compares as equal to a regular file, they + * then individually compare _differently_ to a filename that has + * a dot after the basename (because '\0' < '.' < '/'). + * + * This is used by routines that want to traverse the git namespace + * but then handle conflicting entries together when possible. + */ +int df_name_compare(const char *name1, int len1, int mode1, + const char *name2, int len2, int mode2) +{ + int len = len1 < len2 ? len1 : len2, cmp; + unsigned char c1, c2; + + cmp = memcmp(name1, name2, len); + if (cmp) + return cmp; + /* Directories and files compare equal (same length, same name) */ + if (len1 == len2) + return 0; + c1 = name1[len]; + if (!c1 && S_ISDIR(mode1)) + c1 = '/'; + c2 = name2[len]; + if (!c2 && S_ISDIR(mode2)) + c2 = '/'; + if (c1 == '/' && !c2) + return 0; + if (c2 == '/' && !c1) + return 0; + return c1 - c2; +} + int cache_name_compare(const char *name1, int flags1, const char *name2, int flags2) { int len1 = flags1 & CE_NAMEMASK; @@ -377,7 +412,7 @@ int cache_name_compare(const char *name1, int flags1, const char *name2, int fla return 0; } -int index_name_pos(struct index_state *istate, const char *name, int namelen) +int index_name_pos(const struct index_state *istate, const char *name, int namelen) { int first, last; @@ -1166,7 +1201,7 @@ int discard_index(struct index_state *istate) return 0; } -int unmerged_index(struct index_state *istate) +int unmerged_index(const struct index_state *istate) { int i; for (i = 0; i < istate->cache_nr; i++) { @@ -1311,7 +1346,7 @@ static int ce_write_entry(SHA_CTX *c, int fd, struct cache_entry *ce) return ce_write(c, fd, ondisk, size); } -int write_index(struct index_state *istate, int newfd) +int write_index(const struct index_state *istate, int newfd) { SHA_CTX c; struct cache_header hdr; diff --git a/t/t1005-read-tree-reset.sh b/t/t1005-read-tree-reset.sh index f1b12167b8..8c4556408e 100755 --- a/t/t1005-read-tree-reset.sh +++ b/t/t1005-read-tree-reset.sh @@ -21,7 +21,7 @@ test_expect_success 'setup' ' git commit -m two ' -test_expect_failure 'reset should work' ' +test_expect_success 'reset should work' ' git read-tree -u --reset HEAD^ && git ls-files >actual && diff -u expect actual diff --git a/tree-walk.c b/tree-walk.c index 142205ddc3..02e2aed773 100644 --- a/tree-walk.c +++ b/tree-walk.c @@ -62,7 +62,7 @@ void *fill_tree_descriptor(struct tree_desc *desc, const unsigned char *sha1) static int entry_compare(struct name_entry *a, struct name_entry *b) { - return base_name_compare( + return df_name_compare( a->path, tree_entry_len(a->path, a->sha1), a->mode, b->path, tree_entry_len(b->path, b->sha1), b->mode); } @@ -104,12 +104,48 @@ int tree_entry(struct tree_desc *desc, struct name_entry *entry) return 1; } -void traverse_trees(int n, struct tree_desc *t, const char *base, traverse_callback_t callback) +void setup_traverse_info(struct traverse_info *info, const char *base) { + int pathlen = strlen(base); + static struct traverse_info dummy; + + memset(info, 0, sizeof(*info)); + if (pathlen && base[pathlen-1] == '/') + pathlen--; + info->pathlen = pathlen ? pathlen + 1 : 0; + info->name.path = base; + info->name.sha1 = (void *)(base + pathlen + 1); + if (pathlen) + info->prev = &dummy; +} + +char *make_traverse_path(char *path, const struct traverse_info *info, const struct name_entry *n) +{ + int len = tree_entry_len(n->path, n->sha1); + int pathlen = info->pathlen; + + path[pathlen + len] = 0; + for (;;) { + memcpy(path + pathlen, n->path, len); + if (!pathlen) + break; + path[--pathlen] = '/'; + n = &info->name; + len = tree_entry_len(n->path, n->sha1); + info = info->prev; + pathlen -= len; + } + return path; +} + +int traverse_trees(int n, struct tree_desc *t, struct traverse_info *info) +{ + int ret = 0; struct name_entry *entry = xmalloc(n*sizeof(*entry)); for (;;) { unsigned long mask = 0; + unsigned long dirmask = 0; int i, last; last = -1; @@ -134,25 +170,35 @@ void traverse_trees(int n, struct tree_desc *t, const char *base, traverse_callb mask = 0; } mask |= 1ul << i; + if (S_ISDIR(entry[i].mode)) + dirmask |= 1ul << i; last = i; } if (!mask) break; + dirmask &= mask; /* - * Update the tree entries we've walked, and clear - * all the unused name-entries. + * Clear all the unused name-entries. */ for (i = 0; i < n; i++) { - if (mask & (1ul << i)) { - update_tree_entry(t+i); + if (mask & (1ul << i)) continue; - } entry_clear(entry + i); } - callback(n, mask, entry, base); + ret = info->fn(n, mask, dirmask, entry, info); + if (ret < 0) + break; + if (ret) + mask &= ret; + ret = 0; + for (i = 0; i < n; i++) { + if (mask & (1ul << i)) + update_tree_entry(t + i); + } } free(entry); + return ret; } static int find_tree_entry(struct tree_desc *t, const char *name, unsigned char *result, unsigned *mode) diff --git a/tree-walk.h b/tree-walk.h index db0fbdc701..42110a465f 100644 --- a/tree-walk.h +++ b/tree-walk.h @@ -33,10 +33,27 @@ int tree_entry(struct tree_desc *, struct name_entry *); void *fill_tree_descriptor(struct tree_desc *desc, const unsigned char *sha1); -typedef void (*traverse_callback_t)(int n, unsigned long mask, struct name_entry *entry, const char *base); - -void traverse_trees(int n, struct tree_desc *t, const char *base, traverse_callback_t callback); +struct traverse_info; +typedef int (*traverse_callback_t)(int n, unsigned long mask, unsigned long dirmask, struct name_entry *entry, struct traverse_info *); +int traverse_trees(int n, struct tree_desc *t, struct traverse_info *info); + +struct traverse_info { + struct traverse_info *prev; + struct name_entry name; + int pathlen; + + unsigned long conflicts; + traverse_callback_t fn; + void *data; +}; int get_tree_entry(const unsigned char *, const char *, unsigned char *, unsigned *); +extern char *make_traverse_path(char *path, const struct traverse_info *info, const struct name_entry *n); +extern void setup_traverse_info(struct traverse_info *info, const char *base); + +static inline int traverse_path_len(const struct traverse_info *info, const struct name_entry *n) +{ + return info->pathlen + tree_entry_len(n->path, n->sha1); +} #endif diff --git a/unpack-trees.c b/unpack-trees.c index 3e448d8974..be89d52e8c 100644 --- a/unpack-trees.c +++ b/unpack-trees.c @@ -1,3 +1,4 @@ +#define NO_THE_INDEX_COMPATIBILITY_MACROS #include "cache.h" #include "dir.h" #include "tree.h" @@ -7,268 +8,18 @@ #include "progress.h" #include "refs.h" -#define DBRT_DEBUG 1 - -struct tree_entry_list { - struct tree_entry_list *next; - unsigned int mode; - const char *name; - const unsigned char *sha1; -}; - -static struct tree_entry_list *create_tree_entry_list(struct tree_desc *desc) -{ - struct name_entry one; - struct tree_entry_list *ret = NULL; - struct tree_entry_list **list_p = &ret; - - while (tree_entry(desc, &one)) { - struct tree_entry_list *entry; - - entry = xmalloc(sizeof(struct tree_entry_list)); - entry->name = one.path; - entry->sha1 = one.sha1; - entry->mode = one.mode; - entry->next = NULL; - - *list_p = entry; - list_p = &entry->next; - } - return ret; -} - -static int entcmp(const char *name1, int dir1, const char *name2, int dir2) +static void add_entry(struct unpack_trees_options *o, struct cache_entry *ce, + unsigned int set, unsigned int clear) { - int len1 = strlen(name1); - int len2 = strlen(name2); - int len = len1 < len2 ? len1 : len2; - int ret = memcmp(name1, name2, len); - unsigned char c1, c2; - if (ret) - return ret; - c1 = name1[len]; - c2 = name2[len]; - if (!c1 && dir1) - c1 = '/'; - if (!c2 && dir2) - c2 = '/'; - ret = (c1 < c2) ? -1 : (c1 > c2) ? 1 : 0; - if (c1 && c2 && !ret) - ret = len1 - len2; - return ret; -} - -static inline void remove_entry(int remove) -{ - if (remove >= 0) - remove_cache_entry_at(remove); -} - -static int unpack_trees_rec(struct tree_entry_list **posns, int len, - const char *base, struct unpack_trees_options *o, - struct tree_entry_list *df_conflict_list) -{ - int remove; - int baselen = strlen(base); - int src_size = len + 1; - int retval = 0; - - do { - int i; - const char *first; - int firstdir = 0; - int pathlen; - unsigned ce_size; - struct tree_entry_list **subposns; - struct cache_entry **src; - int any_files = 0; - int any_dirs = 0; - char *cache_name; - int ce_stage; - int skip_entry = 0; - - /* Find the first name in the input. */ - - first = NULL; - cache_name = NULL; - - /* Check the cache */ - if (o->merge && o->pos < active_nr) { - /* This is a bit tricky: */ - /* If the index has a subdirectory (with - * contents) as the first name, it'll get a - * filename like "foo/bar". But that's after - * "foo", so the entry in trees will get - * handled first, at which point we'll go into - * "foo", and deal with "bar" from the index, - * because the base will be "foo/". The only - * way we can actually have "foo/bar" first of - * all the things is if the trees don't - * contain "foo" at all, in which case we'll - * handle "foo/bar" without going into the - * directory, but that's fine (and will return - * an error anyway, with the added unknown - * file case. - */ - - cache_name = active_cache[o->pos]->name; - if (strlen(cache_name) > baselen && - !memcmp(cache_name, base, baselen)) { - cache_name += baselen; - first = cache_name; - } else { - cache_name = NULL; - } - } - -#if DBRT_DEBUG > 1 - if (first) - fprintf(stderr, "index %s\n", first); -#endif - for (i = 0; i < len; i++) { - if (!posns[i] || posns[i] == df_conflict_list) - continue; -#if DBRT_DEBUG > 1 - fprintf(stderr, "%d %s\n", i + 1, posns[i]->name); -#endif - if (!first || entcmp(first, firstdir, - posns[i]->name, - S_ISDIR(posns[i]->mode)) > 0) { - first = posns[i]->name; - firstdir = S_ISDIR(posns[i]->mode); - } - } - /* No name means we're done */ - if (!first) - goto leave_directory; - - pathlen = strlen(first); - ce_size = cache_entry_size(baselen + pathlen); - - src = xcalloc(src_size, sizeof(struct cache_entry *)); - - subposns = xcalloc(len, sizeof(struct tree_list_entry *)); - - remove = -1; - if (cache_name && !strcmp(cache_name, first)) { - any_files = 1; - src[0] = active_cache[o->pos]; - remove = o->pos; - if (o->skip_unmerged && ce_stage(src[0])) - skip_entry = 1; - } - - for (i = 0; i < len; i++) { - struct cache_entry *ce; - - if (!posns[i] || - (posns[i] != df_conflict_list && - strcmp(first, posns[i]->name))) { - continue; - } - - if (posns[i] == df_conflict_list) { - src[i + o->merge] = o->df_conflict_entry; - continue; - } + unsigned int size = ce_size(ce); + struct cache_entry *new = xmalloc(size); - if (S_ISDIR(posns[i]->mode)) { - struct tree *tree = lookup_tree(posns[i]->sha1); - struct tree_desc t; - any_dirs = 1; - parse_tree(tree); - init_tree_desc(&t, tree->buffer, tree->size); - subposns[i] = create_tree_entry_list(&t); - posns[i] = posns[i]->next; - src[i + o->merge] = o->df_conflict_entry; - continue; - } - - if (skip_entry) { - subposns[i] = df_conflict_list; - posns[i] = posns[i]->next; - continue; - } - - if (!o->merge) - ce_stage = 0; - else if (i + 1 < o->head_idx) - ce_stage = 1; - else if (i + 1 > o->head_idx) - ce_stage = 3; - else - ce_stage = 2; - - ce = xcalloc(1, ce_size); - ce->ce_mode = create_ce_mode(posns[i]->mode); - ce->ce_flags = create_ce_flags(baselen + pathlen, - ce_stage); - memcpy(ce->name, base, baselen); - memcpy(ce->name + baselen, first, pathlen + 1); - - any_files = 1; - - hashcpy(ce->sha1, posns[i]->sha1); - src[i + o->merge] = ce; - subposns[i] = df_conflict_list; - posns[i] = posns[i]->next; - } - if (any_files) { - if (skip_entry) { - o->pos++; - while (o->pos < active_nr && - !strcmp(active_cache[o->pos]->name, - src[0]->name)) - o->pos++; - } else if (o->merge) { - int ret; - -#if DBRT_DEBUG > 1 - fprintf(stderr, "%s:\n", first); - for (i = 0; i < src_size; i++) { - fprintf(stderr, " %d ", i); - if (src[i]) - fprintf(stderr, "%06x %s\n", src[i]->ce_mode, sha1_to_hex(src[i]->sha1)); - else - fprintf(stderr, "\n"); - } -#endif - ret = o->fn(src, o, remove); - if (ret < 0) - return ret; + clear |= CE_HASHED | CE_UNHASHED; -#if DBRT_DEBUG > 1 - fprintf(stderr, "Added %d entries\n", ret); -#endif - o->pos += ret; - } else { - remove_entry(remove); - for (i = 0; i < src_size; i++) { - if (src[i]) { - add_cache_entry(src[i], ADD_CACHE_OK_TO_ADD|ADD_CACHE_SKIP_DFCHECK); - } - } - } - } - if (any_dirs) { - char *newbase = xmalloc(baselen + 2 + pathlen); - memcpy(newbase, base, baselen); - memcpy(newbase + baselen, first, pathlen); - newbase[baselen + pathlen] = '/'; - newbase[baselen + pathlen + 1] = '\0'; - if (unpack_trees_rec(subposns, len, newbase, o, - df_conflict_list)) { - retval = -1; - goto leave_directory; - } - free(newbase); - } - free(subposns); - free(src); - } while (1); - - leave_directory: - return retval; + memcpy(new, ce, size); + new->next = NULL; + new->ce_flags = (new->ce_flags & ~clear) | set; + add_index_entry(&o->result, new, ADD_CACHE_OK_TO_ADD|ADD_CACHE_OK_TO_REPLACE|ADD_CACHE_SKIP_DFCHECK); } /* Unlink the last component and attempt to remove leading @@ -308,11 +59,12 @@ static void check_updates(struct unpack_trees_options *o) unsigned cnt = 0, total = 0; struct progress *progress = NULL; char last_symlink[PATH_MAX]; + struct index_state *index = &o->result; int i; if (o->update && o->verbose_update) { - for (total = cnt = 0; cnt < active_nr; cnt++) { - struct cache_entry *ce = active_cache[cnt]; + for (total = cnt = 0; cnt < index->cache_nr; cnt++) { + struct cache_entry *ce = index->cache[cnt]; if (ce->ce_flags & (CE_UPDATE | CE_REMOVE)) total++; } @@ -323,15 +75,15 @@ static void check_updates(struct unpack_trees_options *o) } *last_symlink = '\0'; - for (i = 0; i < active_nr; i++) { - struct cache_entry *ce = active_cache[i]; + for (i = 0; i < index->cache_nr; i++) { + struct cache_entry *ce = index->cache[i]; if (ce->ce_flags & (CE_UPDATE | CE_REMOVE)) display_progress(progress, ++cnt); if (ce->ce_flags & CE_REMOVE) { if (o->update) unlink_entry(ce->name, last_symlink); - remove_cache_entry_at(i); + remove_index_entry_at(&o->result, i); i--; continue; } @@ -346,21 +98,244 @@ static void check_updates(struct unpack_trees_options *o) stop_progress(&progress); } -int unpack_trees(unsigned len, struct tree_desc *t, struct unpack_trees_options *o) +static inline int call_unpack_fn(struct cache_entry **src, struct unpack_trees_options *o) +{ + int ret = o->fn(src, o); + if (ret > 0) + ret = 0; + return ret; +} + +static int unpack_index_entry(struct cache_entry *ce, struct unpack_trees_options *o) +{ + struct cache_entry *src[5] = { ce, }; + + o->pos++; + if (ce_stage(ce)) { + if (o->skip_unmerged) { + add_entry(o, ce, 0, 0); + return 0; + } + } + return call_unpack_fn(src, o); +} + +int traverse_trees_recursive(int n, unsigned long dirmask, unsigned long df_conflicts, struct name_entry *names, struct traverse_info *info) { - struct tree_entry_list **posns; int i; - struct tree_entry_list df_conflict_list; + struct tree_desc t[3]; + struct traverse_info newinfo; + struct name_entry *p; + + p = names; + while (!p->mode) + p++; + + newinfo = *info; + newinfo.prev = info; + newinfo.name = *p; + newinfo.pathlen += tree_entry_len(p->path, p->sha1) + 1; + newinfo.conflicts |= df_conflicts; + + for (i = 0; i < n; i++, dirmask >>= 1) { + const unsigned char *sha1 = NULL; + if (dirmask & 1) + sha1 = names[i].sha1; + fill_tree_descriptor(t+i, sha1); + } + return traverse_trees(n, t, &newinfo); +} + +/* + * Compare the traverse-path to the cache entry without actually + * having to generate the textual representation of the traverse + * path. + * + * NOTE! This *only* compares up to the size of the traverse path + * itself - the caller needs to do the final check for the cache + * entry having more data at the end! + */ +static int do_compare_entry(const struct cache_entry *ce, const struct traverse_info *info, const struct name_entry *n) +{ + int len, pathlen, ce_len; + const char *ce_name; + + if (info->prev) { + int cmp = do_compare_entry(ce, info->prev, &info->name); + if (cmp) + return cmp; + } + pathlen = info->pathlen; + ce_len = ce_namelen(ce); + + /* If ce_len < pathlen then we must have previously hit "name == directory" entry */ + if (ce_len < pathlen) + return -1; + + ce_len -= pathlen; + ce_name = ce->name + pathlen; + + len = tree_entry_len(n->path, n->sha1); + return df_name_compare(ce_name, ce_len, S_IFREG, n->path, len, n->mode); +} + +static int compare_entry(const struct cache_entry *ce, const struct traverse_info *info, const struct name_entry *n) +{ + int cmp = do_compare_entry(ce, info, n); + if (cmp) + return cmp; + + /* + * Even if the beginning compared identically, the ce should + * compare as bigger than a directory leading up to it! + */ + return ce_namelen(ce) > traverse_path_len(info, n); +} + +static struct cache_entry *create_ce_entry(const struct traverse_info *info, const struct name_entry *n, int stage) +{ + int len = traverse_path_len(info, n); + struct cache_entry *ce = xcalloc(1, cache_entry_size(len)); + + ce->ce_mode = create_ce_mode(n->mode); + ce->ce_flags = create_ce_flags(len, stage); + hashcpy(ce->sha1, n->sha1); + make_traverse_path(ce->name, info, n); + + return ce; +} + +static int unpack_nondirectories(int n, unsigned long mask, unsigned long dirmask, struct cache_entry *src[5], + const struct name_entry *names, const struct traverse_info *info) +{ + int i; + struct unpack_trees_options *o = info->data; + unsigned long conflicts; + + /* Do we have *only* directories? Nothing to do */ + if (mask == dirmask && !src[0]) + return 0; + + conflicts = info->conflicts; + if (o->merge) + conflicts >>= 1; + conflicts |= dirmask; + + /* + * Ok, we've filled in up to any potential index entry in src[0], + * now do the rest. + */ + for (i = 0; i < n; i++) { + int stage; + unsigned int bit = 1ul << i; + if (conflicts & bit) { + src[i + o->merge] = o->df_conflict_entry; + continue; + } + if (!(mask & bit)) + continue; + if (!o->merge) + stage = 0; + else if (i + 1 < o->head_idx) + stage = 1; + else if (i + 1 > o->head_idx) + stage = 3; + else + stage = 2; + src[i + o->merge] = create_ce_entry(info, names + i, stage); + } + + if (o->merge) + return call_unpack_fn(src, o); + + n += o->merge; + for (i = 0; i < n; i++) + add_entry(o, src[i], 0, 0); + return 0; +} + +static int unpack_callback(int n, unsigned long mask, unsigned long dirmask, struct name_entry *names, struct traverse_info *info) +{ + struct cache_entry *src[5] = { NULL, }; + struct unpack_trees_options *o = info->data; + const struct name_entry *p = names; + + /* Find first entry with a real name (we could use "mask" too) */ + while (!p->mode) + p++; + + /* Are we supposed to look at the index too? */ + if (o->merge) { + while (o->pos < o->src_index->cache_nr) { + struct cache_entry *ce = o->src_index->cache[o->pos]; + int cmp = compare_entry(ce, info, p); + if (cmp < 0) { + if (unpack_index_entry(ce, o) < 0) + return -1; + continue; + } + if (!cmp) { + o->pos++; + if (ce_stage(ce)) { + /* + * If we skip unmerged index entries, we'll skip this + * entry *and* the tree entries associated with it! + */ + if (o->skip_unmerged) { + add_entry(o, ce, 0, 0); + return mask; + } + } + src[0] = ce; + } + break; + } + } + + if (unpack_nondirectories(n, mask, dirmask, src, names, info) < 0) + return -1; + + /* Now handle any directories.. */ + if (dirmask) { + unsigned long conflicts = mask & ~dirmask; + if (o->merge) { + conflicts <<= 1; + if (src[0]) + conflicts |= 1; + } + if (traverse_trees_recursive(n, dirmask, conflicts, + names, info) < 0) + return -1; + return mask; + } + + return mask; +} + +static int unpack_failed(struct unpack_trees_options *o, const char *message) +{ + discard_index(&o->result); + if (!o->gently) { + if (message) + return error(message); + return -1; + } + return -1; +} + +int unpack_trees(unsigned len, struct tree_desc *t, struct unpack_trees_options *o) +{ static struct cache_entry *dfc; - memset(&df_conflict_list, 0, sizeof(df_conflict_list)); - df_conflict_list.next = &df_conflict_list; + if (len > 4) + die("unpack_trees takes at most four trees"); memset(&state, 0, sizeof(state)); state.base_dir = ""; state.force = 1; state.quiet = 1; state.refresh_cache = 1; + memset(&o->result, 0, sizeof(o->result)); o->merge_size = len; if (!dfc) @@ -368,30 +343,33 @@ int unpack_trees(unsigned len, struct tree_desc *t, struct unpack_trees_options o->df_conflict_entry = dfc; if (len) { - posns = xmalloc(len * sizeof(struct tree_entry_list *)); - for (i = 0; i < len; i++) - posns[i] = create_tree_entry_list(t+i); - - if (unpack_trees_rec(posns, len, o->prefix ? o->prefix : "", - o, &df_conflict_list)) { - if (o->gently) { - discard_cache(); - read_cache(); - } - return -1; - } + const char *prefix = o->prefix ? o->prefix : ""; + struct traverse_info info; + + setup_traverse_info(&info, prefix); + info.fn = unpack_callback; + info.data = o; + + if (traverse_trees(len, t, &info) < 0) + return unpack_failed(o, NULL); } - if (o->trivial_merges_only && o->nontrivial_merge) { - if (o->gently) { - discard_cache(); - read_cache(); + /* Any left-over entries in the index? */ + if (o->merge) { + while (o->pos < o->src_index->cache_nr) { + struct cache_entry *ce = o->src_index->cache[o->pos]; + if (unpack_index_entry(ce, o) < 0) + return unpack_failed(o, NULL); } - return o->gently ? -1 : - error("Merge requires file-level merging"); } + if (o->trivial_merges_only && o->nontrivial_merge) + return unpack_failed(o, "Merge requires file-level merging"); + + o->src_index = NULL; check_updates(o); + if (o->dst_index) + *o->dst_index = o->result; return 0; } @@ -427,7 +405,7 @@ static int verify_uptodate(struct cache_entry *ce, return 0; if (!lstat(ce->name, &st)) { - unsigned changed = ce_match_stat(ce, &st, CE_MATCH_IGNORE_VALID); + unsigned changed = ie_match_stat(o->src_index, ce, &st, CE_MATCH_IGNORE_VALID); if (!changed) return 0; /* @@ -447,10 +425,10 @@ static int verify_uptodate(struct cache_entry *ce, error("Entry '%s' not uptodate. Cannot merge.", ce->name); } -static void invalidate_ce_path(struct cache_entry *ce) +static void invalidate_ce_path(struct cache_entry *ce, struct unpack_trees_options *o) { if (ce) - cache_tree_invalidate_path(active_cache_tree, ce->name); + cache_tree_invalidate_path(o->src_index->cache_tree, ce->name); } /* @@ -495,12 +473,12 @@ static int verify_clean_subdirectory(struct cache_entry *ce, const char *action, * in that directory. */ namelen = strlen(ce->name); - pos = cache_name_pos(ce->name, namelen); + pos = index_name_pos(o->src_index, ce->name, namelen); if (0 <= pos) return cnt; /* we have it as nondirectory */ pos = -pos - 1; - for (i = pos; i < active_nr; i++) { - struct cache_entry *ce = active_cache[i]; + for (i = pos; i < o->src_index->cache_nr; i++) { + struct cache_entry *ce = o->src_index->cache[i]; int len = ce_namelen(ce); if (len < namelen || strncmp(ce->name, ce->name, namelen) || @@ -512,7 +490,7 @@ static int verify_clean_subdirectory(struct cache_entry *ce, const char *action, if (!ce_stage(ce)) { if (verify_uptodate(ce, o)) return -1; - ce->ce_flags |= CE_REMOVE; + add_entry(o, ce, CE_REMOVE, 0); } cnt++; } @@ -598,9 +576,9 @@ static int verify_absent(struct cache_entry *ce, const char *action, * delete this path, which is in a subdirectory that * is being replaced with a blob. */ - cnt = cache_name_pos(ce->name, strlen(ce->name)); + cnt = index_name_pos(&o->result, ce->name, strlen(ce->name)); if (0 <= cnt) { - struct cache_entry *ce = active_cache[cnt]; + struct cache_entry *ce = o->result.cache[cnt]; if (ce->ce_flags & CE_REMOVE) return 0; } @@ -615,7 +593,6 @@ static int verify_absent(struct cache_entry *ce, const char *action, static int merged_entry(struct cache_entry *merge, struct cache_entry *old, struct unpack_trees_options *o) { - merge->ce_flags |= CE_UPDATE; if (old) { /* * See if we can re-use the old CE directly? @@ -629,38 +606,38 @@ static int merged_entry(struct cache_entry *merge, struct cache_entry *old, } else { if (verify_uptodate(old, o)) return -1; - invalidate_ce_path(old); + invalidate_ce_path(old, o); } } else { if (verify_absent(merge, "overwritten", o)) return -1; - invalidate_ce_path(merge); + invalidate_ce_path(merge, o); } - merge->ce_flags &= ~CE_STAGEMASK; - add_cache_entry(merge, ADD_CACHE_OK_TO_ADD|ADD_CACHE_OK_TO_REPLACE); + add_entry(o, merge, CE_UPDATE, CE_STAGEMASK); return 1; } static int deleted_entry(struct cache_entry *ce, struct cache_entry *old, struct unpack_trees_options *o) { - if (old) { - if (verify_uptodate(old, o)) - return -1; - } else + /* Did it exist in the index? */ + if (!old) { if (verify_absent(ce, "removed", o)) return -1; - ce->ce_flags |= CE_REMOVE; - add_cache_entry(ce, ADD_CACHE_OK_TO_ADD|ADD_CACHE_OK_TO_REPLACE); - invalidate_ce_path(ce); + return 0; + } + if (verify_uptodate(old, o)) + return -1; + add_entry(o, ce, CE_REMOVE, 0); + invalidate_ce_path(ce, o); return 1; } static int keep_entry(struct cache_entry *ce, struct unpack_trees_options *o) { - add_cache_entry(ce, ADD_CACHE_OK_TO_ADD); + add_entry(o, ce, 0, 0); return 1; } @@ -680,9 +657,7 @@ static void show_stage_entry(FILE *o, } #endif -int threeway_merge(struct cache_entry **stages, - struct unpack_trees_options *o, - int remove) +int threeway_merge(struct cache_entry **stages, struct unpack_trees_options *o) { struct cache_entry *index; struct cache_entry *head; @@ -759,10 +734,8 @@ int threeway_merge(struct cache_entry **stages, } /* #1 */ - if (!head && !remote && any_anc_missing) { - remove_entry(remove); + if (!head && !remote && any_anc_missing) return 0; - } /* Under the new "aggressive" rule, we resolve mostly trivial * cases that we historically had git-merge-one-file resolve. @@ -794,10 +767,9 @@ int threeway_merge(struct cache_entry **stages, if ((head_deleted && remote_deleted) || (head_deleted && remote && remote_match) || (remote_deleted && head && head_match)) { - remove_entry(remove); if (index) return deleted_entry(index, index, o); - else if (ce && !head_deleted) { + if (ce && !head_deleted) { if (verify_absent(ce, "removed", o)) return -1; } @@ -820,7 +792,6 @@ int threeway_merge(struct cache_entry **stages, return -1; } - remove_entry(remove); o->nontrivial_merge = 1; /* #2, #3, #4, #6, #7, #9, #10, #11. */ @@ -855,9 +826,7 @@ int threeway_merge(struct cache_entry **stages, * "carry forward" rule, please see <Documentation/git-read-tree.txt>. * */ -int twoway_merge(struct cache_entry **src, - struct unpack_trees_options *o, - int remove) +int twoway_merge(struct cache_entry **src, struct unpack_trees_options *o) { struct cache_entry *current = src[0]; struct cache_entry *oldtree = src[1]; @@ -885,7 +854,6 @@ int twoway_merge(struct cache_entry **src, } else if (oldtree && !newtree && same(current, oldtree)) { /* 10 or 11 */ - remove_entry(remove); return deleted_entry(oldtree, current, o); } else if (oldtree && newtree && @@ -895,7 +863,6 @@ int twoway_merge(struct cache_entry **src, } else { /* all other failures */ - remove_entry(remove); if (oldtree) return o->gently ? -1 : reject_merge(oldtree); if (current) @@ -907,7 +874,6 @@ int twoway_merge(struct cache_entry **src, } else if (newtree) return merged_entry(newtree, current, o); - remove_entry(remove); return deleted_entry(oldtree, current, o); } @@ -918,8 +884,7 @@ int twoway_merge(struct cache_entry **src, * stage0 does not have anything there. */ int bind_merge(struct cache_entry **src, - struct unpack_trees_options *o, - int remove) + struct unpack_trees_options *o) { struct cache_entry *old = src[0]; struct cache_entry *a = src[1]; @@ -929,7 +894,7 @@ int bind_merge(struct cache_entry **src, o->merge_size); if (a && old) return o->gently ? -1 : - error("Entry '%s' overlaps. Cannot bind.", a->name); + error("Entry '%s' overlaps with '%s'. Cannot bind.", a->name, old->name); if (!a) return keep_entry(old, o); else @@ -942,9 +907,7 @@ int bind_merge(struct cache_entry **src, * The rule is: * - take the stat information from stage0, take the data from stage1 */ -int oneway_merge(struct cache_entry **src, - struct unpack_trees_options *o, - int remove) +int oneway_merge(struct cache_entry **src, struct unpack_trees_options *o) { struct cache_entry *old = src[0]; struct cache_entry *a = src[1]; @@ -953,18 +916,19 @@ int oneway_merge(struct cache_entry **src, return error("Cannot do a oneway merge of %d trees", o->merge_size); - if (!a) { - remove_entry(remove); + if (!a) return deleted_entry(old, old, o); - } + if (old && same(old, a)) { + int update = 0; if (o->reset) { struct stat st; if (lstat(old->name, &st) || - ce_match_stat(old, &st, CE_MATCH_IGNORE_VALID)) - old->ce_flags |= CE_UPDATE; + ie_match_stat(o->src_index, old, &st, CE_MATCH_IGNORE_VALID)) + update |= CE_UPDATE; } - return keep_entry(old, o); + add_entry(o, old, update, 0); + return 0; } return merged_entry(a, old, o); } diff --git a/unpack-trees.h b/unpack-trees.h index a2df544d04..e8abbcd037 100644 --- a/unpack-trees.h +++ b/unpack-trees.h @@ -4,8 +4,7 @@ struct unpack_trees_options; typedef int (*merge_fn_t)(struct cache_entry **src, - struct unpack_trees_options *options, - int remove); + struct unpack_trees_options *options); struct unpack_trees_options { int reset; @@ -28,14 +27,18 @@ struct unpack_trees_options { struct cache_entry *df_conflict_entry; void *unpack_data; + + struct index_state *dst_index; + const struct index_state *src_index; + struct index_state result; }; extern int unpack_trees(unsigned n, struct tree_desc *t, struct unpack_trees_options *options); -int threeway_merge(struct cache_entry **stages, struct unpack_trees_options *o, int); -int twoway_merge(struct cache_entry **src, struct unpack_trees_options *o, int); -int bind_merge(struct cache_entry **src, struct unpack_trees_options *o, int); -int oneway_merge(struct cache_entry **src, struct unpack_trees_options *o, int); +int threeway_merge(struct cache_entry **stages, struct unpack_trees_options *o); +int twoway_merge(struct cache_entry **src, struct unpack_trees_options *o); +int bind_merge(struct cache_entry **src, struct unpack_trees_options *o); +int oneway_merge(struct cache_entry **src, struct unpack_trees_options *o); #endif |