Skip to content

Commit bc9adea

Browse files
mv: integrate with sparse-index
Teach 'git mv' to work with a sparse index without expanding it for in-cone file moves. Set command_requires_full_index to 0 so that the sparse index is preserved when reading. For --sparse operations that reference out-of-cone paths, expand the full index immediately since those operations need access to individual file entries within sparse directory entries. For in-cone moves, the entries are already individual in the sparse index, so no expansion is needed. This provides a performance benefit for the common case of renaming files within the working directory. Update empty_dir_has_sparse_contents() to use index_name_pos_sparse() (which avoids triggering expansion) and to recognize sparse directory entries directly via S_ISSPARSEDIR, in addition to the existing logic that finds individual skip-worktree entries beneath a directory prefix. Signed-off-by: Derrick Stolee <stolee@gmail.com> Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
1 parent d0242bf commit bc9adea

2 files changed

Lines changed: 37 additions & 8 deletions

File tree

builtin/mv.c

Lines changed: 28 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -24,9 +24,11 @@
2424
#include "read-cache-ll.h"
2525

2626
#include "setup.h"
27+
#include "sparse-index.h"
2728
#include "strvec.h"
2829
#include "submodule.h"
2930
#include "entry.h"
31+
#include "repository.h"
3032

3133
static const char * const builtin_mv_usage[] = {
3234
N_("git mv [-v] [-f] [-n] [-k] <source> <destination>"),
@@ -149,20 +151,26 @@ static int empty_dir_has_sparse_contents(const char *name)
149151
char *with_slash = add_slash(name);
150152
int length = strlen(with_slash);
151153

152-
int pos = index_name_pos(the_repository->index, with_slash, length);
154+
int pos = index_name_pos_sparse(the_repository->index, with_slash, length);
153155
const struct cache_entry *ce;
154156

155-
if (pos < 0) {
156-
pos = -pos - 1;
157-
if (pos >= the_repository->index->cache_nr)
158-
goto free_return;
157+
if (pos >= 0) {
158+
/* Exact match: sparse directory entry in a sparse index */
159159
ce = the_repository->index->cache[pos];
160-
if (strncmp(with_slash, ce->name, length))
161-
goto free_return;
162-
if (ce_skip_worktree(ce))
160+
if (S_ISSPARSEDIR(ce->ce_mode))
163161
ret = 1;
162+
goto free_return;
164163
}
165164

165+
pos = -pos - 1;
166+
if (pos >= the_repository->index->cache_nr)
167+
goto free_return;
168+
ce = the_repository->index->cache[pos];
169+
if (strncmp(with_slash, ce->name, length))
170+
goto free_return;
171+
if (ce_skip_worktree(ce))
172+
ret = 1;
173+
166174
free_return:
167175
free(with_slash);
168176
return ret;
@@ -241,6 +249,8 @@ int cmd_mv(int argc,
241249
struct repo_config_values *cfg = repo_config_values(the_repository);
242250

243251
repo_config(the_repository, git_default_config, NULL);
252+
prepare_repo_settings(the_repository);
253+
the_repository->settings.command_requires_full_index = 0;
244254

245255
argc = parse_options(argc, argv, prefix, builtin_mv_options,
246256
builtin_mv_usage, 0);
@@ -251,6 +261,16 @@ int cmd_mv(int argc,
251261
if (repo_read_index(the_repository) < 0)
252262
die(_("index file corrupt"));
253263

264+
/*
265+
* When --sparse is used, we may need to operate on entries outside
266+
* the sparse-checkout cone which requires a full index. Expand now
267+
* so that the rest of the command works on individual file entries.
268+
* Without --sparse, only in-cone moves are allowed, and those
269+
* entries are already expanded in the sparse index.
270+
*/
271+
if (ignore_sparse)
272+
ensure_full_index(the_repository->index);
273+
254274
internal_prefix_pathspec(&sources, prefix, argv, argc, 0);
255275
CALLOC_ARRAY(modes, argc);
256276

t/t1092-sparse-checkout-compatibility.sh

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2876,6 +2876,15 @@ test_expect_success 'mv: in-cone to existing file collision' '
28762876
test_all_match git diff --cached --name-status
28772877
'
28782878

2879+
test_expect_success 'sparse-index is not expanded: mv in-cone' '
2880+
init_repos &&
2881+
2882+
# Moving a file within the sparse-checkout cone does not
2883+
# require expanding the index since those entries are
2884+
# already individual in the sparse index.
2885+
ensure_not_expanded mv deep/a deep/moved-a
2886+
'
2887+
28792888
test_expect_success 'sparse-index is expanded: mv with out-of-cone source' '
28802889
init_repos &&
28812890

0 commit comments

Comments
 (0)