@@ -118,21 +118,23 @@ static int index_range_of_same_dir(const char *src, int length,
118
118
int cmd_mv (int argc , const char * * argv , const char * prefix )
119
119
{
120
120
int i , flags , gitmodules_modified = 0 ;
121
- int verbose = 0 , show_only = 0 , force = 0 , ignore_errors = 0 ;
121
+ int verbose = 0 , show_only = 0 , force = 0 , ignore_errors = 0 , ignore_sparse = 0 ;
122
122
struct option builtin_mv_options [] = {
123
123
OPT__VERBOSE (& verbose , N_ ("be verbose" )),
124
124
OPT__DRY_RUN (& show_only , N_ ("dry run" )),
125
125
OPT__FORCE (& force , N_ ("force move/rename even if target exists" ),
126
126
PARSE_OPT_NOCOMPLETE ),
127
127
OPT_BOOL ('k' , NULL , & ignore_errors , N_ ("skip move/rename errors" )),
128
+ OPT_BOOL (0 , "sparse" , & ignore_sparse , N_ ("allow updating entries outside of the sparse-checkout cone" )),
128
129
OPT_END (),
129
130
};
130
131
const char * * source , * * destination , * * dest_path , * * submodule_gitfile ;
131
- enum update_mode { BOTH = 0 , WORKING_DIRECTORY , INDEX } * modes ;
132
+ enum update_mode { BOTH = 0 , WORKING_DIRECTORY , INDEX , SPARSE } * modes ;
132
133
struct stat st ;
133
134
struct string_list src_for_dst = STRING_LIST_INIT_NODUP ;
134
135
struct lock_file lock_file = LOCK_INIT ;
135
136
struct cache_entry * ce ;
137
+ struct string_list only_match_skip_worktree = STRING_LIST_INIT_NODUP ;
136
138
137
139
git_config (git_default_config , NULL );
138
140
@@ -176,14 +178,17 @@ int cmd_mv(int argc, const char **argv, const char *prefix)
176
178
const char * src = source [i ], * dst = destination [i ];
177
179
int length , src_is_dir ;
178
180
const char * bad = NULL ;
181
+ int skip_sparse = 0 ;
179
182
180
183
if (show_only )
181
184
printf (_ ("Checking rename of '%s' to '%s'\n" ), src , dst );
182
185
183
186
length = strlen (src );
184
- if (lstat (src , & st ) < 0 )
185
- bad = _ ("bad source" );
186
- else if (!strncmp (src , dst , length ) &&
187
+ if (lstat (src , & st ) < 0 ) {
188
+ /* only error if existence is expected. */
189
+ if (modes [i ] != SPARSE )
190
+ bad = _ ("bad source" );
191
+ } else if (!strncmp (src , dst , length ) &&
187
192
(dst [length ] == 0 || dst [length ] == '/' )) {
188
193
bad = _ ("can not move directory into itself" );
189
194
} else if ((src_is_dir = S_ISDIR (st .st_mode ))
@@ -212,11 +217,12 @@ int cmd_mv(int argc, const char **argv, const char *prefix)
212
217
dst_len = strlen (dst );
213
218
214
219
for (j = 0 ; j < last - first ; j ++ ) {
215
- const char * path = active_cache [first + j ]-> name ;
220
+ const struct cache_entry * ce = active_cache [first + j ];
221
+ const char * path = ce -> name ;
216
222
source [argc + j ] = path ;
217
223
destination [argc + j ] =
218
224
prefix_path (dst , dst_len , path + length + 1 );
219
- modes [argc + j ] = INDEX ;
225
+ modes [argc + j ] = ce_skip_worktree ( ce ) ? SPARSE : INDEX ;
220
226
submodule_gitfile [argc + j ] = NULL ;
221
227
}
222
228
argc += last - first ;
@@ -244,14 +250,36 @@ int cmd_mv(int argc, const char **argv, const char *prefix)
244
250
bad = _ ("multiple sources for the same target" );
245
251
else if (is_dir_sep (dst [strlen (dst ) - 1 ]))
246
252
bad = _ ("destination directory does not exist" );
247
- else
253
+ else {
254
+ /*
255
+ * We check if the paths are in the sparse-checkout
256
+ * definition as a very final check, since that
257
+ * allows us to point the user to the --sparse
258
+ * option as a way to have a successful run.
259
+ */
260
+ if (!ignore_sparse &&
261
+ !path_in_sparse_checkout (src , & the_index )) {
262
+ string_list_append (& only_match_skip_worktree , src );
263
+ skip_sparse = 1 ;
264
+ }
265
+ if (!ignore_sparse &&
266
+ !path_in_sparse_checkout (dst , & the_index )) {
267
+ string_list_append (& only_match_skip_worktree , dst );
268
+ skip_sparse = 1 ;
269
+ }
270
+
271
+ if (skip_sparse )
272
+ goto remove_entry ;
273
+
248
274
string_list_insert (& src_for_dst , dst );
275
+ }
249
276
250
277
if (!bad )
251
278
continue ;
252
279
if (!ignore_errors )
253
280
die (_ ("%s, source=%s, destination=%s" ),
254
281
bad , src , dst );
282
+ remove_entry :
255
283
if (-- argc > 0 ) {
256
284
int n = argc - i ;
257
285
memmove (source + i , source + i + 1 ,
@@ -266,6 +294,12 @@ int cmd_mv(int argc, const char **argv, const char *prefix)
266
294
}
267
295
}
268
296
297
+ if (only_match_skip_worktree .nr ) {
298
+ advise_on_updating_sparse_paths (& only_match_skip_worktree );
299
+ if (!ignore_errors )
300
+ return 1 ;
301
+ }
302
+
269
303
for (i = 0 ; i < argc ; i ++ ) {
270
304
const char * src = source [i ], * dst = destination [i ];
271
305
enum update_mode mode = modes [i ];
@@ -274,7 +308,7 @@ int cmd_mv(int argc, const char **argv, const char *prefix)
274
308
printf (_ ("Renaming %s to %s\n" ), src , dst );
275
309
if (show_only )
276
310
continue ;
277
- if (mode != INDEX && rename (src , dst ) < 0 ) {
311
+ if (mode != INDEX && mode != SPARSE && rename (src , dst ) < 0 ) {
278
312
if (ignore_errors )
279
313
continue ;
280
314
die_errno (_ ("renaming '%s' failed" ), src );
0 commit comments