Meziantou.DeltaBuild
1.5.0
Prefix Reserved
dotnet tool install --global Meziantou.DeltaBuild --version 1.5.0
dotnet new tool-manifest
dotnet tool install --local Meziantou.DeltaBuild --version 1.5.0
#tool dotnet:?package=Meziantou.DeltaBuild&version=1.5.0
nuke :add-package Meziantou.DeltaBuild --version 1.5.0
Meziantou.DeltaBuild
A .NET CLI tool that generates subset solution or build files for incremental CI builds in monorepos. Instead of building everything on every pull request, DeltaBuild analyzes git changes and the MSBuild project dependency graph to determine which projects are actually affected, then produces a filtered solution/build file containing only those projects.
Why?
In a monorepo, a single repository hosts many projects. A typical CI pipeline rebuilds all of them on every commit or pull request, even when a change only touches one leaf project. This wastes time and compute resources.
Meziantou.DeltaBuild solves this by:
- Comparing two git commits to find changed files.
- Building the MSBuild project dependency graph from your solution or traversal file.
- Mapping changed files to the projects that own them (source files, imports,
.props/.targets,.editorconfig, NuGet configs, etc.). - Including transitive dependents — if a library changed, every project that references it (directly or indirectly) is included.
- Writing a new solution/build file that contains only the affected projects.
Your CI pipeline then builds this smaller file instead of the full solution, dramatically reducing build and test times for most pull requests.
Installation
dotnet tool install --global Meziantou.DeltaBuild
Or as a local tool:
dotnet tool install Meziantou.DeltaBuild
Usage
Meziantou.DeltaBuild generate --input <path> --output <path> [options]
Basic example
Meziantou.DeltaBuild generate \
--input MyRepo.sln \
--output DeltaBuild.sln
This compares HEAD against the merge-base of the default remote branch, finds changed files, determines which projects are affected, and writes a filtered DeltaBuild.sln.
CI example (Azure DevOps / GitHub Actions)
Meziantou.DeltaBuild generate \
--input dirs.proj \
--output delta.proj \
--repository .
On GitHub Actions pull_request events, DeltaBuild automatically uses GITHUB_BASE_REF to compute the merge base (git merge-base HEAD origin/<base-ref>), so no explicit --base-commit is needed.
Then build only the affected projects:
dotnet build delta.proj
dotnet test delta.proj
Local development (working tree)
Compare your uncommitted changes (staged, unstaged, and untracked files) against a branch:
Meziantou.DeltaBuild generate \
--input MyRepo.sln \
--output delta.sln \
--base-commit $(git merge-base origin/main HEAD) \
--working-tree
This is useful for quickly checking which projects your local modifications affect before committing.
Shard test projects for parallel CI
Meziantou.DeltaBuild generate \
--input MyRepo.sln \
--output delta.proj \
--test-projects-only \
--shard 1 \
--total-shards 3 \
--shard-separate tests/Smoke/Smoke.Tests.csproj \
--shard-separate tests/Integration/Integration.Tests.csproj
This writes one file (delta.proj) containing only the selected shard (1-based index) of affected test projects.
When --shard-separate is used, listed projects are spread across shards in declaration order (round-robin), so they are separated when possible.
Parameters
Required
| Parameter | Alias | Description |
|---|---|---|
--input |
-i |
Path to the input file. Supported formats: .sln, .slnx, .proj (Traversal SDK), or a single project file (.csproj, .fsproj, .vbproj). |
--output |
-o |
Path for the output file. The format is inferred from the file extension: .sln, .slnx, .proj, .json, or .txt. |
Optional
| Parameter | Alias | Default | Description |
|---|---|---|---|
--repository |
-r |
. (current directory) |
Path to the git repository root. |
--head-commit |
HEAD |
The head commit SHA to compare. Ignored when --working-tree is set. |
|
--base-commit |
Auto-detected | The base commit SHA. When omitted, computed via git merge-base using --base-branch. |
|
--base-branch |
Auto-detected from GitHub Actions PR context or remote | The base branch name used for merge-base detection (e.g., main, origin/main). On GitHub Actions pull request events, defaults to origin/$GITHUB_BASE_REF. |
|
--working-tree |
false |
Compare the base commit against the current working directory instead of a commit. Includes staged, unstaged, and untracked files. When set, --head-commit is ignored. |
|
--include |
(all projects) | Glob patterns to filter which projects to consider. Repeatable. Only projects matching at least one pattern are included. | |
--test-projects-only |
false |
Only include projects where the MSBuild property IsTestProject is true. |
|
--shard |
(none) | Generate only shard number N (1-based). Must be used with --total-shards. |
|
--total-shards |
(none) | Total number of shards used to partition affected projects. Must be used with --shard. |
|
--shard-separate |
(none) | Project paths to spread across different shards in declaration order (repeatable). Must be used with --shard and --total-shards. Paths are relative to the repository root unless absolute. |
|
--no-output-if-empty |
false |
Do not write an output file when no projects are affected. If the output file already exists, it is deleted. | |
--full-rebuild-trigger |
(none) | Glob patterns for files that trigger a full rebuild of all projects. When any changed file matches, every project is included in the output. Repeatable. Replaces defaults when provided. | |
--hierarchical-rebuild-trigger |
**/global.json, **/nuget.config, **/NuGet.config, **/NuGet.Config, **/.editorconfig |
Glob patterns for files that trigger a rebuild of projects in the same folder hierarchy. For example, changing src/global.json rebuilds projects under src/ but not under tests/. A match at the repository root affects all projects. Repeatable. Replaces defaults when provided. |
|
--project-bundle |
(none) | Comma-separated exact project paths that must be built together as a bundle. Repeatable. Paths are relative to the repository root unless absolute. Example: --project-bundle src/B/B.csproj,src/C/C.csproj. |
|
--engine |
MSBuild |
The analysis engine to use (see below). | |
--traversal-before-import |
<output-name>.before.proj |
Path of the import added before the <ProjectReference> items in the generated Traversal SDK file. |
|
--traversal-sdk-version |
(none) | Optional version appended to the Traversal SDK in generated Traversal SDK files. When set to x.y.z, generated files use Sdk="Microsoft.Build.Traversal/x.y.z". |
|
--traversal-after-import |
<output-name>.after.proj |
Path of the import added after the <ProjectReference> items in the generated Traversal SDK file. |
Analysis engines
| Engine | Description |
|---|---|
MSBuild |
Default. Passes individual project paths as entry points to the MSBuild Static Graph API. Works with all input formats. |
RoslynWorkspace |
Uses Roslyn's MSBuildWorkspace to load projects. More compatible because Roslyn sees files added dynamically by MSBuild targets (e.g., source generators). Also tracks .editorconfig and .globalconfig via AnalyzerConfigDocuments. |
StaticGraph |
Passes the input file (solution or traversal) as a single entry point to MSBuild's ProjectGraph, letting MSBuild handle solution/traversal parsing natively with parallel evaluation. Inspired by Petabridge/Incrementalist. |
How it works
- Resolve commits — Determines the base and head commits. If not provided, head defaults to
HEAD. Base is computed viagit merge-baseusingorigin/$GITHUB_BASE_REFon GitHub Actions pull request events, otherwise using the default remote branch. - Get changed files — Runs
git diff --name-onlybetween the two commits. - Parse input — Reads the solution, traversal, or project file to discover the list of projects.
- Filter projects — Applies
--includeglob patterns if provided. - Check full-rebuild triggers — If any changed file matches a
--full-rebuild-triggerpattern, all projects are included and analysis stops. - Check hierarchical-rebuild triggers — For each changed file matching a
--hierarchical-rebuild-triggerpattern, projects in the same folder hierarchy are marked as affected. For example,src/nuget.configaffects projects undersrc/, while a root-levelnuget.configaffects all projects. - Analyze project graph — Uses the selected engine to build the dependency graph and determine which files each project owns (source files, imports,
.props,.targets,.editorconfig,.globalconfig, etc.). - Determine directly affected projects — A project is directly affected if any of its owned files appears in the changed file list, or if it was flagged by a hierarchical trigger.
- Expand impacted projects — Walks up the dependency graph to include transitive dependents, and applies
--project-bundlerules so if one project in a bundle is affected, all bundle members are included too. - Filter test projects (optional) — When
--test-projects-onlyis set, keeps only projects withIsTestProject=true. - Write output — Produces the filtered solution/build file containing only the final affected projects. When
--shardand--total-shardsare set, only the selected shard is written to the provided--outputpath. If--no-output-if-emptyis set and no project is affected in the selected result, the output file is skipped/deleted.
Output formats
| Extension | Format |
|---|---|
.sln |
Visual Studio solution (v12) |
.slnx |
XML-based solution (Visual Studio 2022+) |
.proj |
MSBuild Traversal SDK project with <ProjectReference> items and an <IsTraversal>true</IsTraversal> property. All generated paths are prefixed with $(MSBuildThisFileDirectory) for stable path resolution. Automatically imports <output>.before.proj and <output>.after.proj if they exist, allowing you to inject custom MSBuild logic. |
.json |
JSON array of affected project paths |
.txt |
One project path per line |
Tracked file types
DeltaBuild tracks the following MSBuild item types as owned files for each project:
Compile— Source code files (.cs,.fs,.vb, etc.)Content,None,EmbeddedResource— Static assets and resourcesAdditionalFiles— Files passed to analyzersEditorConfigFiles—.editorconfigfiles discovered by MSBuildGlobalAnalyzerConfigFiles—.globalconfigfilesPage,ApplicationDefinition,Resource— WPF/XAML itemsTypeScriptCompile— TypeScript filesProtobuf—.protofiles (gRPC / protobuf tooling)MauiXaml,MauiCss,MauiImage,MauiIcon,MauiSplashScreen,MauiFont,MauiAsset— .NET MAUI items (MAUI removes**/*.xamlfrom the defaultNoneitems, soMauiXamlis the only item type tracking them)- Import paths —
.props,.targets, and other imported MSBuild files (viaProjectInstance.ImportPaths) - Project file itself — The
.csproj/.fsproj/.vbprojfile
The RoslynWorkspace engine additionally tracks files exposed through Roslyn's Documents, AdditionalDocuments, and AnalyzerConfigDocuments collections.
Customizing tracked items
Projects can extend the list above using MSBuild itself — no command line option is involved. Put the following in a project, or in a Directory.Build.props to apply it to a whole repository:
<Project>
<PropertyGroup>
<DeltaBuildItemsIncludeDefaults>true</DeltaBuildItemsIncludeDefaults>
</PropertyGroup>
<ItemGroup>
<DeltaBuildItems Include="ProtoFile;MyAssets" />
<DeltaBuildIncludeFile Include="../../shared/schema.graphql" />
<DeltaBuildExcludeFile Include="Generated.cs" />
</ItemGroup>
</Project>
DeltaBuildItemscontains item type names, not file paths. Items of these types are tracked exactly like the built-in ones (theirFullPathmetadata is used), which also works for files located outside the project directory.DeltaBuildIncludeFilecontains file paths that are added directly to the project's owned files, for files that no MSBuild item type covers.DeltaBuildExcludeFilecontains file paths that are removed from the owned files of the project, whatever tracked them (item types,DeltaBuildIncludeFile, imports, or Roslyn documents). Exclusions are applied last, and MSBuild wildcards such asInclude="**/*.g.cs"work as usual.DeltaBuildItemsIncludeDefaults=falseonly removes the built-in item types. The project file itself, its import paths, and — with theRoslynWorkspaceengine — Roslyn'sDocuments,AdditionalDocuments, andAnalyzerConfigDocumentsare always tracked.- Values of a custom item type that are not file paths are harmless: they resolve to paths that never match a changed file.
| Product | Versions Compatible and additional computed target framework versions. |
|---|---|
| .NET | net10.0 is compatible. net10.0-android was computed. net10.0-browser was computed. net10.0-ios was computed. net10.0-maccatalyst was computed. net10.0-macos was computed. net10.0-tvos was computed. net10.0-windows was computed. |
This package has no dependencies.
| Version | Downloads | Last Updated |
|---|---|---|
| 1.5.0 | 191 | 9/21/2026 |
| 1.4.1 | 96,319 | 8/16/2026 |
| 1.4.0 | 133 | 8/16/2026 |
| 1.3.4 | 37,719 | 5/10/2026 |
| 1.3.3 | 500 | 5/9/2026 |
| 1.3.1 | 1,869 | 5/2/2026 |
| 1.3.0 | 5,478 | 4/6/2026 |
| 1.2.0 | 855 | 4/5/2026 |
| 1.1.0 | 1,361 | 3/28/2026 |
| 1.0.4 | 305 | 3/27/2026 |
| 1.0.3 | 151 | 3/27/2026 |
| 1.0.2 | 146 | 3/26/2026 |
| 1.0.1 | 107 | 3/26/2026 |
| 1.0.0 | 112 | 3/26/2026 |