merge-ort: implement CE_SKIP_WORKTREE handling with conflicted entries

When merge conflicts occur in paths removed by a sparse-checkout, we need to unsparsify those paths (clear the SKIP_WORKTREE bit), and write out the conflicted file to the working copy. In the very unlikely case that someone manually put a file into the working copy at the location of the SKIP_WORKTREE file, we need to avoid overwriting whatever edits they have made and move that file to a different location first. Signed-off-by: Elijah Newren <newren@gmail.com> Reviewed-by: Derrick Stolee <dstolee@microsoft.com> Signed-off-by: Junio C Hamano <gitster@pobox.com>

Elijah Newren committed Mar 20, 2021 at 00:03 UTC 66b209b86ad3b79728d3f0f22e1bdef6b17a12c1
2 files changed +32 -15
merge-ort.c
+30 -13
@@ -3369,23 +3369,27 @@ static int checkout(struct merge_options *opt,
3369 return ret;
3370 }
3371
3372 -static int record_conflicted_index_entries(struct merge_options *opt,
3373 - struct index_state *index,
3374 - struct strmap *paths,
3375 - struct strmap *conflicted)
3372 +static int record_conflicted_index_entries(struct merge_options *opt)
3373 {
3374 struct hashmap_iter iter;
3375 struct strmap_entry *e;
3376 + struct index_state *index = opt->repo->index;
3377 + struct checkout state = CHECKOUT_INIT;
3378 int errs = 0;
3379 int original_cache_nr;
3380
3382 - if (strmap_empty(conflicted))
3381 + if (strmap_empty(&opt->priv->conflicted))
3382 return 0;
3383
3384 + /* If any entries have skip_worktree set, we'll have to check 'em out */
3385 + state.force = 1;
3386 + state.quiet = 1;
3387 + state.refresh_cache = 1;
3388 + state.istate = index;
3389 original_cache_nr = index->cache_nr;
3390
3391 /* Put every entry from paths into plist, then sort */
3388 - strmap_for_each_entry(conflicted, &iter, e) {
3392 + strmap_for_each_entry(&opt->priv->conflicted, &iter, e) {
3393 const char *path = e->key;
3394 struct conflict_info *ci = e->value;
3395 int pos;
@@ -3426,9 +3430,23 @@ static int record_conflicted_index_entries(struct merge_options *opt,
3430 * the higher order stages. Thus, we need override
3431 * the CE_SKIP_WORKTREE bit and manually write those
3432 * files to the working disk here.
3429 - *
3430 - * TODO: Implement this CE_SKIP_WORKTREE fixup.
3433 */
3434 + if (ce_skip_worktree(ce)) {
3435 + struct stat st;
3436 +
3437 + if (!lstat(path, &st)) {
3438 + char *new_name = unique_path(&opt->priv->paths,
3439 + path,
3440 + "cruft");
3441 +
3442 + path_msg(opt, path, 1,
3443 + _("Note: %s not up to date and in way of checking out conflicted version; old copy renamed to %s"),
3444 + path, new_name);
3445 + errs |= rename(path, new_name);
3446 + free(new_name);
3447 + }
3448 + errs |= checkout_entry(ce, &state, NULL, NULL);
3449 + }
3450
3451 /*
3452 * Mark this cache entry for removal and instead add
@@ -3478,8 +3496,6 @@ void merge_switch_to_result(struct merge_options *opt,
3496 {
3497 assert(opt->priv == NULL);
3498 if (result->clean >= 0 && update_worktree_and_index) {
3481 - struct merge_options_internal *opti = result->priv;
3482 -
3499 trace2_region_enter("merge", "checkout", opt->repo);
3500 if (checkout(opt, head, result->tree)) {
3501 /* failure to function */
@@ -3489,13 +3505,14 @@ void merge_switch_to_result(struct merge_options *opt,
3505 trace2_region_leave("merge", "checkout", opt->repo);
3506
3507 trace2_region_enter("merge", "record_conflicted", opt->repo);
3492 - if (record_conflicted_index_entries(opt, opt->repo->index,
3493 - &opti->paths,
3494 - &opti->conflicted)) {
3508 + opt->priv = result->priv;
3509 + if (record_conflicted_index_entries(opt)) {
3510 /* failure to function */
3511 + opt->priv = NULL;
3512 result->clean = -1;
3513 return;
3514 }
3515 + opt->priv = NULL;
3516 trace2_region_leave("merge", "record_conflicted", opt->repo);
3517 }
3518
t/t6428-merge-conflicts-sparse.sh
+2 -2
@@ -76,7 +76,7 @@ test_setup_numerals () {
76 )
77 }
78
79 -test_expect_merge_algorithm success failure 'conflicting entries written to worktree even if sparse' '
79 +test_expect_success 'conflicting entries written to worktree even if sparse' '
80 test_setup_numerals plain &&
81 (
82 cd numerals_plain &&
@@ -112,7 +112,7 @@ test_expect_merge_algorithm success failure 'conflicting entries written to work
112 )
113 '
114
115 -test_expect_merge_algorithm failure failure 'present-despite-SKIP_WORKTREE handled reasonably' '
115 +test_expect_merge_algorithm failure success 'present-despite-SKIP_WORKTREE handled reasonably' '
116 test_setup_numerals in_the_way &&
117 (
118 cd numerals_in_the_way &&