From 71a8fab31b70c417e8f5b5f716581f89955a7082 Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Mon, 18 Jul 2022 13:13:48 -0700 Subject: [PATCH 1/8] The fourth batch Signed-off-by: Junio C Hamano --- Documentation/RelNotes/2.38.0.txt | 34 +++++++++++++++++++++++++++++++ 1 file changed, 34 insertions(+) diff --git a/Documentation/RelNotes/2.38.0.txt b/Documentation/RelNotes/2.38.0.txt index ceaad51f033c6c..38077632539606 100644 --- a/Documentation/RelNotes/2.38.0.txt +++ b/Documentation/RelNotes/2.38.0.txt @@ -45,6 +45,26 @@ Performance, Internal Implementation, Development Support etc. * Further preparation to turn git-submodule.sh into a builtin continues. + * Apply Coccinelle rule to turn raw memmove() into MOVE_ARRAY() cpp + macro, which would improve maintainability and readability. + + * Teach "make all" to build gitweb as well. + + * Tweak tests so that they still work when the "git init" template + did not create .git/info directory. + + * Add Coccinelle rules to detect the pattern of initializing and then + finalizing a structure without using it in between at all, which + happens after code restructuring and the compilers fail to + recognize as an unused variable. + + * The code to convert between GPG trust level strings and internal + constants we use to represent them have been cleaned up. + + * Support for libnettle as SHA256 implementation has been added. + + * The way "git multi-pack" uses parse-options API has been improved. + Fixes since v2.37 ----------------- @@ -83,6 +103,20 @@ Fixes since v2.37 a repository as its subdirectory, regressed in Git 2.27 days. (merge d6c9a71755 gg/worktree-from-the-above later to maint). + * Recent update to vimdiff layout code has been made more robust + against different end-user vim settings. + (merge f3d7623a13 fr/vimdiff-layout-fix later to maint). + + * Plug various memory leaks. + (merge ece3974ba6 ab/leakfix later to maint). + + * Plug various memory leaks in test-tool commands. + (merge f40a693450 ab/test-tool-leakfix later to maint). + + * Fixes a long-standing corner case bug around directory renames in + the merge-ort strategy. + (merge 751e165424 en/merge-dual-dir-renames-fix later to maint). + * Other code cleanup, docfix, build fix, etc. (merge 5fd9d1738e jk/revisions-doc-markup-fix later to maint). (merge 1971510c35 pb/diff-doc-raw-format later to maint). From 6af0fb2c738a13c903a9f7fdebdc6a8f777616d9 Mon Sep 17 00:00:00 2001 From: Shaoxuan Yuan Date: Tue, 19 Jul 2022 18:32:18 +0800 Subject: [PATCH 2/8] t7002: add tests for moving from in-cone to out-of-cone Add corresponding tests to test that user can move an in-cone to out-of-cone when --sparse is supplied. Such can be either clean or dirty, and moving it results in different behaviors: A clean move should move the to the , both in the working tree and the index, then remove the resulted path from the working tree, and turn on its CE_SKIP_WORKTREE bit. A dirty move should move the to the , both in the working tree and the index, but should *not* remove the resulted path from the working tree and should *not* turn on its CE_SKIP_WORKTREE bit. Instead advise user to "git add" this path and run "git sparse-checkout reapply" to re-sparsify that path. Also make sure that if exists in the index (existing check for if is in the worktree is not enough in in-to-out moves), warn user against the overwrite. And Git should force the overwrite when supplied with -f or --force. Signed-off-by: Shaoxuan Yuan --- t/t7002-mv-sparse-checkout.sh | 148 +++++++++++++++++++++++++++++++++- 1 file changed, 147 insertions(+), 1 deletion(-) diff --git a/t/t7002-mv-sparse-checkout.sh b/t/t7002-mv-sparse-checkout.sh index 71fe29690fd122..c27fcdbfd0ee1e 100755 --- a/t/t7002-mv-sparse-checkout.sh +++ b/t/t7002-mv-sparse-checkout.sh @@ -28,12 +28,25 @@ test_expect_success 'setup' " updated in the index: EOF - cat >sparse_hint <<-EOF + cat >sparse_hint <<-EOF && hint: If you intend to update such entries, try one of the following: hint: * Use the --sparse option. hint: * Disable or modify the sparsity rules. hint: Disable this message with \"git config advice.updateSparsePath false\" EOF + + cat >dirty_error_header <<-EOF && + The following paths have been moved outside the + sparse-checkout definition but are not sparse due to local + modifications. + EOF + + cat >dirty_hint <<-EOF + hint: To correct the sparsity of these paths, do the following: + hint: * Use \"git add --sparse \" to update the index + hint: * Use \"git sparse-checkout reapply\" to apply the sparsity rules + hint: Disable this message with \"git config advice.updateSparsePath false\" + EOF " test_expect_success 'mv refuses to move sparse-to-sparse' ' @@ -290,4 +303,137 @@ test_expect_success 'move sparse file to existing destination with --force and - test_cmp expect sub/file1 ' +test_expect_failure 'move clean path from in-cone to out-of-cone' ' + test_when_finished "cleanup_sparse_checkout" && + setup_sparse_checkout && + + test_must_fail git mv sub/d folder1 2>stderr && + cat sparse_error_header >expect && + echo "folder1/d" >>expect && + cat sparse_hint >>expect && + test_cmp expect stderr && + + git mv --sparse sub/d folder1 2>stderr && + test_must_be_empty stderr && + + test_path_is_missing sub/d && + test_path_is_missing folder1/d && + git ls-files -t >actual && + ! grep -x "H sub/d" actual && + grep -x "S folder1/d" actual +' + +test_expect_failure 'move clean path from in-cone to out-of-cone overwrite' ' + test_when_finished "cleanup_sparse_checkout" && + setup_sparse_checkout && + echo "sub/file1 overwrite" >sub/file1 && + git add sub/file1 && + + test_must_fail git mv sub/file1 folder1 2>stderr && + cat sparse_error_header >expect && + echo "folder1/file1" >>expect && + cat sparse_hint >>expect && + test_cmp expect stderr && + + test_must_fail git mv --sparse sub/file1 folder1 2>stderr && + echo "fatal: destination exists in the index, source=sub/file1, destination=folder1/file1" \ + >expect && + test_cmp expect stderr && + + git mv --sparse -f sub/file1 folder1 2>stderr && + test_must_be_empty stderr && + + test_path_is_missing sub/file1 && + test_path_is_missing folder1/file1 && + git ls-files -t >actual && + ! grep -x "H sub/file1" actual && + grep -x "S folder1/file1" actual && + + # compare file content before move and after move + echo "sub/file1 overwrite" >expect && + git ls-files -s -- folder1/file1 | awk "{print \$2}" >oid && + git cat-file blob $(cat oid) >actual && + test_cmp expect actual +' + +test_expect_failure 'move dirty path from in-cone to out-of-cone' ' + test_when_finished "cleanup_sparse_checkout" && + setup_sparse_checkout && + echo "modified" >>sub/d && + + test_must_fail git mv sub/d folder1 2>stderr && + cat sparse_error_header >expect && + echo "folder1/d" >>expect && + cat sparse_hint >>expect && + test_cmp expect stderr && + + git mv --sparse sub/d folder1 2>stderr && + cat dirty_error_header >expect && + echo "folder1/d" >>expect && + cat dirty_hint >>expect && + test_cmp expect stderr && + + test_path_is_missing sub/d && + test_path_is_file folder1/d && + git ls-files -t >actual && + ! grep -x "H sub/d" actual && + grep -x "H folder1/d" actual +' + +test_expect_failure 'move dir from in-cone to out-of-cone' ' + test_when_finished "cleanup_sparse_checkout" && + setup_sparse_checkout && + + test_must_fail git mv sub/dir folder1 2>stderr && + cat sparse_error_header >expect && + echo "folder1/dir/e" >>expect && + cat sparse_hint >>expect && + test_cmp expect stderr && + + git mv --sparse sub/dir folder1 2>stderr && + test_must_be_empty stderr && + + test_path_is_missing sub/dir && + test_path_is_missing folder1 && + git ls-files -t >actual && + ! grep -x "H sub/dir/e" actual && + grep -x "S folder1/dir/e" actual +' + +test_expect_failure 'move partially-dirty dir from in-cone to out-of-cone' ' + test_when_finished "cleanup_sparse_checkout" && + setup_sparse_checkout && + touch sub/dir/e2 sub/dir/e3 && + git add sub/dir/e2 sub/dir/e3 && + echo "modified" >>sub/dir/e2 && + echo "modified" >>sub/dir/e3 && + + test_must_fail git mv sub/dir folder1 2>stderr && + cat sparse_error_header >expect && + echo "folder1/dir/e" >>expect && + echo "folder1/dir/e2" >>expect && + echo "folder1/dir/e3" >>expect && + cat sparse_hint >>expect && + test_cmp expect stderr && + + git mv --sparse sub/dir folder1 2>stderr && + cat dirty_error_header >expect && + echo "folder1/dir/e2" >>expect && + echo "folder1/dir/e3" >>expect && + cat dirty_hint >>expect && + test_cmp expect stderr && + + test_path_is_missing sub/dir && + test_path_is_missing folder1/dir/e && + test_path_is_file folder1/dir/e2 && + test_path_is_file folder1/dir/e3 && + git ls-files -t >actual && + ! grep -x "H sub/dir/e" actual && + ! grep -x "H sub/dir/e2" actual && + ! grep -x "H sub/dir/e3" actual && + grep -x "S folder1/dir/e" actual && + grep -x "H folder1/dir/e2" actual && + grep -x "H folder1/dir/e3" actual +' + test_done From c2cb975ead3d3f8863aed1a92748590167e4890b Mon Sep 17 00:00:00 2001 From: Shaoxuan Yuan Date: Tue, 19 Jul 2022 18:32:19 +0800 Subject: [PATCH 3/8] mv: add documentation for check_dir_in_index() Using check_dir_in_index without checking if the directory is on-disk could get a false positive for partially sparsified directory. Add a note in the documentation to warn potential user. Signed-off-by: Shaoxuan Yuan --- builtin/mv.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/builtin/mv.c b/builtin/mv.c index 4729bb1a1ab9d3..c8b9069db89ca5 100644 --- a/builtin/mv.c +++ b/builtin/mv.c @@ -132,6 +132,11 @@ static int index_range_of_same_dir(const char *src, int length, * Return 0 if such directory exist (i.e. with any of its contained files not * marked with CE_SKIP_WORKTREE, the directory would be present in working tree). * Return 1 otherwise. + * + * Note: *always* check the directory is not on-disk before this function + * (i.e. using lstat()); + * otherwise it may return a false positive for a partially sparsified + * directory. */ static int check_dir_in_index(const char *name) { From 4e9e7145e951a75ea117915670269be1bef1594b Mon Sep 17 00:00:00 2001 From: Shaoxuan Yuan Date: Tue, 19 Jul 2022 18:32:19 +0800 Subject: [PATCH 4/8] mv: free the *with_slash in check_dir_in_index() *with_slash may be a malloc'd pointer, and when it is, free it. Signed-off-by: Shaoxuan Yuan --- builtin/mv.c | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/builtin/mv.c b/builtin/mv.c index c8b9069db89ca5..23a297d6b827e4 100644 --- a/builtin/mv.c +++ b/builtin/mv.c @@ -140,6 +140,7 @@ static int index_range_of_same_dir(const char *src, int length, */ static int check_dir_in_index(const char *name) { + int ret = 1; const char *with_slash = add_slash(name); int length = strlen(with_slash); @@ -149,14 +150,18 @@ static int check_dir_in_index(const char *name) if (pos < 0) { pos = -pos - 1; if (pos >= the_index.cache_nr) - return 1; + goto free_return; ce = active_cache[pos]; if (strncmp(with_slash, ce->name, length)) - return 1; + goto free_return; if (ce_skip_worktree(ce)) - return 0; + ret = 0; } - return 1; + +free_return: + if (with_slash != name) + free((char *)with_slash); + return ret; } int cmd_mv(int argc, const char **argv, const char *prefix) From ffe59d806e6168a64454548f6a964c0703b7ff61 Mon Sep 17 00:00:00 2001 From: Shaoxuan Yuan Date: Tue, 19 Jul 2022 18:32:19 +0800 Subject: [PATCH 5/8] mv: check if is a SKIP_WORKTREE_DIR Originally, is assumed to be in the working tree. If it is not found as a directory, then it is determined to be either a regular file path, or error out if used under the second form (move into a directory) of 'git-mv'. Such behavior is not ideal, mainly because Git does not look into the index for , which could potentially be a SKIP_WORKTREE_DIR, which we need to determine for the later "moving from in-cone to out-of-cone" patch. Change the logic so that Git first check if is a directory with all its contents sparsified (a SKIP_WORKTREE_DIR). If yes, then treat as a directory exists in the working tree, and thus using the second form of 'git-mv', i.e. move into this , and mark as a SKIP_WORKTREE_DIR. If no, continue the original checking logic. Also add a `dst_w_slash` to reuse the result from `add_slash()`, which was everywhere and can be simplified. Signed-off-by: Shaoxuan Yuan --- builtin/mv.c | 19 ++++++++++++++----- 1 file changed, 14 insertions(+), 5 deletions(-) diff --git a/builtin/mv.c b/builtin/mv.c index 23a297d6b827e4..2e9d5772277c98 100644 --- a/builtin/mv.c +++ b/builtin/mv.c @@ -178,7 +178,8 @@ int cmd_mv(int argc, const char **argv, const char *prefix) OPT_END(), }; const char **source, **destination, **dest_path, **submodule_gitfile; - enum update_mode *modes; + const char *dst_w_slash; + enum update_mode *modes, dst_mode = 0; struct stat st; struct string_list src_for_dst = STRING_LIST_INIT_NODUP; struct lock_file lock_file = LOCK_INIT; @@ -208,6 +209,7 @@ int cmd_mv(int argc, const char **argv, const char *prefix) if (argc == 1 && is_directory(argv[0]) && !is_directory(argv[1])) flags = 0; dest_path = internal_prefix_pathspec(prefix, argv + argc, 1, flags); + dst_w_slash = add_slash(dest_path[0]); submodule_gitfile = xcalloc(argc, sizeof(char *)); if (dest_path[0][0] == '\0') @@ -215,13 +217,20 @@ int cmd_mv(int argc, const char **argv, const char *prefix) destination = internal_prefix_pathspec(dest_path[0], argv, argc, DUP_BASENAME); else if (!lstat(dest_path[0], &st) && S_ISDIR(st.st_mode)) { - dest_path[0] = add_slash(dest_path[0]); - destination = internal_prefix_pathspec(dest_path[0], argv, argc, DUP_BASENAME); + destination = internal_prefix_pathspec(dst_w_slash, argv, argc, DUP_BASENAME); } else { - if (argc != 1) + if (!path_in_sparse_checkout(dst_w_slash, &the_index) && + !check_dir_in_index(dst_w_slash)) { + destination = internal_prefix_pathspec(dst_w_slash, argv, argc, DUP_BASENAME); + dst_mode |= SKIP_WORKTREE_DIR; + } else if (argc != 1) { die(_("destination '%s' is not a directory"), dest_path[0]); - destination = dest_path; + } else { + destination = dest_path; + } } + if (dst_w_slash != dest_path[0]) + free((char *)dst_w_slash); /* Checking */ for (i = 0; i < argc; i++) { From d8d58dfc0dc2bfcf148343917480490fb23994a7 Mon Sep 17 00:00:00 2001 From: Shaoxuan Yuan Date: Tue, 19 Jul 2022 18:32:20 +0800 Subject: [PATCH 6/8] mv: remove BOTH from enum update_mode Since BOTH is not practically used anywhere in the code and its meaning is unclear, remove it. Signed-off-by: Shaoxuan Yuan --- builtin/mv.c | 1 - 1 file changed, 1 deletion(-) diff --git a/builtin/mv.c b/builtin/mv.c index 2e9d5772277c98..d05914a233bdff 100644 --- a/builtin/mv.c +++ b/builtin/mv.c @@ -21,7 +21,6 @@ static const char * const builtin_mv_usage[] = { }; enum update_mode { - BOTH = 0, WORKING_DIRECTORY = (1 << 1), INDEX = (1 << 2), SPARSE = (1 << 3), From 0ad4cf3f6de9054e26b93e37830610637c821da3 Mon Sep 17 00:00:00 2001 From: Shaoxuan Yuan Date: Tue, 19 Jul 2022 18:32:20 +0800 Subject: [PATCH 7/8] mv: from in-cone to out-of-cone Originally, moving an in-cone to an out-of-cone was not possible, mainly because such is a directory that is not present in the working tree. Change the behavior so that we can move an in-cone to out-of-cone when --sparse is supplied. Such can be either clean or dirty, and moving it results in different behaviors: A clean move should move the to the , both in the working tree and the index, then remove the resulted path from the working tree, and turn on its CE_SKIP_WORKTREE bit. A dirty move should move the to the , both in the working tree and the index, but should *not* remove the resulted path from the working tree and should *not* turn on its CE_SKIP_WORKTREE bit. Instead advise user to "git add" this path and run "git sparse-checkout reapply" to re-sparsify that path. Helped-by: Victoria Dye Signed-off-by: Shaoxuan Yuan --- advice.c | 19 +++++++++++++ advice.h | 1 + builtin/mv.c | 50 ++++++++++++++++++++++++++++++----- t/t7002-mv-sparse-checkout.sh | 8 +++--- 4 files changed, 67 insertions(+), 11 deletions(-) diff --git a/advice.c b/advice.c index 6fda9edbc2474f..fd189689437c75 100644 --- a/advice.c +++ b/advice.c @@ -261,3 +261,22 @@ void detach_advice(const char *new_name) fprintf(stderr, fmt, new_name); } + +void advise_on_moving_dirty_path(struct string_list *pathspec_list) +{ + struct string_list_item *item; + + if (!pathspec_list->nr) + return; + + fprintf(stderr, _("The following paths have been moved outside the\n" + "sparse-checkout definition but are not sparse due to local\n" + "modifications.\n")); + for_each_string_list_item(item, pathspec_list) + fprintf(stderr, "%s\n", item->string); + + advise_if_enabled(ADVICE_UPDATE_SPARSE_PATH, + _("To correct the sparsity of these paths, do the following:\n" + "* Use \"git add --sparse \" to update the index\n" + "* Use \"git sparse-checkout reapply\" to apply the sparsity rules")); +} diff --git a/advice.h b/advice.h index 7ddc6cbc1ac8d7..07e0f76833e780 100644 --- a/advice.h +++ b/advice.h @@ -74,5 +74,6 @@ void NORETURN die_conclude_merge(void); void NORETURN die_ff_impossible(void); void advise_on_updating_sparse_paths(struct string_list *pathspec_list); void detach_advice(const char *new_name); +void advise_on_moving_dirty_path(struct string_list *pathspec_list); #endif /* ADVICE_H */ diff --git a/builtin/mv.c b/builtin/mv.c index d05914a233bdff..d35994c443f09a 100644 --- a/builtin/mv.c +++ b/builtin/mv.c @@ -184,6 +184,7 @@ int cmd_mv(int argc, const char **argv, const char *prefix) struct lock_file lock_file = LOCK_INIT; struct cache_entry *ce; struct string_list only_match_skip_worktree = STRING_LIST_INIT_NODUP; + struct string_list dirty_paths = STRING_LIST_INIT_NODUP; git_config(git_default_config, NULL); @@ -414,6 +415,7 @@ int cmd_mv(int argc, const char **argv, const char *prefix) const char *src = source[i], *dst = destination[i]; enum update_mode mode = modes[i]; int pos; + int up_to_date = 0; struct checkout state = CHECKOUT_INIT; state.istate = &the_index; @@ -424,6 +426,7 @@ int cmd_mv(int argc, const char **argv, const char *prefix) if (show_only) continue; if (!(mode & (INDEX | SPARSE | SKIP_WORKTREE_DIR)) && + !(dst_mode & SKIP_WORKTREE_DIR) && rename(src, dst) < 0) { if (ignore_errors) continue; @@ -443,20 +446,52 @@ int cmd_mv(int argc, const char **argv, const char *prefix) pos = cache_name_pos(src, strlen(src)); assert(pos >= 0); + if (!(mode & SPARSE) && !lstat(src, &st)) + up_to_date = !ce_modified(active_cache[pos], &st, 0); rename_cache_entry_at(pos, dst); - if ((mode & SPARSE) && - (path_in_sparse_checkout(dst, &the_index))) { - int dst_pos; + if (ignore_sparse && + core_apply_sparse_checkout && + core_sparse_checkout_cone) { - dst_pos = cache_name_pos(dst, strlen(dst)); - active_cache[dst_pos]->ce_flags &= ~CE_SKIP_WORKTREE; + /* from out-of-cone to in-cone */ + if ((mode & SPARSE) && + path_in_sparse_checkout(dst, &the_index)) { + int dst_pos = cache_name_pos(dst, strlen(dst)); + struct cache_entry *dst_ce = active_cache[dst_pos]; - if (checkout_entry(active_cache[dst_pos], &state, NULL, NULL)) - die(_("cannot checkout %s"), active_cache[dst_pos]->name); + dst_ce->ce_flags &= ~CE_SKIP_WORKTREE; + + if (checkout_entry(dst_ce, &state, NULL, NULL)) + die(_("cannot checkout %s"), dst_ce->name); + continue; + } + + /* from in-cone to out-of-cone */ + if ((dst_mode & SKIP_WORKTREE_DIR) && + !(mode & SPARSE) && + !path_in_sparse_checkout(dst, &the_index)) { + int dst_pos = cache_name_pos(dst, strlen(dst)); + struct cache_entry *dst_ce = active_cache[dst_pos]; + char *src_dir = dirname(xstrdup(src)); + + if (up_to_date) { + dst_ce->ce_flags |= CE_SKIP_WORKTREE; + unlink_or_warn(src); + } else { + string_list_append(&dirty_paths, dst); + safe_create_leading_directories(xstrdup(dst)); + rename(src, dst); + } + if ((mode & INDEX) && is_empty_dir(src_dir)) + rmdir_or_warn(src_dir); + } } } + if (dirty_paths.nr) + advise_on_moving_dirty_path(&dirty_paths); + if (gitmodules_modified) stage_updated_gitmodules(&the_index); @@ -465,6 +500,7 @@ int cmd_mv(int argc, const char **argv, const char *prefix) die(_("Unable to write new index file")); string_list_clear(&src_for_dst, 0); + string_list_clear(&dirty_paths, 0); UNLEAK(source); UNLEAK(dest_path); free(submodule_gitfile); diff --git a/t/t7002-mv-sparse-checkout.sh b/t/t7002-mv-sparse-checkout.sh index c27fcdbfd0ee1e..dafe15b9cf479b 100755 --- a/t/t7002-mv-sparse-checkout.sh +++ b/t/t7002-mv-sparse-checkout.sh @@ -303,7 +303,7 @@ test_expect_success 'move sparse file to existing destination with --force and - test_cmp expect sub/file1 ' -test_expect_failure 'move clean path from in-cone to out-of-cone' ' +test_expect_success 'move clean path from in-cone to out-of-cone' ' test_when_finished "cleanup_sparse_checkout" && setup_sparse_checkout && @@ -356,7 +356,7 @@ test_expect_failure 'move clean path from in-cone to out-of-cone overwrite' ' test_cmp expect actual ' -test_expect_failure 'move dirty path from in-cone to out-of-cone' ' +test_expect_success 'move dirty path from in-cone to out-of-cone' ' test_when_finished "cleanup_sparse_checkout" && setup_sparse_checkout && echo "modified" >>sub/d && @@ -380,7 +380,7 @@ test_expect_failure 'move dirty path from in-cone to out-of-cone' ' grep -x "H folder1/d" actual ' -test_expect_failure 'move dir from in-cone to out-of-cone' ' +test_expect_success 'move dir from in-cone to out-of-cone' ' test_when_finished "cleanup_sparse_checkout" && setup_sparse_checkout && @@ -400,7 +400,7 @@ test_expect_failure 'move dir from in-cone to out-of-cone' ' grep -x "S folder1/dir/e" actual ' -test_expect_failure 'move partially-dirty dir from in-cone to out-of-cone' ' +test_expect_success 'move partially-dirty dir from in-cone to out-of-cone' ' test_when_finished "cleanup_sparse_checkout" && setup_sparse_checkout && touch sub/dir/e2 sub/dir/e3 && From 7e60507efb876ccbbe935ba1995f8a675487b264 Mon Sep 17 00:00:00 2001 From: Shaoxuan Yuan Date: Tue, 19 Jul 2022 18:32:20 +0800 Subject: [PATCH 8/8] mv: check overwrite for in-to-out move Add checking logic for overwritng when moving from in-cone to out-of-cone. It is the index version of the original overwrite logic. Signed-off-by: Shaoxuan Yuan --- builtin/mv.c | 12 ++++++++++++ t/t7002-mv-sparse-checkout.sh | 2 +- 2 files changed, 13 insertions(+), 1 deletion(-) diff --git a/builtin/mv.c b/builtin/mv.c index d35994c443f09a..5ed3bd3431858d 100644 --- a/builtin/mv.c +++ b/builtin/mv.c @@ -365,6 +365,18 @@ int cmd_mv(int argc, const char **argv, const char *prefix) goto act_on_entry; } + if (ignore_sparse && + (dst_mode & SKIP_WORKTREE_DIR) && + index_entry_exists(&the_index, dst, strlen(dst))) { + bad = _("destination exists in the index"); + if (force) { + if (verbose) + warning(_("overwriting '%s'"), dst); + bad = NULL; + } else { + goto act_on_entry; + } + } /* * We check if the paths are in the sparse-checkout * definition as a very final check, since that diff --git a/t/t7002-mv-sparse-checkout.sh b/t/t7002-mv-sparse-checkout.sh index dafe15b9cf479b..5d810f3af03e0c 100755 --- a/t/t7002-mv-sparse-checkout.sh +++ b/t/t7002-mv-sparse-checkout.sh @@ -323,7 +323,7 @@ test_expect_success 'move clean path from in-cone to out-of-cone' ' grep -x "S folder1/d" actual ' -test_expect_failure 'move clean path from in-cone to out-of-cone overwrite' ' +test_expect_success 'move clean path from in-cone to out-of-cone overwrite' ' test_when_finished "cleanup_sparse_checkout" && setup_sparse_checkout && echo "sub/file1 overwrite" >sub/file1 &&