@@ -688,6 +688,61 @@ export interface Commit {
688688 refNames : string [ ] ;
689689}
690690
691+ interface GitConfigSection {
692+ name : string ;
693+ subSectionName ?: string ;
694+ properties : { [ key : string ] : string } ;
695+ }
696+
697+ class GitConfigParser {
698+ private static readonly _lineSeparator = / \r ? \n / ;
699+
700+ private static readonly _commentRegex = / ^ \s * [ # ; ] .* / ;
701+ private static readonly _emptyLineRegex = / ^ \s * $ / ;
702+ private static readonly _propertyRegex = / ^ \s * ( \w + ) \s * = \s * ( .* ) $ / ;
703+ private static readonly _sectionRegex = / ^ \s * \[ \s * ( [ ^ \] ] + ?) \s * ( \" [ ^ " ] + \" ) * \] \s * $ / ;
704+
705+ static parse ( raw : string , sectionName : string ) : GitConfigSection [ ] {
706+ let section : GitConfigSection | undefined ;
707+ const config : { sections : GitConfigSection [ ] } = { sections : [ ] } ;
708+
709+ const addSection = ( section ?: GitConfigSection ) => {
710+ if ( ! section ) { return ; }
711+ config . sections . push ( section ) ;
712+ } ;
713+
714+ for ( const configFileLine of raw . split ( GitConfigParser . _lineSeparator ) ) {
715+ // Ignore empty lines and comments
716+ if ( GitConfigParser . _emptyLineRegex . test ( configFileLine ) ||
717+ GitConfigParser . _commentRegex . test ( configFileLine ) ) {
718+ continue ;
719+ }
720+
721+ // Section
722+ const sectionMatch = configFileLine . match ( GitConfigParser . _sectionRegex ) ;
723+ if ( sectionMatch ?. length === 3 ) {
724+ addSection ( section ) ;
725+ section = sectionMatch [ 1 ] === sectionName ?
726+ { name : sectionMatch [ 1 ] , subSectionName : sectionMatch [ 2 ] ?. replaceAll ( '"' , '' ) , properties : { } } : undefined ;
727+
728+ continue ;
729+ }
730+
731+ // Properties
732+ if ( section ) {
733+ const propertyMatch = configFileLine . match ( GitConfigParser . _propertyRegex ) ;
734+ if ( propertyMatch ?. length === 3 && ! Object . keys ( section . properties ) . includes ( propertyMatch [ 1 ] ) ) {
735+ section . properties [ propertyMatch [ 1 ] ] = propertyMatch [ 2 ] ;
736+ }
737+ }
738+ }
739+
740+ addSection ( section ) ;
741+
742+ return config . sections ;
743+ }
744+ }
745+
691746export class GitStatusParser {
692747
693748 private lastRaw = '' ;
@@ -761,59 +816,39 @@ export interface Submodule {
761816}
762817
763818export function parseGitmodules ( raw : string ) : Submodule [ ] {
764- const regex = / \r ? \n / g;
765- let position = 0 ;
766- let match : RegExpExecArray | null = null ;
767-
768819 const result : Submodule [ ] = [ ] ;
769- let submodule : Partial < Submodule > = { } ;
770-
771- function parseLine ( line : string ) : void {
772- const sectionMatch = / ^ \s * \[ s u b m o d u l e " ( [ ^ " ] + ) " \] \s * $ / . exec ( line ) ;
773-
774- if ( sectionMatch ) {
775- if ( submodule . name && submodule . path && submodule . url ) {
776- result . push ( submodule as Submodule ) ;
777- }
778-
779- const name = sectionMatch [ 1 ] ;
780-
781- if ( name ) {
782- submodule = { name } ;
783- return ;
784- }
785- }
786820
787- if ( ! submodule ) {
788- return ;
821+ for ( const submoduleSection of GitConfigParser . parse ( raw , 'submodule' ) ) {
822+ if ( submoduleSection . subSectionName && submoduleSection . properties [ 'path' ] && submoduleSection . properties [ 'url' ] ) {
823+ result . push ( {
824+ name : submoduleSection . subSectionName ,
825+ path : submoduleSection . properties [ 'path' ] ,
826+ url : submoduleSection . properties [ 'url' ]
827+ } ) ;
789828 }
829+ }
790830
791- const propertyMatch = / ^ \s * ( \w + ) \s * = \s * ( .* ) $ / . exec ( line ) ;
792-
793- if ( ! propertyMatch ) {
794- return ;
795- }
831+ return result ;
832+ }
796833
797- const [ , key , value ] = propertyMatch ;
834+ export function parseGitRemotes ( raw : string ) : Remote [ ] {
835+ const remotes : Remote [ ] = [ ] ;
798836
799- switch ( key ) {
800- case 'path' : submodule . path = value ; break ;
801- case 'url' : submodule . url = value ; break ;
837+ for ( const remoteSection of GitConfigParser . parse ( raw , 'remote' ) ) {
838+ if ( ! remoteSection . subSectionName ) {
839+ continue ;
802840 }
803- }
804-
805- while ( match = regex . exec ( raw ) ) {
806- parseLine ( raw . substring ( position , match . index ) ) ;
807- position = match . index + match [ 0 ] . length ;
808- }
809841
810- parseLine ( raw . substring ( position ) ) ;
811-
812- if ( submodule . name && submodule . path && submodule . url ) {
813- result . push ( submodule as Submodule ) ;
842+ remotes . push ( {
843+ name : remoteSection . subSectionName ,
844+ fetchUrl : remoteSection . properties [ 'url' ] ,
845+ pushUrl : remoteSection . properties [ 'pushurl' ] ?? remoteSection . properties [ 'url' ] ,
846+ // https://github.com/microsoft/vscode/issues/45271
847+ isReadOnly : remoteSection . properties [ 'pushurl' ] === 'no_push'
848+ } ) ;
814849 }
815850
816- return result ;
851+ return remotes ;
817852}
818853
819854const commitRegex = / ( [ 0 - 9 a - f ] { 40 } ) \n ( .* ) \n ( .* ) \n ( .* ) \n ( .* ) \n ( .* ) \n ( .* ) (?: \n ( [ ^ ] * ?) ) ? (?: \x00 ) / gm;
@@ -2148,6 +2183,20 @@ export class Repository {
21482183 }
21492184
21502185 async getRemotes ( ) : Promise < Remote [ ] > {
2186+ try {
2187+ // Attempt to parse the config file
2188+ const remotes = await this . getRemotesFS ( ) ;
2189+ if ( remotes . length === 0 ) {
2190+ throw new Error ( 'No remotes found in the git config file.' ) ;
2191+ }
2192+
2193+ return remotes ;
2194+ }
2195+ catch ( err ) {
2196+ this . logger . warn ( err . message ) ;
2197+ }
2198+
2199+ // Fallback to using git to determine remotes
21512200 const result = await this . exec ( [ 'remote' , '--verbose' ] ) ;
21522201 const lines = result . stdout . trim ( ) . split ( '\n' ) . filter ( l => ! ! l ) ;
21532202 const remotes : MutableRemote [ ] = [ ] ;
@@ -2179,6 +2228,11 @@ export class Repository {
21792228 return remotes ;
21802229 }
21812230
2231+ private async getRemotesFS ( ) : Promise < Remote [ ] > {
2232+ const raw = await fs . readFile ( path . join ( this . dotGit . commonPath ?? this . dotGit . path , 'config' ) , 'utf8' ) ;
2233+ return parseGitRemotes ( raw ) ;
2234+ }
2235+
21822236 async getBranch ( name : string ) : Promise < Branch > {
21832237 if ( name === 'HEAD' ) {
21842238 return this . getHEAD ( ) ;
0 commit comments