X-Git-Url: https://git.tokkee.org/?a=blobdiff_plain;f=unpack-trees.c;h=cac2411b9de7b4889abe6b0b84df25d24a38c7e5;hb=a2f8028d3d661b314d5a784764f2f5f9e4c2dde0;hp=d0367846d4d1d86a16c4c9d20dadb68fa35a94d9;hpb=b8ba1535bda7bd553f46ea57bdbcee1c90e500a0;p=git.git diff --git a/unpack-trees.c b/unpack-trees.c index d0367846d..cac2411b9 100644 --- a/unpack-trees.c +++ b/unpack-trees.c @@ -4,6 +4,7 @@ #include "tree-walk.h" #include "cache-tree.h" #include "unpack-trees.h" +#include "progress.h" #define DBRT_DEBUG 1 @@ -263,10 +264,12 @@ static int unpack_trees_rec(struct tree_entry_list **posns, int len, * directories, in case this unlink is the removal of the * last entry in the directory -- empty directories are removed. */ -static void unlink_entry(char *name) +static void unlink_entry(char *name, char *last_symlink) { char *cp, *prev; + if (has_symlink_leading_path(name, last_symlink)) + return; if (unlink(name)) return; prev = NULL; @@ -288,36 +291,14 @@ static void unlink_entry(char *name) } } -static volatile sig_atomic_t progress_update; - -static void progress_interval(int signum) -{ - progress_update = 1; -} - -static void setup_progress_signal(void) -{ - struct sigaction sa; - struct itimerval v; - - memset(&sa, 0, sizeof(sa)); - sa.sa_handler = progress_interval; - sigemptyset(&sa.sa_mask); - sa.sa_flags = SA_RESTART; - sigaction(SIGALRM, &sa, NULL); - - v.it_interval.tv_sec = 1; - v.it_interval.tv_usec = 0; - v.it_value = v.it_interval; - setitimer(ITIMER_REAL, &v, NULL); -} - static struct checkout state; static void check_updates(struct cache_entry **src, int nr, - struct unpack_trees_options *o) + struct unpack_trees_options *o) { unsigned short mask = htons(CE_UPDATE); - unsigned last_percent = 200, cnt = 0, total = 0; + unsigned cnt = 0, total = 0; + struct progress progress; + char last_symlink[PATH_MAX]; if (o->update && o->verbose_update) { for (total = cnt = 0; cnt < nr; cnt++) { @@ -326,50 +307,33 @@ static void check_updates(struct cache_entry **src, int nr, total++; } - /* Don't bother doing this for very small updates */ - if (total < 250) - total = 0; - - if (total) { - fprintf(stderr, "Checking files out...\n"); - setup_progress_signal(); - progress_update = 1; - } + start_progress_delay(&progress, "Checking %u files out...", + "", total, 50, 2); cnt = 0; } + *last_symlink = '\0'; while (nr--) { struct cache_entry *ce = *src++; - if (total) { - if (!ce->ce_mode || ce->ce_flags & mask) { - unsigned percent; - cnt++; - percent = (cnt * 100) / total; - if (percent != last_percent || - progress_update) { - fprintf(stderr, "%4u%% (%u/%u) done\r", - percent, cnt, total); - last_percent = percent; - progress_update = 0; - } - } - } + if (total) + if (!ce->ce_mode || ce->ce_flags & mask) + display_progress(&progress, ++cnt); if (!ce->ce_mode) { if (o->update) - unlink_entry(ce->name); + unlink_entry(ce->name, last_symlink); continue; } if (ce->ce_flags & mask) { ce->ce_flags &= ~mask; - if (o->update) + if (o->update) { checkout_entry(ce, &state, NULL); + *last_symlink = '\0'; + } } } - if (total) { - signal(SIGALRM, SIG_IGN); - fputc('\n', stderr); - } + if (total) + stop_progress(&progress);; } int unpack_trees(struct object_list *trees, struct unpack_trees_options *o) @@ -450,10 +414,6 @@ static void verify_uptodate(struct cache_entry *ce, return; errno = 0; } - if (o->reset) { - ce->ce_flags |= htons(CE_UPDATE); - return; - } if (errno == ENOENT) return; die("Entry '%s' not uptodate. Cannot merge.", ce->name); @@ -465,6 +425,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. @@ -476,9 +494,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, @@ -554,7 +625,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; @@ -564,13 +634,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]; @@ -586,13 +653,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. */ @@ -644,6 +704,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. @@ -675,11 +752,11 @@ 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]) { + if (stages[i] && stages[i] != o->df_conflict_entry) { keep_entry(stages[i], o); count++; break;