diff options
Diffstat (limited to 'unpack-trees.c')
-rw-r--r-- | unpack-trees.c | 162 |
1 files changed, 102 insertions, 60 deletions
diff --git a/unpack-trees.c b/unpack-trees.c index 89dd279f89..ccfeb6e245 100644 --- a/unpack-trees.c +++ b/unpack-trees.c @@ -5,41 +5,30 @@ #include "cache-tree.h" #include "unpack-trees.h" #include "progress.h" +#include "refs.h" #define DBRT_DEBUG 1 struct tree_entry_list { struct tree_entry_list *next; - unsigned directory : 1; - unsigned executable : 1; - unsigned symlink : 1; unsigned int mode; const char *name; const unsigned char *sha1; }; -static struct tree_entry_list *create_tree_entry_list(struct tree *tree) +static struct tree_entry_list *create_tree_entry_list(struct tree_desc *desc) { - struct tree_desc desc; struct name_entry one; struct tree_entry_list *ret = NULL; struct tree_entry_list **list_p = &ret; - if (!tree->object.parsed) - parse_tree(tree); - - init_tree_desc(&desc, tree->buffer, tree->size); - - while (tree_entry(&desc, &one)) { + 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->directory = S_ISDIR(one.mode) != 0; - entry->executable = (one.mode & S_IXUSR) != 0; - entry->symlink = S_ISLNK(one.mode) != 0; entry->next = NULL; *list_p = entry; @@ -69,10 +58,17 @@ static int entcmp(const char *name1, int dir1, const char *name2, int dir2) 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 i_stk = i_stk; @@ -140,9 +136,9 @@ static int unpack_trees_rec(struct tree_entry_list **posns, int len, #endif if (!first || entcmp(first, firstdir, posns[i]->name, - posns[i]->directory) > 0) { + S_ISDIR(posns[i]->mode)) > 0) { first = posns[i]->name; - firstdir = posns[i]->directory; + firstdir = S_ISDIR(posns[i]->mode); } } /* No name means we're done */ @@ -156,10 +152,11 @@ static int unpack_trees_rec(struct tree_entry_list **posns, int len, 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_cache_entry_at(o->pos); + remove = o->pos; } for (i = 0; i < len; i++) { @@ -176,11 +173,13 @@ static int unpack_trees_rec(struct tree_entry_list **posns, int len, continue; } - if (posns[i]->directory) { + if (S_ISDIR(posns[i]->mode)) { struct tree *tree = lookup_tree(posns[i]->sha1); + struct tree_desc t; any_dirs = 1; parse_tree(tree); - subposns[i] = create_tree_entry_list(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; @@ -223,13 +222,14 @@ static int unpack_trees_rec(struct tree_entry_list **posns, int len, printf("\n"); } #endif - ret = o->fn(src, o); + ret = o->fn(src, o, remove); #if DBRT_DEBUG > 1 printf("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); @@ -336,12 +336,10 @@ static void check_updates(struct cache_entry **src, int nr, stop_progress(&progress);; } -int unpack_trees(struct object_list *trees, struct unpack_trees_options *o) +int unpack_trees(unsigned len, struct tree_desc *t, struct unpack_trees_options *o) { - unsigned len = object_list_length(trees); struct tree_entry_list **posns; int i; - struct object_list *posn = trees; struct tree_entry_list df_conflict_list; static struct cache_entry *dfc; @@ -361,10 +359,9 @@ int unpack_trees(struct object_list *trees, struct unpack_trees_options *o) if (len) { posns = xmalloc(len * sizeof(struct tree_entry_list *)); - for (i = 0; i < len; i++) { - posns[i] = create_tree_entry_list((struct tree *) posn->item); - posn = posn->next; - } + 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)) return -1; @@ -412,6 +409,15 @@ static void verify_uptodate(struct cache_entry *ce, unsigned changed = ce_match_stat(ce, &st, 1); if (!changed) return; + /* + * NEEDSWORK: the current default policy is to allow + * submodule to be out of sync wrt the supermodule + * index. This needs to be tightened later for + * submodules that are marked to be automatically + * checked out. + */ + if (S_ISGITLINK(ntohl(ce->ce_mode))) + return; errno = 0; } if (errno == ENOENT) @@ -425,11 +431,24 @@ 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, +/* + * Check that checking out ce->sha1 in subdir ce->name is not + * going to overwrite any working files. + * + * Currently, git does not checkout subprojects during a superproject + * checkout, so it is not going to overwrite anything. + */ +static int verify_clean_submodule(struct cache_entry *ce, const char *action, + struct unpack_trees_options *o) +{ + return 0; +} + +static int verify_clean_subdirectory(struct cache_entry *ce, const char *action, struct unpack_trees_options *o) { /* - * we are about to extract "path"; we would not want to lose + * we are about to extract "ce->name"; we would not want to lose * anything in the existing directory there. */ int namelen; @@ -437,13 +456,24 @@ static int verify_clean_subdirectory(const char *path, const char *action, struct dir_struct d; char *pathbuf; int cnt = 0; + unsigned char sha1[20]; + + if (S_ISGITLINK(ntohl(ce->ce_mode)) && + resolve_gitlink_ref(ce->name, "HEAD", sha1) == 0) { + /* If we are not going to update the submodule, then + * we don't care. + */ + if (!hashcmp(sha1, ce->sha1)) + return 0; + return verify_clean_submodule(ce, action, o); + } /* * First let's make sure we do not have a local modification * in that directory. */ - namelen = strlen(path); - pos = cache_name_pos(path, namelen); + namelen = strlen(ce->name); + pos = cache_name_pos(ce->name, namelen); if (0 <= pos) return cnt; /* we have it as nondirectory */ pos = -pos - 1; @@ -451,7 +481,7 @@ static int verify_clean_subdirectory(const char *path, const char *action, struct cache_entry *ce = active_cache[i]; int len = ce_namelen(ce); if (len < namelen || - strncmp(path, ce->name, namelen) || + strncmp(ce->name, ce->name, namelen) || ce->name[namelen] != '/') break; /* @@ -469,16 +499,16 @@ static int verify_clean_subdirectory(const char *path, const char *action, * present file that is not ignored. */ pathbuf = xmalloc(namelen + 2); - memcpy(pathbuf, path, namelen); + memcpy(pathbuf, ce->name, 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); + i = read_directory(&d, ce->name, pathbuf, namelen+1, NULL); if (i) die("Updating '%s' would lose untracked files in it", - path); + ce->name); free(pathbuf); return cnt; } @@ -487,7 +517,7 @@ static int verify_clean_subdirectory(const char *path, const char *action, * We do not want to remove or overwrite a working tree file that * is not tracked, unless it is ignored. */ -static void verify_absent(const char *path, const char *action, +static void verify_absent(struct cache_entry *ce, const char *action, struct unpack_trees_options *o) { struct stat st; @@ -495,15 +525,15 @@ static void verify_absent(const char *path, const char *action, if (o->index_only || o->reset || !o->update) return; - if (has_symlink_leading_path(path, NULL)) + if (has_symlink_leading_path(ce->name, NULL)) return; - if (!lstat(path, &st)) { + if (!lstat(ce->name, &st)) { int cnt; - if (o->dir && excluded(o->dir, path)) + if (o->dir && excluded(o->dir, ce->name)) /* - * path is explicitly excluded, so it is Ok to + * ce->name is explicitly excluded, so it is Ok to * overwrite it. */ return; @@ -515,7 +545,7 @@ static void verify_absent(const char *path, const char *action, * files that are in "foo/" we would lose * it. */ - cnt = verify_clean_subdirectory(path, action, o); + cnt = verify_clean_subdirectory(ce, action, o); /* * If this removed entries from the index, @@ -543,7 +573,7 @@ static void verify_absent(const char *path, const char *action, * delete this path, which is in a subdirectory that * is being replaced with a blob. */ - cnt = cache_name_pos(path, strlen(path)); + cnt = cache_name_pos(ce->name, strlen(ce->name)); if (0 <= cnt) { struct cache_entry *ce = active_cache[cnt]; if (!ce_stage(ce) && !ce->ce_mode) @@ -551,7 +581,7 @@ static void verify_absent(const char *path, const char *action, } die("Untracked working tree file '%s' " - "would be %s by merge.", path, action); + "would be %s by merge.", ce->name, action); } } @@ -575,7 +605,7 @@ static int merged_entry(struct cache_entry *merge, struct cache_entry *old, } } else { - verify_absent(merge->name, "overwritten", o); + verify_absent(merge, "overwritten", o); invalidate_ce_path(merge); } @@ -590,7 +620,7 @@ static int deleted_entry(struct cache_entry *ce, struct cache_entry *old, if (old) verify_uptodate(old, o); else - verify_absent(ce->name, "removed", o); + verify_absent(ce, "removed", o); ce->ce_mode = 0; add_cache_entry(ce, ADD_CACHE_OK_TO_ADD|ADD_CACHE_OK_TO_REPLACE); invalidate_ce_path(ce); @@ -620,7 +650,8 @@ static void show_stage_entry(FILE *o, #endif int threeway_merge(struct cache_entry **stages, - struct unpack_trees_options *o) + struct unpack_trees_options *o, + int remove) { struct cache_entry *index; struct cache_entry *head; @@ -698,8 +729,10 @@ int threeway_merge(struct cache_entry **stages, } /* #1 */ - if (!head && !remote && any_anc_missing) + if (!head && !remote && any_anc_missing) { + remove_entry(remove); return 0; + } /* Under the new "aggressive" rule, we resolve mostly trivial * cases that we historically had git-merge-one-file resolve. @@ -707,18 +740,18 @@ 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; + struct cache_entry *ce = NULL; if (index) - path = index->name; + ce = index; else if (head) - path = head->name; + ce = head; else if (remote) - path = remote->name; + ce = remote; else { for (i = 1; i < o->head_idx; i++) { if (stages[i] && stages[i] != o->df_conflict_entry) { - path = stages[i]->name; + ce = stages[i]; break; } } @@ -731,10 +764,11 @@ 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 (path && !head_deleted) - verify_absent(path, "removed", o); + else if (ce && !head_deleted) + verify_absent(ce, "removed", o); return 0; } /* @@ -753,6 +787,7 @@ int threeway_merge(struct cache_entry **stages, verify_uptodate(index, o); } + remove_entry(remove); o->nontrivial_merge = 1; /* #2, #3, #4, #6, #7, #9, #10, #11. */ @@ -788,7 +823,8 @@ int threeway_merge(struct cache_entry **stages, * */ int twoway_merge(struct cache_entry **src, - struct unpack_trees_options *o) + struct unpack_trees_options *o, + int remove) { struct cache_entry *current = src[0]; struct cache_entry *oldtree = src[1]; @@ -816,6 +852,7 @@ 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 && @@ -825,6 +862,7 @@ int twoway_merge(struct cache_entry **src, } else { /* all other failures */ + remove_entry(remove); if (oldtree) reject_merge(oldtree); if (current) @@ -836,8 +874,8 @@ int twoway_merge(struct cache_entry **src, } else if (newtree) return merged_entry(newtree, current, o); - else - return deleted_entry(oldtree, current, o); + remove_entry(remove); + return deleted_entry(oldtree, current, o); } /* @@ -847,7 +885,8 @@ 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) + struct unpack_trees_options *o, + int remove) { struct cache_entry *old = src[0]; struct cache_entry *a = src[1]; @@ -870,7 +909,8 @@ int bind_merge(struct cache_entry **src, * - take the stat information from stage0, take the data from stage1 */ int oneway_merge(struct cache_entry **src, - struct unpack_trees_options *o) + struct unpack_trees_options *o, + int remove) { struct cache_entry *old = src[0]; struct cache_entry *a = src[1]; @@ -879,8 +919,10 @@ int oneway_merge(struct cache_entry **src, return error("Cannot do a oneway merge of %d trees", o->merge_size); - if (!a) + if (!a) { + remove_entry(remove); return deleted_entry(old, old, o); + } if (old && same(old, a)) { if (o->reset) { struct stat st; |