unpack-trees: avoid nuking untracked dir in way of unmerged file

Signed-off-by: Elijah Newren <newren@gmail.com> Signed-off-by: Junio C Hamano <gitster@pobox.com>

Elijah Newren committed Sep 27, 2021 at 16:33 UTC 1fdd51aa13c27403c37b57ead32ef79b81d8128b
2 files changed +32 -5
t/t2500-untracked-overwriting.sh
+1 -1
@@ -197,7 +197,7 @@ test_expect_failure 'git stash and untracked files' '
197 )
198 '
199
200 -test_expect_failure 'git am --abort and untracked dir vs. unmerged file' '
200 +test_expect_success 'git am --abort and untracked dir vs. unmerged file' '
201 test_setup_sequencing am_abort_and_untracked &&
202 (
203 cd sequencing_am_abort_and_untracked &&
unpack-trees.c
+31 -4
@@ -2176,9 +2176,15 @@ static int icase_exists(struct unpack_trees_options *o, const char *name, int le
2176 return src && !ie_match_stat(o->src_index, src, st, CE_MATCH_IGNORE_VALID|CE_MATCH_IGNORE_SKIP_WORKTREE);
2177 }
2178
2179 +enum absent_checking_type {
2180 + COMPLETELY_ABSENT,
2181 + ABSENT_ANY_DIRECTORY
2182 +};
2183 +
2184 static int check_ok_to_remove(const char *name, int len, int dtype,
2185 const struct cache_entry *ce, struct stat *st,
2186 enum unpack_trees_error_types error_type,
2187 + enum absent_checking_type absent_type,
2188 struct unpack_trees_options *o)
2189 {
2190 const struct cache_entry *result;
@@ -2213,6 +2219,10 @@ static int check_ok_to_remove(const char *name, int len, int dtype,
2219 return 0;
2220 }
2221
2222 + /* If we only care about directories, then we can remove */
2223 + if (absent_type == ABSENT_ANY_DIRECTORY)
2224 + return 0;
2225 +
2226 /*
2227 * The previous round may already have decided to
2228 * delete this path, which is in a subdirectory that
@@ -2233,6 +2243,7 @@ static int check_ok_to_remove(const char *name, int len, int dtype,
2243 */
2244 static int verify_absent_1(const struct cache_entry *ce,
2245 enum unpack_trees_error_types error_type,
2246 + enum absent_checking_type absent_type,
2247 struct unpack_trees_options *o)
2248 {
2249 int len;
@@ -2259,7 +2270,8 @@ static int verify_absent_1(const struct cache_entry *ce,
2270 NULL, o);
2271 else
2272 ret = check_ok_to_remove(path, len, DT_UNKNOWN, NULL,
2262 - &st, error_type, o);
2273 + &st, error_type,
2274 + absent_type, o);
2275 }
2276 free(path);
2277 return ret;
@@ -2274,7 +2286,7 @@ static int verify_absent_1(const struct cache_entry *ce,
2286
2287 return check_ok_to_remove(ce->name, ce_namelen(ce),
2288 ce_to_dtype(ce), ce, &st,
2277 - error_type, o);
2289 + error_type, absent_type, o);
2290 }
2291 }
2292
@@ -2284,14 +2296,23 @@ static int verify_absent(const struct cache_entry *ce,
2296 {
2297 if (!o->skip_sparse_checkout && (ce->ce_flags & CE_NEW_SKIP_WORKTREE))
2298 return 0;
2287 - return verify_absent_1(ce, error_type, o);
2299 + return verify_absent_1(ce, error_type, COMPLETELY_ABSENT, o);
2300 +}
2301 +
2302 +static int verify_absent_if_directory(const struct cache_entry *ce,
2303 + enum unpack_trees_error_types error_type,
2304 + struct unpack_trees_options *o)
2305 +{
2306 + if (!o->skip_sparse_checkout && (ce->ce_flags & CE_NEW_SKIP_WORKTREE))
2307 + return 0;
2308 + return verify_absent_1(ce, error_type, ABSENT_ANY_DIRECTORY, o);
2309 }
2310
2311 static int verify_absent_sparse(const struct cache_entry *ce,
2312 enum unpack_trees_error_types error_type,
2313 struct unpack_trees_options *o)
2314 {
2294 - return verify_absent_1(ce, error_type, o);
2315 + return verify_absent_1(ce, error_type, COMPLETELY_ABSENT, o);
2316 }
2317
2318 static int merged_entry(const struct cache_entry *ce,
@@ -2365,6 +2386,12 @@ static int merged_entry(const struct cache_entry *ce,
2386 * Previously unmerged entry left as an existence
2387 * marker by read_index_unmerged();
2388 */
2389 + if (verify_absent_if_directory(merge,
2390 + ERROR_WOULD_LOSE_UNTRACKED_OVERWRITTEN, o)) {
2391 + discard_cache_entry(merge);
2392 + return -1;
2393 + }
2394 +
2395 invalidate_ce_path(old, o);
2396 }
2397