gochanged lists Go packages that have untested changes compared to a base git
branch, tag, or commit. It is intended for use in CI pipelines and local
development workflows where running the full test suite is expensive and only
the affected packages need to be tested.
go install github.com/hpidcock/gochanged@latestgochanged [flags] [packages...]
The packages argument accepts standard Go package patterns (e.g. ./...,
./cmd/...). If no packages are specified, it defaults to ./....
gochanged prints one import path per line to stdout, making it suitable for
use as input to go test or other tools via command substitution.
| Flag | Description |
|---|---|
-b, --branch |
Git ref (branch, tag, or commit SHA) to diff against. Required. |
--why |
Print an explanation to stderr for each package describing why it was selected. |
-v, --version |
Print version and exit. |
Run tests for all packages that changed compared to main:
go test $(gochanged -b main ./...)Run tests against a specific remote branch:
go test $(gochanged -b origin/main ./...)Run tests for changes in the last 5 commits:
go test $(gochanged -b HEAD~5 ./...)Scope to a subtree of the module:
go test $(gochanged -b main ./cmd/...)See why each package was selected:
gochanged -b main -why ./...gochanged determines which packages need testing through the following steps:
-
Resolve packages. The requested package patterns are expanded using
go listto obtain the full set of packages and their import graphs. -
Check for workspace mode. If a
go.workfile is active, all requested packages are printed unconditionally because dependency information across workspace modules is not tracked. -
Compare
go.mod. The currentgo.modis compared against the version at the base ref. If the Go language version (godirective) changed, all packages are printed. Otherwise, individualrequireandreplacedirectives are compared to detect changed module dependencies. -
Diff files with git.
git diff --name-statusis run against the base ref to obtain the list of added, modified, deleted, and renamed files. Each changed file is classified as either a source change or a test-only change (_test.gofiles and anything under atestdatadirectory). -
Map files to packages. Changed directories are matched to their corresponding Go packages.
-
Walk the import graph. A directed acyclic graph of package imports is built. For every directly changed package,
gochangedwalks the graph in reverse (from dependency to dependents) to find all transitively affected packages. Packages whose test imports (TestGoFilesorXTestGoFiles) reference an affected package are also marked. -
Print results. The final set of packages that need testing is printed to stdout, one per line. When
--whyis specified, the reason each package was selected is printed to stderr.
- A file was added, modified, deleted, or renamed compared to the base ref.
- A module dependency version changed in
go.mod. - A
replacedirective was added, removed, or modified ingo.mod. - The
godirective version changed ingo.mod(causes all packages to be selected). - The module did not exist at the base ref (causes all packages to be selected).
gochanged is composed of two internal library packages:
git— Wraps git commands for finding the repository root, reading files at a given ref, and computing file-level diffs.packages— Wrapsgo listandgo envto enumerate Go packages, resolve their imports, and detect workspace mode. Also defines thePackagestruct that mirrors the output ofgo list -json.
- Go toolchain (for
go listand optionally running tests) - Git (the working directory must be inside a git repository)
- All requested packages must belong to the same Go module
MIT — see LICENSE.