╭ changed ────╮ │src/event.zig│ ╰─────────────╯ ╭───────────╮ │← all files│ ╰───────────╯ ╭──────────────────────────────────────────────────────────────╮ │57 /// is rebased and force-pushed. │ │58 pub fn RepoEvent(comptime hash_kind: hash.HashKind) type {│ │59 return struct { │ │60 + parent_oid: ?[hash.byteLen(hash_kind)]u8, │ │61 oid: [hash.byteLen(hash_kind)]u8, │ │62 event: Event, │ │63 }; │ ╰──────────────────────────────────────────────────────────────╯ ╭─────────────────────────────────────────────────────────────────────────────╮ │79 ) !void { │ │80 const Ctx = struct { │ │81 repo_events: []RepoEvent(hash_kind), │ │82 + history_count: u64, │ │83 │ │84 pub fn run(ctx: @This(), cursor: *DB.Cursor(.read_write)) !void {│ │85 const moment = try DB.HashMap(.read_write).init(cursor.*); │ ╰─────────────────────────────────────────────────────────────────────────────╯ ╭──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │ 98 │ │ 99 // for each event we want to consume... │ │100 for (ctx.repo_events) |repo_event| { │ │101 + // map that associates this oid with the current transaction id. │ │102 + // this will be important for reverting things when a rebase occurrs. │ │103 + var object_id_to_tx_id_cursor = try haxy.putCursor(hash.hashInt(hash_kind, "object-id->tx-id")); │ │104 + │ │105 // map with the views as they appeared when each event was consumed. │ │106 // we can use this to see (and revert) the views to any previous state. │ │101 - const object_id_to_views_cursor = try haxy.putCursor(hash.hashInt(hash_kind, "object-id->views")); │ │107 + var object_id_to_views_cursor = try haxy.putCursor(hash.hashInt(hash_kind, "object-id->views")); │ │108 + │ │109 + // if this object id has already been consumed, skip it │ │110 + { │ │111 + const object_id_to_views = try DB.HashMap(.read_only).init(object_id_to_views_cursor.readOnly()); │ │112 + if (null != try object_id_to_views.getCursor(hash.bytesToInt(hash_kind, &repo_event.oid))) { │ │113 + continue; │ │114 + } │ │115 + } │ │116 + │ │117 + // compare last-object-id to the event's parent. this is important │ │118 + // in situations where the branch was rebased, because in that case │ │119 + // the last-object-id may no longer be valid. │ │120 + if (last_object_id_maybe) |*last_object_id| { │ │121 + if (repo_event.parent_oid) |*parent_oid| { │ │122 + if (!std.mem.eql(u8, last_object_id, parent_oid)) { │ │123 + // the last-object-id does not match the current event's parent id. │ │124 + // this means that a rebase occurred. what we need to do is just │ │125 + // revert the two object-id maps to the state they were in when │ │126 + // parent-oid was consumed. │ │127 + │ │128 + const object_id_to_tx_id = try DB.HashMap(.read_only).init(object_id_to_tx_id_cursor.readOnly()); │ │129 + const tx_id_cursor = try object_id_to_tx_id.getCursor(hash.bytesToInt(hash_kind, parent_oid)) orelse return error.ObjectNotFound; │ │130 + const tx_id = try tx_id_cursor.readUint(); │ │131 + const history = try DB.ArrayList(.read_only).init(cursor.db.rootCursor().readOnly()); │ │132 + │ │133 + const old_moment_cursor = try history.getCursor(tx_id) orelse return error.TransactionNotFound; │ │134 + const old_moment = try DB.HashMap(.read_only).init(old_moment_cursor); │ │135 + │ │136 + const old_haxy_cursor = try old_moment.getCursor(hash.hashInt(hash_kind, "haxy")) orelse return error.CursorNotFound; │ │137 + const old_haxy = try DB.HashMap(.read_only).init(old_haxy_cursor); │ │138 + │ │139 + const old_object_id_to_tx_id_cursor = try old_haxy.getCursor(hash.hashInt(hash_kind, "object-id->tx-id")) orelse return error.CursorNotFound;│ │140 + const old_object_id_to_views_cursor = try old_haxy.getCursor(hash.hashInt(hash_kind, "object-id->views")) orelse return error.CursorNotFound;│ │141 + │ │142 + try object_id_to_tx_id_cursor.write(.{ .slot = old_object_id_to_tx_id_cursor.slot() }); │ │143 + try object_id_to_views_cursor.write(.{ .slot = old_object_id_to_views_cursor.slot() }); │ │144 + │ │145 + last_object_id_maybe = parent_oid.*; │ │146 + } │ │147 + } else { │ │148 + // the branch was rebased all the way to the very beginning. │ │149 + // we have a repo event with no parent, which means it is now │ │150 + // the very first event. all we need to do is set the │ │151 + // object-id->views map to be empty so we can rebuild it. │ │152 + try object_id_to_views_cursor.write(.{ .slot = null }); │ │153 + } │ │154 + } else { │ │155 + if (repo_event.parent_oid) |_| { │ │156 + // there is no last-object-id, but this event has a parent. │ │157 + // this is an invalid state. if the event has a parent, that │ │158 + // implies that an event has already been processed, but │ │159 + // if that's true then there would be a last-object-id. │ │160 + return error.UnexpectedParent; │ │161 + } │ │162 + } │ │163 + │ │164 + // associate this object id with this transaction id │ │165 + const object_id_to_tx_id = try DB.HashMap(.read_write).init(object_id_to_tx_id_cursor); │ │166 + try object_id_to_tx_id.put(hash.bytesToInt(hash_kind, &repo_event.oid), .{ .uint = ctx.history_count }); │ │167 + │ │168 + // init the object-id->views map │ │169 const object_id_to_views = try DB.HashMap(.read_write).init(object_id_to_views_cursor); │ │170 │ │171 // create a new views map for the current event we are consuming │ ╰──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │220 │ │221 // create a new transaction in the database that runs the above-defined Ctx function │ │222 const history = try DB.ArrayList(.read_write).init(db.rootCursor()); │ │156 - try history.appendContext(.{ .slot = try history.getSlot(-1) }, Ctx{ .repo_events = repo_events });│ │223 + try history.appendContext(.{ .slot = try history.getSlot(-1) }, Ctx{ │ │224 + .repo_events = repo_events, │ │225 + .history_count = try history.count(), │ │226 + }); │ │227 } │ │228 │ │229 fn upsert( │ ╰────────────────────────────────────────────────────────────────────────────────────────────────────────────╯