Skip to content

Commit edf4ea5

Browse files
authored
Simplify git clone API (microsoft#272912)
- Don't allow to skip the cache - Only have "none" post commit action - Consolidate post commit logic - I ended up keeping the `Uri | null` return type for the `clone` API. It's just too useful, and there's no reason why we couldn't pass it back if the user didn't decide to open the clone and reload the window.
1 parent 3cc447e commit edf4ea5

File tree

3 files changed

+76
-76
lines changed

3 files changed

+76
-76
lines changed

extensions/git/src/api/api1.ts

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -395,6 +395,11 @@ export class ApiImpl implements API {
395395
}
396396
}
397397

398+
async getRepositoryWorkspace(uri: Uri): Promise<Uri[] | null> {
399+
const workspaces = this.#model.repositoryCache.get(uri.toString());
400+
return workspaces ? workspaces.map(r => Uri.file(r.workspacePath)) : null;
401+
}
402+
398403
async init(root: Uri, options?: InitOptions): Promise<Repository | null> {
399404
const path = root.fsPath;
400405
await this.#model.git.init(path, options);
@@ -404,7 +409,7 @@ export class ApiImpl implements API {
404409

405410
async clone(uri: Uri, options?: CloneOptions): Promise<Uri | null> {
406411
const parentPath = options?.parentPath?.fsPath;
407-
const result = await this.#cloneManager.clone(uri.toString(), { parentPath, recursive: options?.recursive, ref: options?.ref, postCloneAction: options?.postCloneAction, skipCache: options?.skipCache });
412+
const result = await this.#cloneManager.clone(uri.toString(), { parentPath, recursive: options?.recursive, ref: options?.ref, postCloneAction: options?.postCloneAction });
408413
return result ? Uri.file(result) : null;
409414
}
410415

extensions/git/src/api/git.d.ts

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -193,8 +193,7 @@ export interface CloneOptions {
193193
/**
194194
* If no postCloneAction is provided, then the users setting for git.openAfterClone is used.
195195
*/
196-
postCloneAction?: 'none' | 'open' | 'prompt';
197-
skipCache?: boolean;
196+
postCloneAction?: 'none';
198197
}
199198

200199
export interface RefQuery {
@@ -379,9 +378,12 @@ export interface API {
379378
toGitUri(uri: Uri, ref: string): Uri;
380379
getRepository(uri: Uri): Repository | null;
381380
getRepositoryRoot(uri: Uri): Promise<Uri | null>;
381+
getRepositoryWorkspace(uri: Uri): Promise<Uri[] | null>;
382382
init(root: Uri, options?: InitOptions): Promise<Repository | null>;
383383
/**
384-
* @returns The URI of either the cloned repository, or the workspace file or folder which contains the cloned repository.
384+
* Checks the cache of known cloned repositories, and clones if the repository is not found.
385+
* Make sure to pass `postCloneAction` 'none' if you want to have the uri where you can find the repository returned.
386+
* @returns The URI of a folder or workspace file which, when opened, will open the cloned repository.
385387
*/
386388
clone(uri: Uri, options?: CloneOptions): Promise<Uri | null>;
387389
openRepository(root: Uri): Promise<Repository | null>;

extensions/git/src/cloneManager.ts

Lines changed: 65 additions & 72 deletions
Original file line numberDiff line numberDiff line change
@@ -12,14 +12,14 @@ import { RepositoryCache, RepositoryCacheInfo } from './repositoryCache';
1212
import TelemetryReporter from '@vscode/extension-telemetry';
1313
import { Model } from './model';
1414

15-
type PostCloneAction = 'none' | 'open' | 'prompt';
15+
type ApiPostCloneAction = 'none';
16+
enum PostCloneAction { Open, OpenNewWindow, AddToWorkspace, None }
1617

1718
export interface CloneOptions {
1819
parentPath?: string;
1920
ref?: string;
2021
recursive?: boolean;
21-
postCloneAction?: PostCloneAction;
22-
skipCache?: boolean;
22+
postCloneAction?: ApiPostCloneAction;
2323
}
2424

2525
export class CloneManager {
@@ -29,13 +29,13 @@ export class CloneManager {
2929

3030
clone(url?: string, options: CloneOptions = {}) {
3131
const cachedRepository = url ? this.repositoryCache.get(url) : undefined;
32-
if (url && !options.skipCache && cachedRepository && (cachedRepository.length > 0)) {
32+
if (url && cachedRepository && (cachedRepository.length > 0)) {
3333
return this.tryOpenExistingRepository(cachedRepository, url, options.postCloneAction, options.parentPath, options.ref);
3434
}
3535
return this.cloneRepository(url, options.parentPath, options);
3636
}
3737

38-
private async cloneRepository(url?: string, parentPath?: string, options: { recursive?: boolean; ref?: string; postCloneAction?: PostCloneAction } = {}): Promise<string | undefined> {
38+
private async cloneRepository(url?: string, parentPath?: string, options: { recursive?: boolean; ref?: string; postCloneAction?: ApiPostCloneAction } = {}): Promise<string | undefined> {
3939
if (!url || typeof url !== 'string') {
4040
url = await pickRemoteSource({
4141
providerLabel: provider => l10n.t('Clone from {0}', provider.name),
@@ -97,65 +97,7 @@ export class CloneManager {
9797
(progress, token) => this.model.git.clone(url!, { parentPath: parentPath!, progress, recursive: options.recursive, ref: options.ref }, token)
9898
);
9999

100-
const config = workspace.getConfiguration('git');
101-
const openAfterClone = config.get<'always' | 'alwaysNewWindow' | 'whenNoFolderOpen' | 'prompt'>('openAfterClone');
102-
103-
enum PostCloneAction { Open, OpenNewWindow, AddToWorkspace, None }
104-
let action: PostCloneAction | undefined = undefined;
105-
106-
if (options.postCloneAction) {
107-
if (options.postCloneAction === 'open') {
108-
action = PostCloneAction.Open;
109-
} else if (options.postCloneAction === 'none') {
110-
action = PostCloneAction.None;
111-
}
112-
} else {
113-
if (openAfterClone === 'always') {
114-
action = PostCloneAction.Open;
115-
} else if (openAfterClone === 'alwaysNewWindow') {
116-
action = PostCloneAction.OpenNewWindow;
117-
} else if (openAfterClone === 'whenNoFolderOpen' && !workspace.workspaceFolders) {
118-
action = PostCloneAction.Open;
119-
}
120-
}
121-
122-
if (action === undefined) {
123-
let message = l10n.t('Would you like to open the cloned repository?');
124-
const open = l10n.t('Open');
125-
const openNewWindow = l10n.t('Open in New Window');
126-
const choices = [open, openNewWindow];
127-
128-
const addToWorkspace = l10n.t('Add to Workspace');
129-
if (workspace.workspaceFolders) {
130-
message = l10n.t('Would you like to open the cloned repository, or add it to the current workspace?');
131-
choices.push(addToWorkspace);
132-
}
133-
134-
const result = await window.showInformationMessage(message, { modal: true }, ...choices);
135-
136-
action = result === open ? PostCloneAction.Open
137-
: result === openNewWindow ? PostCloneAction.OpenNewWindow
138-
: result === addToWorkspace ? PostCloneAction.AddToWorkspace : undefined;
139-
}
140-
141-
/* __GDPR__
142-
"clone" : {
143-
"owner": "lszomoru",
144-
"outcome" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "comment": "The outcome of the git operation" },
145-
"openFolder": { "classification": "SystemMetaData", "purpose": "PerformanceAndHealth", "isMeasurement": true, "comment": "Indicates whether the folder is opened following the clone operation" }
146-
}
147-
*/
148-
this.telemetryReporter.sendTelemetryEvent('clone', { outcome: 'success' }, { openFolder: action === PostCloneAction.Open || action === PostCloneAction.OpenNewWindow ? 1 : 0 });
149-
150-
const uri = Uri.file(repositoryPath);
151-
152-
if (action === PostCloneAction.Open) {
153-
commands.executeCommand('vscode.openFolder', uri, { forceReuseWindow: true });
154-
} else if (action === PostCloneAction.AddToWorkspace) {
155-
workspace.updateWorkspaceFolders(workspace.workspaceFolders!.length, 0, { uri });
156-
} else if (action === PostCloneAction.OpenNewWindow) {
157-
commands.executeCommand('vscode.openFolder', uri, { forceNewWindow: true });
158-
}
100+
await this.doPostCloneAction(repositoryPath, options.postCloneAction);
159101

160102
return repositoryPath;
161103
} catch (err) {
@@ -183,14 +125,64 @@ export class CloneManager {
183125
}
184126
}
185127

186-
private async doPostCloneAction(target: string, postCloneAction?: PostCloneAction): Promise<undefined> {
187-
const forceReuseWindow = ((workspace.workspaceFile === undefined) && (workspace.workspaceFolders === undefined));
188-
if (postCloneAction === 'open') {
189-
await commands.executeCommand('vscode.openFolder', Uri.file(target), { forceReuseWindow });
128+
private async doPostCloneAction(target: string, postCloneAction?: ApiPostCloneAction): Promise<void> {
129+
const config = workspace.getConfiguration('git');
130+
const openAfterClone = config.get<'always' | 'alwaysNewWindow' | 'whenNoFolderOpen' | 'prompt'>('openAfterClone');
131+
132+
let action: PostCloneAction | undefined = undefined;
133+
134+
if (postCloneAction && postCloneAction === 'none') {
135+
action = PostCloneAction.None;
136+
} else {
137+
if (openAfterClone === 'always') {
138+
action = PostCloneAction.Open;
139+
} else if (openAfterClone === 'alwaysNewWindow') {
140+
action = PostCloneAction.OpenNewWindow;
141+
} else if (openAfterClone === 'whenNoFolderOpen' && !workspace.workspaceFolders) {
142+
action = PostCloneAction.Open;
143+
}
144+
}
145+
146+
if (action === undefined) {
147+
let message = l10n.t('Would you like to open the cloned repository?');
148+
const open = l10n.t('Open');
149+
const openNewWindow = l10n.t('Open in New Window');
150+
const choices = [open, openNewWindow];
151+
152+
const addToWorkspace = l10n.t('Add to Workspace');
153+
if (workspace.workspaceFolders) {
154+
message = l10n.t('Would you like to open the cloned repository, or add it to the current workspace?');
155+
choices.push(addToWorkspace);
156+
}
157+
158+
const result = await window.showInformationMessage(message, { modal: true }, ...choices);
159+
160+
action = result === open ? PostCloneAction.Open
161+
: result === openNewWindow ? PostCloneAction.OpenNewWindow
162+
: result === addToWorkspace ? PostCloneAction.AddToWorkspace : undefined;
163+
}
164+
165+
/* __GDPR__
166+
"clone" : {
167+
"owner": "lszomoru",
168+
"outcome" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "comment": "The outcome of the git operation" },
169+
"openFolder": { "classification": "SystemMetaData", "purpose": "PerformanceAndHealth", "isMeasurement": true, "comment": "Indicates whether the folder is opened following the clone operation" }
170+
}
171+
*/
172+
this.telemetryReporter.sendTelemetryEvent('clone', { outcome: 'success' }, { openFolder: action === PostCloneAction.Open || action === PostCloneAction.OpenNewWindow ? 1 : 0 });
173+
174+
const uri = Uri.file(target);
175+
176+
if (action === PostCloneAction.Open) {
177+
commands.executeCommand('vscode.openFolder', uri, { forceReuseWindow: true });
178+
} else if (action === PostCloneAction.AddToWorkspace) {
179+
workspace.updateWorkspaceFolders(workspace.workspaceFolders!.length, 0, { uri });
180+
} else if (action === PostCloneAction.OpenNewWindow) {
181+
commands.executeCommand('vscode.openFolder', uri, { forceNewWindow: true });
190182
}
191183
}
192184

193-
private async chooseExistingRepository(url: string, existingCachedRepositories: RepositoryCacheInfo[], ref: string | undefined, parentPath?: string, postCloneAction?: PostCloneAction): Promise<string | undefined> {
185+
private async chooseExistingRepository(url: string, existingCachedRepositories: RepositoryCacheInfo[], ref: string | undefined, parentPath?: string, postCloneAction?: ApiPostCloneAction): Promise<string | undefined> {
194186
try {
195187
const items: { label: string; description?: string; item?: RepositoryCacheInfo }[] = existingCachedRepositories.map(knownFolder => {
196188
const isWorkspace = knownFolder.workspacePath.endsWith('.code-workspace');
@@ -213,7 +205,7 @@ export class CloneManager {
213205
}
214206
}
215207

216-
private async tryOpenExistingRepository(cachedRepository: RepositoryCacheInfo[], url: string, postCloneAction?: PostCloneAction, parentPath?: string, ref?: string): Promise<string | undefined> {
208+
private async tryOpenExistingRepository(cachedRepository: RepositoryCacheInfo[], url: string, postCloneAction?: ApiPostCloneAction, parentPath?: string, ref?: string): Promise<string | undefined> {
217209
// Gather existing folders/workspace files (ignore ones that no longer exist)
218210
const existingCachedRepositories: RepositoryCacheInfo[] = (await Promise.all<RepositoryCacheInfo | undefined>(cachedRepository.map(async folder => {
219211
const stat = await fs.promises.stat(folder.workspacePath).catch(() => undefined);
@@ -243,8 +235,9 @@ export class CloneManager {
243235
repoForWorkspace = await this.chooseExistingRepository(url, existingCachedRepositories, ref, parentPath, postCloneAction);
244236
}
245237
if (repoForWorkspace) {
246-
return this.doPostCloneAction(repoForWorkspace, postCloneAction);
238+
await this.doPostCloneAction(repoForWorkspace, postCloneAction);
239+
return repoForWorkspace;
247240
}
248-
return undefined;
241+
return;
249242
}
250243
}

0 commit comments

Comments
 (0)