Path: blob/main/src/vs/workbench/contrib/git/common/utils.ts
13401 views
/*---------------------------------------------------------------------------------------------1* Copyright (c) Microsoft Corporation. All rights reserved.2* Licensed under the MIT License. See License.txt in the project root for license information.3*--------------------------------------------------------------------------------------------*/45import { equalsIgnoreCase } from '../../../../base/common/strings.js';6import { URI } from '../../../../base/common/uri.js';7import { GitRemote, GitRepositoryState } from './gitService.js';89export function hasGitHubRemotes(repositoryState: GitRepositoryState): boolean {10const hosts = ['github.com', 'ghe.com'];11const remotes = getOrderedRemotes(repositoryState!)12.filter(remote => !!remote.fetchUrl)13.map(remote => parseRemoteUrl(remote.fetchUrl!));1415for (const remote of remotes) {16if (!remote?.host) {17continue;18}1920if (21hosts.some(host => equalsIgnoreCase(remote.host, host)) ||22hosts.some(host => remote.host.endsWith(host))23) {24return true;25}26}2728return false;29}3031function getOrderedRemotes(repositoryState: GitRepositoryState): readonly GitRemote[] {32if (repositoryState.remotes.length < 2) {33return repositoryState.remotes;34}3536const remotes = new Map<string, GitRemote>();3738// If there's an upstream remote, use that39const remoteIndex = repositoryState.remotes.findIndex(r => r.name === repositoryState.HEAD?.upstream?.remote);40if (remoteIndex !== -1) {41const fetchUrl = repositoryState.remotes[remoteIndex].fetchUrl;42if (fetchUrl) {43remotes.set(repositoryState.remotes[remoteIndex].name, repositoryState.remotes[remoteIndex]);44}45}4647// If there's a remote named "origin", use that48const originIndex = repositoryState.remotes.findIndex(r => r.name === 'origin');49if (originIndex !== -1) {50const fetchUrl = repositoryState.remotes[originIndex].fetchUrl;51if (fetchUrl) {52remotes.set(repositoryState.remotes[originIndex].name, repositoryState.remotes[originIndex]);53}54}5556// Add everything else57for (const remote of repositoryState.remotes) {58if (!remotes.has(remote.name)) {59remotes.set(remote.name, remote);60}61}6263return Array.from(remotes.values());64}6566function parseRemoteUrl(fetchUrl: string): { host: string; rawHost: string; path: string } | undefined {67fetchUrl = fetchUrl.trim();68try {69// Normalize git shorthand syntax ([email protected]:user/repo.git) into an explicit ssh:// url70// See https://git-scm.com/docs/git-clone/2.35.0#_git_urls71if (/^[\w\d\-]+@/i.test(fetchUrl)) {72const parts = fetchUrl.split(':');73if (parts.length !== 2) {74return undefined;75}76fetchUrl = 'ssh://' + parts[0] + '/' + parts[1];77}7879const repoUrl = URI.parse(fetchUrl);80const authority = repoUrl.authority;81const path = repoUrl.path;82if (!(equalsIgnoreCase(repoUrl.scheme, 'ssh') || equalsIgnoreCase(repoUrl.scheme, 'https') || equalsIgnoreCase(repoUrl.scheme, 'http'))) {83return;84}8586const splitAuthority = authority.split('@');87if (splitAuthority.length > 2) { // Invalid, too many @ symbols88return undefined;89}9091const extractedHost = splitAuthority.at(-1);92if (!extractedHost) {93return;94}9596const rawHost = extractedHost97.toLowerCase()98.replace(/:\d+$/, ''); // Remove optional port99100const normalizedHost = rawHost101.replace(/^[\w\-]+-/, '') // Remove common ssh syntax: abc-github.com102.replace(/-[\w\-]+$/, '');// Remove common ssh syntax: github.com-abc103104return { host: normalizedHost, rawHost, path: path };105} catch (err) {106return undefined;107}108}109110111