A merge took an old tree over two merged increments
Table of Contents
This page is a capture in the inbox bucket of the product backlog — a pre-sprint idea, not yet pulled into a sprint as a story.
1. What
Commit 1398383538 ("[synthetic] Restore the planners a merge dropped") states
one intent: bring back three files of the synthetic trade population planner that
an earlier merge dropped. It was made a child of 6b28050821, the main tip at the
time, and its tree replaced 174 of that tree's files with copies taken before
those merges. Merging PR #4293 brought it to main as 9e500a223f:
$ git diff --stat 1398383538^ 1398383538 | tail -1 294 files changed, 1875 insertions(+), 37112 deletions(-) $ git diff --diff-filter=D --name-only 1398383538^ 1398383538 | wc -l 174
What went with it:
- Both increments of the streaming story, merged minutes earlier in #4287 and
#4289:
change_feed, the collection-shaped change, the ticker wiring and the session door's verifier. - The whole curve volatility smile family in refdata: domain, table, json, event, generator, protocol, handler and history-provider files, with their models, recipes, seeded conventions and SQL.
- The shell's refdata commands, the generated TypeScript for those protocols, the refdata conventions registrar, parts of the SQL schema and populate scripts, and three work records.
2. How it was found
By accident, and that is the point. The streaming work was blocked by a build error, and when main was fixed the increments were simply gone. Nothing in the flow says so: the merge compiles, the suites that matter to the surviving files pass, and the deletions sit in a diff nobody reads once the pull request is green.
3. How much was missing, measured
build/scripts/merge_loss_report.py answers it for any suspect commit:
$ build/scripts/merge_loss_report.py 1398383538 6b28050821 origin/main gone and not mentioned since: 168 changed and not mentioned since: 87 changed and touched later, so read the hunks: 63
The third list matters because a later commit touching a file does not prove it
restored what the revert removed. Reading those hunks found the day-counter change
of 4ca29c3a00, which explains all 32 files that still hold fewer lines than the
baseline, and 31 files where later work added more.
4. How it was repaired
hotfix/restore-what-the-reverted-merge-dropped reverts 1398383538 and
re-applies the three planner files it meant to restore, so the intent survives
and the collateral does not. Ten conflicts, each a path a later commit has since
rewritten: the feed and its design, the streaming and door work records, the trade
operations service and its case, and the canonical-form task. Each keeps the later
version.
The result is checked against the baseline, not against hope:
$ build/scripts/merge_loss_report.py 1398383538 6b28050821 HEAD --expected <the three planner files> gone and not mentioned since: 0 changed and not mentioned since: 0 kept from the suspect commit on purpose: 3 changed and touched later, so read the hunks: 63 (all later work, or explained by 4ca29c3a00)
5. Why it happened, and the guard that would catch it
The habit that caused it is ordinary: to repair a lost file, take it from the
commit that last had it. Run as git checkout <old> -- <paths> it is safe. Run
across a directory, or with a pathspec that matches more than expected, it
overwrites every newer file under it, and the diff is too large to read.
Two guards are cheap and neither exists:
- A merge or commit that deletes files the branch never touched is suspect by construction. Refuse it, or make the pull request say so in its own words.
- A post-merge check that every path the first parent had is still present, or
was deleted by a commit that says it deleted it. The report script is the
whole of it:
merge_loss_report.py <merge> --baseline <first parent>and fail when the first two lists are non-empty.
6. References
1398383538, the commit,9e500a223f, the merge, and6b28050821, the tree it took the old copies from.build/scripts/merge_loss_report.py, the report.- One HTTP Server: designing ores.http to replace the web BFF, whose increments were the part first noticed.
7. See also
- An events collection cannot be wildcarded, found by the live run that only became possible once the feed was back.