@@ -1052,13 +1052,15 @@ static struct cache_entry *create_ce_entry(const struct traverse_info *info,
1052
1052
const struct name_entry * n ,
1053
1053
int stage ,
1054
1054
struct index_state * istate ,
1055
- int is_transient )
1055
+ int is_transient ,
1056
+ int is_sparse_directory )
1056
1057
{
1057
1058
size_t len = traverse_path_len (info , tree_entry_len (n ));
1059
+ size_t alloc_len = is_sparse_directory ? len + 1 : len ;
1058
1060
struct cache_entry * ce =
1059
1061
is_transient ?
1060
- make_empty_transient_cache_entry (len , NULL ) :
1061
- make_empty_cache_entry (istate , len );
1062
+ make_empty_transient_cache_entry (alloc_len , NULL ) :
1063
+ make_empty_cache_entry (istate , alloc_len );
1062
1064
1063
1065
ce -> ce_mode = create_ce_mode (n -> mode );
1064
1066
ce -> ce_flags = create_ce_flags (stage );
@@ -1067,6 +1069,13 @@ static struct cache_entry *create_ce_entry(const struct traverse_info *info,
1067
1069
/* len+1 because the cache_entry allocates space for NUL */
1068
1070
make_traverse_path (ce -> name , len + 1 , info , n -> path , n -> pathlen );
1069
1071
1072
+ if (is_sparse_directory ) {
1073
+ ce -> name [len ] = '/' ;
1074
+ ce -> name [len + 1 ] = '\0' ;
1075
+ ce -> ce_namelen ++ ;
1076
+ ce -> ce_flags |= CE_SKIP_WORKTREE ;
1077
+ }
1078
+
1070
1079
return ce ;
1071
1080
}
1072
1081
@@ -1085,10 +1094,17 @@ static int unpack_single_entry(int n, unsigned long mask,
1085
1094
struct unpack_trees_options * o = info -> data ;
1086
1095
unsigned long conflicts = info -> df_conflicts | dirmask ;
1087
1096
1088
- /* Do we have *only* directories? Nothing to do */
1089
1097
if (mask == dirmask && !src [0 ])
1090
1098
return 0 ;
1091
1099
1100
+ /*
1101
+ * When we have a sparse directory entry for src[0],
1102
+ * then this isn't necessarily a directory-file conflict.
1103
+ */
1104
+ if (mask == dirmask && src [0 ] &&
1105
+ S_ISSPARSEDIR (src [0 ]-> ce_mode ))
1106
+ conflicts = 0 ;
1107
+
1092
1108
/*
1093
1109
* Ok, we've filled in up to any potential index entry in src[0],
1094
1110
* now do the rest.
@@ -1118,7 +1134,9 @@ static int unpack_single_entry(int n, unsigned long mask,
1118
1134
* not stored in the index. otherwise construct the
1119
1135
* cache entry from the index aware logic.
1120
1136
*/
1121
- src [i + o -> merge ] = create_ce_entry (info , names + i , stage , & o -> result , o -> merge );
1137
+ src [i + o -> merge ] = create_ce_entry (info , names + i , stage ,
1138
+ & o -> result , o -> merge ,
1139
+ bit & dirmask );
1122
1140
}
1123
1141
1124
1142
if (o -> merge ) {
@@ -1222,16 +1240,71 @@ static int find_cache_pos(struct traverse_info *info,
1222
1240
return -1 ;
1223
1241
}
1224
1242
1243
+ /*
1244
+ * Given a sparse directory entry 'ce', compare ce->name to
1245
+ * info->name + '/' + p->path + '/' if info->name is non-empty.
1246
+ * Compare ce->name to p->path + '/' otherwise. Note that
1247
+ * ce->name must end in a trailing '/' because it is a sparse
1248
+ * directory entry.
1249
+ */
1250
+ static int sparse_dir_matches_path (const struct cache_entry * ce ,
1251
+ struct traverse_info * info ,
1252
+ const struct name_entry * p )
1253
+ {
1254
+ assert (S_ISSPARSEDIR (ce -> ce_mode ));
1255
+ assert (ce -> name [ce -> ce_namelen - 1 ] == '/' );
1256
+
1257
+ if (info -> namelen )
1258
+ return ce -> ce_namelen == info -> namelen + p -> pathlen + 2 &&
1259
+ ce -> name [info -> namelen ] == '/' &&
1260
+ !strncmp (ce -> name , info -> name , info -> namelen ) &&
1261
+ !strncmp (ce -> name + info -> namelen + 1 , p -> path , p -> pathlen );
1262
+ return ce -> ce_namelen == p -> pathlen + 1 &&
1263
+ !strncmp (ce -> name , p -> path , p -> pathlen );
1264
+ }
1265
+
1225
1266
static struct cache_entry * find_cache_entry (struct traverse_info * info ,
1226
1267
const struct name_entry * p )
1227
1268
{
1269
+ struct cache_entry * ce ;
1228
1270
int pos = find_cache_pos (info , p -> path , p -> pathlen );
1229
1271
struct unpack_trees_options * o = info -> data ;
1230
1272
1231
1273
if (0 <= pos )
1232
1274
return o -> src_index -> cache [pos ];
1233
- else
1275
+
1276
+ /*
1277
+ * Check for a sparse-directory entry named "path/".
1278
+ * Due to the input p->path not having a trailing
1279
+ * slash, the negative 'pos' value overshoots the
1280
+ * expected position, hence "-2" instead of "-1".
1281
+ */
1282
+ pos = - pos - 2 ;
1283
+
1284
+ if (pos < 0 || pos >= o -> src_index -> cache_nr )
1234
1285
return NULL ;
1286
+
1287
+ /*
1288
+ * Due to lexicographic sorting and sparse directory
1289
+ * entries ending with a trailing slash, our path as a
1290
+ * sparse directory (e.g "subdir/") and our path as a
1291
+ * file (e.g. "subdir") might be separated by other
1292
+ * paths (e.g. "subdir-").
1293
+ */
1294
+ while (pos >= 0 ) {
1295
+ ce = o -> src_index -> cache [pos ];
1296
+
1297
+ if (strncmp (ce -> name , p -> path , p -> pathlen ))
1298
+ return NULL ;
1299
+
1300
+ if (S_ISSPARSEDIR (ce -> ce_mode ) &&
1301
+ sparse_dir_matches_path (ce , info , p ))
1302
+ return ce ;
1303
+
1304
+ pos -- ;
1305
+ }
1306
+
1307
+ return NULL ;
1235
1308
}
1236
1309
1237
1310
static void debug_path (struct traverse_info * info )
@@ -1266,6 +1339,21 @@ static void debug_unpack_callback(int n,
1266
1339
debug_name_entry (i , names + i );
1267
1340
}
1268
1341
1342
+ /*
1343
+ * Returns true if and only if the given cache_entry is a
1344
+ * sparse-directory entry that matches the given name_entry
1345
+ * from the tree walk at the given traverse_info.
1346
+ */
1347
+ static int is_sparse_directory_entry (struct cache_entry * ce ,
1348
+ struct name_entry * name ,
1349
+ struct traverse_info * info )
1350
+ {
1351
+ if (!ce || !name || !S_ISSPARSEDIR (ce -> ce_mode ))
1352
+ return 0 ;
1353
+
1354
+ return sparse_dir_matches_path (ce , info , name );
1355
+ }
1356
+
1269
1357
/*
1270
1358
* Note that traverse_by_cache_tree() duplicates some logic in this function
1271
1359
* without actually calling it. If you change the logic here you may need to
@@ -1352,9 +1440,12 @@ static int unpack_callback(int n, unsigned long mask, unsigned long dirmask, str
1352
1440
}
1353
1441
}
1354
1442
1355
- if (traverse_trees_recursive (n , dirmask , mask & ~dirmask ,
1356
- names , info ) < 0 )
1443
+ if (!is_sparse_directory_entry (src [0 ], names , info ) &&
1444
+ traverse_trees_recursive (n , dirmask , mask & ~dirmask ,
1445
+ names , info ) < 0 ) {
1357
1446
return -1 ;
1447
+ }
1448
+
1358
1449
return mask ;
1359
1450
}
1360
1451
0 commit comments