Artifact e648dbe67f01c6838299a275f4d581b43129f1095b3c4e161a425789dced0841:
- File
internal/index/landingmerge_test.go
— part of check-in
[06475f5aee]
at
2026-08-26 11:19:27
on branch landing-merge
— Link a landing to the check-in it produced
settled_by names the reply that decided the state, not the commit, so this was assumed to need an upstream change. It does not: a merge check-in lists its primary parent first and the branches it merged after, so ord > 0 is the branch tip that landed, and the landing knows which branch it proposed. Structural, not a text or time heuristic. Resolves 18 of 18 accepted landings in this store.
Bounded by when the landing OPENED, not when it was accepted: the merge lands before the artifact recording it is written.
The landing page says what it landed as; a merge row on the timeline names the proposal and who approved it. Decorated, not collapsed — a merge is a real DAG node and hiding it would leave the graph drawing edges to a row the timeline does not show. (user: dmestas, size: 5211) [annotate] [blame] [check-ins using] [more...]
package index import ( "testing" ) // foldMerge records a check-in whose second parent is a branch tip — // the shape zit writes when it lands a branch. func foldMerge(t *testing.T, ix *Index, hash, branch, date, comment string, parents ...string) { t.Helper() if _, err := ix.db.Exec(`INSERT OR REPLACE INTO checkin (hash, date, user, comment, branch) VALUES (?, ?, 'dmestas', ?, ?)`, hash, date, comment, branch); err != nil { t.Fatal(err) } for i, p := range parents { if _, err := ix.db.Exec(`INSERT OR REPLACE INTO checkin_parent (child, parent, ord) VALUES (?, ?, ?)`, hash, p, i); err != nil { t.Fatal(err) } } } // landed builds an accepted landing with a branch tip and its merge. func landed(t *testing.T, ix *Index, root, branch, title string) { t.Helper() foldForumBody(t, ix, root, "", title, "2026-01-01T10:00:00", landingBody(branch, "trunk", "please")) foldForumBody(t, ix, root+"ff", root, "", "2026-01-03T10:00:00", landingState("accepted", "landed")) foldMerge(t, ix, "tip"+branch, branch, "2026-01-02T10:00:00", "work on "+branch) foldMerge(t, ix, "mrg"+branch, "trunk", "2026-01-02T12:00:00", "land "+branch, "prevtrunk", "tip"+branch) } // landing.settled_by holds the reply that DECIDED the state — a forum // post hash, not a commit — which was read as meaning nothing connects // a landing to the check-in it produced. The answer was already in the // DAG: a merge check-in lists its primary parent first and the branches // it merged after, so ord > 0 IS the tip that landed. func TestLandingResolvesToItsMergeCheckin(t *testing.T) { ix := openIndex(t) landed(t, ix, "aaaa1111", "feat-x", "Ship it") got, err := ix.MergesForLandings([]string{"aaaa1111"}) if err != nil { t.Fatal(err) } m, ok := got["aaaa1111"] if !ok { t.Fatal("an accepted landing resolved to no check-in") } if m.Checkin != "mrgfeat-x" { t.Errorf("resolved to %q, want the merge on trunk", m.Checkin) } if m.Comment != "land feat-x" { t.Errorf("comment %q", m.Comment) } } // The merge lands BEFORE the artifact recording the verdict is written // — one landing in this forge merged at 12:46:55 and was recorded at // 12:49:51 — so bounding the search on the verdict silently loses // exactly the check-ins this exists to find. A proposal cannot be // landed before it is made; that is the bound that holds. func TestMergeIsFoundEvenWhenItPrecedesTheVerdict(t *testing.T) { ix := openIndex(t) foldForumBody(t, ix, "aaaa1111", "", "Ship it", "2026-01-01T10:00:00", landingBody("feat-x", "trunk", "please")) foldMerge(t, ix, "tipfeat-x", "feat-x", "2026-01-02T12:46:55", "work") foldMerge(t, ix, "mrgfeat-x", "trunk", "2026-01-02T12:46:55", "land it", "prevtrunk", "tipfeat-x") // The verdict is filed three minutes AFTER the merge. foldForumBody(t, ix, "cccc3333", "aaaa1111", "", "2026-01-02T12:49:51", landingState("accepted", "landed")) got, err := ix.MergesForLandings([]string{"aaaa1111"}) if err != nil { t.Fatal(err) } if _, ok := got["aaaa1111"]; !ok { t.Fatal("the merge was lost because it preceded its own verdict record") } } // Open and rejected landings produced nothing, and must not borrow a // merge that happens to touch the same branch. func TestUnsettledLandingsResolveToNothing(t *testing.T) { ix := openIndex(t) foldForumBody(t, ix, "aaaa1111", "", "Still open", "2026-01-01T10:00:00", landingBody("feat-x", "trunk", "please")) foldMerge(t, ix, "tipfeat-x", "feat-x", "2026-01-02T10:00:00", "work") foldMerge(t, ix, "mrgfeat-x", "trunk", "2026-01-02T12:00:00", "merged by hand", "prevtrunk", "tipfeat-x") got, err := ix.MergesForLandings([]string{"aaaa1111"}) if err != nil { t.Fatal(err) } if m, ok := got["aaaa1111"]; ok { t.Errorf("an OPEN landing claimed check-in %q", m.Checkin) } foldForumBody(t, ix, "bbbb2222", "aaaa1111", "", "2026-01-03T10:00:00", landingState("rejected", "no")) if got, _ := ix.MergesForLandings([]string{"aaaa1111"}); len(got) != 0 { t.Error("a REJECTED landing claimed a check-in") } } // A merge somebody made by hand carries no landing, and the timeline // decoration's absence is what distinguishes the two. func TestTimelineDecoratesOnlyLandingMerges(t *testing.T) { ix := openIndex(t) landed(t, ix, "aaaa1111", "feat-x", "Ship it") // An unrelated merge of a branch no landing proposed. foldMerge(t, ix, "tiphotfix", "hotfix", "2026-01-04T10:00:00", "urgent") foldMerge(t, ix, "mrghotfix", "trunk", "2026-01-04T12:00:00", "merge hotfix", "prevtrunk", "tiphotfix") rows := []TimelineEntry{ {Kind: "ci", Hash: "mrgfeat-x", IsMerge: true}, {Kind: "ci", Hash: "mrghotfix", IsMerge: true}, {Kind: "ci", Hash: "tipfeat-x"}, } if err := ix.attachLandings(rows); err != nil { t.Fatal(err) } if rows[0].Landing != "aaaa1111" || rows[0].LandingTitle != "Ship it" { t.Errorf("landing merge not decorated: %+v", rows[0]) } if rows[0].LandingApprover != "dan" { t.Errorf("approver %q, want the author of the verdict", rows[0].LandingApprover) } if rows[1].Landing != "" { t.Errorf("a hand-made merge was decorated with %q", rows[1].Landing) } if rows[2].Landing != "" { t.Error("a non-merge check-in was decorated") } }