Skip to content

Commit 6668ac9

Browse files
committed
Always decode the first two bytes of a commit ID into ushort as big endian
This fixes a non-deterministic version computation that varied based on endianness of the processor. Most computers use little-endian, which produced a decimal version that when converted back to hex with a typical calculator would have the first two commit ID bytes in swapped positions. Now with this change, we "fix" the endianness. We choose big endian since it won't swap the first two bytes like little endian did. But this presents another problem: all the generated versions now have a different value for the 4th integer component, and `nbgv get-commits` will no longer match a commit when it built using the little-endian version of NB.GV. I'll fix this in a subsequent commit so that this CLI tool will match on commits allowing the order to be swapped. Fixes #637
1 parent 3272834 commit 6668ac9

File tree

4 files changed

+25
-5
lines changed

4 files changed

+25
-5
lines changed

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -93,7 +93,7 @@ public void AsUInt16Test()
9393
{
9494
// The hash code is the int32 representation of the first 4 bytes
9595
var objectId = GitObjectId.ParseHex(this.shaAsHexAsciiByteArray);
96-
Assert.Equal(0x914e, objectId.AsUInt16());
96+
Assert.Equal(0x4e91, objectId.AsUInt16());
9797
Assert.Equal(0, GitObjectId.Empty.GetHashCode());
9898
}
9999

src/NerdBank.GitVersioning.Tests/VersionOracleTests.cs

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -845,6 +845,26 @@ public void GitCommitIdShort()
845845
}
846846
}
847847

848+
[Fact]
849+
public void GitCommidIdLeading16BitsDecodedWithBigEndian()
850+
{
851+
this.WriteVersionFile(new VersionOptions { Version = SemanticVersion.Parse("1.2"), GitCommitIdShortAutoMinimum = 4 });
852+
this.InitializeSourceControl();
853+
this.AddCommits(1);
854+
var oracle = new VersionOracle(this.Context);
855+
856+
string leadingFourChars = this.Context.GitCommitId.Substring(0, 4);
857+
ushort expectedNumber = FromHex(leadingFourChars);
858+
ushort actualNumber = checked((ushort)oracle.Version.Revision);
859+
this.Logger.WriteLine("First two characters from commit ID in hex is {0}", leadingFourChars);
860+
this.Logger.WriteLine("First two characters, converted to a number is {0}", expectedNumber);
861+
this.Logger.WriteLine("Generated 16-bit ushort from commit ID is {0}, whose hex representation is {1}", actualNumber, ToHex(actualNumber));
862+
Assert.Equal(expectedNumber, actualNumber);
863+
864+
static string ToHex(ushort number) => number.ToString("X");
865+
static ushort FromHex(string hex) => ushort.Parse(hex, System.Globalization.NumberStyles.HexNumber);
866+
}
867+
848868
[Fact(Skip = "Slow test")]
849869
public void GetVersionHeight_VeryLongHistory()
850870
{

src/NerdBank.GitVersioning/LibGit2/LibGit2GitExtensions.cs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,8 @@
33
namespace Nerdbank.GitVersioning.LibGit2
44
{
55
using System;
6+
using System.Buffers.Binary;
67
using System.Collections.Generic;
7-
using System.Globalization;
88
using System.IO;
99
using System.Linq;
1010
using System.Runtime.InteropServices;
@@ -106,7 +106,7 @@ public static int GetHeight(LibGit2Context context, Func<Commit, bool>? continue
106106
public static ushort GetTruncatedCommitIdAsUInt16(this Commit commit)
107107
{
108108
Requires.NotNull(commit, nameof(commit));
109-
return BitConverter.ToUInt16(commit.Id.RawId, 0);
109+
return BinaryPrimitives.ReadUInt16BigEndian(commit.Id.RawId);
110110
}
111111

112112
/// <summary>
@@ -301,7 +301,7 @@ private static bool IsCommitIdMismatch(Version version, VersionOptions versionOp
301301
/// <returns><c>True</c> if the object's ID starts with <paramref name="leadingBytes"/> after applying the <paramref name="bitMask"/>.</returns>
302302
private static bool StartsWith(this ObjectId @object, ushort leadingBytes, ushort bitMask = 0xffff)
303303
{
304-
ushort truncatedObjectId = BitConverter.ToUInt16(@object.RawId, 0);
304+
ushort truncatedObjectId = BinaryPrimitives.ReadUInt16BigEndian(@object.RawId);
305305
return (truncatedObjectId & bitMask) == leadingBytes;
306306
}
307307

src/NerdBank.GitVersioning/ManagedGit/GitObjectId.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -167,7 +167,7 @@ public override bool Equals(object? obj)
167167
/// <returns>
168168
/// A <see cref="ushort"/> which represents the first two bytes of this <see cref="GitObjectId"/>.
169169
/// </returns>
170-
public ushort AsUInt16() => BinaryPrimitives.ReadUInt16LittleEndian(this.Value.Slice(0, 2));
170+
public ushort AsUInt16() => BinaryPrimitives.ReadUInt16BigEndian(this.Value.Slice(0, 2));
171171

172172
/// <summary>
173173
/// Returns the SHA1 hash of this object.

0 commit comments

Comments
 (0)