@@ -387,7 +387,7 @@ private static void CreateParentsAndDirectory(string fullPath)
387387 }
388388 }
389389
390- internal static void MoveDirectory ( string sourceFullPath , string destFullPath )
390+ private static void MoveDirectoryCore ( string sourceFullPath , string destFullPath )
391391 {
392392 ReadOnlySpan < char > destNoDirectorySeparator = Path . TrimEndingDirectorySeparator ( destFullPath . AsSpan ( ) ) ;
393393 ReadOnlySpan < char > srcNoDirectorySeparator = Path . TrimEndingDirectorySeparator ( sourceFullPath . AsSpan ( ) ) ;
@@ -399,7 +399,7 @@ internal static void MoveDirectory(string sourceFullPath, string destFullPath)
399399 throw new IOException ( SR . Format ( SR . IO_PathNotFound_Path , sourceFullPath ) ) ;
400400 }
401401
402- // The destination must not exist.
402+ // The destination must not exist (unless it is a case-sensitive rename) .
403403 // On Unix 'rename' will overwrite the destination file if it already exists, we need to manually check.
404404 if ( Interop . Sys . LStat ( destNoDirectorySeparator , out Interop . Sys . FileStatus destFileStatus ) >= 0 )
405405 {
@@ -410,30 +410,32 @@ internal static void MoveDirectory(string sourceFullPath, string destFullPath)
410410 {
411411 throw new DirectoryNotFoundException ( SR . Format ( SR . IO_PathNotFound_Path , sourceFullPath ) ) ;
412412 }
413- // Source and destination must not be the same file.
413+ // Source and destination must not be the same file unless it is a case-sensitive rename .
414414 else if ( sourceFileStatus . Dev == destFileStatus . Dev &&
415415 sourceFileStatus . Ino == destFileStatus . Ino )
416416 {
417- throw new IOException ( SR . IO_SourceDestMustBeDifferent ) ;
417+ // Assume the file system is case-insensitive, and allow a Rename when the FileName casing changes.
418+ if ( ! srcNoDirectorySeparator . Equals ( destNoDirectorySeparator , StringComparison . OrdinalIgnoreCase ) ||
419+ Path . GetFileName ( srcNoDirectorySeparator ) . SequenceEqual ( Path . GetFileName ( destNoDirectorySeparator ) ) )
420+ {
421+ throw new IOException ( SR . IO_SourceDestMustBeDifferent ) ;
422+ }
423+ // Fall through to Rename.
418424 }
419425 // When the path ends with a directory separator, it must be a directory.
420426 else if ( ( sourceFileStatus . Mode & Interop . Sys . FileTypes . S_IFMT ) != Interop . Sys . FileTypes . S_IFDIR
421427 && Path . EndsInDirectorySeparator ( sourceFullPath ) )
422428 {
423429 throw new IOException ( SR . Format ( SR . IO_PathNotFound_Path , sourceFullPath ) ) ;
424430 }
425-
426- throw new IOException ( SR . Format ( SR . IO_AlreadyExists_Name , destFullPath ) ) ;
431+ else
432+ {
433+ throw new IOException ( SR . Format ( SR . IO_AlreadyExists_Name , destFullPath ) ) ;
434+ }
427435 }
428436
429437 if ( Interop . Sys . Rename ( sourceFullPath , destNoDirectorySeparator ) < 0 )
430438 {
431- // Source and destination must not be the same file.
432- if ( srcNoDirectorySeparator . Equals ( destNoDirectorySeparator , PathInternal . StringComparison ) )
433- {
434- throw new IOException ( SR . IO_SourceDestMustBeDifferent ) ;
435- }
436-
437439 Interop . ErrorInfo errorInfo = Interop . Sys . GetLastErrorInfo ( ) ;
438440 switch ( errorInfo . Error )
439441 {
0 commit comments