Skip to content

Commit

Permalink
Intercept and convert \\wsl.localhost\ to \\wsl$\ paths
Browse files Browse the repository at this point in the history
Resolves #10025
Resolves #10815
  • Loading branch information
RussKie committed Apr 23, 2023
1 parent 1b31830 commit 42ce074
Show file tree
Hide file tree
Showing 4 changed files with 66 additions and 22 deletions.
2 changes: 1 addition & 1 deletion GitCommands/Git/GitModule.cs
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,7 @@ public sealed class GitModule : IGitModule

public GitModule(string? workingDir)
{
WorkingDir = (workingDir ?? "").NormalizePath().EnsureTrailingPathSeparator();
WorkingDir = (workingDir ?? "").NormalizePath().NormalizeWslPath().EnsureTrailingPathSeparator();
WorkingDirGitDir = GitDirectoryResolverInstance.Resolve(WorkingDir);
_indexLockManager = new IndexLockManager(this);
_commitDataManager = new CommitDataManager(() => this);
Expand Down
40 changes: 31 additions & 9 deletions GitCommands/PathUtil.cs
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ public static class PathUtil

// Windows build 21354 supports wsl.localhost too, not supported for WSL Git
private const string WslPrefix = @"\\wsl$\";
private const string WslLocalhostPrefix = @"\\wsl.localhost\";

public static readonly char PosixDirectorySeparatorChar = '/';
public static readonly char NativeDirectorySeparatorChar = Path.DirectorySeparatorChar;
Expand Down Expand Up @@ -146,6 +147,23 @@ public static string NormalizePath(this string path)
}
}

/// <summary>
/// Replaces <c>\\wsl.localhost\</c> with <c>\\wsl$\</c>, if found. Else returns the <paramref name="path"/> untouched.
/// </summary>
/// <param name="path">The path to normalize.</param>
/// <returns>The WSL normalized path.</returns>
public static string NormalizeWslPath(this string path)
{
// NOTE: path is expected to be already normalized!

if (!IsWslLocalhostPrefixPath(path))
{
return path;
}

return path.Replace(WslLocalhostPrefix, WslPrefix, StringComparison.OrdinalIgnoreCase);
}

public static string Resolve(string path, string relativePath = "")
{
if (string.IsNullOrWhiteSpace(path))
Expand Down Expand Up @@ -198,23 +216,26 @@ private static string ResolveRelativePath(string path, string relativePath)
/// Check if the path is any known path for WSL that may require special handling
/// </summary>
/// <param name="path">Path to check</param>
/// <returns>true if a path is a known WSL path</returns>
public static bool IsWslPath(string path)
/// <returns><see langword="true"/> if a path is a known WSL path; otherwise, <see langword="false"/>.</returns>
public static bool IsWslPath(string? path)
{
return !string.IsNullOrWhiteSpace(path)
&& (IsWslPrefixPath(path)
|| path.ToLower().StartsWith(@"\\wsl.localhost\"));
&& (IsWslPrefixPath(path) || IsWslLocalhostPrefixPath(path));
}

/// <summary>
/// Check if the path starts with '\\wsl.localhost\'.
/// </summary>
/// <param name="path">Path to check</param>
/// <returns><see langword="true"/> if the path starts with '\\wsl.localhost\'; otherwise, <see langword="false"/>.</returns>
private static bool IsWslLocalhostPrefixPath(string path) => path.StartsWith(WslLocalhostPrefix, StringComparison.OrdinalIgnoreCase);

/// <summary>
/// Check if the path is has handled specially for WSL
/// </summary>
/// <param name="path">Path to check</param>
/// <returns>true if the path is a WSL path with internal handling</returns>
private static bool IsWslPrefixPath(string path)
{
return path.ToLower().StartsWith(WslPrefix);
}
/// <returns><see langword="true"/> if the path is a WSL path with internal handling; otherwise, <see langword="false"/>.</returns>
private static bool IsWslPrefixPath(string path) => path.StartsWith(WslPrefix, StringComparison.OrdinalIgnoreCase);

/// <summary>
/// Get the name of the distribution (like "Ubuntu-20.04") for WSL2 paths.
Expand Down Expand Up @@ -536,6 +557,7 @@ public static bool TryDeleteDirectory(this string? path, [NotNullWhen(returnValu

internal readonly struct TestAccessor
{
public static bool IsWslLocalhostPrefixPath(string path) => PathUtil.IsWslLocalhostPrefixPath(path);
public static bool IsWslPrefixPath(string path) => PathUtil.IsWslPrefixPath(path);
}
}
Expand Down
10 changes: 8 additions & 2 deletions GitExtensions/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -34,12 +34,18 @@ private static void Main()
Application.SetCompatibleTextRenderingDefault(false);
Application.SetHighDpiMode(HighDpiMode.SystemAware);

bool checkForIllegalCrossThreadCalls = false;
#if DEBUG
checkForIllegalCrossThreadCalls = true;
#endif
if (ThisAssembly.Git.IsDirty)
{
// In non official builds force to fail for cross-thread operations so we can fix those.
Control.CheckForIllegalCrossThreadCalls = true;
checkForIllegalCrossThreadCalls = true;
}

// In non official builds force to fail for cross-thread operations so we can fix those.
Control.CheckForIllegalCrossThreadCalls = checkForIllegalCrossThreadCalls;

// If an error happens before we had a chance to init the environment information
// the call to GetInformation() from BugReporter.ShowNBug() will fail.
// There's no perf hit calling Initialise() multiple times.
Expand Down
36 changes: 26 additions & 10 deletions UnitTests/GitCommands.Tests/Helpers/PathUtilTest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -177,16 +177,10 @@ public void GetRepositoryNameTest()
}

[Platform(Include = "Win")]
[TestCase(null)]
[TestCase("")]
[TestCase(" ")]
[TestCase("c:")]
public void NormalizePath(string path)
{
PathUtil.NormalizePath(path).Should().BeEmpty();
}

[Platform(Include = "Win")]
[TestCase(null, "")]
[TestCase("", "")]
[TestCase(" ", "")]
[TestCase("c:", "")]
[TestCase("C:\\", "C:\\")]
[TestCase("a:\\folder\\filename.txt", "a:\\folder\\filename.txt")]
[TestCase("a:\\folder\\..\\filename.txt", "a:\\filename.txt")]
Expand All @@ -206,6 +200,19 @@ public void NormalizePath(string path, string expected)
PathUtil.NormalizePath(path).Should().Be(expected);
}

[Platform(Include = "Win")]
[TestCase("", "")]
[TestCase(" ", " ")]
[TestCase("c:", "c:")]
[TestCase("C:\\", "C:\\")]
[TestCase(@"\\wsl$\Ubuntu\home\jack\work\", @"\\wsl$\Ubuntu\home\jack\work\")]
[TestCase(@"\\Wsl.LoCALhosT\Ubuntu\home\jack\work\", @"\\wsl$\Ubuntu\home\jack\work\")]
[TestCase(@"\\wsl.localhost\Ubuntu\home\jack\work\", @"\\wsl$\Ubuntu\home\jack\work\")]
public void NormalizeWslPath(string path, string expected)
{
PathUtil.NormalizeWslPath(path).Should().Be(expected);
}

[TestCase(@"C:\WORK\GitExtensions\", @"C:\WORK\GitExtensions\")]
[TestCase(@"\\my-pc\Work\GitExtensions\", @"\\my-pc\Work\GitExtensions\")]
[TestCase(@"\\wsl$\Ubuntu\home\jack\work\", @"\\wsl$\Ubuntu\home\jack\work\")]
Expand Down Expand Up @@ -255,6 +262,15 @@ public void ResolveWsl(string input, Type expectedException)
Assert.Throws(expectedException, () => PathUtil.ResolveWsl(input));
}

[TestCase(@"C:\work\..\GitExtensions\", false)]
[TestCase(@"\\Wsl$\Ubuntu\work\..\GitExtensions\", false)]
[TestCase(@"\\wsl.localhost\Ubuntu\work\..\GitExtensions\", true)]
[TestCase(@"\\wsl.localhost/Ubuntu\work\..\GitExtensions\", false)]
public void IsWslLocalhostPath(string path, bool expected)
{
PathUtil.TestAccessor.IsWslLocalhostPrefixPath(path).Should().Be(expected);
}

[TestCase(@"\\Wsl$\Ubuntu\work\..\GitExtensions\", true, true)]
[TestCase(@"\\wsl$\Ubuntu\work\..\GitExtensions\", true, true)]
[TestCase(@"C:\work\..\GitExtensions\", false, false)]
Expand Down

0 comments on commit 42ce074

Please sign in to comment.