@@ -5,6 +5,7 @@ import { spawnPromise } from 'spawn-rx';
55import * as fs from 'fs' ;
66import * as path from 'path' ;
77import * as tmp from 'tmp' ;
8+ import * as iconv from 'iconv-lite' ;
89
910export { IGit } ;
1011export * from './models/models' ;
@@ -21,25 +22,43 @@ let git: IGit;
2122const defaultLogParams = [ `log` , `--name-status` , `--full-history` , `-M` , `--date=iso8601` , `--format=%H -%nauthor %an%nauthor-date %ai%nparents %P%nsummary %B%nfilename ?` ] ;
2223const defaultStashParams = [ `stash` , `list` , `--name-status` , `--full-history` , `-M` , `--format=%H -%nauthor-date %ai%nreflog-selector %gd%nsummary %B%nfilename ?` ] ;
2324
24- async function gitCommand ( cwd : string , ...args : any [ ] ) {
25+ let defaultEncoding = 'utf8' ;
26+ export function setDefaultEncoding ( encoding : string ) {
27+ defaultEncoding = iconv . encodingExists ( encoding ) ? encoding : 'utf8' ;
28+ }
29+
30+ const GitWarnings = [
31+ / N o t a g i t r e p o s i t o r y / ,
32+ / i s o u t s i d e r e p o s i t o r y / ,
33+ / n o s u c h p a t h / ,
34+ / d o e s n o t h a v e a n y c o m m i t s /
35+ ] ;
36+
37+ async function gitCommand ( options : { cwd : string , encoding ?: string } , ...args : any [ ] ) {
2538 try {
2639 // Fixes https://github.com/eamodio/vscode-gitlens/issues/73
2740 // See https://stackoverflow.com/questions/4144417/how-to-handle-asian-characters-in-file-names-in-git-on-os-x
2841 args . splice ( 0 , 0 , '-c' , 'core.quotepath=false' ) ;
2942
30- const s = await spawnPromise ( git . path , args , { cwd : cwd } ) ;
31- Logger . log ( 'git' , ...args , ` cwd='${ cwd } '` ) ;
32- return s ;
43+ const opts = { encoding : 'utf8' , ...options } ;
44+ const s = await spawnPromise ( git . path , args , { cwd : options . cwd , encoding : ( opts . encoding === 'utf8' ) ? 'utf8' : 'binary' } ) ;
45+ Logger . log ( 'git' , ...args , ` cwd='${ options . cwd } '` ) ;
46+ if ( opts . encoding === 'utf8' || opts . encoding === 'binary' ) return s ;
47+
48+ return iconv . decode ( Buffer . from ( s , 'binary' ) , opts . encoding ) ;
3349 }
3450 catch ( ex ) {
3551 const msg = ex && ex . toString ( ) ;
36- if ( msg && ( msg . includes ( 'Not a git repository' ) || msg . includes ( 'is outside repository' ) || msg . includes ( 'no such path' ) || msg . includes ( 'does not have any commits' ) ) ) {
37- Logger . warn ( 'git' , ...args , ` cwd='${ cwd } '` , msg && `\n ${ msg . replace ( / \r ? \n | \r / g, ' ' ) } ` ) ;
38- return '' ;
39- }
40- else {
41- Logger . error ( ex , 'git' , ...args , ` cwd='${ cwd } '` , msg && `\n ${ msg . replace ( / \r ? \n | \r / g, ' ' ) } ` ) ;
52+ if ( msg ) {
53+ for ( const warning of GitWarnings ) {
54+ if ( warning . test ( msg ) ) {
55+ Logger . warn ( 'git' , ...args , ` cwd='${ options . cwd } '` , msg && `\n ${ msg . replace ( / \r ? \n | \r / g, ' ' ) } ` ) ;
56+ return '' ;
57+ }
58+ }
4259 }
60+
61+ Logger . error ( ex , 'git' , ...args , ` cwd='${ options . cwd } '` , msg && `\n ${ msg . replace ( / \r ? \n | \r / g, ' ' ) } ` ) ;
4362 throw ex ;
4463 }
4564}
@@ -62,14 +81,14 @@ export class Git {
6281 static async getRepoPath ( cwd : string | undefined ) {
6382 if ( cwd === undefined ) return '' ;
6483
65- const data = await gitCommand ( cwd , 'rev-parse' , '--show-toplevel' ) ;
84+ const data = await gitCommand ( { cwd } , 'rev-parse' , '--show-toplevel' ) ;
6685 if ( ! data ) return '' ;
6786
6887 return data . replace ( / \r ? \n | \r / g, '' ) . replace ( / \\ / g, '/' ) ;
6988 }
7089
7190 static async getVersionedFile ( repoPath : string | undefined , fileName : string , branchOrSha : string ) {
72- const data = await Git . show ( repoPath , fileName , branchOrSha ) ;
91+ const data = await Git . show ( repoPath , fileName , branchOrSha , 'binary' ) ;
7392
7493 const suffix = Git . isSha ( branchOrSha ) ? branchOrSha . substring ( 0 , 8 ) : branchOrSha ;
7594 const ext = path . extname ( fileName ) ;
@@ -82,7 +101,7 @@ export class Git {
82101 }
83102
84103 Logger . log ( `getVersionedFile('${ repoPath } ', '${ fileName } ', ${ branchOrSha } ); destination=${ destination } ` ) ;
85- fs . appendFile ( destination , data , err => {
104+ fs . appendFile ( destination , data , { encoding : 'binary' } , err => {
86105 if ( err ) {
87106 reject ( err ) ;
88107 return ;
@@ -144,7 +163,7 @@ export class Git {
144163 params . push ( sha ) ;
145164 }
146165
147- return gitCommand ( root , ...params , `--` , file ) ;
166+ return gitCommand ( { cwd : root } , ...params , `--` , file ) ;
148167 }
149168
150169 static branch ( repoPath : string , all : boolean ) {
@@ -153,19 +172,19 @@ export class Git {
153172 params . push ( `-a` ) ;
154173 }
155174
156- return gitCommand ( repoPath , ...params ) ;
175+ return gitCommand ( { cwd : repoPath } , ...params ) ;
157176 }
158177
159178 static async config_get ( key : string , repoPath ?: string ) {
160179 try {
161- return await gitCommand ( repoPath || '' , `config` , `--get` , key ) ;
180+ return await gitCommand ( { cwd : repoPath || '' } , `config` , `--get` , key ) ;
162181 }
163182 catch ( ex ) {
164183 return '' ;
165184 }
166185 }
167186
168- static diff ( repoPath : string , fileName : string , sha1 ?: string , sha2 ?: string ) {
187+ static diff ( repoPath : string , fileName : string , sha1 ?: string , sha2 ?: string , encoding ?: string ) {
169188 const params = [ `diff` , `--diff-filter=M` , `-M` ] ;
170189 if ( sha1 ) {
171190 params . push ( sha1 ) ;
@@ -174,7 +193,7 @@ export class Git {
174193 params . push ( sha2 ) ;
175194 }
176195
177- return gitCommand ( repoPath , ...params , '--' , fileName ) ;
196+ return gitCommand ( { cwd : repoPath , encoding : encoding || defaultEncoding } , ...params , '--' , fileName ) ;
178197 }
179198
180199 static diff_nameStatus ( repoPath : string , sha1 ?: string , sha2 ?: string ) {
@@ -186,7 +205,7 @@ export class Git {
186205 params . push ( sha2 ) ;
187206 }
188207
189- return gitCommand ( repoPath , ...params ) ;
208+ return gitCommand ( { cwd : repoPath } , ...params ) ;
190209 }
191210
192211 static difftool_dirDiff ( repoPath : string , sha1 : string , sha2 ?: string ) {
@@ -195,7 +214,7 @@ export class Git {
195214 params . push ( sha2 ) ;
196215 }
197216
198- return gitCommand ( repoPath , ...params ) ;
217+ return gitCommand ( { cwd : repoPath } , ...params ) ;
199218 }
200219
201220 static log ( repoPath : string , sha ?: string , maxCount ?: number , reverse : boolean = false ) {
@@ -213,7 +232,7 @@ export class Git {
213232 params . push ( sha ) ;
214233 }
215234 }
216- return gitCommand ( repoPath , ...params ) ;
235+ return gitCommand ( { cwd : repoPath } , ...params ) ;
217236 }
218237
219238 static log_file ( repoPath : string , fileName : string , sha ?: string , maxCount ?: number , reverse : boolean = false , startLine ?: number , endLine ?: number ) {
@@ -250,7 +269,7 @@ export class Git {
250269 params . push ( `--` ) ;
251270 params . push ( file ) ;
252271
253- return gitCommand ( root , ...params ) ;
272+ return gitCommand ( { cwd : root } , ...params ) ;
254273 }
255274
256275 static log_search ( repoPath : string , search : string [ ] = [ ] , maxCount ?: number ) {
@@ -259,46 +278,46 @@ export class Git {
259278 params . push ( `-n${ maxCount } ` ) ;
260279 }
261280
262- return gitCommand ( repoPath , ...params , ...search ) ;
281+ return gitCommand ( { cwd : repoPath } , ...params , ...search ) ;
263282 }
264283
265284 static async ls_files ( repoPath : string , fileName : string ) : Promise < string > {
266285 try {
267- return await gitCommand ( repoPath , 'ls-files' , fileName ) ;
286+ return await gitCommand ( { cwd : repoPath } , 'ls-files' , fileName ) ;
268287 }
269288 catch ( ex ) {
270289 return '' ;
271290 }
272291 }
273292
274293 static remote ( repoPath : string ) : Promise < string > {
275- return gitCommand ( repoPath , 'remote' , '-v' ) ;
294+ return gitCommand ( { cwd : repoPath } , 'remote' , '-v' ) ;
276295 }
277296
278297 static remote_url ( repoPath : string , remote : string ) : Promise < string > {
279- return gitCommand ( repoPath , 'remote' , 'get-url' , remote ) ;
298+ return gitCommand ( { cwd : repoPath } , 'remote' , 'get-url' , remote ) ;
280299 }
281300
282- static show ( repoPath : string | undefined , fileName : string , branchOrSha : string ) {
301+ static show ( repoPath : string | undefined , fileName : string , branchOrSha : string , encoding ?: string ) {
283302 const [ file , root ] = Git . splitPath ( fileName , repoPath ) ;
284303 branchOrSha = branchOrSha . replace ( '^' , '' ) ;
285304
286305 if ( Git . isUncommitted ( branchOrSha ) ) return Promise . reject ( new Error ( `sha=${ branchOrSha } is uncommitted` ) ) ;
287- return gitCommand ( root , 'show' , `${ branchOrSha } :./${ file } ` ) ;
306+ return gitCommand ( { cwd : root , encoding : encoding || defaultEncoding } , 'show' , `${ branchOrSha } :./${ file } ` ) ;
288307 }
289308
290309 static stash_apply ( repoPath : string , stashName : string , deleteAfter : boolean ) {
291310 if ( ! stashName ) return undefined ;
292- return gitCommand ( repoPath , 'stash' , deleteAfter ? 'pop' : 'apply' , stashName ) ;
311+ return gitCommand ( { cwd : repoPath } , 'stash' , deleteAfter ? 'pop' : 'apply' , stashName ) ;
293312 }
294313
295314 static stash_delete ( repoPath : string , stashName : string ) {
296315 if ( ! stashName ) return undefined ;
297- return gitCommand ( repoPath , 'stash' , 'drop' , stashName ) ;
316+ return gitCommand ( { cwd : repoPath } , 'stash' , 'drop' , stashName ) ;
298317 }
299318
300319 static stash_list ( repoPath : string ) {
301- return gitCommand ( repoPath , ...defaultStashParams ) ;
320+ return gitCommand ( { cwd : repoPath } , ...defaultStashParams ) ;
302321 }
303322
304323 static stash_save ( repoPath : string , message ?: string , unstagedOnly : boolean = false ) {
@@ -309,18 +328,18 @@ export class Git {
309328 if ( message ) {
310329 params . push ( message ) ;
311330 }
312- return gitCommand ( repoPath , ...params ) ;
331+ return gitCommand ( { cwd : repoPath } , ...params ) ;
313332 }
314333
315334 static status ( repoPath : string , porcelainVersion : number = 1 ) : Promise < string > {
316335 const porcelain = porcelainVersion >= 2 ? `--porcelain=v${ porcelainVersion } ` : '--porcelain' ;
317- return gitCommand ( repoPath , 'status' , porcelain , '--branch' ) ;
336+ return gitCommand ( { cwd : repoPath } , 'status' , porcelain , '--branch' ) ;
318337 }
319338
320339 static status_file ( repoPath : string , fileName : string , porcelainVersion : number = 1 ) : Promise < string > {
321340 const [ file , root ] = Git . splitPath ( fileName , repoPath ) ;
322341
323342 const porcelain = porcelainVersion >= 2 ? `--porcelain=v${ porcelainVersion } ` : '--porcelain' ;
324- return gitCommand ( root , 'status' , porcelain , file ) ;
343+ return gitCommand ( { cwd : root } , 'status' , porcelain , file ) ;
325344 }
326345}
0 commit comments