From caabebf2a1b7f950df3955c51692fed67f605795 Mon Sep 17 00:00:00 2001 From: nuno maduro Date: Thu, 23 Apr 2026 10:56:17 -0700 Subject: [PATCH] wip --- src/Plugins/Tia.php | 13 ++++++++++--- src/Plugins/Tia/ChangedFiles.php | 31 +++++++++++++++++++++++++++++-- 2 files changed, 39 insertions(+), 5 deletions(-) diff --git a/src/Plugins/Tia.php b/src/Plugins/Tia.php index 9a7af931..c6e2f60c 100644 --- a/src/Plugins/Tia.php +++ b/src/Plugins/Tia.php @@ -833,14 +833,21 @@ final class Tia implements AddsOutput, HandlesArguments, Terminable return $arguments; } - $changed = $changedFiles->since($graph->recordedAtSha($this->branch)) ?? []; + $branchSha = $graph->recordedAtSha($this->branch); + $changed = $changedFiles->since($branchSha) ?? []; // Drop files whose content hash matches the last-run snapshot. This // is the "dirty but identical" filter: if a file is uncommitted but // its content hasn't moved since the last `--tia` invocation, its // dependents already re-ran last time and don't need re-running - // again. - $changed = $changedFiles->filterUnchangedSinceLastRun($changed, $graph->lastRunTree($this->branch)); + // again. Passing the recorded sha also catches reverts: a file + // that was edited last run but is now back to its committed + // form no longer looks "changed". + $changed = $changedFiles->filterUnchangedSinceLastRun( + $changed, + $graph->lastRunTree($this->branch), + $branchSha, + ); $affected = $changed === [] ? [] : $graph->affected($changed); diff --git a/src/Plugins/Tia/ChangedFiles.php b/src/Plugins/Tia/ChangedFiles.php index 2b511a39..37aeac92 100644 --- a/src/Plugins/Tia/ChangedFiles.php +++ b/src/Plugins/Tia/ChangedFiles.php @@ -42,7 +42,7 @@ final readonly class ChangedFiles * @param array $lastRunTree path → content hash from last run. * @return array */ - public function filterUnchangedSinceLastRun(array $files, array $lastRunTree): array + public function filterUnchangedSinceLastRun(array $files, array $lastRunTree, ?string $sha = null): array { if ($lastRunTree === []) { return $files; @@ -87,9 +87,36 @@ final readonly class ChangedFiles $hash = ContentHash::of($absolute); - if ($hash === false || $hash !== $snapshot) { + if ($hash === false) { $remaining[] = $file; + + continue; } + + if ($hash === $snapshot) { + // Same state as the last TIA invocation — unchanged. + continue; + } + + // Differs from the snapshot, but may still be a revert back + // to the committed version (scenario: last run had an edit, + // this run reverted it). Skipping this check causes stale + // snapshots from previous scenarios to cascade into the + // current run's invalidation set. Cheap to verify via + // `git show :`. + if ($sha !== null && $sha !== '') { + $baselineContent = $this->contentAtSha($sha, $file); + + if ($baselineContent !== null) { + $baselineHash = ContentHash::ofContent($file, $baselineContent); + + if ($hash === $baselineHash) { + continue; + } + } + } + + $remaining[] = $file; } return $remaining;