Merge branch 'en/removing-untracked-fixes'

Various fixes in code paths that move untracked files away to make room.

* en/removing-untracked-fixes:
  Documentation: call out commands that nuke untracked files/directories
  Comment important codepaths regarding nuking untracked files/dirs
  unpack-trees: avoid nuking untracked dir in way of locally deleted file
  unpack-trees: avoid nuking untracked dir in way of unmerged file
  Change unpack_trees' 'reset' flag into an enum
  Remove ignored files by default when they are in the way
  unpack-trees: make dir an internal-only struct
  unpack-trees: introduce preserve_ignored to unpack_trees_options
  read-tree, merge-recursive: overwrite ignored files by default
  checkout, read-tree: fix leak of unpack_trees_options.dir
  t2500: add various tests for nuking untracked files
This commit is contained in:
Junio C Hamano
2021-10-13 15:15:57 -07:00
23 changed files with 366 additions and 74 deletions

View File

@ -1694,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);
@ -1707,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) {
@ -1868,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;
@ -2158,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;
@ -2195,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
@ -2215,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);
@ -2240,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;
@ -2255,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);
}
}
@ -2265,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,
@ -2346,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);
}
@ -2363,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);