From 775a2b03b3f6d01cd94f24b665a8c4f774ef0605 Mon Sep 17 00:00:00 2001 From: Frederik Carlier Date: Tue, 27 Apr 2021 09:55:57 +0200 Subject: [PATCH 1/3] Improve alternate parsing --- .../ManagedGit/GitRepositoryTests.cs | 19 ++++++++ .../ManagedGit/GitRepository.cs | 45 +++++++++++++++---- 2 files changed, 55 insertions(+), 9 deletions(-) diff --git a/src/NerdBank.GitVersioning.Tests/ManagedGit/GitRepositoryTests.cs b/src/NerdBank.GitVersioning.Tests/ManagedGit/GitRepositoryTests.cs index fb8a3ba0..c7a70c65 100644 --- a/src/NerdBank.GitVersioning.Tests/ManagedGit/GitRepositoryTests.cs +++ b/src/NerdBank.GitVersioning.Tests/ManagedGit/GitRepositoryTests.cs @@ -286,6 +286,25 @@ public void GetMissingObjectByShaTest() } } + [Fact] + public void ParseAlternates_SingleValue_Test() + { + var alternates = GitRepository.ParseAlternates(Encoding.UTF8.GetBytes("/home/git/nbgv/.git/objects\n")); + Assert.Collection( + alternates, + a => Assert.Equal("/home/git/nbgv/.git/objects", a)); + } + + [Fact] + public void ParseAlternates_TwoValues_Test() + { + var alternates = GitRepository.ParseAlternates(Encoding.UTF8.GetBytes("/home/git/nbgv/.git/objects:../../clone/.git/objects\n")); + Assert.Collection( + alternates, + a => Assert.Equal("/home/git/nbgv/.git/objects", a), + a => Assert.Equal("../../clone/.git/objects", a)); + } + private static void AssertPath(string expected, string actual) { Assert.Equal( diff --git a/src/NerdBank.GitVersioning/ManagedGit/GitRepository.cs b/src/NerdBank.GitVersioning/ManagedGit/GitRepository.cs index 227e2b89..ab8428be 100644 --- a/src/NerdBank.GitVersioning/ManagedGit/GitRepository.cs +++ b/src/NerdBank.GitVersioning/ManagedGit/GitRepository.cs @@ -6,6 +6,7 @@ using System.Globalization; using System.IO; using System.Linq; +using System.Runtime.InteropServices; using System.Text; namespace Nerdbank.GitVersioning.ManagedGit @@ -110,16 +111,14 @@ public GitRepository(string workingDirectory, string gitDirectory, string common var length = alternateStream!.Read(alternates); alternates = alternates.Slice(0, length); - int index = 0; - - while ((index = alternates.IndexOf((byte)':')) > 0) + foreach(var alternate in ParseAlternates(alternates)) { - var alternate = GetString(alternates.Slice(0, index)); - alternate = Path.GetFullPath(Path.Combine(this.ObjectDirectory, alternate)); - - this.alternates.Add(GitRepository.Create(workingDirectory, gitDirectory, commonDirectory, alternate)); - - alternates = alternates.Slice(index + 1); + this.alternates.Add( + GitRepository.Create( + workingDirectory, + gitDirectory, + commonDirectory, + objectDirectory: Path.GetFullPath(Path.Combine(this.ObjectDirectory, alternate)))); } } @@ -714,5 +713,33 @@ public static unsafe string GetString(ReadOnlySpan bytes) return Encoding.GetString(pBytes, bytes.Length); } } + + /// + /// Parses the contents of the alternates file, and returns a list of (relative) paths to the alternate object directories. + /// + /// + /// The contents of the alternates files. + /// + /// + /// A list of (relative) paths to the alternate object directories. + public static List ParseAlternates(ReadOnlySpan alternates) + { + List values = new List(); + + int index = 0; + + // The alternates path is colon (:)-separated. On Windows, there may be full paths, such as + // C:/Users/username/source/repos/nbgv/.git, which also contain a colon. Because the colon + // can only appear at the second position, we skip the first two characters (e.g. C:) on Windows. + int skipCount = RuntimeInformation.IsOSPlatform(OSPlatform.Windows) ? 2 : 0; + + while (alternates.Length > skipCount && (index = alternates.Slice(skipCount).IndexOfAny((byte)':', (byte)'\n')) > 0) + { + values.Add(GetString(alternates.Slice(0, skipCount + index))); + alternates = alternates.Slice(skipCount + index + 1); + } + + return values; + } } } From cb37baab2b7c93c6ae6d15bf7b12c63e165e67b7 Mon Sep 17 00:00:00 2001 From: Frederik Carlier Date: Fri, 30 Apr 2021 11:19:36 +0200 Subject: [PATCH 2/3] Update src/NerdBank.GitVersioning/ManagedGit/GitRepository.cs Co-authored-by: Andrew Arnott --- src/NerdBank.GitVersioning/ManagedGit/GitRepository.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/NerdBank.GitVersioning/ManagedGit/GitRepository.cs b/src/NerdBank.GitVersioning/ManagedGit/GitRepository.cs index ab8428be..6b7452a1 100644 --- a/src/NerdBank.GitVersioning/ManagedGit/GitRepository.cs +++ b/src/NerdBank.GitVersioning/ManagedGit/GitRepository.cs @@ -721,7 +721,8 @@ public static unsafe string GetString(ReadOnlySpan bytes) /// The contents of the alternates files. /// /// - /// A list of (relative) paths to the alternate object directories. + /// A list of (relative) paths to the alternate object directories. + /// public static List ParseAlternates(ReadOnlySpan alternates) { List values = new List(); From c311a3f5b8b93dbd72c62090820a1da0527191f1 Mon Sep 17 00:00:00 2001 From: Frederik Carlier Date: Fri, 30 Apr 2021 11:25:58 +0200 Subject: [PATCH 3/3] Add unit tests for alternates with colons in their path (e.g. C:/Users/) --- .../ManagedGit/GitRepositoryTests.cs | 13 ++++++++++++ .../ManagedGit/GitRepository.cs | 21 +++++++++++++++---- 2 files changed, 30 insertions(+), 4 deletions(-) diff --git a/src/NerdBank.GitVersioning.Tests/ManagedGit/GitRepositoryTests.cs b/src/NerdBank.GitVersioning.Tests/ManagedGit/GitRepositoryTests.cs index c7a70c65..82174ee9 100644 --- a/src/NerdBank.GitVersioning.Tests/ManagedGit/GitRepositoryTests.cs +++ b/src/NerdBank.GitVersioning.Tests/ManagedGit/GitRepositoryTests.cs @@ -305,6 +305,19 @@ public void ParseAlternates_TwoValues_Test() a => Assert.Equal("../../clone/.git/objects", a)); } + [Fact] + public void ParseAlternates_PathWithColon_Test() + { + var alternates = GitRepository.ParseAlternates( + Encoding.UTF8.GetBytes("C:/Users/nbgv/objects:C:/Users/nbgv2/objects/:../../clone/.git/objects\n"), + 2); + Assert.Collection( + alternates, + a => Assert.Equal("C:/Users/nbgv/objects", a), + a => Assert.Equal("C:/Users/nbgv2/objects/", a), + a => Assert.Equal("../../clone/.git/objects", a)); + } + private static void AssertPath(string expected, string actual) { Assert.Equal( diff --git a/src/NerdBank.GitVersioning/ManagedGit/GitRepository.cs b/src/NerdBank.GitVersioning/ManagedGit/GitRepository.cs index 6b7452a1..d6d829d2 100644 --- a/src/NerdBank.GitVersioning/ManagedGit/GitRepository.cs +++ b/src/NerdBank.GitVersioning/ManagedGit/GitRepository.cs @@ -111,7 +111,7 @@ public GitRepository(string workingDirectory, string gitDirectory, string common var length = alternateStream!.Read(alternates); alternates = alternates.Slice(0, length); - foreach(var alternate in ParseAlternates(alternates)) + foreach (var alternate in ParseAlternates(alternates)) { this.alternates.Add( GitRepository.Create( @@ -724,16 +724,29 @@ public static unsafe string GetString(ReadOnlySpan bytes) /// A list of (relative) paths to the alternate object directories. /// public static List ParseAlternates(ReadOnlySpan alternates) + => ParseAlternates(alternates, RuntimeInformation.IsOSPlatform(OSPlatform.Windows) ? 2 : 0); + + /// + /// Parses the contents of the alternates file, and returns a list of (relative) paths to the alternate object directories. + /// + /// + /// The contents of the alternates files. + /// + /// + /// The number of bytes to skip in the span when looking for a delimiter. + /// + /// + /// A list of (relative) paths to the alternate object directories. + /// + public static List ParseAlternates(ReadOnlySpan alternates, int skipCount) { List values = new List(); - int index = 0; + int index; // The alternates path is colon (:)-separated. On Windows, there may be full paths, such as // C:/Users/username/source/repos/nbgv/.git, which also contain a colon. Because the colon // can only appear at the second position, we skip the first two characters (e.g. C:) on Windows. - int skipCount = RuntimeInformation.IsOSPlatform(OSPlatform.Windows) ? 2 : 0; - while (alternates.Length > skipCount && (index = alternates.Slice(skipCount).IndexOfAny((byte)':', (byte)'\n')) > 0) { values.Add(GetString(alternates.Slice(0, skipCount + index)));