╭ changed ─────────────╮ │src/event/PatchRev.zig│ ╰──────────────────────╯ ╭───────────╮ │← all files│ ╰───────────╯ ╭──────────────────────────────────╮ │19 base_tree_oid: []const u8,│ │20 head_tree_oid: []const u8,│ │21 patch_oid: []const u8, │ │22 + commit_count: ?u64 = null,│ │23 }; │ │24 │ │25 const Self = @This(); │ ╰──────────────────────────────────╯ ╭─────────────────────────────────────────────────────────────────────────────╮ │27 pub const merge_policy: evt.MergePolicy = .target_wins; │ │28 pub const record_map_key = "event-id->patchrev"; │ │29 pub const all_id_set_key = "patchrev-id-set"; │ │30 + │ │31 +const commit_count_limit = 256; │ │32 │ │33 // prepare a revision event and its patch reference in the caller's arena│ │34 pub fn prepare( │ ╰─────────────────────────────────────────────────────────────────────────────╯ ╭───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │ 60 entries[1] = .{ .tree = .{ .name = "head", .oid = try allocator.dupe(u8, &source_object.content.commit.tree) } }; │ │ 61 return .{ │ │ 62 .event = .{ .id = id_hex, .author = author, .timestamp = timestamp, .tree_entries = entries, .event = .{ .patchrev = revision } },│ │ 60 - .revision = .{ .id = id_hex, .source_oid = revision.source_oid, .squash_oid = try allocator.dupe(u8, &squash) }, │ │ 63 + .revision = .{ │ │ 64 + .id = id_hex, │ │ 65 + .source_oid = revision.source_oid, │ │ 66 + .squash_oid = try allocator.dupe(u8, &squash), │ │ 67 + .commit_count = commitCount(repo_kind, repo_opts, state.readOnly(), io, allocator, base_oid, source_oid) catch null, │ │ 68 + }, │ │ 69 }; │ │ 70 +} │ │ 71 + │ │ 72 +// only show the depth difference if a short first-parent walk reaches the base │ │ 73 +pub fn commitCount( │ │ 74 + comptime repo_kind: rp.RepoKind, │ │ 75 + comptime repo_opts: rp.RepoOpts(repo_kind), │ │ 76 + state: rp.Repo(repo_kind, repo_opts).State(.read_only), │ │ 77 + io: std.Io, │ │ 78 + allocator: std.mem.Allocator, │ │ 79 + base_oid: []const u8, │ │ 80 + source_oid: []const u8, │ │ 81 +) !?u64 { │ │ 82 + switch (repo_kind) { │ │ 83 + .git => return null, │ │ 84 + .xit => { │ │ 85 + const oid_len = comptime hash.hexLen(repo_opts.hash); │ │ 86 + if (base_oid.len != oid_len or source_oid.len != oid_len) return error.InvalidOid; │ │ 87 + const base_count = try state.commitCount(io, allocator, .{ .oid = base_oid[0..oid_len] }); │ │ 88 + const source_count = try state.commitCount(io, allocator, .{ .oid = source_oid[0..oid_len] }); │ │ 89 + const count = std.math.sub(u64, source_count, base_count) catch return null; │ │ 90 + if (count > commit_count_limit) return null; │ │ 91 + │ │ 92 + // depth subtraction is only valid if the base is on the source's first-parent chain │ │ 93 + var oid = source_oid[0..oid_len].*; │ │ 94 + var remaining = count; │ │ 95 + while (remaining > 0) : (remaining -= 1) { │ │ 96 + var object = try obj.Object(.xit, repo_opts).initCommit(state, io, allocator, &oid); │ │ 97 + defer object.deinit(); │ │ 98 + const parent = object.content.commit.metadata.firstParent() orelse return null; │ │ 99 + oid = parent.*; │ │100 + } │ │101 + return if (std.mem.eql(u8, &oid, base_oid)) count else null; │ │102 + }, │ │103 + } │ │104 } │ │105 │ │106 // create the mergeable squash commit represented by this patch revision │ ╰───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯