Skip to content

Commit

Permalink
create git reset command
Browse files Browse the repository at this point in the history
  • Loading branch information
sergiolms committed Oct 15, 2024
1 parent 6b54385 commit e6953f6
Show file tree
Hide file tree
Showing 7 changed files with 129 additions and 10 deletions.
13 changes: 10 additions & 3 deletions src/commands/git/reset.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 { GitReference, 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 @@ -69,8 +71,13 @@ export class ResetGitCommand extends QuickCommand<State> {
return this._canSkipConfirm;
}

execute(state: ResetStepState) {
state.repo.reset(...state.flags, state.reference.ref);
async execute(state: ResetStepState) {
try {
await state.repo.git.reset(state.flags, state.reference.ref);
} catch (ex) {
Logger.error(ex, this.title);
void showGenericErrorMessage(ex.message);
}
}

protected async *steps(state: PartialStepState<State>): StepGenerator {
Expand Down Expand Up @@ -156,7 +163,7 @@ export class ResetGitCommand extends QuickCommand<State> {
}

endSteps(state);
this.execute(state as ResetStepState);
await this.execute(state as ResetStepState);
}

return state.counter < 0 ? StepResultBreak : undefined;
Expand Down
30 changes: 28 additions & 2 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,
ResetError,
ResetErrorReason,
StashPushError,
StashPushErrorReason,
WorkspaceUntrustedError,
Expand Down Expand Up @@ -100,6 +102,11 @@ export const GitErrors = {
tagConflict: /! \[rejected\].*\(would clobber existing tag\)/m,
unmergedFiles: /is not possible because you have unmerged files/i,
unstagedChanges: /You have unstaged changes/i,
unmergedChanges: /error:\s*you need to resolve your current index first/i,
ambiguousArgument: /fatal:\s*ambiguous argument ['"].+['"]: unknown revision or path not in the working tree/i,
entryNotUpToDate: /error:\s*Entry ['"].+['"] not uptodate\. Cannot merge\./i,
changesWouldBeOverwritten: /error:\s*Your local changes to the following files would be overwritten/i,
refLocked: /fatal:\s*cannot lock ref ['"].+['"]: unable to create file/i,
};

const GitWarnings = {
Expand Down Expand Up @@ -160,6 +167,14 @@ function getStdinUniqueKey(): number {
type ExitCodeOnlyGitCommandOptions = GitCommandOptions & { exitCodeOnly: true };
export type PushForceOptions = { withLease: true; ifIncludes?: boolean } | { withLease: false; ifIncludes?: never };

const resetErrorAndReason = [
[unmergedChanges, ResetErrorReason.UnmergedChanges],
[ambiguousArgument, ResetErrorReason.AmbiguousArgument],
[entryNotUpToDate, ResetErrorReason.EntryNotUpToDate],
[changesWouldBeOverwritten, ResetErrorReason.LocalChangesWouldBeOverwritten],
[refLocked, ResetErrorReason.RefLocked],
];

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 @@ -1571,8 +1586,19 @@ export class Git {
return this.git<string>({ cwd: repoPath }, 'remote', 'get-url', remote);
}

reset(repoPath: string | undefined, pathspecs: string[]) {
return this.git<string>({ cwd: repoPath }, 'reset', '-q', '--', ...pathspecs);
reset(repoPath: string, pathspecs: string[], ...args: string[]) {
try {
return this.git<string>({ cwd: repoPath }, 'reset', '-q', ...args, '--', ...pathspecs);
} catch (ex) {
const msg: string = ex?.toString() ?? '';
for (const [error, reason] of resetErrorAndReason) {
if (error.test(msg)) {
throw new ResetError(reason, ex);
}
}

throw new ResetError(ResetErrorReason.Other, ex);
}
}

async rev_list(
Expand Down
12 changes: 12 additions & 0 deletions src/env/node/git/localGitProvider.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5784,6 +5784,18 @@ export class LocalGitProvider implements GitProvider, Disposable {
}
}

@log()
async reset(repoPath: string, options?: { hard?: boolean; soft?: boolean }, ref?: string): Promise<void> {
const flags = [];
if (options?.hard) {
flags.push('--hard');
} else if (options?.soft) {
flags.push('--soft');
}

await this.git.reset(repoPath, [], ...flags, ref);
}

@log({ args: { 2: false } })
async runGitCommandViaTerminal(
repoPath: string,
Expand Down
55 changes: 55 additions & 0 deletions src/git/errors.ts
Original file line number Diff line number Diff line change
Expand Up @@ -489,3 +489,58 @@ export class WorktreeDeleteError extends Error {
Error.captureStackTrace?.(this, WorktreeDeleteError);
}
}

export const enum ResetErrorReason {
UnmergedChanges,
AmbiguousArgument,
EntryNotUpToDate,
LocalChangesWouldBeOverwritten,
RefLocked,
Other,
}

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

readonly original?: Error;
readonly reason: ResetErrorReason | undefined;
constructor(reason?: ResetErrorReason, original?: Error);
constructor(message?: string, original?: Error);
constructor(messageOrReason: string | ResetErrorReason | undefined, original?: Error) {
let message;
let reason: ResetErrorReason | undefined;
if (messageOrReason == null) {
message = 'Unable to reset';
} else if (typeof messageOrReason === 'string') {
message = messageOrReason;
reason = undefined;
} else {
reason = messageOrReason;
message = 'Unable to reset';
switch (reason) {
case ResetErrorReason.UnmergedChanges:
message = `${message} because there are unmerged changes`;
break;
case ResetErrorReason.AmbiguousArgument:
message = `${message} because the argument is ambiguous`;
break;
case ResetErrorReason.EntryNotUpToDate:
message = `${message} because the entry is not up to date`;
break;
case ResetErrorReason.LocalChangesWouldBeOverwritten:
message = `${message} because local changes would be overwritten`;
break;
case ResetErrorReason.RefLocked:
message = `${message} because the ref is locked`;
break;
}
}
super(message);

this.original = original;
this.reason = reason;
Error.captureStackTrace?.(this, ResetError);
}
}
2 changes: 2 additions & 0 deletions src/git/gitProvider.ts
Original file line number Diff line number Diff line change
Expand Up @@ -120,6 +120,8 @@ export interface GitProviderRepository {
pruneRemote?(repoPath: string, name: string): Promise<void>;
removeRemote?(repoPath: string, name: string): Promise<void>;

reset?(repoPath: string, options?: { hard?: boolean; soft?: boolean }, ref?: string): Promise<void>;

applyUnreachableCommitForPatch?(
repoPath: string,
ref: 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 @@ -1333,6 +1333,28 @@ export class GitProviderService implements Disposable {
return provider.removeRemote(path, name);
}

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

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

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

@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 @@ -843,11 +843,6 @@ export class Repository implements Disposable {
);
}

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

resume() {
if (!this._suspended) return;

Expand Down

0 comments on commit e6953f6

Please sign in to comment.