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
37 changes: 22 additions & 15 deletions src/commands/sync.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import {
getCurrentBranch,
checkout,
pullBranch,
fetchAndUpdateBranch,
rebaseBranch,
mergeBranch,
deleteBranch,
Expand Down Expand Up @@ -41,12 +42,11 @@ async function runSync(options: { all?: boolean }): Promise<void> {

// ── Step 1: Always pull default branch ───────────────────────────────────────
const startBranch = await getCurrentBranch();
if (startBranch !== defaultBranch) {
await checkout(defaultBranch);
}
await withSpinner(`Pulling ${defaultBranch}...`, () => pullBranch(defaultBranch));
if (startBranch !== defaultBranch) {
await checkout(startBranch);
if (startBranch === defaultBranch) {
await withSpinner(`Pulling ${defaultBranch}...`, () => pullBranch(defaultBranch));
} else {
// Update local default branch ref via fetch without checking it out
await withSpinner(`Pulling ${defaultBranch}...`, () => fetchAndUpdateBranch(defaultBranch));
}
console.log(theme.success(` ${symbols.success} Pulled latest ${defaultBranch}`));

Expand Down Expand Up @@ -148,6 +148,18 @@ async function runSync(options: { all?: boolean }): Promise<void> {
const diverged = await hasDiverged(branch.branchName, defaultBranch);
if (!diverged) continue;

const isCurrentBranch = branch.branchName === currentBranch;

// For non-current branches without a worktree we can't rebase/merge without checkout
if (!isCurrentBranch && !branch.worktreePath) {
console.log(
theme.muted(
` ${symbols.arrow} ${branch.branchName} is behind ${defaultBranch} — switch to it to update`,
),
);
continue;
}

const action = await select<'rebase' | 'merge' | 'skip'>({
message: `${defaultBranch} has new commits not in ${branch.branchName}. What do you want to do?`,
options: [
Expand All @@ -159,18 +171,17 @@ async function runSync(options: { all?: boolean }): Promise<void> {

if (action === 'skip') continue;

if (currentBranch !== branch.branchName) {
await checkout(branch.branchName);
}
// Run in the worktree directory for non-current branches; current branch uses process cwd
const cwd = isCurrentBranch ? undefined : (branch.worktreePath ?? undefined);

try {
if (action === 'rebase') {
await rebaseBranch(defaultBranch);
await rebaseBranch(defaultBranch, cwd);
console.log(
theme.success(` ${symbols.success} Rebased ${branch.branchName} onto ${defaultBranch}`),
);
} else {
await mergeBranch(defaultBranch);
await mergeBranch(defaultBranch, true, cwd);
console.log(
theme.success(` ${symbols.success} Merged ${defaultBranch} into ${branch.branchName}`),
);
Expand All @@ -181,10 +192,6 @@ async function runSync(options: { all?: boolean }): Promise<void> {
theme.warning(` ${symbols.warning} ${action} failed for ${branch.branchName}: ${msg}`),
);
}

if (currentBranch !== branch.branchName) {
await checkout(currentBranch);
}
}

await configManager.saveBranches(projectId, branchesFile);
Expand Down
8 changes: 4 additions & 4 deletions src/git/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -141,9 +141,9 @@ export async function removeWorktree(worktreePath: string): Promise<void> {
if (result.exitCode !== 0) throw new GitError(`git worktree remove failed: ${result.stderr}`);
}

export async function mergeBranch(branch: string, noFF = true): Promise<void> {
export async function mergeBranch(branch: string, noFF = true, cwd?: string): Promise<void> {
const args = noFF ? ['merge', '--no-ff', branch] : ['merge', branch];
const result = await execa('git', args, { reject: false });
const result = await execa('git', args, { reject: false, ...(cwd ? { cwd } : {}) });
if (result.exitCode !== 0) throw new GitError(`git merge failed: ${result.stderr}`);
}

Expand Down Expand Up @@ -188,7 +188,7 @@ export async function getWorktreePathForBranch(branch: string): Promise<string |
return null;
}

export async function rebaseBranch(onto: string): Promise<void> {
const result = await execa('git', ['rebase', onto], { reject: false });
export async function rebaseBranch(onto: string, cwd?: string): Promise<void> {
const result = await execa('git', ['rebase', onto], { reject: false, ...(cwd ? { cwd } : {}) });
if (result.exitCode !== 0) throw new GitError(`git rebase failed: ${result.stderr}`);
}