From c0cec9d635f72bd7b0b5a5e6ced928afe5d4bb84 Mon Sep 17 00:00:00 2001 From: Dmitry Zhelnin Date: Thu, 10 Oct 2024 00:13:09 +0300 Subject: [PATCH] fix GitCommitDate being author date rather than commit date --- .../DisabledGit/DisabledGitContext.cs | 2 + src/NerdBank.GitVersioning/GitContext.cs | 5 +++ .../LibGit2/LibGit2Context.cs | 5 ++- .../Managed/ManagedGitContext.cs | 5 ++- .../ManagedGit/GitCommit.cs | 5 +++ .../ManagedGit/GitCommitReader.cs | 43 +++++++++++++++---- .../ManagedGit/GitRepository.cs | 4 +- .../NoGit/NoGitContext.cs | 2 + src/NerdBank.GitVersioning/VersionOracle.cs | 5 +++ .../AssemblyVersionInfo.cs | 7 +++ .../GetBuildVersion.cs | 9 ++++ .../build/Nerdbank.GitVersioning.targets | 1 + .../BuildIntegrationTests.cs | 5 ++- .../ManagedGit/GitCommitReaderTests.cs | 6 ++- ...t-4497b0eaaa89abf0e6d70961ad5f04fd3a49cbc6 | 2 +- ...t-d56dc3ed179053abef2097d1120b4507769bcf1a | 2 +- 16 files changed, 91 insertions(+), 17 deletions(-) diff --git a/src/NerdBank.GitVersioning/DisabledGit/DisabledGitContext.cs b/src/NerdBank.GitVersioning/DisabledGit/DisabledGitContext.cs index 0b9c5ca6..df6576c6 100644 --- a/src/NerdBank.GitVersioning/DisabledGit/DisabledGitContext.cs +++ b/src/NerdBank.GitVersioning/DisabledGit/DisabledGitContext.cs @@ -24,6 +24,8 @@ public DisabledGitContext(string workingTreePath) public override DateTimeOffset? GitCommitDate => null; + public override DateTimeOffset? GitCommitAuthorDate => null; + public override string? HeadCanonicalName => null; public override IReadOnlyCollection? HeadTags => null; diff --git a/src/NerdBank.GitVersioning/GitContext.cs b/src/NerdBank.GitVersioning/GitContext.cs index e7d30c1a..4db82865 100644 --- a/src/NerdBank.GitVersioning/GitContext.cs +++ b/src/NerdBank.GitVersioning/GitContext.cs @@ -113,6 +113,11 @@ public string RepoRelativeProjectDirectory /// public abstract DateTimeOffset? GitCommitDate { get; } + /// + /// Gets the date that the commit identified by was authored. + /// + public abstract DateTimeOffset? GitCommitAuthorDate { get; } + /// /// Gets the canonical name for HEAD's position (e.g. refs/heads/main). /// diff --git a/src/NerdBank.GitVersioning/LibGit2/LibGit2Context.cs b/src/NerdBank.GitVersioning/LibGit2/LibGit2Context.cs index 00294c7d..684c9a2e 100644 --- a/src/NerdBank.GitVersioning/LibGit2/LibGit2Context.cs +++ b/src/NerdBank.GitVersioning/LibGit2/LibGit2Context.cs @@ -51,7 +51,10 @@ internal LibGit2Context(string workingTreeDirectory, string dotGitPath, string? public override bool IsHead => this.Repository.Head?.Tip?.Equals(this.Commit) ?? false; /// - public override DateTimeOffset? GitCommitDate => this.Commit?.Author.When; + public override DateTimeOffset? GitCommitDate => this.Commit?.Committer.When; + + /// + public override DateTimeOffset? GitCommitAuthorDate => this.Commit?.Author.When; /// public override string HeadCanonicalName => this.Repository.Head.CanonicalName; diff --git a/src/NerdBank.GitVersioning/Managed/ManagedGitContext.cs b/src/NerdBank.GitVersioning/Managed/ManagedGitContext.cs index b2197799..2fc987a5 100644 --- a/src/NerdBank.GitVersioning/Managed/ManagedGitContext.cs +++ b/src/NerdBank.GitVersioning/Managed/ManagedGitContext.cs @@ -53,7 +53,10 @@ internal ManagedGitContext(string workingDirectory, string dotGitPath, string? c public override bool IsHead => this.Repository.GetHeadCommit().Equals(this.Commit); /// - public override DateTimeOffset? GitCommitDate => this.Commit is { } commit ? (commit.Author?.Date ?? this.Repository.GetCommit(commit.Sha, readAuthor: true).Author?.Date) : null; + public override DateTimeOffset? GitCommitDate => this.Commit is { } commit ? (commit.Committer?.Date ?? this.Repository.GetCommit(commit.Sha, readAuthor: true).Committer?.Date) : null; + + /// + public override DateTimeOffset? GitCommitAuthorDate => this.Commit is { } commit ? (commit.Author?.Date ?? this.Repository.GetCommit(commit.Sha, readAuthor: true).Author?.Date) : null; /// public override string HeadCanonicalName => this.Repository.GetHeadAsReferenceOrSha().ToString() ?? throw new InvalidOperationException("Unable to determine the HEAD position."); diff --git a/src/NerdBank.GitVersioning/ManagedGit/GitCommit.cs b/src/NerdBank.GitVersioning/ManagedGit/GitCommit.cs index 2e01be1d..35df3947 100644 --- a/src/NerdBank.GitVersioning/ManagedGit/GitCommit.cs +++ b/src/NerdBank.GitVersioning/ManagedGit/GitCommit.cs @@ -48,6 +48,11 @@ public struct GitCommit : IEquatable /// public GitSignature? Author { get; set; } + /// + /// Gets or sets the committer of this commit. + /// + public GitSignature? Committer { get; set; } + public static bool operator ==(GitCommit left, GitCommit right) { return Equals(left, right); diff --git a/src/NerdBank.GitVersioning/ManagedGit/GitCommitReader.cs b/src/NerdBank.GitVersioning/ManagedGit/GitCommitReader.cs index a59ced3b..6d9b4cdf 100644 --- a/src/NerdBank.GitVersioning/ManagedGit/GitCommitReader.cs +++ b/src/NerdBank.GitVersioning/ManagedGit/GitCommitReader.cs @@ -19,6 +19,7 @@ public static class GitCommitReader private static readonly byte[] TreeStart = GitRepository.Encoding.GetBytes("tree "); private static readonly byte[] ParentStart = GitRepository.Encoding.GetBytes("parent "); private static readonly byte[] AuthorStart = GitRepository.Encoding.GetBytes("author "); + private static readonly byte[] CommitterStart = GitRepository.Encoding.GetBytes("committer "); /// /// Reads a object from a . @@ -30,7 +31,7 @@ public static class GitCommitReader /// The of the commit. /// /// - /// A value indicating whether to populate the field. + /// A value indicating whether to populate the and fields. /// /// /// The . @@ -67,7 +68,7 @@ public static GitCommit Read(Stream stream, GitObjectId sha, bool readAuthor = f /// The of the commit. /// /// - /// A value indicating whether to populate the field. + /// A value indicating whether to populate the and fields. /// /// /// The . @@ -102,11 +103,22 @@ public static GitCommit Read(ReadOnlySpan commit, GitObjectId sha, bool re buffer = buffer.Slice(ParentLineLength); } - GitSignature signature = default; + GitSignature author = default; + GitSignature committer = default; - if (readAuthor && !TryReadAuthor(buffer, out signature)) + if (readAuthor) { - throw new GitException(); + if (!TryReadAuthor(buffer, out author, out int lineLength)) + { + throw new GitException(); + } + + buffer = buffer.Slice(lineLength); + + if (!TryReadCommitter(buffer, out committer)) + { + throw new GitException(); + } } return new GitCommit() @@ -116,7 +128,8 @@ public static GitCommit Read(ReadOnlySpan commit, GitObjectId sha, bool re SecondParent = secondParent, AdditionalParents = additionalParents, Tree = tree, - Author = readAuthor ? signature : null, + Author = readAuthor ? author : null, + Committer = readAuthor ? committer : null, }; } @@ -153,16 +166,27 @@ private static bool TryReadParent(ReadOnlySpan line, out GitObjectId paren return true; } - private static bool TryReadAuthor(ReadOnlySpan line, out GitSignature signature) + private static bool TryReadAuthor(ReadOnlySpan line, out GitSignature signature, out int lineLength) + { + return TryReadSignature(line, AuthorStart, out signature, out lineLength); + } + + private static bool TryReadCommitter(ReadOnlySpan line, out GitSignature signature) + { + return TryReadSignature(line, CommitterStart, out signature, out _); + } + + private static bool TryReadSignature(ReadOnlySpan line, byte[] signatureStart, out GitSignature signature, out int lineLength) { signature = default; + lineLength = default; - if (!line.Slice(0, AuthorStart.Length).SequenceEqual(AuthorStart)) + if (!line.Slice(0, signatureStart.Length).SequenceEqual(signatureStart)) { return false; } - line = line.Slice(AuthorStart.Length); + line = line.Slice(signatureStart.Length); int emailStart = line.IndexOf((byte)'<'); int emailEnd = line.IndexOf((byte)'>'); @@ -179,6 +203,7 @@ private static bool TryReadAuthor(ReadOnlySpan line, out GitSignature sign long ticks = long.Parse(GitRepository.GetString(time.Slice(0, offsetStart))); signature.Date = DateTimeOffset.FromUnixTimeSeconds(ticks); + lineLength = signatureStart.Length + lineEnd + 1; return true; } } diff --git a/src/NerdBank.GitVersioning/ManagedGit/GitRepository.cs b/src/NerdBank.GitVersioning/ManagedGit/GitRepository.cs index cb1ae626..7d37f715 100644 --- a/src/NerdBank.GitVersioning/ManagedGit/GitRepository.cs +++ b/src/NerdBank.GitVersioning/ManagedGit/GitRepository.cs @@ -296,7 +296,7 @@ public GitObjectId GetHeadCommitSha() /// Gets the current HEAD commit, if available. /// /// - /// A value indicating whether to populate the field. + /// A value indicating whether to populate the and fields. /// /// /// The current HEAD commit, or if not available. @@ -320,7 +320,7 @@ public GitObjectId GetHeadCommitSha() /// The Git object Id of the commit. /// /// - /// A value indicating whether to populate the field. + /// A value indicating whether to populate the and fields. /// /// /// The requested commit. diff --git a/src/NerdBank.GitVersioning/NoGit/NoGitContext.cs b/src/NerdBank.GitVersioning/NoGit/NoGitContext.cs index ab169d89..24e86179 100644 --- a/src/NerdBank.GitVersioning/NoGit/NoGitContext.cs +++ b/src/NerdBank.GitVersioning/NoGit/NoGitContext.cs @@ -30,6 +30,8 @@ public NoGitContext(string workingTreePath) /// public override DateTimeOffset? GitCommitDate => null; + public override DateTimeOffset? GitCommitAuthorDate => null; + /// public override string? HeadCanonicalName => null; diff --git a/src/NerdBank.GitVersioning/VersionOracle.cs b/src/NerdBank.GitVersioning/VersionOracle.cs index d9994492..89157ee3 100644 --- a/src/NerdBank.GitVersioning/VersionOracle.cs +++ b/src/NerdBank.GitVersioning/VersionOracle.cs @@ -265,6 +265,11 @@ public IEnumerable BuildMetadataWithCommitId /// public DateTimeOffset? GitCommitDate => this.context.GitCommitDate; + /// + /// Gets the Git revision control commit author date for HEAD (the current source code version). + /// + public DateTimeOffset? GitCommitAuthorDate => this.context.GitCommitAuthorDate; + /// /// Gets or sets the number of commits in the longest single path between /// the specified commit and the most distant ancestor (inclusive) diff --git a/src/Nerdbank.GitVersioning.Tasks/AssemblyVersionInfo.cs b/src/Nerdbank.GitVersioning.Tasks/AssemblyVersionInfo.cs index cf68c5b0..28bb3320 100644 --- a/src/Nerdbank.GitVersioning.Tasks/AssemblyVersionInfo.cs +++ b/src/Nerdbank.GitVersioning.Tasks/AssemblyVersionInfo.cs @@ -91,6 +91,8 @@ the code is regenerated. public string GitCommitDateTicks { get; set; } + public string GitCommitAuthorDateTicks { get; set; } + public bool EmitThisAssemblyClass { get; set; } = true; /// @@ -440,6 +442,11 @@ private void GenerateAssemblyAttributes() fields.Add("GitCommitDate", (new DateTime(gitCommitDateTicks, DateTimeKind.Utc), true)); } + if (long.TryParse(this.GitCommitAuthorDateTicks, out long gitCommitAuthorDateTicks)) + { + fields.Add("GitCommitAuthorDate", (new DateTime(gitCommitAuthorDateTicks, DateTimeKind.Utc), true)); + } + if (this.AdditionalThisAssemblyFields is object) { foreach (ITaskItem item in this.AdditionalThisAssemblyFields) diff --git a/src/Nerdbank.GitVersioning.Tasks/GetBuildVersion.cs b/src/Nerdbank.GitVersioning.Tasks/GetBuildVersion.cs index f38aeb25..2a5113b6 100644 --- a/src/Nerdbank.GitVersioning.Tasks/GetBuildVersion.cs +++ b/src/Nerdbank.GitVersioning.Tasks/GetBuildVersion.cs @@ -165,6 +165,13 @@ public GetBuildVersion() [Output] public string GitCommitDateTicks { get; private set; } + /// + /// Gets the Git revision control commit author date for HEAD (the current source code version), expressed as the number of 100-nanosecond + /// intervals that have elapsed since January 1, 0001 at 00:00:00.000 in the Gregorian calendar. + /// + [Output] + public string GitCommitAuthorDateTicks { get; private set; } + /// /// Gets the number of commits in the longest single path between /// the specified commit and the most distant ancestor (inclusive) @@ -266,6 +273,7 @@ protected override bool ExecuteInner() this.GitCommitId = oracle.GitCommitId; this.GitCommitIdShort = oracle.GitCommitIdShort; this.GitCommitDateTicks = oracle.GitCommitDate is not null ? oracle.GitCommitDate.Value.UtcTicks.ToString(CultureInfo.InvariantCulture) : null; + this.GitCommitAuthorDateTicks = oracle.GitCommitAuthorDate is not null ? oracle.GitCommitAuthorDate.Value.UtcTicks.ToString(CultureInfo.InvariantCulture) : null; this.GitVersionHeight = oracle.VersionHeight; this.BuildMetadataFragment = oracle.BuildMetadataFragment; this.CloudBuildNumber = oracle.CloudBuildNumberEnabled ? oracle.CloudBuildNumber : null; @@ -314,6 +322,7 @@ protected override bool ExecuteInner() { "GitCommitId", this.GitCommitId }, { "GitCommitIdShort", this.GitCommitIdShort }, { "GitCommitDateTicks", this.GitCommitDateTicks }, + { "GitCommitAuthorDateTicks", this.GitCommitAuthorDateTicks }, { "GitVersionHeight", this.GitVersionHeight.ToString(CultureInfo.InvariantCulture) }, { "BuildNumber", this.BuildNumber.ToString(CultureInfo.InvariantCulture) }, { "BuildVersionNumberComponent", this.BuildNumber.ToString(CultureInfo.InvariantCulture) }, diff --git a/src/Nerdbank.GitVersioning.Tasks/build/Nerdbank.GitVersioning.targets b/src/Nerdbank.GitVersioning.Tasks/build/Nerdbank.GitVersioning.targets index 64ea798e..4cba9f6f 100644 --- a/src/Nerdbank.GitVersioning.Tasks/build/Nerdbank.GitVersioning.targets +++ b/src/Nerdbank.GitVersioning.Tasks/build/Nerdbank.GitVersioning.targets @@ -169,6 +169,7 @@ PrereleaseVersion="$(PrereleaseVersion)" GitCommitId="$(GitCommitId)" GitCommitDateTicks="$(GitCommitDateTicks)" + GitCommitAuthorDateTicks="$(GitCommitAuthorDateTicks)" EmitNonVersionCustomAttributes="$(NBGV_EmitNonVersionCustomAttributes)" EmitThisAssemblyClass="$(NBGV_EmitThisAssemblyClass)" AdditionalThisAssemblyFields="@(AdditionalThisAssemblyFields)" diff --git a/test/Nerdbank.GitVersioning.Tests/BuildIntegrationTests.cs b/test/Nerdbank.GitVersioning.Tests/BuildIntegrationTests.cs index 74d66985..de6b10b0 100644 --- a/test/Nerdbank.GitVersioning.Tests/BuildIntegrationTests.cs +++ b/test/Nerdbank.GitVersioning.Tests/BuildIntegrationTests.cs @@ -304,7 +304,8 @@ protected void AssertStandardProperties(VersionOptions versionOptions, BuildResu Assert.Equal(idAsVersion.Build.ToString(), buildResult.BuildVersionNumberComponent); Assert.Equal($"{idAsVersion.Major}.{idAsVersion.Minor}.{idAsVersion.Build}", buildResult.BuildVersionSimple); Assert.Equal(this.LibGit2Repository.Head.Tip.Id.Sha, buildResult.GitCommitId); - Assert.Equal(this.LibGit2Repository.Head.Tip.Author.When.UtcTicks.ToString(CultureInfo.InvariantCulture), buildResult.GitCommitDateTicks); + Assert.Equal(this.LibGit2Repository.Head.Tip.Committer.When.UtcTicks.ToString(CultureInfo.InvariantCulture), buildResult.GitCommitDateTicks); + Assert.Equal(this.LibGit2Repository.Head.Tip.Author.When.UtcTicks.ToString(CultureInfo.InvariantCulture), buildResult.GitCommitAuthorDateTicks); Assert.Equal(commitIdShort, buildResult.GitCommitIdShort); Assert.Equal(versionHeight.ToString(), buildResult.GitVersionHeight); Assert.Equal($"{version.Major}.{version.Minor}", buildResult.MajorMinorVersion); @@ -558,6 +559,8 @@ internal BuildResults(BuildResult buildResult, IReadOnlyList log public string GitCommitDateTicks => this.BuildResult.ProjectStateAfterBuild.GetPropertyValue("GitCommitDateTicks"); + public string GitCommitAuthorDateTicks => this.BuildResult.ProjectStateAfterBuild.GetPropertyValue("GitCommitAuthorDateTicks"); + public string GitVersionHeight => this.BuildResult.ProjectStateAfterBuild.GetPropertyValue("GitVersionHeight"); public string SemVerBuildSuffix => this.BuildResult.ProjectStateAfterBuild.GetPropertyValue("SemVerBuildSuffix"); diff --git a/test/Nerdbank.GitVersioning.Tests/ManagedGit/GitCommitReaderTests.cs b/test/Nerdbank.GitVersioning.Tests/ManagedGit/GitCommitReaderTests.cs index c4c96d60..cf3624be 100644 --- a/test/Nerdbank.GitVersioning.Tests/ManagedGit/GitCommitReaderTests.cs +++ b/test/Nerdbank.GitVersioning.Tests/ManagedGit/GitCommitReaderTests.cs @@ -32,7 +32,11 @@ public void ReadTest() Assert.Equal(new DateTimeOffset(2020, 10, 6, 13, 40, 09, TimeSpan.FromHours(-6)), author.Date); Assert.Equal("andrewarnott@gmail.com", author.Email); - // Committer and commit message are not read + GitSignature committer = commit.Committer.Value; + + Assert.Equal("Andrew Arnott", committer.Name); + Assert.Equal(new DateTimeOffset(2020, 10, 6, 14, 40, 09, TimeSpan.FromHours(-6)), committer.Date); + Assert.Equal("andrewarnott@gmail.com", committer.Email); } } diff --git a/test/Nerdbank.GitVersioning.Tests/ManagedGit/commit-4497b0eaaa89abf0e6d70961ad5f04fd3a49cbc6 b/test/Nerdbank.GitVersioning.Tests/ManagedGit/commit-4497b0eaaa89abf0e6d70961ad5f04fd3a49cbc6 index 903016e3..547d4aad 100644 --- a/test/Nerdbank.GitVersioning.Tests/ManagedGit/commit-4497b0eaaa89abf0e6d70961ad5f04fd3a49cbc6 +++ b/test/Nerdbank.GitVersioning.Tests/ManagedGit/commit-4497b0eaaa89abf0e6d70961ad5f04fd3a49cbc6 @@ -1,6 +1,6 @@ tree f914b48023c7c804a4f3be780d451f31aef74ac1 parent 06cc627f28736c0d13506b0414126580fe37c6f3 author Andrew Arnott 1602013209 -0600 -committer Andrew Arnott 1602013209 -0600 +committer Andrew Arnott 1602016809 -0600 Set version to '3.4-alpha' diff --git a/test/Nerdbank.GitVersioning.Tests/ManagedGit/commit-d56dc3ed179053abef2097d1120b4507769bcf1a b/test/Nerdbank.GitVersioning.Tests/ManagedGit/commit-d56dc3ed179053abef2097d1120b4507769bcf1a index 7163d498..e6bcbd19 100644 --- a/test/Nerdbank.GitVersioning.Tests/ManagedGit/commit-d56dc3ed179053abef2097d1120b4507769bcf1a +++ b/test/Nerdbank.GitVersioning.Tests/ManagedGit/commit-d56dc3ed179053abef2097d1120b4507769bcf1a @@ -2,6 +2,6 @@ tree f914b48023c7c804a4f3be780d451f31aef74ac1 parent 4497b0eaaa89abf0e6d70961ad5f04fd3a49cbc6 parent 0989e8fe0cd0e0900173b26decdfb24bc0cc8232 author Andrew Arnott 1602013209 -0600 -committer Andrew Arnott 1602013209 -0600 +committer Andrew Arnott 1602016809 -0600 Merge branch 'v3.3'