Skip to content
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.

Commit b3280b6

Browse files
committedNov 21, 2024··
add git revert command
1 parent 693969d commit b3280b6

File tree

8 files changed

+151
-21
lines changed

8 files changed

+151
-21
lines changed
 

‎package.json

+13-13
Original file line numberDiff line numberDiff line change
@@ -19566,29 +19566,29 @@
1956619566
"bundle": "webpack --mode production",
1956719567
"bundle:extension": "webpack --mode production --config-name extension:node",
1956819568
"clean": "pnpx rimraf --glob dist out .vscode-test .vscode-test-web .eslintcache* tsconfig*.tsbuildinfo",
19569-
"clean:all": "pnpm run clean && pnpm run clean:deps",
19569+
"clean:all": "npm run clean && npm run clean:deps",
1957019570
"clean:lint": "pnpx rimraf .eslintcache",
1957119571
"clean:deps": "pnpx rimraf node_modules",
1957219572
"copy:images": "webpack --config webpack.config.images.mjs",
19573-
"graph:link": "pnpm link @gitkraken/gitkraken-components",
19574-
"graph:link:main": "pushd \"../GitKrakenComponents\" && pnpm link && popd && pnpm graph:link",
19575-
"graph:unlink": "pnpm unlink @gitkraken/gitkraken-components && pnpm install --force",
19576-
"graph:unlink:main": "pnpm graph:unlink && pushd \"../GitKrakenComponents\" && pnpm unlink && popd",
19573+
"graph:link": "npm link @gitkraken/gitkraken-components",
19574+
"graph:link:main": "pushd \"../GitKrakenComponents\" && npm link && popd && npm graph:link",
19575+
"graph:unlink": "npm unlink @gitkraken/gitkraken-components && npm install --force",
19576+
"graph:unlink:main": "npm graph:unlink && pushd \"../GitKrakenComponents\" && npm unlink && popd",
1957719577
"icons:apply": "node ./scripts/applyIconsContribution.mjs",
1957819578
"icons:svgo": "svgo -q -f ./images/icons/ --config svgo.config.js",
19579-
"lint": "pnpm run clean:lint && eslint .",
19580-
"lint:fix": "pnpm run clean:lint && eslint . --fix",
19581-
"lint:webviews": "pnpm run clean:lint && eslint \"src/webviews/apps/**/*.ts?(x)\"",
19579+
"lint": "npm run clean:lint && eslint .",
19580+
"lint:fix": "npm run clean:lint && eslint . --fix",
19581+
"lint:webviews": "npm run clean:lint && eslint \"src/webviews/apps/**/*.ts?(x)\"",
1958219582
"package": "vsce package --no-dependencies",
19583-
"package-pre": "pnpm run patch-pre && pnpm run package --pre-release",
19583+
"package-pre": "npm run patch-pre && npm run package --pre-release",
1958419584
"patch-pre": "node ./scripts/applyPreReleasePatch.mjs",
1958519585
"prep-release": "node ./scripts/prep-release.mjs",
1958619586
"pretty": "prettier --config .prettierrc --write .",
1958719587
"pretty:check": "prettier --config .prettierrc --check .",
1958819588
"pub": "vsce publish --no-dependencies",
1958919589
"pub-pre": "vsce publish --no-dependencies --pre-release",
19590-
"rebuild": "pnpm run reset && pnpm run build",
19591-
"reset": "pnpm run clean && pnpm install --force",
19590+
"rebuild": "npm run reset && npm run build",
19591+
"reset": "npm run clean && npm install --force",
1959219592
"test": "vscode-test",
1959319593
"test:e2e": "playwright test -c tests/e2e/playwright.config.ts",
1959419594
"watch": "webpack --watch --mode development",
@@ -19602,8 +19602,8 @@
1960219602
"update-dts:main": "pushd \"src/@types\" && pnpx @vscode/dts main && popd",
1960319603
"update-emoji": "node ./scripts/generateEmojiShortcodeMap.mjs",
1960419604
"update-licenses": "node ./scripts/generateLicenses.mjs",
19605-
"pretest": "pnpm run build:tests",
19606-
"vscode:prepublish": "pnpm run bundle"
19605+
"pretest": "npm run build:tests",
19606+
"vscode:prepublish": "npm run bundle"
1960719607
},
1960819608
"dependencies": {
1960919609
"@gitkraken/gitkraken-components": "10.7.0",

‎src/commands/git/revert.ts

+13-3
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,10 @@ import type { GitLog } from '../../git/models/log';
44
import type { GitRevisionReference } from '../../git/models/reference';
55
import { getReferenceLabel } from '../../git/models/reference';
66
import type { Repository } from '../../git/models/repository';
7+
import { showGenericErrorMessage } from '../../messages';
78
import type { FlagsQuickPickItem } from '../../quickpicks/items/flags';
89
import { createFlagsQuickPickItem } from '../../quickpicks/items/flags';
10+
import { Logger } from '../../system/logger';
911
import type { ViewsWithRepositoryFolders } from '../../views/viewBase';
1012
import type {
1113
PartialStepState,
@@ -71,8 +73,16 @@ export class RevertGitCommand extends QuickCommand<State> {
7173
return false;
7274
}
7375

74-
execute(state: RevertStepState<State<GitRevisionReference[]>>) {
75-
state.repo.revert(...state.flags, ...state.references.map(c => c.ref).reverse());
76+
async execute(state: RevertStepState<State<GitRevisionReference[]>>) {
77+
const references = state.references.map(c => c.ref).reverse();
78+
for (const ref of references) {
79+
try {
80+
await state.repo.git.revert(ref, state.flags);
81+
} catch (ex) {
82+
Logger.error(ex, this.title);
83+
void showGenericErrorMessage(ex.message);
84+
}
85+
}
7686
}
7787

7888
protected async *steps(state: PartialStepState<State>): StepGenerator {
@@ -160,7 +170,7 @@ export class RevertGitCommand extends QuickCommand<State> {
160170
state.flags = result;
161171

162172
endSteps(state);
163-
this.execute(state as RevertStepState<State<GitRevisionReference[]>>);
173+
await this.execute(state as RevertStepState<State<GitRevisionReference[]>>);
164174
}
165175

166176
return state.counter < 0 ? StepResultBreak : undefined;

‎src/env/node/git/git.ts

+24
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,8 @@ import {
2020
PullErrorReason,
2121
PushError,
2222
PushErrorReason,
23+
RevertError,
24+
RevertErrorReason,
2325
StashPushError,
2426
StashPushErrorReason,
2527
TagError,
@@ -105,6 +107,7 @@ export const GitErrors = {
105107
tagNotFound: /tag .* not found/i,
106108
invalidTagName: /invalid tag name/i,
107109
remoteRejected: /rejected because the remote contains work/i,
110+
revertHasConflicts: /(error: could not revert .*) (hint: After resolving the conflicts)/gi,
108111
};
109112

110113
const GitWarnings = {
@@ -173,6 +176,12 @@ const tagErrorAndReason: [RegExp, TagErrorReason][] = [
173176
[GitErrors.remoteRejected, TagErrorReason.RemoteRejected],
174177
];
175178

179+
const revertErrorAndReason = [
180+
[GitErrors.badRevision, RevertErrorReason.BadRevision],
181+
[GitErrors.invalidObjectName, RevertErrorReason.InvalidObjectName],
182+
[GitErrors.revertHasConflicts, RevertErrorReason.Conflict],
183+
];
184+
176185
export class Git {
177186
/** Map of running git commands -- avoids running duplicate overlaping commands */
178187
private readonly pendingCommands = new Map<string, Promise<string | Buffer>>();
@@ -1588,6 +1597,21 @@ export class Git {
15881597
return this.git<string>({ cwd: repoPath }, 'reset', '-q', '--', ...pathspecs);
15891598
}
15901599

1600+
revert(repoPath: string, ...args: string[]) {
1601+
try {
1602+
return this.git<string>({ cwd: repoPath }, 'revert', ...args);
1603+
} catch (ex) {
1604+
const msg: string = ex?.toString() ?? '';
1605+
for (const [error, reason] of revertErrorAndReason) {
1606+
if (error.test(msg)) {
1607+
throw new RevertError(reason, ex);
1608+
}
1609+
}
1610+
1611+
throw new RevertError(RevertErrorReason.Other, ex);
1612+
}
1613+
}
1614+
15911615
async rev_list(
15921616
repoPath: string,
15931617
ref: string,

‎src/env/node/git/localGitProvider.ts

+19
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ import {
3131
PullError,
3232
PushError,
3333
PushErrorReason,
34+
RevertError,
3435
StashApplyError,
3536
StashApplyErrorReason,
3637
StashPushError,
@@ -6082,6 +6083,24 @@ export class LocalGitProvider implements GitProvider, Disposable {
60826083
return worktrees;
60836084
}
60846085

6086+
@log()
6087+
async revert(repoPath: string, ref: string, options?: { edit?: boolean }): Promise<void> {
6088+
const args = [];
6089+
if (options.edit !== undefined) {
6090+
args.push(options.edit ? '--edit' : '--no-edit');
6091+
}
6092+
6093+
try {
6094+
await this.git.revert(repoPath, ...args, ref);
6095+
} catch (ex) {
6096+
if (ex instanceof RevertError) {
6097+
throw ex.WithRef(ref);
6098+
}
6099+
6100+
throw ex;
6101+
}
6102+
}
6103+
60856104
@log()
60866105
// eslint-disable-next-line @typescript-eslint/require-await
60876106
async getWorktreesDefaultUri(repoPath: string): Promise<Uri | undefined> {

‎src/git/errors.ts

+59
Original file line numberDiff line numberDiff line change
@@ -567,3 +567,62 @@ export class TagError extends Error {
567567
return this;
568568
}
569569
}
570+
571+
export const enum RevertErrorReason {
572+
BadRevision,
573+
InvalidObjectName,
574+
Conflict,
575+
Other,
576+
}
577+
578+
export class RevertError extends Error {
579+
static is(ex: unknown, reason?: RevertErrorReason): ex is RevertError {
580+
return ex instanceof RevertError && (reason == null || ex.reason === reason);
581+
}
582+
583+
readonly original?: Error;
584+
readonly reason: RevertErrorReason | undefined;
585+
ref?: string;
586+
587+
constructor(reason?: RevertErrorReason, original?: Error, ref?: string);
588+
constructor(message?: string, original?: Error);
589+
constructor(messageOrReason: string | RevertErrorReason | undefined, original?: Error, ref?: string) {
590+
let message;
591+
let reason: RevertErrorReason | undefined;
592+
if (messageOrReason == null) {
593+
message = 'Unable to revert';
594+
} else if (typeof messageOrReason === 'string') {
595+
message = messageOrReason;
596+
reason = undefined;
597+
} else {
598+
reason = messageOrReason;
599+
message = RevertError.buildRevertErrorMessage(reason, ref);
600+
}
601+
super(message);
602+
603+
this.original = original;
604+
this.reason = reason;
605+
this.ref = ref;
606+
Error.captureStackTrace?.(this, RevertError);
607+
}
608+
609+
WithRef(ref: string): this {
610+
this.ref = ref;
611+
this.message = RevertError.buildRevertErrorMessage(this.reason, ref);
612+
return this;
613+
}
614+
615+
private static buildRevertErrorMessage(reason?: RevertErrorReason, ref?: string): string {
616+
const baseMessage = `Unable to revert${ref ? ` revision '${ref}'` : ''}`;
617+
switch (reason) {
618+
case RevertErrorReason.BadRevision:
619+
return `${baseMessage} because it is not a valid revision.`;
620+
case RevertErrorReason.InvalidObjectName:
621+
return `${baseMessage} because it is not a valid object name.`;
622+
case RevertErrorReason.Conflict:
623+
return `${baseMessage} it has unresolved conflicts. Resolve the conflicts and try again.`;
624+
default:
625+
return `${baseMessage}.`;
626+
}
627+
}
628+
}

‎src/git/gitProvider.ts

+1
Original file line numberDiff line numberDiff line change
@@ -125,6 +125,7 @@ export interface GitProviderRepository {
125125
addRemote?(repoPath: string, name: string, url: string, options?: { fetch?: boolean }): Promise<void>;
126126
pruneRemote?(repoPath: string, name: string): Promise<void>;
127127
removeRemote?(repoPath: string, name: string): Promise<void>;
128+
revert?(repoPath: string, ref: string, options?: { edit?: boolean }): Promise<void>;
128129

129130
applyUnreachableCommitForPatch?(
130131
repoPath: string,

‎src/git/gitProviderService.ts

+22
Original file line numberDiff line numberDiff line change
@@ -1334,6 +1334,28 @@ export class GitProviderService implements Disposable {
13341334
return provider.removeRemote(path, name);
13351335
}
13361336

1337+
@log()
1338+
async revert(repoPath: string | Uri, ref: string, flags: string[] | undefined = []): Promise<void> {
1339+
const { provider, path } = this.getProvider(repoPath);
1340+
if (provider.revert == null) throw new ProviderNotSupportedError(provider.descriptor.name);
1341+
1342+
const options: { edit?: boolean } = {};
1343+
for (const flag of flags) {
1344+
switch (flag) {
1345+
case '--edit':
1346+
options.edit = true;
1347+
break;
1348+
case '--no-edit':
1349+
options.edit = false;
1350+
break;
1351+
default:
1352+
break;
1353+
}
1354+
}
1355+
1356+
return provider.revert(path, ref, options);
1357+
}
1358+
13371359
@log()
13381360
applyChangesToWorkingFile(uri: GitUri, ref1?: string, ref2?: string): Promise<void> {
13391361
const { provider } = this.getProvider(uri);

‎src/git/models/repository.ts

-5
Original file line numberDiff line numberDiff line change
@@ -870,11 +870,6 @@ export class Repository implements Disposable {
870870
}
871871
}
872872

873-
@log()
874-
revert(...args: string[]) {
875-
void this.runTerminalCommand('revert', ...args);
876-
}
877-
878873
async setRemoteAsDefault(remote: GitRemote, value: boolean = true) {
879874
await this.container.storage.storeWorkspace('remote:default', value ? remote.name : undefined);
880875

0 commit comments

Comments
 (0)
Please sign in to comment.