diff options
author | Vincent Ambo <mail@tazj.in> | 2020-11-21T18·20+0100 |
---|---|---|
committer | Vincent Ambo <mail@tazj.in> | 2020-11-21T18·45+0100 |
commit | f4609b896fac842433bd495c166d5987852a6a73 (patch) | |
tree | 95511c465c54c4f5d27e5d39ce187e2a1dd82bd3 /third_party/git/unpack-trees.c | |
parent | 082c006c04343a78d87b6c6ab3608c25d6213c3f (diff) |
merge(3p/git): Merge git subtree at v2.29.2 r/1890
This also bumps the stable nixpkgs to 20.09 as of 2020-11-21, because there is some breakage in the git build related to the netrc credentials helper which someone has taken care of in nixpkgs. The stable channel is not used for anything other than git, so this should be fine. Change-Id: I3575a19dab09e1e9556cf8231d717de9890484fb
Diffstat (limited to 'third_party/git/unpack-trees.c')
-rw-r--r-- | third_party/git/unpack-trees.c | 533 |
1 files changed, 353 insertions, 180 deletions
diff --git a/third_party/git/unpack-trees.c b/third_party/git/unpack-trees.c index 62276d4fef5f..323280dd48b2 100644 --- a/third_party/git/unpack-trees.c +++ b/third_party/git/unpack-trees.c @@ -1,5 +1,5 @@ #include "cache.h" -#include "argv-array.h" +#include "strvec.h" #include "repository.h" #include "config.h" #include "dir.h" @@ -11,12 +11,11 @@ #include "refs.h" #include "attr.h" #include "split-index.h" -#include "dir.h" #include "submodule.h" #include "submodule-config.h" #include "fsmonitor.h" #include "object-store.h" -#include "fetch-object.h" +#include "promisor-remote.h" /* * Error messages expected by scripts out of plumbing commands such as @@ -25,7 +24,7 @@ * situation better. See how "git checkout" and "git merge" replaces * them using setup_unpack_trees_porcelain(), for example. */ -static const char *unpack_plumbing_errors[NB_UNPACK_TREES_ERROR_TYPES] = { +static const char *unpack_plumbing_errors[NB_UNPACK_TREES_WARNING_TYPES] = { /* ERROR_WOULD_OVERWRITE */ "Entry '%s' would be overwritten by merge. Cannot merge.", @@ -44,17 +43,20 @@ static const char *unpack_plumbing_errors[NB_UNPACK_TREES_ERROR_TYPES] = { /* ERROR_BIND_OVERLAP */ "Entry '%s' overlaps with '%s'. Cannot bind.", - /* ERROR_SPARSE_NOT_UPTODATE_FILE */ - "Entry '%s' not uptodate. Cannot update sparse checkout.", + /* ERROR_WOULD_LOSE_SUBMODULE */ + "Submodule '%s' cannot checkout new HEAD.", - /* ERROR_WOULD_LOSE_ORPHANED_OVERWRITTEN */ - "Working tree file '%s' would be overwritten by sparse checkout update.", + /* NB_UNPACK_TREES_ERROR_TYPES; just a meta value */ + "", - /* ERROR_WOULD_LOSE_ORPHANED_REMOVED */ - "Working tree file '%s' would be removed by sparse checkout update.", + /* WARNING_SPARSE_NOT_UPTODATE_FILE */ + "Path '%s' not uptodate; will not remove from working tree.", - /* ERROR_WOULD_LOSE_SUBMODULE */ - "Submodule '%s' cannot checkout new HEAD.", + /* WARNING_SPARSE_UNMERGED_FILE */ + "Path '%s' unmerged; will not remove from working tree.", + + /* WARNING_SPARSE_ORPHANED_NOT_OVERWRITTEN */ + "Path '%s' already present; will not overwrite with sparse update.", }; #define ERRORMSG(o,type) \ @@ -104,7 +106,7 @@ void setup_unpack_trees_porcelain(struct unpack_trees_options *opts, const char **msgs = opts->msgs; const char *msg; - argv_array_init(&opts->msgs_to_free); + strvec_init(&opts->msgs_to_free); if (!strcmp(cmd, "checkout")) msg = advice_commit_before_merge @@ -122,7 +124,7 @@ void setup_unpack_trees_porcelain(struct unpack_trees_options *opts, "Please commit your changes or stash them before you %s.") : _("Your local changes to the following files would be overwritten by %s:\n%%s"); msgs[ERROR_WOULD_OVERWRITE] = msgs[ERROR_NOT_UPTODATE_FILE] = - argv_array_pushf(&opts->msgs_to_free, msg, cmd, cmd); + strvec_pushf(&opts->msgs_to_free, msg, cmd, cmd); msgs[ERROR_NOT_UPTODATE_DIR] = _("Updating the following directories would lose untracked files in them:\n%s"); @@ -143,7 +145,7 @@ void setup_unpack_trees_porcelain(struct unpack_trees_options *opts, "Please move or remove them before you %s.") : _("The following untracked working tree files would be removed by %s:\n%%s"); msgs[ERROR_WOULD_LOSE_UNTRACKED_REMOVED] = - argv_array_pushf(&opts->msgs_to_free, msg, cmd, cmd); + strvec_pushf(&opts->msgs_to_free, msg, cmd, cmd); if (!strcmp(cmd, "checkout")) msg = advice_commit_before_merge @@ -161,7 +163,7 @@ void setup_unpack_trees_porcelain(struct unpack_trees_options *opts, "Please move or remove them before you %s.") : _("The following untracked working tree files would be overwritten by %s:\n%%s"); msgs[ERROR_WOULD_LOSE_UNTRACKED_OVERWRITTEN] = - argv_array_pushf(&opts->msgs_to_free, msg, cmd, cmd); + strvec_pushf(&opts->msgs_to_free, msg, cmd, cmd); /* * Special case: ERROR_BIND_OVERLAP refers to a pair of paths, we @@ -169,15 +171,16 @@ void setup_unpack_trees_porcelain(struct unpack_trees_options *opts, */ msgs[ERROR_BIND_OVERLAP] = _("Entry '%s' overlaps with '%s'. Cannot bind."); - msgs[ERROR_SPARSE_NOT_UPTODATE_FILE] = - _("Cannot update sparse checkout: the following entries are not up to date:\n%s"); - msgs[ERROR_WOULD_LOSE_ORPHANED_OVERWRITTEN] = - _("The following working tree files would be overwritten by sparse checkout update:\n%s"); - msgs[ERROR_WOULD_LOSE_ORPHANED_REMOVED] = - _("The following working tree files would be removed by sparse checkout update:\n%s"); msgs[ERROR_WOULD_LOSE_SUBMODULE] = _("Cannot update submodule:\n%s"); + msgs[WARNING_SPARSE_NOT_UPTODATE_FILE] = + _("The following paths are not up to date and were left despite sparse patterns:\n%s"); + msgs[WARNING_SPARSE_UNMERGED_FILE] = + _("The following paths are unmerged and were left despite sparse patterns:\n%s"); + msgs[WARNING_SPARSE_ORPHANED_NOT_OVERWRITTEN] = + _("The following paths were already present and thus not updated despite sparse patterns:\n%s"); + opts->show_all_errors = 1; /* rejected paths may not have a static buffer */ for (i = 0; i < ARRAY_SIZE(opts->unpack_rejects); i++) @@ -186,7 +189,7 @@ void setup_unpack_trees_porcelain(struct unpack_trees_options *opts, void clear_unpack_trees_porcelain(struct unpack_trees_options *opts) { - argv_array_clear(&opts->msgs_to_free); + strvec_clear(&opts->msgs_to_free); memset(opts->msgs, 0, sizeof(opts->msgs)); } @@ -227,7 +230,7 @@ static int add_rejected_path(struct unpack_trees_options *o, /* * Otherwise, insert in a list for future display by - * display_error_msgs() + * display_(error|warning)_msgs() */ string_list_append(&o->unpack_rejects[e], path); return -1; @@ -238,13 +241,16 @@ static int add_rejected_path(struct unpack_trees_options *o, */ static void display_error_msgs(struct unpack_trees_options *o) { - int e, i; - int something_displayed = 0; + int e; + unsigned error_displayed = 0; for (e = 0; e < NB_UNPACK_TREES_ERROR_TYPES; e++) { struct string_list *rejects = &o->unpack_rejects[e]; + if (rejects->nr > 0) { + int i; struct strbuf path = STRBUF_INIT; - something_displayed = 1; + + error_displayed = 1; for (i = 0; i < rejects->nr; i++) strbuf_addf(&path, "\t%s\n", rejects->items[i].string); error(ERRORMSG(o, e), super_prefixed(path.buf)); @@ -252,10 +258,36 @@ static void display_error_msgs(struct unpack_trees_options *o) } string_list_clear(rejects, 0); } - if (something_displayed) + if (error_displayed) fprintf(stderr, _("Aborting\n")); } +/* + * display all the warning messages stored in a nice way + */ +static void display_warning_msgs(struct unpack_trees_options *o) +{ + int e; + unsigned warning_displayed = 0; + for (e = NB_UNPACK_TREES_ERROR_TYPES + 1; + e < NB_UNPACK_TREES_WARNING_TYPES; e++) { + struct string_list *rejects = &o->unpack_rejects[e]; + + if (rejects->nr > 0) { + int i; + struct strbuf path = STRBUF_INIT; + + warning_displayed = 1; + for (i = 0; i < rejects->nr; i++) + strbuf_addf(&path, "\t%s\n", rejects->items[i].string); + warning(ERRORMSG(o, e), super_prefixed(path.buf)); + strbuf_release(&path); + } + string_list_clear(rejects, 0); + } + if (warning_displayed) + fprintf(stderr, _("After fixing the above paths, you may want to run `git sparse-checkout reapply`.\n")); +} static int check_submodule_move_head(const struct cache_entry *ce, const char *old_id, const char *new_id, @@ -276,9 +308,9 @@ static int check_submodule_move_head(const struct cache_entry *ce, } /* - * Preform the loading of the repository's gitmodules file. This function is + * Perform the loading of the repository's gitmodules file. This function is * used by 'check_update()' to perform loading of the gitmodules file in two - * differnt situations: + * different situations: * (1) before removing entries from the working tree if the gitmodules file has * been marked for removal. This situation is specified by 'state' == NULL. * (2) before checking out entries to the working tree if the gitmodules file @@ -292,19 +324,19 @@ static void load_gitmodules_file(struct index_state *index, if (pos >= 0) { struct cache_entry *ce = index->cache[pos]; if (!state && ce->ce_flags & CE_WT_REMOVE) { - repo_read_gitmodules(the_repository); + repo_read_gitmodules(the_repository, 0); } else if (state && (ce->ce_flags & CE_UPDATE)) { submodule_free(the_repository); checkout_entry(ce, state, NULL, NULL); - repo_read_gitmodules(the_repository); + repo_read_gitmodules(the_repository, 0); } } } -static struct progress *get_progress(struct unpack_trees_options *o) +static struct progress *get_progress(struct unpack_trees_options *o, + struct index_state *index) { unsigned cnt = 0, total = 0; - struct index_state *index = &o->result; if (!o->update || !o->verbose_update) return NULL; @@ -358,12 +390,12 @@ static void report_collided_checkout(struct index_state *index) string_list_clear(&list, 0); } -static int check_updates(struct unpack_trees_options *o) +static int check_updates(struct unpack_trees_options *o, + struct index_state *index) { unsigned cnt = 0; int errs = 0; struct progress *progress; - struct index_state *index = &o->result; struct checkout state = CHECKOUT_INIT; int i; @@ -372,16 +404,22 @@ static int check_updates(struct unpack_trees_options *o) state.quiet = 1; state.refresh_cache = 1; state.istate = index; + clone_checkout_metadata(&state.meta, &o->meta, NULL); + + if (!o->update || o->dry_run) { + remove_marked_cache_entries(index, 0); + trace_performance_leave("check_updates"); + return 0; + } if (o->clone) setup_collided_checkout_detection(&state, index); - progress = get_progress(o); + progress = get_progress(o, index); - if (o->update) - git_attr_set_direction(GIT_ATTR_CHECKOUT); + git_attr_set_direction(GIT_ATTR_CHECKOUT); - if (should_update_submodules() && o->update && !o->dry_run) + if (should_update_submodules()) load_gitmodules_file(index, NULL); for (i = 0; i < index->cache_nr; i++) { @@ -389,18 +427,18 @@ static int check_updates(struct unpack_trees_options *o) if (ce->ce_flags & CE_WT_REMOVE) { display_progress(progress, ++cnt); - if (o->update && !o->dry_run) - unlink_entry(ce); + unlink_entry(ce); } } + remove_marked_cache_entries(index, 0); remove_scheduled_dirs(); - if (should_update_submodules() && o->update && !o->dry_run) + if (should_update_submodules()) load_gitmodules_file(index, &state); enable_delayed_checkout(&state); - if (repository_format_partial_clone && o->update && !o->dry_run) { + if (has_promisor_remote()) { /* * Prefetch the objects that are to be checked out in the loop * below. @@ -418,9 +456,8 @@ static int check_updates(struct unpack_trees_options *o) continue; oid_array_append(&to_fetch, &ce->oid); } - if (to_fetch.nr) - fetch_objects(repository_format_partial_clone, - to_fetch.oid, to_fetch.nr); + promisor_remote_get_direct(the_repository, + to_fetch.oid, to_fetch.nr); oid_array_clear(&to_fetch); } for (i = 0; i < index->cache_nr; i++) { @@ -432,15 +469,12 @@ static int check_updates(struct unpack_trees_options *o) ce->name); display_progress(progress, ++cnt); ce->ce_flags &= ~CE_UPDATE; - if (o->update && !o->dry_run) { - errs |= checkout_entry(ce, &state, NULL, NULL); - } + errs |= checkout_entry(ce, &state, NULL, NULL); } } stop_progress(&progress); errs |= finish_delayed_checkout(&state, NULL); - if (o->update) - git_attr_set_direction(GIT_ATTR_CHECKIN); + git_attr_set_direction(GIT_ATTR_CHECKIN); if (o->clone) report_collided_checkout(index); @@ -503,19 +537,39 @@ static int apply_sparse_checkout(struct index_state *istate, * also stat info may have lost after merged_entry() so calling * verify_uptodate() again may fail */ - if (!(ce->ce_flags & CE_UPDATE) && verify_uptodate_sparse(ce, o)) + if (!(ce->ce_flags & CE_UPDATE) && + verify_uptodate_sparse(ce, o)) { + ce->ce_flags &= ~CE_SKIP_WORKTREE; return -1; + } ce->ce_flags |= CE_WT_REMOVE; ce->ce_flags &= ~CE_UPDATE; } if (was_skip_worktree && !ce_skip_worktree(ce)) { - if (verify_absent_sparse(ce, ERROR_WOULD_LOSE_UNTRACKED_OVERWRITTEN, o)) + if (verify_absent_sparse(ce, WARNING_SPARSE_ORPHANED_NOT_OVERWRITTEN, o)) return -1; ce->ce_flags |= CE_UPDATE; } return 0; } +static int warn_conflicted_path(struct index_state *istate, + int i, + struct unpack_trees_options *o) +{ + char *conflicting_path = istate->cache[i]->name; + int count = 0; + + add_rejected_path(o, WARNING_SPARSE_UNMERGED_FILE, conflicting_path); + + /* Find out how many higher stage entries are at same path */ + while ((++count) + i < istate->cache_nr && + !strcmp(conflicting_path, istate->cache[count + i]->name)) + ; /* do nothing */ + + return count; +} + static inline int call_unpack_fn(const struct cache_entry * const *src, struct unpack_trees_options *o) { @@ -632,7 +686,7 @@ static int unpack_index_entry(struct cache_entry *ce, return ret; } -static int find_cache_pos(struct traverse_info *, const struct name_entry *); +static int find_cache_pos(struct traverse_info *, const char *p, size_t len); static void restore_cache_bottom(struct traverse_info *info, int bottom) { @@ -651,7 +705,7 @@ static int switch_cache_bottom(struct traverse_info *info) if (o->diff_index_cached) return 0; ret = o->cache_bottom; - pos = find_cache_pos(info->prev, &info->name); + pos = find_cache_pos(info->prev, info->name, info->namelen); if (pos < -1) o->cache_bottom = -2 - pos; @@ -686,21 +740,21 @@ static int index_pos_by_traverse_info(struct name_entry *names, struct traverse_info *info) { struct unpack_trees_options *o = info->data; - int len = traverse_path_len(info, names); - char *name = xmalloc(len + 1 /* slash */ + 1 /* NUL */); + struct strbuf name = STRBUF_INIT; int pos; - make_traverse_path(name, info, names); - name[len++] = '/'; - name[len] = '\0'; - pos = index_name_pos(o->src_index, name, len); + strbuf_make_traverse_path(&name, info, names->path, names->pathlen); + strbuf_addch(&name, '/'); + pos = index_name_pos(o->src_index, name.buf, name.len); if (pos >= 0) BUG("This is a directory and should not exist in index"); pos = -pos - 1; - if (!starts_with(o->src_index->cache[pos]->name, name) || - (pos > 0 && starts_with(o->src_index->cache[pos-1]->name, name))) - BUG("pos must point at the first entry in this directory"); - free(name); + if (pos >= o->src_index->cache_nr || + !starts_with(o->src_index->cache[pos]->name, name.buf) || + (pos > 0 && starts_with(o->src_index->cache[pos-1]->name, name.buf))) + BUG("pos %d doesn't point to the first entry of %s in index", + pos, name.buf); + strbuf_release(&name); return pos; } @@ -811,8 +865,10 @@ static int traverse_trees_recursive(int n, unsigned long dirmask, newinfo = *info; newinfo.prev = info; newinfo.pathspec = info->pathspec; - newinfo.name = *p; - newinfo.pathlen += tree_entry_len(p) + 1; + newinfo.name = p->path; + newinfo.namelen = p->pathlen; + newinfo.mode = p->mode; + newinfo.pathlen = st_add3(newinfo.pathlen, tree_entry_len(p), 1); newinfo.df_conflicts |= df_conflicts; /* @@ -863,14 +919,18 @@ static int traverse_trees_recursive(int n, unsigned long dirmask, * itself - the caller needs to do the final check for the cache * entry having more data at the end! */ -static int do_compare_entry_piecewise(const struct cache_entry *ce, const struct traverse_info *info, const struct name_entry *n) +static int do_compare_entry_piecewise(const struct cache_entry *ce, + const struct traverse_info *info, + const char *name, size_t namelen, + unsigned mode) { - int len, pathlen, ce_len; + int pathlen, ce_len; const char *ce_name; if (info->prev) { int cmp = do_compare_entry_piecewise(ce, info->prev, - &info->name); + info->name, info->namelen, + info->mode); if (cmp) return cmp; } @@ -884,15 +944,15 @@ static int do_compare_entry_piecewise(const struct cache_entry *ce, const struct ce_len -= pathlen; ce_name = ce->name + pathlen; - len = tree_entry_len(n); - return df_name_compare(ce_name, ce_len, S_IFREG, n->path, len, n->mode); + return df_name_compare(ce_name, ce_len, S_IFREG, name, namelen, mode); } static int do_compare_entry(const struct cache_entry *ce, const struct traverse_info *info, - const struct name_entry *n) + const char *name, size_t namelen, + unsigned mode) { - int len, pathlen, ce_len; + int pathlen, ce_len; const char *ce_name; int cmp; @@ -902,7 +962,7 @@ static int do_compare_entry(const struct cache_entry *ce, * it is quicker to use the precomputed version. */ if (!info->traverse_path) - return do_compare_entry_piecewise(ce, info, n); + return do_compare_entry_piecewise(ce, info, name, namelen, mode); cmp = strncmp(ce->name, info->traverse_path, info->pathlen); if (cmp) @@ -917,13 +977,12 @@ static int do_compare_entry(const struct cache_entry *ce, ce_len -= pathlen; ce_name = ce->name + pathlen; - len = tree_entry_len(n); - return df_name_compare(ce_name, ce_len, S_IFREG, n->path, len, n->mode); + return df_name_compare(ce_name, ce_len, S_IFREG, name, namelen, 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); + int cmp = do_compare_entry(ce, info, n->path, n->pathlen, n->mode); if (cmp) return cmp; @@ -931,7 +990,7 @@ static int compare_entry(const struct cache_entry *ce, const struct traverse_inf * 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); + return ce_namelen(ce) > traverse_path_len(info, tree_entry_len(n)); } static int ce_in_traverse_path(const struct cache_entry *ce, @@ -939,7 +998,8 @@ static int ce_in_traverse_path(const struct cache_entry *ce, { if (!info->prev) return 1; - if (do_compare_entry(ce, info->prev, &info->name)) + if (do_compare_entry(ce, info->prev, + info->name, info->namelen, info->mode)) return 0; /* * If ce (blob) is the same name as the path (which is a tree @@ -954,7 +1014,7 @@ static struct cache_entry *create_ce_entry(const struct traverse_info *info, struct index_state *istate, int is_transient) { - int len = traverse_path_len(info, n); + size_t len = traverse_path_len(info, tree_entry_len(n)); struct cache_entry *ce = is_transient ? make_empty_transient_cache_entry(len) : @@ -964,7 +1024,8 @@ static struct cache_entry *create_ce_entry(const struct traverse_info *info, ce->ce_flags = create_ce_flags(stage); ce->ce_namelen = len; oidcpy(&ce->oid, &n->oid); - make_traverse_path(ce->name, info, n); + /* len+1 because the cache_entry allocates space for NUL */ + make_traverse_path(ce->name, len + 1, info, n->path, n->pathlen); return ce; } @@ -1057,13 +1118,12 @@ static int unpack_failed(struct unpack_trees_options *o, const char *message) * the directory. */ static int find_cache_pos(struct traverse_info *info, - const struct name_entry *p) + const char *p, size_t p_len) { int pos; struct unpack_trees_options *o = info->data; struct index_state *index = o->src_index; int pfxlen = info->pathlen; - int p_len = tree_entry_len(p); for (pos = o->cache_bottom; pos < index->cache_nr; pos++) { const struct cache_entry *ce = index->cache[pos]; @@ -1099,7 +1159,7 @@ static int find_cache_pos(struct traverse_info *info, ce_len = ce_slash - ce_name; else ce_len = ce_namelen(ce) - pfxlen; - cmp = name_compare(p->path, p_len, ce_name, ce_len); + cmp = name_compare(p, p_len, ce_name, ce_len); /* * Exact match; if we have a directory we need to * delay returning it. @@ -1114,7 +1174,7 @@ static int find_cache_pos(struct traverse_info *info, * E.g. ce_name == "t-i", and p->path == "t"; we may * have "t/a" in the index. */ - if (p_len < ce_len && !memcmp(ce_name, p->path, p_len) && + if (p_len < ce_len && !memcmp(ce_name, p, p_len) && ce_name[p_len] < '/') continue; /* keep looking */ break; @@ -1125,7 +1185,7 @@ static int find_cache_pos(struct traverse_info *info, static struct cache_entry *find_cache_entry(struct traverse_info *info, const struct name_entry *p) { - int pos = find_cache_pos(info, p); + int pos = find_cache_pos(info, p->path, p->pathlen); struct unpack_trees_options *o = info->data; if (0 <= pos) @@ -1138,10 +1198,10 @@ static void debug_path(struct traverse_info *info) { if (info->prev) { debug_path(info->prev); - if (*info->prev->name.path) + if (*info->prev->name) putchar('/'); } - printf("%s", info->name.path); + printf("%s", info->name); } static void debug_name_entry(int i, struct name_entry *n) @@ -1265,7 +1325,9 @@ static int clear_ce_flags_1(struct index_state *istate, struct cache_entry **cache, int nr, struct strbuf *prefix, int select_mask, int clear_mask, - struct exclude_list *el, int defval); + struct pattern_list *pl, + enum pattern_match_result default_match, + int progress_nr); /* Whole directory matching */ static int clear_ce_flags_dir(struct index_state *istate, @@ -1273,19 +1335,24 @@ static int clear_ce_flags_dir(struct index_state *istate, struct strbuf *prefix, char *basename, int select_mask, int clear_mask, - struct exclude_list *el, int defval) + struct pattern_list *pl, + enum pattern_match_result default_match, + int progress_nr) { struct cache_entry **cache_end; int dtype = DT_DIR; - int ret = is_excluded_from_list(prefix->buf, prefix->len, - basename, &dtype, el, istate); int rc; + enum pattern_match_result ret, orig_ret; + orig_ret = path_matches_pattern_list(prefix->buf, prefix->len, + basename, &dtype, pl, istate); strbuf_addch(prefix, '/'); /* If undecided, use matching result of parent dir in defval */ - if (ret < 0) - ret = defval; + if (orig_ret == UNDECIDED) + ret = default_match; + else + ret = orig_ret; for (cache_end = cache; cache_end != cache + nr; cache_end++) { struct cache_entry *ce = *cache_end; @@ -1293,24 +1360,31 @@ static int clear_ce_flags_dir(struct index_state *istate, break; } - /* - * TODO: check el, if there are no patterns that may conflict - * with ret (iow, we know in advance the incl/excl - * decision for the entire directory), clear flag here without - * calling clear_ce_flags_1(). That function will call - * the expensive is_excluded_from_list() on every entry. - */ - rc = clear_ce_flags_1(istate, cache, cache_end - cache, - prefix, - select_mask, clear_mask, - el, ret); + if (pl->use_cone_patterns && orig_ret == MATCHED_RECURSIVE) { + struct cache_entry **ce = cache; + rc = cache_end - cache; + + while (ce < cache_end) { + (*ce)->ce_flags &= ~clear_mask; + ce++; + } + } else if (pl->use_cone_patterns && orig_ret == NOT_MATCHED) { + rc = cache_end - cache; + } else { + rc = clear_ce_flags_1(istate, cache, cache_end - cache, + prefix, + select_mask, clear_mask, + pl, ret, + progress_nr); + } + strbuf_setlen(prefix, prefix->len - 1); return rc; } /* * Traverse the index, find every entry that matches according to - * o->el. Do "ce_flags &= ~clear_mask" on those entries. Return the + * o->pl. Do "ce_flags &= ~clear_mask" on those entries. Return the * number of traversed entries. * * If select_mask is non-zero, only entries whose ce_flags has on of @@ -1327,9 +1401,11 @@ static int clear_ce_flags_1(struct index_state *istate, struct cache_entry **cache, int nr, struct strbuf *prefix, int select_mask, int clear_mask, - struct exclude_list *el, int defval) + struct pattern_list *pl, + enum pattern_match_result default_match, + int progress_nr) { - struct cache_entry **cache_end = cache + nr; + struct cache_entry **cache_end = nr ? cache + nr : cache; /* * Process all entries that have the given prefix and meet @@ -1338,10 +1414,14 @@ static int clear_ce_flags_1(struct index_state *istate, while(cache != cache_end) { struct cache_entry *ce = *cache; const char *name, *slash; - int len, dtype, ret; + int len, dtype; + enum pattern_match_result ret; + + display_progress(istate->progress, progress_nr); if (select_mask && !(ce->ce_flags & select_mask)) { cache++; + progress_nr++; continue; } @@ -1362,58 +1442,84 @@ static int clear_ce_flags_1(struct index_state *istate, prefix, prefix->buf + prefix->len - len, select_mask, clear_mask, - el, defval); + pl, default_match, + progress_nr); /* clear_c_f_dir eats a whole dir already? */ if (processed) { cache += processed; + progress_nr += processed; strbuf_setlen(prefix, prefix->len - len); continue; } strbuf_addch(prefix, '/'); - cache += clear_ce_flags_1(istate, cache, cache_end - cache, - prefix, - select_mask, clear_mask, el, defval); + processed = clear_ce_flags_1(istate, cache, cache_end - cache, + prefix, + select_mask, clear_mask, pl, + default_match, progress_nr); + + cache += processed; + progress_nr += processed; + strbuf_setlen(prefix, prefix->len - len - 1); continue; } /* Non-directory */ dtype = ce_to_dtype(ce); - ret = is_excluded_from_list(ce->name, ce_namelen(ce), - name, &dtype, el, istate); - if (ret < 0) - ret = defval; - if (ret > 0) + ret = path_matches_pattern_list(ce->name, + ce_namelen(ce), + name, &dtype, pl, istate); + if (ret == UNDECIDED) + ret = default_match; + if (ret == MATCHED || ret == MATCHED_RECURSIVE) ce->ce_flags &= ~clear_mask; cache++; + progress_nr++; } + + display_progress(istate->progress, progress_nr); return nr - (cache_end - cache); } static int clear_ce_flags(struct index_state *istate, int select_mask, int clear_mask, - struct exclude_list *el) + struct pattern_list *pl, + int show_progress) { static struct strbuf prefix = STRBUF_INIT; + char label[100]; + int rval; strbuf_reset(&prefix); - - return clear_ce_flags_1(istate, + if (show_progress) + istate->progress = start_delayed_progress( + _("Updating index flags"), + istate->cache_nr); + + xsnprintf(label, sizeof(label), "clear_ce_flags(0x%08lx,0x%08lx)", + (unsigned long)select_mask, (unsigned long)clear_mask); + trace2_region_enter("unpack_trees", label, the_repository); + rval = clear_ce_flags_1(istate, istate->cache, istate->cache_nr, &prefix, select_mask, clear_mask, - el, 0); + pl, 0, 0); + trace2_region_leave("unpack_trees", label, the_repository); + + stop_progress(&istate->progress); + return rval; } /* * Set/Clear CE_NEW_SKIP_WORKTREE according to $GIT_DIR/info/sparse-checkout */ -static void mark_new_skip_worktree(struct exclude_list *el, +static void mark_new_skip_worktree(struct pattern_list *pl, struct index_state *istate, - int select_flag, int skip_wt_flag) + int select_flag, int skip_wt_flag, + int show_progress) { int i; @@ -1437,9 +1543,23 @@ static void mark_new_skip_worktree(struct exclude_list *el, * 2. Widen worktree according to sparse-checkout file. * Matched entries will have skip_wt_flag cleared (i.e. "in") */ - clear_ce_flags(istate, select_flag, skip_wt_flag, el); + clear_ce_flags(istate, select_flag, skip_wt_flag, pl, show_progress); +} + +static void populate_from_existing_patterns(struct unpack_trees_options *o, + struct pattern_list *pl) +{ + char *sparse = git_pathdup("info/sparse-checkout"); + + pl->use_cone_patterns = core_sparse_checkout_cone; + if (add_patterns_from_file_to_list(sparse, "", 0, pl, NULL) < 0) + o->skip_sparse_checkout = 1; + else + o->pl = pl; + free(sparse); } + static int verify_absent(const struct cache_entry *, enum unpack_trees_error_types, struct unpack_trees_options *); @@ -1453,22 +1573,19 @@ int unpack_trees(unsigned len, struct tree_desc *t, struct unpack_trees_options { int i, ret; static struct cache_entry *dfc; - struct exclude_list el; + struct pattern_list pl; + int free_pattern_list = 0; if (len > MAX_UNPACK_TREES) die("unpack_trees takes at most %d trees", MAX_UNPACK_TREES); trace_performance_enter(); - memset(&el, 0, sizeof(el)); if (!core_apply_sparse_checkout || !o->update) o->skip_sparse_checkout = 1; - if (!o->skip_sparse_checkout) { - char *sparse = git_pathdup("info/sparse-checkout"); - if (add_excludes_from_file_to_list(sparse, "", 0, &el, NULL) < 0) - o->skip_sparse_checkout = 1; - else - o->el = ⪙ - free(sparse); + if (!o->skip_sparse_checkout && !o->pl) { + memset(&pl, 0, sizeof(pl)); + free_pattern_list = 1; + populate_from_existing_patterns(o, &pl); } memset(&o->result, 0, sizeof(o->result)); @@ -1494,11 +1611,15 @@ int unpack_trees(unsigned len, struct tree_desc *t, struct unpack_trees_options o->merge_size = len; mark_all_ce_unused(o->src_index); + if (o->src_index->fsmonitor_last_update) + o->result.fsmonitor_last_update = o->src_index->fsmonitor_last_update; + /* * Sparse checkout loop #1: set NEW_SKIP_WORKTREE on existing entries */ if (!o->skip_sparse_checkout) - mark_new_skip_worktree(o->el, o->src_index, 0, CE_NEW_SKIP_WORKTREE); + mark_new_skip_worktree(o->pl, o->src_index, 0, + CE_NEW_SKIP_WORKTREE, o->verbose_update); if (!dfc) dfc = xcalloc(1, cache_entry_size(0)); @@ -1556,14 +1677,14 @@ int unpack_trees(unsigned len, struct tree_desc *t, struct unpack_trees_options } if (!o->skip_sparse_checkout) { - int empty_worktree = 1; - /* * Sparse checkout loop #2: set NEW_SKIP_WORKTREE on entries not in loop #1 - * If the will have NEW_SKIP_WORKTREE, also set CE_SKIP_WORKTREE + * If they will have NEW_SKIP_WORKTREE, also set CE_SKIP_WORKTREE * so apply_sparse_checkout() won't attempt to remove it from worktree */ - mark_new_skip_worktree(o->el, &o->result, CE_ADDED, CE_SKIP_WORKTREE | CE_NEW_SKIP_WORKTREE); + mark_new_skip_worktree(o->pl, &o->result, + CE_ADDED, CE_SKIP_WORKTREE | CE_NEW_SKIP_WORKTREE, + o->verbose_update); ret = 0; for (i = 0; i < o->result.cache_nr; i++) { @@ -1578,36 +1699,24 @@ int unpack_trees(unsigned len, struct tree_desc *t, struct unpack_trees_options * correct CE_NEW_SKIP_WORKTREE */ if (ce->ce_flags & CE_ADDED && - verify_absent(ce, ERROR_WOULD_LOSE_UNTRACKED_OVERWRITTEN, o)) { - if (!o->show_all_errors) - goto return_failed; - ret = -1; - } - - if (apply_sparse_checkout(&o->result, ce, o)) { - if (!o->show_all_errors) - goto return_failed; - ret = -1; - } - if (!ce_skip_worktree(ce)) - empty_worktree = 0; + verify_absent(ce, WARNING_SPARSE_ORPHANED_NOT_OVERWRITTEN, o)) + ret = 1; + if (apply_sparse_checkout(&o->result, ce, o)) + ret = 1; } - if (ret < 0) - goto return_failed; - /* - * Sparse checkout is meant to narrow down checkout area - * but it does not make sense to narrow down to empty working - * tree. This is usually a mistake in sparse checkout rules. - * Do not allow users to do that. - */ - if (o->result.cache_nr && empty_worktree) { - ret = unpack_failed(o, "Sparse checkout leaves no entry on working directory"); - goto done; + if (ret == 1) { + /* + * Inability to sparsify or de-sparsify individual + * paths is not an error, but just a warning. + */ + if (o->show_all_errors) + display_warning_msgs(o); + ret = 0; } } - ret = check_updates(o) ? (-2) : 0; + ret = check_updates(o, &o->result) ? (-2) : 0; if (o->dst_index) { move_index_extensions(&o->result, o->src_index); if (!ret) { @@ -1630,8 +1739,9 @@ int unpack_trees(unsigned len, struct tree_desc *t, struct unpack_trees_options o->src_index = NULL; done: + if (free_pattern_list) + clear_pattern_list(&pl); trace_performance_leave("unpack_trees"); - clear_exclude_list(&el); return ret; return_failed: @@ -1644,6 +1754,74 @@ return_failed: goto done; } +/* + * Update SKIP_WORKTREE bits according to sparsity patterns, and update + * working directory to match. + * + * CE_NEW_SKIP_WORKTREE is used internally. + */ +enum update_sparsity_result update_sparsity(struct unpack_trees_options *o) +{ + enum update_sparsity_result ret = UPDATE_SPARSITY_SUCCESS; + struct pattern_list pl; + int i; + unsigned old_show_all_errors; + int free_pattern_list = 0; + + old_show_all_errors = o->show_all_errors; + o->show_all_errors = 1; + + /* Sanity checks */ + if (!o->update || o->index_only || o->skip_sparse_checkout) + BUG("update_sparsity() is for reflecting sparsity patterns in working directory"); + if (o->src_index != o->dst_index || o->fn) + BUG("update_sparsity() called wrong"); + + trace_performance_enter(); + + /* If we weren't given patterns, use the recorded ones */ + if (!o->pl) { + memset(&pl, 0, sizeof(pl)); + free_pattern_list = 1; + populate_from_existing_patterns(o, &pl); + if (o->skip_sparse_checkout) + goto skip_sparse_checkout; + } + + /* Set NEW_SKIP_WORKTREE on existing entries. */ + mark_all_ce_unused(o->src_index); + mark_new_skip_worktree(o->pl, o->src_index, 0, + CE_NEW_SKIP_WORKTREE, o->verbose_update); + + /* Then loop over entries and update/remove as needed */ + ret = UPDATE_SPARSITY_SUCCESS; + for (i = 0; i < o->src_index->cache_nr; i++) { + struct cache_entry *ce = o->src_index->cache[i]; + + + if (ce_stage(ce)) { + /* -1 because for loop will increment by 1 */ + i += warn_conflicted_path(o->src_index, i, o) - 1; + ret = UPDATE_SPARSITY_WARNINGS; + continue; + } + + if (apply_sparse_checkout(o->src_index, ce, o)) + ret = UPDATE_SPARSITY_WARNINGS; + } + +skip_sparse_checkout: + if (check_updates(o, o->src_index)) + ret = UPDATE_SPARSITY_WORKTREE_UPDATE_FAILURES; + + display_warning_msgs(o); + o->show_all_errors = old_show_all_errors; + if (free_pattern_list) + clear_pattern_list(&pl); + trace_performance_leave("update_sparsity"); + return ret; +} + /* Here come the merge functions */ static int reject_merge(const struct cache_entry *ce, @@ -1728,7 +1906,7 @@ int verify_uptodate(const struct cache_entry *ce, static int verify_uptodate_sparse(const struct cache_entry *ce, struct unpack_trees_options *o) { - return verify_uptodate_1(ce, o, ERROR_SPARSE_NOT_UPTODATE_FILE); + return verify_uptodate_1(ce, o, WARNING_SPARSE_NOT_UPTODATE_FILE); } /* @@ -1754,9 +1932,6 @@ static void invalidate_ce_path(const struct cache_entry *ce, /* * 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(const char *old_sha1, const struct cache_entry *ce, @@ -1969,11 +2144,7 @@ static int verify_absent_sparse(const struct cache_entry *ce, enum unpack_trees_error_types error_type, struct unpack_trees_options *o) { - enum unpack_trees_error_types orphaned_error = error_type; - if (orphaned_error == ERROR_WOULD_LOSE_UNTRACKED_OVERWRITTEN) - orphaned_error = ERROR_WOULD_LOSE_ORPHANED_OVERWRITTEN; - - return verify_absent_1(ce, orphaned_error, o); + return verify_absent_1(ce, error_type, o); } static int merged_entry(const struct cache_entry *ce, @@ -2006,7 +2177,7 @@ static int merged_entry(const struct cache_entry *ce, } invalidate_ce_path(merge, o); - if (submodule_from_ce(ce)) { + if (submodule_from_ce(ce) && file_exists(ce->name)) { int ret = check_submodule_move_head(ce, NULL, oid_to_hex(&ce->oid), o); @@ -2035,7 +2206,7 @@ static int merged_entry(const struct cache_entry *ce, invalidate_ce_path(old, o); } - if (submodule_from_ce(ce)) { + if (submodule_from_ce(ce) && file_exists(ce->name)) { int ret = check_submodule_move_head(ce, oid_to_hex(&old->oid), oid_to_hex(&ce->oid), o); @@ -2050,7 +2221,8 @@ static int merged_entry(const struct cache_entry *ce, invalidate_ce_path(old, o); } - do_add_entry(o, merge, update, CE_STAGEMASK); + if (do_add_entry(o, merge, update, CE_STAGEMASK) < 0) + return -1; return 1; } @@ -2374,7 +2546,8 @@ int oneway_merge(const struct cache_entry * const *src, if (old && same(old, a)) { int update = 0; - if (o->reset && o->update && !ce_uptodate(old) && !ce_skip_worktree(old)) { + if (o->reset && o->update && !ce_uptodate(old) && !ce_skip_worktree(old) && + !(old->ce_flags & CE_FSMONITOR_VALID)) { struct stat st; if (lstat(old->name, &st) || ie_match_stat(o->src_index, old, &st, CE_MATCH_IGNORE_VALID|CE_MATCH_IGNORE_SKIP_WORKTREE)) |