Skip to content

Commit aabcae3

Browse files
fix(MORG-48): sync without checking out branches (#37)
Use `git fetch origin main:main` to update the local default branch ref without checkout. For non-current branches in --all mode, run rebase/merge in their worktree directory (via cwd) instead of switching to them; branches without a worktree are skipped with a message to switch manually.
1 parent 5916b0a commit aabcae3

2 files changed

Lines changed: 26 additions & 19 deletions

File tree

src/commands/sync.ts

Lines changed: 22 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ import {
1212
getCurrentBranch,
1313
checkout,
1414
pullBranch,
15+
fetchAndUpdateBranch,
1516
rebaseBranch,
1617
mergeBranch,
1718
deleteBranch,
@@ -41,12 +42,11 @@ async function runSync(options: { all?: boolean }): Promise<void> {
4142

4243
// ── Step 1: Always pull default branch ───────────────────────────────────────
4344
const startBranch = await getCurrentBranch();
44-
if (startBranch !== defaultBranch) {
45-
await checkout(defaultBranch);
46-
}
47-
await withSpinner(`Pulling ${defaultBranch}...`, () => pullBranch(defaultBranch));
48-
if (startBranch !== defaultBranch) {
49-
await checkout(startBranch);
45+
if (startBranch === defaultBranch) {
46+
await withSpinner(`Pulling ${defaultBranch}...`, () => pullBranch(defaultBranch));
47+
} else {
48+
// Update local default branch ref via fetch without checking it out
49+
await withSpinner(`Pulling ${defaultBranch}...`, () => fetchAndUpdateBranch(defaultBranch));
5050
}
5151
console.log(theme.success(` ${symbols.success} Pulled latest ${defaultBranch}`));
5252

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

151+
const isCurrentBranch = branch.branchName === currentBranch;
152+
153+
// For non-current branches without a worktree we can't rebase/merge without checkout
154+
if (!isCurrentBranch && !branch.worktreePath) {
155+
console.log(
156+
theme.muted(
157+
` ${symbols.arrow} ${branch.branchName} is behind ${defaultBranch} — switch to it to update`,
158+
),
159+
);
160+
continue;
161+
}
162+
151163
const action = await select<'rebase' | 'merge' | 'skip'>({
152164
message: `${defaultBranch} has new commits not in ${branch.branchName}. What do you want to do?`,
153165
options: [
@@ -159,18 +171,17 @@ async function runSync(options: { all?: boolean }): Promise<void> {
159171

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

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

166177
try {
167178
if (action === 'rebase') {
168-
await rebaseBranch(defaultBranch);
179+
await rebaseBranch(defaultBranch, cwd);
169180
console.log(
170181
theme.success(` ${symbols.success} Rebased ${branch.branchName} onto ${defaultBranch}`),
171182
);
172183
} else {
173-
await mergeBranch(defaultBranch);
184+
await mergeBranch(defaultBranch, true, cwd);
174185
console.log(
175186
theme.success(` ${symbols.success} Merged ${defaultBranch} into ${branch.branchName}`),
176187
);
@@ -181,10 +192,6 @@ async function runSync(options: { all?: boolean }): Promise<void> {
181192
theme.warning(` ${symbols.warning} ${action} failed for ${branch.branchName}: ${msg}`),
182193
);
183194
}
184-
185-
if (currentBranch !== branch.branchName) {
186-
await checkout(currentBranch);
187-
}
188195
}
189196

190197
await configManager.saveBranches(projectId, branchesFile);

src/git/index.ts

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -141,9 +141,9 @@ export async function removeWorktree(worktreePath: string): Promise<void> {
141141
if (result.exitCode !== 0) throw new GitError(`git worktree remove failed: ${result.stderr}`);
142142
}
143143

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

@@ -188,7 +188,7 @@ export async function getWorktreePathForBranch(branch: string): Promise<string |
188188
return null;
189189
}
190190

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

0 commit comments

Comments
 (0)