| |
| |
| |
| |
| |
| |
| |
| |
| |
|
|
| import { createLogger, getErrorMessage } from '@automaker/utils'; |
| import { performPull } from './pull-service.js'; |
| import { performPush } from './push-service.js'; |
| import type { PullResult } from './pull-service.js'; |
| import type { PushResult } from './push-service.js'; |
|
|
| const logger = createLogger('SyncService'); |
|
|
| |
| |
| |
|
|
| export interface SyncOptions { |
| |
| remote?: string; |
| } |
|
|
| export interface SyncResult { |
| success: boolean; |
| error?: string; |
| branch?: string; |
| |
| pulled?: boolean; |
| |
| pushed?: boolean; |
| |
| hasConflicts?: boolean; |
| |
| conflictFiles?: string[]; |
| |
| conflictSource?: 'pull' | 'stash'; |
| |
| isFastForward?: boolean; |
| |
| isMerge?: boolean; |
| |
| autoResolved?: boolean; |
| message?: string; |
| } |
|
|
| |
| |
| |
|
|
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| export async function performSync( |
| worktreePath: string, |
| options?: SyncOptions |
| ): Promise<SyncResult> { |
| const targetRemote = options?.remote || 'origin'; |
|
|
| |
| logger.info('Sync: starting pull', { worktreePath, remote: targetRemote }); |
|
|
| let pullResult: PullResult; |
| try { |
| pullResult = await performPull(worktreePath, { |
| remote: targetRemote, |
| stashIfNeeded: true, |
| }); |
| } catch (pullError) { |
| return { |
| success: false, |
| error: `Sync pull failed: ${getErrorMessage(pullError)}`, |
| }; |
| } |
|
|
| if (!pullResult.success) { |
| return { |
| success: false, |
| branch: pullResult.branch, |
| pulled: false, |
| pushed: false, |
| error: `Sync pull failed: ${pullResult.error}`, |
| hasConflicts: pullResult.hasConflicts, |
| conflictFiles: pullResult.conflictFiles, |
| conflictSource: pullResult.conflictSource, |
| }; |
| } |
|
|
| |
| if (pullResult.hasConflicts) { |
| return { |
| success: false, |
| branch: pullResult.branch, |
| pulled: true, |
| pushed: false, |
| hasConflicts: true, |
| conflictFiles: pullResult.conflictFiles, |
| conflictSource: pullResult.conflictSource, |
| isFastForward: pullResult.isFastForward, |
| isMerge: pullResult.isMerge, |
| error: 'Sync stopped: pull resulted in merge conflicts. Resolve conflicts and try again.', |
| message: pullResult.message, |
| }; |
| } |
|
|
| |
| logger.info('Sync: pull succeeded, starting push', { worktreePath, remote: targetRemote }); |
|
|
| let pushResult: PushResult; |
| try { |
| pushResult = await performPush(worktreePath, { |
| remote: targetRemote, |
| }); |
| } catch (pushError) { |
| return { |
| success: false, |
| branch: pullResult.branch, |
| pulled: true, |
| pushed: false, |
| isFastForward: pullResult.isFastForward, |
| isMerge: pullResult.isMerge, |
| error: `Sync push failed: ${getErrorMessage(pushError)}`, |
| }; |
| } |
|
|
| if (!pushResult.success) { |
| |
| if (pushResult.diverged) { |
| logger.info('Sync: push diverged after pull, retrying with autoResolve', { |
| worktreePath, |
| remote: targetRemote, |
| }); |
|
|
| try { |
| const retryResult = await performPush(worktreePath, { |
| remote: targetRemote, |
| autoResolve: true, |
| }); |
|
|
| if (retryResult.success) { |
| return { |
| success: true, |
| branch: retryResult.branch, |
| pulled: true, |
| pushed: true, |
| autoResolved: true, |
| isFastForward: pullResult.isFastForward, |
| isMerge: pullResult.isMerge, |
| message: 'Sync completed (push required auto-resolve).', |
| }; |
| } |
|
|
| return { |
| success: false, |
| branch: retryResult.branch, |
| pulled: true, |
| pushed: false, |
| hasConflicts: retryResult.hasConflicts, |
| conflictFiles: retryResult.conflictFiles, |
| error: retryResult.error, |
| }; |
| } catch (retryError) { |
| return { |
| success: false, |
| branch: pullResult.branch, |
| pulled: true, |
| pushed: false, |
| error: `Sync push retry failed: ${getErrorMessage(retryError)}`, |
| }; |
| } |
| } |
|
|
| return { |
| success: false, |
| branch: pushResult.branch, |
| pulled: true, |
| pushed: false, |
| isFastForward: pullResult.isFastForward, |
| isMerge: pullResult.isMerge, |
| error: `Sync push failed: ${pushResult.error}`, |
| }; |
| } |
|
|
| return { |
| success: true, |
| branch: pushResult.branch, |
| pulled: pullResult.pulled ?? true, |
| pushed: true, |
| isFastForward: pullResult.isFastForward, |
| isMerge: pullResult.isMerge, |
| message: pullResult.pulled |
| ? 'Sync completed: pulled latest changes and pushed.' |
| : 'Sync completed: already up to date, pushed local commits.', |
| }; |
| } |
|
|