@@ -283,37 +283,42 @@ type CloneResult = {
283283 cleanup : ( ) => Promise < void > ;
284284} ;
285285
286- const isSparseEligible = ( include ?: string [ ] ) => {
287- if ( ! include || include . length === 0 ) {
288- return false ;
289- }
290- for ( const pattern of include ) {
291- if ( ! pattern || pattern . includes ( "**" ) ) {
292- return false ;
293- }
294- }
295- return true ;
296- } ;
286+ const patternHasGlob = ( pattern : string ) =>
287+ pattern . includes ( "*" ) || pattern . includes ( "?" ) || pattern . includes ( "[" ) ;
288+
289+ const normalizeSparsePatterns = ( include ?: string [ ] ) =>
290+ ( include ?? [ ] ) . map ( ( pattern ) => pattern . replace ( / \\ / g, "/" ) ) . filter ( Boolean ) ;
297291
298- const extractSparsePaths = ( include ?: string [ ] ) => {
299- if ( ! include ) {
300- return [ ] ;
292+ const resolveSparseSpec = ( include ?: string [ ] ) => {
293+ const normalized = normalizeSparsePatterns ( include ) ;
294+ if ( normalized . length === 0 ) {
295+ return { enabled : false , mode : "cone" as const , patterns : [ ] as string [ ] } ;
301296 }
302- const paths = include . map ( ( pattern ) => {
303- const normalized = pattern . replace ( / \\ / g, "/" ) ;
304- const starIndex = normalized . indexOf ( "*" ) ;
305- const base = starIndex === - 1 ? normalized : normalized . slice ( 0 , starIndex ) ;
297+ const hasDoubleStar = normalized . some ( ( pattern ) => pattern . includes ( "**" ) ) ;
298+ const hasLiteral = normalized . some ( ( pattern ) => ! patternHasGlob ( pattern ) ) ;
299+ if ( hasDoubleStar || hasLiteral ) {
300+ return { enabled : true , mode : "no-cone" as const , patterns : normalized } ;
301+ }
302+ const paths = normalized . map ( ( pattern ) => {
303+ const starIndex = pattern . indexOf ( "*" ) ;
304+ const base = starIndex === - 1 ? pattern : pattern . slice ( 0 , starIndex ) ;
306305 return base . replace ( / \/ + $ | \/ $ / , "" ) ;
307306 } ) ;
308- return Array . from ( new Set ( paths . filter ( ( value ) => value . length > 0 ) ) ) ;
307+ const uniquePaths = Array . from (
308+ new Set ( paths . filter ( ( value ) => value . length > 0 ) ) ,
309+ ) ;
310+ if ( uniquePaths . length === 0 ) {
311+ return { enabled : true , mode : "no-cone" as const , patterns : normalized } ;
312+ }
313+ return { enabled : true , mode : "cone" as const , patterns : uniquePaths } ;
309314} ;
310315
311316const cloneRepo = async ( params : FetchParams , outDir : string ) => {
312317 if ( params . offline ) {
313318 throw new Error ( `Cannot clone ${ params . repo } while offline.` ) ;
314319 }
315320 const isCommitRef = / ^ [ 0 - 9 a - f ] { 7 , 40 } $ / i. test ( params . ref ) ;
316- const useSparse = isSparseEligible ( params . include ) ;
321+ const sparseSpec = resolveSparseSpec ( params . include ) ;
317322 const buildCloneArgs = ( ) => {
318323 const cloneArgs = [
319324 "clone" ,
@@ -326,7 +331,7 @@ const cloneRepo = async (params: FetchParams, outDir: string) => {
326331 return cloneArgs ;
327332 } ;
328333 const cloneArgs = buildCloneArgs ( ) ;
329- if ( useSparse ) {
334+ if ( sparseSpec . enabled ) {
330335 cloneArgs . push ( "--sparse" ) ;
331336 }
332337 if ( ! isCommitRef ) {
@@ -347,14 +352,16 @@ const cloneRepo = async (params: FetchParams, outDir: string) => {
347352 logger : params . logger ,
348353 offline : params . offline ,
349354 } ) ;
350- if ( useSparse ) {
351- const sparsePaths = extractSparsePaths ( params . include ) ;
352- if ( sparsePaths . length > 0 ) {
353- await git ( [ "-C" , outDir , "sparse-checkout" , "set" , ...sparsePaths ] , {
354- timeoutMs : params . timeoutMs ,
355- logger : params . logger ,
356- } ) ;
355+ if ( sparseSpec . enabled ) {
356+ const sparseArgs = [ "-C" , outDir , "sparse-checkout" , "set" ] ;
357+ if ( sparseSpec . mode === "no-cone" ) {
358+ sparseArgs . push ( "--no-cone" ) ;
357359 }
360+ sparseArgs . push ( ...sparseSpec . patterns ) ;
361+ await git ( sparseArgs , {
362+ timeoutMs : params . timeoutMs ,
363+ logger : params . logger ,
364+ } ) ;
358365 }
359366 await git (
360367 [ "-C" , outDir , "checkout" , "--quiet" , "--detach" , params . resolvedCommit ] ,
@@ -394,11 +401,14 @@ const addWorktreeFromCache = async (
394401 allowFileProtocol : true ,
395402 } ,
396403 ) ;
397- const sparsePaths = isSparseEligible ( params . include )
398- ? extractSparsePaths ( params . include )
399- : [ ] ;
400- if ( sparsePaths . length > 0 ) {
401- await git ( [ "-C" , outDir , "sparse-checkout" , "set" , ...sparsePaths ] , {
404+ const sparseSpec = resolveSparseSpec ( params . include ) ;
405+ if ( sparseSpec . enabled ) {
406+ const sparseArgs = [ "-C" , outDir , "sparse-checkout" , "set" ] ;
407+ if ( sparseSpec . mode === "no-cone" ) {
408+ sparseArgs . push ( "--no-cone" ) ;
409+ }
410+ sparseArgs . push ( ...sparseSpec . patterns ) ;
411+ await git ( sparseArgs , {
402412 timeoutMs : params . timeoutMs ,
403413 logger : params . logger ,
404414 allowFileProtocol : true ,
@@ -518,7 +528,7 @@ const cloneOrUpdateRepo = async (
518528 const cacheExists = await exists ( cachePath ) ;
519529 const cacheValid = cacheExists && ( await isValidGitRepo ( cachePath ) ) ;
520530 const isCommitRef = / ^ [ 0 - 9 a - f ] { 7 , 40 } $ / i. test ( params . ref ) ;
521- const useSparse = isSparseEligible ( params . include ) ;
531+ const sparseSpec = resolveSparseSpec ( params . include ) ;
522532 let usedCache = cacheValid ;
523533 let worktreeUsed = false ;
524534
@@ -554,7 +564,7 @@ const cloneOrUpdateRepo = async (
554564 localCloneArgs . splice ( 2 , 0 , "--filter=blob:none" ) ;
555565 }
556566
557- if ( useSparse ) {
567+ if ( sparseSpec . enabled ) {
558568 localCloneArgs . push ( "--sparse" ) ;
559569 }
560570
@@ -575,15 +585,17 @@ const cloneOrUpdateRepo = async (
575585 forceProgress : Boolean ( params . progressLogger ) ,
576586 } ) ;
577587
578- if ( useSparse ) {
579- const sparsePaths = extractSparsePaths ( params . include ) ;
580- if ( sparsePaths . length > 0 ) {
581- await git ( [ "-C" , outDir , "sparse-checkout" , "set" , ...sparsePaths ] , {
582- timeoutMs : params . timeoutMs ,
583- allowFileProtocol : true ,
584- logger : params . logger ,
585- } ) ;
588+ if ( sparseSpec . enabled ) {
589+ const sparseArgs = [ "-C" , outDir , "sparse-checkout" , "set" ] ;
590+ if ( sparseSpec . mode === "no-cone" ) {
591+ sparseArgs . push ( "--no-cone" ) ;
586592 }
593+ sparseArgs . push ( ...sparseSpec . patterns ) ;
594+ await git ( sparseArgs , {
595+ timeoutMs : params . timeoutMs ,
596+ allowFileProtocol : true ,
597+ logger : params . logger ,
598+ } ) ;
587599 }
588600
589601 await ensureCommitAvailable ( outDir , params . resolvedCommit , {
0 commit comments