╭ changed ────╮ │src/event.zig│ ╰─────────────╯ ╭───────────╮ │← all files│ ╰───────────╯ ╭──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │232 │ │233 var arena = std.heap.ArenaAllocator.init(allocator); │ │234 defer arena.deinit(); │ │235 - │ │236 - // compute a list of events that haven't been consumed yet │ │237 - const RepoEvent = struct { │ │238 - parent_oids: []const [hash.hexLen(repo_opts.hash)]u8, │ │239 - oid: [hash.byteLen(repo_opts.hash)]u8, │ │240 - }; │ │241 - var repo_events: std.ArrayList(RepoEvent) = .empty; │ │242 - defer repo_events.deinit(allocator); │ │243 - { │ │244 - var commit_iter = try obj.ObjectIterator(.xit, repo_opts, .full).init(state.readOnly(), io, allocator, .{ .kind = .commit });│ │245 - defer commit_iter.deinit(); │ │246 - │ │247 - const head_oid = (try rf.readRecur(.xit, repo_opts, state.readOnly(), io, .{ .ref = ref })) orelse return error.OidNotFound; │ │248 - try commit_iter.include(&head_oid); │ │249 - │ │250 - // walk the commits and add all new events we haven't consumed yet. │ │251 - // the `next` call is using the arena because we need the parent_oids │ │252 - // slice to live beyond this scope. │ │253 - // TODO: `repo_events` could in theory contain an unbounded number │ │254 - // of events, so there is an OOM risk here. we definitely need to │ │255 - // guard against this. │ │256 - while (try commit_iter.next(arena.allocator())) |commit_object| { │ │257 - var oid: [hash.byteLen(repo_opts.hash)]u8 = undefined; │ │258 - _ = try std.fmt.hexToBytes(&oid, &commit_object.oid); │ │259 - │ │260 - // if this object id has already been consumed, prune its ancestry from │ │261 - // the iterator. other queued paths, such as the second parent of a │ │262 - // merge commit, can still be walked until they hit a consumed commit │ │263 - // or run out of history. │ │264 - if (null != try haxy_moments.getCursor(hash.bytesToInt(repo_opts.hash, &oid))) { │ │265 - try commit_iter.exclude(&commit_object.oid); │ │266 - continue; │ │267 - } │ │235 │ │269 - const parent_oids = commit_object.content.commit.metadata.parent_oids orelse return error.ParentOidsNotFound; │ │270 - try repo_events.append(allocator, .{ .parent_oids = parent_oids, .oid = oid }); │ │271 - } │ │272 - } │ │236 + var repo_event_iter = try RepoEventIterator(repo_opts).init(state.readOnly(), io, allocator, haxy_moments.readOnly(), ref); │ │237 + defer repo_event_iter.deinit(io, allocator); │ │238 │ │239 // if there are no events to process, look at the oid at HEAD and update │ │240 // the last_object_id to point to it. this is important in situations │ ╰──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │241 // where we do a merge and then force-push to remove the merge. see the │ │242 // merge test for an example. │ │278 - if (repo_events.items.len == 0) { │ │243 + if (repo_event_iter.newest_object_id == null) { │ │244 const head_oid_hex = (try rf.readRecur(.xit, repo_opts, state.readOnly(), io, .{ .ref = ref })) orelse return error.OidNotFound;│ │245 var head_oid: [hash.byteLen(repo_opts.hash)]u8 = undefined; │ │246 _ = try std.fmt.hexToBytes(&head_oid, &head_oid_hex); │ ╰─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭───────────────────────────────────────────────────────────────────────────────────────────────────────╮ │262 // we consumed. if it isn't, then we know the haxy state needs to be reverted. │ │263 if (last_object_id_maybe) |*last_object_id| { │ │264 var is_rebased = false; │ │265 + const newest_object_id = repo_event_iter.newest_object_id orelse return error.OidNotFound;│ │266 │ │267 _ = mrg.getDescendent( │ │268 .xit, │ ╰───────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭───────────────────────────────────────────────────────────────────────╮ │271 io, │ │272 allocator, │ │273 &std.fmt.bytesToHex(last_object_id, .lower), │ │308 - &std.fmt.bytesToHex(repo_events.items[0].oid, .lower),│ │274 + &std.fmt.bytesToHex(&newest_object_id, .lower), │ │275 ) catch |err| switch (err) { │ │276 error.DescendentNotFound => is_rebased = true, │ │277 else => |e| return e, │ ╰───────────────────────────────────────────────────────────────────────╯ ╭─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │278 }; │ │279 │ │280 if (is_rebased) { │ │315 - const parent_oids = repo_events.items[repo_events.items.len - 1].parent_oids; │ │281 + const oldest_object_id = repo_event_iter.oldest_object_id orelse return error.OidNotFound; │ │282 + var oldest_object = try obj.Object(.xit, repo_opts, .full).init(state.readOnly(), io, allocator, &std.fmt.bytesToHex(oldest_object_id, .lower));│ │283 + defer oldest_object.deinit(); │ │284 + │ │285 + const parent_oids = oldest_object.content.commit.metadata.parent_oids orelse return error.ParentOidsNotFound; │ │286 switch (parent_oids.len) { │ │287 0 => { │ │288 // the branch was rebased all the way to the very beginning. │ ╰─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭─────────────────────────────────────────────────────────────────────────────────────╮ │320 }, │ │321 else => return error.UnexpectedParentCount, │ │322 } │ │353 - } │ │354 - } else { │ │355 - if (repo_events.items[repo_events.items.len - 1].parent_oids.len != 0) {│ │356 - // there is no last_object_id, but this event has a parent. │ │357 - // this is an invalid state. if the event has a parent, that │ │358 - // implies that an event has already been processed, but │ │359 - // if that's true then there would be a last_object_id. │ │360 - return error.UnexpectedParent; │ │323 } │ │324 } │ │325 │ ╰─────────────────────────────────────────────────────────────────────────────────────╯ ╭───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │364 - // iterate over the events in reverse order │ │365 - // (they are sorted by most recent events first) │ │366 - for (0..repo_events.items.len) |i| { │ │367 - const repo_event = repo_events.items[repo_events.items.len - i - 1]; │ │326 + while (try repo_event_iter.next()) |repo_event_oid| { │ │327 + var commit_object = try obj.Object(.xit, repo_opts, .full).init(state.readOnly(), io, allocator, &std.fmt.bytesToHex(repo_event_oid, .lower));│ │328 + defer commit_object.deinit(); │ │329 + │ │330 + const parent_oids = commit_object.content.commit.metadata.parent_oids orelse return error.ParentOidsNotFound; │ │331 │ │332 // create a moment for this object id │ │370 - var haxy_moment_cursor = try haxy_moments.putCursor(hash.bytesToInt(repo_opts.hash, &repo_event.oid)); │ │333 + var haxy_moment_cursor = try haxy_moments.putCursor(hash.bytesToInt(repo_opts.hash, &repo_event_oid)); │ │334 │ │335 // if there was a previous object id, set this haxy moment's initial value to it. │ │336 // this efficiently "clones" the map so we make further modifications based on it. │ ╰───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │374 - if (repo_event.parent_oids.len > 0) { │ │337 + if (parent_oids.len > 0) { │ │338 var first_parent_oid: [hash.byteLen(repo_opts.hash)]u8 = undefined; │ │376 - _ = try std.fmt.hexToBytes(&first_parent_oid, &repo_event.parent_oids[0]); │ │339 + _ = try std.fmt.hexToBytes(&first_parent_oid, &parent_oids[0]); │ │340 │ │341 const first_parent_haxy_moment_cursor = try haxy_moments.getCursor(hash.bytesToInt(repo_opts.hash, &first_parent_oid)) orelse return error.CursorNotFound;│ │342 try haxy_moment_cursor.write(.{ .slot = first_parent_haxy_moment_cursor.slot() }); │ ╰───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭───────────────────────────────────────────────────────────────────────────────────╮ │347 // merge changes from every parent after the first parent. the common │ │348 // ancestor is the baseline, so a later parent only contributes values│ │349 // it changed relative to the merge base. │ │387 - if (repo_event.parent_oids.len > 1) { │ │388 - for (repo_event.parent_oids[1..]) |*parent_oid| { │ │350 + if (parent_oids.len > 1) { │ │351 + for (parent_oids[1..]) |*parent_oid| { │ │352 var oid: [hash.byteLen(repo_opts.hash)]u8 = undefined; │ │353 _ = try std.fmt.hexToBytes(&oid, parent_oid); │ │354 │ ╰───────────────────────────────────────────────────────────────────────────────────╯ ╭───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │355 const parent_haxy_moment_cursor = try haxy_moments.getCursor(hash.bytesToInt(repo_opts.hash, &oid)) orelse return error.CursorNotFound; │ │356 const parent_haxy_moment = try DB.HashMap(.read_only).init(parent_haxy_moment_cursor); │ │357 │ │395 - const baseline_oid_hex = try mrg.commonAncestor(.xit, repo_opts, state.readOnly(), io, allocator, &repo_event.parent_oids[0], parent_oid); │ │358 + const baseline_oid_hex = try mrg.commonAncestor(.xit, repo_opts, state.readOnly(), io, allocator, &parent_oids[0], parent_oid); │ │359 var baseline_oid: [hash.byteLen(repo_opts.hash)]u8 = undefined; │ │360 _ = try std.fmt.hexToBytes(&baseline_oid, &baseline_oid_hex); │ │361 const baseline_haxy_moment_cursor = try haxy_moments.getCursor(hash.bytesToInt(repo_opts.hash, &baseline_oid)) orelse return error.CursorNotFound;│ ╰───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭──────╮ │next →│ ╰──────╯