╭ changed ────╮ │src/event.zig│ ╰─────────────╯ ╭───────────╮ │← all files│ ╰───────────╯ ╭───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │287 const baseline_haxy_moment_cursor = try haxy_moments.getCursor(hash.bytesToInt(repo_opts.hash, &baseline_oid)) orelse return error.CursorNotFound;│ │288 const baseline_haxy_moment = try DB.HashMap(.read_only).init(baseline_haxy_moment_cursor); │ │289 │ │290 - try mergeChangedMapEntries(DB, haxy_moment, parent_haxy_moment, baseline_haxy_moment, true); │ │290 + try mergeChangedMapEntries(DB, allocator, haxy_moment, parent_haxy_moment, baseline_haxy_moment, true); │ │291 } │ │292 } │ │293 │ ╰───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭──────────────────────────────────────────╮ │566 │ │567 fn mergeChangedMapEntries( │ │568 comptime DB: type, │ │569 + allocator: std.mem.Allocator, │ │570 target: DB.HashMap(.read_write), │ │571 parent: DB.HashMap(.read_only), │ │572 baseline: DB.HashMap(.read_only),│ ╰──────────────────────────────────────────╯ ╭─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │598 if (tag == .hash_map) { │ │599 const target_existing_cursor = try target.getCursor(kv_pair.hash) orelse return error.MergeConflict;│ │600 if (target_existing_cursor.slot().tag != .hash_map) { │ │600 - return error.MergeConflict; │ │601 + return error.UnexpectedTag; │ │602 } │ │603 │ │604 const target_child_cursor = try target.putCursor(kv_pair.hash); │ ╰─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │605 const target_child = try DB.HashMap(.read_write).init(target_child_cursor); │ │606 const parent_child = try DB.HashMap(.read_only).init(kv_pair.value_cursor); │ │607 const baseline_child = try DB.HashMap(.read_only).init(baseline_value_cursor); │ │607 - try mergeChangedMapEntries(DB, target_child, parent_child, baseline_child, false); │ │608 + try mergeChangedMapEntries(DB, allocator, target_child, parent_child, baseline_child, false); │ │609 + } else if (tag == .sorted_set) { │ │610 + const target_existing_cursor = try target.getCursor(kv_pair.hash) orelse return error.MergeConflict;│ │611 + if (target_existing_cursor.slot().tag != .sorted_set) { │ │612 + return error.UnexpectedTag; │ │613 + } │ │614 + │ │615 + const target_child_cursor = try target.putCursor(kv_pair.hash); │ │616 + const target_child = try DB.SortedSet(.read_write).init(target_child_cursor); │ │617 + const parent_child = try DB.SortedSet(.read_only).init(kv_pair.value_cursor); │ │618 + const baseline_child = try DB.SortedSet(.read_only).init(baseline_value_cursor); │ │619 + try mergeChangedSetEntries(DB, allocator, target_child, parent_child, baseline_child); │ │620 } else if (is_top_level) { │ │621 // the moment-index is a top-level key in the haxy moment │ │622 // and is not a map. we don't want to do any merge of this. │ ╰─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭──────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │623 continue; │ │624 } else { │ │625 const target_value_cursor = try target.getCursor(kv_pair.hash) orelse return error.MergeConflict;│ │626 + if (target_value_cursor.slot().tag != baseline_value_cursor.slot().tag) { │ │627 + return error.UnexpectedTag; │ │628 + } │ │629 if (target_value_cursor.slot().value != baseline_value_cursor.slot().value) { │ │630 return error.MergeConflict; │ │631 } │ ╰──────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭───────────────────────────────────────────────────────────────────────────────────╮ │635 } │ │636 } │ │637 │ │638 +// three-way merge of a sorted set: apply the parent's changes relative to the│ │639 +// baseline. keys have no values, so additions and removals can't conflict. │ │640 +fn mergeChangedSetEntries( │ │641 + comptime DB: type, │ │642 + allocator: std.mem.Allocator, │ │643 + target: DB.SortedSet(.read_write), │ │644 + parent: DB.SortedSet(.read_only), │ │645 + baseline: DB.SortedSet(.read_only), │ │646 +) !void { │ │647 + var parent_iter = try parent.iterator(); │ │648 + while (try parent_iter.next()) |kv_pair_cursor| { │ │649 + const kv_pair = try kv_pair_cursor.readKeyValuePair(); │ │650 + const key = try kv_pair.key_cursor.readBytesAlloc(allocator, null); │ │651 + defer allocator.free(key); │ │652 + if (!try baseline.contains(key)) try target.put(key); │ │653 + } │ │654 + │ │655 + var baseline_iter = try baseline.iterator(); │ │656 + while (try baseline_iter.next()) |kv_pair_cursor| { │ │657 + const kv_pair = try kv_pair_cursor.readKeyValuePair(); │ │658 + const key = try kv_pair.key_cursor.readBytesAlloc(allocator, null); │ │659 + defer allocator.free(key); │ │660 + if (!try parent.contains(key)) _ = try target.remove(key); │ │661 + } │ │662 +} │ │663 + │ │664 fn upsertField( │ │665 comptime DB: type, │ │666 comptime hash_kind: hash.HashKind, │ ╰───────────────────────────────────────────────────────────────────────────────────╯