import { getGitBranches, getGitStatus } from '@/lib/gitApi'; import type { CreateWorktreeArgs, ProjectRef } from '@/lib/worktrees/worktreeManager'; import { createWorktree } from '@/lib/worktrees/worktreeManager'; import { getRootBranch } from '@/lib/worktrees/worktreeStatus'; const parseTrackingRef = (tracking: string | null | undefined): { remote: string; branch: string } | null => { const value = String(tracking || '').trim().replace(/^remotes\//, ''); if (!value) { return null; } const separatorIndex = value.indexOf('/'); if (separatorIndex <= 0 || separatorIndex >= value.length - 1) { return null; } return { remote: value.slice(0, separatorIndex), branch: value.slice(separatorIndex + 1), }; }; const normalizeBranchName = (value: string): string => { return String(value || '') .trim() .replace(/^refs\/heads\//, '') .replace(/^heads\//, '') .replace(/^remotes\//, ''); }; const resolveLocalBranchName = (args: CreateWorktreeArgs): string => { if (args.branchName) { return normalizeBranchName(args.branchName); } if (args.mode === 'existing') { return normalizeBranchName(args.existingBranch || args.preferredName || ''); } return normalizeBranchName(args.preferredName || ''); }; export const resolveRootTrackingRemote = async (projectDirectory: string): Promise => { const rootBranch = await getRootBranch(projectDirectory); try { const branchState = await getGitBranches(projectDirectory); const tracking = branchState.branches?.[rootBranch]?.tracking || null; const parsed = parseTrackingRef(tracking); if (parsed?.remote) { return parsed.remote; } } catch { // ignore and fallback to status tracking } try { const status = await getGitStatus(projectDirectory); const parsed = parseTrackingRef(status.tracking); if (parsed?.remote) { return parsed.remote; } } catch { // ignore } return null; }; export const resolveWorktreeUpstreamDefaults = async ( projectDirectory: string, localBranch: string ): Promise<{ setUpstream: true; upstreamRemote: string; upstreamBranch: string } | null> => { const remote = await resolveRootTrackingRemote(projectDirectory); const normalizedBranch = normalizeBranchName(localBranch); if (!remote || !normalizedBranch) { return null; } return { setUpstream: true, upstreamRemote: remote, upstreamBranch: normalizedBranch, }; }; export const withWorktreeUpstreamDefaults = async ( projectDirectory: string, args: CreateWorktreeArgs, options?: { resolvedRootTrackingRemote?: string | null } ): Promise => { const localBranch = resolveLocalBranchName(args); const resolvedRemote = options?.resolvedRootTrackingRemote; const defaults = resolvedRemote === undefined ? await resolveWorktreeUpstreamDefaults(projectDirectory, localBranch) : (resolvedRemote && normalizeBranchName(localBranch) ? { setUpstream: true as const, upstreamRemote: resolvedRemote, upstreamBranch: normalizeBranchName(localBranch), } : null); if (!defaults) { return args; } return { ...args, setUpstream: args.setUpstream ?? defaults.setUpstream, upstreamRemote: args.upstreamRemote || defaults.upstreamRemote, upstreamBranch: args.upstreamBranch || defaults.upstreamBranch, }; }; export const createWorktreeWithDefaults = async ( project: ProjectRef, args: CreateWorktreeArgs, options?: { resolvedRootTrackingRemote?: string | null } ) => { const resolvedArgs = await withWorktreeUpstreamDefaults(project.path, args, options); return createWorktree(project, resolvedArgs); };