@@ -16,9 +16,8 @@ import { CancellationToken, Progress } from 'vscode';
1616import { URI } from 'vscode-uri' ;
1717import { detectEncoding } from './encoding' ;
1818import { Ref , RefType , Branch , Remote , GitErrorCodes , LogOptions , Change , Status } from './api/git' ;
19- import * as nls from 'vscode-nls' ;
20-
21- const localize = nls . loadMessageBundle ( ) ;
19+ import * as byline from 'byline' ;
20+ import { StringDecoder } from 'string_decoder' ;
2221
2322// https://github.com/microsoft/vscode/issues/65693
2423const MAX_CLI_LENGTH = 30000 ;
@@ -166,10 +165,10 @@ export interface SpawnOptions extends cp.SpawnOptions {
166165 encoding ?: string ;
167166 log ?: boolean ;
168167 cancellationToken ?: CancellationToken ;
169- progress ?: Progress < { message ?: string , increment : number } > ;
168+ onSpawn ?: ( childProcess : cp . ChildProcess ) => void ;
170169}
171170
172- async function exec ( child : cp . ChildProcess , cancellationToken ?: CancellationToken , progress ?: Progress < { message ?: string , increment : number } > ) : Promise < IExecutionResult < Buffer > > {
171+ async function exec ( child : cp . ChildProcess , cancellationToken ?: CancellationToken ) : Promise < IExecutionResult < Buffer > > {
173172 if ( ! child . stdout || ! child . stderr ) {
174173 throw new GitError ( { message : 'Failed to get stdout or stderr from git process.' } ) ;
175174 }
@@ -190,9 +189,6 @@ async function exec(child: cp.ChildProcess, cancellationToken?: CancellationToke
190189 disposables . push ( toDisposable ( ( ) => ee . removeListener ( name , fn ) ) ) ;
191190 } ;
192191
193- const cloneProgressOutput = [ 'Receiving objects' , 'Resolving deltas' ] ;
194- let prevInc = 0 ;
195-
196192 let result = Promise . all < any > ( [
197193 new Promise < number > ( ( c , e ) => {
198194 once ( child , 'error' , cpErrorHandler ( e ) ) ;
@@ -205,27 +201,7 @@ async function exec(child: cp.ChildProcess, cancellationToken?: CancellationToke
205201 } ) ,
206202 new Promise < string > ( c => {
207203 const buffers : Buffer [ ] = [ ] ;
208- on ( child . stderr , 'data' , ( b : Buffer ) => {
209- buffers . push ( b ) ;
210- const s = b . toString ( ) ;
211-
212- // Check for git clone progress reporting
213- cloneProgressOutput . forEach ( cloneOutput => {
214- if ( s . startsWith ( cloneOutput ) ) {
215- const idx = s . indexOf ( '%' ) ;
216- const inc = parseInt ( s . slice ( idx - 3 , idx ) ) ;
217-
218- if ( progress ) {
219- progress . report ( {
220- message : localize ( cloneOutput . toLowerCase ( ) , cloneOutput ) + ': ' + inc + '%' ,
221- increment : inc - prevInc
222- } ) ;
223-
224- prevInc = inc ;
225- }
226- }
227- } ) ;
228- } ) ;
204+ on ( child . stderr , 'data' , ( b : Buffer ) => buffers . push ( b ) ) ;
229205 once ( child . stderr , 'close' , ( ) => c ( Buffer . concat ( buffers ) . toString ( 'utf8' ) ) ) ;
230206 } )
231207 ] ) as Promise < [ number , Buffer , string ] > ;
@@ -368,7 +344,7 @@ export class Git {
368344 return ;
369345 }
370346
371- async clone ( url : string , parentPath : string , progress : Progress < { message ?: string , increment : number } > , cancellationToken ?: CancellationToken ) : Promise < string > {
347+ async clone ( url : string , parentPath : string , progress : Progress < { increment : number } > , cancellationToken ?: CancellationToken ) : Promise < string > {
372348 let baseFolderName = decodeURI ( url ) . replace ( / [ \/ ] + $ / , '' ) . replace ( / ^ .* [ \/ \\ ] / , '' ) . replace ( / \. g i t $ / , '' ) || 'repository' ;
373349 let folderName = baseFolderName ;
374350 let folderPath = path . join ( parentPath , folderName ) ;
@@ -381,8 +357,36 @@ export class Git {
381357
382358 await mkdirp ( parentPath ) ;
383359
360+ const onSpawn = ( child : cp . ChildProcess ) => {
361+ const decoder = new StringDecoder ( 'utf8' ) ;
362+ const lineStream = new byline . LineStream ( { encoding : 'utf8' } ) ;
363+ child . stderr . on ( 'data' , ( buffer : Buffer ) => lineStream . write ( decoder . write ( buffer ) ) ) ;
364+
365+ let totalProgress = 0 ;
366+ let previousProgress = 0 ;
367+
368+ lineStream . on ( 'data' , ( line : string ) => {
369+ let match : RegExpMatchArray | null = null ;
370+
371+ if ( match = / C o u n t i n g o b j e c t s : \s * ( \d + ) % / i. exec ( line ) ) {
372+ totalProgress = Math . floor ( parseInt ( match [ 1 ] ) * 0.1 ) ;
373+ } else if ( match = / C o m p r e s s i n g o b j e c t s : \s * ( \d + ) % / i. exec ( line ) ) {
374+ totalProgress = 10 + Math . floor ( parseInt ( match [ 1 ] ) * 0.1 ) ;
375+ } else if ( match = / R e c e i v i n g o b j e c t s : \s * ( \d + ) % / i. exec ( line ) ) {
376+ totalProgress = 20 + Math . floor ( parseInt ( match [ 1 ] ) * 0.4 ) ;
377+ } else if ( match = / R e s o l v i n g d e l t a s : \s * ( \d + ) % / i. exec ( line ) ) {
378+ totalProgress = 60 + Math . floor ( parseInt ( match [ 1 ] ) * 0.4 ) ;
379+ }
380+
381+ if ( totalProgress !== previousProgress ) {
382+ progress . report ( { increment : totalProgress - previousProgress } ) ;
383+ previousProgress = totalProgress ;
384+ }
385+ } ) ;
386+ } ;
387+
384388 try {
385- await this . exec ( parentPath , [ 'clone' , url . includes ( ' ' ) ? encodeURI ( url ) : url , folderPath , '--progress' ] , { cancellationToken, progress } ) ;
389+ await this . exec ( parentPath , [ 'clone' , url . includes ( ' ' ) ? encodeURI ( url ) : url , folderPath , '--progress' ] , { cancellationToken, onSpawn } ) ;
386390 } catch ( err ) {
387391 if ( err . stderr ) {
388392 err . stderr = err . stderr . replace ( / ^ C l o n i n g .+ $ / m, '' ) . trim ( ) ;
@@ -428,11 +432,15 @@ export class Git {
428432 private async _exec ( args : string [ ] , options : SpawnOptions = { } ) : Promise < IExecutionResult < string > > {
429433 const child = this . spawn ( args , options ) ;
430434
435+ if ( options . onSpawn ) {
436+ options . onSpawn ( child ) ;
437+ }
438+
431439 if ( options . input ) {
432440 child . stdin . end ( options . input , 'utf8' ) ;
433441 }
434442
435- const bufferResult = await exec ( child , options . cancellationToken , options . progress ) ;
443+ const bufferResult = await exec ( child , options . cancellationToken ) ;
436444
437445 if ( options . log !== false && bufferResult . stderr . length > 0 ) {
438446 this . log ( `${ bufferResult . stderr } \n` ) ;
0 commit comments