@@ -1069,6 +1069,67 @@ static struct cache_entry *create_ce_entry(const struct traverse_info *info,
10691069 return ce ;
10701070}
10711071
1072+ /*
1073+ * Determine whether the path specified by 'p' should be unpacked as a new
1074+ * sparse directory in a sparse index. A new sparse directory 'A/':
1075+ * - must be outside the sparse cone.
1076+ * - must not already be in the index (i.e., no index entry with name 'A/'
1077+ * exists).
1078+ * - must not have any child entries in the index (i.e., no index entry
1079+ * 'A/<something>' exists).
1080+ * If 'p' meets the above requirements, return 1; otherwise, return 0.
1081+ */
1082+ static int entry_is_new_sparse_dir (const struct traverse_info * info ,
1083+ const struct name_entry * p )
1084+ {
1085+ int res , pos ;
1086+ struct strbuf dirpath = STRBUF_INIT ;
1087+ struct unpack_trees_options * o = info -> data ;
1088+
1089+ if (!S_ISDIR (p -> mode ))
1090+ return 0 ;
1091+
1092+ /*
1093+ * If the path is inside the sparse cone, it can't be a sparse directory.
1094+ */
1095+ strbuf_add (& dirpath , info -> traverse_path , info -> pathlen );
1096+ strbuf_add (& dirpath , p -> path , p -> pathlen );
1097+ strbuf_addch (& dirpath , '/' );
1098+ if (path_in_cone_mode_sparse_checkout (dirpath .buf , o -> src_index )) {
1099+ res = 0 ;
1100+ goto cleanup ;
1101+ }
1102+
1103+ pos = index_name_pos_sparse (o -> src_index , dirpath .buf , dirpath .len );
1104+ if (pos >= 0 ) {
1105+ /* Path is already in the index, not a new sparse dir */
1106+ res = 0 ;
1107+ goto cleanup ;
1108+ }
1109+
1110+ /* Where would this sparse dir be inserted into the index? */
1111+ pos = - pos - 1 ;
1112+ if (pos >= o -> src_index -> cache_nr ) {
1113+ /*
1114+ * Sparse dir would be inserted at the end of the index, so we
1115+ * know it has no child entries.
1116+ */
1117+ res = 1 ;
1118+ goto cleanup ;
1119+ }
1120+
1121+ /*
1122+ * If the dir has child entries in the index, the first would be at the
1123+ * position the sparse directory would be inserted. If the entry at this
1124+ * position is inside the dir, not a new sparse dir.
1125+ */
1126+ res = strncmp (o -> src_index -> cache [pos ]-> name , dirpath .buf , dirpath .len );
1127+
1128+ cleanup :
1129+ strbuf_release (& dirpath );
1130+ return res ;
1131+ }
1132+
10721133/*
10731134 * Note that traverse_by_cache_tree() duplicates some logic in this function
10741135 * without actually calling it. If you change the logic here you may need to
@@ -1078,21 +1139,44 @@ static int unpack_single_entry(int n, unsigned long mask,
10781139 unsigned long dirmask ,
10791140 struct cache_entry * * src ,
10801141 const struct name_entry * names ,
1081- const struct traverse_info * info )
1142+ const struct traverse_info * info ,
1143+ int * is_new_sparse_dir )
10821144{
10831145 int i ;
10841146 struct unpack_trees_options * o = info -> data ;
10851147 unsigned long conflicts = info -> df_conflicts | dirmask ;
1148+ const struct name_entry * p = names ;
10861149
1087- if (mask == dirmask && !src [0 ])
1088- return 0 ;
1150+ * is_new_sparse_dir = 0 ;
1151+ if (mask == dirmask && !src [0 ]) {
1152+ /*
1153+ * If we're not in a sparse index, we can't unpack a directory
1154+ * without recursing into it, so we return.
1155+ */
1156+ if (!o -> src_index -> sparse_index )
1157+ return 0 ;
1158+
1159+ /* Find first entry with a real name (we could use "mask" too) */
1160+ while (!p -> mode )
1161+ p ++ ;
1162+
1163+ /*
1164+ * If the directory is completely missing from the index but
1165+ * would otherwise be a sparse directory, we should unpack it.
1166+ * If not, we'll return and continue recursively traversing the
1167+ * tree.
1168+ */
1169+ * is_new_sparse_dir = entry_is_new_sparse_dir (info , p );
1170+ if (!* is_new_sparse_dir )
1171+ return 0 ;
1172+ }
10891173
10901174 /*
1091- * When we have a sparse directory entry for src[0],
1092- * then this isn't necessarily a directory-file conflict.
1175+ * When we are unpacking a sparse directory, then this isn't necessarily
1176+ * a directory-file conflict.
10931177 */
1094- if (mask == dirmask && src [ 0 ] &&
1095- S_ISSPARSEDIR (src [0 ]-> ce_mode ))
1178+ if (mask == dirmask &&
1179+ ( * is_new_sparse_dir || ( src [ 0 ] && S_ISSPARSEDIR (src [0 ]-> ce_mode )) ))
10961180 conflicts = 0 ;
10971181
10981182 /*
@@ -1352,7 +1436,7 @@ static int unpack_sparse_callback(int n, unsigned long mask, unsigned long dirma
13521436{
13531437 struct cache_entry * src [MAX_UNPACK_TREES + 1 ] = { NULL , };
13541438 struct unpack_trees_options * o = info -> data ;
1355- int ret ;
1439+ int ret , is_new_sparse_dir ;
13561440
13571441 assert (o -> merge );
13581442
@@ -1376,7 +1460,7 @@ static int unpack_sparse_callback(int n, unsigned long mask, unsigned long dirma
13761460 * "index" tree (i.e., names[0]) and adjust 'names', 'n', 'mask', and
13771461 * 'dirmask' accordingly.
13781462 */
1379- ret = unpack_single_entry (n - 1 , mask >> 1 , dirmask >> 1 , src , names + 1 , info );
1463+ ret = unpack_single_entry (n - 1 , mask >> 1 , dirmask >> 1 , src , names + 1 , info , & is_new_sparse_dir );
13801464
13811465 if (src [0 ])
13821466 discard_cache_entry (src [0 ]);
@@ -1394,6 +1478,7 @@ static int unpack_callback(int n, unsigned long mask, unsigned long dirmask, str
13941478 struct cache_entry * src [MAX_UNPACK_TREES + 1 ] = { NULL , };
13951479 struct unpack_trees_options * o = info -> data ;
13961480 const struct name_entry * p = names ;
1481+ int is_new_sparse_dir ;
13971482
13981483 /* Find first entry with a real name (we could use "mask" too) */
13991484 while (!p -> mode )
@@ -1440,7 +1525,7 @@ static int unpack_callback(int n, unsigned long mask, unsigned long dirmask, str
14401525 }
14411526 }
14421527
1443- if (unpack_single_entry (n , mask , dirmask , src , names , info ) < 0 )
1528+ if (unpack_single_entry (n , mask , dirmask , src , names , info , & is_new_sparse_dir ) )
14441529 return -1 ;
14451530
14461531 if (o -> merge && src [0 ]) {
@@ -1478,6 +1563,7 @@ static int unpack_callback(int n, unsigned long mask, unsigned long dirmask, str
14781563 }
14791564
14801565 if (!is_sparse_directory_entry (src [0 ], names , info ) &&
1566+ !is_new_sparse_dir &&
14811567 traverse_trees_recursive (n , dirmask , mask & ~dirmask ,
14821568 names , info ) < 0 ) {
14831569 return -1 ;
0 commit comments