read-tree: make three-way merge sparse-aware

Enable use of 'merged_sparse_dir' in 'threeway_merge'. As with two-way merge, the contents of each conflicted sparse directory are merged without referencing the index, avoiding sparse index expansion. As with two-way merge, the 't/t1092-sparse-checkout-compatibility.sh' test 'read-tree --merge with edit/edit conflicts in sparse directories' confirms that three-way merges with edit/edit changes (both with and without conflicts) inside a sparse directory result in the correct index state or error message. To ensure the index is not unnecessarily expanded, add three-way merge cases to 'sparse index is not expanded: read-tree'. Signed-off-by: Victoria Dye <vdye@github.com> Signed-off-by: Junio C Hamano <gitster@pobox.com>

Victoria Dye committed Mar 1, 2022 at 20:24 UTC f27c170f645e6b8ed642c49c503964893ee26a4f
3 files changed +29 -14
builtin/read-tree.c
-5
@@ -234,11 +234,6 @@ int cmd_read_tree(int argc, const char **argv, const char *cmd_prefix)
234 break;
235 case 3:
236 default:
237 - /*
238 - * TODO: update threeway_merge to handle edit/edit conflicts in
239 - * sparse directories.
240 - */
241 - ensure_full_index(&the_index);
237 opts.fn = threeway_merge;
238 break;
239 }
t/t1092-sparse-checkout-compatibility.sh
+3 -1
@@ -1413,7 +1413,9 @@ test_expect_success 'sparse index is not expanded: read-tree' '
1413 init_repos &&
1414
1415 ensure_not_expanded checkout -b test-branch update-folder1 &&
1416 - for MERGE_TREES in "base update-folder2" \
1416 + for MERGE_TREES in "base HEAD update-folder2" \
1417 + "base HEAD rename-base" \
1418 + "base update-folder2" \
1419 "base rename-base" \
1420 "update-folder2"
1421 do
unpack-trees.c
+26 -8
@@ -2643,16 +2643,24 @@ int threeway_merge(const struct cache_entry * const *stages,
2643 */
2644 /* #14, #14ALT, #2ALT */
2645 if (remote && !df_conflict_head && head_match && !remote_match) {
2646 - if (index && !same(index, remote) && !same(index, head))
2647 - return reject_merge(index, o);
2646 + if (index && !same(index, remote) && !same(index, head)) {
2647 + if (S_ISSPARSEDIR(index->ce_mode))
2648 + return merged_sparse_dir(stages, 4, o);
2649 + else
2650 + return reject_merge(index, o);
2651 + }
2652 return merged_entry(remote, index, o);
2653 }
2654 /*
2655 * If we have an entry in the index cache, then we want to
2656 * make sure that it matches head.
2657 */
2654 - if (index && !same(index, head))
2655 - return reject_merge(index, o);
2658 + if (index && !same(index, head)) {
2659 + if (S_ISSPARSEDIR(index->ce_mode))
2660 + return merged_sparse_dir(stages, 4, o);
2661 + else
2662 + return reject_merge(index, o);
2663 + }
2664
2665 if (head) {
2666 /* #5ALT, #15 */
@@ -2714,11 +2722,21 @@ int threeway_merge(const struct cache_entry * const *stages,
2722
2723 }
2724
2717 - /* Below are "no merge" cases, which require that the index be
2718 - * up-to-date to avoid the files getting overwritten with
2719 - * conflict resolution files.
2720 - */
2725 + /* Handle "no merge" cases (see t/t1000-read-tree-m-3way.sh) */
2726 if (index) {
2727 + /*
2728 + * If we've reached the "no merge" cases and we're merging
2729 + * a sparse directory, we may have an "edit/edit" conflict that
2730 + * can be resolved by individually merging directory contents.
2731 + */
2732 + if (S_ISSPARSEDIR(index->ce_mode))
2733 + return merged_sparse_dir(stages, 4, o);
2734 +
2735 + /*
2736 + * If we're not merging a sparse directory, ensure the index is
2737 + * up-to-date to avoid files getting overwritten with conflict
2738 + * resolution files
2739 + */
2740 if (verify_uptodate(index, o))
2741 return -1;
2742 }