Skip to content

Commit 2b7e01b

Browse files
committed
Refactors Git provider into sub-providers
- Patch operations - Staging operations - Stash operations - Status operations - Worktree operations Adds GitCache to centralize the caching for the git providers
1 parent d0762e5 commit 2b7e01b

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

53 files changed

+2020
-2031
lines changed

src/@types/global.d.ts

+24
Original file line numberDiff line numberDiff line change
@@ -40,4 +40,28 @@ export declare global {
4040
: never;
4141

4242
export type UnwrapCustomEvent<T> = T extends CustomEvent<infer U> ? U : never;
43+
44+
export type RemoveFirstArg<F> = F extends {
45+
(first: any, ...args: infer A1): infer R1;
46+
(first: any, ...args: infer A2): infer R2;
47+
(first: any, ...args: infer A3): infer R3;
48+
(first: any, ...args: infer A4): infer R4;
49+
}
50+
? ((...args: A1) => R1) & ((...args: A2) => R2) & ((...args: A3) => R3) & ((...args: A4) => R4)
51+
: F extends {
52+
(first: any, ...args: infer A1): infer R1;
53+
(first: any, ...args: infer A2): infer R2;
54+
(first: any, ...args: infer A3): infer R3;
55+
}
56+
? ((...args: A1) => R1) & ((...args: A2) => R2) & ((...args: A3) => R3)
57+
: F extends {
58+
(first: any, ...args: infer A1): infer R1;
59+
(first: any, ...args: infer A2): infer R2;
60+
}
61+
? ((...args: A1) => R1) & ((...args: A2) => R2)
62+
: F extends {
63+
(first: any, ...args: infer A1): infer R1;
64+
}
65+
? (...args: A1) => R1
66+
: never;
4367
}

src/annotations/gutterChangesAnnotationProvider.ts

+3-4
Original file line numberDiff line numberDiff line change
@@ -131,10 +131,9 @@ export class GutterChangesAnnotationProvider extends AnnotationProviderBase<Chan
131131
localChanges = false;
132132
}
133133
} else {
134-
const status = await this.container.git.getStatusForFile(
135-
this.trackedDocument.uri.repoPath!,
136-
this.trackedDocument.uri,
137-
);
134+
const status = await this.container.git
135+
.status(this.trackedDocument.uri.repoPath!)
136+
.getStatusForFile?.(this.trackedDocument.uri);
138137
const commits = status?.getPseudoCommits(
139138
this.container,
140139
await this.container.git.getCurrentUser(this.trackedDocument.uri.repoPath!),

src/commands/closeUnchangedFiles.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ export class CloseUnchangedFilesCommand extends GlCommandBase {
2727
const repository = await getRepositoryOrShowPicker('Close All Unchanged Files');
2828
if (repository == null) return;
2929

30-
const status = await this.container.git.getStatus(repository.uri);
30+
const status = await this.container.git.status(repository.uri).getStatus();
3131
if (status == null) {
3232
void window.showWarningMessage('Unable to close unchanged files');
3333

src/commands/diffLineWithWorking.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -63,7 +63,7 @@ export class DiffLineWithWorkingCommand extends ActiveEditorCommand {
6363

6464
// If the line is uncommitted, use previous commit (or index if the file is staged)
6565
if (args.commit.isUncommitted) {
66-
const status = await this.container.git.getStatusForFile(gitUri.repoPath!, gitUri);
66+
const status = await this.container.git.status(gitUri.repoPath!).getStatusForFile?.(gitUri);
6767
if (status?.indexStatus != null) {
6868
lhsSha = uncommittedStaged;
6969
lhsUri = this.container.git.getAbsoluteUri(

src/commands/diffWithRevision.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -61,7 +61,7 @@ export class DiffWithRevisionCommand extends ActiveEditorCommand {
6161
getState: async () => {
6262
const items: (CommandQuickPickItem | DirectiveQuickPickItem)[] = [];
6363

64-
const status = await this.container.git.getStatus(gitUri.repoPath);
64+
const status = await this.container.git.status(gitUri.repoPath!).getStatus();
6565
if (status != null) {
6666
for (const f of status.files) {
6767
if (f.workingTreeStatus === '?' || f.workingTreeStatus === '!') {

src/commands/diffWithRevisionFrom.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,7 @@ export class DiffWithRevisionFromCommand extends ActiveEditorCommand {
4949
if (args?.stash) {
5050
const title = `Open Changes with Stash${pad(GlyphChars.Dot, 2, 2)}`;
5151
const pick = await showStashPicker(
52-
this.container.git.getStash(gitUri.repoPath),
52+
this.container.git.stash(gitUri.repoPath)?.getStash(),
5353
`${title}${gitUri.getFormattedFileName({ truncateTo: quickPickTitleMaxChars - title.length })}`,
5454
'Choose a stash to compare with',
5555
{

src/commands/diffWithWorking.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -76,7 +76,7 @@ export class DiffWithWorkingCommand extends ActiveEditorCommand {
7676

7777
// If we are a fake "staged" sha, check the status
7878
if (gitUri.isUncommittedStaged) {
79-
const status = await this.container.git.getStatusForFile(gitUri.repoPath!, gitUri);
79+
const status = await this.container.git.status(gitUri.repoPath!).getStatusForFile?.(gitUri);
8080
if (status?.indexStatus != null) {
8181
void (await executeCommand<DiffWithCommandArgs>(GlCommand.DiffWith, {
8282
repoPath: gitUri.repoPath,

src/commands/externalDiff.ts

+2-2
Original file line numberDiff line numberDiff line change
@@ -88,7 +88,7 @@ export class ExternalDiffCommand extends GlCommandBase {
8888
const repository = await getRepositoryOrShowPicker('Open All Changes (difftool)');
8989
if (repository == null) return undefined;
9090

91-
const status = await this.container.git.getStatus(repository.uri);
91+
const status = await this.container.git.status(repository.uri).getStatus();
9292
if (status == null) {
9393
return window.showInformationMessage("The repository doesn't have any changes");
9494
}
@@ -130,7 +130,7 @@ export class ExternalDiffCommand extends GlCommandBase {
130130
if (!repoPath) return;
131131

132132
const uri = editor.document.uri;
133-
const status = await this.container.git.getStatusForFile(repoPath, uri);
133+
const status = await this.container.git.status(repoPath).getStatusForFile?.(uri);
134134
if (status == null) {
135135
void window.showInformationMessage("The current file doesn't have any changes");
136136

src/commands/git/pull.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -193,7 +193,7 @@ export class PullGitCommand extends QuickCommand<State> {
193193
}
194194
} else {
195195
const [repo] = state.repos;
196-
const [status, lastFetched] = await Promise.all([repo.git.getStatus(), repo.getLastFetched()]);
196+
const [status, lastFetched] = await Promise.all([repo.git.status().getStatus(), repo.getLastFetched()]);
197197

198198
let lastFetchedOn = '';
199199
if (lastFetched !== 0) {

src/commands/git/push.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -294,7 +294,7 @@ export class PushGitCommand extends QuickCommand<State> {
294294
}
295295
}
296296
} else {
297-
const status = await repo.git.getStatus();
297+
const status = await repo.git.status().getStatus();
298298

299299
const branch: GitBranchReference = {
300300
refType: 'branch',

src/commands/git/stash.ts

+4-4
Original file line numberDiff line numberDiff line change
@@ -322,7 +322,7 @@ export class StashGitCommand extends QuickCommand<State> {
322322
while (this.canStepsContinue(state)) {
323323
if (state.counter < 3 || state.reference == null) {
324324
const result: StepResult<GitStashReference> = yield* pickStashStep(state, context, {
325-
gitStash: await this.container.git.getStash(state.repo.path),
325+
gitStash: await state.repo.git.stash()?.getStash(),
326326
placeholder: (_context, stash) =>
327327
stash == null
328328
? `No stashes found in ${state.repo.formattedName}`
@@ -427,7 +427,7 @@ export class StashGitCommand extends QuickCommand<State> {
427427
while (this.canStepsContinue(state)) {
428428
if (state.counter < 3 || !state.references?.length) {
429429
const result: StepResult<GitStashReference[]> = yield* pickStashesStep(state, context, {
430-
gitStash: await this.container.git.getStash(state.repo.path),
430+
gitStash: await state.repo.git.stash()?.getStash(),
431431
placeholder: (_context, stash) =>
432432
stash == null ? `No stashes found in ${state.repo.formattedName}` : 'Choose stashes to delete',
433433
picked: state.references?.map(r => r.ref),
@@ -481,7 +481,7 @@ export class StashGitCommand extends QuickCommand<State> {
481481
while (this.canStepsContinue(state)) {
482482
if (state.counter < 3 || state.reference == null) {
483483
const result: StepResult<GitStashCommit> = yield* pickStashStep(state, context, {
484-
gitStash: await this.container.git.getStash(state.repo.path),
484+
gitStash: await state.repo.git.stash()?.getStash(),
485485
placeholder: (_context, stash) =>
486486
stash == null ? `No stashes found in ${state.repo.formattedName}` : 'Choose a stash',
487487
picked: state.reference?.ref,
@@ -719,7 +719,7 @@ export class StashGitCommand extends QuickCommand<State> {
719719
while (this.canStepsContinue(state)) {
720720
if (state.counter < 3 || state.reference == null) {
721721
const result: StepResult<GitStashReference> = yield* pickStashStep(state, context, {
722-
gitStash: await this.container.git.getStash(state.repo.path),
722+
gitStash: await state.repo.git.stash()?.getStash(),
723723
placeholder: (_context, stash) =>
724724
stash == null ? `No stashes found in ${state.repo.formattedName}` : 'Choose a stash to rename',
725725
picked: state.reference?.ref,

src/commands/git/status.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -82,7 +82,7 @@ export class StatusGitCommand extends QuickCommand<State> {
8282
}
8383
}
8484

85-
context.status = (await state.repo.git.getStatus())!;
85+
context.status = (await state.repo.git.status().getStatus())!;
8686
if (context.status == null) return;
8787

8888
context.title = `${this.title}${pad(GlyphChars.Dot, 2, 2)}${getReferenceLabel(

src/commands/git/switch.ts

+3-4
Original file line numberDiff line numberDiff line change
@@ -201,10 +201,9 @@ export class SwitchGitCommand extends QuickCommand<State> {
201201
if (isBranchReference(state.reference) && !state.reference.remote) {
202202
state.createBranch = undefined;
203203

204-
const worktree = await this.container.git.getWorktree(
205-
state.reference.repoPath,
206-
w => w.branch?.name === state.reference!.name,
207-
);
204+
const worktree = await this.container.git
205+
.worktrees(state.reference.repoPath)
206+
?.getWorktree(w => w.branch?.name === state.reference!.name);
208207
if (worktree != null && !worktree.isDefault) {
209208
if (state.fastForwardTo != null) {
210209
state.repos[0].merge('--ff-only', state.fastForwardTo.ref);

src/commands/git/worktree.ts

+8-10
Original file line numberDiff line numberDiff line change
@@ -381,7 +381,7 @@ export class WorktreeGitCommand extends QuickCommand<State> {
381381

382382
private async *createCommandSteps(state: CreateStepState, context: Context): AsyncStepResultGenerator<void> {
383383
if (context.defaultUri == null) {
384-
context.defaultUri = await state.repo.git.getWorktreesDefaultUri();
384+
context.defaultUri = await state.repo.git.worktrees()?.getWorktreesDefaultUri();
385385
}
386386

387387
if (state.flags == null) {
@@ -835,7 +835,7 @@ export class WorktreeGitCommand extends QuickCommand<State> {
835835
}
836836

837837
private async *deleteCommandSteps(state: DeleteStepState, context: Context): StepGenerator {
838-
context.worktrees = await state.repo.git.getWorktrees();
838+
context.worktrees = (await state.repo.git.worktrees()?.getWorktrees()) ?? [];
839839

840840
if (state.flags == null) {
841841
state.flags = [];
@@ -900,7 +900,7 @@ export class WorktreeGitCommand extends QuickCommand<State> {
900900
}
901901
}
902902

903-
await state.repo.git.deleteWorktree(uri, { force: force });
903+
await state.repo.git.worktrees()?.deleteWorktree(uri, { force: force });
904904
succeeded = true;
905905
} catch (ex) {
906906
skipHasChangesPrompt = false;
@@ -1044,7 +1044,7 @@ export class WorktreeGitCommand extends QuickCommand<State> {
10441044
while (this.canStepsContinue(state)) {
10451045
if (state.counter < 3 || state.worktree == null) {
10461046
context.title = getTitle(state.subcommand);
1047-
context.worktrees ??= await state.repo.git.getWorktrees();
1047+
context.worktrees ??= (await state.repo.git.worktrees()?.getWorktrees()) ?? [];
10481048

10491049
const result = yield* pickWorktreeStep(state, context, {
10501050
excludeOpened: true,
@@ -1146,7 +1146,7 @@ export class WorktreeGitCommand extends QuickCommand<State> {
11461146
context.title = state?.overrides?.title ?? getTitle(state.subcommand);
11471147

11481148
if (state.counter < 3 || state.worktree == null) {
1149-
context.worktrees ??= await state.repo.git.getWorktrees();
1149+
context.worktrees ??= (await state.repo.git.worktrees()?.getWorktrees()) ?? [];
11501150

11511151
let placeholder;
11521152
switch (state.changes.type) {
@@ -1205,17 +1205,15 @@ export class WorktreeGitCommand extends QuickCommand<State> {
12051205
endSteps(state);
12061206

12071207
try {
1208-
const commit = await this.container.git.createUnreachableCommitForPatch(
1209-
state.worktree.uri,
1208+
const patchProvider = this.container.git.patch(state.worktree.repoPath);
1209+
const commit = await patchProvider?.createUnreachableCommitForPatch(
12101210
state.changes.contents,
12111211
state.changes.baseSha,
12121212
'Copied Changes',
12131213
);
12141214
if (commit == null) return;
12151215

1216-
await this.container.git.applyUnreachableCommitForPatch(state.worktree.uri, commit.sha, {
1217-
stash: false,
1218-
});
1216+
await patchProvider?.applyUnreachableCommitForPatch(commit.sha, { stash: false });
12191217
void window.showInformationMessage(`Changes copied successfully`);
12201218
} catch (ex) {
12211219
if (ex instanceof CancellationError) return;

src/commands/openChangedFiles.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ export class OpenChangedFilesCommand extends GlCommandBase {
2828
const repository = await getRepositoryOrShowPicker('Open All Changed Files');
2929
if (repository == null) return;
3030

31-
const status = await this.container.git.getStatus(repository.uri);
31+
const status = await this.container.git.status(repository.uri).getStatus();
3232
if (status == null) {
3333
void window.showWarningMessage('Unable to open changed files');
3434

src/commands/openFileAtRevision.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -143,7 +143,7 @@ export class OpenFileAtRevisionCommand extends ActiveEditorCommand {
143143
getState: async () => {
144144
const items: (CommandQuickPickItem | DirectiveQuickPickItem)[] = [];
145145

146-
const status = await this.container.git.getStatus(gitUri.repoPath);
146+
const status = await this.container.git.status(gitUri.repoPath!).getStatus();
147147
if (status != null) {
148148
for (const f of status.files) {
149149
if (f.workingTreeStatus === '?' || f.workingTreeStatus === '!') {

src/commands/openFileAtRevisionFrom.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,7 @@ export class OpenFileAtRevisionFromCommand extends ActiveEditorCommand {
4949

5050
const title = `Open Changes with Stash${pad(GlyphChars.Dot, 2, 2)}`;
5151
const pick = await showStashPicker(
52-
this.container.git.getStash(gitUri.repoPath),
52+
this.container.git.stash(gitUri.repoPath)?.getStash(),
5353
`${title}${gitUri.getFormattedFileName({ truncateTo: quickPickTitleMaxChars - title.length })}`,
5454
'Choose a stash to compare with',
5555
// Stashes should always come with files, so this should be fine (but protect it just in case)

src/commands/openOnlyChangedFiles.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ export class OpenOnlyChangedFilesCommand extends GlCommandBase {
2929
const repository = await getRepositoryOrShowPicker('Open Changed & Close Unchanged Files');
3030
if (repository == null) return;
3131

32-
const status = await this.container.git.getStatus(repository.uri);
32+
const status = await this.container.git.status(repository.uri).getStatus();
3333
if (status == null) {
3434
void window.showWarningMessage('Unable to open changed & close unchanged files');
3535

src/commands/patches.ts

+5-8
Original file line numberDiff line numberDiff line change
@@ -210,8 +210,10 @@ export class ApplyPatchFromClipboardCommand extends GlCommandBase {
210210
const patch = await env.clipboard.readText();
211211
let repo = this.container.git.highlander;
212212

213+
const patchProvider = repo?.uri != null ? this.container.git.patch(repo.uri) : undefined;
214+
213215
// Make sure it looks like a valid patch
214-
const valid = patch.length ? await this.container.git.validatePatch(repo?.uri ?? Uri.file(''), patch) : false;
216+
const valid = patch.length ? await patchProvider?.validatePatch(patch) : false;
215217
if (!valid) {
216218
void window.showWarningMessage('No valid patch found in the clipboard');
217219
return;
@@ -221,15 +223,10 @@ export class ApplyPatchFromClipboardCommand extends GlCommandBase {
221223
if (repo == null) return;
222224

223225
try {
224-
const commit = await this.container.git.createUnreachableCommitForPatch(
225-
repo.uri,
226-
patch,
227-
'HEAD',
228-
'Pasted Patch',
229-
);
226+
const commit = await patchProvider?.createUnreachableCommitForPatch(patch, 'HEAD', 'Pasted Patch');
230227
if (commit == null) return;
231228

232-
await this.container.git.applyUnreachableCommitForPatch(repo.uri, commit.sha, { stash: false });
229+
await patchProvider?.applyUnreachableCommitForPatch(commit.sha, { stash: false });
233230
void window.showInformationMessage(`Patch applied successfully`);
234231
} catch (ex) {
235232
if (ex instanceof CancellationError) return;

src/commands/quickCommand.steps.ts

+2-1
Original file line numberDiff line numberDiff line change
@@ -246,7 +246,8 @@ export async function getWorktrees(
246246
},
247247
): Promise<WorktreeQuickPickItem[]> {
248248
const worktrees =
249-
repoOrWorktrees instanceof Repository ? await repoOrWorktrees.git.getWorktrees() : repoOrWorktrees;
249+
repoOrWorktrees instanceof Repository ? await repoOrWorktrees.git.worktrees()?.getWorktrees() : repoOrWorktrees;
250+
if (worktrees == null) return [];
250251

251252
const items = filterMap(
252253
await Promise.allSettled(

src/env/node/git/git.ts

+8
Original file line numberDiff line numberDiff line change
@@ -352,6 +352,14 @@ export class Git {
352352
return (await this.getLocation()).version;
353353
}
354354

355+
async ensureGitVersion(version: string, prefix: string, suffix: string): Promise<void> {
356+
if (await this.isAtLeastVersion(version)) return;
357+
358+
throw new Error(
359+
`${prefix} requires a newer version of Git (>= ${version}) than is currently installed (${await this.version()}).${suffix}`,
360+
);
361+
}
362+
355363
async isAtLeastVersion(minimum: string): Promise<boolean> {
356364
const result = compare(fromString(await this.version()), fromString(minimum));
357365
return result !== -1;

0 commit comments

Comments
 (0)