@@ -12,10 +12,13 @@ import { EventEmitter } from 'events';
1212import iconv = require( 'iconv-lite' ) ;
1313import * as filetype from 'file-type' ;
1414import { assign , groupBy , denodeify , IDisposable , toDisposable , dispose , mkdirp , readBytes , detectUnicodeEncoding , Encoding , onceEvent , splitInChunks , Limiter } from './util' ;
15- import { CancellationToken } from 'vscode' ;
15+ 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 ( ) ;
1922
2023// https://github.com/microsoft/vscode/issues/65693
2124const MAX_CLI_LENGTH = 30000 ;
@@ -163,9 +166,10 @@ export interface SpawnOptions extends cp.SpawnOptions {
163166 encoding ?: string ;
164167 log ?: boolean ;
165168 cancellationToken ?: CancellationToken ;
169+ progress ?: Progress < { message ?: string , increment : number } > ;
166170}
167171
168- async function exec ( child : cp . ChildProcess , cancellationToken ?: CancellationToken ) : Promise < IExecutionResult < Buffer > > {
172+ async function exec ( child : cp . ChildProcess , cancellationToken ?: CancellationToken , progress ?: Progress < { message ?: string , increment : number } > ) : Promise < IExecutionResult < Buffer > > {
169173 if ( ! child . stdout || ! child . stderr ) {
170174 throw new GitError ( { message : 'Failed to get stdout or stderr from git process.' } ) ;
171175 }
@@ -186,6 +190,9 @@ async function exec(child: cp.ChildProcess, cancellationToken?: CancellationToke
186190 disposables . push ( toDisposable ( ( ) => ee . removeListener ( name , fn ) ) ) ;
187191 } ;
188192
193+ const cloneProgressOutput = [ 'Receiving objects' , 'Resolving deltas' ] ;
194+ let prevInc = 0 ;
195+
189196 let result = Promise . all < any > ( [
190197 new Promise < number > ( ( c , e ) => {
191198 once ( child , 'error' , cpErrorHandler ( e ) ) ;
@@ -198,7 +205,27 @@ async function exec(child: cp.ChildProcess, cancellationToken?: CancellationToke
198205 } ) ,
199206 new Promise < string > ( c => {
200207 const buffers : Buffer [ ] = [ ] ;
201- on ( child . stderr , 'data' , ( b : Buffer ) => buffers . push ( b ) ) ;
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+ } ) ;
202229 once ( child . stderr , 'close' , ( ) => c ( Buffer . concat ( buffers ) . toString ( 'utf8' ) ) ) ;
203230 } )
204231 ] ) as Promise < [ number , Buffer , string ] > ;
@@ -341,7 +368,7 @@ export class Git {
341368 return ;
342369 }
343370
344- async clone ( url : string , parentPath : string , cancellationToken ?: CancellationToken ) : Promise < string > {
371+ async clone ( url : string , parentPath : string , progress : Progress < { message ?: string , increment : number } > , cancellationToken ?: CancellationToken ) : Promise < string > {
345372 let baseFolderName = decodeURI ( url ) . replace ( / [ \/ ] + $ / , '' ) . replace ( / ^ .* [ \/ \\ ] / , '' ) . replace ( / \. g i t $ / , '' ) || 'repository' ;
346373 let folderName = baseFolderName ;
347374 let folderPath = path . join ( parentPath , folderName ) ;
@@ -355,7 +382,7 @@ export class Git {
355382 await mkdirp ( parentPath ) ;
356383
357384 try {
358- await this . exec ( parentPath , [ 'clone' , url . includes ( ' ' ) ? encodeURI ( url ) : url , folderPath ] , { cancellationToken } ) ;
385+ await this . exec ( parentPath , [ 'clone' , url . includes ( ' ' ) ? encodeURI ( url ) : url , folderPath , '--progress' ] , { cancellationToken, progress } ) ;
359386 } catch ( err ) {
360387 if ( err . stderr ) {
361388 err . stderr = err . stderr . replace ( / ^ C l o n i n g .+ $ / m, '' ) . trim ( ) ;
@@ -405,7 +432,7 @@ export class Git {
405432 child . stdin . end ( options . input , 'utf8' ) ;
406433 }
407434
408- const bufferResult = await exec ( child , options . cancellationToken ) ;
435+ const bufferResult = await exec ( child , options . cancellationToken , options . progress ) ;
409436
410437 if ( options . log !== false && bufferResult . stderr . length > 0 ) {
411438 this . log ( `${ bufferResult . stderr } \n` ) ;
0 commit comments