Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { Combobox } from "@components/ui/combobox/Combobox";
import { useGitInteractionStore } from "@features/git-interaction/state/gitInteractionStore";
import { getSuggestedBranchName } from "@features/git-interaction/utils/deriveBranchName";
import { invalidateGitBranchQueries } from "@features/git-interaction/utils/gitCacheKeys";
import { GitBranch, Plus } from "@phosphor-icons/react";
import { Flex, Spinner, Tooltip } from "@radix-ui/themes";
Expand All @@ -20,6 +21,7 @@ interface BranchSelectorProps {
onBranchSelect?: (branch: string | null) => void;
cloudBranches?: string[];
cloudBranchesLoading?: boolean;
taskId?: string;
}

export function BranchSelector({
Expand All @@ -34,6 +36,7 @@ export function BranchSelector({
onBranchSelect,
cloudBranches,
cloudBranchesLoading,
taskId,
}: BranchSelectorProps) {
const [open, setOpen] = useState(false);
const trpc = useTRPC();
Expand Down Expand Up @@ -140,7 +143,9 @@ export function BranchSelector({
className="combobox-footer-button"
onClick={() => {
setOpen(false);
actions.openBranch();
actions.openBranch(
taskId ? getSuggestedBranchName(taskId) : undefined,
);
}}
>
<Flex
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import {
sanitizeBranchName,
validateBranchName,
} from "@features/git-interaction/utils/branchNameValidation";
import { getSuggestedBranchName } from "@features/git-interaction/utils/deriveBranchName";
import { invalidateGitBranchQueries } from "@features/git-interaction/utils/gitCacheKeys";
import { updateGitCacheFromSnapshot } from "@features/git-interaction/utils/updateGitCache";
import { trpc, trpcClient } from "@renderer/trpc";
Expand Down Expand Up @@ -167,7 +168,7 @@ export function useGitInteraction(
publish: () => modal.openPush("publish"),
"view-pr": () => viewPr(),
"create-pr": () => openCreatePr(),
"branch-here": () => modal.openBranch(),
"branch-here": () => modal.openBranch(getSuggestedBranchName(taskId)),
};
actionMap[id]();
};
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ interface GitInteractionActions {
openCommit: (nextStep: CommitNextStep) => void;
openPush: (mode: PushMode) => void;
openPr: (defaultTitle?: string, defaultBody?: string) => void;
openBranch: () => void;
openBranch: (suggestedName?: string) => void;
closeCommit: () => void;
closePush: () => void;
closePr: () => void;
Expand Down Expand Up @@ -127,8 +127,12 @@ export const useGitInteractionStore = create<GitInteractionStore>((set) => ({
prError: null,
prOpen: true,
}),
openBranch: () =>
set({ branchName: "", branchError: null, branchOpen: true }),
openBranch: (suggestedName) =>
set({
branchName: suggestedName ?? "",
branchError: null,
branchOpen: true,
}),
closeCommit: () => set({ commitOpen: false, commitError: null }),
closePush: () =>
set({
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
import { describe, expect, it } from "vitest";
import { deriveBranchName } from "./deriveBranchName";

describe("deriveBranchName", () => {
it("converts a simple title to a branch name", () => {
expect(deriveBranchName("Fix authentication login bug", "abc123")).toBe(
"posthog/fix-authentication-login-bug",
);
});

it("handles special characters", () => {
expect(deriveBranchName("PostHog issue #1234", "abc123")).toBe(
"posthog/posthog-issue-1234",
);
});

it("collapses consecutive dashes", () => {
expect(deriveBranchName("Fix the bug", "abc123")).toBe(
"posthog/fix-the-bug",
);
});

it("strips leading and trailing dashes", () => {
expect(deriveBranchName(" Fix bug ", "abc123")).toBe("posthog/fix-bug");
});

it("truncates long titles", () => {
const longTitle =
"This is a very long task title that should be truncated to a reasonable length for git";
const result = deriveBranchName(longTitle, "abc123");
expect(result.length).toBeLessThanOrEqual(68); // 60 slug + "posthog/" prefix
expect(result.startsWith("posthog/")).toBe(true);
expect(result.endsWith("-")).toBe(false);
});

it("falls back to task ID when title is empty", () => {
expect(deriveBranchName("", "abc123")).toBe("posthog/task-abc123");
});

it("falls back to task ID when title is only whitespace", () => {
expect(deriveBranchName(" ", "abc123")).toBe("posthog/task-abc123");
});

it("falls back to task ID when title is only special characters", () => {
expect(deriveBranchName("!@#$%", "abc123")).toBe("posthog/task-abc123");
});
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
import type { Task } from "@shared/types";
import { queryClient } from "@utils/queryClient";

export function deriveBranchName(title: string, fallbackId: string): string {
const slug = title
.toLowerCase()
.trim()
.replace(/[^a-z0-9-]+/g, "-")
.replace(/-{2,}/g, "-")
.replace(/^-|-$/g, "")
.slice(0, 60)
.replace(/-$/, "");

if (!slug) return `posthog/task-${fallbackId}`;
return `posthog/${slug}`;
}

export function getSuggestedBranchName(taskId: string): string {
const queries = queryClient.getQueriesData<Task[]>({
queryKey: ["tasks", "list"],
});
let task: Task | undefined;
for (const [, tasks] of queries) {
task = tasks?.find((t) => t.id === taskId);
if (task) break;
}
const fallbackId = task?.task_number
? String(task.task_number)
: (task?.slug ?? taskId);
return deriveBranchName(task?.title ?? "", fallbackId);
}
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ interface ModeAndBranchRowProps {
} | null;
disabled?: boolean;
isBashMode?: boolean;
taskId?: string;
}

function ModeAndBranchRow({
Expand All @@ -42,6 +43,7 @@ function ModeAndBranchRow({
cloudDiffStats,
disabled,
isBashMode,
taskId,
}: ModeAndBranchRowProps) {
const { currentBranch: gitBranch, diffStats } = useGitQueries(
repoPath ?? undefined,
Expand Down Expand Up @@ -113,6 +115,7 @@ function ModeAndBranchRow({
currentBranch={currentBranch}
disabled={disabled}
variant="ghost"
taskId={taskId}
/>
</Flex>
)}
Expand Down Expand Up @@ -350,6 +353,7 @@ export const MessageEditor = forwardRef<EditorHandle, MessageEditorProps>(
cloudDiffStats={cloudDiffStats}
disabled={disabled}
isBashMode={isBashMode}
taskId={taskId}
/>
</Flex>
);
Expand Down
Loading