╭──────────╮ │← previous│ ╰──────────╯ ╭ changed ────╮ │src/event.zig│ ╰─────────────╯ ╭───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │296 try haxy_moment.put(hash.hashInt(repo_opts.hash, "moment-index"), .{ .uint = try haxy.count() - 1 }); │ │297 │ │298 // consume the event unless it's a merge commit │ │259 - if (!repo_event.is_merge) { │ │299 + if (repo_event.parent_oids.len <= 1) { │ │300 var commit_object = try obj.Object(.xit, repo_opts, .full).init(state.readOnly(), io, allocator, &std.fmt.bytesToHex(repo_event.oid, .lower));│ │301 defer commit_object.deinit(); │ │262 - │ │263 - var arena = std.heap.ArenaAllocator.init(allocator); │ │264 - defer arena.deinit(); │ │302 │ │303 // read the message from the commit │ │304 try commit_object.object_reader.seekTo(commit_object.content.commit.message_position); │ ╰───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭───────────────────────────────────────────────────────────────────────────────────────────────────╮ │351 else => @compileError("upsert expects a struct"), │ │352 } │ │353 } │ │354 + │ │355 +fn mergeChangedMapEntries( │ │356 + comptime DB: type, │ │357 + target: DB.HashMap(.read_write), │ │358 + parent: DB.HashMap(.read_only), │ │359 + baseline: DB.HashMap(.read_only), │ │360 + comptime is_top_level: bool, │ │361 +) !void { │ │362 + var parent_iter = try parent.iterator(); │ │363 + while (try parent_iter.next()) |kv_pair_cursor| { │ │364 + const kv_pair = try kv_pair_cursor.readKeyValuePair(); │ │365 │ │366 + const baseline_value_cursor = try baseline.getCursor(kv_pair.hash) orelse { │ │367 + try target.put(kv_pair.hash, .{ .slot = kv_pair.value_cursor.slot() }); │ │368 + continue; │ │369 + }; │ │370 + │ │371 + if (baseline_value_cursor.slot().value == kv_pair.value_cursor.slot().value) { │ │372 + continue; │ │373 + } │ │374 + │ │375 + if (kv_pair.value_cursor.slot().tag != baseline_value_cursor.slot().tag) { │ │376 + return error.UnexpectedTag; │ │377 + } │ │378 + │ │379 + const tag = kv_pair.value_cursor.slot().tag; │ │380 + │ │381 + if (tag == .hash_map) { │ │382 + const target_child_cursor = try target.putCursor(kv_pair.hash); │ │383 + const target_child = try DB.HashMap(.read_write).init(target_child_cursor); │ │384 + const parent_child = try DB.HashMap(.read_only).init(kv_pair.value_cursor); │ │385 + const baseline_child = try DB.HashMap(.read_only).init(baseline_value_cursor); │ │386 + try mergeChangedMapEntries(DB, target_child, parent_child, baseline_child, false);│ │387 + } else if (is_top_level) { │ │388 + continue; │ │389 + } else { │ │390 + try target.put(kv_pair.hash, .{ .slot = kv_pair.value_cursor.slot() }); │ │391 + } │ │392 + } │ │393 +} │ │394 + │ │395 fn upsertField( │ │396 comptime DB: type, │ │397 comptime hash_kind: hash.HashKind, │ ╰───────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭ changed ─────────╮ │src/test/event.zig│ ╰──────────────────╯ ╭──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │634 try std.testing.expectEqualStrings(events_to_consume2[0].data.issue.tags, first_issue.issue.tags); │ │635 } │ │636 } │ │637 + │ │638 + // │ │639 + // insert issues as commits in the repo │ │640 + // │ │641 + │ │642 + { │ │643 + var json: std.Io.Writer.Allocating = .init(std.testing.allocator); │ │644 + defer json.deinit(); │ │645 + │ │646 + for (events_to_consume, 0..) |event, i| { │ │647 + json.clearRetainingCapacity(); │ │648 + │ │649 + try std.json.Stringify.value(event, .{}, &json.writer); │ │650 + │ │651 + // commit the event into a special branch │ │652 + _ = try repo.commitAtRef( │ │653 + io, │ │654 + allocator, │ │655 + // make the first event have no parents so we completely rebase the branch, │ │656 + // undoing the merge that we did above │ │657 + .{ .parent_oids = if (i == 0) &.{} else null, .message = json.written() }, │ │658 + null, │ │659 + .{ .kind = .head, .name = "haxy/meta" }, │ │660 + ); │ │661 + } │ │662 + } │ │663 + │ │664 + // │ │665 + // consume events into the database │ │666 + // │ │667 + │ │668 + { │ │669 + try evt.consume(repo_opts, io, allocator, &repo, .{ .kind = .head, .name = "haxy/meta" }); │ │670 + │ │671 + const history = try Repo.DB.ArrayList(.read_only).init(repo.core.db.rootCursor().readOnly()); │ │672 + │ │673 + // read the moment we just created │ │674 + const moment_cursor = try history.getCursor(-1) orelse return error.NotFound; │ │675 + const moment = try Repo.DB.HashMap(.read_only).init(moment_cursor); │ │676 + │ │677 + // get the last object id │ │678 + const last_object_id_cursor = try moment.getCursor(hash.hashInt(repo_opts.hash, "haxy-last-object-id")) orelse return error.NotFound; │ │679 + var last_object_id: [hash.byteLen(repo_opts.hash)]u8 = undefined; │ │680 + _ = try last_object_id_cursor.readBytes(&last_object_id); │ │681 + │ │682 + const haxy_cursor = try moment.getCursor(hash.hashInt(repo_opts.hash, "haxy")) orelse return error.NotFound; │ │683 + const haxy = try Repo.DB.ArrayList(.read_only).init(haxy_cursor); │ │684 + │ │685 + try std.testing.expectEqual(1, try haxy.count()); │ │686 + │ │687 + const haxy_moments_cursor = try haxy.getCursor(-1) orelse return error.NotFound; │ │688 + const haxy_moments = try Repo.DB.HashMap(.read_only).init(haxy_moments_cursor); │ │689 + │ │690 + const haxy_moment_cursor = try haxy_moments.getCursor(hash.bytesToInt(repo_opts.hash, &last_object_id)) orelse return error.NotFound; │ │691 + const haxy_moment = try Repo.DB.HashMap(.read_only).init(haxy_moment_cursor); │ │692 + │ │693 + // get the map of issues │ │694 + const event_id_to_issue_cursor = try haxy_moment.getCursor(hash.hashInt(repo_opts.hash, "event-id->issue")) orelse return error.NotFound;│ │695 + const event_id_to_issue = try Repo.DB.HashMap(.read_only).init(event_id_to_issue_cursor); │ │696 + │ │697 + // make sure the new issue is not there │ │698 + { │ │699 + // get the issue out of the map │ │700 + var first_issue_id: [evt.event_id_size]u8 = undefined; │ │701 + _ = try std.fmt.hexToBytes(&first_issue_id, &events_to_consume2[0].id); │ │702 + const first_issue_cursor = try event_id_to_issue.getCursor(hash.hashInt(repo_opts.hash, &first_issue_id)); │ │703 + try std.testing.expect(null == first_issue_cursor); │ │704 + } │ │705 + } │ │706 } │ │707 │ ╰──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯