Skip to content

Commit 3d229ee

Browse files
committed
add BranchError and BranchErrorReason
1 parent bfa69cf commit 3d229ee

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,
@@ -510,7 +514,34 @@ export class Git {
510514
}
511515

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

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