diff --git a/src/System.Management.Automation/namespaces/FileSystemProvider.cs b/src/System.Management.Automation/namespaces/FileSystemProvider.cs
index b62454ac5ce..54c68653ea9 100644
--- a/src/System.Management.Automation/namespaces/FileSystemProvider.cs
+++ b/src/System.Management.Automation/namespaces/FileSystemProvider.cs
@@ -98,7 +98,75 @@ public FileSystemProvider()
///
private static string NormalizePath(string path)
{
- return path.Replace(StringLiterals.AlternatePathSeparator, StringLiterals.DefaultPathSeparator);
+ return GetCorrectCasedPath(path.Replace(StringLiterals.AlternatePathSeparator, StringLiterals.DefaultPathSeparator));
+ }
+
+ ///
+ /// Get the correct casing for a path. This method assumes it's being called by NormalizePath()
+ /// so that the path is already normalized.
+ ///
+ ///
+ /// The path to retrieve.
+ ///
+ ///
+ /// The path with accurate casing if item exists, otherwise it returns path that was passed in.
+ ///
+ private static string GetCorrectCasedPath(string path)
+ {
+ // Only apply to directories where there are issues with some tools if the casing
+ // doesn't match the source like git
+ if (Directory.Exists(path))
+ {
+ string exactPath = string.Empty;
+ int itemsToSkip = 0;
+ if (Utils.PathIsUnc(path))
+ {
+ // With the Split method, a UNC path like \\server\share, we need to skip
+ // trying to enumerate the server and share, so skip the first two empty
+ // strings, then server, and finally share name.
+ itemsToSkip = 4;
+ }
+
+ foreach (string item in path.Split(StringLiterals.DefaultPathSeparator))
+ {
+ if (itemsToSkip-- > 0)
+ {
+ // This handles the UNC server and share and 8.3 short path syntax
+ exactPath += item + StringLiterals.DefaultPathSeparator;
+ continue;
+ }
+ else if (string.IsNullOrEmpty(exactPath))
+ {
+ // This handles the drive letter or / root path start
+ exactPath = item + StringLiterals.DefaultPathSeparator;
+ }
+ else if (string.IsNullOrEmpty(item))
+ {
+ // This handles the trailing slash case
+ continue;
+ }
+ else if (item.Contains('~'))
+ {
+ // This handles short path names
+ exactPath += StringLiterals.DefaultPathSeparator + item;
+ }
+ else
+ {
+ exactPath = Directory.GetFileSystemEntries(exactPath, item).First();
+ }
+ }
+
+ if (path.EndsWith(StringLiterals.DefaultPathSeparator))
+ {
+ return exactPath + StringLiterals.DefaultPathSeparator;
+ }
+
+ return exactPath;
+ }
+ else
+ {
+ return path;
+ }
}
///
diff --git a/test/powershell/Modules/Microsoft.PowerShell.Management/Set-Location.Tests.ps1 b/test/powershell/Modules/Microsoft.PowerShell.Management/Set-Location.Tests.ps1
index 5824419f3b1..10a276b32b6 100644
--- a/test/powershell/Modules/Microsoft.PowerShell.Management/Set-Location.Tests.ps1
+++ b/test/powershell/Modules/Microsoft.PowerShell.Management/Set-Location.Tests.ps1
@@ -70,6 +70,24 @@ Describe "Set-Location" -Tags "CI" {
Should -Throw -ErrorId "PathNotFound,Microsoft.PowerShell.Commands.SetLocationCommand"
}
+ It "Should use actual casing of folder on case-insensitive filesystem" -Skip:($IsLinux) {
+ $testPath = New-Item -ItemType Directory -Path testdrive:/teST
+ Set-Location $testPath.FullName.ToUpper()
+ $(Get-Location).Path | Should -BeExactly $testPath.FullName
+ }
+
+ It "Should use actual casing of folder on case-sensitive filesystem: " -Skip:(!$IsLinux)
+ {
+ $dir = "teST"
+ $testPathLower = New-Item -ItemType Directory -Path (Join-Path $TestDrive $dir.ToLower())
+ $testPathUpper = New-Item -ItemType Directory -Path (Join-Path $TestDrive $dir.ToUpper())
+ Set-Location $testPathLower.FullName
+ $(Get-Location).Path | Should -BeExactly $testPathLower.FullName
+ Set-Location $testPathUpper.FullName
+ $(Get-Location).Path | Should -BeExactly $testPathUpper.FullName
+ { Set-Locaiton (Join-Path $TestDrive $dir) } | Should -Throw -ErrorId "PathNotFound,Microsoft.PowerShell.Commands.SetLocationCommand"
+ }
+
Context 'Set-Location with no arguments' {
It 'Should go to $env:HOME when Set-Location run with no arguments from FileSystem provider' {