╭─────────────╮ │scroll to top│ ╰─────────────╯ ╭ PatchRev.zig ─────────────────────────────────────────────────╮ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ ╰───────────────────────────────────────────────────────────────╯
╭────╮ ╭ clone ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ╮ │http│ ssh │ │ ╰────╯ ╰ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ╯
184 ) !void { 185 const record_key = hash.hashInt(hash_kind, event_id); 186 const records = try DB.HashMap(.read_write).init(try haxy_moment.putCursor(hash.hashInt(hash_kind, record_map_key))); 187 188 var existing_maybe: ?Record = null; 189 const existing_cursor_maybe = try records.getCursor(record_key); 190 if (existing_cursor_maybe) |cursor| { 191 existing_maybe = try evt.read(Record, DB, hash_kind, arena, try DB.HashMap(.read_only).init(cursor)); 192 } 193 194 var record = record_maybe orelse try evt.removedRecord(Record, DB, hash_kind, haxy_moment.readOnly(), existing_maybe); 195 196 try validateOid(hash_kind, record.event.base_oid); 197 try validateOid(hash_kind, record.event.source_oid); 198 try validateOid(hash_kind, record.event_oid); 199 try validateOid(hash_kind, record.base_tree_oid); 200 try validateOid(hash_kind, record.head_tree_oid); 201 try validateOid(hash_kind, record.patch_oid); 202 203 if (existing_maybe) |existing| { 204 if (!std.mem.eql(u8, existing.event.base_oid, record.event.base_oid) or 205 !std.mem.eql(u8, existing.event.source_oid, record.event.source_oid) or 206 !std.mem.eql(u8, existing.event.message, record.event.message) or 207 !std.mem.eql(u8, existing.base_tree_oid, record.base_tree_oid) or 208 !std.mem.eql(u8, existing.head_tree_oid, record.head_tree_oid) or 209 !std.mem.eql(u8, existing.patch_oid, record.patch_oid)) 210 { 211 return error.PatchRevChanged; 212 } 213 record.created_order = existing.created_order; 214 record.author_email = existing.author_email; 215 record.event_oid = existing.event_oid; 216 } 217 218 const cursor = try records.putCursor(record_key); 219 try evt.upsert(Record, DB, hash_kind, try DB.HashMap(.read_write).init(cursor), record); 220 try evt.indexEvent(DB, hash_kind, haxy_moment, event_id, .patchrev, existing_maybe, record); 221 222 if (existing_cursor_maybe == null) { 223 const ids = try DB.SortedSet(.read_write).init(try haxy_moment.putCursor(hash.hashInt(hash_kind, all_id_set_key))); 224 try ids.put(&evt.orderKeyDesc(record.created_order, event_id)); 225 } 226 } 227 228 pub fn validateOid(comptime hash_kind: hash.HashKind, oid: []const u8) !void { 229 if (oid.len != hash.hexLen(hash_kind)) return error.InvalidOid; 230 var bytes: [hash.byteLen(hash_kind)]u8 = undefined; 231 _ = try std.fmt.hexToBytes(&bytes, oid); 232 if (!std.mem.eql(u8, oid, &std.fmt.bytesToHex(bytes, .lower))) return error.InvalidOid; 233 } 234 235 pub fn readById( 236 comptime DB: type, 237 comptime hash_kind: hash.HashKind, 238 haxy_moment: DB.HashMap(.read_only), 239 arena: *std.heap.ArenaAllocator, 240 id: *const [evt.event_id_size]u8, 241 ) !?Record { 242 const records_cursor = try haxy_moment.getCursor(hash.hashInt(hash_kind, record_map_key)) orelse return null; 243 const records = try DB.HashMap(.read_only).init(records_cursor); 244 const record_cursor = try records.getCursor(hash.hashInt(hash_kind, id)) orelse return null; 245 return try evt.read(Record, DB, hash_kind, arena, try DB.HashMap(.read_only).init(record_cursor)); 246 } 247 248 pub const WithId = struct { 249 id: [evt.event_id_size]u8, 250 record: Record, 251 }; 252 253 pub fn readNewest( 254 comptime DB: type, 255 comptime hash_kind: hash.HashKind, 256 haxy_moment: DB.HashMap(.read_only), 257 arena: *std.heap.ArenaAllocator, 258 ) !?WithId { 259 const ids_cursor = try haxy_moment.getCursor(hash.hashInt(hash_kind, all_id_set_key)) orelse return null; 260 const ids = try DB.SortedSet(.read_only).init(ids_cursor); 261 var iter = try ids.iteratorFromIndex(0); 262 while (try iter.next()) |cursor| { 263 const id = try evt.readOrderKeyId(DB, cursor); 264 const record = (try readById(DB, hash_kind, haxy_moment, arena, &id)) orelse continue; 265 if (!record.removed) return .{ .id = id, .record = record }; 266 } 267 return null; 268 } 269 270 // squash commits referenced only by patch revision records 271 pub fn gcRoots( 272 comptime DB: type, 273 comptime hash_kind: hash.HashKind, 274 allocator: std.mem.Allocator, 275 haxy_moment: DB.HashMap(.read_only), 276 ) ![][hash.hexLen(hash_kind)]u8 { 277 var roots: std.ArrayList([hash.hexLen(hash_kind)]u8) = .empty; 278 errdefer roots.deinit(allocator); 279 var arena = std.heap.ArenaAllocator.init(allocator); 280 defer arena.deinit(); 281 282 const ids_cursor = try haxy_moment.getCursor(hash.hashInt(hash_kind, all_id_set_key)) orelse return try roots.toOwnedSlice(allocator); 283 const ids = try DB.SortedSet(.read_only).init(ids_cursor); 284 var iter = try ids.iteratorFromIndex(0); 285 while (try iter.next()) |cursor| { 286 _ = arena.reset(.retain_capacity); 287 const id = try evt.readOrderKeyId(DB, cursor); 288 const record = (try readById(DB, hash_kind, haxy_moment, &arena, &id)) orelse continue; 289 var oid: [hash.hexLen(hash_kind)]u8 = undefined; 290 if (record.patch_oid.len != oid.len) return error.InvalidOid; 291 @memcpy(&oid, record.patch_oid); 292 try roots.append(allocator, oid); 293 } 294 295 return try roots.toOwnedSlice(allocator); 296 } 297