Skip to content

Commit

Permalink
add git revert command
Browse files Browse the repository at this point in the history
  • Loading branch information
sergiolms committed Nov 21, 2024
1 parent 693969d commit b3280b6
Show file tree
Hide file tree
Showing 8 changed files with 151 additions and 21 deletions.
26 changes: 13 additions & 13 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -19566,29 +19566,29 @@
"bundle": "webpack --mode production",
"bundle:extension": "webpack --mode production --config-name extension:node",
"clean": "pnpx rimraf --glob dist out .vscode-test .vscode-test-web .eslintcache* tsconfig*.tsbuildinfo",
"clean:all": "pnpm run clean && pnpm run clean:deps",
"clean:all": "npm run clean && npm run clean:deps",
"clean:lint": "pnpx rimraf .eslintcache",
"clean:deps": "pnpx rimraf node_modules",
"copy:images": "webpack --config webpack.config.images.mjs",
"graph:link": "pnpm link @gitkraken/gitkraken-components",
"graph:link:main": "pushd \"../GitKrakenComponents\" && pnpm link && popd && pnpm graph:link",
"graph:unlink": "pnpm unlink @gitkraken/gitkraken-components && pnpm install --force",
"graph:unlink:main": "pnpm graph:unlink && pushd \"../GitKrakenComponents\" && pnpm unlink && popd",
"graph:link": "npm link @gitkraken/gitkraken-components",
"graph:link:main": "pushd \"../GitKrakenComponents\" && npm link && popd && npm graph:link",
"graph:unlink": "npm unlink @gitkraken/gitkraken-components && npm install --force",
"graph:unlink:main": "npm graph:unlink && pushd \"../GitKrakenComponents\" && npm unlink && popd",
"icons:apply": "node ./scripts/applyIconsContribution.mjs",
"icons:svgo": "svgo -q -f ./images/icons/ --config svgo.config.js",
"lint": "pnpm run clean:lint && eslint .",
"lint:fix": "pnpm run clean:lint && eslint . --fix",
"lint:webviews": "pnpm run clean:lint && eslint \"src/webviews/apps/**/*.ts?(x)\"",
"lint": "npm run clean:lint && eslint .",
"lint:fix": "npm run clean:lint && eslint . --fix",
"lint:webviews": "npm run clean:lint && eslint \"src/webviews/apps/**/*.ts?(x)\"",
"package": "vsce package --no-dependencies",
"package-pre": "pnpm run patch-pre && pnpm run package --pre-release",
"package-pre": "npm run patch-pre && npm run package --pre-release",
"patch-pre": "node ./scripts/applyPreReleasePatch.mjs",
"prep-release": "node ./scripts/prep-release.mjs",
"pretty": "prettier --config .prettierrc --write .",
"pretty:check": "prettier --config .prettierrc --check .",
"pub": "vsce publish --no-dependencies",
"pub-pre": "vsce publish --no-dependencies --pre-release",
"rebuild": "pnpm run reset && pnpm run build",
"reset": "pnpm run clean && pnpm install --force",
"rebuild": "npm run reset && npm run build",
"reset": "npm run clean && npm install --force",
"test": "vscode-test",
"test:e2e": "playwright test -c tests/e2e/playwright.config.ts",
"watch": "webpack --watch --mode development",
Expand All @@ -19602,8 +19602,8 @@
"update-dts:main": "pushd \"src/@types\" && pnpx @vscode/dts main && popd",
"update-emoji": "node ./scripts/generateEmojiShortcodeMap.mjs",
"update-licenses": "node ./scripts/generateLicenses.mjs",
"pretest": "pnpm run build:tests",
"vscode:prepublish": "pnpm run bundle"
"pretest": "npm run build:tests",
"vscode:prepublish": "npm run bundle"
},
"dependencies": {
"@gitkraken/gitkraken-components": "10.7.0",
Expand Down
16 changes: 13 additions & 3 deletions src/commands/git/revert.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,10 @@ import type { GitLog } from '../../git/models/log';
import type { GitRevisionReference } from '../../git/models/reference';
import { getReferenceLabel } from '../../git/models/reference';
import type { Repository } from '../../git/models/repository';
import { showGenericErrorMessage } from '../../messages';
import type { FlagsQuickPickItem } from '../../quickpicks/items/flags';
import { createFlagsQuickPickItem } from '../../quickpicks/items/flags';
import { Logger } from '../../system/logger';
import type { ViewsWithRepositoryFolders } from '../../views/viewBase';
import type {
PartialStepState,
Expand Down Expand Up @@ -71,8 +73,16 @@ export class RevertGitCommand extends QuickCommand<State> {
return false;
}

execute(state: RevertStepState<State<GitRevisionReference[]>>) {
state.repo.revert(...state.flags, ...state.references.map(c => c.ref).reverse());
async execute(state: RevertStepState<State<GitRevisionReference[]>>) {
const references = state.references.map(c => c.ref).reverse();
for (const ref of references) {
try {
await state.repo.git.revert(ref, state.flags);
} catch (ex) {
Logger.error(ex, this.title);
void showGenericErrorMessage(ex.message);
}
}
}

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

endSteps(state);
this.execute(state as RevertStepState<State<GitRevisionReference[]>>);
await this.execute(state as RevertStepState<State<GitRevisionReference[]>>);
}

return state.counter < 0 ? StepResultBreak : undefined;
Expand Down
24 changes: 24 additions & 0 deletions src/env/node/git/git.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@ import {
PullErrorReason,
PushError,
PushErrorReason,
RevertError,
RevertErrorReason,
StashPushError,
StashPushErrorReason,
TagError,
Expand Down Expand Up @@ -105,6 +107,7 @@ export const GitErrors = {
tagNotFound: /tag .* not found/i,
invalidTagName: /invalid tag name/i,
remoteRejected: /rejected because the remote contains work/i,
revertHasConflicts: /(error: could not revert .*) (hint: After resolving the conflicts)/gi,
};

const GitWarnings = {
Expand Down Expand Up @@ -173,6 +176,12 @@ const tagErrorAndReason: [RegExp, TagErrorReason][] = [
[GitErrors.remoteRejected, TagErrorReason.RemoteRejected],
];

const revertErrorAndReason = [
[GitErrors.badRevision, RevertErrorReason.BadRevision],
[GitErrors.invalidObjectName, RevertErrorReason.InvalidObjectName],
[GitErrors.revertHasConflicts, RevertErrorReason.Conflict],
];

export class Git {
/** Map of running git commands -- avoids running duplicate overlaping commands */
private readonly pendingCommands = new Map<string, Promise<string | Buffer>>();
Expand Down Expand Up @@ -1588,6 +1597,21 @@ export class Git {
return this.git<string>({ cwd: repoPath }, 'reset', '-q', '--', ...pathspecs);
}

revert(repoPath: string, ...args: string[]) {
try {
return this.git<string>({ cwd: repoPath }, 'revert', ...args);
} catch (ex) {
const msg: string = ex?.toString() ?? '';
for (const [error, reason] of revertErrorAndReason) {
if (error.test(msg)) {
throw new RevertError(reason, ex);
}
}

throw new RevertError(RevertErrorReason.Other, ex);
}
}

async rev_list(
repoPath: string,
ref: string,
Expand Down
19 changes: 19 additions & 0 deletions src/env/node/git/localGitProvider.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ import {
PullError,
PushError,
PushErrorReason,
RevertError,
StashApplyError,
StashApplyErrorReason,
StashPushError,
Expand Down Expand Up @@ -6082,6 +6083,24 @@ export class LocalGitProvider implements GitProvider, Disposable {
return worktrees;
}

@log()
async revert(repoPath: string, ref: string, options?: { edit?: boolean }): Promise<void> {
const args = [];
if (options.edit !== undefined) {
args.push(options.edit ? '--edit' : '--no-edit');
}

try {
await this.git.revert(repoPath, ...args, ref);
} catch (ex) {
if (ex instanceof RevertError) {
throw ex.WithRef(ref);
}

throw ex;
}
}

@log()
// eslint-disable-next-line @typescript-eslint/require-await
async getWorktreesDefaultUri(repoPath: string): Promise<Uri | undefined> {
Expand Down
59 changes: 59 additions & 0 deletions src/git/errors.ts
Original file line number Diff line number Diff line change
Expand Up @@ -567,3 +567,62 @@ export class TagError extends Error {
return this;
}
}

export const enum RevertErrorReason {
BadRevision,
InvalidObjectName,
Conflict,
Other,
}

export class RevertError extends Error {
static is(ex: unknown, reason?: RevertErrorReason): ex is RevertError {
return ex instanceof RevertError && (reason == null || ex.reason === reason);
}

readonly original?: Error;
readonly reason: RevertErrorReason | undefined;
ref?: string;

constructor(reason?: RevertErrorReason, original?: Error, ref?: string);
constructor(message?: string, original?: Error);
constructor(messageOrReason: string | RevertErrorReason | undefined, original?: Error, ref?: string) {
let message;
let reason: RevertErrorReason | undefined;
if (messageOrReason == null) {
message = 'Unable to revert';
} else if (typeof messageOrReason === 'string') {
message = messageOrReason;
reason = undefined;
} else {
reason = messageOrReason;
message = RevertError.buildRevertErrorMessage(reason, ref);
}
super(message);

this.original = original;
this.reason = reason;
this.ref = ref;
Error.captureStackTrace?.(this, RevertError);
}

WithRef(ref: string): this {
this.ref = ref;
this.message = RevertError.buildRevertErrorMessage(this.reason, ref);
return this;
}

private static buildRevertErrorMessage(reason?: RevertErrorReason, ref?: string): string {
const baseMessage = `Unable to revert${ref ? ` revision '${ref}'` : ''}`;
switch (reason) {
case RevertErrorReason.BadRevision:
return `${baseMessage} because it is not a valid revision.`;
case RevertErrorReason.InvalidObjectName:
return `${baseMessage} because it is not a valid object name.`;
case RevertErrorReason.Conflict:
return `${baseMessage} it has unresolved conflicts. Resolve the conflicts and try again.`;
default:
return `${baseMessage}.`;
}
}
}
1 change: 1 addition & 0 deletions src/git/gitProvider.ts
Original file line number Diff line number Diff line change
Expand Up @@ -125,6 +125,7 @@ export interface GitProviderRepository {
addRemote?(repoPath: string, name: string, url: string, options?: { fetch?: boolean }): Promise<void>;
pruneRemote?(repoPath: string, name: string): Promise<void>;
removeRemote?(repoPath: string, name: string): Promise<void>;
revert?(repoPath: string, ref: string, options?: { edit?: boolean }): Promise<void>;

applyUnreachableCommitForPatch?(
repoPath: string,
Expand Down
22 changes: 22 additions & 0 deletions src/git/gitProviderService.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1334,6 +1334,28 @@ export class GitProviderService implements Disposable {
return provider.removeRemote(path, name);
}

@log()
async revert(repoPath: string | Uri, ref: string, flags: string[] | undefined = []): Promise<void> {
const { provider, path } = this.getProvider(repoPath);
if (provider.revert == null) throw new ProviderNotSupportedError(provider.descriptor.name);

const options: { edit?: boolean } = {};
for (const flag of flags) {
switch (flag) {
case '--edit':
options.edit = true;
break;
case '--no-edit':
options.edit = false;
break;
default:
break;
}
}

return provider.revert(path, ref, options);
}

@log()
applyChangesToWorkingFile(uri: GitUri, ref1?: string, ref2?: string): Promise<void> {
const { provider } = this.getProvider(uri);
Expand Down
5 changes: 0 additions & 5 deletions src/git/models/repository.ts
Original file line number Diff line number Diff line change
Expand Up @@ -870,11 +870,6 @@ export class Repository implements Disposable {
}
}

@log()
revert(...args: string[]) {
void this.runTerminalCommand('revert', ...args);
}

async setRemoteAsDefault(remote: GitRemote, value: boolean = true) {
await this.container.storage.storeWorkspace('remote:default', value ? remote.name : undefined);

Expand Down

0 comments on commit b3280b6

Please sign in to comment.