Skip to content

Commit 2363be0

Browse files
authored
Merge pull request #596 from qmfrederik/features/alternates
Improve alternate parsing
2 parents e3477ca + c311a3f commit 2363be0

File tree

2 files changed

+82
-9
lines changed

2 files changed

+82
-9
lines changed

src/NerdBank.GitVersioning.Tests/ManagedGit/GitRepositoryTests.cs

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -286,6 +286,38 @@ public void GetMissingObjectByShaTest()
286286
}
287287
}
288288

289+
[Fact]
290+
public void ParseAlternates_SingleValue_Test()
291+
{
292+
var alternates = GitRepository.ParseAlternates(Encoding.UTF8.GetBytes("/home/git/nbgv/.git/objects\n"));
293+
Assert.Collection(
294+
alternates,
295+
a => Assert.Equal("/home/git/nbgv/.git/objects", a));
296+
}
297+
298+
[Fact]
299+
public void ParseAlternates_TwoValues_Test()
300+
{
301+
var alternates = GitRepository.ParseAlternates(Encoding.UTF8.GetBytes("/home/git/nbgv/.git/objects:../../clone/.git/objects\n"));
302+
Assert.Collection(
303+
alternates,
304+
a => Assert.Equal("/home/git/nbgv/.git/objects", a),
305+
a => Assert.Equal("../../clone/.git/objects", a));
306+
}
307+
308+
[Fact]
309+
public void ParseAlternates_PathWithColon_Test()
310+
{
311+
var alternates = GitRepository.ParseAlternates(
312+
Encoding.UTF8.GetBytes("C:/Users/nbgv/objects:C:/Users/nbgv2/objects/:../../clone/.git/objects\n"),
313+
2);
314+
Assert.Collection(
315+
alternates,
316+
a => Assert.Equal("C:/Users/nbgv/objects", a),
317+
a => Assert.Equal("C:/Users/nbgv2/objects/", a),
318+
a => Assert.Equal("../../clone/.git/objects", a));
319+
}
320+
289321
private static void AssertPath(string expected, string actual)
290322
{
291323
Assert.Equal(

src/NerdBank.GitVersioning/ManagedGit/GitRepository.cs

Lines changed: 50 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
using System.Globalization;
77
using System.IO;
88
using System.Linq;
9+
using System.Runtime.InteropServices;
910
using System.Text;
1011

1112
namespace Nerdbank.GitVersioning.ManagedGit
@@ -110,16 +111,14 @@ public GitRepository(string workingDirectory, string gitDirectory, string common
110111
var length = alternateStream!.Read(alternates);
111112
alternates = alternates.Slice(0, length);
112113

113-
int index = 0;
114-
115-
while ((index = alternates.IndexOf((byte)':')) > 0)
114+
foreach (var alternate in ParseAlternates(alternates))
116115
{
117-
var alternate = GetString(alternates.Slice(0, index));
118-
alternate = Path.GetFullPath(Path.Combine(this.ObjectDirectory, alternate));
119-
120-
this.alternates.Add(GitRepository.Create(workingDirectory, gitDirectory, commonDirectory, alternate));
121-
122-
alternates = alternates.Slice(index + 1);
116+
this.alternates.Add(
117+
GitRepository.Create(
118+
workingDirectory,
119+
gitDirectory,
120+
commonDirectory,
121+
objectDirectory: Path.GetFullPath(Path.Combine(this.ObjectDirectory, alternate))));
123122
}
124123
}
125124

@@ -722,5 +721,47 @@ public static unsafe string GetString(ReadOnlySpan<byte> bytes)
722721
return Encoding.GetString(pBytes, bytes.Length);
723722
}
724723
}
724+
725+
/// <summary>
726+
/// Parses the contents of the alternates file, and returns a list of (relative) paths to the alternate object directories.
727+
/// </summary>
728+
/// <param name="alternates">
729+
/// The contents of the alternates files.
730+
/// </param>
731+
/// <returns>
732+
/// A list of (relative) paths to the alternate object directories.
733+
/// </returns>
734+
public static List<string> ParseAlternates(ReadOnlySpan<byte> alternates)
735+
=> ParseAlternates(alternates, RuntimeInformation.IsOSPlatform(OSPlatform.Windows) ? 2 : 0);
736+
737+
/// <summary>
738+
/// Parses the contents of the alternates file, and returns a list of (relative) paths to the alternate object directories.
739+
/// </summary>
740+
/// <param name="alternates">
741+
/// The contents of the alternates files.
742+
/// </param>
743+
/// <param name="skipCount">
744+
/// The number of bytes to skip in the span when looking for a delimiter.
745+
/// </param>
746+
/// <returns>
747+
/// A list of (relative) paths to the alternate object directories.
748+
/// </returns>
749+
public static List<string> ParseAlternates(ReadOnlySpan<byte> alternates, int skipCount)
750+
{
751+
List<string> values = new List<string>();
752+
753+
int index;
754+
755+
// The alternates path is colon (:)-separated. On Windows, there may be full paths, such as
756+
// C:/Users/username/source/repos/nbgv/.git, which also contain a colon. Because the colon
757+
// can only appear at the second position, we skip the first two characters (e.g. C:) on Windows.
758+
while (alternates.Length > skipCount && (index = alternates.Slice(skipCount).IndexOfAny((byte)':', (byte)'\n')) > 0)
759+
{
760+
values.Add(GetString(alternates.Slice(0, skipCount + index)));
761+
alternates = alternates.Slice(skipCount + index + 1);
762+
}
763+
764+
return values;
765+
}
725766
}
726767
}

0 commit comments

Comments
 (0)