╭─────────────╮ │scroll to top│ ╰─────────────╯ ╭ Patch.zig ────────────────────────────────────────────────────╮ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ ╰───────────────────────────────────────────────────────────────╯
╭ find file ─ ─ ─ ─ ─ ─ ─ ─ ─ ─╮ ╭────╮ ╭ clone ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─╮ │ │ │http│ ssh │ │ ╰ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─╯ ╰────╯ ╰ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─╯
318 defer arena.deinit(); 319 const record = (try evt.readFromRepo(Self, repo_kind, repo_opts, io, allocator, &arena, repo, id)) orelse return error.NotFound; 320 if (record.removed) return error.NotFound; 321 322 var updated = record.event; 323 switch (change) { 324 .status => |status| { 325 if (updated.status.kind() == status) return; 326 updated.status = switch (status) { 327 .open => .open, 328 .closed => .closed, 329 .merged => return error.InvalidPatchStatus, 330 }; 331 // a closed patch may have missed source branch updates 332 if (status == .open and updated.source_branch != null) updated.revision = null; 333 }, 334 .fields => |fields| { 335 if (!branchValid(fields.target_branch)) return error.InvalidTargetBranch; 336 if ((try repo.readRef(io, .{ .kind = .head, .name = fields.target_branch })) == null) return error.InvalidTargetBranch; 337 updated.title = fields.title; 338 updated.labels = fields.labels; 339 updated.description = fields.description; 340 if (!std.mem.eql(u8, updated.target_branch, fields.target_branch)) updated.revision = null; 341 updated.target_branch = fields.target_branch; 342 }, 343 .resolve => |resolve| { 344 updated = try resolveFields(repo_kind, repo_opts, io, allocator, &arena, repo, id, record, resolve); 345 }, 346 } 347 if (!fieldsValid(updated.title, updated.labels)) return error.InvalidFields; 348 349 if (updated.source_branch != null and updated.revision == null) return @import("../patch.zig").writeBranchPatch(host, repo_kind, repo_opts, io, allocator, repo, std.fmt.bytesToHex(id.*, .lower), updated, record.event, author); 350 try evt.consume(host, .repo, repo_kind, repo_opts, io, allocator, repo, evt.events_ref, &.{.{ 351 .id = std.fmt.bytesToHex(id.*, .lower), 352 .timestamp = @intCast(std.Io.Timestamp.now(io, .real).toSeconds()), 353 .author = author, 354 .event = .{ .patch = updated }, 355 }}); 356 } 357 358 fn resolveFields( 359 comptime repo_kind: rp.RepoKind, 360 comptime repo_opts: rp.RepoOpts(repo_kind), 361 io: std.Io, 362 allocator: std.mem.Allocator, 363 arena: *std.heap.ArenaAllocator, 364 repo: *rp.Repo(repo_kind, repo_opts), 365 id: *const [evt.event_id_size]u8, 366 live: Record, 367 resolve: Resolve, 368 ) !Self { 369 const DB = evt.EventDB(repo_opts.hash); 370 var updated = live.event; 371 if (resolve.title) |title| updated.title = title; 372 if (resolve.labels) |labels| updated.labels = labels; 373 374 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; 375 defer if (event_db_maybe) |*event_db| event_db.deinit(io, allocator); 376 const moment = (if (event_db_maybe) |*event_db| 377 evt.currentMomentFromDb(repo_opts.hash, event_db.db) 378 else if (repo_kind == .git) 379 return updated 380 else 381 evt.currentMoment(repo_opts, repo)) catch return updated; 382 const conflicts_cursor = (try moment.getCursor(hash.hashInt(repo_opts.hash, conflicts_key))) orelse return updated; 383 const conflicts = try DB.SortedMap(.read_only).init(conflicts_cursor); 384 const conflict_cursor = (try conflicts.getCursor(&evt.orderKeyDesc(live.created_order, id))) orelse return updated; 385 const entry = try DB.HashMap(.read_only).init(conflict_cursor); 386 const fields_cursor = (try entry.getCursor(hash.hashInt(repo_opts.hash, evt.conflicted_fields_key))) orelse return updated; 387 const fields = try fields_cursor.readBytesAlloc(arena.allocator(), null); 388 const their_cursor = (try entry.getCursor(hash.hashInt(repo_opts.hash, evt.their_record_key))) orelse return updated; 389 const theirs = try evt.read(Record, DB, repo_opts.hash, arena, try DB.HashMap(.read_only).init(their_cursor)); 390 391 const original_target_branch = updated.target_branch; 392 var field_iter = std.mem.splitScalar(u8, fields, ' '); 393 while (field_iter.next()) |field| { 394 if (std.mem.eql(u8, field, "status") and fieldListed(resolve.theirs, ',', field)) updated.status = theirs.event.status; 395 if (std.mem.eql(u8, field, "revision") and fieldListed(resolve.theirs, ',', field)) updated.revision = theirs.event.revision; 396 if (std.mem.eql(u8, field, "target_branch") and fieldListed(resolve.theirs, ',', field)) updated.target_branch = theirs.event.target_branch; 397 } 398 if (!std.mem.eql(u8, original_target_branch, updated.target_branch)) updated.revision = null; 399 400 if (!fieldListed(fields, ' ', "description")) return updated; 401 var base_description: []const u8 = ""; 402 if (try entry.getCursor(hash.hashInt(repo_opts.hash, evt.base_record_key))) |base_cursor| { 403 const base = try evt.read(Record, DB, repo_opts.hash, arena, try DB.HashMap(.read_only).init(base_cursor)); 404 base_description = base.event.description; 405 } 406 const chunks = try diff3.chunks(allocator, arena, base_description, live.event.description, theirs.event.description); 407 var resolutions: std.ArrayList([]const u8) = .empty; 408 var hunk_index: usize = 0; 409 for (chunks) |chunk| { 410 if (chunk != .conflict) continue; 411 try resolutions.append(arena.allocator(), if (hunk_index < resolve.hunks.len) resolve.hunks[hunk_index] else (chunk.conflict.ours orelse "")); 412 hunk_index += 1; 413 } 414 updated.description = try diff3.assemble(arena.allocator(), chunks, resolutions.items); 415 return updated; 416 } 417 418 fn fieldListed(fields: []const u8, delimiter: u8, name: []const u8) bool { 419 var iter = std.mem.splitScalar(u8, fields, delimiter); 420 while (iter.next()) |field| { 421 if (std.mem.eql(u8, field, name)) return true; 422 } 423 return false; 424 } 425 426 fn revisionKey(allocator: std.mem.Allocator, target_branch: []const u8, oid: []const u8) ![]u8 { 427 return std.mem.concat(allocator, u8, &.{ target_branch, "\x00", oid }); 428 } 429 430 fn statusSet( 431 comptime DB: type, 432 statuses: DB.SortedMap(.read_write), 433 status: StatusKind, 434 ) !DB.SortedSet(.read_write) { 435 return DB.SortedSet(.read_write).init(try statuses.putCursor(@tagName(status))); 436 } 437 438 fn removeFromLabelSets( 439 comptime DB: type, 440 label_statuses: DB.SortedMap(.read_write), 441 labels: []const u8, 442 status: StatusKind, 443 order_key: []const u8, 444 ) !void { 445 var label_iter = labelIterator(labels); 446 while (label_iter.next()) |label| { 447 if (label.len == 0 or label.len > label_max_len) continue; 448 var key_buffer: LabelStatusKey = undefined; 449 const key = try labelStatusKey(&key_buffer, label, status); 450 const set = try DB.SortedSet(.read_write).init(try label_statuses.putCursor(key)); 451 _ = try set.remove(order_key); 452 if (0 == try set.count()) _ = try label_statuses.remove(key); 453 } 454 } 455 456 pub fn readById( 457 comptime DB: type, 458 comptime hash_kind: hash.HashKind, 459 haxy_moment: DB.HashMap(.read_only), 460 arena: *std.heap.ArenaAllocator, 461 id: *const [evt.event_id_size]u8, 462 ) !?Record { 463 const records_cursor = try haxy_moment.getCursor(hash.hashInt(hash_kind, record_map_key)) orelse return null; 464 const records = try DB.HashMap(.read_only).init(records_cursor); 465 const record_cursor = try records.getCursor(hash.hashInt(hash_kind, id)) orelse return null; 466 return try evt.read(Record, DB, hash_kind, arena, try DB.HashMap(.read_only).init(record_cursor)); 467 } 468