11import * as cp from 'child_process' ;
22import { Config } from './config' ;
3- import { GitCommandStatus , GitCommit , GitCommitDetails , GitCommitNode , GitFileChangeType , GitRef , GitResetMode , GitUnsavedChanges } from './types' ;
3+ import { GitCommandStatus , GitCommit , GitCommitDetails , GitCommitNode , GitFileChangeType , GitRef , GitResetMode , GitUnsavedChangesCmdResp } from './types' ;
44
55const eolRegex = / \r \n | \r | \n / g;
66const gitLogSeparator = 'XX7Nal-YARtTpjCikii9nJxER19D6diSyk-AWkPb' ;
@@ -14,38 +14,41 @@ export class DataSource {
1414 this . workspaceDir = workspaceDir ;
1515 }
1616
17- public isGitRepository ( ) : boolean {
18- try {
19- cp . execSync ( 'git rev-parse --git-dir' , { cwd : this . workspaceDir } ) ;
20- return true ;
21- } catch ( e ) {
22- return false ;
23- }
17+ public isGitRepository ( ) {
18+ return new Promise < boolean > ( ( resolve ) => {
19+ cp . exec ( 'git rev-parse --git-dir' , { cwd : this . workspaceDir } , ( err ) => {
20+ resolve ( ! err ) ;
21+ } ) ;
22+ } ) ;
2423 }
2524
26- public getBranches ( showRemoteBranches : boolean ) : string [ ] {
27- try {
28- let lines = cp . execSync ( 'git branch' + ( showRemoteBranches ? ' -a' : '' ) , { cwd : this . workspaceDir } ) . toString ( ) . split ( eolRegex ) ;
29- let branches : string [ ] = [ ] ;
30- for ( let i = 0 ; i < lines . length - 1 ; i ++ ) {
31- let active = lines [ i ] [ 0 ] === '*' ;
32- let name = lines [ i ] . substring ( 2 ) . split ( ' ' ) [ 0 ] ;
33- if ( active ) {
34- branches . unshift ( name ) ;
25+ public getBranches ( showRemoteBranches : boolean ) {
26+ return new Promise < string [ ] > ( ( resolve ) => {
27+ cp . exec ( 'git branch' + ( showRemoteBranches ? ' -a' : '' ) , { cwd : this . workspaceDir } , ( err , stdout ) => {
28+ if ( ! err ) {
29+ let lines = stdout . split ( eolRegex ) ;
30+ let branches : string [ ] = [ ] ;
31+ for ( let i = 0 ; i < lines . length - 1 ; i ++ ) {
32+ let active = lines [ i ] [ 0 ] === '*' ;
33+ let name = lines [ i ] . substring ( 2 ) . split ( ' ' ) [ 0 ] ;
34+ if ( active ) {
35+ branches . unshift ( name ) ;
36+ } else {
37+ branches . push ( name ) ;
38+ }
39+ }
40+ resolve ( branches ) ;
3541 } else {
36- branches . push ( name ) ;
42+ resolve ( [ ] ) ;
3743 }
38- }
39- return branches ;
40- } catch ( e ) {
41- return [ ] ;
42- }
44+ } ) ;
45+ } ) ;
4346 }
4447
45- public getCommits ( branch : string , maxCommits : number , showRemoteBranches : boolean , currentBranch : string | null ) {
48+ public async getCommits ( branch : string , maxCommits : number , showRemoteBranches : boolean , currentBranch : string | null ) {
4649 let i , j ;
47- let commits = this . getGitLog ( branch , maxCommits + 1 , showRemoteBranches ) ;
48- let refs = this . getRefs ( showRemoteBranches ) ;
50+ let commits = await this . getGitLog ( branch , maxCommits + 1 , showRemoteBranches ) ;
51+ let refs = await this . getRefs ( showRemoteBranches ) ;
4952 let unsavedChanges = null ;
5053
5154 let moreCommitsAvailable = commits . length === maxCommits + 1 ;
@@ -59,7 +62,7 @@ export class DataSource {
5962 }
6063 }
6164 if ( currentBranchHash !== null && ( branch === '' || branch === currentBranch ) ) {
62- unsavedChanges = ( new Config ( ) ) . showUncommittedChanges ( ) ? this . getGitUnsavedChanges ( ) : null ;
65+ unsavedChanges = ( new Config ( ) ) . showUncommittedChanges ( ) ? await this . getGitUnsavedChanges ( ) : null ;
6366 if ( unsavedChanges !== null ) {
6467 for ( j = 0 ; j < commits . length ; j ++ ) {
6568 if ( currentBranchHash === commits [ j ] . hash ) {
@@ -99,136 +102,181 @@ export class DataSource {
99102 return { commits : commitNodes , moreCommitsAvailable : moreCommitsAvailable } ;
100103 }
101104
102- public commitDetails ( commitHash : string ) {
105+ public async commitDetails ( commitHash : string ) {
103106 try {
104- let lines = cp . execSync ( 'git show --quiet ' + commitHash + ' --format="' + gitCommitDetailsFormat + '"' , { cwd : this . workspaceDir } ) . toString ( ) . split ( eolRegex ) ;
105- let commitInfo = lines [ 0 ] . split ( gitLogSeparator ) ;
106- let details : GitCommitDetails = {
107- hash : commitInfo [ 0 ] ,
108- parents : commitInfo [ 1 ] . split ( ' ' ) ,
109- author : commitInfo [ 2 ] ,
110- email : commitInfo [ 3 ] ,
111- date : parseInt ( commitInfo [ 4 ] ) ,
112- committer : commitInfo [ 5 ] ,
113- body : commitInfo [ 6 ] ,
114- fileChanges : [ ]
115- } ;
116-
107+ let details = await new Promise < GitCommitDetails > ( ( resolve , reject ) => {
108+ cp . exec ( 'git show --quiet ' + commitHash + ' --format="' + gitCommitDetailsFormat + '"' , { cwd : this . workspaceDir } , ( err , stdout ) => {
109+ if ( ! err ) {
110+ let lines = stdout . split ( eolRegex ) ;
111+ let commitInfo = lines [ 0 ] . split ( gitLogSeparator ) ;
112+ resolve ( {
113+ hash : commitInfo [ 0 ] ,
114+ parents : commitInfo [ 1 ] . split ( ' ' ) ,
115+ author : commitInfo [ 2 ] ,
116+ email : commitInfo [ 3 ] ,
117+ date : parseInt ( commitInfo [ 4 ] ) ,
118+ committer : commitInfo [ 5 ] ,
119+ body : commitInfo [ 6 ] ,
120+ fileChanges : [ ]
121+ } ) ;
122+ } else {
123+ reject ( ) ;
124+ }
125+ } ) ;
126+ } ) ;
117127 let fileLookup : { [ file : string ] : number } = { } ;
118- lines = cp . execSync ( 'git diff-tree --name-status -r -m --root --find-renames --diff-filter=AMDR ' + commitHash , { cwd : this . workspaceDir } ) . toString ( ) . split ( eolRegex ) ;
119- for ( let i = 1 ; i < lines . length - 1 ; i ++ ) {
120- let line = lines [ i ] . split ( '\t' ) ;
121- if ( line . length < 2 ) break ;
122- let oldFilePath = line [ 1 ] . replace ( / \\ / g, '/' ) , newFilePath = line [ line . length - 1 ] . replace ( / \\ / g, '/' ) ;
123- fileLookup [ newFilePath ] = details . fileChanges . length ;
124- details . fileChanges . push ( { oldFilePath : oldFilePath , newFilePath : newFilePath , type : < GitFileChangeType > line [ 0 ] [ 0 ] , additions : null , deletions : null } ) ;
125- }
126- lines = cp . execSync ( 'git diff-tree --numstat -r -m --root --find-renames --diff-filter=AMDR ' + commitHash , { cwd : this . workspaceDir } ) . toString ( ) . split ( eolRegex ) ;
127- for ( let i = 1 ; i < lines . length - 1 ; i ++ ) {
128- let line = lines [ i ] . split ( '\t' ) ;
129- if ( line . length !== 3 ) break ;
130- let fileName = line [ 2 ] . replace ( / ( .* ) { .* = > ( .* ) } / , '$1$2' ) . replace ( / .* = > ( .* ) / , '$1' ) ;
131- if ( typeof fileLookup [ fileName ] === 'number' ) {
132- details . fileChanges [ fileLookup [ fileName ] ] . additions = parseInt ( line [ 0 ] ) ;
133- details . fileChanges [ fileLookup [ fileName ] ] . deletions = parseInt ( line [ 1 ] ) ;
134- }
135- }
128+ await new Promise ( ( resolve , reject ) => {
129+ cp . exec ( 'git diff-tree --name-status -r -m --root --find-renames --diff-filter=AMDR ' + commitHash , { cwd : this . workspaceDir } , ( err , stdout ) => {
130+ if ( ! err ) {
131+ let lines = stdout . split ( eolRegex ) ;
132+ for ( let i = 1 ; i < lines . length - 1 ; i ++ ) {
133+ let line = lines [ i ] . split ( '\t' ) ;
134+ if ( line . length < 2 ) break ;
135+ let oldFilePath = line [ 1 ] . replace ( / \\ / g, '/' ) , newFilePath = line [ line . length - 1 ] . replace ( / \\ / g, '/' ) ;
136+ fileLookup [ newFilePath ] = details . fileChanges . length ;
137+ details . fileChanges . push ( { oldFilePath : oldFilePath , newFilePath : newFilePath , type : < GitFileChangeType > line [ 0 ] [ 0 ] , additions : null , deletions : null } ) ;
138+ }
139+ resolve ( ) ;
140+ } else {
141+ reject ( ) ;
142+ }
143+ } ) ;
144+ } ) ;
145+ await new Promise ( ( resolve , reject ) => {
146+ cp . exec ( 'git diff-tree --numstat -r -m --root --find-renames --diff-filter=AMDR ' + commitHash , { cwd : this . workspaceDir } , ( err , stdout ) => {
147+ if ( ! err ) {
148+ let lines = stdout . split ( eolRegex ) ;
149+ for ( let i = 1 ; i < lines . length - 1 ; i ++ ) {
150+ let line = lines [ i ] . split ( '\t' ) ;
151+ if ( line . length !== 3 ) break ;
152+ let fileName = line [ 2 ] . replace ( / ( .* ) { .* = > ( .* ) } / , '$1$2' ) . replace ( / .* = > ( .* ) / , '$1' ) ;
153+ if ( typeof fileLookup [ fileName ] === 'number' ) {
154+ details . fileChanges [ fileLookup [ fileName ] ] . additions = parseInt ( line [ 0 ] ) ;
155+ details . fileChanges [ fileLookup [ fileName ] ] . deletions = parseInt ( line [ 1 ] ) ;
156+ }
157+ }
158+ resolve ( ) ;
159+ } else {
160+ reject ( ) ;
161+ }
162+
163+ } ) ;
164+ } ) ;
136165 return details ;
137166 } catch ( e ) {
138167 return null ;
139168 }
140169 }
141170
142- public getFile ( commitHash : string , filePath : string ) {
143- try {
144- return cp . execSync ( 'git show "' + commitHash + '":"' + filePath + '"' , { cwd : this . workspaceDir } ) . toString ( ) ;
145- } catch ( e ) {
146- return '' ;
147- }
171+ public async getCommitFile ( commitHash : string , filePath : string ) {
172+ return new Promise < string > ( ( resolve ) => {
173+ cp . exec ( 'git show "' + commitHash + '":"' + filePath + '"' , { cwd : this . workspaceDir } , ( err , stdout ) => {
174+ resolve ( ! err ? stdout : '' ) ;
175+ } ) ;
176+ } ) ;
148177 }
149178
150- public addTag ( tagName : string , commitHash : string ) : GitCommandStatus {
179+ public addTag ( tagName : string , commitHash : string ) {
151180 return this . runGitCommand ( 'git tag -a ' + escapeRefName ( tagName ) + ' -m "" ' + commitHash ) ;
152181 }
153182
154- public deleteTag ( tagName : string ) : GitCommandStatus {
183+ public deleteTag ( tagName : string ) {
155184 return this . runGitCommand ( 'git tag -d ' + escapeRefName ( tagName ) ) ;
156185 }
157186
158- public createBranch ( branchName : string , commitHash : string ) : GitCommandStatus {
187+ public createBranch ( branchName : string , commitHash : string ) {
159188 return this . runGitCommand ( 'git branch ' + escapeRefName ( branchName ) + ' ' + commitHash ) ;
160189 }
161190
162- public checkoutBranch ( branchName : string , remoteBranch : string | null ) : GitCommandStatus {
191+ public checkoutBranch ( branchName : string , remoteBranch : string | null ) {
163192 return this . runGitCommand ( 'git checkout ' + ( remoteBranch === null ? escapeRefName ( branchName ) : ' -b ' + escapeRefName ( branchName ) + ' ' + escapeRefName ( remoteBranch ) ) ) ;
164193 }
165194
166- public deleteBranch ( branchName : string , forceDelete : boolean ) : GitCommandStatus {
195+ public deleteBranch ( branchName : string , forceDelete : boolean ) {
167196 return this . runGitCommand ( 'git branch --delete' + ( forceDelete ? ' --force' : '' ) + ' ' + escapeRefName ( branchName ) ) ;
168197 }
169198
170- public renameBranch ( oldName : string , newName : string ) : GitCommandStatus {
199+ public renameBranch ( oldName : string , newName : string ) {
171200 return this . runGitCommand ( 'git branch -m ' + escapeRefName ( oldName ) + ' ' + escapeRefName ( newName ) ) ;
172201 }
173202
174- public resetToCommit ( commitHash : string , resetMode : GitResetMode ) : GitCommandStatus {
203+ public resetToCommit ( commitHash : string , resetMode : GitResetMode ) {
175204 return this . runGitCommand ( 'git reset --' + resetMode + ' ' + commitHash ) ;
176205 }
177206
178- private runGitCommand ( command : string ) : GitCommandStatus {
179- try {
180- cp . execSync ( command , { cwd : this . workspaceDir } ) ;
181- return null ;
182- } catch ( e ) {
183- let lines = e . message . split ( eolRegex ) ;
184- return lines . slice ( 1 , lines . length - 1 ) . join ( '\n' ) ;
185- }
207+ private async runGitCommand ( command : string ) {
208+ return new Promise < GitCommandStatus > ( ( resolve ) => {
209+ cp . exec ( command , { cwd : this . workspaceDir } , ( err ) => {
210+ if ( ! err ) {
211+ resolve ( null ) ;
212+ } else {
213+ let lines = err . message . split ( eolRegex ) ;
214+ resolve ( lines . slice ( 1 , lines . length - 1 ) . join ( '\n' ) ) ;
215+ }
216+ } ) ;
217+ } ) ;
186218 }
187219
188- private getRefs ( showRemoteBranches : boolean ) : GitRef [ ] {
189- try {
190- let lines = cp . execSync ( 'git show-ref ' + ( showRemoteBranches ? '' : '--heads --tags' ) + ' -d' , { cwd : this . workspaceDir } ) . toString ( ) . split ( eolRegex ) ;
191- let refs : GitRef [ ] = [ ] ;
192- for ( let i = 0 ; i < lines . length - 1 ; i ++ ) {
193- let line = lines [ i ] . split ( ' ' ) ;
194- if ( line . length < 2 ) continue ;
195-
196- let hash = line . shift ( ) ! ;
197- let ref = line . join ( ' ' ) ;
198-
199- if ( ref . startsWith ( 'refs/heads/' ) ) {
200- refs . push ( { hash : hash , name : ref . substring ( 11 ) , type : 'head' } ) ;
201- } else if ( ref . startsWith ( 'refs/tags/' ) ) {
202- refs . push ( { hash : hash , name : ( ref . endsWith ( '^{}' ) ? ref . substring ( 10 , ref . length - 3 ) : ref . substring ( 10 ) ) , type : 'tag' } ) ;
203- } else if ( ref . startsWith ( 'refs/remotes/' ) ) {
204- refs . push ( { hash : hash , name : ref . substring ( 13 ) , type : 'remote' } ) ;
220+ private async getRefs ( showRemoteBranches : boolean ) {
221+ return new Promise < GitRef [ ] > ( ( resolve ) => {
222+ cp . exec ( 'git show-ref ' + ( showRemoteBranches ? '' : '--heads --tags' ) + ' -d' , { cwd : this . workspaceDir } , ( err , stdout ) => {
223+ if ( ! err ) {
224+ let lines = stdout . split ( eolRegex ) ;
225+ let refs : GitRef [ ] = [ ] ;
226+ for ( let i = 0 ; i < lines . length - 1 ; i ++ ) {
227+ let line = lines [ i ] . split ( ' ' ) ;
228+ if ( line . length < 2 ) continue ;
229+
230+ let hash = line . shift ( ) ! ;
231+ let ref = line . join ( ' ' ) ;
232+
233+ if ( ref . startsWith ( 'refs/heads/' ) ) {
234+ refs . push ( { hash : hash , name : ref . substring ( 11 ) , type : 'head' } ) ;
235+ } else if ( ref . startsWith ( 'refs/tags/' ) ) {
236+ refs . push ( { hash : hash , name : ( ref . endsWith ( '^{}' ) ? ref . substring ( 10 , ref . length - 3 ) : ref . substring ( 10 ) ) , type : 'tag' } ) ;
237+ } else if ( ref . startsWith ( 'refs/remotes/' ) ) {
238+ refs . push ( { hash : hash , name : ref . substring ( 13 ) , type : 'remote' } ) ;
239+ }
240+ }
241+ resolve ( refs ) ;
242+ } else {
243+ resolve ( [ ] ) ;
205244 }
206- }
207- return refs ;
208- } catch ( e ) {
209- return [ ] ;
210- }
245+ } ) ;
246+ } ) ;
211247 }
212248
213- private getGitLog ( branch : string , num : number , showRemoteBranches : boolean ) : GitCommit [ ] {
214- try {
215- let lines = cp . execSync ( 'git log ' + ( branch !== '' ? escapeRefName ( branch ) : '--branches' + ( showRemoteBranches ? ' --remotes' : '' ) ) + ' --max-count=' + num + ' --format="' + gitLogFormat + '"' , { cwd : this . workspaceDir } ) . toString ( ) . split ( eolRegex ) ;
216- let gitCommits : GitCommit [ ] = [ ] ;
217- for ( let i = 0 ; i < lines . length - 1 ; i ++ ) {
218- let line = lines [ i ] . split ( gitLogSeparator ) ;
219- if ( line . length !== 6 ) break ;
220- gitCommits . push ( { hash : line [ 0 ] , parentHashes : line [ 1 ] . split ( ' ' ) , author : line [ 2 ] , email : line [ 3 ] , date : parseInt ( line [ 4 ] ) , message : line [ 5 ] } ) ;
221- }
222- return gitCommits ;
223- } catch ( e ) {
224- return [ ] ;
225- }
249+ private async getGitLog ( branch : string , num : number , showRemoteBranches : boolean ) {
250+ return new Promise < GitCommit [ ] > ( ( resolve ) => {
251+ cp . exec ( 'git log ' + ( branch !== '' ? escapeRefName ( branch ) : '--branches' + ( showRemoteBranches ? ' --remotes' : '' ) ) + ' --max-count=' + num + ' --format="' + gitLogFormat + '"' , { cwd : this . workspaceDir } , ( err , stdout ) => {
252+ if ( ! err ) {
253+ let lines = stdout . split ( eolRegex ) ;
254+ let gitCommits : GitCommit [ ] = [ ] ;
255+ for ( let i = 0 ; i < lines . length - 1 ; i ++ ) {
256+ let line = lines [ i ] . split ( gitLogSeparator ) ;
257+ if ( line . length !== 6 ) break ;
258+ gitCommits . push ( { hash : line [ 0 ] , parentHashes : line [ 1 ] . split ( ' ' ) , author : line [ 2 ] , email : line [ 3 ] , date : parseInt ( line [ 4 ] ) , message : line [ 5 ] } ) ;
259+ }
260+ resolve ( gitCommits ) ;
261+ } else {
262+ resolve ( [ ] ) ;
263+ }
264+ } ) ;
265+ } ) ;
226266 }
227267
228- private getGitUnsavedChanges ( ) : GitUnsavedChanges | null {
268+ private getGitUnsavedChanges ( ) {
229269 try {
230- let lines = cp . execSync ( 'git status -s --branch --untracked-files --porcelain' , { cwd : this . workspaceDir } ) . toString ( ) . split ( eolRegex ) ;
231- return lines . length > 2 ? { branch : lines [ 0 ] . substring ( 3 ) . split ( '...' ) [ 0 ] , changes : lines . length - 2 } : null ;
270+ return new Promise < GitUnsavedChangesCmdResp > ( ( resolve , reject ) => {
271+ cp . exec ( 'git status -s --branch --untracked-files --porcelain' , { cwd : this . workspaceDir } , ( err , stdout ) => {
272+ if ( ! err ) {
273+ let lines = stdout . split ( eolRegex ) ;
274+ resolve ( lines . length > 2 ? { branch : lines [ 0 ] . substring ( 3 ) . split ( '...' ) [ 0 ] , changes : lines . length - 2 } : null ) ;
275+ } else {
276+ reject ( ) ;
277+ }
278+ } ) ;
279+ } ) ;
232280 } catch ( e ) {
233281 return null ;
234282 }
0 commit comments