3
3
* See the LICENSE file in the project root for license information.
4
4
*/
5
5
6
- import * as fs from 'fs' ;
6
+ // The current implementation is based on https://github.com/microsoft/vscode/blob/master/extensions/git/src/git.ts#L50-L141
7
+ /*---------------------------------------------------------------------------------------------
8
+ * Copyright (c) Microsoft Corporation. All rights reserved.
9
+ * Licensed under the MIT License. See License.txt in the project root for license information.
10
+ *--------------------------------------------------------------------------------------------*/
11
+
7
12
import * as path from 'path' ;
13
+ import * as which from 'which' ;
8
14
import * as cp from 'child_process' ;
9
15
10
16
/**
11
- * Bare minimum information of the locally available git executable.
17
+ * Bare minimum information of the locally available Git executable.
12
18
*/
13
19
export interface Git {
14
20
15
21
/**
16
22
* The FS path to the Git executable.
17
23
*/
18
- readonly path : string ,
24
+ readonly path : string ;
19
25
/**
20
26
* The Git version. [`git --version`]
21
27
*/
22
- readonly version : string ,
28
+ readonly version : string ;
23
29
/**
24
30
* The path to wherever your core Git programs are installed. [`git --exec-path`]
25
31
*/
26
- readonly execPath : string
32
+ readonly execPath : string ;
27
33
28
34
}
29
35
30
36
/**
31
37
* Resolves to the path of the locally available Git executable. Will be rejected if Git cannot be found on the system.
38
+ * `hint` can be provided as the initial lookup path, and `onLookup` function is used for logging during the Git discovery.
32
39
*/
33
- export default function ( ) : Promise < Git > {
34
- switch ( process . platform ) {
35
- case 'win32' : return findGitWin32 ( ) ;
36
- default : return findGitNix ( ) ;
37
- }
40
+ export default async function ( { hint, onLookup } : { hint : string | undefined , onLookup : ( path : string ) => void } = { hint : undefined , onLookup : ( ) => { } } ) : Promise < Git > {
41
+ const first = hint ? findSpecificGit ( hint , onLookup ) : Promise . reject < Git > ( null ) ;
42
+ return first
43
+ . then ( undefined , ( ) => {
44
+ switch ( process . platform ) {
45
+ case 'darwin' : return findGitDarwin ( onLookup ) ;
46
+ case 'win32' : return findGitWin32 ( onLookup ) ;
47
+ default : return which ( 'git' ) . then ( gitOnPath => findSpecificGit ( gitOnPath , onLookup ) ) ;
48
+ }
49
+ } )
50
+ . then ( null , ( ) => Promise . reject ( new Error ( 'Git installation not found.' ) ) ) ;
38
51
}
39
52
40
- async function findGit ( path : string ) : Promise < Git > {
41
- return new Promise < Git > ( async ( resolve , reject ) => {
42
- try {
43
- resolve ( {
44
- path,
45
- version : parseVersion ( await exec ( path , '--version' ) ) ,
46
- execPath : normalizePath ( await exec ( path , '--exec-path' ) )
47
- } ) ;
48
- } catch ( error ) {
49
- reject ( error ) ;
50
- }
53
+ function toUtf8String ( buffers : Buffer [ ] ) : string {
54
+ return Buffer . concat ( buffers ) . toString ( 'utf8' ) . trim ( ) ;
55
+ }
56
+
57
+ function parseVersion ( raw : string ) : string {
58
+ return raw . replace ( / ^ g i t v e r s i o n / , '' ) ;
59
+ }
60
+
61
+ function normalizePath ( pathToNormalize : string ) : string {
62
+ return path . normalize ( pathToNormalize ) ;
63
+ }
64
+
65
+ function findSpecificGit ( path : string , onLookup : ( path : string ) => void ) : Promise < Git > {
66
+ return new Promise < Git > ( ( resolve , reject ) => {
67
+ onLookup ( path ) ;
68
+
69
+ Promise . all ( [
70
+ exec ( path , '--version' ) ,
71
+ exec ( path , '--exec-path' )
72
+ ] ) . then ( ( [ version , execPath ] ) => {
73
+ resolve ( { path, version : parseVersion ( version ) , execPath : normalizePath ( execPath ) } ) ;
74
+ } ) . catch ( e => {
75
+ reject ( e ) ;
76
+ } )
51
77
} ) ;
52
78
}
53
79
@@ -61,69 +87,75 @@ async function exec(path: string, command: string | string[]): Promise<string> {
61
87
} ) ;
62
88
}
63
89
64
- async function findGitNix ( ) : Promise < Git > {
65
- return new Promise < Git > ( ( resolve , reject ) => {
66
- cp . exec ( 'which git' , ( error , buffer ) => {
67
- if ( error ) {
68
- return reject ( 'Git not found. ') ;
90
+ function findGitDarwin ( onLookup : ( path : string ) => void ) : Promise < Git > {
91
+ return new Promise < Git > ( ( c , e ) => {
92
+ cp . exec ( 'which git' , ( err , gitPathBuffer ) => {
93
+ if ( err ) {
94
+ return e ( 'git not found') ;
69
95
}
70
- const path = buffer . toString ( ) . replace ( / ^ \s + | \s + $ / g, '' ) ;
71
- if ( path !== 'usr/bin/git' || process . platform !== 'darwin' ) {
72
- return resolve ( findGit ( path ) ) ;
96
+
97
+ const path = gitPathBuffer . toString ( ) . replace ( / ^ \s + | \s + $ / g, '' ) ;
98
+
99
+ function getVersion ( path : string ) {
100
+ onLookup ( path ) ;
101
+
102
+ // make sure git executes
103
+ cp . exec ( 'git --version' , ( err , stdout ) => {
104
+
105
+ if ( err ) {
106
+ return e ( 'git not found' ) ;
107
+ }
108
+
109
+ const version = parseVersion ( stdout . trim ( ) ) ;
110
+ cp . exec ( 'git --exec-path' , ( err , stdout ) => {
111
+
112
+ if ( err ) {
113
+ return e ( 'git not found' ) ;
114
+ }
115
+
116
+ const execPath = normalizePath ( stdout . trim ( ) ) ;
117
+ return c ( { path, version, execPath } ) ;
118
+ } ) ;
119
+
120
+ } ) ;
73
121
}
74
- cp . exec ( 'xcode-select -p' , ( error : any ) => {
75
- if ( error && error . code === 2 ) {
76
- return reject ( new Error ( 'Git not found.' ) ) ;
122
+
123
+ if ( path !== '/usr/bin/git' ) {
124
+ return getVersion ( path ) ;
125
+ }
126
+
127
+ // must check if XCode is installed
128
+ cp . exec ( 'xcode-select -p' , ( err : any ) => {
129
+ if ( err && err . code === 2 ) {
130
+ // git is not installed, and launching /usr/bin/git
131
+ // will prompt the user to install it
132
+
133
+ return e ( 'git not found' ) ;
77
134
}
78
- resolve ( findGit ( path ) ) ;
135
+
136
+ getVersion ( path ) ;
79
137
} ) ;
80
138
} ) ;
81
139
} ) ;
82
140
}
83
141
84
- async function findSystemGitWin32 ( base : string ) : Promise < Git > {
142
+ function findSystemGitWin32 ( base : string , onLookup : ( path : string ) => void ) : Promise < Git > {
85
143
if ( ! base ) {
86
- throw new Error ( ` Git not found.` ) ;
144
+ return Promise . reject < Git > ( 'Not found' ) ;
87
145
}
88
- return findGit ( path . join ( base , 'Git' , 'cmd' , 'git.exe' ) ) ;
89
- }
90
146
91
- async function findGitHubGitWin32 ( ) : Promise < Git > {
92
- const github = path . join ( env ( 'LOCALAPPDATA' ) , 'GitHub' ) ;
93
- return new Promise < Git > ( ( resolve , reject ) => {
94
- fs . readdir ( github , ( error : NodeJS . ErrnoException , files : string [ ] ) => {
95
- if ( error ) {
96
- return reject ( error ) ;
97
- }
98
- const git = files . filter ( file => / ^ P o r t a b l e G i t / . test ( file ) ) . shift ( ) ;
99
- if ( ! git ) {
100
- return reject ( new Error ( `Git not found.` ) ) ;
101
- }
102
- resolve ( findGit ( path . join ( github , git , 'cmd' , 'git.exe' ) ) ) ;
103
- } )
104
- } ) ;
105
- }
106
-
107
- async function findGitWin32 ( ) : Promise < Git > {
108
- return findSystemGitWin32 ( env ( 'ProgramW6432' ) )
109
- . then ( undefined , ( ) => findSystemGitWin32 ( env ( 'ProgramFiles(x86)' ) )
110
- . then ( undefined , ( ) => findSystemGitWin32 ( env ( 'ProgramFiles' ) )
111
- . then ( undefined , ( ) => findGit ( 'git' ) )
112
- . then ( undefined , ( ) => findGitHubGitWin32 ( ) ) ) ) ;
113
- }
114
-
115
- function toUtf8String ( buffers : Buffer [ ] ) : string {
116
- return Buffer . concat ( buffers ) . toString ( 'utf8' ) . trim ( ) ;
147
+ return findSpecificGit ( path . join ( base , 'Git' , 'cmd' , 'git.exe' ) , onLookup ) ;
117
148
}
118
149
119
- function parseVersion ( raw : string ) : string {
120
- return raw . replace ( / ^ g i t v e r s i o n / , '' ) ;
150
+ async function findGitWin32InPath ( onLookup : ( path : string ) => void ) : Promise < Git > {
151
+ const whichPromise = new Promise < string > ( ( c , e ) => which ( 'git.exe' , ( err , path ) => err ? e ( err ) : c ( path ) ) ) ;
152
+ return whichPromise . then ( path => findSpecificGit ( path , onLookup ) ) ;
121
153
}
122
154
123
- function normalizePath ( pathToNormalize : string ) : string {
124
- return path . normalize ( pathToNormalize ) ;
155
+ async function findGitWin32 ( onLookup : ( path : string ) => void ) : Promise < Git > {
156
+ return findSystemGitWin32 ( process . env [ 'ProgramW6432' ] as string , onLookup )
157
+ . then ( undefined , ( ) => findSystemGitWin32 ( process . env [ 'ProgramFiles(x86)' ] as string , onLookup ) )
158
+ . then ( undefined , ( ) => findSystemGitWin32 ( process . env [ 'ProgramFiles' ] as string , onLookup ) )
159
+ . then ( undefined , ( ) => findSystemGitWin32 ( path . join ( process . env [ 'LocalAppData' ] as string , 'Programs' ) , onLookup ) )
160
+ . then ( undefined , ( ) => findGitWin32InPath ( onLookup ) ) ;
125
161
}
126
-
127
- function env ( key : string ) : string {
128
- return process . env [ key ] || '' ;
129
- }
0 commit comments