Skip to content

Commit 2a12ac8

Browse files
committed
Use a new .gitignore parser / batch file openings
1 parent 8168208 commit 2a12ac8

26 files changed

+285
-141
lines changed

src/ConfigCat.Cli.Models/Git/GitRepositoryInfo.cs

-2
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,6 @@ namespace ConfigCat.Cli.Models.Git;
44

55
public class GitRepositoryInfo
66
{
7-
public string WorkingDirectory { get; set; }
8-
97
public string Branch { get; set; }
108

119
public string CurrentCommitHash { get; set; }

src/ConfigCat.Cli.Services/ConfigCat.Cli.Services.csproj

+5-1
Original file line numberDiff line numberDiff line change
@@ -10,8 +10,12 @@
1010
</ItemGroup>
1111

1212
<ItemGroup>
13-
<PackageReference Include="DotNet.Glob" Version="3.1.3" />
13+
<PackageReference Include="Ignore" Version="0.2.1" />
1414
<PackageReference Include="Trybot" Version="2.4.4" />
1515
</ItemGroup>
1616

17+
<ItemGroup>
18+
<InternalsVisibleTo Include="ConfigCat.Cli.Tests" />
19+
</ItemGroup>
20+
1721
</Project>

src/ConfigCat.Cli.Services/Extensions/IOExtensions.cs

+3
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,9 @@ namespace System.IO;
88
public static class FileSystemExtensions
99
{
1010
public static bool IsIgnoreFile(this FileInfo info) => IgnoreFile.IgnoreFileNames.Contains(info.Name);
11+
12+
public static bool SameDirectory(this DirectoryInfo dir1, DirectoryInfo dir2) =>
13+
Path.GetRelativePath(dir1.FullName, dir2.FullName) == ".";
1114

1215
public static async Task<bool> IsBinaryAsync(this FileInfo file, CancellationToken token)
1316
{

src/ConfigCat.Cli.Services/Extensions/SystemExtensions.cs

+1-1
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ namespace System;
99

1010
public static class SystemExtensions
1111
{
12-
public static string AsSlash(this string text) => text.Replace(Path.DirectorySeparatorChar, '/');
12+
public static string WithSlashes(this string text) => text.Replace(Path.DirectorySeparatorChar, '/');
1313

1414
public static string RemoveDashes(this string text) => text.Replace("-", string.Empty).Replace("_", string.Empty);
1515

src/ConfigCat.Cli.Services/FileSystem/FileCollector.cs

+45-18
Original file line numberDiff line numberDiff line change
@@ -10,31 +10,27 @@ namespace ConfigCat.Cli.Services.FileSystem;
1010

1111
public interface IFileCollector
1212
{
13-
Task<IEnumerable<FileInfo>> CollectAsync(DirectoryInfo rootDirectory, CancellationToken token);
13+
Task<IEnumerable<FileInfo>> CollectAsync(DirectoryInfo searchDirectory, DirectoryInfo gitRepoDir,
14+
CancellationToken token);
1415
}
1516

1617
public class FileCollector(IOutput output) : IFileCollector
1718
{
18-
public async Task<IEnumerable<FileInfo>> CollectAsync(DirectoryInfo rootDirectory, CancellationToken token)
19+
public async Task<IEnumerable<FileInfo>> CollectAsync(DirectoryInfo searchDirectory, DirectoryInfo gitRepoDir,
20+
CancellationToken token)
1921
{
2022
using var spinner = output.CreateSpinner(token);
2123

22-
var files = rootDirectory.GetFiles("*", new EnumerationOptions
23-
{
24-
RecurseSubdirectories = true,
25-
IgnoreInaccessible = true,
26-
AttributesToSkip = FileAttributes.System
27-
});
28-
var ignoreFiles = files.Where(f => f.IsIgnoreFile()).ToArray();
29-
var filesToReturn = files.Except(ignoreFiles);
30-
var ignores = ignoreFiles
31-
.Select(ignoreFile => new IgnoreFile(ignoreFile, rootDirectory))
32-
.Cast<IgnorePolicy>()
33-
.ToList();
34-
35-
ignores.Add(new GlobalIgnorePolicy(rootDirectory, "**/.git/**", "*.lock", "*lock.json", "*.graphql", "*.md", ".dockerignore"));
24+
var (ignoreFiles, filesToReturn) = GetFilesFromWorkSpace(searchDirectory, gitRepoDir);
25+
List<IgnorePolicy> policies =
26+
[
27+
new GlobalIgnorePolicy(gitRepoDir ?? searchDirectory, ".git", "*.lock", "*lock.json", "*.graphql", "*.md",
28+
".dockerignore")
29+
];
30+
policies.AddRange(ignoreFiles
31+
.Select(ignoreFile => new IgnoreFile(ignoreFile, gitRepoDir ?? searchDirectory)));
3632

37-
foreach (var ignore in ignores)
33+
foreach (var ignore in policies)
3834
{
3935
if (ignore is not IgnoreFile ignoreFile) continue;
4036
output.Verbose($"Using ignore file {ignoreFile.File.FullName}");
@@ -43,7 +39,7 @@ public async Task<IEnumerable<FileInfo>> CollectAsync(DirectoryInfo rootDirector
4339

4440
return filesToReturn.Where(f =>
4541
{
46-
foreach (var ignore in ignores.Where(i => i.Handles(f)).OrderByDescending(i => i.Rank))
42+
foreach (var ignore in policies.Where(i => i.Handles(f)).OrderByDescending(i => i.Rank))
4743
{
4844
if (ignore.IsAccepting(f))
4945
return true;
@@ -55,4 +51,35 @@ public async Task<IEnumerable<FileInfo>> CollectAsync(DirectoryInfo rootDirector
5551
return true;
5652
});
5753
}
54+
55+
internal static (IEnumerable<FileInfo> ignoreFiles, IEnumerable<FileInfo> files) GetFilesFromWorkSpace(
56+
DirectoryInfo searchDirectory, DirectoryInfo gitRepoDir)
57+
{
58+
var allFiles = EnumerateFiles(searchDirectory, true).ToArray();
59+
var ignoreFiles = allFiles.Where(f => f.IsIgnoreFile()).ToArray();
60+
var otherFiles = allFiles.Except(ignoreFiles);
61+
if (gitRepoDir == null || searchDirectory.SameDirectory(gitRepoDir))
62+
return (ignoreFiles, otherFiles);
63+
64+
// We are in a git repository's subdirectory, so we walk up to
65+
// the root folder and collect all .ignore files along the way.
66+
var currentDir = searchDirectory;
67+
do
68+
{
69+
currentDir = currentDir.Parent;
70+
ignoreFiles = ignoreFiles.Concat(EnumerateFiles(currentDir, false).Where(f => f.IsIgnoreFile())).ToArray();
71+
} while (currentDir != null && !currentDir.SameDirectory(gitRepoDir));
72+
73+
return (ignoreFiles, otherFiles);
74+
}
75+
76+
private static IEnumerable<FileInfo> EnumerateFiles(DirectoryInfo directory, bool recurse) =>
77+
directory == null
78+
? []
79+
: directory.EnumerateFiles("*", new EnumerationOptions
80+
{
81+
RecurseSubdirectories = recurse,
82+
IgnoreInaccessible = true,
83+
AttributesToSkip = FileAttributes.System
84+
});
5885
}
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
using System;
22
using System.IO;
3+
using Ignore;
34

45
namespace ConfigCat.Cli.Services.FileSystem.Ignore;
56

@@ -9,16 +10,13 @@ internal class GlobalIgnorePolicy : IgnorePolicy
910

1011
public GlobalIgnorePolicy(DirectoryInfo rootDirectory, params string[] patterns)
1112
{
12-
foreach (var pattern in patterns)
13-
base.IgnoreMatcher.Add(pattern);
14-
1513
this.rootDirectory = rootDirectory;
14+
foreach (var pattern in patterns)
15+
base.DenyRules.Add(new IgnoreRule(pattern));
1616
}
1717

18-
public override bool IsAccepting(FileInfo file) => base.IsAcceptingInternal(file.FullName.Replace(this.rootDirectory.FullName, string.Empty));
19-
20-
public override bool IsIgnoring(FileInfo file) => base.IsIgnoringInternal(file.FullName.Replace(this.rootDirectory.FullName, string.Empty));
18+
public override bool Handles(FileInfo file) => true;
2119

22-
public override bool Handles(FileInfo file) =>
23-
file.DirectoryName.AsSlash().Contains(this.rootDirectory.FullName.AsSlash().TrimEnd('/'));
20+
protected override string PreProcessFilePath(FileInfo file) =>
21+
Path.GetRelativePath(this.rootDirectory.FullName, file.FullName).WithSlashes();
2422
}

src/ConfigCat.Cli.Services/FileSystem/Ignore/IgnoreFile.cs

+9-9
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
using System.Linq;
44
using System.Threading;
55
using System.Threading.Tasks;
6+
using Ignore;
67

78
namespace ConfigCat.Cli.Services.FileSystem.Ignore;
89

@@ -24,12 +25,11 @@ public async Task LoadIgnoreFileAsync(CancellationToken token)
2425
this.ProcessPatterns(lines);
2526
}
2627

27-
public override bool IsAccepting(FileInfo file) => base.IsAcceptingInternal(file.FullName.Replace(this.File.DirectoryName, string.Empty));
28-
29-
public override bool IsIgnoring(FileInfo file) => base.IsIgnoringInternal(file.FullName.Replace(this.File.DirectoryName, string.Empty));
30-
3128
public override bool Handles(FileInfo file) =>
32-
file.DirectoryName.AsSlash().Contains(this.File.DirectoryName.AsSlash().TrimEnd('/'));
29+
file.DirectoryName.WithSlashes().Contains(this.File.DirectoryName.WithSlashes().TrimEnd('/'));
30+
31+
protected override string PreProcessFilePath(FileInfo file) =>
32+
Path.GetRelativePath(this.File.DirectoryName, file.FullName).WithSlashes();
3333

3434
private void ProcessPatterns(string[] patterns)
3535
{
@@ -41,15 +41,15 @@ private void ProcessPatterns(string[] patterns)
4141

4242
if (current.StartsWith('!'))
4343
{
44-
current = current.Substring(1);
45-
base.AcceptMatcher.Add(current);
44+
current = current[1..];
45+
base.AcceptRules.Add(new IgnoreRule(current));
4646
continue;
4747
}
4848

4949
if (pattern.StartsWith(@"\#") || pattern.StartsWith(@"\!"))
50-
current = current.Substring(1);
50+
current = current[1..];
5151

52-
base.IgnoreMatcher.Add(current);
52+
base.DenyRules.Add(new IgnoreRule(current));
5353
}
5454
}
5555
}
Original file line numberDiff line numberDiff line change
@@ -1,22 +1,23 @@
11
using System;
2+
using System.Collections.Generic;
23
using System.IO;
4+
using System.Linq;
5+
using Ignore;
36

47
namespace ConfigCat.Cli.Services.FileSystem.Ignore;
58

69
internal abstract class IgnorePolicy
710
{
8-
protected readonly IgnoreRuleSet AcceptMatcher = new();
9-
protected readonly IgnoreRuleSet IgnoreMatcher = new();
11+
protected readonly List<IgnoreRule> AcceptRules = [];
12+
protected readonly List<IgnoreRule> DenyRules = [];
1013

11-
public abstract bool IsAccepting(FileInfo file);
14+
public bool IsAccepting(FileInfo file) => AcceptRules.Any(rule => rule.IsMatch(PreProcessFilePath(file)));
1215

13-
public abstract bool IsIgnoring(FileInfo file);
16+
public bool IsIgnoring(FileInfo file) => DenyRules.Any(rule => rule.IsMatch(PreProcessFilePath(file)));
1417

1518
public abstract bool Handles(FileInfo file);
19+
20+
protected abstract string PreProcessFilePath(FileInfo file);
1621

17-
public int Rank { get; protected set; }
18-
19-
protected bool IsAcceptingInternal(string filePath) => this.AcceptMatcher.HasMatch(filePath.AsSlash().AsSpan());
20-
21-
protected bool IsIgnoringInternal(string filePath) => this.IgnoreMatcher.HasMatch(filePath.AsSlash().AsSpan());
22+
public int Rank { get; protected init; }
2223
}

src/ConfigCat.Cli.Services/FileSystem/Ignore/IgnoreRuleSet.cs

-37
This file was deleted.

0 commit comments

Comments
 (0)