From 0321ca5d96714f50116453b3272dcecb9920f23d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=C3=A3o=20Moreno?= Date: Fri, 27 Nov 2020 12:22:04 +0100 Subject: [PATCH] fixes #106664 --- extensions/git/src/api/api1.ts | 2 +- extensions/git/src/api/git.d.ts | 1 + extensions/git/src/remoteSource.ts | 46 ++++++++++++++++--- extensions/github/src/remoteSourceProvider.ts | 28 +++++++++-- extensions/github/src/typings/git.d.ts | 2 + 5 files changed, 67 insertions(+), 12 deletions(-) diff --git a/extensions/git/src/api/api1.ts b/extensions/git/src/api/api1.ts index 8bcdba4411262..0a309c6f96b63 100644 --- a/extensions/git/src/api/api1.ts +++ b/extensions/git/src/api/api1.ts @@ -357,7 +357,7 @@ export function registerAPICommands(extension: GitExtensionImpl): Disposable { return; } - return pickRemoteSource(extension.model, opts); + return pickRemoteSource(extension.model, opts as any); })); return Disposable.from(...disposables); diff --git a/extensions/git/src/api/git.d.ts b/extensions/git/src/api/git.d.ts index f63b0b12c3c72..b9a09632b665b 100644 --- a/extensions/git/src/api/git.d.ts +++ b/extensions/git/src/api/git.d.ts @@ -212,6 +212,7 @@ export interface RemoteSourceProvider { readonly icon?: string; // codicon name readonly supportsQuery?: boolean; getRemoteSources(query?: string): ProviderResult; + getBranches?(url: string): ProviderResult; publishRepository?(repository: Repository): Promise; } diff --git a/extensions/git/src/remoteSource.ts b/extensions/git/src/remoteSource.ts index 3676832c5c434..cbe437d6af0e1 100644 --- a/extensions/git/src/remoteSource.ts +++ b/extensions/git/src/remoteSource.ts @@ -82,9 +82,17 @@ export interface PickRemoteSourceOptions { readonly providerLabel?: (provider: RemoteSourceProvider) => string; readonly urlLabel?: string; readonly providerName?: string; + readonly branch?: boolean; // then result is PickRemoteSourceResult } -export async function pickRemoteSource(model: Model, options: PickRemoteSourceOptions = {}): Promise { +export interface PickRemoteSourceResult { + readonly url: string; + readonly branch?: string; +} + +export async function pickRemoteSource(model: Model, options: PickRemoteSourceOptions & { branch?: false | undefined }): Promise; +export async function pickRemoteSource(model: Model, options: PickRemoteSourceOptions & { branch: true }): Promise; +export async function pickRemoteSource(model: Model, options: PickRemoteSourceOptions = {}): Promise { const quickpick = window.createQuickPick<(QuickPickItem & { provider?: RemoteSourceProvider, url?: string })>(); quickpick.ignoreFocusOut = true; @@ -93,7 +101,7 @@ export async function pickRemoteSource(model: Model, options: PickRemoteSourceOp .filter(provider => provider.name === options.providerName)[0]; if (provider) { - return await pickProviderSource(provider); + return await pickProviderSource(provider, options); } } @@ -127,24 +135,48 @@ export async function pickRemoteSource(model: Model, options: PickRemoteSourceOp if (result.url) { return result.url; } else if (result.provider) { - return await pickProviderSource(result.provider); + return await pickProviderSource(result.provider, options); } } return undefined; } -async function pickProviderSource(provider: RemoteSourceProvider): Promise { +async function pickProviderSource(provider: RemoteSourceProvider, options: PickRemoteSourceOptions = {}): Promise { const quickpick = new RemoteSourceProviderQuickPick(provider); const remote = await quickpick.pick(); + let url: string | undefined; + if (remote) { if (typeof remote.url === 'string') { - return remote.url; + url = remote.url; } else if (remote.url.length > 0) { - return await window.showQuickPick(remote.url, { ignoreFocusOut: true, placeHolder: localize('pick url', "Choose a URL to clone from.") }); + url = await window.showQuickPick(remote.url, { ignoreFocusOut: true, placeHolder: localize('pick url', "Choose a URL to clone from.") }); } } - return undefined; + if (!url || !options.branch) { + return url; + } + + if (!provider.getBranches) { + return { url }; + } + + const branches = await provider.getBranches(url); + + if (!branches) { + return { url }; + } + + const branch = await window.showQuickPick(branches, { + placeHolder: localize('branch name', "Branch name") + }); + + if (!branch) { + return { url }; + } + + return { url, branch }; } diff --git a/extensions/github/src/remoteSourceProvider.ts b/extensions/github/src/remoteSourceProvider.ts index f16bf07cf7a43..c2803fe26784b 100644 --- a/extensions/github/src/remoteSourceProvider.ts +++ b/extensions/github/src/remoteSourceProvider.ts @@ -8,6 +8,12 @@ import { getOctokit } from './auth'; import { Octokit } from '@octokit/rest'; import { publishRepository } from './publish'; +function parse(url: string): { owner: string, repo: string } | undefined { + const match = /^https:\/\/github\.com\/([^/]+)\/([^/]+)\.git/i.exec(url) + || /^git@github\.com:([^/]+)\/([^/]+)\.git/i.exec(url); + return (match && { owner: match[1], repo: match[2] }) ?? undefined; +} + function asRemoteSource(raw: any): RemoteSource { return { name: `$(github) ${raw.full_name}`, @@ -30,11 +36,10 @@ export class GithubRemoteSourceProvider implements RemoteSourceProvider { const octokit = await getOctokit(); if (query) { - const match = /^https:\/\/github\.com\/([^/]+)\/([^/]+)\.git/i.exec(query) - || /^git@github\.com:([^/]+)\/([^/]+)\.git/i.exec(query); + const repository = parse(query); - if (match) { - const raw = await octokit.repos.get({ owner: match[1], repo: match[2] }); + if (repository) { + const raw = await octokit.repos.get(repository); return [asRemoteSource(raw.data)]; } } @@ -75,6 +80,21 @@ export class GithubRemoteSourceProvider implements RemoteSourceProvider { return raw.data.items.map(asRemoteSource); } + async getBranches(url: string): Promise { + const repository = parse(url); + + if (!repository) { + return []; + } + + const octokit = await getOctokit(); + const branches = await octokit.repos.listBranches(repository); + const repo = await octokit.repos.get(repository); + const defaultBranch = repo.data.default_branch; + + return branches.data.map(b => b.name).sort((a, b) => a === defaultBranch ? -1 : b === defaultBranch ? 1 : 0); + } + publishRepository(repository: Repository): Promise { return publishRepository(this.gitAPI, repository); } diff --git a/extensions/github/src/typings/git.d.ts b/extensions/github/src/typings/git.d.ts index 54f21b96dbb49..b9a09632b665b 100644 --- a/extensions/github/src/typings/git.d.ts +++ b/extensions/github/src/typings/git.d.ts @@ -130,6 +130,7 @@ export interface CommitOptions { signoff?: boolean; signCommit?: boolean; empty?: boolean; + noVerify?: boolean; } export interface BranchQuery { @@ -211,6 +212,7 @@ export interface RemoteSourceProvider { readonly icon?: string; // codicon name readonly supportsQuery?: boolean; getRemoteSources(query?: string): ProviderResult; + getBranches?(url: string): ProviderResult; publishRepository?(repository: Repository): Promise; }