A develcustomization/<partner> update PR merged with Squash and merge produces a single-parent commit. The content lands correctly, but devel stops being an ancestor of the partner branch — so the next devel update re-presents the entire already-merged history as new changes, with heavy conflicts.

The failure is silent and deferred

Nothing goes wrong at merge time. The tree is right, CI is green, the release ships. The cost lands one release later, on whoever runs the next devel update — and it looks like a mysteriously enormous conflict set against code that is already present.

Mechanism

A devel-update PR is, by construction, a merge of two long-lived lines. GitHub offers three merge buttons:

ButtonParents on the resultgit merge-base --is-ancestor devel partner afterwards
Create a merge commit2 (partner tip + devel tip)true — devel is reachable
Squash and merge1 (partner tip only)false
Rebase and merge1 per replayed commitfalse

Squash flattens every commit in the PR into one new commit whose sole parent is the base branch tip. The devel-side commits are not referenced from the partner branch at all — they only exist on devel. Git therefore has no record that they were ever merged.

How to detect it after the fact

Count parents on the merge result:

git cat-file -p <merge-sha> | grep -c '^parent '
# 2 → real merge commit, ancestry preserved
# 1 → squash (or rebase), ancestry LOST

Or ask directly:

git merge-base --is-ancestor origin/devel origin/customization/<partner> && echo preserved || echo lost

Via the GitHub API without a clone (also avoids the stale-ref trap of narrowed-fetch-refspec-stale-devel-merge and the output mangling of rtk-git-log-hides-merge-commits):

gh api repos/TechTeamer/<repo>/commits/<sha> --jq '.parents[].sha'

Confirmed instance — CIB 1.9.11.102, 2026-09-03

Both release PRs of FKITDEV-9197 were squash-merged (verified via the GitHub API):

RepoSquash commitParentsCollapsed
vuer_oss12a8a9e328221829ae6d383fd5e23eda9cf81a388abcc4c733 only334 commits / ~11 months
vuer_cssca60fac34ac95b661336587b455924ab55e52defb043527c72 only334 commits / ~11 months

Full context: cib-1.9.11.102.

What is and is not at risk

For Agents

Content is not at risk. A squash commit’s tree is identical to the PR head’s tree. Verify it before drawing any alarm:

gh api repos/TechTeamer/<repo>/commits/<squash-sha> --jq .commit.tree.sha
gh api repos/TechTeamer/<repo>/commits/<pr-head-sha> --jq .commit.tree.sha

Equal SHAs ⇒ the shipped bits are exactly what was reviewed and tested. In the CIB case they were equal in both repos (df853239b1… for oss, 38079f89c7… for css).

History shape is at risk, and that is what future merges reason about.

Downstream effects of lost ancestry:

  • git merge origin/devel on the partner branch replays the whole span as “incoming”, conflicting against the squashed-in equivalents.
  • git log customization/<partner>..devel / git rev-list --count become meaningless as drift measures — they over-report by the size of the squashed span.
  • Per-ticket attribution inside the collapsed range is gone from the partner line; release-ticket-collector-style tooling that walks partner history to build a changelog will not see those commits.
  • git blame on the partner branch attributes eleven months of core changes to one commit and one author.

Mitigations

Ordered by preference.

  1. Prevent it: always use “Create a merge commit” for develcustomization/* PRs. Squash is appropriate for feature PRs into devel, never for a sync merge. This is the only cost-free option and it must be decided before the merge button is pressed.
  2. Record a no-op merge afterwards. On the partner branch, git merge -s ours <devel-sha-that-was-squashed-in> re-establishes ancestry without changing the tree. Content stays exactly as tagged; the next update sees the span as merged. Needs a commit on the partner branch and must satisfy the bracketed-ticket rule of techteamer-commit-message-ruleset — and customization/** enforces it.
  3. Absorb the conflicts next round. Budget a much larger devel-update effort and resolve mechanically, treating “already present” hunks as such. Slow and error-prone; only if 1 and 2 are unavailable.

Option 2 is the standard remedy but it is a history rewrite of intent, not just bookkeeping — get a human decision before doing it on a released partner branch.