Git - parse config file for remotes (#164151)

This commit is contained in:
Ladislau Szomoru
2022-10-21 15:57:22 +02:00
committed by GitHub
parent 3c72d30b8c
commit 798cd3bcac
2 changed files with 186 additions and 48 deletions

View File

@@ -773,61 +773,98 @@ export interface Submodule {
}
export function parseGitmodules(raw: string): Submodule[] {
const regex = /\r?\n/g;
let position = 0;
let match: RegExpExecArray | null = null;
const result: Submodule[] = [];
let submodule: Partial<Submodule> = {};
function parseLine(line: string): void {
const sectionMatch = /^\s*\[submodule "([^"]+)"\]\s*$/.exec(line);
if (sectionMatch) {
if (submodule.name && submodule.path && submodule.url) {
result.push(submodule as Submodule);
}
const name = sectionMatch[1];
if (name) {
submodule = { name };
return;
}
for (const submoduleSection of parseGitConfig(raw, 'submodule')) {
if (submoduleSection.label && submoduleSection.properties['path'] && submoduleSection.properties['url']) {
result.push({
name: submoduleSection.label,
path: submoduleSection.properties['path'],
url: submoduleSection.properties['url']
});
}
if (!submodule) {
return;
}
const propertyMatch = /^\s*(\w+)\s*=\s*(.*)$/.exec(line);
if (!propertyMatch) {
return;
}
const [, key, value] = propertyMatch;
switch (key) {
case 'path': submodule.path = value; break;
case 'url': submodule.url = value; break;
}
}
while (match = regex.exec(raw)) {
parseLine(raw.substring(position, match.index));
position = match.index + match[0].length;
}
parseLine(raw.substring(position));
if (submodule.name && submodule.path && submodule.url) {
result.push(submodule as Submodule);
}
return result;
}
interface GitConfigSection {
label?: string;
properties: { [key: string]: string };
}
function parseGitConfig(raw: string, section: string): Iterable<GitConfigSection> {
const sections: GitConfigSection[] = [];
const sectionHeaderRegex = /^\[.+\]$/gm;
const sectionRegex = new RegExp(`^\\s*\\[\\s*${section}\\s*("[^"]+")*\\]$`, 'm');
const parseSectionProperties = (sectionRaw: string): { [key: string]: string } => {
const properties: { [key: string]: string } = {};
for (const propertyLine of sectionRaw.split(/\r?\n/)) {
const propertyMatch = /^\s*(\w+)\s*=\s*(.*)$/.exec(propertyLine);
if (!propertyMatch) {
continue;
}
const [, key, value] = propertyMatch;
if (properties[key]) {
continue;
}
properties[key] = value;
}
return properties;
};
const parseSection = (sectionRaw: string) => {
const sectionMatch = sectionRegex.exec(sectionRaw);
if (!sectionMatch) {
return;
}
sections.push({
label: sectionMatch.length === 2 ? sectionMatch[1].replaceAll('"', '') : undefined,
properties: parseSectionProperties(sectionRaw.substring(sectionMatch[0].length))
});
};
let position = 0;
let match: RegExpExecArray | null = null;
while (match = sectionHeaderRegex.exec(raw)) {
parseSection(raw.substring(position, match.index));
position = match.index;
}
parseSection(raw.substring(position));
return sections;
}
export function parseGitRemotes(raw: string): Remote[] {
const remotes: Remote[] = [];
// Remote sections
for (const remoteSection of parseGitConfig(raw, 'remote')) {
if (!remoteSection.label) {
continue;
}
remotes.push({
name: remoteSection.label,
fetchUrl: remoteSection.properties['url'],
pushUrl: remoteSection.properties['pushurl'] ?? remoteSection.properties['url'],
// https://github.com/microsoft/vscode/issues/45271
isReadOnly: remoteSection.properties['pushurl'] === 'no_push'
});
}
return remotes;
}
const commitRegex = /([0-9a-f]{40})\n(.*)\n(.*)\n(.*)\n(.*)\n(.*)\n(.*)(?:\n([^]*?))?(?:\x00)/gm;
export function parseGitCommits(data: string): Commit[] {
@@ -2160,6 +2197,20 @@ export class Repository {
}
async getRemotes(): Promise<Remote[]> {
try {
// Attempt to parse the config file
const remotes = await this.getRemotesFS();
if (remotes.length === 0) {
throw new Error('No remotes found in the git config file.');
}
return remotes;
}
catch (err) {
this.logger.warn(err.message);
}
// Fallback to using git to determine remotes
const result = await this.exec(['remote', '--verbose']);
const lines = result.stdout.trim().split('\n').filter(l => !!l);
const remotes: MutableRemote[] = [];
@@ -2191,6 +2242,10 @@ export class Repository {
return remotes;
}
async getRemotesFS(): Promise<Remote[]> {
return parseGitRemotes(await fs.readFile(path.join(this.dotGit.commonPath ?? this.dotGit.path, 'config'), 'utf8'));
}
async getBranch(name: string): Promise<Branch> {
if (name === 'HEAD') {
return this.getHEAD();