mirror of
https://github.com/getpaseo/paseo.git
synced 2026-09-14 20:36:44 +08:00
fix(server): preserve worktree include cleanup
This commit is contained in:
@@ -39,7 +39,6 @@ export interface CreatePaseoWorktreeInput extends CreateWorktreeCoreInput {
|
||||
export interface CreatePaseoWorktreeResult {
|
||||
worktree: WorktreeConfig;
|
||||
worktreeIncludeSummary?: WorktreeIncludeSummary;
|
||||
worktreeIncludeSummary?: WorktreeIncludeSummary;
|
||||
intent: WorktreeCreationIntent;
|
||||
workspace: PersistedWorkspaceRecord;
|
||||
repoRoot: string;
|
||||
@@ -121,7 +120,6 @@ async function createPaseoWorktreeWithPriority(
|
||||
return {
|
||||
worktree: createdWorktree.worktree,
|
||||
worktreeIncludeSummary: createdWorktree.worktreeIncludeSummary,
|
||||
worktreeIncludeSummary: createdWorktree.worktreeIncludeSummary,
|
||||
intent: createdWorktree.intent,
|
||||
workspace,
|
||||
repoRoot: createdWorktree.repoRoot,
|
||||
|
||||
@@ -1,7 +1,12 @@
|
||||
import { createNameId } from "mnemonic-id";
|
||||
|
||||
import type { ForgeService } from "../services/forge-service.js";
|
||||
import { createWorktree, slugify, validateBranchSlug, type WorktreeConfig } from "../utils/worktree.js";
|
||||
import {
|
||||
createWorktree,
|
||||
slugify,
|
||||
validateBranchSlug,
|
||||
type WorktreeConfig,
|
||||
} from "../utils/worktree.js";
|
||||
import type { WorktreeIncludeSummary } from "../utils/worktree-include.js";
|
||||
import {
|
||||
resolveWorktreeCreationIntent,
|
||||
@@ -39,7 +44,6 @@ export interface CreateWorktreeCoreDeps {
|
||||
export interface CreateWorktreeCoreResult {
|
||||
worktree: WorktreeConfig;
|
||||
worktreeIncludeSummary?: WorktreeIncludeSummary;
|
||||
worktreeIncludeSummary?: WorktreeIncludeSummary;
|
||||
intent: WorktreeCreationIntent;
|
||||
repoRoot: string;
|
||||
created: boolean;
|
||||
|
||||
@@ -1154,12 +1154,18 @@ export async function deletePaseoWorktree({
|
||||
}
|
||||
|
||||
export async function rollbackCreatedPaseoWorktree(
|
||||
options: DeletePaseoWorktreeOptions,
|
||||
options: DeletePaseoWorktreeOptions & { createdBranchName?: string },
|
||||
cause: unknown,
|
||||
): Promise<never> {
|
||||
let cleanupError: unknown;
|
||||
try {
|
||||
await deletePaseoWorktree(options);
|
||||
if (options.createdBranchName && options.cwd) {
|
||||
await runGitCommand(["branch", "--delete", "--force", options.createdBranchName], {
|
||||
cwd: options.cwd,
|
||||
acceptExitCodes: [0, 1],
|
||||
});
|
||||
}
|
||||
} catch (error) {
|
||||
cleanupError = error;
|
||||
}
|
||||
@@ -1228,10 +1234,22 @@ export const createWorktree = async ({
|
||||
}: CreateWorktreeOptions): Promise<CreatedWorktreeConfig> => {
|
||||
const sourcePlan = await resolveWorktreeSourcePlan({ cwd, source, desiredSlug: worktreeSlug });
|
||||
const paseoWorktreesBaseRoot = resolvePaseoWorktreesBaseRoot({ paseoHome, worktreesRoot });
|
||||
const worktreeIncludePlan = await readWorktreeIncludePlan({
|
||||
sourceRoot: cwd,
|
||||
excludedSourceRoots: [paseoWorktreesBaseRoot],
|
||||
});
|
||||
const worktreeIncludePlan = await (async () => {
|
||||
try {
|
||||
return await readWorktreeIncludePlan({
|
||||
sourceRoot: cwd,
|
||||
excludedSourceRoots: [paseoWorktreesBaseRoot],
|
||||
});
|
||||
} catch (error) {
|
||||
if (sourcePlan.createdBranchName) {
|
||||
await runGitCommand(["branch", "--delete", "--force", sourcePlan.createdBranchName], {
|
||||
cwd,
|
||||
acceptExitCodes: [0, 1],
|
||||
});
|
||||
}
|
||||
throw error;
|
||||
}
|
||||
})();
|
||||
let worktreePath = join(await getPaseoWorktreesRoot(cwd, paseoHome, worktreesRoot), worktreeSlug);
|
||||
mkdirSync(dirname(worktreePath), { recursive: true });
|
||||
|
||||
@@ -1289,7 +1307,14 @@ export const createWorktree = async ({
|
||||
};
|
||||
} catch (error) {
|
||||
return rollbackCreatedPaseoWorktree(
|
||||
{ cwd, worktreePath, teardownCwds: [], paseoHome, worktreesBaseRoot: worktreesRoot },
|
||||
{
|
||||
cwd,
|
||||
worktreePath,
|
||||
teardownCwds: [],
|
||||
paseoHome,
|
||||
worktreesBaseRoot: worktreesRoot,
|
||||
createdBranchName: sourcePlan.createdBranchName,
|
||||
},
|
||||
error,
|
||||
);
|
||||
}
|
||||
@@ -1317,6 +1342,7 @@ interface ResolveWorktreeSourcePlanOptions {
|
||||
|
||||
interface WorktreeSourcePlan {
|
||||
branchName: string;
|
||||
createdBranchName?: string;
|
||||
// Display name and exact ref are two different facts. The name cannot round-trip to a
|
||||
// commit — "main" resolves local-first even when the worktree was cut from a fork's
|
||||
// upstream — so comparisons and actions read the ref and the UI reads the name.
|
||||
@@ -1354,6 +1380,7 @@ async function resolveWorktreeSourcePlan({
|
||||
|
||||
return {
|
||||
branchName: newBranchName,
|
||||
createdBranchName: newBranchName,
|
||||
metadataBaseRefName: normalizedBaseBranch,
|
||||
metadataBaseRef: resolvedBaseBranch,
|
||||
changeRequestLookupTarget: createPaseoWorktreeChangeRequestHint({
|
||||
@@ -1374,11 +1401,22 @@ async function resolveWorktreeSourcePlan({
|
||||
} catch {
|
||||
throw new UnknownBranchError({ branchName: source.branchName, cwd });
|
||||
}
|
||||
return {
|
||||
branchName: source.branchName,
|
||||
createdBranchName: source.branchName,
|
||||
metadataBaseRefName: source.branchName,
|
||||
changeRequestLookupTarget: createPaseoWorktreeChangeRequestHint({
|
||||
headRef: source.branchName,
|
||||
localBranchName: source.branchName,
|
||||
}),
|
||||
addArguments: [source.branchName],
|
||||
};
|
||||
}
|
||||
if (await isBranchCheckedOut(cwd, source.branchName)) {
|
||||
const branchName = await resolveUniqueLocalBranchName(cwd, source.branchName);
|
||||
return {
|
||||
branchName,
|
||||
createdBranchName: branchName,
|
||||
metadataBaseRefName: source.branchName,
|
||||
changeRequestLookupTarget: createPaseoWorktreeChangeRequestHint({
|
||||
headRef: branchName,
|
||||
@@ -1447,6 +1485,7 @@ async function resolveWorktreeSourcePlan({
|
||||
|
||||
return {
|
||||
branchName: localBranchName,
|
||||
createdBranchName: localBranchName,
|
||||
metadataBaseRefName: normalizedBaseRefName,
|
||||
changeRequestLookupTarget: createPaseoWorktreeChangeRequestHint({
|
||||
headRef: source.headRef,
|
||||
|
||||
Reference in New Issue
Block a user