8
8
import * as path from 'path' ;
9
9
import * as os from 'os' ;
10
10
import * as fs from 'graceful-fs' ;
11
- import { NamedPackageDir , Logger } from '@salesforce/core' ;
11
+ import { NamedPackageDir , Logger , SfdxError } from '@salesforce/core' ;
12
12
import * as git from 'isomorphic-git' ;
13
- import { pathIsInFolder } from './functions' ;
13
+ import { chunkArray , pathIsInFolder } from './functions' ;
14
14
15
- const gitIgnoreFileName = '.gitignore' ;
16
- /**
17
- * returns the full path to where we store the shadow repo
18
- */
15
+ /** returns the full path to where we store the shadow repo */
19
16
const getGitDir = ( orgId : string , projectPath : string ) : string => {
20
17
return path . join ( projectPath , '.sfdx' , 'orgs' , orgId , 'localSourceTracking' ) ;
21
18
} ;
@@ -53,11 +50,17 @@ export class ShadowRepo {
53
50
private packageDirs ! : NamedPackageDir [ ] ;
54
51
private status ! : StatusRow [ ] ;
55
52
private logger ! : Logger ;
53
+ private isWindows : boolean ;
54
+
55
+ /** do not try to add more than this many files at a time through isogit. You'll hit EMFILE: too many open files even with graceful-fs */
56
+ private maxFileAdd : number ;
56
57
57
58
private constructor ( options : ShadowRepoOptions ) {
58
59
this . gitDir = getGitDir ( options . orgId , options . projectPath ) ;
59
60
this . projectPath = options . projectPath ;
60
61
this . packageDirs = options . packageDirs ;
62
+ this . isWindows = os . type ( ) === 'Windows_NT' ;
63
+ this . maxFileAdd = this . isWindows ? 8000 : 15000 ;
61
64
}
62
65
63
66
// think of singleton behavior but unique to the projectPath
@@ -113,14 +116,12 @@ export class ShadowRepo {
113
116
*/
114
117
public async getStatus ( noCache = false ) : Promise < StatusRow [ ] > {
115
118
if ( ! this . status || noCache ) {
116
- // only ask about OS once but use twice
117
- const isWindows = os . type ( ) === 'Windows_NT' ;
118
119
// iso-git uses relative, posix paths
119
120
// but packageDirs has already resolved / normalized them
120
121
// so we need to make them project-relative again and convert if windows
121
122
const filepaths = this . packageDirs
122
123
. map ( ( dir ) => path . relative ( this . projectPath , dir . fullPath ) )
123
- . map ( ( p ) => ( isWindows ? p . split ( path . sep ) . join ( path . posix . sep ) : p ) ) ;
124
+ . map ( ( p ) => ( this . isWindows ? p . split ( path . sep ) . join ( path . posix . sep ) : p ) ) ;
124
125
125
126
// status hasn't been initalized yet
126
127
this . status = await git . statusMatrix ( {
@@ -135,12 +136,12 @@ export class ShadowRepo {
135
136
// no lwc tests
136
137
! f . includes ( '__tests__' ) &&
137
138
// no gitignore files
138
- ! f . endsWith ( gitIgnoreFileName ) &&
139
+ ! f . endsWith ( '.gitignore' ) &&
139
140
// isogit uses `startsWith` for filepaths so it's possible to get a false positive
140
141
filepaths . some ( ( pkgDir ) => pathIsInFolder ( f , pkgDir ) ) ,
141
142
} ) ;
142
143
// isomorphic-git stores things in unix-style tree. Convert to windows-style if necessary
143
- if ( isWindows ) {
144
+ if ( this . isWindows ) {
144
145
this . status = this . status . map ( ( row ) => [ path . normalize ( row [ FILE ] ) , row [ HEAD ] , row [ WORKDIR ] , row [ 3 ] ] ) ;
145
146
}
146
147
}
@@ -229,13 +230,26 @@ export class ShadowRepo {
229
230
}
230
231
231
232
if ( deployedFiles . length ) {
232
- await git . add ( {
233
- fs,
234
- dir : this . projectPath ,
235
- gitdir : this . gitDir ,
236
- filepath : [ ...new Set ( deployedFiles ) ] ,
237
- force : true ,
238
- } ) ;
233
+ const chunks = chunkArray ( [ ...new Set ( deployedFiles ) ] , this . maxFileAdd ) ;
234
+ for ( const chunk of chunks ) {
235
+ try {
236
+ await git . add ( {
237
+ fs,
238
+ dir : this . projectPath ,
239
+ gitdir : this . gitDir ,
240
+ filepath : chunk ,
241
+ force : true ,
242
+ } ) ;
243
+ } catch ( e ) {
244
+ if ( e instanceof git . Errors . MultipleGitError ) {
245
+ this . logger . error ( 'multiple errors on git.add' , e . errors . slice ( 0 , 5 ) ) ;
246
+ const error = new SfdxError ( e . message , e . name , [ ] , 1 ) ;
247
+ error . setData ( e . errors ) ;
248
+ throw error ;
249
+ }
250
+ throw e ;
251
+ }
252
+ }
239
253
}
240
254
241
255
for ( const filepath of [ ...new Set ( deletedFiles ) ] ) {
0 commit comments