╭ changed ──╮ │src/web.zig│ ╰───────────╯ ╭───────────╮ │← all files│ ╰───────────╯ ╭────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │522 // newlines so the text renders the same on every host │ │523 const description = try std.mem.replaceOwned(u8, allocator, description_crlf, "\r\n", "\n"); │ │524 defer allocator.free(description); │ │525 + const target_branch = (try parseFormField(allocator, body, "target_branch")) orelse try allocator.dupe(u8, "");│ │526 + defer allocator.free(target_branch); │ │527 │ │528 const valid = switch (kind) { │ │529 .issue => evt.Issue.fieldsValid(title, tags), │ ╰────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │559 const user_id = request_author.user_id orelse return respondLoginRequired(request); │ │560 const request_repo = (try requestRepoSource(io, allocator, host, base)) orelse return respondRemoveNotFound(request); │ │561 defer request_repo.deinit(allocator); │ │562 + if (!try request_repo.source.hasBranch(io, allocator, target_branch)) { │ │563 + const form_location = try std.fmt.allocPrint(allocator, "{s}/patches/new", .{base}); │ │564 + defer allocator.free(form_location); │ │565 + try request.respond("", .{ .status = .see_other, .extra_headers = &.{.{ .name = "location", .value = form_location }} });│ │566 + return; │ │567 + } │ │568 const repo_id = evt.parseEventId(std.fs.path.basename(request_repo.source.path)) catch return respondRemoveNotFound(request);│ │569 var admin_repo = try rp.Repo(.xit, evt.admin_repo_opts).open(io, allocator, .{ .path = remote.admin_repo_path }); │ │570 defer admin_repo.deinit(io, allocator); │ ╰──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭────────────────────────────────────────────────────────────────────────────────────╮ │577 .title = title, │ │578 .description = description, │ │579 .tags = tags, │ │580 + .target_branch = target_branch, │ │581 .author = author, │ │582 .timestamp = @intCast(std.Io.Timestamp.now(io, .real).toSeconds()),│ │583 }); │ ╰────────────────────────────────────────────────────────────────────────────────────╯ ╭─────────────────────────────────────────────────────────────╮ │1304 }); │ │1305 } │ │1306 │ │1298 -// replace the title, tags and description of a thread.│ │1307 +// replace a thread's editable fields. │ │1308 fn handleThreadEdit( │ │1309 io: std.Io, │ │1310 request: *std.http.Server.Request, │ ╰─────────────────────────────────────────────────────────────╯ ╭─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │1331 // newlines so the text renders the same on every host │ │1332 const description = try std.mem.replaceOwned(u8, allocator, description_crlf, "\r\n", "\n"); │ │1333 defer allocator.free(description); │ │1334 + const target_branch = (try parseFormField(allocator, body, "target_branch")) orelse try allocator.dupe(u8, "");│ │1335 + defer allocator.free(target_branch); │ │1336 │ │1337 // invalid fields send the user back to the edit form │ │1338 const valid = switch (parts.thread_kind) { │ ╰─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │1359 }; │ │1360 const request_repo = (try requestRepoSource(io, allocator, host, parts.repo_base)) orelse return respondRemoveNotFound(request);│ │1361 defer request_repo.deinit(allocator); │ │1362 + if (!try request_repo.source.hasBranch(io, allocator, target_branch)) { │ │1363 + const form_location = try std.fmt.allocPrint(allocator, "{s}/edit", .{base}); │ │1364 + defer allocator.free(form_location); │ │1365 + try request.respond("", .{ .status = .see_other, .extra_headers = &.{.{ .name = "location", .value = form_location }} }); │ │1366 + return; │ │1367 + } │ │1368 const repo_id = evt.parseEventId(std.fs.path.basename(request_repo.source.path)) catch return respondRemoveNotFound(request); │ │1369 var admin_repo = try rp.Repo(.xit, evt.admin_repo_opts).open(io, allocator, .{ .path = remote.admin_repo_path }); │ │1370 defer admin_repo.deinit(io, allocator); │ ╰──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭─────────────────────────────────────────────────────────────────────────────────────╮ │1380 .title = title, │ │1381 .tags = tags, │ │1382 .description = description, │ │1383 + .target_branch = target_branch, │ │1384 .author = author, │ │1385 .timestamp = @intCast(std.Io.Timestamp.now(io, .real).toSeconds()),│ │1386 })) { │ ╰─────────────────────────────────────────────────────────────────────────────────────╯ ╭─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │1408 .title = title, │ │1409 .tags = tags, │ │1410 .description = description, │ │1411 + .target_branch = target_branch, │ │1412 } }, author), │ │1413 .discuss => updateDiscussion(io, allocator, host, parts.repo_base, &parts.thread_id, title, tags, description, author),│ │1414 else => unreachable, │ ╰─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯