@@ -1298,6 +1298,15 @@ impl Glob {
12981298 return ( self . cwd . clone ( ) , None ) ;
12991299 }
13001300
1301+ // When nocase is true on a case-sensitive filesystem (Linux), we can't use
1302+ // literal prefix optimization because the prefix case might not match the
1303+ // actual filesystem case. For example, pattern "SRC/**" won't find directory
1304+ // "src" on Linux even with nocase:true.
1305+ // On case-insensitive filesystems (macOS, Windows), this is not an issue.
1306+ if self . nocase && !self . is_case_insensitive_platform ( ) {
1307+ return ( self . cwd . clone ( ) , None ) ;
1308+ }
1309+
13011310 // Check if any pattern is absolute (has a root like C:/, /, or //server/share/)
13021311 // If we have absolute patterns, we need to handle them specially
13031312 let has_absolute_pattern = self . patterns . iter ( ) . any ( |p| p. is_absolute ( ) ) ;
@@ -1457,6 +1466,12 @@ impl Glob {
14571466 /// 2. There are multiple distinct first-level prefixes (e.g., `src` and `test`)
14581467 /// 3. All prefixes point to existing directories
14591468 fn should_use_multi_base_walking ( & self ) -> bool {
1469+ // When nocase is true on a case-sensitive filesystem (Linux), we can't use
1470+ // multi-base walking because the prefix case might not match the filesystem.
1471+ if self . nocase && !self . is_case_insensitive_platform ( ) {
1472+ return false ;
1473+ }
1474+
14601475 // Quick check: if any pattern has no prefix, we can't use multi-base
14611476 if self . patterns . iter ( ) . any ( |p| p. literal_prefix ( ) . is_none ( ) ) {
14621477 return false ;
@@ -1811,6 +1826,17 @@ impl Glob {
18111826 self . patterns . iter ( ) . all ( |p| p. max_depth ( ) == Some ( 0 ) )
18121827 }
18131828
1829+ /// Check if the current platform has a case-insensitive filesystem by default.
1830+ ///
1831+ /// This is used to determine if we can use prefix-based walking optimizations
1832+ /// with nocase:true. On macOS and Windows, the filesystem is typically case-insensitive,
1833+ /// so "SRC" and "src" refer to the same directory. On Linux, they're different.
1834+ #[ inline]
1835+ fn is_case_insensitive_platform ( & self ) -> bool {
1836+ // macOS (darwin) and Windows (win32) have case-insensitive filesystems by default
1837+ cfg ! ( target_os = "macos" ) || cfg ! ( target_os = "windows" )
1838+ }
1839+
18141840 /// Resolve shallow patterns using direct readdir.
18151841 ///
18161842 /// This is a fast path for patterns like `*.js` that only match at the root level.
@@ -4130,6 +4156,7 @@ mod tests {
41304156
41314157 // Absolute pattern tests (Task 4.1.1)
41324158
4159+ #[ cfg( unix) ]
41334160 #[ test]
41344161 fn test_absolute_pattern_unix ( ) {
41354162 // Test absolute Unix path pattern
@@ -4198,6 +4225,7 @@ mod tests {
41984225 . any( |r| r. contains( "foo.txt" ) || r. contains( "bar.txt" ) ) ) ;
41994226 }
42004227
4228+ #[ cfg( unix) ]
42014229 #[ test]
42024230 fn test_absolute_pattern_with_literal_prefix ( ) {
42034231 // Test that absolute patterns with literal prefixes work correctly
@@ -4434,8 +4462,14 @@ mod tests {
44344462 assert ! ( !results. is_empty( ) ) ;
44354463 let result = & results[ 0 ] ;
44364464 assert ! ( result. contains( "foo.txt" ) ) ;
4437- // Absolute path should start with / (Unix) or drive letter (Windows)
4438- assert ! ( result. starts_with( '/' ) || result. chars( ) . nth( 1 ) == Some ( ':' ) ) ;
4465+ // Absolute path should start with:
4466+ // - Unix: /
4467+ // - Windows: drive letter (C:) or UNC (\\) or extended-length (\\?\)
4468+ assert ! (
4469+ result. starts_with( '/' )
4470+ || result. chars( ) . nth( 1 ) == Some ( ':' )
4471+ || result. starts_with( "\\ \\ " )
4472+ ) ;
44394473 }
44404474
44414475 #[ test]
0 commit comments