@@ -371,12 +371,19 @@ static int match_pathspec_item(const struct index_state *istate,
371
371
!ps_strncmp (item , match , name , namelen ))
372
372
return MATCHED_RECURSIVELY_LEADING_PATHSPEC ;
373
373
374
- /* name" doesn't match up to the first wild character */
374
+ /* name doesn't match up to the first wild character */
375
375
if (item -> nowildcard_len < item -> len &&
376
376
ps_strncmp (item , match , name ,
377
377
item -> nowildcard_len - prefix ))
378
378
return 0 ;
379
379
380
+ /*
381
+ * name has no wildcard, and it didn't match as a leading
382
+ * pathspec so return.
383
+ */
384
+ if (item -> nowildcard_len == item -> len )
385
+ return 0 ;
386
+
380
387
/*
381
388
* Here is where we would perform a wildmatch to check if
382
389
* "name" can be matched as a directory (or a prefix) against
@@ -1652,6 +1659,8 @@ static enum path_treatment treat_directory(struct dir_struct *dir,
1652
1659
const char * dirname , int len , int baselen , int exclude ,
1653
1660
const struct pathspec * pathspec )
1654
1661
{
1662
+ int nested_repo = 0 ;
1663
+
1655
1664
/* The "len-1" is to strip the final '/' */
1656
1665
switch (directory_exists_in_index (istate , dirname , len - 1 )) {
1657
1666
case index_directory :
@@ -1661,15 +1670,16 @@ static enum path_treatment treat_directory(struct dir_struct *dir,
1661
1670
return path_none ;
1662
1671
1663
1672
case index_nonexistent :
1664
- if (dir -> flags & DIR_SKIP_NESTED_GIT ) {
1665
- int nested_repo ;
1673
+ if (( dir -> flags & DIR_SKIP_NESTED_GIT ) ||
1674
+ !( dir -> flags & DIR_NO_GITLINKS )) {
1666
1675
struct strbuf sb = STRBUF_INIT ;
1667
1676
strbuf_addstr (& sb , dirname );
1668
1677
nested_repo = is_nonbare_repository_dir (& sb );
1669
1678
strbuf_release (& sb );
1670
- if (nested_repo )
1671
- return path_none ;
1672
1679
}
1680
+ if (nested_repo )
1681
+ return ((dir -> flags & DIR_SKIP_NESTED_GIT ) ? path_none :
1682
+ (exclude ? path_excluded : path_untracked ));
1673
1683
1674
1684
if (dir -> flags & DIR_SHOW_OTHER_DIRECTORIES )
1675
1685
break ;
@@ -1697,13 +1707,6 @@ static enum path_treatment treat_directory(struct dir_struct *dir,
1697
1707
1698
1708
return path_none ;
1699
1709
}
1700
- if (!(dir -> flags & DIR_NO_GITLINKS )) {
1701
- struct strbuf sb = STRBUF_INIT ;
1702
- strbuf_addstr (& sb , dirname );
1703
- if (is_nonbare_repository_dir (& sb ))
1704
- return exclude ? path_excluded : path_untracked ;
1705
- strbuf_release (& sb );
1706
- }
1707
1710
return path_recurse ;
1708
1711
}
1709
1712
@@ -2123,6 +2126,40 @@ static void close_cached_dir(struct cached_dir *cdir)
2123
2126
}
2124
2127
}
2125
2128
2129
+ static void add_path_to_appropriate_result_list (struct dir_struct * dir ,
2130
+ struct untracked_cache_dir * untracked ,
2131
+ struct cached_dir * cdir ,
2132
+ struct index_state * istate ,
2133
+ struct strbuf * path ,
2134
+ int baselen ,
2135
+ const struct pathspec * pathspec ,
2136
+ enum path_treatment state )
2137
+ {
2138
+ /* add the path to the appropriate result list */
2139
+ switch (state ) {
2140
+ case path_excluded :
2141
+ if (dir -> flags & DIR_SHOW_IGNORED )
2142
+ dir_add_name (dir , istate , path -> buf , path -> len );
2143
+ else if ((dir -> flags & DIR_SHOW_IGNORED_TOO ) ||
2144
+ ((dir -> flags & DIR_COLLECT_IGNORED ) &&
2145
+ exclude_matches_pathspec (path -> buf , path -> len ,
2146
+ pathspec )))
2147
+ dir_add_ignored (dir , istate , path -> buf , path -> len );
2148
+ break ;
2149
+
2150
+ case path_untracked :
2151
+ if (dir -> flags & DIR_SHOW_IGNORED )
2152
+ break ;
2153
+ dir_add_name (dir , istate , path -> buf , path -> len );
2154
+ if (cdir -> fdir )
2155
+ add_untracked (untracked , path -> buf + baselen );
2156
+ break ;
2157
+
2158
+ default :
2159
+ break ;
2160
+ }
2161
+ }
2162
+
2126
2163
/*
2127
2164
* Read a directory tree. We currently ignore anything but
2128
2165
* directories, regular files and symlinks. That's because git
@@ -2147,6 +2184,15 @@ static enum path_treatment read_directory_recursive(struct dir_struct *dir,
2147
2184
struct untracked_cache_dir * untracked , int check_only ,
2148
2185
int stop_at_first_file , const struct pathspec * pathspec )
2149
2186
{
2187
+ /*
2188
+ * WARNING WARNING WARNING:
2189
+ *
2190
+ * Any updates to the traversal logic here may need corresponding
2191
+ * updates in treat_leading_path(). See the commit message for the
2192
+ * commit adding this warning as well as the commit preceding it
2193
+ * for details.
2194
+ */
2195
+
2150
2196
struct cached_dir cdir ;
2151
2197
enum path_treatment state , subdir_state , dir_state = path_none ;
2152
2198
struct strbuf path = STRBUF_INIT ;
@@ -2226,29 +2272,9 @@ static enum path_treatment read_directory_recursive(struct dir_struct *dir,
2226
2272
continue ;
2227
2273
}
2228
2274
2229
- /* add the path to the appropriate result list */
2230
- switch (state ) {
2231
- case path_excluded :
2232
- if (dir -> flags & DIR_SHOW_IGNORED )
2233
- dir_add_name (dir , istate , path .buf , path .len );
2234
- else if ((dir -> flags & DIR_SHOW_IGNORED_TOO ) ||
2235
- ((dir -> flags & DIR_COLLECT_IGNORED ) &&
2236
- exclude_matches_pathspec (path .buf , path .len ,
2237
- pathspec )))
2238
- dir_add_ignored (dir , istate , path .buf , path .len );
2239
- break ;
2240
-
2241
- case path_untracked :
2242
- if (dir -> flags & DIR_SHOW_IGNORED )
2243
- break ;
2244
- dir_add_name (dir , istate , path .buf , path .len );
2245
- if (cdir .fdir )
2246
- add_untracked (untracked , path .buf + baselen );
2247
- break ;
2248
-
2249
- default :
2250
- break ;
2251
- }
2275
+ add_path_to_appropriate_result_list (dir , untracked , & cdir ,
2276
+ istate , & path , baselen ,
2277
+ pathspec , state );
2252
2278
}
2253
2279
close_cached_dir (& cdir );
2254
2280
out :
@@ -2278,41 +2304,104 @@ static int treat_leading_path(struct dir_struct *dir,
2278
2304
const char * path , int len ,
2279
2305
const struct pathspec * pathspec )
2280
2306
{
2307
+ /*
2308
+ * WARNING WARNING WARNING:
2309
+ *
2310
+ * Any updates to the traversal logic here may need corresponding
2311
+ * updates in treat_leading_path(). See the commit message for the
2312
+ * commit adding this warning as well as the commit preceding it
2313
+ * for details.
2314
+ */
2315
+
2281
2316
struct strbuf sb = STRBUF_INIT ;
2282
- int baselen , rc = 0 ;
2317
+ int prevlen , baselen ;
2283
2318
const char * cp ;
2284
- int old_flags = dir -> flags ;
2319
+ struct cached_dir cdir ;
2320
+ struct dirent * de ;
2321
+ enum path_treatment state = path_none ;
2322
+
2323
+ /*
2324
+ * For each directory component of path, we are going to check whether
2325
+ * that path is relevant given the pathspec. For example, if path is
2326
+ * foo/bar/baz/
2327
+ * then we will ask treat_path() whether we should go into foo, then
2328
+ * whether we should go into bar, then whether baz is relevant.
2329
+ * Checking each is important because e.g. if path is
2330
+ * .git/info/
2331
+ * then we need to check .git to know we shouldn't traverse it.
2332
+ * If the return from treat_path() is:
2333
+ * * path_none, for any path, we return false.
2334
+ * * path_recurse, for all path components, we return true
2335
+ * * <anything else> for some intermediate component, we make sure
2336
+ * to add that path to the relevant list but return false
2337
+ * signifying that we shouldn't recurse into it.
2338
+ */
2285
2339
2286
2340
while (len && path [len - 1 ] == '/' )
2287
2341
len -- ;
2288
2342
if (!len )
2289
2343
return 1 ;
2344
+
2345
+ /*
2346
+ * We need a manufactured dirent with sufficient space to store a
2347
+ * leading directory component of path in its d_name. Here, we
2348
+ * assume that the dirent's d_name is either declared as
2349
+ * char d_name[BIG_ENOUGH]
2350
+ * or that it is declared at the end of the struct as
2351
+ * char d_name[]
2352
+ * For either case, padding with len+1 bytes at the end will ensure
2353
+ * sufficient storage space.
2354
+ */
2355
+ de = xcalloc (1 , st_add3 (sizeof (struct dirent ), len , 1 ));
2356
+ memset (& cdir , 0 , sizeof (cdir ));
2357
+ cdir .de = de ;
2358
+ #if defined(DT_UNKNOWN ) && !defined(NO_D_TYPE_IN_DIRENT )
2359
+ de -> d_type = DT_DIR ;
2360
+ #endif
2290
2361
baselen = 0 ;
2291
- dir -> flags &= ~ DIR_SHOW_OTHER_DIRECTORIES ;
2362
+ prevlen = 0 ;
2292
2363
while (1 ) {
2293
- cp = path + baselen + !!baselen ;
2364
+ prevlen = baselen + !!baselen ;
2365
+ cp = path + prevlen ;
2294
2366
cp = memchr (cp , '/' , path + len - cp );
2295
2367
if (!cp )
2296
2368
baselen = len ;
2297
2369
else
2298
2370
baselen = cp - path ;
2299
- strbuf_setlen (& sb , 0 );
2371
+ strbuf_reset (& sb );
2300
2372
strbuf_add (& sb , path , baselen );
2301
2373
if (!is_directory (sb .buf ))
2302
2374
break ;
2303
- if (simplify_away (sb .buf , sb .len , pathspec ))
2304
- break ;
2305
- if (treat_one_path (dir , NULL , istate , & sb , baselen , pathspec ,
2306
- DT_DIR , NULL ) == path_none )
2375
+ strbuf_reset (& sb );
2376
+ strbuf_add (& sb , path , prevlen );
2377
+ memcpy (de -> d_name , path + prevlen , baselen - prevlen );
2378
+ de -> d_name [baselen - prevlen ] = '\0' ;
2379
+ state = treat_path (dir , NULL , & cdir , istate , & sb , prevlen ,
2380
+ pathspec );
2381
+ if (state == path_untracked &&
2382
+ get_dtype (cdir .de , istate , sb .buf , sb .len ) == DT_DIR &&
2383
+ (dir -> flags & DIR_SHOW_IGNORED_TOO ||
2384
+ do_match_pathspec (istate , pathspec , sb .buf , sb .len ,
2385
+ baselen , NULL , DO_MATCH_LEADING_PATHSPEC ) == MATCHED_RECURSIVELY_LEADING_PATHSPEC )) {
2386
+ add_path_to_appropriate_result_list (dir , NULL , & cdir ,
2387
+ istate ,
2388
+ & sb , baselen ,
2389
+ pathspec , state );
2390
+ state = path_recurse ;
2391
+ }
2392
+
2393
+ if (state != path_recurse )
2307
2394
break ; /* do not recurse into it */
2308
- if (len <= baselen ) {
2309
- rc = 1 ;
2395
+ if (len <= baselen )
2310
2396
break ; /* finished checking */
2311
- }
2312
2397
}
2398
+ add_path_to_appropriate_result_list (dir , NULL , & cdir , istate ,
2399
+ & sb , baselen , pathspec ,
2400
+ state );
2401
+
2402
+ free (de );
2313
2403
strbuf_release (& sb );
2314
- dir -> flags = old_flags ;
2315
- return rc ;
2404
+ return state == path_recurse ;
2316
2405
}
2317
2406
2318
2407
static const char * get_ident_string (void )
0 commit comments