╭─────────────╮ │scroll to top│ ╰─────────────╯ ╭ PatchRev.zig ─────────────────────────────────────────────────╮ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ ╰───────────────────────────────────────────────────────────────╯
╭────╮ ╭ clone ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ╮ │http│ ssh │ │ ╰────╯ ╰ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ╯
27 pub const record_map_key = "event-id->patchrev"; 28 pub const all_id_set_key = "patchrev-id-set"; 29 30 // create the mergeable squash commit represented by this patch revision 31 pub fn writeSquashCommit( 32 comptime repo_kind: rp.RepoKind, 33 comptime repo_opts: rp.RepoOpts(repo_kind), 34 state: rp.Repo(repo_kind, repo_opts).State(.read_write), 35 io: std.Io, 36 allocator: std.mem.Allocator, 37 event: Self, 38 head_tree_oid: *const [hash.hexLen(repo_opts.hash)]u8, 39 author: []const u8, 40 committer: []const u8, 41 timestamp: u64, 42 ) ![hash.hexLen(repo_opts.hash)]u8 { 43 var base_oid: [hash.hexLen(repo_opts.hash)]u8 = undefined; 44 if (event.base_oid.len != base_oid.len) return error.InvalidPatch; 45 @memcpy(&base_oid, event.base_oid); 46 try validateOid(repo_opts.hash, &base_oid); 47 const parent_oids = [_][hash.hexLen(repo_opts.hash)]u8{base_oid}; 48 return try obj.writeCommitWithoutRef(repo_kind, repo_opts, state, io, allocator, .{ 49 .author = author, 50 .committer = committer, 51 .message = event.message, 52 .parent_oids = &parent_oids, 53 .timestamp = timestamp, 54 }, head_tree_oid); 55 } 56 57 pub fn readTrees( 58 comptime repo_kind: rp.RepoKind, 59 comptime repo_opts: rp.RepoOpts(repo_kind), 60 state: rp.Repo(repo_kind, repo_opts).State(.read_only), 61 io: std.Io, 62 allocator: std.mem.Allocator, 63 tree_oid: *const [hash.hexLen(repo_opts.hash)]u8, 64 ) !struct { 65 base: [hash.hexLen(repo_opts.hash)]u8, 66 head: [hash.hexLen(repo_opts.hash)]u8, 67 } { 68 var tree_object = try obj.Object(repo_kind, repo_opts).init(state, io, allocator, tree_oid); 69 defer tree_object.deinit(); 70 71 const entries = switch (tree_object.content) { 72 .tree => |tree| tree.entries, 73 else => return error.InvalidPatch, 74 }; 75 if (entries.count() != 2) return error.InvalidPatch; 76 const base = entries.get("base") orelse return error.InvalidPatch; 77 const head = entries.get("head") orelse return error.InvalidPatch; 78 if (!base.isTree() or !head.isTree()) return error.InvalidPatch; 79 80 const base_oid = std.fmt.bytesToHex(base.oid, .lower); 81 const head_oid = std.fmt.bytesToHex(head.oid, .lower); 82 var base_reader = try obj.ObjectReader(repo_kind, repo_opts).init(state, io, allocator, &base_oid); 83 defer base_reader.deinit(); 84 var head_reader = try obj.ObjectReader(repo_kind, repo_opts).init(state, io, allocator, &head_oid); 85 defer head_reader.deinit(); 86 if (base_reader.header().kind != .tree or head_reader.header().kind != .tree) return error.InvalidPatch; 87 88 return .{ .base = base_oid, .head = head_oid }; 89 } 90 91 pub fn consume( 92 comptime DB: type, 93 comptime hash_kind: hash.HashKind, 94 haxy_moment: DB.HashMap(.read_write), 95 event_id: *const [evt.event_id_size]u8, 96 record_maybe: ?Record, 97 arena: *std.heap.ArenaAllocator, 98 _: ?[]const u8, 99 ) !void { 100 const record_key = hash.hashInt(hash_kind, event_id); 101 const records = try DB.HashMap(.read_write).init(try haxy_moment.putCursor(hash.hashInt(hash_kind, record_map_key))); 102 103 var existing_maybe: ?Record = null; 104 const existing_cursor_maybe = try records.getCursor(record_key); 105 if (existing_cursor_maybe) |cursor| { 106 existing_maybe = try evt.read(Record, DB, hash_kind, arena, try DB.HashMap(.read_only).init(cursor)); 107 } 108 109 var record = record_maybe orelse try evt.removedRecord(Record, DB, hash_kind, haxy_moment.readOnly(), existing_maybe); 110 111 try validateOid(hash_kind, record.event.base_oid); 112 try validateOid(hash_kind, record.event.source_oid); 113 try validateOid(hash_kind, record.event_oid); 114 try validateOid(hash_kind, record.base_tree_oid); 115 try validateOid(hash_kind, record.head_tree_oid); 116 try validateOid(hash_kind, record.patch_oid); 117 118 if (existing_maybe) |existing| { 119 if (!std.mem.eql(u8, existing.event.base_oid, record.event.base_oid) or 120 !std.mem.eql(u8, existing.event.source_oid, record.event.source_oid) or 121 !std.mem.eql(u8, existing.event.message, record.event.message) or 122 !std.mem.eql(u8, existing.base_tree_oid, record.base_tree_oid) or 123 !std.mem.eql(u8, existing.head_tree_oid, record.head_tree_oid) or 124 !std.mem.eql(u8, existing.patch_oid, record.patch_oid)) 125 { 126 return error.PatchRevChanged; 127 } 128 record.created_order = existing.created_order; 129 record.author_email = existing.author_email; 130 record.event_oid = existing.event_oid; 131 } 132 133 const cursor = try records.putCursor(record_key); 134 try evt.upsert(Record, DB, hash_kind, try DB.HashMap(.read_write).init(cursor), record); 135 try evt.indexEvent(DB, hash_kind, haxy_moment, event_id, .patchrev, existing_maybe, record); 136 137 if (existing_cursor_maybe == null) { 138 const ids = try DB.SortedSet(.read_write).init(try haxy_moment.putCursor(hash.hashInt(hash_kind, all_id_set_key))); 139 try ids.put(&evt.orderKeyDesc(record.created_order, event_id)); 140 } 141 } 142 143 pub fn validateOid(comptime hash_kind: hash.HashKind, oid: []const u8) !void { 144 var bytes: [hash.byteLen(hash_kind)]u8 = undefined; 145 _ = try std.fmt.hexToBytes(&bytes, oid); 146 if (!std.mem.eql(u8, oid, &std.fmt.bytesToHex(bytes, .lower))) return error.InvalidOid; 147 } 148 149 pub fn readById( 150 comptime DB: type, 151 comptime hash_kind: hash.HashKind, 152 haxy_moment: DB.HashMap(.read_only), 153 arena: *std.heap.ArenaAllocator, 154 id: *const [evt.event_id_size]u8, 155 ) !?Record { 156 const records_cursor = try haxy_moment.getCursor(hash.hashInt(hash_kind, record_map_key)) orelse return null; 157 const records = try DB.HashMap(.read_only).init(records_cursor); 158 const record_cursor = try records.getCursor(hash.hashInt(hash_kind, id)) orelse return null; 159 return try evt.read(Record, DB, hash_kind, arena, try DB.HashMap(.read_only).init(record_cursor)); 160 } 161 162 pub const WithId = struct { 163 id: [evt.event_id_size]u8, 164 record: Record, 165 }; 166 167 pub fn readNewest( 168 comptime DB: type, 169 comptime hash_kind: hash.HashKind, 170 haxy_moment: DB.HashMap(.read_only), 171 arena: *std.heap.ArenaAllocator, 172 ) !?WithId { 173 const ids_cursor = try haxy_moment.getCursor(hash.hashInt(hash_kind, all_id_set_key)) orelse return null; 174 const ids = try DB.SortedSet(.read_only).init(ids_cursor); 175 var iter = try ids.iteratorFromIndex(0); 176 while (try iter.next()) |cursor| { 177 const id = try evt.readOrderKeyId(DB, cursor); 178 const record = (try readById(DB, hash_kind, haxy_moment, arena, &id)) orelse continue; 179 if (!record.removed) return .{ .id = id, .record = record }; 180 } 181 return null; 182 } 183 184 // squash commits referenced only by patch revision records 185 pub fn gcRoots( 186 comptime DB: type, 187 comptime hash_kind: hash.HashKind, 188 allocator: std.mem.Allocator, 189 haxy_moment: DB.HashMap(.read_only), 190 ) ![][hash.hexLen(hash_kind)]u8 { 191 var roots: std.ArrayList([hash.hexLen(hash_kind)]u8) = .empty; 192 errdefer roots.deinit(allocator); 193 var arena = std.heap.ArenaAllocator.init(allocator); 194 defer arena.deinit(); 195 196 const ids_cursor = try haxy_moment.getCursor(hash.hashInt(hash_kind, all_id_set_key)) orelse return try roots.toOwnedSlice(allocator); 197 const ids = try DB.SortedSet(.read_only).init(ids_cursor); 198 var iter = try ids.iteratorFromIndex(0); 199 while (try iter.next()) |cursor| { 200 _ = arena.reset(.retain_capacity); 201 const id = try evt.readOrderKeyId(DB, cursor); 202 const record = (try readById(DB, hash_kind, haxy_moment, &arena, &id)) orelse continue; 203 var oid: [hash.hexLen(hash_kind)]u8 = undefined; 204 if (record.patch_oid.len != oid.len) return error.InvalidOid; 205 @memcpy(&oid, record.patch_oid); 206 try roots.append(allocator, oid); 207 } 208 209 return try roots.toOwnedSlice(allocator); 210 } 211