ci: filter a rewritten branch against the merge base !296

merged merged by cmc on 2026-09-06 22:25 UTC · krz/gitbay:ci-rebase-diffbase-176 into main

Discussion

cmc

Path filters computed a push's changed files as old..new, where old is the branch's previous tip. That is only "what this push changed" when the push is a fast-forward. A force-push rewrites the branch, and then old..new is the difference between two histories — after a rebase, whatever the new base added and nothing the branch itself touched.

So every filter concluded its job was unnecessary, and a rebased branch reported build: success with test, sonar and vuln never run and never recorded as skipped. On a repository that only accepts fast-forward merges, rebasing is the normal path, not an edge case — this is how unverified code gets merged.

Observed landing this release: four branches, three rebases, sixteen jobs that should have run and did not.

The fix reuses the merge-base derivation #171 already added for a branch's first push: if old is not an ancestor of sha, the branch was rewritten and the old tip is not a meaningful base, so fall back to the merge base with the default branch. That is the honest base either way — a filter is deciding about the branch's relationship to its target, and the merge base is what expresses it. An IsAncestor error falls open to the merge base too, keeping the existing rule that a filter which cannot be evaluated must never silently skip CI.

TestQueueBranchBuildsRebaseFiltersAgainstMergeBase builds the trap: a branch that changes app/a.go, rebased onto a main whose new commit is docs-only. It asserts the fixture reproduces the trap (old..new contains the docs file and not the code file) before asserting a build is queued, so it cannot pass vacuously. It fails without the fix. TestQueueBranchBuildsOrdinaryPushStillFilters continues to pass, so an ordinary docs-only push still filters.

Closes #176