Skip to content

Commit bc7e691

Browse files
Copilotkartheekp-ms
andcommitted
Optimize ContentFileUtils to avoid expensive file globbing when patterns don't contain wildcards
Co-authored-by: kartheekp-ms <[email protected]>
1 parent fa8bd94 commit bc7e691

1 file changed

Lines changed: 41 additions & 15 deletions

File tree

src/NuGet.Core/NuGet.Commands/RestoreCommand/ContentFiles/ContentFileUtils.cs

Lines changed: 41 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,14 @@ internal static class ContentFileUtils
2121
{
2222
private const string ContentFilesFolderName = "contentFiles/";
2323

24+
/// <summary>
25+
/// Checks if a pattern contains glob wildcards (* or ?)
26+
/// </summary>
27+
private static bool ContainsGlobWildcards(string pattern)
28+
{
29+
return pattern.IndexOfAny(new[] { '*', '?' }) != -1;
30+
}
31+
2432
/// <summary>
2533
/// Get all content groups that have the nearest TxM
2634
/// </summary>
@@ -107,13 +115,21 @@ internal static List<LockFileContentFile> GetContentFileGroup(
107115
// this is validated in the nuspec reader
108116
Debug.Assert(filesEntry.Include != null, "invalid contentFiles entry");
109117

110-
// Create a filesystem matcher for globbing patterns
111-
var matcher = new Matcher(StringComparison.OrdinalIgnoreCase);
112-
matcher.AddInclude(filesEntry.Include);
118+
// Optimize for the common case: exact file path without wildcards
119+
// When there are no wildcards and no exclude, use simple string comparison
120+
bool useSimpleMatch = !ContainsGlobWildcards(filesEntry.Include) && filesEntry.Exclude == null;
113121

114-
if (filesEntry.Exclude != null)
122+
Matcher? matcher = null;
123+
if (!useSimpleMatch)
115124
{
116-
matcher.AddExclude(filesEntry.Exclude);
125+
// Create a filesystem matcher for globbing patterns
126+
matcher = new Matcher(StringComparison.OrdinalIgnoreCase);
127+
matcher.AddInclude(filesEntry.Include);
128+
129+
if (filesEntry.Exclude != null)
130+
{
131+
matcher.AddExclude(filesEntry.Exclude);
132+
}
117133
}
118134

119135
// Check each file against the patterns
@@ -128,17 +144,27 @@ internal static List<LockFileContentFile> GetContentFileGroup(
128144
{
129145
var relativePath = file.Substring(rootFolderPathLength, file.Length - rootFolderPathLength);
130146

131-
// Check if the nuspec group include/exclude patterns apply to the file
132-
var globbingDirectory = new FileProviderGlobbingDirectory(
133-
fileProvider: new SingleFileProvider(relativePath),
134-
fileInfo: rootDirectory,
135-
parent: null);
136-
137-
// Currently Matcher only returns the file name not the full path, each file must be
138-
// check individually.
139-
var matchResults = matcher.Execute(globbingDirectory);
147+
bool isMatch;
148+
if (useSimpleMatch)
149+
{
150+
// Fast path: simple case-insensitive string comparison
151+
isMatch = string.Equals(relativePath, filesEntry.Include, StringComparison.OrdinalIgnoreCase);
152+
}
153+
else
154+
{
155+
// Check if the nuspec group include/exclude patterns apply to the file
156+
var globbingDirectory = new FileProviderGlobbingDirectory(
157+
fileProvider: new SingleFileProvider(relativePath),
158+
fileInfo: rootDirectory,
159+
parent: null);
160+
161+
// Currently Matcher only returns the file name not the full path, each file must be
162+
// check individually.
163+
var matchResults = matcher!.Execute(globbingDirectory);
164+
isMatch = matchResults.HasMatches;
165+
}
140166

141-
if (matchResults.HasMatches)
167+
if (isMatch)
142168
{
143169
entries.Add(filesEntry);
144170
}

0 commit comments

Comments
 (0)