╭ changed ───────────────╮ │src/ui/Repo/Branches.zig│ ╰────────────────────────╯ ╭───────────╮ │← all files│ ╰───────────╯ ╭─────────────────────────────────────────────╮ │ 5 const inp = @import("../input.zig"); │ │ 6 const Comment = @import("./Comment.zig");│ │ 7 const xit = @import("xit"); │ │ 8 +const rp = xit.repo; │ │ 9 const xitui = xit.xitui; │ │10 const wgt = xitui.widget; │ │11 const layout = xitui.layout; │ ╰─────────────────────────────────────────────╯ ╭───────────────────────────────────────────────────────────────────────────╮ │15 │ │16 const wasm = builtin.target.cpu.arch == .wasm32; │ │17 │ │17 -// the repo's protected branches, which only its owners see │ │18 +// the repo's default and protected branches, which only its owners see│ │19 identity: []const u8, │ │20 +// the branch HEAD names ("" when detached) │ │21 +default_branch: []const u8, │ │22 names: []const []const u8, │ │23 │ │24 const Self = @This(); │ ╰───────────────────────────────────────────────────────────────────────────╯ ╭──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │25 │ │23 -pub const Action = enum { protect, unprotect }; │ │26 +pub const Action = enum { default, protect, unprotect }; │ │27 │ │25 -pub fn init(arena: *std.heap.ArenaAllocator, identity: []const u8, repo: evt.Repo) !Self { │ │28 +pub fn init(arena: *std.heap.ArenaAllocator, identity: []const u8, repo: evt.Repo, default_branch: []const u8) !Self {│ │29 const aa = arena.allocator(); │ │30 return .{ │ │31 .identity = try aa.dupe(u8, identity), │ ╰──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭──────────────────────────────────────────────────────────────╮ │32 + .default_branch = try aa.dupe(u8, default_branch),│ │33 .names = try repo.protectedBranchNames(aa), │ │34 }; │ │35 } │ ╰──────────────────────────────────────────────────────────────╯ ╭─────────────────────────────────────────────────────────────────────────────────╮ │36 │ │33 -// protect or unprotect `branch` in the repo `identity` names as `actor`, one│ │34 -// of its owners. returns why a protect wrote nothing, or null once written │ │37 +// make `branch` the default, or protect or unprotect it, in the repo │ │38 +// `identity` names as `actor`, one of its owners. returns why nothing was │ │39 +// written, or null once written │ │40 pub fn perform( │ │41 io: std.Io, │ │42 allocator: std.mem.Allocator, │ ╰─────────────────────────────────────────────────────────────────────────────────╯ ╭───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │58 break :blk found.event_id; │ │59 }; │ │60 │ │56 - // only an existing branch can be protected, and its row links to it, so │ │57 - // its encoded name must fit in a route │ │58 - if (action == .protect) { │ │61 + // only an existing branch can be the default or protected, and a protected │ │62 + // branch's row links to it, so its encoded name must fit in a route │ │63 + if (action != .unprotect) { │ │64 if ((try ui.urlEncodeRef(arena.allocator(), branch)).len > ui.RoutablePage.ref_route_max_len) return .too_long;│ │65 const path = try evt.repoPath(allocator, users_dir, &owner_id, &repo_id); │ │66 defer allocator.free(path); │ ╰───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │67 const source = ui.RepoSource{ .path = path, .repo_kind = .xit }; │ │68 if (!try source.hasBranch(io, allocator, branch)) return .not_found; │ │69 + // the default branch is HEAD, which the repo page, the commits tab and clones follow │ │70 + if (action == .default) { │ │71 + var any_repo = try rp.AnyRepo(.xit, .{}).open(io, allocator, .{ .path = path }); │ │72 + defer any_repo.deinit(io, allocator); │ │73 + switch (any_repo) { │ │74 + inline else => |*repo| try repo.resetAdd(io, .{ .ref = .{ .kind = .head, .name = branch } }), │ │75 + } │ │76 + return null; │ │77 + } │ │78 } │ │79 evt.protectBranch(io, allocator, users_dir, &owner_id, &repo_id, branch, action == .protect, actor.author) catch |err| {│ │80 if (ui.Session.FormFeedback.BranchesFailure.fromError(err)) |failure| return failure; │ ╰────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭──────────────────────────────────────────────────────────────────────────────────────────────────╮ │89 session: *ui.Session, │ │90 │ │91 const about_index = 0; │ │78 - const add_index = 1; │ │92 + const default_index = 1; │ │93 + const add_index = 2; │ │94 │ │95 pub fn init(allocator: std.mem.Allocator, data: *const Self, session: *ui.Session) !View {│ │96 const aa = session.page_arena.allocator(); │ ╰──────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭───────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │101 │ │102 { │ │103 var about = try ui.widget.initAbout(allocator, &.{ │ │104 + .{ .text = "the default branch is what the repo page shows and clones check out.\n" },│ │105 .{ .text = "protected branches can't be deleted or force pushed.\n" }, │ │106 .{ .text = "owners", .style = .{ .bold = true } }, │ │107 .{ .text = " can push directly to them.\n" }, │ ╰───────────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │111 errdefer about.deinit(allocator); │ │112 try box.children.put(allocator, about.getFocus().id, .{ .widget = .{ .box = about }, .rect = null, .min_size = null }); │ │113 } │ │ 98 - { │ │ 99 - var add = try wgt.Box(ui.Widget).init(allocator, .{ .border = null, .direction = .horiz }); │ │100 - errdefer add.deinit(allocator); │ │101 - add.getFocus().kind = .{ .custom = try aa.print("form:{s}/protect", .{url}) }; │ │102 - var name = try wgt.TextInput.init(allocator, .{ .top_label = .{ .text = " add a protected branch " }, .name = "name", .visible_width = 30, .round_corners = true, .render_content = session.is_terminal });│ │103 - errdefer name.deinit(allocator); │ │104 - name.getFocus().mode = .all; │ │105 - if (session.formFeedback(.repo_branches)) |saved| try name.setContent(allocator, saved.name); │ │106 - add.getFocus().child_id = name.getFocus().id; │ │107 - try add.children.put(allocator, name.getFocus().id, .{ .widget = .{ .text_input = name }, .rect = null, .min_size = .{ .width = null, .height = 3 } }); │ │108 - try box.children.put(allocator, add.getFocus().id, .{ .widget = .{ .box = add }, .rect = null, .min_size = .{ .width = null, .height = 3 } }); │ │109 - } │ │114 + // a failed submission keeps its text in the input it came from │ │115 + const saved = session.formFeedback(.repo_branches); │ │116 + try addFormRow(allocator, &box, session, try aa.print("form:{s}/default", .{url}), " default branch ", if (saved) |value| (if (value.default) value.name else data.default_branch) else data.default_branch); │ │117 + try addFormRow(allocator, &box, session, try aa.print("form:{s}/protect", .{url}), " add a protected branch ", if (saved) |value| (if (value.default) "" else value.name) else ""); │ │118 │ │119 // the branch rows follow the add row in the same box │ │120 for (data.names) |branch| { │ ╰────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭──────╮ │next →│ ╰──────╯