Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Return correct casing of filesystem path during normalization #9250

Merged
merged 8 commits into from Apr 23, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
Expand Up @@ -98,7 +98,75 @@ public FileSystemProvider()
/// </returns>
private static string NormalizePath(string path)
{
return path.Replace(StringLiterals.AlternatePathSeparator, StringLiterals.DefaultPathSeparator);
return GetCorrectCasedPath(path.Replace(StringLiterals.AlternatePathSeparator, StringLiterals.DefaultPathSeparator));
}

/// <summary>
/// Get the correct casing for a path. This method assumes it's being called by NormalizePath()
/// so that the path is already normalized.
/// </summary>
/// <param name="path">
/// The path to retrieve.
/// </param>
/// <returns>
/// The path with accurate casing if item exists, otherwise it returns path that was passed in.
/// </returns>
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;
}
}

/// <summary>
Expand Down
Expand Up @@ -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: <dir>" -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' {
Expand Down