Filters are unions

Separate --filter flags add up; braces intersect a path and diff.

From turborepo, filtering/patterns.md.

Apps that changed

ui and web changed. Dry-run a build of changed apps.

Two filtersBad
– in scopewanted 1
--filter=./apps/* --filter=[HEAD^1]
One filter, bracesGood
– in scopewanted 1
--filter={./apps/*}[HEAD^1]

Filter playground

Pick a filter to see what it selects.

--filter=web...Selects

turbo run build --filter=web...            # web and what it depends on
turbo run test  --filter=...ui             # ui and what depends on it
turbo run test  --filter=...^ui            # only ui's dependents
turbo run build --filter=./apps/* --filter=!admin
turbo run build --filter={./apps/*}[HEAD^1]   # intersection
turbo run build --filter=web... --dry         # check before you run

Why it matters, pros and cons

Why it matters

Each --filter adds packages to the run. To keep only packages matching both a path and a change, both must sit in one filter.

Pros

  • One flag says exactly which packages run.
  • ... follows the graph both ways.
  • --dry shows the scope before work starts.

Cons

  • Brace syntax is easy to forget.
  • ! only removes, it never adds.
  • For PRs, --affected is simpler.