Skip to content

g1t/apps/web/app/components/base-branch.tsx

96 lines3,566 bytesCodeBlame

Pick any line to see why it is the way it is: the commit, the pull request and issue it came from, and what the agent was thinking.

Teams and CODEOWNERS, labels and milestones, dependency updates, the security suite, and a clearer top bar1import { GitBranch, Pencil } from "lucide-react";
2import { useState } from "react";
3import { useFetcher } from "react-router";
4
5import { ErrorText } from "./ui";
Merge branch 'worktree-agent-ae1299e92e4462012'6import { Hint } from "./ui/hint";
Teams and CODEOWNERS, labels and milestones, dependency updates, the security suite, and a clearer top bar7import { Command, CommandEmpty, CommandGroup, CommandInput, CommandItem, CommandList } from "./ui/combobox";
8import { Popover, PopoverContent, PopoverTrigger } from "./ui/popover";
9
10/**
11 * The branch a pull request merges into, and for someone who may change
12 * it, a menu of the repository's branches. The names are fetched when the
13 * menu opens; choosing one posts `action=base` with `base`.
14 */
15export function BaseBranch({
16 base,
17 head,
18 canChange,
19 branchesUrl,
20}: {
21 base: string;
22 /** Its own branch, which it cannot merge into. */
23 head: string | null;
24 canChange: boolean;
25 /** Where the branch names are, as JSON. */
26 branchesUrl: string;
27}) {
28 const names = useFetcher<{ branches: string[] }>();
29 const save = useFetcher<{ error?: string } | null>();
30 const [open, setOpen] = useState(false);
31 const shown = save.state !== "idle" ? String(save.formData?.get("base") ?? base) : base;
32 const choose = (branch: string) => {
33 setOpen(false);
34 if (branch === base) return;
35 const form = new FormData();
36 form.set("action", "base");
37 form.set("base", branch);
38 save.submit(form, { method: "post" });
39 };
40 const name = <span className="font-mono text-fg">{shown}</span>;
41 if (!canChange) return name;
42 return (
43 <>
44 <Popover
45 open={open}
46 onOpenChange={(next) => {
47 setOpen(next);
48 if (next && names.state === "idle" && !names.data) names.load(branchesUrl);
49 }}
50 >
Merge branch 'worktree-agent-ae1299e92e4462012'51 <Hint label="Change the branch it merges into">
52 <PopoverTrigger asChild>
53 <button
54 type="button"
55 className="group inline-flex items-center gap-1 rounded-md border border-transparent px-1 font-mono text-fg transition-colors hover:border-line hover:bg-raised"
56 >
57 {shown}
58 <Pencil size={12} className="text-faint group-hover:text-fg" />
59 <span className="sr-only">Change the branch it merges into</span>
60 </button>
61 </PopoverTrigger>
62 </Hint>
Teams and CODEOWNERS, labels and milestones, dependency updates, the security suite, and a clearer top bar63 <PopoverContent align="start" className="w-72 p-0">
64 <Command>
65 <CommandInput placeholder="Find a branch to merge into" />
66 <CommandList>
67 {names.data ? (
68 <CommandEmpty>No branch by that name.</CommandEmpty>
69 ) : (
70 <p className="px-3 py-4 text-sm text-faint">Reading branches…</p>
71 )}
72 <CommandGroup heading="Merge into">
73 {(names.data?.branches ?? [])
74 .filter((branch) => branch !== head)
75 .map((branch) => (
76 <CommandItem key={branch} value={branch} onSelect={() => choose(branch)}>
77 <GitBranch />
78 <span className="truncate font-mono text-[0.8125rem]">{branch}</span>
79 </CommandItem>
80 ))}
81 </CommandGroup>
82 </CommandList>
83 </Command>
84 <p className="border-t border-line px-3 py-2 text-xs text-faint">
85 Its checks, mergeability and whether it is up to date are worked out against the new base.
86 </p>
87 </PopoverContent>
88 </Popover>
89 {save.data?.error && (
90 <span className="basis-full">
91 <ErrorText>{save.data.error}</ErrorText>
92 </span>
93 )}
94 </>
95 );
96}

This file's history is long; its oldest lines are credited to the oldest commit read.