@@ -21,7 +21,6 @@ static const char * const builtin_mv_usage[] = {
21
21
};
22
22
23
23
enum update_mode {
24
- BOTH = 0 ,
25
24
WORKING_DIRECTORY = (1 << 1 ),
26
25
INDEX = (1 << 2 ),
27
26
SPARSE = (1 << 3 ),
@@ -72,7 +71,7 @@ static const char **internal_prefix_pathspec(const char *prefix,
72
71
static const char * add_slash (const char * path )
73
72
{
74
73
size_t len = strlen (path );
75
- if (path [len - 1 ] != '/' ) {
74
+ if (len && path [len - 1 ] != '/' ) {
76
75
char * with_slash = xmalloc (st_add (len , 2 ));
77
76
memcpy (with_slash , path , len );
78
77
with_slash [len ++ ] = '/' ;
@@ -125,16 +124,15 @@ static int index_range_of_same_dir(const char *src, int length,
125
124
}
126
125
127
126
/*
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.
127
+ * Given the path of a directory that does not exist on-disk, check whether the
128
+ * directory contains any entries in the index with the SKIP_WORKTREE flag
129
+ * enabled.
130
+ * Return 1 if such index entries exist.
131
+ * Return 0 otherwise.
135
132
*/
136
- static int check_dir_in_index (const char * name )
133
+ static int empty_dir_has_sparse_contents (const char * name )
137
134
{
135
+ int ret = 0 ;
138
136
const char * with_slash = add_slash (name );
139
137
int length = strlen (with_slash );
140
138
@@ -144,14 +142,18 @@ static int check_dir_in_index(const char *name)
144
142
if (pos < 0 ) {
145
143
pos = - pos - 1 ;
146
144
if (pos >= the_index .cache_nr )
147
- return 1 ;
145
+ goto free_return ;
148
146
ce = active_cache [pos ];
149
147
if (strncmp (with_slash , ce -> name , length ))
150
- return 1 ;
148
+ goto free_return ;
151
149
if (ce_skip_worktree (ce ))
152
- return 0 ;
150
+ ret = 1 ;
153
151
}
154
- return 1 ;
152
+
153
+ free_return :
154
+ if (with_slash != name )
155
+ free ((char * )with_slash );
156
+ return ret ;
155
157
}
156
158
157
159
int cmd_mv (int argc , const char * * argv , const char * prefix )
@@ -168,12 +170,17 @@ int cmd_mv(int argc, const char **argv, const char *prefix)
168
170
OPT_END (),
169
171
};
170
172
const char * * source , * * destination , * * dest_path , * * submodule_gitfile ;
171
- enum update_mode * modes ;
173
+ const char * dst_w_slash ;
174
+ const char * * src_dir = NULL ;
175
+ int src_dir_nr = 0 , src_dir_alloc = 0 ;
176
+ struct strbuf a_src_dir = STRBUF_INIT ;
177
+ enum update_mode * modes , dst_mode = 0 ;
172
178
struct stat st ;
173
179
struct string_list src_for_dst = STRING_LIST_INIT_NODUP ;
174
180
struct lock_file lock_file = LOCK_INIT ;
175
181
struct cache_entry * ce ;
176
182
struct string_list only_match_skip_worktree = STRING_LIST_INIT_NODUP ;
183
+ struct string_list dirty_paths = STRING_LIST_INIT_NODUP ;
177
184
178
185
git_config (git_default_config , NULL );
179
186
@@ -198,19 +205,39 @@ int cmd_mv(int argc, const char **argv, const char *prefix)
198
205
if (argc == 1 && is_directory (argv [0 ]) && !is_directory (argv [1 ]))
199
206
flags = 0 ;
200
207
dest_path = internal_prefix_pathspec (prefix , argv + argc , 1 , flags );
208
+ dst_w_slash = add_slash (dest_path [0 ]);
201
209
submodule_gitfile = xcalloc (argc , sizeof (char * ));
202
210
203
211
if (dest_path [0 ][0 ] == '\0' )
204
212
/* special case: "." was normalized to "" */
205
213
destination = internal_prefix_pathspec (dest_path [0 ], argv , argc , DUP_BASENAME );
206
214
else if (!lstat (dest_path [0 ], & st ) &&
207
215
S_ISDIR (st .st_mode )) {
208
- dest_path [0 ] = add_slash (dest_path [0 ]);
209
- destination = internal_prefix_pathspec (dest_path [0 ], argv , argc , DUP_BASENAME );
216
+ destination = internal_prefix_pathspec (dst_w_slash , argv , argc , DUP_BASENAME );
210
217
} else {
211
- if (argc != 1 )
218
+ if (!path_in_sparse_checkout (dst_w_slash , & the_index ) &&
219
+ empty_dir_has_sparse_contents (dst_w_slash )) {
220
+ destination = internal_prefix_pathspec (dst_w_slash , argv , argc , DUP_BASENAME );
221
+ dst_mode = SKIP_WORKTREE_DIR ;
222
+ } else if (argc != 1 ) {
212
223
die (_ ("destination '%s' is not a directory" ), dest_path [0 ]);
213
- destination = dest_path ;
224
+ } else {
225
+ destination = dest_path ;
226
+ /*
227
+ * <destination> is a file outside of sparse-checkout
228
+ * cone. Insist on cone mode here for backward
229
+ * compatibility. We don't want dst_mode to be assigned
230
+ * for a file when the repo is using no-cone mode (which
231
+ * is deprecated at this point) sparse-checkout. As
232
+ * SPARSE here is only considering cone-mode situation.
233
+ */
234
+ if (!path_in_cone_mode_sparse_checkout (destination [0 ], & the_index ))
235
+ dst_mode = SPARSE ;
236
+ }
237
+ }
238
+ if (dst_w_slash != dest_path [0 ]) {
239
+ free ((char * )dst_w_slash );
240
+ dst_w_slash = NULL ;
214
241
}
215
242
216
243
/* Checking */
@@ -232,7 +259,7 @@ int cmd_mv(int argc, const char **argv, const char *prefix)
232
259
if (pos < 0 ) {
233
260
const char * src_w_slash = add_slash (src );
234
261
if (!path_in_sparse_checkout (src_w_slash , & the_index ) &&
235
- ! check_dir_in_index (src )) {
262
+ empty_dir_has_sparse_contents (src )) {
236
263
modes [i ] |= SKIP_WORKTREE_DIR ;
237
264
goto dir_check ;
238
265
}
@@ -290,6 +317,10 @@ int cmd_mv(int argc, const char **argv, const char *prefix)
290
317
291
318
/* last - first >= 1 */
292
319
modes [i ] |= WORKING_DIRECTORY ;
320
+
321
+ ALLOC_GROW (src_dir , src_dir_nr + 1 , src_dir_alloc );
322
+ src_dir [src_dir_nr ++ ] = src ;
323
+
293
324
n = argc + last - first ;
294
325
REALLOC_ARRAY (source , n );
295
326
REALLOC_ARRAY (destination , n );
@@ -346,6 +377,18 @@ int cmd_mv(int argc, const char **argv, const char *prefix)
346
377
goto act_on_entry ;
347
378
}
348
379
380
+ if (ignore_sparse &&
381
+ (dst_mode & (SKIP_WORKTREE_DIR | SPARSE )) &&
382
+ index_entry_exists (& the_index , dst , strlen (dst ))) {
383
+ bad = _ ("destination exists in the index" );
384
+ if (force ) {
385
+ if (verbose )
386
+ warning (_ ("overwriting '%s'" ), dst );
387
+ bad = NULL ;
388
+ } else {
389
+ goto act_on_entry ;
390
+ }
391
+ }
349
392
/*
350
393
* We check if the paths are in the sparse-checkout
351
394
* definition as a very final check, since that
@@ -396,6 +439,7 @@ int cmd_mv(int argc, const char **argv, const char *prefix)
396
439
const char * src = source [i ], * dst = destination [i ];
397
440
enum update_mode mode = modes [i ];
398
441
int pos ;
442
+ int sparse_and_dirty = 0 ;
399
443
struct checkout state = CHECKOUT_INIT ;
400
444
state .istate = & the_index ;
401
445
@@ -406,6 +450,7 @@ int cmd_mv(int argc, const char **argv, const char *prefix)
406
450
if (show_only )
407
451
continue ;
408
452
if (!(mode & (INDEX | SPARSE | SKIP_WORKTREE_DIR )) &&
453
+ !(dst_mode & (SKIP_WORKTREE_DIR | SPARSE )) &&
409
454
rename (src , dst ) < 0 ) {
410
455
if (ignore_errors )
411
456
continue ;
@@ -425,20 +470,81 @@ int cmd_mv(int argc, const char **argv, const char *prefix)
425
470
426
471
pos = cache_name_pos (src , strlen (src ));
427
472
assert (pos >= 0 );
473
+ if (!(mode & SPARSE ) && !lstat (src , & st ))
474
+ sparse_and_dirty = ce_modified (active_cache [pos ], & st , 0 );
428
475
rename_cache_entry_at (pos , dst );
429
476
430
- if ((mode & SPARSE ) &&
431
- (path_in_sparse_checkout (dst , & the_index ))) {
432
- int dst_pos ;
477
+ if (ignore_sparse &&
478
+ core_apply_sparse_checkout &&
479
+ core_sparse_checkout_cone ) {
480
+ /*
481
+ * NEEDSWORK: we are *not* paying attention to
482
+ * "out-to-out" move (<source> is out-of-cone and
483
+ * <destination> is out-of-cone) at this point. It
484
+ * should be added in a future patch.
485
+ */
486
+ if ((mode & SPARSE ) &&
487
+ path_in_sparse_checkout (dst , & the_index )) {
488
+ /* from out-of-cone to in-cone */
489
+ int dst_pos = cache_name_pos (dst , strlen (dst ));
490
+ struct cache_entry * dst_ce = active_cache [dst_pos ];
491
+
492
+ dst_ce -> ce_flags &= ~CE_SKIP_WORKTREE ;
493
+
494
+ if (checkout_entry (dst_ce , & state , NULL , NULL ))
495
+ die (_ ("cannot checkout %s" ), dst_ce -> name );
496
+ } else if ((dst_mode & (SKIP_WORKTREE_DIR | SPARSE )) &&
497
+ !(mode & SPARSE ) &&
498
+ !path_in_sparse_checkout (dst , & the_index )) {
499
+ /* from in-cone to out-of-cone */
500
+ int dst_pos = cache_name_pos (dst , strlen (dst ));
501
+ struct cache_entry * dst_ce = active_cache [dst_pos ];
433
502
434
- dst_pos = cache_name_pos (dst , strlen (dst ));
435
- active_cache [dst_pos ]-> ce_flags &= ~CE_SKIP_WORKTREE ;
503
+ /*
504
+ * if src is clean, it will suffice to remove it
505
+ */
506
+ if (!sparse_and_dirty ) {
507
+ dst_ce -> ce_flags |= CE_SKIP_WORKTREE ;
508
+ unlink_or_warn (src );
509
+ } else {
510
+ /*
511
+ * if src is dirty, move it to the
512
+ * destination and create leading
513
+ * dirs if necessary
514
+ */
515
+ char * dst_dup = xstrdup (dst );
516
+ string_list_append (& dirty_paths , dst );
517
+ safe_create_leading_directories (dst_dup );
518
+ FREE_AND_NULL (dst_dup );
519
+ rename (src , dst );
520
+ }
521
+ }
522
+ }
523
+ }
436
524
437
- if (checkout_entry (active_cache [dst_pos ], & state , NULL , NULL ))
438
- die (_ ("cannot checkout %s" ), active_cache [dst_pos ]-> name );
525
+ /*
526
+ * cleanup the empty src_dirs
527
+ */
528
+ for (i = 0 ; i < src_dir_nr ; i ++ ) {
529
+ int dummy ;
530
+ strbuf_addstr (& a_src_dir , src_dir [i ]);
531
+ /*
532
+ * if entries under a_src_dir are all moved away,
533
+ * recursively remove a_src_dir to cleanup
534
+ */
535
+ if (index_range_of_same_dir (a_src_dir .buf , a_src_dir .len ,
536
+ & dummy , & dummy ) < 1 ) {
537
+ remove_dir_recursively (& a_src_dir , 0 );
439
538
}
539
+ strbuf_reset (& a_src_dir );
440
540
}
441
541
542
+ strbuf_release (& a_src_dir );
543
+ free (src_dir );
544
+
545
+ if (dirty_paths .nr )
546
+ advise_on_moving_dirty_path (& dirty_paths );
547
+
442
548
if (gitmodules_modified )
443
549
stage_updated_gitmodules (& the_index );
444
550
@@ -447,6 +553,7 @@ int cmd_mv(int argc, const char **argv, const char *prefix)
447
553
die (_ ("Unable to write new index file" ));
448
554
449
555
string_list_clear (& src_for_dst , 0 );
556
+ string_list_clear (& dirty_paths , 0 );
450
557
UNLEAK (source );
451
558
UNLEAK (dest_path );
452
559
free (submodule_gitfile );
0 commit comments