This commit is contained in:
nuno maduro
2026-04-23 10:56:17 -07:00
parent 470a5833d4
commit caabebf2a1
2 changed files with 39 additions and 5 deletions

View File

@ -833,14 +833,21 @@ final class Tia implements AddsOutput, HandlesArguments, Terminable
return $arguments; 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 // Drop files whose content hash matches the last-run snapshot. This
// is the "dirty but identical" filter: if a file is uncommitted but // is the "dirty but identical" filter: if a file is uncommitted but
// its content hasn't moved since the last `--tia` invocation, its // its content hasn't moved since the last `--tia` invocation, its
// dependents already re-ran last time and don't need re-running // dependents already re-ran last time and don't need re-running
// again. // again. Passing the recorded sha also catches reverts: a file
$changed = $changedFiles->filterUnchangedSinceLastRun($changed, $graph->lastRunTree($this->branch)); // 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); $affected = $changed === [] ? [] : $graph->affected($changed);

View File

@ -42,7 +42,7 @@ final readonly class ChangedFiles
* @param array<string, string> $lastRunTree path → content hash from last run. * @param array<string, string> $lastRunTree path → content hash from last run.
* @return array<int, string> * @return array<int, string>
*/ */
public function filterUnchangedSinceLastRun(array $files, array $lastRunTree): array public function filterUnchangedSinceLastRun(array $files, array $lastRunTree, ?string $sha = null): array
{ {
if ($lastRunTree === []) { if ($lastRunTree === []) {
return $files; return $files;
@ -87,9 +87,36 @@ final readonly class ChangedFiles
$hash = ContentHash::of($absolute); $hash = ContentHash::of($absolute);
if ($hash === false || $hash !== $snapshot) { if ($hash === false) {
$remaining[] = $file; $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 <sha>:<path>`.
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; return $remaining;