Merges history lines from several sources into one deduplicated
stream — the aggregation step behind mix temper.merge.
Dedupe is byte-exact on whole trimmed lines: overlapping CI cache restores and re-downloaded artifacts duplicate identical lines, and only those. Callers trim before handing lines in (as the reader trims before decoding), so whitespace-padded copies of one record collapse instead of decoding into double-counted records downstream. Lines are otherwise opaque — a merge rewrites files, so it must not drop what it merely does not interpret. Suite summaries and future schema versions pass through verbatim; only lines the codec calls corrupt (truncated cache tails) are dropped, and counted.
Part of the functional core: lines in, lines out, no side effects.
Summary
Functions
Deduplicates lines, keeping the first occurrence of each.
Types
@type result() :: %{ lines: [String.t()], duplicates: non_neg_integer(), corrupt: non_neg_integer() }
Kept lines in first-seen order, plus counts of what was not kept.
Functions
@spec merge(Enumerable.t()) :: result()
Deduplicates lines, keeping the first occurrence of each.
Lines must arrive trimmed; blank lines are ignored. Output order is input order, which makes a merge deterministic for the same inputs.