Skip to content

Commit f652672

Browse files
derrickstoleegitster
authored andcommitted
dir: select directories correctly
When matching a path against a list of patterns, the ones that require a directory match previously did not work when a filename is specified. This was fine when all pattern-matching was done within methods such as unpack_trees() that check a directory before recursing into the contained files. However, other commands will start matching individual files against pattern lists without that recursive approach. The last_matching_pattern_from_list() logic performs some checks on the filetype of a path within the index when the PATTERN_FLAG_MUSTBEDIR flag is set. This works great when setting SKIP_WORKTREE bits within unpack_trees(), but doesn't work well when passing an arbitrary path such as a file within a matching directory. We extract the logic around determining the file type, but attempt to avoid checking the filesystem if the parent directory already matches the sparse-checkout patterns. The new path_matches_dir_pattern() method includes a 'path_parent' parameter that is used to store the parent directory of 'pathname' between multiple pattern matching tests. This is loaded lazily, only on the first pattern it finds that has the PATTERN_FLAG_MUSTBEDIR flag. If we find that a path has a parent directory, we start by checking to see if that parent directory matches the pattern. If so, then we do not need to query the index for the type (which can be expensive). If we find that the parent does not match, then we still must check the type from the index for the given pathname. Note that this does not affect cone mode pattern matching, but instead the more general -- and slower -- full pattern set. Thus, this does not affect the sparse index. Helped-by: Ævar Arnfjörð Bjarmason <[email protected]> Signed-off-by: Derrick Stolee <[email protected]> Signed-off-by: Junio C Hamano <[email protected]>
1 parent edd2cd3 commit f652672

File tree

1 file changed

+49
-5
lines changed

1 file changed

+49
-5
lines changed

dir.c

Lines changed: 49 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1303,6 +1303,44 @@ int match_pathname(const char *pathname, int pathlen,
13031303
WM_PATHNAME) == 0;
13041304
}
13051305

1306+
static int path_matches_dir_pattern(const char *pathname,
1307+
int pathlen,
1308+
struct strbuf **path_parent,
1309+
int *dtype,
1310+
struct path_pattern *pattern,
1311+
struct index_state *istate)
1312+
{
1313+
if (!*path_parent) {
1314+
char *slash;
1315+
CALLOC_ARRAY(*path_parent, 1);
1316+
strbuf_add(*path_parent, pathname, pathlen);
1317+
slash = find_last_dir_sep((*path_parent)->buf);
1318+
1319+
if (slash)
1320+
strbuf_setlen(*path_parent, slash - (*path_parent)->buf);
1321+
else
1322+
strbuf_setlen(*path_parent, 0);
1323+
}
1324+
1325+
/*
1326+
* If the parent directory matches the pattern, then we do not
1327+
* need to check for dtype.
1328+
*/
1329+
if ((*path_parent)->len &&
1330+
match_pathname((*path_parent)->buf, (*path_parent)->len,
1331+
pattern->base,
1332+
pattern->baselen ? pattern->baselen - 1 : 0,
1333+
pattern->pattern, pattern->nowildcardlen,
1334+
pattern->patternlen, pattern->flags))
1335+
return 1;
1336+
1337+
*dtype = resolve_dtype(*dtype, istate, pathname, pathlen);
1338+
if (*dtype != DT_DIR)
1339+
return 0;
1340+
1341+
return 1;
1342+
}
1343+
13061344
/*
13071345
* Scan the given exclude list in reverse to see whether pathname
13081346
* should be ignored. The first match (i.e. the last on the list), if
@@ -1318,6 +1356,7 @@ static struct path_pattern *last_matching_pattern_from_list(const char *pathname
13181356
{
13191357
struct path_pattern *res = NULL; /* undecided */
13201358
int i;
1359+
struct strbuf *path_parent = NULL;
13211360

13221361
if (!pl->nr)
13231362
return NULL; /* undefined */
@@ -1327,11 +1366,10 @@ static struct path_pattern *last_matching_pattern_from_list(const char *pathname
13271366
const char *exclude = pattern->pattern;
13281367
int prefix = pattern->nowildcardlen;
13291368

1330-
if (pattern->flags & PATTERN_FLAG_MUSTBEDIR) {
1331-
*dtype = resolve_dtype(*dtype, istate, pathname, pathlen);
1332-
if (*dtype != DT_DIR)
1333-
continue;
1334-
}
1369+
if (pattern->flags & PATTERN_FLAG_MUSTBEDIR &&
1370+
!path_matches_dir_pattern(pathname, pathlen, &path_parent,
1371+
dtype, pattern, istate))
1372+
continue;
13351373

13361374
if (pattern->flags & PATTERN_FLAG_NODIR) {
13371375
if (match_basename(basename,
@@ -1355,6 +1393,12 @@ static struct path_pattern *last_matching_pattern_from_list(const char *pathname
13551393
break;
13561394
}
13571395
}
1396+
1397+
if (path_parent) {
1398+
strbuf_release(path_parent);
1399+
free(path_parent);
1400+
}
1401+
13581402
return res;
13591403
}
13601404

0 commit comments

Comments
 (0)