From 2cf85fb4f14104da04e243c0edc2803aa98aa52c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sebastian=20H=C3=B6nel?= Date: Thu, 21 Nov 2024 10:15:23 +0100 Subject: [PATCH 1/5] Add new extension methods and an equality comparer for Commits --- Util/Extensions/CommitExtensions.cs | 82 +++++++++++++++++++++++++++++ 1 file changed, 82 insertions(+) create mode 100644 Util/Extensions/CommitExtensions.cs diff --git a/Util/Extensions/CommitExtensions.cs b/Util/Extensions/CommitExtensions.cs new file mode 100644 index 0000000..9d245ab --- /dev/null +++ b/Util/Extensions/CommitExtensions.cs @@ -0,0 +1,82 @@ +using LibGit2Sharp; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Util.Extensions +{ + /// + /// A simple equality comparer for commit objects based in their ID + /// and commit message. + /// + public class CommitEqualityComparer : IEqualityComparer + { + /// + /// True if the reference is the same or both Id and Message are equal. + /// + /// + /// + /// + public bool Equals(Commit x, Commit y) + { + if (ReferenceEquals(x, y)) return true; + + return x is Commit && y is Commit && x.Id == y.Id && x.Message == y.Message; + } + + /// + /// Hashcode based on the commit itself, its Id and Message. + /// + /// + /// + public int GetHashCode(Commit obj) + { + return obj.GetHashCode() ^ obj.Id.GetHashCode() ^ obj.Message.GetHashCode(); + } + } + + + /// + /// Static class with extensions for commits. + /// + public static class CommitExtensions + { + /// + /// Traverses the tree of parent commits and returns up to N generations + /// in a . Note that within this tree, some commits + /// can point to the same parent. Therefore, a set is returned. + /// + /// + /// + /// + public static ISet ParentGenerations(this Commit commit, UInt32 numGenerations) + { + var results = new HashSet(comparer: new CommitEqualityComparer()); + if (numGenerations == 0) + { + return results; + } + + // Let's decrease it. Since it's an UInt32, it was > 0 before (no overflow here). + numGenerations--; + + + foreach (var parent in commit.Parents) + { + results.Add(parent); + + if (numGenerations > 0) + { + foreach (var parent_parent in parent.ParentGenerations(numGenerations: numGenerations)) + { + results.AddAll(parent.ParentGenerations(numGenerations: numGenerations)); + } + } + } + + return results; + } + } +} From 9e0cf7f20f0d22e099f590fef07f9e717c33a2bb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sebastian=20H=C3=B6nel?= Date: Thu, 21 Nov 2024 10:15:53 +0100 Subject: [PATCH 2/5] Include new extensions for commits in Utils project --- Util/Extensions/DirectoryInfoExtensions.cs | 3 ++- Util/Util.csproj | 1 + 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/Util/Extensions/DirectoryInfoExtensions.cs b/Util/Extensions/DirectoryInfoExtensions.cs index 4b601e0..6b52686 100644 --- a/Util/Extensions/DirectoryInfoExtensions.cs +++ b/Util/Extensions/DirectoryInfoExtensions.cs @@ -40,7 +40,8 @@ public static Boolean TryDelete(this DirectoryInfo directoryInfo, Boolean recurs { directoryInfo.Delete(recursive); return true; - } catch + } + catch { return false; } diff --git a/Util/Util.csproj b/Util/Util.csproj index fb23c4f..9af9872 100644 --- a/Util/Util.csproj +++ b/Util/Util.csproj @@ -257,6 +257,7 @@ + From 1d3b3e2f7dc9b343fd840676b406561c90c51397 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sebastian=20H=C3=B6nel?= Date: Thu, 21 Nov 2024 10:16:12 +0100 Subject: [PATCH 3/5] Add test for parent generations --- GitToolsTests/SourceExport/Export.cs | 39 ++++++++++++++++++++++++++-- 1 file changed, 37 insertions(+), 2 deletions(-) diff --git a/GitToolsTests/SourceExport/Export.cs b/GitToolsTests/SourceExport/Export.cs index 80a10d3..fb62e45 100644 --- a/GitToolsTests/SourceExport/Export.cs +++ b/GitToolsTests/SourceExport/Export.cs @@ -34,12 +34,13 @@ public class Export public static DirectoryInfo SolutionDirectory => new DirectoryInfo(Path.GetDirectoryName(Assembly.GetExecutingAssembly().Locati‌​on)).Parent.Parent; - public static Tuple GetPair(string sha1, int ctxLines = 3) { + public static Tuple GetPair(string sha1, int ctxLines = 3) + { var repo = SolutionDirectory.FullName.OpenRepository(); var span = new GitCommitSpan(repo, sinceDateTimeOrCommitSha: sha1, untilDatetimeOrCommitSha: sha1); var commit = span.FilteredCommits.Single(); - return Tuple.Create(new ExportCommitPair(repo, commit, commit.Parents.Single(), new LibGit2Sharp.CompareOptions() { ContextLines = ctxLines }), repo, span); + return Tuple.Create(new ExportCommitPair(repo, commit, commit.Parents.First(), new LibGit2Sharp.CompareOptions() { ContextLines = ctxLines }), repo, span); } /// @@ -189,5 +190,39 @@ public void TestExportCommits_8f05() tp.Item2.Dispose(); tp.Item3.Dispose(); } + + [TestMethod] + public void TestGenerations_9b70() + { + var tp = GetPair("9b70"); + var commit = tp.Item1.Child; + + Assert.IsTrue(commit.ParentGenerations(numGenerations: 0).Count() == 0); + + // In its first generation, it has two parents: ef1db10 and 8f05cad + var s = commit.ParentGenerations(numGenerations: 1); + Assert.AreEqual(2, s.Count); + Assert.AreEqual(1, s.Where(c => c.ShaShort() == "ef1db10").Count()); + Assert.AreEqual(1, s.Where(c => c.ShaShort() == "8f05cad").Count()); + + // In its second generation, ef1db has one parent and 8f05 has one parent + s = commit.ParentGenerations(numGenerations: 2); + Assert.AreEqual(4, s.Count); + Assert.AreEqual(1, s.Where(c => c.ShaShort() == "ef1db10").Count()); + Assert.AreEqual(1, s.Where(c => c.ShaShort() == "8f05cad").Count()); + Assert.AreEqual(1, s.Where(c => c.ShaShort() == "7710093").Count()); + Assert.AreEqual(1, s.Where(c => c.ShaShort() == "90ae132").Count()); + + // In its third generation, 7710093 has one parent and 90ae has two parents! + // However, one of 90ae's parents is 8f05cad again, so there should be 6 commits in the set! + s = commit.ParentGenerations(numGenerations: 3); + Assert.AreEqual(6, s.Count); + Assert.AreEqual(1, s.Where(c => c.ShaShort() == "ef1db10").Count()); + Assert.AreEqual(1, s.Where(c => c.ShaShort() == "8f05cad").Count()); + Assert.AreEqual(1, s.Where(c => c.ShaShort() == "7710093").Count()); + Assert.AreEqual(1, s.Where(c => c.ShaShort() == "90ae132").Count()); + Assert.AreEqual(1, s.Where(c => c.ShaShort() == "9992e0a").Count()); + Assert.AreEqual(1, s.Where(c => c.ShaShort() == "4b938a6").Count()); + } } } From 65bf787cc48061b8d2f721cef1fcdb581a29a04e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sebastian=20H=C3=B6nel?= Date: Thu, 21 Nov 2024 10:16:33 +0100 Subject: [PATCH 4/5] Implement option/switch to extract data from parent generations --- GitTools/Program.cs | 20 ++++++++++++++++++-- 1 file changed, 18 insertions(+), 2 deletions(-) diff --git a/GitTools/Program.cs b/GitTools/Program.cs index cb8fa52..b55edb1 100644 --- a/GitTools/Program.cs +++ b/GitTools/Program.cs @@ -20,6 +20,7 @@ using GitTools.Analysis.SimpleAnalyzer; using GitTools.Prompting; using GitTools.SourceExport; +using LibGit2Sharp; using LINQtoCSV; using Microsoft.Extensions.Logging; using MySql.Data.MySqlClient; @@ -41,6 +42,8 @@ using Util.Extensions; using Util.Logging; using CompareOptions = LibGit2Sharp.CompareOptions; +using Configuration = Util.Configuration; +using LogLevel = Microsoft.Extensions.Logging.LogLevel; namespace GitTools @@ -276,7 +279,7 @@ static void Main(string[] args) logger.LogWarning($"Context-Lines has been set to the maximum value of ({Int32.MaxValue})."); } - var commits = span.FilteredCommits.ToList(); + var commits = span.FilteredCommits.ToHashSet(); // For each of the span's commits, we will make pairs of commit and parent. // This means, we will create one pair for each parent. Then, these pairs // are processed according to the policy and returned. @@ -285,6 +288,16 @@ static void Main(string[] args) ContextLines = options.CmdExport_ContextLines.Value }; + // Let's check if parent generations should be exported. + if (options.CmdExport_ParentGens > 0u) + { + // Make a new set containing the original commits and their parents + logger.LogWarning($"Including {options.CmdExport_ParentGens} parent generations. Original number of commits is {commits.Count}."); + commits = new HashSet( + commits.Concat(commits.SelectMany(commit => commit.ParentGenerations(numGenerations: options.CmdExport_ParentGens)))); + logger.LogWarning($"Number of commits increased to {commits.Count} by including parent generations."); + } + var pairs = commits.SelectMany(commit => { var parents = commit.Parents.ToList(); @@ -295,7 +308,7 @@ static void Main(string[] args) return parents.Select(parent => new ExportCommitPair(repo: repo, child: commit, parent: parent, compareOptions: compOptions)); }).ToList(); - logger.LogInformation($"Found {commits.Count} Commits and {pairs.Count} pairs."); + logger.LogInformation($"Found {commits.Count()} Commits and {pairs.Count} pairs."); logger.LogInformation($"Processing all pairs {(options.ExecutionPolicy == ExecutionPolicy.Linear ? "sequentially" : "in parallel")}."); @@ -604,6 +617,9 @@ internal class CommandLineOptions [Option("full-code", Required = false, HelpText = "Optional Boolean. Option for the command --cmd-export-source. If present, overrides the command --context-lines by setting it to " + nameof(Int32) + "." + nameof(Int32.MaxValue) + " if true. If --context-lines was not specified, this option *defaults to true* if --cmd-export-source is either Files or Commits. Exporting full code allows to fully reconstruct the original source code.")] public Boolean? CmdExport_FullCode { get; set; } + + [Option("parent-gens", Required = false, DefaultValue = 0u, HelpText = "Optional. Option for the command --cmd-export-source. If greater than zero, will export data from up to n parent generations, for each commit.")] + public UInt32 CmdExport_ParentGens { get; set; } #endregion [Option('h', "help", Required = false, DefaultValue = false, HelpText = "Print this help-text and exit.")] From 6dcd69cf18aa78c897dcd27c9b297b57abdd6d9f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sebastian=20H=C3=B6nel?= Date: Thu, 21 Nov 2024 10:16:51 +0100 Subject: [PATCH 5/5] Change version v2024.11+u3 --- GitDensity/Properties/AssemblyInfo.cs | 2 +- GitDensityTests/Properties/AssemblyInfo.cs | 2 +- GitHours/Properties/AssemblyInfo.cs | 2 +- GitHoursTests/Properties/AssemblyInfo.cs | 2 +- GitMetrics/Properties/AssemblyInfo.cs | 2 +- GitTools/Properties/AssemblyInfo.cs | 2 +- GitToolsTests/Properties/AssemblyInfo.cs | 2 +- Util/Properties/AssemblyInfo.cs | 2 +- UtilTests/Properties/AssemblyInfo.cs | 2 +- 9 files changed, 9 insertions(+), 9 deletions(-) diff --git a/GitDensity/Properties/AssemblyInfo.cs b/GitDensity/Properties/AssemblyInfo.cs index 14b3d1a..bb902c0 100644 --- a/GitDensity/Properties/AssemblyInfo.cs +++ b/GitDensity/Properties/AssemblyInfo.cs @@ -52,4 +52,4 @@ [assembly: InternalsVisibleTo("GitDensityTests")] -[assembly: AssemblyInformationalVersion("v2024.11+u2")] +[assembly: AssemblyInformationalVersion("v2024.11+u3")] diff --git a/GitDensityTests/Properties/AssemblyInfo.cs b/GitDensityTests/Properties/AssemblyInfo.cs index 9c2e882..aa6249c 100644 --- a/GitDensityTests/Properties/AssemblyInfo.cs +++ b/GitDensityTests/Properties/AssemblyInfo.cs @@ -33,4 +33,4 @@ [assembly: AssemblyVersion("1.0.0.0")] [assembly: AssemblyFileVersion("1.0.0.0")] -[assembly: AssemblyInformationalVersion("v2024.11+u2")] +[assembly: AssemblyInformationalVersion("v2024.11+u3")] diff --git a/GitHours/Properties/AssemblyInfo.cs b/GitHours/Properties/AssemblyInfo.cs index c10095b..06abea9 100644 --- a/GitHours/Properties/AssemblyInfo.cs +++ b/GitHours/Properties/AssemblyInfo.cs @@ -53,4 +53,4 @@ [assembly: InternalsVisibleTo("GitDensity")] [assembly: InternalsVisibleTo("GitHoursTests")] -[assembly: AssemblyInformationalVersion("v2024.11+u2")] +[assembly: AssemblyInformationalVersion("v2024.11+u3")] diff --git a/GitHoursTests/Properties/AssemblyInfo.cs b/GitHoursTests/Properties/AssemblyInfo.cs index fc45085..7719803 100644 --- a/GitHoursTests/Properties/AssemblyInfo.cs +++ b/GitHoursTests/Properties/AssemblyInfo.cs @@ -34,4 +34,4 @@ [assembly: AssemblyVersion("1.0.0.0")] [assembly: AssemblyFileVersion("1.0.0.0")] -[assembly: AssemblyInformationalVersion("v2024.11+u2")] +[assembly: AssemblyInformationalVersion("v2024.11+u3")] diff --git a/GitMetrics/Properties/AssemblyInfo.cs b/GitMetrics/Properties/AssemblyInfo.cs index d160959..245126d 100644 --- a/GitMetrics/Properties/AssemblyInfo.cs +++ b/GitMetrics/Properties/AssemblyInfo.cs @@ -49,4 +49,4 @@ [assembly: AssemblyVersion("1.0.0.0")] [assembly: AssemblyFileVersion("1.0.0.0")] -[assembly: AssemblyInformationalVersion("v2024.11+u2")] +[assembly: AssemblyInformationalVersion("v2024.11+u3")] diff --git a/GitTools/Properties/AssemblyInfo.cs b/GitTools/Properties/AssemblyInfo.cs index 8a31373..7b822ad 100644 --- a/GitTools/Properties/AssemblyInfo.cs +++ b/GitTools/Properties/AssemblyInfo.cs @@ -52,4 +52,4 @@ [assembly: InternalsVisibleTo("GitToolsTests")] -[assembly: AssemblyInformationalVersion("v2024.11+u2")] +[assembly: AssemblyInformationalVersion("v2024.11+u3")] diff --git a/GitToolsTests/Properties/AssemblyInfo.cs b/GitToolsTests/Properties/AssemblyInfo.cs index 75a2e28..0e720cb 100644 --- a/GitToolsTests/Properties/AssemblyInfo.cs +++ b/GitToolsTests/Properties/AssemblyInfo.cs @@ -34,4 +34,4 @@ [assembly: AssemblyVersion("1.0.0.0")] [assembly: AssemblyFileVersion("1.0.0.0")] -[assembly: AssemblyInformationalVersion("v2024.11+u2")] +[assembly: AssemblyInformationalVersion("v2024.11+u3")] diff --git a/Util/Properties/AssemblyInfo.cs b/Util/Properties/AssemblyInfo.cs index eb2fd7b..548f315 100644 --- a/Util/Properties/AssemblyInfo.cs +++ b/Util/Properties/AssemblyInfo.cs @@ -51,4 +51,4 @@ [assembly: AssemblyFileVersion("1.0.0.0")] [assembly: InternalsVisibleTo("UtilTests")] -[assembly: AssemblyInformationalVersion("v2024.11+u2")] +[assembly: AssemblyInformationalVersion("v2024.11+u3")] diff --git a/UtilTests/Properties/AssemblyInfo.cs b/UtilTests/Properties/AssemblyInfo.cs index 462468b..82167d6 100644 --- a/UtilTests/Properties/AssemblyInfo.cs +++ b/UtilTests/Properties/AssemblyInfo.cs @@ -33,4 +33,4 @@ [assembly: AssemblyVersion("1.0.0.0")] [assembly: AssemblyFileVersion("1.0.0.0")] -[assembly: AssemblyInformationalVersion("v2024.11+u2")] +[assembly: AssemblyInformationalVersion("v2024.11+u3")]