Skip to main content

release_range_start

Function release_range_start 

Source
pub fn release_range_start(
    project_root: &Path,
    baseline_tag: &str,
) -> Result<String, VersionError>
Expand description

Resolve the commit range start for D-08’s conventional-commit classifier, given the baseline tag name (e.g. "v2.0.0").

This exists because every release in this repository squash-merges develop into main, so no develop-side commit is ever an ancestor of the release tag it was squashed into — a -X ours sync merge-back restores ancestry in the OTHER direction only (the tag becomes an ancestor of HEAD, which is what makes D-07’s --merged HEAD reachability filter work), but the commits the tag released stay outside its ancestry forever. A literal baseline..HEAD range therefore re-includes the entire pre-release history on every subsequent ship — measured live 2026-07-27: v2.0.0..HEAD is 677 non-merge commits (62 feat), against 5 (0 feat) for the anchored range this function computes. See 25-01-PLAN.md’s <measured_correction>.

Anchor rule (generalized 2026-07-28 to fix CR-03 — 25-REVIEW.md, 25-VERIFICATION.md GAP 2):

  • Walk git rev-list --ancestry-path --reverse <tag>..HEAD oldest-first. For each candidate commit C in order: if C has no first parent (a root commit), or the baseline tag is NOT an ancestor of C’s first parent, C is where the tag’s line joined HEAD’s line — return C immediately.
  • If every candidate’s first parent already descends from the tag, the tag already sat on this mainline throughout (the ordinary, non-squashed case, e.g. v1.8.0..v1.8.1) — return the tag unchanged.
  • If the ancestry path is empty, the tag is at HEAD — return the tag.

CR-03 — the previous rule inspected only the ancestry path’s FIRST commit (C1). When a commit lands directly on trunk between the tag and the sync-merge-back (a hotfix pushed straight to main), that intervening commit becomes C1; its first parent IS the tag commit, so merge-base --is-ancestor <tag> <tag> is trivially true, and the old rule wrongly concluded the tag already sat on mainline — returning the literal tag..HEAD range and re-admitting pre-release develop history. Walking the FULL path instead of just C1 fixes this: the sync merge itself still fails the ancestor test and is returned once the walk reaches it.

Anchoring at the LAST merge commit instead (a plausible-looking alternative) is WRONG on this repository. GitFlow::merge_feature_into_develop (git.rs:86) merges every phase branch into develop with git merge --no-ff, so ordinary post-release feature work also produces merge commits on the ancestry path — not just the sync-merge-back. Anchoring at the last one would silently truncate the range at that later feature merge instead of the sync merge, dropping any commits between the two from classification — a feat!: in that position would be dropped unnoticed, the exact false negative D-09 exists to prevent. See tests::feature_merge_after_sync_merge_does_not_move_the_anchor.