╭ changed ────────╮ │src/serve_ssh.zig│ ╰─────────────────╯ ╭───────────╮ │← all files│ ╰───────────╯ ╭───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │489 const fork_record = (try evt.readForkById(io, allocator, &admin_arena, handler.server.users_dir, &forker_id, &fork_id)) orelse│ │490 return writeError(sess, "patch draft not found"); │ │491 if (fork_record.removed) return writeError(sess, "patch draft not found"); │ │492 + // the url keeps the name the fork was made under, even after a rename │ │493 + if (!std.mem.eql(u8, fork_record.event.repo_name, forker_repo.name)) return writeError(sess, "patch draft not found"); │ │494 const user = (try evt.User.readById(evt.AdminDB, evt.admin_repo_opts.hash, admin_moment, &admin_arena, &forker_id)) orelse │ │495 return writeError(sess, "invalid patch draft"); │ │496 if (user.removed) return writeError(sess, "invalid patch draft"); │ ╰───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │499 │ │500 const target_owner_id = try evt.parseEventId(&fork_record.event.repo_user_id); │ │501 const target_id = try evt.parseEventId(&fork_record.event.repo_id); │ │500 - const target = (try evt.readRepoById(io, allocator, &admin_arena, handler.server.users_dir, &target_owner_id, &target_id, forker_id)) orelse│ │501 - return writeError(sess, "repo not found"); │ │502 - // patches that are off take their drafts with them │ │503 - const patch_role = target.repo.event.patch_role orelse return writeError(sess, "patch draft not found"); │ │504 - if (service == .receive_pack) { │ │505 - const role = target.role orelse return writeError(sess, "repo not found"); │ │506 - if (!role.atLeast(patch_role)) return writeError(sess, "unauthorized: your role in this repo may not write patches"); │ │502 + // a deleted target, or one whose owner was deleted, leaves the fork fetchable but not pushable │ │503 + const target = try evt.readRepoById(io, allocator, &admin_arena, handler.server.users_dir, &target_owner_id, &target_id, forker_id); │ │504 + if (target) |value| { │ │505 + // patches that are off take their drafts with them │ │506 + const patch_role = value.repo.event.patch_role orelse return writeError(sess, "patch draft not found"); │ │507 + if (service == .receive_pack) { │ │508 + const role = value.role orelse return writeError(sess, "repo not found"); │ │509 + if (!role.atLeast(patch_role)) return writeError(sess, "unauthorized: your role in this repo may not write patches"); │ │510 + } │ │511 } │ │512 │ │513 const draft_path = try fork.forkPath(allocator, handler.server.users_dir, &forker_id, &fork_id); │ ╰─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │521 inline else => |*repo| try repo.uploadPack(io, allocator, reader, writer, .{ .protocol_version = protocol_version }, &sideband),│ │522 } │ │523 } else { │ │520 - if (!std.mem.eql(u8, target.repo.event.name, forker_repo.name)) return writeError(sess, "patch draft belongs to another repo"); │ │524 + if (target == null) return writeError(sess, "the repo this patch draft targets was deleted"); │ │525 const target_path = try evt.repoPath(allocator, handler.server.users_dir, &target_owner_id, &target_id); │ │526 defer allocator.free(target_path); │ │527 const author: evt.CommitAuthor = .{ .name = user.event.name, .email = user.event.email }; │ ╰─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯