╭ changed ────╮ │build.zig.zon│ ╰─────────────╯ ╭─────────────────────────────────────────────────────────────────────────────────────────────────────╮ │ 3 .version = "0.0.0", │ │ 4 .dependencies = .{ │ │ 5 .xit = .{ │ │ 6 - .url = "git+https://github.com/xit-vcs/xit#496405c8208b24cc3ab75b89a2c67e6ba6fbd1f7",│ │ 7 - .hash = "xit-0.0.0-Ub3nDDpKUwH_qx-7x1HiYYaaGcaBQkHfpMlXtDJmj-9p", │ │ 6 + .url = "git+https://github.com/xit-vcs/xit#a594c9e17c97aa3b233e7cbbc9aa666c5f0e3063",│ │ 7 + .hash = "xit-0.0.0-Ub3nDM9TUwErU0debgLgn_KM2Ftq2eWale27GydrEn2J", │ │ 8 }, │ │ 9 }, │ │10 .paths = .{ │ ╰─────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭ changed ────╮ │src/event.zig│ ╰─────────────╯ ╭────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │1368 } │ │1369 } │ │1370 │ │1371 +pub fn readFromRepo( │ │1372 + comptime T: type, │ │1373 + comptime repo_kind: rp.RepoKind, │ │1374 + comptime repo_opts: rp.RepoOpts(repo_kind), │ │1375 + io: std.Io, │ │1376 + allocator: std.mem.Allocator, │ │1377 + arena: *std.heap.ArenaAllocator, │ │1378 + repo: *rp.Repo(repo_kind, repo_opts), │ │1379 + id: *const [event_id_size]u8, │ │1380 +) !?T.Record { │ │1381 + var event_db_maybe: ?LocalEventDB(repo_opts.hash) = if (repo_kind == .git) try LocalEventDB(repo_opts.hash).openReadOnly(io, allocator, repo.core.repo_dir) else null;│ │1382 + defer if (event_db_maybe) |*event_db| event_db.deinit(io, allocator); │ │1383 + const moment = (if (event_db_maybe) |*event_db| │ │1384 + currentMomentFromDb(repo_opts.hash, event_db.db) │ │1385 + else if (repo_kind == .git) │ │1386 + return null │ │1387 + else │ │1388 + currentMoment(repo_opts, repo)) catch return null; │ │1389 + return T.readById(EventDB(repo_opts.hash), repo_opts.hash, moment, arena, id); │ │1390 +} │ │1391 + │ │1392 pub fn currentMoment( │ │1393 comptime repo_opts: rp.RepoOpts(.xit), │ │1394 repo: *rp.Repo(.xit, repo_opts), │ ╰────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭ changed ──────────╮ │src/event/Patch.zig│ ╰───────────────────╯ ╭─────────────────────────────────────────────────────────────────────────────╮ │68 │ │69 pub const Merged = struct { │ │70 revision: MergeRevision, │ │71 - before_oid: []const u8, // all zeroes if the target did not exist│ │72 - after_oid: []const u8, │ │71 + patchrev_id: [evt.event_id_size * 2]u8, │ │72 }; │ │73 │ │74 pub fn kind(self: Status) StatusKind { │ ╰─────────────────────────────────────────────────────────────────────────────╯ ╭────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │182 switch (record.event.status) { │ │183 .open, .closed => {}, │ │184 .merged => |merged| { │ │186 - try evt.PatchRev.validateOid(hash_kind, merged.before_oid); │ │187 - try evt.PatchRev.validateOid(hash_kind, merged.after_oid); │ │185 + const merged_id = try evt.parseEventId(&merged.patchrev_id); │ │186 + const merged_revision = (try evt.PatchRev.readById(DB, hash_kind, haxy_moment.readOnly(), arena, &merged_id)) orelse return error.InvalidPatch;│ │187 + if (merged_revision.removed) return error.InvalidPatch; │ │188 const id = revision_id orelse return error.InvalidPatch; │ │189 const selected = record.event.revision orelse return error.InvalidPatch; │ │190 const revision = (try evt.PatchRev.readById(DB, hash_kind, haxy_moment.readOnly(), arena, &id)) orelse return error.InvalidPatch; │ ╰────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │301 ) !void { │ │302 var arena = std.heap.ArenaAllocator.init(allocator); │ │303 defer arena.deinit(); │ │304 - const record = (try readFromRepo(repo_kind, repo_opts, io, allocator, &arena, repo, id)) orelse return error.NotFound; │ │304 + const record = (try evt.readFromRepo(Self, repo_kind, repo_opts, io, allocator, &arena, repo, id)) orelse return error.NotFound;│ │305 if (record.removed) return error.NotFound; │ │306 │ │307 var updated = record.event; │ ╰─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │406 if (std.mem.eql(u8, field, name)) return true; │ │407 } │ │408 return false; │ │409 -} │ │410 - │ │411 -pub fn readFromRepo( │ │412 - comptime repo_kind: rp.RepoKind, │ │413 - comptime repo_opts: rp.RepoOpts(repo_kind), │ │414 - io: std.Io, │ │415 - allocator: std.mem.Allocator, │ │416 - arena: *std.heap.ArenaAllocator, │ │417 - repo: *rp.Repo(repo_kind, repo_opts), │ │418 - id: *const [evt.event_id_size]u8, │ │419 -) !?Record { │ │420 - var event_db_maybe: ?evt.LocalEventDB(repo_opts.hash) = if (repo_kind == .git) try evt.LocalEventDB(repo_opts.hash).openReadOnly(io, allocator, repo.core.repo_dir) else null;│ │421 - defer if (event_db_maybe) |*event_db| event_db.deinit(io, allocator); │ │422 - const moment = (if (event_db_maybe) |*event_db| │ │423 - evt.currentMomentFromDb(repo_opts.hash, event_db.db) │ │424 - else if (repo_kind == .git) │ │425 - return null │ │426 - else │ │427 - evt.currentMoment(repo_opts, repo)) catch return null; │ │428 - return readById(evt.EventDB(repo_opts.hash), repo_opts.hash, moment, arena, id); │ │409 } │ │410 │ │411 fn revisionKey(allocator: std.mem.Allocator, target_branch: []const u8, oid: []const u8) ![]u8 { │ ╰───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭ changed ─────────────╮ │src/event/PatchRev.zig│ ╰──────────────────────╯ ╭────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │44 timestamp: u64, │ │45 ) !struct { event: evt.EventWithId, revision: evt.Patch.Revision } { │ │46 const allocator = arena.allocator(); │ │47 - var base_object = try obj.Object(repo_kind, repo_opts).initCommit(state.readOnly(), io, allocator, base_oid); │ │48 - defer base_object.deinit(); │ │47 + const base_tree = if (std.mem.allEqual(u8, base_oid, '0')) blk: { │ │48 + // a merge into a newly created branch starts from an empty tree │ │49 + var reader = std.Io.Reader.fixed(""); │ │50 + var oid: [hash.byteLen(repo_opts.hash)]u8 = undefined; │ │51 + try obj.writeObject(repo_kind, repo_opts, state, io, allocator, &reader, .{ .kind = .tree, .size = 0 }, &oid); │ │52 + break :blk std.fmt.bytesToHex(oid, .lower); │ │53 + } else blk: { │ │54 + var base_object = try obj.Object(repo_kind, repo_opts).initCommit(state.readOnly(), io, allocator, base_oid); │ │55 + defer base_object.deinit(); │ │56 + break :blk base_object.content.commit.tree; │ │57 + }; │ │58 var source_object = try obj.Object(repo_kind, repo_opts).initCommit(state.readOnly(), io, allocator, source_oid); │ │59 defer source_object.deinit(); │ │60 const revision = Self{ .base_oid = try allocator.dupe(u8, base_oid), .source_oid = try allocator.dupe(u8, source_oid), .message = message };│ ╰────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │65 io.random(&id); │ │66 const id_hex = std.fmt.bytesToHex(id, .lower); │ │67 const entries = try allocator.alloc(evt.EventTreeEntry, 2); │ │59 - entries[0] = .{ .tree = .{ .name = "base", .oid = try allocator.dupe(u8, &base_object.content.commit.tree) } }; │ │68 + entries[0] = .{ .tree = .{ .name = "base", .oid = try allocator.dupe(u8, &base_tree) } }; │ │69 entries[1] = .{ .tree = .{ .name = "head", .oid = try allocator.dupe(u8, &source_object.content.commit.tree) } }; │ │70 return .{ │ │71 .event = .{ .id = id_hex, .author = author, .timestamp = timestamp, .tree_entries = entries, .event = .{ .patchrev = revision } },│ ╰──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭─────────────────────────────────────────────────────────────────────────────────────────────╮ │134 .author = author, │ │135 .committer = committer, │ │136 .message = event.message, │ │128 - .parent_oids = &parent_oids, │ │137 + .parent_oids = if (std.mem.allEqual(u8, &base_oid, '0')) &.{} else &parent_oids,│ │138 .timestamp = timestamp, │ │139 }, head_tree_oid); │ │140 } │ ╰─────────────────────────────────────────────────────────────────────────────────────────────╯ ╭ changed ────╮ │src/patch.zig│ ╰─────────────╯ ╭────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │26 defer arena.deinit(); │ │27 if (repo_kind == .git) { │ │28 if (expected_patch) |expected| { │ │29 - const current = (try evt.Patch.readFromRepo(repo_kind, repo_opts, io, allocator, &arena, repo, &try evt.parseEventId(&id))) orelse return error.PatchOutOfDate; │ │29 + const current = (try evt.readFromRepo(evt.Patch, repo_kind, repo_opts, io, allocator, &arena, repo, &try evt.parseEventId(&id))) orelse return error.PatchOutOfDate;│ │30 if (current.removed or !evt.fieldEqual(evt.Patch, current.event, expected)) return error.PatchOutOfDate; │ │31 } │ │32 const events = try branchEvents(repo_kind, repo_opts, .{ .core = &repo.core, .extra = .{} }, io, &arena, id, patch, author); │ ╰────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭──────╮ │next →│ ╰──────╯