@@ -688,6 +688,61 @@ export interface Commit {
688
688
refNames : string [ ] ;
689
689
}
690
690
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
+
691
746
export class GitStatusParser {
692
747
693
748
private lastRaw = '' ;
@@ -761,59 +816,39 @@ export interface Submodule {
761
816
}
762
817
763
818
export function parseGitmodules ( raw : string ) : Submodule [ ] {
764
- const regex = / \r ? \n / g;
765
- let position = 0 ;
766
- let match : RegExpExecArray | null = null ;
767
-
768
819
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
- }
786
820
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
+ } ) ;
789
828
}
829
+ }
790
830
791
- const propertyMatch = / ^ \s * ( \w + ) \s * = \s * ( .* ) $ / . exec ( line ) ;
792
-
793
- if ( ! propertyMatch ) {
794
- return ;
795
- }
831
+ return result ;
832
+ }
796
833
797
- const [ , key , value ] = propertyMatch ;
834
+ export function parseGitRemotes ( raw : string ) : Remote [ ] {
835
+ const remotes : Remote [ ] = [ ] ;
798
836
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 ;
802
840
}
803
- }
804
-
805
- while ( match = regex . exec ( raw ) ) {
806
- parseLine ( raw . substring ( position , match . index ) ) ;
807
- position = match . index + match [ 0 ] . length ;
808
- }
809
841
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
+ } ) ;
814
849
}
815
850
816
- return result ;
851
+ return remotes ;
817
852
}
818
853
819
854
const commitRegex = / ( [ 0 - 9 a - f ] { 40 } ) \n ( .* ) \n ( .* ) \n ( .* ) \n ( .* ) \n ( .* ) \n ( .* ) (?: \n ( [ ^ ] * ?) ) ? (?: \x00 ) / gm;
@@ -2148,6 +2183,20 @@ export class Repository {
2148
2183
}
2149
2184
2150
2185
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
2151
2200
const result = await this . exec ( [ 'remote' , '--verbose' ] ) ;
2152
2201
const lines = result . stdout . trim ( ) . split ( '\n' ) . filter ( l => ! ! l ) ;
2153
2202
const remotes : MutableRemote [ ] = [ ] ;
@@ -2179,6 +2228,11 @@ export class Repository {
2179
2228
return remotes ;
2180
2229
}
2181
2230
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
+
2182
2236
async getBranch ( name : string ) : Promise < Branch > {
2183
2237
if ( name === 'HEAD' ) {
2184
2238
return this . getHEAD ( ) ;
0 commit comments