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