fix: Fetch remote before merge/push in initiative approval
approveInitiative was merging and pushing with a stale local defaultBranch, causing "rejected (fetch first)" when origin/main had advanced since the last project sync. Now fetches remote and fast-forwards the target branch before merging.
This commit is contained in:
@@ -49,6 +49,8 @@ function createMocks() {
|
|||||||
getMergeBase: vi.fn().mockResolvedValue('abc123'),
|
getMergeBase: vi.fn().mockResolvedValue('abc123'),
|
||||||
pushBranch: vi.fn(),
|
pushBranch: vi.fn(),
|
||||||
checkMergeability: vi.fn().mockResolvedValue({ mergeable: true }),
|
checkMergeability: vi.fn().mockResolvedValue({ mergeable: true }),
|
||||||
|
fetchRemote: vi.fn(),
|
||||||
|
fastForwardBranch: vi.fn(),
|
||||||
};
|
};
|
||||||
|
|
||||||
const phaseRepository = {
|
const phaseRepository = {
|
||||||
|
|||||||
@@ -637,7 +637,16 @@ export class ExecutionOrchestrator {
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Fetch remote so local branches are up-to-date before merge/push
|
||||||
|
await this.branchManager.fetchRemote(clonePath);
|
||||||
|
|
||||||
if (strategy === 'merge_and_push') {
|
if (strategy === 'merge_and_push') {
|
||||||
|
// Fast-forward local defaultBranch to match origin before merging
|
||||||
|
try {
|
||||||
|
await this.branchManager.fastForwardBranch(clonePath, project.defaultBranch);
|
||||||
|
} catch (ffErr) {
|
||||||
|
log.warn({ project: project.name, err: (ffErr as Error).message }, 'fast-forward of default branch failed — attempting merge anyway');
|
||||||
|
}
|
||||||
const result = await this.branchManager.mergeBranch(clonePath, initiative.branch, project.defaultBranch);
|
const result = await this.branchManager.mergeBranch(clonePath, initiative.branch, project.defaultBranch);
|
||||||
if (!result.success) {
|
if (!result.success) {
|
||||||
throw new Error(`Failed to merge ${initiative.branch} into ${project.defaultBranch} for project ${project.name}: ${result.message}`);
|
throw new Error(`Failed to merge ${initiative.branch} into ${project.defaultBranch} for project ${project.name}: ${result.message}`);
|
||||||
|
|||||||
@@ -75,4 +75,17 @@ export interface BranchManager {
|
|||||||
* Uses `git merge-tree --write-tree` (git 2.38+).
|
* Uses `git merge-tree --write-tree` (git 2.38+).
|
||||||
*/
|
*/
|
||||||
checkMergeability(repoPath: string, sourceBranch: string, targetBranch: string): Promise<MergeabilityResult>;
|
checkMergeability(repoPath: string, sourceBranch: string, targetBranch: string): Promise<MergeabilityResult>;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Fetch all refs from a remote.
|
||||||
|
* Defaults to 'origin' if no remote specified.
|
||||||
|
*/
|
||||||
|
fetchRemote(repoPath: string, remote?: string): Promise<void>;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Fast-forward a local branch to match its remote-tracking counterpart.
|
||||||
|
* No-op if already up to date. Throws if fast-forward is not possible
|
||||||
|
* (i.e. the branches have diverged).
|
||||||
|
*/
|
||||||
|
fastForwardBranch(repoPath: string, branch: string, remote?: string): Promise<void>;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -195,4 +195,17 @@ export class SimpleGitBranchManager implements BranchManager {
|
|||||||
log.debug({ repoPath, sourceBranch, targetBranch }, 'merge-tree check: clean');
|
log.debug({ repoPath, sourceBranch, targetBranch }, 'merge-tree check: clean');
|
||||||
return { mergeable: true };
|
return { mergeable: true };
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async fetchRemote(repoPath: string, remote = 'origin'): Promise<void> {
|
||||||
|
const git = simpleGit(repoPath);
|
||||||
|
await git.fetch(remote);
|
||||||
|
log.info({ repoPath, remote }, 'fetched remote');
|
||||||
|
}
|
||||||
|
|
||||||
|
async fastForwardBranch(repoPath: string, branch: string, remote = 'origin'): Promise<void> {
|
||||||
|
const git = simpleGit(repoPath);
|
||||||
|
const remoteBranch = `${remote}/${branch}`;
|
||||||
|
await git.raw(['merge', '--ff-only', remoteBranch, branch]);
|
||||||
|
log.info({ repoPath, branch, remoteBranch }, 'fast-forwarded branch');
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user