GitPedia

Dotnet affected

.NET tool for determining which projects are affected by a set of changes. Useful for large projects or monorepos.

From leonardochaia·Updated June 24, 2026·View on GitHub·

A .NET Tool for determining which projects are affected by a set of changes. This tool is particularly useful for large projects or mono-repositories. The project is written primarily in C#, distributed under the MIT License license, first published in 2021. Key topics include: build-tool, dotnet, dotnet-tools, monorepo, msbuild.

Latest release: v6.2.0
March 3, 2026View Changelog →

dotnet-affected

A .NET Tool for determining which projects are affected by a set of changes. This tool is particularly useful for large
projects or mono-repositories.

Features

dotnet-affected works by comparing two versions of your code, usually by a commit range in CI, or HEAD against your
current working directory.

  1. Detects which MSBuild Projects have changed based on the files that changed.
  2. When using Central Package Management, detects which NuGet Packages have changed.
  3. Detects which projects are affected by projects or packages that have changed.
  4. Detects changes to Directory.Build.props/.targets and other input files.
  5. Detects changes to any file referenced by the MSBuild project.
  6. Outputs an MSBuild Traversal SDK Project that can
    be used to dotnet build and test which projects where changed/affected.
  7. Outputs a text file which can be used to deploy only what's needed or feed to other tools.
  8. Supports .csproj, .fsproj and .vbproj.
  9. Supports SDK and non-SDK style projects.

How it works

dotnet-affected discovers all .csproj, .fsproj and .vbproj from filesystem. Then, uses MSBuild to build
a ProjectGraph of all projects and which projects they depend on.

Then git diff is ran, to determine which files have changed. These files are then mapped to which project they belong
to, and we get a list of which projects have any changes.

dotnet-affected will also detect changes to Directory.Packages.props and determine which NuGet packages have been
added/deleted/updated.

With the changed projects, and changed NuGet Packages, it uses the ProjectGraph to find which projects are affected by
those changes.

For example, given this project structure:

  1. Inventory.Shared
  2. Inventory (depends on .1)
  3. Inventory.Tests (depends on .2)
  4. Inventory.Shared.Tests (depends on .1)

When .2 changes, .3 will be affected so we will build and test .2 and .3. There's no need to build and test .4 since .1
has not changed.

When 1. changes, everything needs to be built/test, since, transitively, they all depend on .1.

Installation

The tool can be installed using dotnet install:

shell
dotnet tool install dotnet-affected

It can also be installed globally with the --global flag but installing using local tools is recommended so all devs
share the same version, and so you share the same version in CI as well.

You can then run the tool using dotnet affected in the root of your repository.

text
$ dotnet affected --help Description: Determines which projects are affected by a set of changes. Usage: affected [command] [options] Options: -p, --repository-path <repository-path> Path to the root of the repository, where the .git directory is. [Defaults to current directory, or solution's directory when using --solution-path] --solution-path <solution-path> Path to a Solution file (.sln) used to discover projects that may be affected. When omitted, will search for project files inside --repository-path. -v, --verbose Write useful messages or just the desired output. [default: False] --assume-changes <assume-changes> Hypothetically assume that given projects have changed instead of using Git diff to determine them. --from <from> A branch or commit to compare against --to. --to <to> A branch or commit to compare against --from -f, --format <format> Space separated list of formatters to write the output. [default: traversal] --dry-run Doesn't create files, outputs to stdout instead. [default: False] --output-dir <output-dir> The directory where the output file(s) will be generated If relative, it's relative to the --repository-path --output-name <output-name> The name for the file to create for each format. Format extension is appended to this name. [default: affected] --version Show version information -?, -h, --help Show help and usage information Commands: describe Prints the current changed and affected projects.

SDK based installation

Alternatively, dotnet-affected can be executed directly by MSBuild without using the dotnet-affected CLI.

File: ci.props

xml
<Project Sdk="DotnetAffected.Tasks/3.0.0;Microsoft.Build.Traversal/3.2.0"> <Target Name="_DotnetAffectedCheck" AfterTargets="DotnetAffectedCheck"> <!-- Print all affected projects --> <Message Text=" >> %(ProjectReference.Identity)" Importance="high"/> </Target> </Project>

Now when you execute

shell
dotnet build ci.props

Only the affected projects are built!

For more information see DotnetAffected.Tasks

Locating Git Repository

dotnet-affected needs the path to your Git repository (where the .git folder is) so it can run git diff. By default,
dotnet-affected will attempt to interpret the current working directory as a git repository.

This can be customized using --repository-path, shorthand -p.

Locally, it's recommended to run the tool at the repository root to simplify things. In CI, you usually provide the
working directory that your CI provider gives you in an environment variable.

Project Discovery

By default, projects are discovered by recursively searching the --repository-path, or current working directory if
not specified.

This is quite useful for projects that do not have Solution Files and are using something
like SlnGen
to generate solutions.

However, when you do have a Solution File, the --solution-file can be used to discover projects from the Solution
instead. This can also be used to filter down which projects the tool discovers, if you don't want to discover all
present in file system.

When using --solution-file, only the projects included in the Solution will be considered for changes.

For example, if changes are made to projects that are not referenced by the Solution file, those changes will be ignored
and dotnet-affected will output that nothing.

Note that, if your Solution file is not at the root of your Git Repository (where the .git directory is), you still
need to specify --repository-path. For example:

shell
dotnet affected --repository-path /home/lchaia/monorepo --solution-path /home/lchaia/monorepo/my-big-project/MyBigProjectSolution.sln

Build/test affected projects

In order to build only what is affected, the tool outputs an MSBuild Traversal project that can can then be feed
to dotnet build.

For example, the below command outputs affected.proj at the current directory, by comparing your changes against the
current HEAD.

text
$ dotnet affected --verbose Discovering projects from /home/lchaia/dev/dotnet-affected Building Dependency Graph Built Graph with 8 Projects in 0.31s 1 files have changed inside 1 projects 0 NuGet Packages have changed 1 projects are affected by these changes Changed Projects Name Path /home/lchaia/dev/dotnet-affected/src/dotnet-affected/dotnet-affected.csproj Affected Projects Name Path dotnet-affected.Tests /home/lchaia/dev/dotnet-affected/src/dotnet-affected.Tests/dotnet-affected.Tests.csproj WRITE: /home/lchaia/dev/dotnet-affected/affected.proj

The contents of affected.proj are:

xml
<Project Sdk="Microsoft.Build.Traversal/3.0.3"> <ItemGroup> <ProjectReference Include="/home/lchaia/dev/dotnet-affected/src/dotnet-affected.Tests/dotnet-affected.Tests.csproj"/> <ProjectReference Include="/home/lchaia/dev/dotnet-affected/src/dotnet-affected/dotnet-affected.csproj"/> </ItemGroup> </Project>

You can then use dotnet test (or any other dotnet commands) against the resulting affected.proj file:

shell
dotnet test affected.proj

Affected projects between commit ranges

By default, dotnet-affected will compare changes between your working directory against the current HEAD. This can be
changed by providing the --from and --to parameters. Commit sha or branch names can be used.

Examples:

shell
# Compares HEAD against working directory dotnet affected # Compares HEAD against branch chore/target-net7 dotnet affected --from chore/target-net7 # Compares main against branch chore/target-net7 dotnet affected --from chore/target-net7 --to main

Output Formatting

The --format command line option can be used to choose which output formats will be used.

dotnet-affected currently supports following format options:

  • traversal: Traversal SDK project file.
  • text: Plain text file containing all affected project paths.
  • json: JSON file containing all affected project names and paths.

Example:

text
$ dotnet affected -v --format text Discovering projects from /home/lchaia/dev/dotnet-affected Building Dependency Graph Built Graph with 8 Projects in 0.31s 1 files have changed inside 1 projects 0 NuGet Packages have changed 1 projects are affected by these changes Changed Projects Name Path /home/lchaia/dev/dotnet-affected/src/dotnet-affected/dotnet-affected.csproj Affected Projects Name Path dotnet-affected.Tests /home/lchaia/dev/dotnet-affected/src/dotnet-affected.Tests/dotnet-affected.Tests.csproj WRITE: /home/lchaia/dev/dotnet-affected/affected.txt
text
$ cat affected.txt  ✔ /home/lchaia/dev/dotnet-affected/src/dotnet-affected/dotnet-affected.csproj /home/lchaia/dev/dotnet-affected/src/dotnet-affected.Tests/dotnet-affected.Tests.csproj

Multiple Output Formats

This tool supports generating multiple output files by providing space-seperated format options to the --format flag.

Example:

text
$ dotnet affected -v --format text traversal json Discovering projects from /home/lchaia/dev/dotnet-affected Building Dependency Graph Built Graph with 8 Projects in 0.39s 1 files have changed inside 1 projects 0 NuGet Packages have changed 1 projects are affected by these changes Changed Projects Name Path /home/lchaia/dev/dotnet-affected/src/dotnet-affected/dotnet-affected.csproj Affected Projects Name Path dotnet-affected.Tests /home/lchaia/dev/dotnet-affected/src/dotnet-affected.Tests/dotnet-affected.Tests.csproj WRITE: /home/lchaia/dev/dotnet-affected/affected.txt WRITE: /home/lchaia/dev/dotnet-affected/affected.proj WRITE: /home/lchaia/dev/dotnet-affected/affected.json

Excluding Projects

Projects can be excluded by using the --exclude (shorthand -e) argument. It expects a dotnet Regular Expression
that will be matched against each Project's Full Path.

In the below example, dotnet-affected.Tests is excluded due to the regular expression provided.

shell
$ dotnet affected --dry-run --verbose -e .Tests. 1 files have changed referenced by 1 projects 0 NuGet Packages have changed 1 projects are affected by these changes 1 projects were excluded Changed Projects Name Path dotnet-affected /home/lchaia/dev/dotnet-affected/src/dotnet-affected/dotnet-affected.csproj Affected Projects Name Path dotnet-affected.Benchmarks /home/lchaia/dev/dotnet-affected/benchmarks/dotnet-affected.Benchmarks/dotnet-affected.Benchmarks.csproj Excluded Projects Name Path dotnet-affected.Tests /home/lchaia/dev/dotnet-affected/test/dotnet-affected.Tests/dotnet-affected.Tests.csproj DRY-RUN: WRITE /home/lchaia/dev/dotnet-affected/affected.proj DRY-RUN: CONTENTS: <Project Sdk="Microsoft.Build.Traversal/3.0.3"> <ItemGroup> <ProjectReference Include="/home/lchaia/dev/dotnet-affected/benchmarks/dotnet-affected.Benchmarks/dotnet-affected.Benchmarks.csproj" /> <ProjectReference Include="/home/lchaia/dev/dotnet-affected/src/dotnet-affected/dotnet-affected.csproj" /> </ItemGroup> </Project>

Continuous Integration

For usage in CI, it's recommended to use the --from and --to options with the environment variables provided by your
build tool.

dotnet-affected can be used in any CI system where you dotnet is present. You can install the tool and run
dotnet affected commands as if locally.

However, an action is provided for Github actions
and having a way to simplify this for other CI systems would be welcome.

Building branches/tags

For example, for building a branch a setup like this could be used:

shell
# Replace env vars with what your CI system gives you dotnet affected \ --from $LAST_SUCCESSFUL_BUILD_COMMIT \ --to $CURRENT_COMMIT_HASH dotnet test affected.proj

It's important to note that CI system triggers a build per push, not per commit. Which means a set of commits may be
built, instead of just one. There is also the case where the previous build/s have failed, so we need to build from the
latest commit that has a successful build.

There's an in-depth explanation of the problem in here

When using GitHub Actions, leonardochaia/dotnet-affected@v1 can be used to execute dotnet-affected. This can be
combined with nrwl/last-successful-commit-action to
build/test only what's affected since last succesful commit.

You can see a complete example
for building branches with GitHub actions here.

Building Pull Requests

For building PRs, we need to provide the target branch/commit and the PR branch/commit.

shell
dotnet affected generate --from origin/main --to $CURRENT_COMMIT_HASH dotnet test affected.proj

You can see a complete example
for building PRs with GitHub actions here.

Don't build/test/deploy when no projects have changed

If nothing has changed, or the changes are not related to any of the discovered projects, there is no need to
run dotnet test.

In order to detect this, dotnet affected will exit with an exit status code 166. You can use this to prevent spending
time on unnecessary tasks when nothing has changed.

Note that dotnet affected returns 166 when nothing has changed, not to be confused when nothing is affected. If
projects have changed, but nothing is affected by those changes, we still need to build those that changed.

shell
dotnet affected # [..] other args if [ "$?" -eq 0 ]; then dotnet build affected.proj fi

When using GitHub Actions, conditions can be added to skip steps when nothing has changed or is affected:

yaml
- name: Install dependencies if: success() && steps.affected.outputs.affected != '' run: dotnet restore affected.proj

Complete example

Which projects do I need to re deploy

In order to determine what projects need to be deployed since our previous release, we can use dotnet-affected to
determine which projects were affected from the previous release to the current one.

shell
dotnet affected --from releases/v1.0.0 --to releases/v2.0.0

Of course this assumes that your .NET dependencies also represent system's dependencies. For example, if your systems
communicate through HTTP and you don't share any assemblies between them, this won't work. But, if your systems share a
common assembly with data transfer objects, or auto-generated HttpClients for example, this works wonderful.

Describe Command

dotnet-affected includes a describe command that outputs to stdout in a readable fashion which projects have changed
and which projects are affected by those changes.

text
$ dotnet affected describe 1 files have changed inside 1 projects 0 NuGet Packages have changed 1 projects are affected by these changes Changed Projects Name Path /home/lchaia/dev/dotnet-affected/src/dotnet-affected/dotnet-affected.csproj Affected Projects Name Path dotnet-affected.Tests /home/lchaia/dev/dotnet-affected/src/dotnet-affected.Tests/dotnet-affected.Tests.csproj

Troubleshooting

Some useful commands and flags are included for troubleshooting or just observing what would be affected by a small
change to a system.

Dry Running

Sometimes it is useful to see what the tool would do under certain situation.

When adding the --dry-run flag, dotnet-affected will write to stdout instead of generating output files.

text
$ dotnet affected --dry-run DRY-RUN: WRITE /home/lchaia/dev/dotnet-affected/affected.proj DRY-RUN: CONTENTS: <Project Sdk="Microsoft.Build.Traversal/3.0.3"> <ItemGroup> <ProjectReference Include="/home/lchaia/dev/dotnet-affected/src/dotnet-affected.Tests/dotnet-affected.Tests.csproj" /> <ProjectReference Include="/home/lchaia/dev/dotnet-affected/src/dotnet-affected/dotnet-affected.csproj" /> </ItemGroup> </Project>

Assume Changes

You can also use --assume-changes some-project-name in order to fake changes being made to a certain project. This
let's you see what would be affected if that project changed.

text
$ dotnet-affected --dry-run --assume-changes dotnet-affected.Tests DRY-RUN: WRITE /home/lchaia/dev/dotnet-affected/affected.proj DRY-RUN: CONTENTS: <Project Sdk="Microsoft.Build.Traversal/3.0.3"> <ItemGroup> <ProjectReference Include="/home/lchaia/dev/dotnet-affected/src/dotnet-affected.Tests/dotnet-affected.Tests.csproj" /> </ItemGroup> </Project>

Contributing

We accept PRs! Feel free to file issues if you encounter any problem.

If you wanna build the solution, these are the steps:

Note: Windows users, you can either use WSL or GitBash, or use PowerShell replacing .sh scripts with .ps1

Installing the SDK

First run this script to locally install the proper versions of the .NET SDKs we are using. This won't affect other .NET
projects that you have.

shell
./eng/install-sdk.sh

It will install the SDKs at ./eng/.dotnet.

Activating your console

Before running any dotnet commands, you need to activate the SDK by running:

shell
. ./eng/activate.sh

If you run dotnet --info you should see all SDK installed.

You can then build using.

shell
dotnet build

Or open your favorite ide through the activated command line and it will use the locally installed .NET

shell
source ./eng/activate.sh rider Affected.sln

Contributors

Showing top 12 contributors by commit count.

View all contributors on GitHub →

This article is auto-generated from leonardochaia/dotnet-affected via the GitHub API.Last fetched: 6/28/2026