protected override (bool isSuccess, SError error) HandleProject(Config config, string repoPath, string projectPath) { // TODO: setting to warn of duplicates when treating git files in a case-insensitive manner . . . // Because `thing.txt` and `Thing.txt` could theoretically both exist . . . HashSet <string> filesTrackedByGit = GitHelper.GetFilesFromGitForProject(repoPath, projectPath); HashSet <string> filesIncludedInProjectFile = ProjectFileHelper.GetFilesFromProjectFile(projectPath, out HashSet <string> duplicates); string projectName = ProjectFileHelper.GetProjectFileName(projectPath); // Filter out project files, because project files do not include themselves . . . var self = FileHelper.NormalizePath(Path.GetRelativePath(repoPath, projectPath)); filesTrackedByGit.Remove(self); // Remove any other files our config says we can ignore before we compare . . . filesTrackedByGit.RemoveWhere(config.IsIgnorable); // Project files are case-insensitive . . . // But git is case-sensitive . . . var filesNotIncludedInProjectFile = filesTrackedByGit.Where(m => !filesIncludedInProjectFile.Contains(m, StringComparer.CurrentCultureIgnoreCase)).ToList(); if (filesNotIncludedInProjectFile?.Any() == true) { _errorOccurred = true; Log($"{projectName}:"); foreach (var file in filesNotIncludedInProjectFile) { LogError($" ({Ordinal}) \t{file}", includePrefix: false); Ordinal++; } NL(); } else { Log($"'{projectName}' is not missing any files."); NL(); } if (ShouldCheckForDuplicates) { if (duplicates?.Any() == true) { Log($"{projectName}:"); foreach (var duplicate in duplicates) { LogError($" ({Ordinal}) \t DUPLICATE {duplicate}", includePrefix: false); Ordinal++; } } } return(Success); }