After modify/delete merge conflict happens in a file skipped by sparse
checkout, "git reset --merge", which implements the "--abort" actions, and
"git reset --hard" fail with message "Entry * not uptodate. Cannot update
sparse checkout." The reason is that the entry is verified in
apply_sparse_checkout() for being up-to-date even when it has a conflict.
Checking conflicted entry for being up-to-date is not performed in other
cases. One obvious reason to not check it is that it is already modified
by inserting conflict marks.

Fix by not checking conflicted entries before performing reset.
Also, add test case which verifies the issue is fixed.

Signed-off-by: Max Kirillov <m...@max630.net>
---
I have tried to use sparse-checkout for merging and cherrypicking, to save on IO
and disk space. It works, mostly, but there are issues here and there.
This one was low hanging, and also pretty annoying.

 t/t3035-merge-sparse.sh | 46 +++++++++++++++++++++++++++++++++++++++++
 unpack-trees.c          |  2 +-
 2 files changed, 47 insertions(+), 1 deletion(-)
 create mode 100755 t/t3035-merge-sparse.sh

diff --git a/t/t3035-merge-sparse.sh b/t/t3035-merge-sparse.sh
new file mode 100755
index 0000000000..c6b2b0b82a
--- /dev/null
+++ b/t/t3035-merge-sparse.sh
@@ -0,0 +1,46 @@
+#!/bin/sh
+
+test_description='merge with sparse files'
+
+. ./test-lib.sh
+
+# test_file $filename $content
+test_file () {
+       echo "$2" > "$1" &&
+       git add "$1"
+}
+
+# test_commit_this $message_and_tag
+test_commit_this () {
+       git commit -m "$1" &&
+       git tag "$1"
+}
+
+test_expect_success 'setup' '
+       test_file checked-out init &&
+       test_file modify_delete modify_delete_init &&
+       test_commit_this init &&
+       test_file modify_delete modify_delete_theirs &&
+       test_commit_this theirs &&
+       git reset --hard init &&
+       git rm modify_delete &&
+       test_commit_this ours &&
+       git config core.sparseCheckout true &&
+       echo "/checked-out" >.git/info/sparse-checkout &&
+       git reset --hard &&
+       ! git merge theirs
+'
+
+test_expect_success 'reset --hard works after the conflict' '
+       git reset --hard
+'
+
+test_expect_success 'setup: conflict back' '
+       ! git merge theirs
+'
+
+test_expect_success 'Merge abort works after the conflict' '
+       git merge --abort
+'
+
+test_done
diff --git a/unpack-trees.c b/unpack-trees.c
index e73745051e..65ae0721a6 100644
--- a/unpack-trees.c
+++ b/unpack-trees.c
@@ -468,7 +468,7 @@ 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) && !(ce->ce_flags & 
CE_CONFLICTED) && verify_uptodate_sparse(ce, o))
                        return -1;
                ce->ce_flags |= CE_WT_REMOVE;
                ce->ce_flags &= ~CE_UPDATE;
-- 
2.17.0.1185.g782057d875

Reply via email to