Skip to content

Commit 31db995

Browse files
committed
add BranchError and BranchErrorReason
1 parent 4782abf commit 31db995

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
@@ -74,6 +74,10 @@ const textDecoder = new TextDecoder('utf8');
7474
const rootSha = '4b825dc642cb6eb9a060e54bf8d69288fbee4904';
7575

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

511515
async branch(repoPath: string, ...args: string[]): Promise<void> {
512-
return this.git<string>({ cwd: repoPath }, 'branch', ...args);
516+
try {
517+
await this.git<string>({ cwd: repoPath }, 'branch', ...args);
518+
} catch (ex) {
519+
const msg: string = ex?.toString() ?? '';
520+
let reason: BranchErrorReason = BranchErrorReason.Other;
521+
switch (true) {
522+
case GitErrors.noRemoteReference.test(msg) || GitErrors.noRemoteReference.test(ex.stderr ?? ''):
523+
reason = BranchErrorReason.NoRemoteReference;
524+
break;
525+
case GitErrors.invalidBranchName.test(msg) || GitErrors.invalidBranchName.test(ex.stderr ?? ''):
526+
reason = BranchErrorReason.InvalidBranchName;
527+
break;
528+
case GitErrors.branchAlreadyExists.test(msg) || GitErrors.branchAlreadyExists.test(ex.stderr ?? ''):
529+
reason = BranchErrorReason.BranchAlreadyExists;
530+
break;
531+
case GitErrors.branchNotFullyMerged.test(msg) || GitErrors.branchNotFullyMerged.test(ex.stderr ?? ''):
532+
reason = BranchErrorReason.BranchNotFullyMerged;
533+
break;
534+
case GitErrors.branchNotYetBorn.test(msg) || GitErrors.branchNotYetBorn.test(ex.stderr ?? ''):
535+
reason = BranchErrorReason.BranchNotYetBorn;
536+
break;
537+
case GitErrors.branchFastForwardRejected.test(msg) ||
538+
GitErrors.branchFastForwardRejected.test(ex.stderr ?? ''):
539+
reason = BranchErrorReason.BranchFastForwardRejected;
540+
break;
541+
}
542+
throw new BranchError(reason, ex);
543+
}
513544
}
514545

515546
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)