@@ -118,21 +118,23 @@ static int index_range_of_same_dir(const char *src, int length,
118118int cmd_mv (int argc , const char * * argv , const char * prefix )
119119{
120120 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 ;
122122 struct option builtin_mv_options [] = {
123123 OPT__VERBOSE (& verbose , N_ ("be verbose" )),
124124 OPT__DRY_RUN (& show_only , N_ ("dry run" )),
125125 OPT__FORCE (& force , N_ ("force move/rename even if target exists" ),
126126 PARSE_OPT_NOCOMPLETE ),
127127 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" )),
128129 OPT_END (),
129130 };
130131 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 ;
132133 struct stat st ;
133134 struct string_list src_for_dst = STRING_LIST_INIT_NODUP ;
134135 struct lock_file lock_file = LOCK_INIT ;
135136 struct cache_entry * ce ;
137+ struct string_list only_match_skip_worktree = STRING_LIST_INIT_NODUP ;
136138
137139 git_config (git_default_config , NULL );
138140
@@ -176,14 +178,17 @@ int cmd_mv(int argc, const char **argv, const char *prefix)
176178 const char * src = source [i ], * dst = destination [i ];
177179 int length , src_is_dir ;
178180 const char * bad = NULL ;
181+ int skip_sparse = 0 ;
179182
180183 if (show_only )
181184 printf (_ ("Checking rename of '%s' to '%s'\n" ), src , dst );
182185
183186 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 ) &&
187192 (dst [length ] == 0 || dst [length ] == '/' )) {
188193 bad = _ ("can not move directory into itself" );
189194 } 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)
212217 dst_len = strlen (dst );
213218
214219 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 ;
216222 source [argc + j ] = path ;
217223 destination [argc + j ] =
218224 prefix_path (dst , dst_len , path + length + 1 );
219- modes [argc + j ] = INDEX ;
225+ modes [argc + j ] = ce_skip_worktree ( ce ) ? SPARSE : INDEX ;
220226 submodule_gitfile [argc + j ] = NULL ;
221227 }
222228 argc += last - first ;
@@ -244,14 +250,36 @@ int cmd_mv(int argc, const char **argv, const char *prefix)
244250 bad = _ ("multiple sources for the same target" );
245251 else if (is_dir_sep (dst [strlen (dst ) - 1 ]))
246252 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+
248274 string_list_insert (& src_for_dst , dst );
275+ }
249276
250277 if (!bad )
251278 continue ;
252279 if (!ignore_errors )
253280 die (_ ("%s, source=%s, destination=%s" ),
254281 bad , src , dst );
282+ remove_entry :
255283 if (-- argc > 0 ) {
256284 int n = argc - i ;
257285 memmove (source + i , source + i + 1 ,
@@ -266,6 +294,12 @@ int cmd_mv(int argc, const char **argv, const char *prefix)
266294 }
267295 }
268296
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+
269303 for (i = 0 ; i < argc ; i ++ ) {
270304 const char * src = source [i ], * dst = destination [i ];
271305 enum update_mode mode = modes [i ];
@@ -274,7 +308,7 @@ int cmd_mv(int argc, const char **argv, const char *prefix)
274308 printf (_ ("Renaming %s to %s\n" ), src , dst );
275309 if (show_only )
276310 continue ;
277- if (mode != INDEX && rename (src , dst ) < 0 ) {
311+ if (mode != INDEX && mode != SPARSE && rename (src , dst ) < 0 ) {
278312 if (ignore_errors )
279313 continue ;
280314 die_errno (_ ("renaming '%s' failed" ), src );
0 commit comments