Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
microsoft
GitHub Repository: microsoft/vscode
Path: blob/main/extensions/git/src/api/api1.ts
3320 views
1
/*---------------------------------------------------------------------------------------------
2
* Copyright (c) Microsoft Corporation. All rights reserved.
3
* Licensed under the MIT License. See License.txt in the project root for license information.
4
*--------------------------------------------------------------------------------------------*/
5
6
/* eslint-disable local/code-no-native-private */
7
8
import { Model } from '../model';
9
import { Repository as BaseRepository, Resource } from '../repository';
10
import { InputBox, Git, API, Repository, Remote, RepositoryState, Branch, ForcePushMode, Ref, Submodule, Commit, Change, RepositoryUIState, Status, LogOptions, APIState, CommitOptions, RefType, CredentialsProvider, BranchQuery, PushErrorHandler, PublishEvent, FetchOptions, RemoteSourceProvider, RemoteSourcePublisher, PostCommitCommandsProvider, RefQuery, BranchProtectionProvider, InitOptions, SourceControlHistoryItemDetailsProvider, GitErrorCodes } from './git';
11
import { Event, SourceControlInputBox, Uri, SourceControl, Disposable, commands, CancellationToken } from 'vscode';
12
import { combinedDisposable, filterEvent, mapEvent } from '../util';
13
import { toGitUri } from '../uri';
14
import { GitExtensionImpl } from './extension';
15
import { GitBaseApi } from '../git-base';
16
import { PickRemoteSourceOptions } from '../typings/git-base';
17
import { OperationKind, OperationResult } from '../operation';
18
19
class ApiInputBox implements InputBox {
20
#inputBox: SourceControlInputBox;
21
22
constructor(inputBox: SourceControlInputBox) { this.#inputBox = inputBox; }
23
24
set value(value: string) { this.#inputBox.value = value; }
25
get value(): string { return this.#inputBox.value; }
26
}
27
28
export class ApiChange implements Change {
29
#resource: Resource;
30
constructor(resource: Resource) { this.#resource = resource; }
31
32
get uri(): Uri { return this.#resource.resourceUri; }
33
get originalUri(): Uri { return this.#resource.original; }
34
get renameUri(): Uri | undefined { return this.#resource.renameResourceUri; }
35
get status(): Status { return this.#resource.type; }
36
}
37
38
export class ApiRepositoryState implements RepositoryState {
39
#repository: BaseRepository;
40
readonly onDidChange: Event<void>;
41
42
constructor(repository: BaseRepository) {
43
this.#repository = repository;
44
this.onDidChange = this.#repository.onDidRunGitStatus;
45
}
46
47
get HEAD(): Branch | undefined { return this.#repository.HEAD; }
48
/**
49
* @deprecated Use ApiRepository.getRefs() instead.
50
*/
51
get refs(): Ref[] { console.warn('Deprecated. Use ApiRepository.getRefs() instead.'); return []; }
52
get remotes(): Remote[] { return [...this.#repository.remotes]; }
53
get submodules(): Submodule[] { return [...this.#repository.submodules]; }
54
get rebaseCommit(): Commit | undefined { return this.#repository.rebaseCommit; }
55
56
get mergeChanges(): Change[] { return this.#repository.mergeGroup.resourceStates.map(r => new ApiChange(r)); }
57
get indexChanges(): Change[] { return this.#repository.indexGroup.resourceStates.map(r => new ApiChange(r)); }
58
get workingTreeChanges(): Change[] { return this.#repository.workingTreeGroup.resourceStates.map(r => new ApiChange(r)); }
59
get untrackedChanges(): Change[] { return this.#repository.untrackedGroup.resourceStates.map(r => new ApiChange(r)); }
60
}
61
62
export class ApiRepositoryUIState implements RepositoryUIState {
63
#sourceControl: SourceControl;
64
readonly onDidChange: Event<void>;
65
66
constructor(sourceControl: SourceControl) {
67
this.#sourceControl = sourceControl;
68
this.onDidChange = mapEvent<boolean, void>(this.#sourceControl.onDidChangeSelection, () => null);
69
}
70
71
get selected(): boolean { return this.#sourceControl.selected; }
72
}
73
74
export class ApiRepository implements Repository {
75
#repository: BaseRepository;
76
77
readonly rootUri: Uri;
78
readonly inputBox: InputBox;
79
readonly state: RepositoryState;
80
readonly ui: RepositoryUIState;
81
82
readonly onDidCommit: Event<void>;
83
readonly onDidCheckout: Event<void>;
84
85
constructor(repository: BaseRepository) {
86
this.#repository = repository;
87
88
this.rootUri = Uri.file(this.#repository.root);
89
this.inputBox = new ApiInputBox(this.#repository.inputBox);
90
this.state = new ApiRepositoryState(this.#repository);
91
this.ui = new ApiRepositoryUIState(this.#repository.sourceControl);
92
93
this.onDidCommit = mapEvent<OperationResult, void>(
94
filterEvent(this.#repository.onDidRunOperation, e => e.operation.kind === OperationKind.Commit), () => null);
95
this.onDidCheckout = mapEvent<OperationResult, void>(
96
filterEvent(this.#repository.onDidRunOperation, e => e.operation.kind === OperationKind.Checkout || e.operation.kind === OperationKind.CheckoutTracking), () => null);
97
}
98
99
apply(patch: string, reverse?: boolean): Promise<void> {
100
return this.#repository.apply(patch, reverse);
101
}
102
103
getConfigs(): Promise<{ key: string; value: string }[]> {
104
return this.#repository.getConfigs();
105
}
106
107
getConfig(key: string): Promise<string> {
108
return this.#repository.getConfig(key);
109
}
110
111
setConfig(key: string, value: string): Promise<string> {
112
return this.#repository.setConfig(key, value);
113
}
114
115
unsetConfig(key: string): Promise<string> {
116
return this.#repository.unsetConfig(key);
117
}
118
119
getGlobalConfig(key: string): Promise<string> {
120
return this.#repository.getGlobalConfig(key);
121
}
122
123
getObjectDetails(treeish: string, path: string): Promise<{ mode: string; object: string; size: number }> {
124
return this.#repository.getObjectDetails(treeish, path);
125
}
126
127
detectObjectType(object: string): Promise<{ mimetype: string; encoding?: string }> {
128
return this.#repository.detectObjectType(object);
129
}
130
131
buffer(ref: string, filePath: string): Promise<Buffer> {
132
return this.#repository.buffer(ref, filePath);
133
}
134
135
show(ref: string, path: string): Promise<string> {
136
return this.#repository.show(ref, path);
137
}
138
139
getCommit(ref: string): Promise<Commit> {
140
return this.#repository.getCommit(ref);
141
}
142
143
add(paths: string[]) {
144
return this.#repository.add(paths.map(p => Uri.file(p)));
145
}
146
147
revert(paths: string[]) {
148
return this.#repository.revert(paths.map(p => Uri.file(p)));
149
}
150
151
clean(paths: string[]) {
152
return this.#repository.clean(paths.map(p => Uri.file(p)));
153
}
154
155
diff(cached?: boolean) {
156
return this.#repository.diff(cached);
157
}
158
159
diffWithHEAD(): Promise<Change[]>;
160
diffWithHEAD(path: string): Promise<string>;
161
diffWithHEAD(path?: string): Promise<string | Change[]> {
162
return this.#repository.diffWithHEAD(path);
163
}
164
165
diffWith(ref: string): Promise<Change[]>;
166
diffWith(ref: string, path: string): Promise<string>;
167
diffWith(ref: string, path?: string): Promise<string | Change[]> {
168
return this.#repository.diffWith(ref, path);
169
}
170
171
diffIndexWithHEAD(): Promise<Change[]>;
172
diffIndexWithHEAD(path: string): Promise<string>;
173
diffIndexWithHEAD(path?: string): Promise<string | Change[]> {
174
return this.#repository.diffIndexWithHEAD(path);
175
}
176
177
diffIndexWith(ref: string): Promise<Change[]>;
178
diffIndexWith(ref: string, path: string): Promise<string>;
179
diffIndexWith(ref: string, path?: string): Promise<string | Change[]> {
180
return this.#repository.diffIndexWith(ref, path);
181
}
182
183
diffBlobs(object1: string, object2: string): Promise<string> {
184
return this.#repository.diffBlobs(object1, object2);
185
}
186
187
diffBetween(ref1: string, ref2: string): Promise<Change[]>;
188
diffBetween(ref1: string, ref2: string, path: string): Promise<string>;
189
diffBetween(ref1: string, ref2: string, path?: string): Promise<string | Change[]> {
190
return this.#repository.diffBetween(ref1, ref2, path);
191
}
192
193
hashObject(data: string): Promise<string> {
194
return this.#repository.hashObject(data);
195
}
196
197
createBranch(name: string, checkout: boolean, ref?: string | undefined): Promise<void> {
198
return this.#repository.branch(name, checkout, ref);
199
}
200
201
deleteBranch(name: string, force?: boolean): Promise<void> {
202
return this.#repository.deleteBranch(name, force);
203
}
204
205
getBranch(name: string): Promise<Branch> {
206
return this.#repository.getBranch(name);
207
}
208
209
getBranches(query: BranchQuery, cancellationToken?: CancellationToken): Promise<Ref[]> {
210
return this.#repository.getBranches(query, cancellationToken);
211
}
212
213
getBranchBase(name: string): Promise<Branch | undefined> {
214
return this.#repository.getBranchBase(name);
215
}
216
217
setBranchUpstream(name: string, upstream: string): Promise<void> {
218
return this.#repository.setBranchUpstream(name, upstream);
219
}
220
221
getRefs(query: RefQuery, cancellationToken?: CancellationToken): Promise<Ref[]> {
222
return this.#repository.getRefs(query, cancellationToken);
223
}
224
225
checkIgnore(paths: string[]): Promise<Set<string>> {
226
return this.#repository.checkIgnore(paths);
227
}
228
229
getMergeBase(ref1: string, ref2: string): Promise<string | undefined> {
230
return this.#repository.getMergeBase(ref1, ref2);
231
}
232
233
tag(name: string, message: string, ref?: string | undefined): Promise<void> {
234
return this.#repository.tag({ name, message, ref });
235
}
236
237
deleteTag(name: string): Promise<void> {
238
return this.#repository.deleteTag(name);
239
}
240
241
status(): Promise<void> {
242
return this.#repository.status();
243
}
244
245
checkout(treeish: string): Promise<void> {
246
return this.#repository.checkout(treeish);
247
}
248
249
addRemote(name: string, url: string): Promise<void> {
250
return this.#repository.addRemote(name, url);
251
}
252
253
removeRemote(name: string): Promise<void> {
254
return this.#repository.removeRemote(name);
255
}
256
257
renameRemote(name: string, newName: string): Promise<void> {
258
return this.#repository.renameRemote(name, newName);
259
}
260
261
fetch(arg0?: FetchOptions | string | undefined,
262
ref?: string | undefined,
263
depth?: number | undefined,
264
prune?: boolean | undefined
265
): Promise<void> {
266
if (arg0 !== undefined && typeof arg0 !== 'string') {
267
return this.#repository.fetch(arg0);
268
}
269
270
return this.#repository.fetch({ remote: arg0, ref, depth, prune });
271
}
272
273
pull(unshallow?: boolean): Promise<void> {
274
return this.#repository.pull(undefined, unshallow);
275
}
276
277
push(remoteName?: string, branchName?: string, setUpstream: boolean = false, force?: ForcePushMode): Promise<void> {
278
return this.#repository.pushTo(remoteName, branchName, setUpstream, force);
279
}
280
281
blame(path: string): Promise<string> {
282
return this.#repository.blame(path);
283
}
284
285
log(options?: LogOptions): Promise<Commit[]> {
286
return this.#repository.log(options);
287
}
288
289
commit(message: string, opts?: CommitOptions): Promise<void> {
290
return this.#repository.commit(message, { ...opts, postCommitCommand: null });
291
}
292
293
merge(ref: string): Promise<void> {
294
return this.#repository.merge(ref);
295
}
296
297
mergeAbort(): Promise<void> {
298
return this.#repository.mergeAbort();
299
}
300
301
applyStash(index?: number): Promise<void> {
302
return this.#repository.applyStash(index);
303
}
304
305
popStash(index?: number): Promise<void> {
306
return this.#repository.popStash(index);
307
}
308
309
dropStash(index?: number): Promise<void> {
310
return this.#repository.dropStash(index);
311
}
312
}
313
314
export class ApiGit implements Git {
315
#model: Model;
316
317
private _env: { [key: string]: string } | undefined;
318
319
constructor(model: Model) { this.#model = model; }
320
321
get path(): string { return this.#model.git.path; }
322
323
get env(): { [key: string]: string } {
324
if (this._env === undefined) {
325
this._env = Object.freeze(this.#model.git.env);
326
}
327
328
return this._env;
329
}
330
}
331
332
export class ApiImpl implements API {
333
#model: Model;
334
readonly git: ApiGit;
335
336
constructor(model: Model) {
337
this.#model = model;
338
this.git = new ApiGit(this.#model);
339
}
340
341
get state(): APIState {
342
return this.#model.state;
343
}
344
345
get onDidChangeState(): Event<APIState> {
346
return this.#model.onDidChangeState;
347
}
348
349
get onDidPublish(): Event<PublishEvent> {
350
return this.#model.onDidPublish;
351
}
352
353
get onDidOpenRepository(): Event<Repository> {
354
return mapEvent(this.#model.onDidOpenRepository, r => new ApiRepository(r));
355
}
356
357
get onDidCloseRepository(): Event<Repository> {
358
return mapEvent(this.#model.onDidCloseRepository, r => new ApiRepository(r));
359
}
360
361
get repositories(): Repository[] {
362
return this.#model.repositories.map(r => new ApiRepository(r));
363
}
364
365
toGitUri(uri: Uri, ref: string): Uri {
366
return toGitUri(uri, ref);
367
}
368
369
getRepository(uri: Uri): Repository | null {
370
const result = this.#model.getRepository(uri);
371
return result ? new ApiRepository(result) : null;
372
}
373
374
async getRepositoryRoot(uri: Uri): Promise<Uri | null> {
375
const repository = this.getRepository(uri);
376
if (repository) {
377
return repository.rootUri;
378
}
379
380
try {
381
const root = await this.#model.git.getRepositoryRoot(uri.fsPath);
382
return Uri.file(root);
383
} catch (err) {
384
if (
385
err.gitErrorCode === GitErrorCodes.NotAGitRepository ||
386
err.gitErrorCode === GitErrorCodes.NotASafeGitRepository
387
) {
388
return null;
389
}
390
391
throw err;
392
}
393
}
394
395
async init(root: Uri, options?: InitOptions): Promise<Repository | null> {
396
const path = root.fsPath;
397
await this.#model.git.init(path, options);
398
await this.#model.openRepository(path);
399
return this.getRepository(root) || null;
400
}
401
402
async openRepository(root: Uri): Promise<Repository | null> {
403
if (root.scheme !== 'file') {
404
return null;
405
}
406
407
await this.#model.openRepository(root.fsPath);
408
return this.getRepository(root) || null;
409
}
410
411
registerRemoteSourceProvider(provider: RemoteSourceProvider): Disposable {
412
const disposables: Disposable[] = [];
413
414
if (provider.publishRepository) {
415
disposables.push(this.#model.registerRemoteSourcePublisher(provider as RemoteSourcePublisher));
416
}
417
disposables.push(GitBaseApi.getAPI().registerRemoteSourceProvider(provider));
418
419
return combinedDisposable(disposables);
420
}
421
422
registerRemoteSourcePublisher(publisher: RemoteSourcePublisher): Disposable {
423
return this.#model.registerRemoteSourcePublisher(publisher);
424
}
425
426
registerCredentialsProvider(provider: CredentialsProvider): Disposable {
427
return this.#model.registerCredentialsProvider(provider);
428
}
429
430
registerPostCommitCommandsProvider(provider: PostCommitCommandsProvider): Disposable {
431
return this.#model.registerPostCommitCommandsProvider(provider);
432
}
433
434
registerPushErrorHandler(handler: PushErrorHandler): Disposable {
435
return this.#model.registerPushErrorHandler(handler);
436
}
437
438
registerSourceControlHistoryItemDetailsProvider(provider: SourceControlHistoryItemDetailsProvider): Disposable {
439
return this.#model.registerSourceControlHistoryItemDetailsProvider(provider);
440
}
441
442
registerBranchProtectionProvider(root: Uri, provider: BranchProtectionProvider): Disposable {
443
return this.#model.registerBranchProtectionProvider(root, provider);
444
}
445
}
446
447
function getRefType(type: RefType): string {
448
switch (type) {
449
case RefType.Head: return 'Head';
450
case RefType.RemoteHead: return 'RemoteHead';
451
case RefType.Tag: return 'Tag';
452
}
453
454
return 'unknown';
455
}
456
457
function getStatus(status: Status): string {
458
switch (status) {
459
case Status.INDEX_MODIFIED: return 'INDEX_MODIFIED';
460
case Status.INDEX_ADDED: return 'INDEX_ADDED';
461
case Status.INDEX_DELETED: return 'INDEX_DELETED';
462
case Status.INDEX_RENAMED: return 'INDEX_RENAMED';
463
case Status.INDEX_COPIED: return 'INDEX_COPIED';
464
case Status.MODIFIED: return 'MODIFIED';
465
case Status.DELETED: return 'DELETED';
466
case Status.UNTRACKED: return 'UNTRACKED';
467
case Status.IGNORED: return 'IGNORED';
468
case Status.INTENT_TO_ADD: return 'INTENT_TO_ADD';
469
case Status.INTENT_TO_RENAME: return 'INTENT_TO_RENAME';
470
case Status.TYPE_CHANGED: return 'TYPE_CHANGED';
471
case Status.ADDED_BY_US: return 'ADDED_BY_US';
472
case Status.ADDED_BY_THEM: return 'ADDED_BY_THEM';
473
case Status.DELETED_BY_US: return 'DELETED_BY_US';
474
case Status.DELETED_BY_THEM: return 'DELETED_BY_THEM';
475
case Status.BOTH_ADDED: return 'BOTH_ADDED';
476
case Status.BOTH_DELETED: return 'BOTH_DELETED';
477
case Status.BOTH_MODIFIED: return 'BOTH_MODIFIED';
478
}
479
480
return 'UNKNOWN';
481
}
482
483
export function registerAPICommands(extension: GitExtensionImpl): Disposable {
484
const disposables: Disposable[] = [];
485
486
disposables.push(commands.registerCommand('git.api.getRepositories', () => {
487
const api = extension.getAPI(1);
488
return api.repositories.map(r => r.rootUri.toString());
489
}));
490
491
disposables.push(commands.registerCommand('git.api.getRepositoryState', (uri: string) => {
492
const api = extension.getAPI(1);
493
const repository = api.getRepository(Uri.parse(uri));
494
495
if (!repository) {
496
return null;
497
}
498
499
const state = repository.state;
500
501
const ref = (ref: Ref | undefined) => (ref && { ...ref, type: getRefType(ref.type) });
502
const change = (change: Change) => ({
503
uri: change.uri.toString(),
504
originalUri: change.originalUri.toString(),
505
renameUri: change.renameUri?.toString(),
506
status: getStatus(change.status)
507
});
508
509
return {
510
HEAD: ref(state.HEAD),
511
refs: state.refs.map(ref),
512
remotes: state.remotes,
513
submodules: state.submodules,
514
rebaseCommit: state.rebaseCommit,
515
mergeChanges: state.mergeChanges.map(change),
516
indexChanges: state.indexChanges.map(change),
517
workingTreeChanges: state.workingTreeChanges.map(change)
518
};
519
}));
520
521
disposables.push(commands.registerCommand('git.api.getRemoteSources', (opts?: PickRemoteSourceOptions) => {
522
return commands.executeCommand('git-base.api.getRemoteSources', opts);
523
}));
524
525
return Disposable.from(...disposables);
526
}
527
528