Skip to content

Commit 6337cd0

Browse files
committed
add BranchError and BranchErrorReason
1 parent 7924d2a commit 6337cd0

File tree

2 files changed

+86
-1
lines changed

2 files changed

+86
-1
lines changed

src/env/node/git/git.ts

Lines changed: 32 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,10 @@ const textDecoder = new TextDecoder('utf8');
7575
const rootSha = '4b825dc642cb6eb9a060e54bf8d69288fbee4904';
7676

7777
export const GitErrors = {
78+
noRemoteReference: /unable to delete '.+?': remote ref does not exist/i,
79+
invalidBranchName: /fatal: '.+?' is not a valid branch name/i,
80+
branchAlreadyExists: /fatal: A branch named '.+?' already exists/i,
81+
branchNotFullyMerged: /error: The branch '.+?' is not fully merged/i,
7882
badRevision: /bad revision '(.*?)'/i,
7983
cantLockRef: /cannot lock ref|unable to update local ref/i,
8084
changesWouldBeOverwritten: /Your local changes to the following files would be overwritten/i,
@@ -523,7 +527,34 @@ export class Git {
523527
}
524528

525529
async branch(repoPath: string, ...args: string[]): Promise<void> {
526-
return this.git<string>({ cwd: repoPath }, 'branch', ...args);
530+
try {
531+
await this.git<string>({ cwd: repoPath }, 'branch', ...args);
532+
} catch (ex) {
533+
const msg: string = ex?.toString() ?? '';
534+
let reason: BranchErrorReason = BranchErrorReason.Other;
535+
switch (true) {
536+
case GitErrors.noRemoteReference.test(msg) || GitErrors.noRemoteReference.test(ex.stderr ?? ''):
537+
reason = BranchErrorReason.NoRemoteReference;
538+
break;
539+
case GitErrors.invalidBranchName.test(msg) || GitErrors.invalidBranchName.test(ex.stderr ?? ''):
540+
reason = BranchErrorReason.InvalidBranchName;
541+
break;
542+
case GitErrors.branchAlreadyExists.test(msg) || GitErrors.branchAlreadyExists.test(ex.stderr ?? ''):
543+
reason = BranchErrorReason.BranchAlreadyExists;
544+
break;
545+
case GitErrors.branchNotFullyMerged.test(msg) || GitErrors.branchNotFullyMerged.test(ex.stderr ?? ''):
546+
reason = BranchErrorReason.BranchNotFullyMerged;
547+
break;
548+
case GitErrors.branchNotYetBorn.test(msg) || GitErrors.branchNotYetBorn.test(ex.stderr ?? ''):
549+
reason = BranchErrorReason.BranchNotYetBorn;
550+
break;
551+
case GitErrors.branchFastForwardRejected.test(msg) ||
552+
GitErrors.branchFastForwardRejected.test(ex.stderr ?? ''):
553+
reason = BranchErrorReason.BranchFastForwardRejected;
554+
break;
555+
}
556+
throw new BranchError(reason, ex);
557+
}
527558
}
528559

529560
branch__set_upstream(repoPath: string, branch: string, remote: string, remoteBranch: string) {

src/git/errors.ts

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -218,6 +218,60 @@ export class PushError extends Error {
218218
}
219219
}
220220

221+
export const enum BranchErrorReason {
222+
BranchAlreadyExists,
223+
BranchNotFullyMerged,
224+
NoRemoteReference,
225+
InvalidBranchName,
226+
Other,
227+
}
228+
229+
export class BranchError extends Error {
230+
static is(ex: unknown, reason?: BranchErrorReason): ex is BranchError {
231+
return ex instanceof BranchError && (reason == null || ex.reason === reason);
232+
}
233+
234+
readonly original?: Error;
235+
readonly reason: BranchErrorReason | undefined;
236+
237+
constructor(reason?: BranchErrorReason, original?: Error, branch?: string);
238+
constructor(message?: string, original?: Error);
239+
constructor(messageOrReason: string | BranchErrorReason | undefined, original?: Error, branch?: string) {
240+
let message;
241+
const baseMessage = `Unable to perform action on branch${branch ? ` '${branch}'` : ''}`;
242+
let reason: BranchErrorReason | undefined;
243+
if (messageOrReason == null) {
244+
message = baseMessage;
245+
} else if (typeof messageOrReason === 'string') {
246+
message = messageOrReason;
247+
reason = undefined;
248+
} else {
249+
reason = messageOrReason;
250+
switch (reason) {
251+
case BranchErrorReason.BranchAlreadyExists:
252+
message = `${baseMessage} because it already exists.`;
253+
break;
254+
case BranchErrorReason.BranchNotFullyMerged:
255+
message = `${baseMessage} because it is not fully merged.`;
256+
break;
257+
case BranchErrorReason.NoRemoteReference:
258+
message = `${baseMessage} because the remote reference does not exist.`;
259+
break;
260+
case BranchErrorReason.InvalidBranchName:
261+
message = `${baseMessage} because the branch name is invalid.`;
262+
break;
263+
default:
264+
message = baseMessage;
265+
}
266+
}
267+
super(message);
268+
269+
this.original = original;
270+
this.reason = reason;
271+
Error.captureStackTrace?.(this, BranchError);
272+
}
273+
}
274+
221275
export const enum PullErrorReason {
222276
Conflict,
223277
GitIdentity,

0 commit comments

Comments
 (0)