diff options
Diffstat (limited to 'unpack-trees.c')
-rw-r--r-- | unpack-trees.c | 263 |
1 files changed, 213 insertions, 50 deletions
diff --git a/unpack-trees.c b/unpack-trees.c index f88a69f8e7..a7e1712d23 100644 --- a/unpack-trees.c +++ b/unpack-trees.c @@ -111,17 +111,17 @@ void setup_unpack_trees_porcelain(struct unpack_trees_options *opts, strvec_init(&opts->msgs_to_free); if (!strcmp(cmd, "checkout")) - msg = advice_commit_before_merge + msg = advice_enabled(ADVICE_COMMIT_BEFORE_MERGE) ? _("Your local changes to the following files would be overwritten by checkout:\n%%s" "Please commit your changes or stash them before you switch branches.") : _("Your local changes to the following files would be overwritten by checkout:\n%%s"); else if (!strcmp(cmd, "merge")) - msg = advice_commit_before_merge + msg = advice_enabled(ADVICE_COMMIT_BEFORE_MERGE) ? _("Your local changes to the following files would be overwritten by merge:\n%%s" "Please commit your changes or stash them before you merge.") : _("Your local changes to the following files would be overwritten by merge:\n%%s"); else - msg = advice_commit_before_merge + msg = advice_enabled(ADVICE_COMMIT_BEFORE_MERGE) ? _("Your local changes to the following files would be overwritten by %s:\n%%s" "Please commit your changes or stash them before you %s.") : _("Your local changes to the following files would be overwritten by %s:\n%%s"); @@ -132,17 +132,17 @@ void setup_unpack_trees_porcelain(struct unpack_trees_options *opts, _("Updating the following directories would lose untracked files in them:\n%s"); if (!strcmp(cmd, "checkout")) - msg = advice_commit_before_merge + msg = advice_enabled(ADVICE_COMMIT_BEFORE_MERGE) ? _("The following untracked working tree files would be removed by checkout:\n%%s" "Please move or remove them before you switch branches.") : _("The following untracked working tree files would be removed by checkout:\n%%s"); else if (!strcmp(cmd, "merge")) - msg = advice_commit_before_merge + msg = advice_enabled(ADVICE_COMMIT_BEFORE_MERGE) ? _("The following untracked working tree files would be removed by merge:\n%%s" "Please move or remove them before you merge.") : _("The following untracked working tree files would be removed by merge:\n%%s"); else - msg = advice_commit_before_merge + msg = advice_enabled(ADVICE_COMMIT_BEFORE_MERGE) ? _("The following untracked working tree files would be removed by %s:\n%%s" "Please move or remove them before you %s.") : _("The following untracked working tree files would be removed by %s:\n%%s"); @@ -150,17 +150,17 @@ void setup_unpack_trees_porcelain(struct unpack_trees_options *opts, strvec_pushf(&opts->msgs_to_free, msg, cmd, cmd); if (!strcmp(cmd, "checkout")) - msg = advice_commit_before_merge + msg = advice_enabled(ADVICE_COMMIT_BEFORE_MERGE) ? _("The following untracked working tree files would be overwritten by checkout:\n%%s" "Please move or remove them before you switch branches.") : _("The following untracked working tree files would be overwritten by checkout:\n%%s"); else if (!strcmp(cmd, "merge")) - msg = advice_commit_before_merge + msg = advice_enabled(ADVICE_COMMIT_BEFORE_MERGE) ? _("The following untracked working tree files would be overwritten by merge:\n%%s" "Please move or remove them before you merge.") : _("The following untracked working tree files would be overwritten by merge:\n%%s"); else - msg = advice_commit_before_merge + msg = advice_enabled(ADVICE_COMMIT_BEFORE_MERGE) ? _("The following untracked working tree files would be overwritten by %s:\n%%s" "Please move or remove them before you %s.") : _("The following untracked working tree files would be overwritten by %s:\n%%s"); @@ -392,6 +392,11 @@ static void report_collided_checkout(struct index_state *index) string_list_clear(&list, 0); } +static int must_checkout(const struct cache_entry *ce) +{ + return ce->ce_flags & CE_UPDATE; +} + static int check_updates(struct unpack_trees_options *o, struct index_state *index) { @@ -442,28 +447,12 @@ static int check_updates(struct unpack_trees_options *o, if (should_update_submodules()) load_gitmodules_file(index, &state); - if (has_promisor_remote()) { + if (has_promisor_remote()) /* * Prefetch the objects that are to be checked out in the loop * below. */ - struct oid_array to_fetch = OID_ARRAY_INIT; - for (i = 0; i < index->cache_nr; i++) { - struct cache_entry *ce = index->cache[i]; - - if (!(ce->ce_flags & CE_UPDATE) || - S_ISGITLINK(ce->ce_mode)) - continue; - if (!oid_object_info_extended(the_repository, &ce->oid, - NULL, - OBJECT_INFO_FOR_PREFETCH)) - continue; - oid_array_append(&to_fetch, &ce->oid); - } - promisor_remote_get_direct(the_repository, - to_fetch.oid, to_fetch.nr); - oid_array_clear(&to_fetch); - } + prefetch_cache_entries(index, must_checkout); get_parallel_checkout_configs(&pc_workers, &pc_threshold); @@ -473,7 +462,7 @@ static int check_updates(struct unpack_trees_options *o, for (i = 0; i < index->cache_nr; i++) { struct cache_entry *ce = index->cache[i]; - if (ce->ce_flags & CE_UPDATE) { + if (must_checkout(ce)) { size_t last_pc_queue_size = pc_queue_size(); if (ce->ce_flags & CE_WT_REMOVE) @@ -490,7 +479,7 @@ static int check_updates(struct unpack_trees_options *o, errs |= run_parallel_checkout(&state, pc_workers, pc_threshold, progress, &cnt); stop_progress(&progress); - errs |= finish_delayed_checkout(&state, NULL); + errs |= finish_delayed_checkout(&state, NULL, o->verbose_update); git_attr_set_direction(GIT_ATTR_CHECKIN); if (o->clone) @@ -600,6 +589,13 @@ static void mark_ce_used(struct cache_entry *ce, struct unpack_trees_options *o) { ce->ce_flags |= CE_UNPACKED; + /* + * If this is a sparse directory, don't advance cache_bottom. + * That will be advanced later using the cache-tree data. + */ + if (S_ISSPARSEDIR(ce->ce_mode)) + return; + if (o->cache_bottom < o->src_index->cache_nr && o->src_index->cache[o->cache_bottom] == ce) { int bottom = o->cache_bottom; @@ -797,7 +793,7 @@ static int traverse_by_cache_tree(int pos, int nr_entries, int nr_names, BUG("We need cache-tree to do this optimization"); /* - * Do what unpack_callback() and unpack_nondirectories() normally + * Do what unpack_callback() and unpack_single_entry() normally * do. But we walk all paths in an iterative loop instead. * * D/F conflicts and higher stage entries are not a concern @@ -976,6 +972,7 @@ static int do_compare_entry(const struct cache_entry *ce, int pathlen, ce_len; const char *ce_name; int cmp; + unsigned ce_mode; /* * If we have not precomputed the traverse path, it is quicker @@ -998,7 +995,8 @@ static int do_compare_entry(const struct cache_entry *ce, ce_len -= pathlen; ce_name = ce->name + pathlen; - return df_name_compare(ce_name, ce_len, S_IFREG, name, namelen, mode); + ce_mode = S_ISSPARSEDIR(ce->ce_mode) ? S_IFDIR : S_IFREG; + return df_name_compare(ce_name, ce_len, ce_mode, name, namelen, mode); } static int compare_entry(const struct cache_entry *ce, const struct traverse_info *info, const struct name_entry *n) @@ -1008,6 +1006,16 @@ static int compare_entry(const struct cache_entry *ce, const struct traverse_inf return cmp; /* + * At this point, we know that we have a prefix match. If ce + * is a sparse directory, then allow an exact match. This only + * works when the input name is a directory, since ce->name + * ends in a directory separator. + */ + if (S_ISSPARSEDIR(ce->ce_mode) && + ce->ce_namelen == traverse_path_len(info, tree_entry_len(n)) + 1) + return 0; + + /* * Even if the beginning compared identically, the ce should * compare as bigger than a directory leading up to it! */ @@ -1033,13 +1041,15 @@ static struct cache_entry *create_ce_entry(const struct traverse_info *info, const struct name_entry *n, int stage, struct index_state *istate, - int is_transient) + int is_transient, + int is_sparse_directory) { size_t len = traverse_path_len(info, tree_entry_len(n)); + size_t alloc_len = is_sparse_directory ? len + 1 : len; struct cache_entry *ce = is_transient ? - make_empty_transient_cache_entry(len, NULL) : - make_empty_cache_entry(istate, len); + make_empty_transient_cache_entry(alloc_len, NULL) : + make_empty_cache_entry(istate, alloc_len); ce->ce_mode = create_ce_mode(n->mode); ce->ce_flags = create_ce_flags(stage); @@ -1048,6 +1058,13 @@ static struct cache_entry *create_ce_entry(const struct traverse_info *info, /* len+1 because the cache_entry allocates space for NUL */ make_traverse_path(ce->name, len + 1, info, n->path, n->pathlen); + if (is_sparse_directory) { + ce->name[len] = '/'; + ce->name[len + 1] = '\0'; + ce->ce_namelen++; + ce->ce_flags |= CE_SKIP_WORKTREE; + } + return ce; } @@ -1056,21 +1073,28 @@ static struct cache_entry *create_ce_entry(const struct traverse_info *info, * without actually calling it. If you change the logic here you may need to * check and change there as well. */ -static int unpack_nondirectories(int n, unsigned long mask, - unsigned long dirmask, - struct cache_entry **src, - const struct name_entry *names, - const struct traverse_info *info) +static int unpack_single_entry(int n, unsigned long mask, + unsigned long dirmask, + struct cache_entry **src, + const struct name_entry *names, + const struct traverse_info *info) { int i; struct unpack_trees_options *o = info->data; unsigned long conflicts = info->df_conflicts | dirmask; - /* Do we have *only* directories? Nothing to do */ if (mask == dirmask && !src[0]) return 0; /* + * When we have a sparse directory entry for src[0], + * then this isn't necessarily a directory-file conflict. + */ + if (mask == dirmask && src[0] && + S_ISSPARSEDIR(src[0]->ce_mode)) + conflicts = 0; + + /* * Ok, we've filled in up to any potential index entry in src[0], * now do the rest. */ @@ -1099,7 +1123,9 @@ static int unpack_nondirectories(int n, unsigned long mask, * not stored in the index. otherwise construct the * cache entry from the index aware logic. */ - src[i + o->merge] = create_ce_entry(info, names + i, stage, &o->result, o->merge); + src[i + o->merge] = create_ce_entry(info, names + i, stage, + &o->result, o->merge, + bit & dirmask); } if (o->merge) { @@ -1203,16 +1229,73 @@ static int find_cache_pos(struct traverse_info *info, return -1; } +/* + * Given a sparse directory entry 'ce', compare ce->name to + * info->name + '/' + p->path + '/' if info->name is non-empty. + * Compare ce->name to p->path + '/' otherwise. Note that + * ce->name must end in a trailing '/' because it is a sparse + * directory entry. + */ +static int sparse_dir_matches_path(const struct cache_entry *ce, + struct traverse_info *info, + const struct name_entry *p) +{ + assert(S_ISSPARSEDIR(ce->ce_mode)); + assert(ce->name[ce->ce_namelen - 1] == '/'); + + if (info->namelen) + return ce->ce_namelen == info->namelen + p->pathlen + 2 && + ce->name[info->namelen] == '/' && + !strncmp(ce->name, info->name, info->namelen) && + !strncmp(ce->name + info->namelen + 1, p->path, p->pathlen); + return ce->ce_namelen == p->pathlen + 1 && + !strncmp(ce->name, p->path, p->pathlen); +} + static struct cache_entry *find_cache_entry(struct traverse_info *info, const struct name_entry *p) { + const char *path; int pos = find_cache_pos(info, p->path, p->pathlen); struct unpack_trees_options *o = info->data; if (0 <= pos) return o->src_index->cache[pos]; - else + + /* + * Check for a sparse-directory entry named "path/". + * Due to the input p->path not having a trailing + * slash, the negative 'pos' value overshoots the + * expected position, hence "-2" instead of "-1". + */ + pos = -pos - 2; + + if (pos < 0 || pos >= o->src_index->cache_nr) return NULL; + + /* + * Due to lexicographic sorting and sparse directory + * entries ending with a trailing slash, our path as a + * sparse directory (e.g "subdir/") and our path as a + * file (e.g. "subdir") might be separated by other + * paths (e.g. "subdir-"). + */ + while (pos >= 0) { + struct cache_entry *ce = o->src_index->cache[pos]; + + if (!skip_prefix(ce->name, info->traverse_path, &path) || + strncmp(path, p->path, p->pathlen) || + path[p->pathlen] != '/') + return NULL; + + if (S_ISSPARSEDIR(ce->ce_mode) && + sparse_dir_matches_path(ce, info, p)) + return ce; + + pos--; + } + + return NULL; } static void debug_path(struct traverse_info *info) @@ -1248,6 +1331,21 @@ static void debug_unpack_callback(int n, } /* + * Returns true if and only if the given cache_entry is a + * sparse-directory entry that matches the given name_entry + * from the tree walk at the given traverse_info. + */ +static int is_sparse_directory_entry(struct cache_entry *ce, + struct name_entry *name, + struct traverse_info *info) +{ + if (!ce || !name || !S_ISSPARSEDIR(ce->ce_mode)) + return 0; + + return sparse_dir_matches_path(ce, info, name); +} + +/* * Note that traverse_by_cache_tree() duplicates some logic in this function * without actually calling it. If you change the logic here you may need to * check and change there as well. @@ -1303,7 +1401,7 @@ static int unpack_callback(int n, unsigned long mask, unsigned long dirmask, str } } - if (unpack_nondirectories(n, mask, dirmask, src, names, info) < 0) + if (unpack_single_entry(n, mask, dirmask, src, names, info) < 0) return -1; if (o->merge && src[0]) { @@ -1333,9 +1431,12 @@ static int unpack_callback(int n, unsigned long mask, unsigned long dirmask, str } } - if (traverse_trees_recursive(n, dirmask, mask & ~dirmask, - names, info) < 0) + if (!is_sparse_directory_entry(src[0], names, info) && + traverse_trees_recursive(n, dirmask, mask & ~dirmask, + names, info) < 0) { return -1; + } + return mask; } @@ -1593,9 +1694,15 @@ int unpack_trees(unsigned len, struct tree_desc *t, struct unpack_trees_options static struct cache_entry *dfc; struct pattern_list pl; int free_pattern_list = 0; + struct dir_struct dir = DIR_INIT; + + if (o->reset == UNPACK_RESET_INVALID) + BUG("o->reset had a value of 1; should be UNPACK_TREES_*_UNTRACKED"); if (len > MAX_UNPACK_TREES) die("unpack_trees takes at most %d trees", MAX_UNPACK_TREES); + if (o->dir) + BUG("o->dir is for internal use only"); trace_performance_enter(); trace2_region_enter("unpack_trees", "unpack_trees", the_repository); @@ -1606,6 +1713,16 @@ int unpack_trees(unsigned len, struct tree_desc *t, struct unpack_trees_options ensure_full_index(o->dst_index); } + if (o->reset == UNPACK_RESET_OVERWRITE_UNTRACKED && + o->preserve_ignored) + BUG("UNPACK_RESET_OVERWRITE_UNTRACKED incompatible with preserved ignored files"); + + if (!o->preserve_ignored) { + o->dir = &dir; + o->dir->flags |= DIR_SHOW_IGNORED; + setup_standard_excludes(o->dir); + } + if (!core_apply_sparse_checkout || !o->update) o->skip_sparse_checkout = 1; if (!o->skip_sparse_checkout && !o->pl) { @@ -1767,6 +1884,10 @@ int unpack_trees(unsigned len, struct tree_desc *t, struct unpack_trees_options done: if (free_pattern_list) clear_pattern_list(&pl); + if (o->dir) { + dir_clear(o->dir); + o->dir = NULL; + } trace2_region_leave("unpack_trees", "unpack_trees", the_repository); trace_performance_leave("unpack_trees"); return ret; @@ -2057,9 +2178,15 @@ static int icase_exists(struct unpack_trees_options *o, const char *name, int le return src && !ie_match_stat(o->src_index, src, st, CE_MATCH_IGNORE_VALID|CE_MATCH_IGNORE_SKIP_WORKTREE); } +enum absent_checking_type { + COMPLETELY_ABSENT, + ABSENT_ANY_DIRECTORY +}; + static int check_ok_to_remove(const char *name, int len, int dtype, const struct cache_entry *ce, struct stat *st, enum unpack_trees_error_types error_type, + enum absent_checking_type absent_type, struct unpack_trees_options *o) { const struct cache_entry *result; @@ -2094,6 +2221,10 @@ static int check_ok_to_remove(const char *name, int len, int dtype, return 0; } + /* If we only care about directories, then we can remove */ + if (absent_type == ABSENT_ANY_DIRECTORY) + return 0; + /* * The previous round may already have decided to * delete this path, which is in a subdirectory that @@ -2114,12 +2245,14 @@ static int check_ok_to_remove(const char *name, int len, int dtype, */ static int verify_absent_1(const struct cache_entry *ce, enum unpack_trees_error_types error_type, + enum absent_checking_type absent_type, struct unpack_trees_options *o) { int len; struct stat st; - if (o->index_only || o->reset || !o->update) + if (o->index_only || !o->update || + o->reset == UNPACK_RESET_OVERWRITE_UNTRACKED) return 0; len = check_leading_path(ce->name, ce_namelen(ce), 0); @@ -2139,7 +2272,8 @@ static int verify_absent_1(const struct cache_entry *ce, NULL, o); else ret = check_ok_to_remove(path, len, DT_UNKNOWN, NULL, - &st, error_type, o); + &st, error_type, + absent_type, o); } free(path); return ret; @@ -2154,7 +2288,7 @@ static int verify_absent_1(const struct cache_entry *ce, return check_ok_to_remove(ce->name, ce_namelen(ce), ce_to_dtype(ce), ce, &st, - error_type, o); + error_type, absent_type, o); } } @@ -2164,14 +2298,23 @@ static int verify_absent(const struct cache_entry *ce, { if (!o->skip_sparse_checkout && (ce->ce_flags & CE_NEW_SKIP_WORKTREE)) return 0; - return verify_absent_1(ce, error_type, o); + return verify_absent_1(ce, error_type, COMPLETELY_ABSENT, o); +} + +static int verify_absent_if_directory(const struct cache_entry *ce, + enum unpack_trees_error_types error_type, + struct unpack_trees_options *o) +{ + if (!o->skip_sparse_checkout && (ce->ce_flags & CE_NEW_SKIP_WORKTREE)) + return 0; + return verify_absent_1(ce, error_type, ABSENT_ANY_DIRECTORY, o); } static int verify_absent_sparse(const struct cache_entry *ce, enum unpack_trees_error_types error_type, struct unpack_trees_options *o) { - return verify_absent_1(ce, error_type, o); + return verify_absent_1(ce, error_type, COMPLETELY_ABSENT, o); } static int merged_entry(const struct cache_entry *ce, @@ -2245,6 +2388,12 @@ static int merged_entry(const struct cache_entry *ce, * Previously unmerged entry left as an existence * marker by read_index_unmerged(); */ + if (verify_absent_if_directory(merge, + ERROR_WOULD_LOSE_UNTRACKED_OVERWRITTEN, o)) { + discard_cache_entry(merge); + return -1; + } + invalidate_ce_path(old, o); } @@ -2262,7 +2411,10 @@ static int deleted_entry(const struct cache_entry *ce, if (verify_absent(ce, ERROR_WOULD_LOSE_UNTRACKED_REMOVED, o)) return -1; return 0; + } else if (verify_absent_if_directory(ce, ERROR_WOULD_LOSE_UNTRACKED_REMOVED, o)) { + return -1; } + if (!(old->ce_flags & CE_CONFLICTED) && verify_uptodate(old, o)) return -1; add_entry(o, ce, CE_REMOVE, 0); @@ -2509,6 +2661,17 @@ int twoway_merge(const struct cache_entry * const *src, same(current, oldtree) && !same(current, newtree)) { /* 20 or 21 */ return merged_entry(newtree, current, o); + } else if (current && !oldtree && newtree && + S_ISSPARSEDIR(current->ce_mode) != S_ISSPARSEDIR(newtree->ce_mode) && + ce_stage(current) == 0) { + /* + * This case is a directory/file conflict across the sparse-index + * boundary. When we are changing from one path to another via + * 'git checkout', then we want to replace one entry with another + * via merged_entry(). If there are staged changes, then we should + * reject the merge instead. + */ + return merged_entry(newtree, current, o); } else return reject_merge(current, o); } |