Judge a merge in one place for hearing it and for the catch-up (ADR 0457, issue 1336, review round 1)

This commit is contained in:
2026-10-20 13:12:23 +00:00
parent 29bbe4d682
commit bde1691f0a
+50 -18
View File
@@ -124,6 +124,52 @@ func durationSetting(v any) (time.Duration, bool) {
return 0, false
}
// mergeFacts is what hearMerge judges a merge by, kept apart so the catch-up (ADR 0457) judges a merge the way hearing it will.
type mergeFacts struct {
merged time.Time
touches []inventory.Entry
later []inventory.BatchedMerge
owed bool
from, packaging []inventory.Entry
already int
addsModuleToBuilds bool // a first merge into a repository the mesh builds nothing from: firstBuilds acts on it
}
// factsOfMerge reads what hearing a merge turns on: the modules it touches whatever the history, the later merges of its
// branch, whether it is owed late, and the modules it moves.
func factsOfMerge(ctx context.Context, inv *inventory.Inventory, m link.SourceMoved, now time.Time,
entries []inventory.Entry, read map[string][]inventory.ReadRepository) (mergeFacts, error) {
repository := m.Owner + "/" + m.Repo
f := mergeFacts{}
var err error
f.merged, err = time.Parse(time.RFC3339Nano, m.MergedAt)
if err != nil {
f.merged = now
}
// **What it touches, history or not** (review of this change): a merge heard after a later merge of its
// branch was cut reads as history for every module that cut marked seen, and was dropped from the record
// here, never named by the walk that carried it.
raw := m
raw.MergedAt = ""
f.touches = wouldMove(raw, entries, read)
if len(f.touches) > 0 {
if f.later, err = inv.LaterMergesOf(ctx, repository, m.Base, f.merged.UTC()); err != nil {
return f, err
}
}
if f.owed, err = owedLate(ctx, inv, m, f.merged, f.touches); err != nil {
return f, err
}
f.from, f.packaging, f.already = mergeCandidates(m, entries, read)
f.addsModuleToBuilds = f.already == 0 && len(addedDirs(m, entries, read)) > 0
return f, nil
}
// plans says hearing the merge puts it in a batch or a walk.
func (f mergeFacts) plans() bool {
return len(f.from) > 0 || len(f.packaging) > 0 || len(f.later) > 0 || f.owed
}
// hearMerge puts a merge the forge announced into the batch that answers it, at the moment it was heard,
// and cuts what is due.
func (f following) hearMerge(ctx context.Context, m link.SourceMoved, now time.Time) error {
@@ -148,27 +194,13 @@ func (f following) hearMerge(ctx context.Context, m link.SourceMoved, now time.T
if err != nil {
return notNow(err)
}
merged, err := time.Parse(time.RFC3339Nano, m.MergedAt)
if err != nil {
merged = now
}
// **What it touches, history or not** (review of this change): a merge heard after a later merge of its
// branch was cut reads as history for every module that cut marked seen, and was dropped from the record
// here, never named by the walk that carried it.
raw := m
raw.MergedAt = ""
touches := wouldMove(raw, entries, read)
var later []inventory.BatchedMerge
if len(touches) > 0 {
if later, err = inv.LaterMergesOf(ctx, repository, m.Base, merged.UTC()); err != nil {
return notNow(err)
}
}
owed, err := owedLate(ctx, inv, m, merged, touches)
facts, err := factsOfMerge(ctx, inv, m, now, entries, read)
if err != nil {
return notNow(err)
}
if from, packaging, already := mergeCandidates(m, entries, read); len(from) == 0 && len(packaging) == 0 && len(later) == 0 && !owed {
merged, touches := facts.merged, facts.touches
if !facts.plans() {
already := facts.already
// "Already built from it" and "nothing reads it" are different facts, and reading the first
// as the second sends somebody looking for a broken trigger when the mesh is up to date.
if already > 0 {