@@ -12,6 +12,31 @@ const log = logger.scope("git");
1212const execAsync = promisify ( exec ) ;
1313const fsPromises = fs . promises ;
1414
15+ const getAllFilesInDirectory = async (
16+ directoryPath : string ,
17+ basePath : string ,
18+ ) : Promise < string [ ] > => {
19+ const files : string [ ] = [ ] ;
20+ const entries = await fsPromises . readdir ( path . join ( directoryPath , basePath ) , {
21+ withFileTypes : true ,
22+ } ) ;
23+
24+ for ( const entry of entries ) {
25+ const relativePath = path . join ( basePath , entry . name ) ;
26+ if ( entry . isDirectory ( ) ) {
27+ const subFiles = await getAllFilesInDirectory (
28+ directoryPath ,
29+ relativePath ,
30+ ) ;
31+ files . push ( ...subFiles ) ;
32+ } else {
33+ files . push ( relativePath ) ;
34+ }
35+ }
36+
37+ return files ;
38+ } ;
39+
1540const CLONE_MAX_BUFFER = 10 * 1024 * 1024 ;
1641
1742export interface GitHubRepo {
@@ -215,7 +240,25 @@ const getChangedFilesAgainstHead = async (
215240
216241 // Only add untracked files not already seen
217242 if ( statusCode === "??" && ! seenPaths . has ( filePath ) ) {
218- files . push ( { path : filePath , status : "untracked" } ) ;
243+ // Check if it's a directory (git shows directories with trailing /)
244+ if ( filePath . endsWith ( "/" ) ) {
245+ const dirPath = filePath . slice ( 0 , - 1 ) ;
246+ try {
247+ const dirFiles = await getAllFilesInDirectory (
248+ directoryPath ,
249+ dirPath ,
250+ ) ;
251+ for ( const file of dirFiles ) {
252+ if ( ! seenPaths . has ( file ) ) {
253+ files . push ( { path : file , status : "untracked" } ) ;
254+ }
255+ }
256+ } catch {
257+ // Directory might not exist or be inaccessible
258+ }
259+ } else {
260+ files . push ( { path : filePath , status : "untracked" } ) ;
261+ }
219262 }
220263 }
221264
@@ -274,20 +317,40 @@ const getDiffStats = async (directoryPath: string): Promise<DiffStats> => {
274317 cwd : directoryPath ,
275318 } ) ;
276319
320+ const countLinesInFile = async ( filePath : string ) : Promise < number > => {
321+ try {
322+ const { stdout : wcOutput } = await execAsync ( `wc -l < "${ filePath } "` , {
323+ cwd : directoryPath ,
324+ } ) ;
325+ return parseInt ( wcOutput . trim ( ) , 10 ) || 0 ;
326+ } catch {
327+ return 0 ;
328+ }
329+ } ;
330+
277331 for ( const line of statusOutput . trim ( ) . split ( "\n" ) . filter ( Boolean ) ) {
278332 const statusCode = line . substring ( 0 , 2 ) ;
279333 if ( statusCode === "??" ) {
280- filesChanged ++ ;
281- // Count lines in untracked file
282334 const filePath = line . substring ( 3 ) ;
283- try {
284- const { stdout : wcOutput } = await execAsync (
285- `wc -l < "${ filePath } "` ,
286- { cwd : directoryPath } ,
287- ) ;
288- linesAdded += parseInt ( wcOutput . trim ( ) , 10 ) || 0 ;
289- } catch {
290- // File might be binary or inaccessible
335+
336+ // Check if it's a directory (git shows directories with trailing /)
337+ if ( filePath . endsWith ( "/" ) ) {
338+ const dirPath = filePath . slice ( 0 , - 1 ) ;
339+ try {
340+ const dirFiles = await getAllFilesInDirectory (
341+ directoryPath ,
342+ dirPath ,
343+ ) ;
344+ for ( const file of dirFiles ) {
345+ filesChanged ++ ;
346+ linesAdded += await countLinesInFile ( file ) ;
347+ }
348+ } catch {
349+ // Directory might not exist or be inaccessible
350+ }
351+ } else {
352+ filesChanged ++ ;
353+ linesAdded += await countLinesInFile ( filePath ) ;
291354 }
292355 }
293356 }
0 commit comments