TIA: change detection misses all changes when the Pest project is a subdirectory of the git repository (monorepo)
Nobody has claimed this yet.
- Dominant language
- PHP
- Stars
- 11.7k
- Forks
- 538
- Avg merge
- 4d 11h
- Merged PRs (30d)
- 8
Description
What happened
In a monorepo where the Pest project lives in a subdirectory of the git repository (e.g. backend/), TIA never detects any PHP source change. In the worst case it replays the previous results as passing without executing the tests affected by the change.
repo/ <- git root
├── frontend/
└── backend/ <- Laravel app, composer.json, pest runs here
├── app/
└── tests/
Reproduction:
- Record a baseline:
PEST_ALLOW_XDEBUG=1 XDEBUG_MODE=coverage vendor/bin/pest --tia(frombackend/, all green, graph written). - Make a semantic change to
backend/app/Services/Billing/PartialInvoiceService.php(e.g. add aprivate const). - Run
vendor/bin/pest --tiaagain.
Expected: the tests covering that file are re-executed (in our suite: 40 tests across 4 files).
Actual: Tests: 1669 passed (…, 1669 replayed) in ~3s — the change maps to zero tests and everything is replayed as unchanged.
With no coverage driver loaded the failure is the safe-but-slow variant instead: WARN Detected PHP source changes but no coverage driver is available followed by a full-suite run on every invocation.
Root cause
src/Plugins/Tia/ChangedFiles.php collects changed paths via:
workingTreeChanges()→git status --porcelain -z --untracked-files=all(cwd = project root)diffSinceSha()→git diff --name-only <sha>..HEAD
Both git commands print paths relative to the repository root (backend/app/Services/...), regardless of cwd. Downstream these are consumed as project-relative paths:
currentHash()prepends the project root →…/backend/backend/app/...→is_file()false- the dependency-graph keys are project-relative (
app/Services/...) → the changed path never matches an edge → affected set is empty
Verified identical logic in v4.7.5 (installed) and v5.0.0 (GitHub source). Projects whose composer root equals the git root never notice, since both path notions coincide.
Proof it is exactly this
Creating a throwaway git repository inside backend/ (making project root == git root) and repeating the same experiment yields correct behavior immediately: Tests: 1669 passed (…, 40 affected, 1629 replayed) in ~100s. Removing the inner repo restores the broken behavior.
Suggested fix
Ask git for the prefix once (git rev-parse --show-prefix, empty at repo root) and strip it from every path returned by workingTreeChanges() / diffSinceSha() before they are compared with project-relative graph keys; paths outside the prefix (sibling packages, e.g. frontend/…) can be discarded or kept for the structural-change checks.
Environment
- Pest v4.7.5 (same code confirmed in v5.0.0)
- PHP 8.4.10, Laravel 12.64, PostgreSQL, macOS (darwin arm64)
- Layout: Laravel app in
backend/subdirectory of the git repo,tests/Pest.phpmapsTests\TestCaseonto Feature + Unit, suite is 100% functional Pest style (1669 tests)
Contributor guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Research direction
Start with src/Plugins/Tia/ChangedFiles.php and trace workingTreeChanges() and diffSinceSha(), then reproduce the monorepo case from the issue. Verify that Git paths from a repository-rooted command align with project-relative graph keys, that sibling paths are handled, and that the affected tests are rerun instead of replayed.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- git, php
- Domain
- testing-qa
- Issue type
- Bug
- Difficulty
- 3/5
- Estimated time
- 1-2 days
- Activity status
- Quiet
- Clarity
- Clearly specified
- Newbie friendliness
- 68/100