X-Git-Url: https://asedeno.scripts.mit.edu/gitweb/?a=blobdiff_plain;f=unpack-trees.c;h=5139481358d0838a95db3fdb62e32221eb7343b1;hb=42c4b58059fa9af65e90f2c418bb551e30d1d32f;hp=2e2232cbb07e61de3be74302fba67142a58a857b;hpb=e27e609bbf81271318d99f2643f378f3fde6c6c6;p=git.git diff --git a/unpack-trees.c b/unpack-trees.c index 2e2232cbb..513948135 100644 --- a/unpack-trees.c +++ b/unpack-trees.c @@ -27,8 +27,7 @@ static struct tree_entry_list *create_tree_entry_list(struct tree *tree) if (!tree->object.parsed) parse_tree(tree); - desc.buf = tree->buffer; - desc.size = tree->size; + init_tree_desc(&desc, tree->buffer, tree->size); while (tree_entry(&desc, &one)) { struct tree_entry_list *entry; @@ -71,7 +70,6 @@ static int entcmp(const char *name1, int dir1, const char *name2, int dir2) static int unpack_trees_rec(struct tree_entry_list **posns, int len, const char *base, struct unpack_trees_options *o, - int *indpos, struct tree_entry_list *df_conflict_list) { int baselen = strlen(base); @@ -101,7 +99,7 @@ static int unpack_trees_rec(struct tree_entry_list **posns, int len, cache_name = NULL; /* Check the cache */ - if (o->merge && *indpos < active_nr) { + 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 @@ -119,7 +117,7 @@ static int unpack_trees_rec(struct tree_entry_list **posns, int len, * file case. */ - cache_name = active_cache[*indpos]->name; + cache_name = active_cache[o->pos]->name; if (strlen(cache_name) > baselen && !memcmp(cache_name, base, baselen)) { cache_name += baselen; @@ -159,8 +157,8 @@ static int unpack_trees_rec(struct tree_entry_list **posns, int len, if (cache_name && !strcmp(cache_name, first)) { any_files = 1; - src[0] = active_cache[*indpos]; - remove_cache_entry_at(*indpos); + src[0] = active_cache[o->pos]; + remove_cache_entry_at(o->pos); } for (i = 0; i < len; i++) { @@ -229,7 +227,7 @@ static int unpack_trees_rec(struct tree_entry_list **posns, int len, #if DBRT_DEBUG > 1 printf("Added %d entries\n", ret); #endif - *indpos += ret; + o->pos += ret; } else { for (i = 0; i < src_size; i++) { if (src[i]) { @@ -245,7 +243,7 @@ static int unpack_trees_rec(struct tree_entry_list **posns, int len, newbase[baselen + pathlen] = '/'; newbase[baselen + pathlen + 1] = '\0'; if (unpack_trees_rec(subposns, len, newbase, o, - indpos, df_conflict_list)) { + df_conflict_list)) { retval = -1; goto leave_directory; } @@ -376,7 +374,6 @@ static void check_updates(struct cache_entry **src, int nr, int unpack_trees(struct object_list *trees, struct unpack_trees_options *o) { - int indpos = 0; unsigned len = object_list_length(trees); struct tree_entry_list **posns; int i; @@ -405,7 +402,7 @@ int unpack_trees(struct object_list *trees, struct unpack_trees_options *o) posn = posn->next; } if (unpack_trees_rec(posns, len, o->prefix ? o->prefix : "", - o, &indpos, &df_conflict_list)) + o, &df_conflict_list)) return -1; } @@ -468,6 +465,64 @@ static void invalidate_ce_path(struct cache_entry *ce) cache_tree_invalidate_path(active_cache_tree, ce->name); } +static int verify_clean_subdirectory(const char *path, const char *action, + struct unpack_trees_options *o) +{ + /* + * we are about to extract "path"; we would not want to lose + * anything in the existing directory there. + */ + int namelen; + int pos, i; + struct dir_struct d; + char *pathbuf; + int cnt = 0; + + /* + * First let's make sure we do not have a local modification + * in that directory. + */ + namelen = strlen(path); + pos = cache_name_pos(path, 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]; + int len = ce_namelen(ce); + if (len < namelen || + strncmp(path, ce->name, namelen) || + ce->name[namelen] != '/') + break; + /* + * ce->name is an entry in the subdirectory. + */ + if (!ce_stage(ce)) { + verify_uptodate(ce, o); + ce->ce_mode = 0; + } + cnt++; + } + + /* + * Then we need to make sure that we do not lose a locally + * present file that is not ignored. + */ + pathbuf = xmalloc(namelen + 2); + memcpy(pathbuf, path, namelen); + strcpy(pathbuf+namelen, "/"); + + memset(&d, 0, sizeof(d)); + if (o->dir) + d.exclude_per_dir = o->dir->exclude_per_dir; + i = read_directory(&d, path, pathbuf, namelen+1, NULL); + if (i) + die("Updating '%s' would lose untracked files in it", + path); + free(pathbuf); + return cnt; +} + /* * We do not want to remove or overwrite a working tree file that * is not tracked, unless it is ignored. @@ -479,9 +534,62 @@ static void verify_absent(const char *path, const char *action, if (o->index_only || o->reset || !o->update) return; - if (!lstat(path, &st) && !(o->dir && excluded(o->dir, path))) + + if (!lstat(path, &st)) { + int cnt; + + if (o->dir && excluded(o->dir, path)) + /* + * path is explicitly excluded, so it is Ok to + * overwrite it. + */ + return; + if (S_ISDIR(st.st_mode)) { + /* + * We are checking out path "foo" and + * found "foo/." in the working tree. + * This is tricky -- if we have modified + * files that are in "foo/" we would lose + * it. + */ + cnt = verify_clean_subdirectory(path, action, o); + + /* + * If this removed entries from the index, + * what that means is: + * + * (1) the caller unpack_trees_rec() saw path/foo + * in the index, and it has not removed it because + * it thinks it is handling 'path' as blob with + * D/F conflict; + * (2) we will return "ok, we placed a merged entry + * in the index" which would cause o->pos to be + * incremented by one; + * (3) however, original o->pos now has 'path/foo' + * marked with "to be removed". + * + * We need to increment it by the number of + * deleted entries here. + */ + o->pos += cnt; + return; + } + + /* + * The previous round may already have decided to + * delete this path, which is in a subdirectory that + * is being replaced with a blob. + */ + cnt = cache_name_pos(path, strlen(path)); + if (0 <= cnt) { + struct cache_entry *ce = active_cache[cnt]; + if (!ce_stage(ce) && !ce->ce_mode) + return; + } + die("Untracked working tree file '%s' " "would be %s by merge.", path, action); + } } static int merged_entry(struct cache_entry *merge, struct cache_entry *old, @@ -526,7 +634,7 @@ static int deleted_entry(struct cache_entry *ce, struct cache_entry *old, return 1; } -static int keep_entry(struct cache_entry *ce) +static int keep_entry(struct cache_entry *ce, struct unpack_trees_options *o) { add_cache_entry(ce, ADD_CACHE_OK_TO_ADD); return 1; @@ -557,7 +665,6 @@ int threeway_merge(struct cache_entry **stages, int count; int head_match = 0; int remote_match = 0; - const char *path = NULL; int df_conflict_head = 0; int df_conflict_remote = 0; @@ -567,13 +674,10 @@ int threeway_merge(struct cache_entry **stages, int i; for (i = 1; i < o->head_idx; i++) { - if (!stages[i]) + if (!stages[i] || stages[i] == o->df_conflict_entry) any_anc_missing = 1; - else { - if (!path) - path = stages[i]->name; + else no_anc_exists = 0; - } } index = stages[0]; @@ -589,13 +693,6 @@ int threeway_merge(struct cache_entry **stages, remote = NULL; } - if (!path && index) - path = index->name; - if (!path && head) - path = head->name; - if (!path && remote) - path = remote->name; - /* First, if there's a #16 situation, note that to prevent #13 * and #14. */ @@ -647,6 +744,23 @@ int threeway_merge(struct cache_entry **stages, if (o->aggressive) { int head_deleted = !head && !df_conflict_head; int remote_deleted = !remote && !df_conflict_remote; + const char *path = NULL; + + if (index) + path = index->name; + else if (head) + path = head->name; + else if (remote) + path = remote->name; + else { + for (i = 1; i < o->head_idx; i++) { + if (stages[i] && stages[i] != o->df_conflict_entry) { + path = stages[i]->name; + break; + } + } + } + /* * Deleted in both. * Deleted in one and unchanged in the other. @@ -678,12 +792,12 @@ int threeway_merge(struct cache_entry **stages, o->nontrivial_merge = 1; - /* #2, #3, #4, #6, #7, #9, #11. */ + /* #2, #3, #4, #6, #7, #9, #10, #11. */ count = 0; if (!head_match || !remote_match) { for (i = 1; i < o->head_idx; i++) { - if (stages[i]) { - keep_entry(stages[i]); + if (stages[i] && stages[i] != o->df_conflict_entry) { + keep_entry(stages[i], o); count++; break; } @@ -696,8 +810,8 @@ int threeway_merge(struct cache_entry **stages, show_stage_entry(stderr, "remote ", stages[remote_match]); } #endif - if (head) { count += keep_entry(head); } - if (remote) { count += keep_entry(remote); } + if (head) { count += keep_entry(head, o); } + if (remote) { count += keep_entry(remote, o); } return count; } @@ -714,12 +828,18 @@ int twoway_merge(struct cache_entry **src, struct unpack_trees_options *o) { struct cache_entry *current = src[0]; - struct cache_entry *oldtree = src[1], *newtree = src[2]; + struct cache_entry *oldtree = src[1]; + struct cache_entry *newtree = src[2]; if (o->merge_size != 2) return error("Cannot do a twoway merge of %d trees", o->merge_size); + if (oldtree == o->df_conflict_entry) + oldtree = NULL; + if (newtree == o->df_conflict_entry) + newtree = NULL; + if (current) { if ((!oldtree && !newtree) || /* 4 and 5 */ (!oldtree && newtree && @@ -727,9 +847,9 @@ int twoway_merge(struct cache_entry **src, (oldtree && newtree && same(oldtree, newtree)) || /* 14 and 15 */ (oldtree && newtree && - !same(oldtree, newtree) && /* 18 and 19*/ + !same(oldtree, newtree) && /* 18 and 19 */ same(current, newtree))) { - return keep_entry(current); + return keep_entry(current, o); } else if (oldtree && !newtree && same(current, oldtree)) { /* 10 or 11 */ @@ -775,7 +895,7 @@ int bind_merge(struct cache_entry **src, if (a && old) die("Entry '%s' overlaps. Cannot bind.", a->name); if (!a) - return keep_entry(old); + return keep_entry(old, o); else return merged_entry(a, NULL, o); } @@ -805,7 +925,7 @@ int oneway_merge(struct cache_entry **src, ce_match_stat(old, &st, 1)) old->ce_flags |= htons(CE_UPDATE); } - return keep_entry(old); + return keep_entry(old, o); } return merged_entry(a, old, o); }