Problem Summary
When a .csharpierignore file exists in a subdirectory, it completely overrides the patterns defined in the root .csharpierignore for all files within that subdirectory.
This behavior breaks common monorepo and multi-project workflows where you want:
- To exclude a subproject directory when formatting from the repository root.
- To include files when formatting from within that subproject directory.
Current Behavior
The current implementation in IgnoreFile.cs
(lines 112–118 @ commit 019c82e) searches for .csharpierignore files ascending from the base directory and uses only the first one found:
if (foundCSharpierIgnoreFilePath is null)
{
var csharpierIgnoreFilePath = fileSystem.Path.Combine(
directoryInfo.FullName,
".csharpierignore"
);
if (fileSystem.File.Exists(csharpierIgnoreFilePath))
{
foundCSharpierIgnoreFilePath = csharpierIgnoreFilePath;
}
}
This means that once a .csharpierignore is found in a subdirectory, all parent ignore rules are ignored for files under that directory.
Example Scenario
Project structure
/project-root/
├── .csharpierignore # Root ignore file
├── src/
│ ├── MainProject/
│ └── Mobile/ # Subproject with different tooling
│ ├── .csharpierignore # Subproject-specific ignore file
│ └── *.cs files
Root .csharpierignore
src/Mobile/.csharpierignore
Expected Behavior
Actual Behavior
Root Cause
In IgnoreFile.cs
(lines 102–148 @ commit 019c82e), the FindIgnorePaths logic is:
- Find the first (closest)
.csharpierignore ascending from the directory
- Find all
.gitignore files ascending until .git/
This results in:
- Only one
.csharpierignore being evaluated per file
- Parent
.csharpierignore files being completely ignored when a child file exists
Proposed Solution (Preferred)
Invert the priority order to match how most ignore systems behave
(ESLint, Prettier, EditorConfig, .gitignore):
- Start with root
.csharpierignore patterns
- Layer more specific patterns from subdirectory
.csharpierignore files
- Allow negation (
!) in child files to override parent rules
Example:
# Root .csharpierignore
src/Mobile/**
# src/Mobile/.csharpierignore
!**/*
**/*.Designer.cs
This would allow directory-level re-enabling while keeping exclusions explicit.
Alternative Solution (Less Breaking)
If inverting priority is too disruptive:
- Always evaluate all
.csharpierignore files in the ancestor path
- Apply patterns from root to leaf
- Allow negation patterns to override parent patterns
This would align .csharpierignore behavior with how .gitignore is already handled internally.
Current Workaround
The only available workaround today is to:
- ❌ Avoid using subdirectory
.csharpierignore files entirely
- ❌ Put formatting rules into
.gitignore instead
This is suboptimal because it mixes version control concerns with formatting concerns.
Related Code References
Impact
This affects:
- Monorepos with multiple projects
- Solutions with subprojects using different tooling
- Any setup where ignore behavior should depend on invocation context
Problem Summary
When a
.csharpierignorefile exists in a subdirectory, it completely overrides the patterns defined in the root.csharpierignorefor all files within that subdirectory.This behavior breaks common monorepo and multi-project workflows where you want:
Current Behavior
The current implementation in
IgnoreFile.cs(lines 112–118 @ commit
019c82e) searches for.csharpierignorefiles ascending from the base directory and uses only the first one found:This means that once a
.csharpierignoreis found in a subdirectory, all parent ignore rules are ignored for files under that directory.Example Scenario
Project structure
Root
.csharpierignoresrc/Mobile/.csharpierignoreExpected Behavior
dotnet csharpier format .from project rootsrc/Mobile/**should be completely ignoreddotnet csharpier format .from src/Mobile/*.Designer.csfiles should be ignoredActual Behavior
Running from root:
(because
src/Mobile/.csharpierignoretakes precedence and does not ignore the directory)Running from
src/Mobile/:Root Cause
In
IgnoreFile.cs(lines 102–148 @ commit
019c82e), theFindIgnorePathslogic is:.csharpierignoreascending from the directory.gitignorefiles ascending until.git/This results in:
.csharpierignorebeing evaluated per file.csharpierignorefiles being completely ignored when a child file existsProposed Solution (Preferred)
Invert the priority order to match how most ignore systems behave
(ESLint, Prettier, EditorConfig,
.gitignore):.csharpierignorepatterns.csharpierignorefiles!) in child files to override parent rulesExample:
This would allow directory-level re-enabling while keeping exclusions explicit.
Alternative Solution (Less Breaking)
If inverting priority is too disruptive:
.csharpierignorefiles in the ancestor pathThis would align
.csharpierignorebehavior with how.gitignoreis already handled internally.Current Workaround
The only available workaround today is to:
.csharpierignorefiles entirely.gitignoreinsteadThis is suboptimal because it mixes version control concerns with formatting concerns.
Related Code References
Only first
.csharpierignoreis discoveredcsharpier/Src/CSharpier.Cli/IgnoreFile.cs
Lines 112 to 118 in 019c82e
FindIgnorePathsimplementationcsharpier/Src/CSharpier.Cli/IgnoreFile.cs
Lines 102 to 148 in 019c82e
Ignore evaluation logic
csharpier/Src/CSharpier.Cli/IgnoreFile.cs
Lines 17 to 29 in 019c82e
Impact
This affects: