Skip to content

Commit b91a2b6

Browse files
ffyuandagitster
authored andcommitted
mv: add check_dir_in_index() and solve general dir check issue
Originally, moving a <source> directory which is not on-disk due to its existence outside of sparse-checkout cone, "giv mv" command errors out with "bad source". Add a helper check_dir_in_index() function to see if a directory name exists in the index. Also add a SKIP_WORKTREE_DIR bit to mark such directories. Change the checking logic, so that such <source> directory makes "giv mv" command warns with "advise_on_updating_sparse_paths()" instead of "bad source"; also user now can supply a "--sparse" flag so this operation can be carried out successfully. Helped-by: Victoria Dye <[email protected]> Helped-by: Derrick Stolee <[email protected]> Signed-off-by: Shaoxuan Yuan <[email protected]> Acked-by: Derrick Stolee <[email protected]> Signed-off-by: Junio C Hamano <[email protected]>
1 parent 24ea81d commit b91a2b6

File tree

3 files changed

+47
-9
lines changed

3 files changed

+47
-9
lines changed

builtin/mv.c

Lines changed: 44 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ enum update_mode {
2525
WORKING_DIRECTORY = (1 << 1),
2626
INDEX = (1 << 2),
2727
SPARSE = (1 << 3),
28+
SKIP_WORKTREE_DIR = (1 << 4),
2829
};
2930

3031
#define DUP_BASENAME 1
@@ -123,6 +124,36 @@ static int index_range_of_same_dir(const char *src, int length,
123124
return last - first;
124125
}
125126

127+
/*
128+
* Check if an out-of-cone directory should be in the index. Imagine this case
129+
* that all the files under a directory are marked with 'CE_SKIP_WORKTREE' bit
130+
* and thus the directory is sparsified.
131+
*
132+
* Return 0 if such directory exist (i.e. with any of its contained files not
133+
* marked with CE_SKIP_WORKTREE, the directory would be present in working tree).
134+
* Return 1 otherwise.
135+
*/
136+
static int check_dir_in_index(const char *name)
137+
{
138+
const char *with_slash = add_slash(name);
139+
int length = strlen(with_slash);
140+
141+
int pos = cache_name_pos(with_slash, length);
142+
const struct cache_entry *ce;
143+
144+
if (pos < 0) {
145+
pos = -pos - 1;
146+
if (pos >= the_index.cache_nr)
147+
return 1;
148+
ce = active_cache[pos];
149+
if (strncmp(with_slash, ce->name, length))
150+
return 1;
151+
if (ce_skip_worktree(ce))
152+
return 0;
153+
}
154+
return 1;
155+
}
156+
126157
int cmd_mv(int argc, const char **argv, const char *prefix)
127158
{
128159
int i, flags, gitmodules_modified = 0;
@@ -184,7 +215,7 @@ int cmd_mv(int argc, const char **argv, const char *prefix)
184215
/* Checking */
185216
for (i = 0; i < argc; i++) {
186217
const char *src = source[i], *dst = destination[i];
187-
int length, src_is_dir;
218+
int length;
188219
const char *bad = NULL;
189220
int skip_sparse = 0;
190221

@@ -198,12 +229,17 @@ int cmd_mv(int argc, const char **argv, const char *prefix)
198229

199230
pos = cache_name_pos(src, length);
200231
if (pos < 0) {
232+
const char *src_w_slash = add_slash(src);
233+
if (!path_in_sparse_checkout(src_w_slash, &the_index) &&
234+
!check_dir_in_index(src)) {
235+
modes[i] |= SKIP_WORKTREE_DIR;
236+
goto dir_check;
237+
}
201238
/* only error if existence is expected. */
202239
if (!(modes[i] & SPARSE))
203240
bad = _("bad source");
204241
goto act_on_entry;
205242
}
206-
207243
ce = active_cache[pos];
208244
if (!ce_skip_worktree(ce)) {
209245
bad = _("bad source");
@@ -230,12 +266,14 @@ int cmd_mv(int argc, const char **argv, const char *prefix)
230266
bad = _("can not move directory into itself");
231267
goto act_on_entry;
232268
}
233-
if ((src_is_dir = S_ISDIR(st.st_mode))
269+
if (S_ISDIR(st.st_mode)
234270
&& lstat(dst, &st) == 0) {
235271
bad = _("cannot move directory over file");
236272
goto act_on_entry;
237273
}
238-
if (src_is_dir) {
274+
275+
dir_check:
276+
if (S_ISDIR(st.st_mode)) {
239277
int j, dst_len, n;
240278
int first = cache_name_pos(src, length), last;
241279

@@ -369,7 +407,7 @@ int cmd_mv(int argc, const char **argv, const char *prefix)
369407
printf(_("Renaming %s to %s\n"), src, dst);
370408
if (show_only)
371409
continue;
372-
if (!(mode & (INDEX | SPARSE)) &&
410+
if (!(mode & (INDEX | SPARSE | SKIP_WORKTREE_DIR)) &&
373411
rename(src, dst) < 0) {
374412
if (ignore_errors)
375413
continue;
@@ -384,7 +422,7 @@ int cmd_mv(int argc, const char **argv, const char *prefix)
384422
1);
385423
}
386424

387-
if (mode & (WORKING_DIRECTORY))
425+
if (mode & (WORKING_DIRECTORY | SKIP_WORKTREE_DIR))
388426
continue;
389427

390428
pos = cache_name_pos(src, strlen(src));

t/t1092-sparse-checkout-compatibility.sh

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1828,7 +1828,7 @@ test_expect_success 'checkout behaves oddly with df-conflict-2' '
18281828
test_cmp full-checkout-err sparse-index-err
18291829
'
18301830

1831-
test_expect_failure 'mv directory from out-of-cone to in-cone' '
1831+
test_expect_success 'mv directory from out-of-cone to in-cone' '
18321832
init_repos &&
18331833
18341834
# <source> as a sparse directory (or SKIP_WORKTREE_DIR without enabling

t/t7002-mv-sparse-checkout.sh

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -219,7 +219,7 @@ test_expect_success 'refuse to move file to non-skip-worktree sparse path' '
219219
test_cmp expect stderr
220220
'
221221

222-
test_expect_failure 'refuse to move out-of-cone directory without --sparse' '
222+
test_expect_success 'refuse to move out-of-cone directory without --sparse' '
223223
test_when_finished "cleanup_sparse_checkout" &&
224224
setup_sparse_checkout &&
225225
@@ -230,7 +230,7 @@ test_expect_failure 'refuse to move out-of-cone directory without --sparse' '
230230
test_cmp expect stderr
231231
'
232232

233-
test_expect_failure 'can move out-of-cone directory with --sparse' '
233+
test_expect_success 'can move out-of-cone directory with --sparse' '
234234
test_when_finished "cleanup_sparse_checkout" &&
235235
setup_sparse_checkout &&
236236

0 commit comments

Comments
 (0)