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
4 changes: 1 addition & 3 deletions apps/code/src/main/services/git/service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,6 @@ import { DiscardFileChangesSaga } from "@posthog/git/sagas/discard";
import { PullSaga } from "@posthog/git/sagas/pull";
import { PushSaga } from "@posthog/git/sagas/push";
import { parseGitHubUrl } from "@posthog/git/utils";
import { isCodeBranch } from "@shared/constants";
import { inject, injectable } from "inversify";
import { MAIN_TOKENS } from "../../di/tokens";
import { logger } from "../../utils/logger";
Expand Down Expand Up @@ -229,8 +228,7 @@ export class GitService extends TypedEventEmitter<GitServiceEvents> {
}

public async getAllBranches(directoryPath: string): Promise<string[]> {
const branches = await getAllBranches(directoryPath);
return branches.filter((branch) => !isCodeBranch(branch));
return getAllBranches(directoryPath);
}

public async createBranch(
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +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 { getSuggestedBranchName } from "@features/git-interaction/utils/getSuggestedBranchName";
import { invalidateGitBranchQueries } from "@features/git-interaction/utils/gitCacheKeys";
import { GitBranch, Plus } from "@phosphor-icons/react";
import { Flex, Spinner, Tooltip } from "@radix-ui/themes";
Expand Down Expand Up @@ -144,7 +144,9 @@ export function BranchSelector({
onClick={() => {
setOpen(false);
actions.openBranch(
taskId ? getSuggestedBranchName(taskId) : undefined,
taskId
? getSuggestedBranchName(taskId, repoPath ?? undefined)
: undefined,
);
}}
>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,14 +14,14 @@ import {
createBranch,
getBranchNameInputState,
} from "@features/git-interaction/utils/branchCreation";
import { getSuggestedBranchName } from "@features/git-interaction/utils/deriveBranchName";
import { updateGitCacheFromSnapshot } from "@features/git-interaction/utils/updateGitCache";
import { trpc, trpcClient } from "@renderer/trpc";
import { ANALYTICS_EVENTS } from "@shared/types/analytics";
import { useQueryClient } from "@tanstack/react-query";
import { track } from "@utils/analytics";
import { logger } from "@utils/logger";
import { useMemo } from "react";
import { getSuggestedBranchName } from "../utils/getSuggestedBranchName";

const log = logger.scope("git-interaction");

Expand Down Expand Up @@ -167,7 +167,8 @@ export function useGitInteraction(
publish: () => modal.openPush("publish"),
"view-pr": () => viewPr(),
"create-pr": () => openCreatePr(),
"branch-here": () => modal.openBranch(getSuggestedBranchName(taskId)),
"branch-here": () =>
modal.openBranch(getSuggestedBranchName(taskId, repoPath)),
};
actionMap[id]();
};
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,17 @@ export function useGitQueries(repoPath?: string) {
),
);

useQuery(
trpc.git.getAllBranches.queryOptions(
{ directoryPath: repoPath as string },
{
enabled: repoEnabled,
...GIT_QUERY_DEFAULTS,
staleTime: 60_000,
},
),
);

const hasChanges = changedFiles.length > 0;
const aheadOfRemote = syncStatus?.aheadOfRemote ?? 0;
const behind = syncStatus?.behind ?? 0;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,44 +4,48 @@ 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",
"posthog-code/fix-authentication-login-bug",
);
});

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

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

it("strips leading and trailing dashes", () => {
expect(deriveBranchName(" Fix bug ", "abc123")).toBe("posthog/fix-bug");
expect(deriveBranchName(" Fix bug ", "abc123")).toBe(
"posthog-code/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.length).toBeLessThanOrEqual(73); // 60 slug + "posthog-code/" prefix
expect(result.startsWith("posthog-code/")).toBe(true);
expect(result.endsWith("-")).toBe(false);
});

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

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

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

export function deriveBranchName(title: string, fallbackId: string): string {
const slug = title
Expand All @@ -11,21 +10,6 @@ export function deriveBranchName(title: string, fallbackId: string): string {
.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);
if (!slug) return `${BRANCH_PREFIX}task-${fallbackId}`;
return `${BRANCH_PREFIX}${slug}`;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
import { deriveBranchName } from "@features/git-interaction/utils/deriveBranchName";
import { trpc } from "@renderer/trpc";
import type { Task } from "@shared/types";
import { queryClient } from "@utils/queryClient";

export function getSuggestedBranchName(
taskId: string,
repoPath?: 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);
const base = deriveBranchName(task?.title ?? "", fallbackId);

if (!repoPath) return base;

const cached = queryClient.getQueryData<string[]>(
trpc.git.getAllBranches.queryKey({ directoryPath: repoPath }),
);
if (!cached?.includes(base)) return base;

let n = 2;
while (cached.includes(`${base}-${n}`)) n++;
return `${base}-${n}`;
}
14 changes: 0 additions & 14 deletions apps/code/src/shared/constants.ts
Original file line number Diff line number Diff line change
@@ -1,18 +1,4 @@
/**
* Branch naming conventions.
* - Reading: Accept all prefixes for backwards compatibility
* - Writing: Always use BRANCH_PREFIX (posthog-code/)
*/
export const BRANCH_PREFIX = "posthog-code/";
export const LEGACY_BRANCH_PREFIXES = ["twig/", "array/", "posthog/"];

export function isCodeBranch(branchName: string): boolean {
return (
branchName.startsWith(BRANCH_PREFIX) ||
LEGACY_BRANCH_PREFIXES.some((p) => branchName.startsWith(p))
);
}

export const DATA_DIR = ".posthog-code";
export const WORKTREES_DIR = ".posthog-code/worktrees";
export const LEGACY_DATA_DIRS = [
Expand Down
Loading