From 766f24f4b07a5d79f5b311720e62ae7bf94d0843 Mon Sep 17 00:00:00 2001 From: leo Date: Tue, 10 Oct 2023 11:25:57 +0800 Subject: [PATCH] optimize: reduce memory used by commit's author/committer data --- src/Commands/Blame.cs | 4 ++- src/Commands/Branches.cs | 4 +-- src/Commands/Command.cs | 2 +- src/Commands/Commits.cs | 14 +++++++-- src/Commands/Stashes.cs | 6 +++- src/Models/Commit.cs | 28 ++++++++++++++---- src/Models/Stash.cs | 9 +++++- src/Models/User.cs | 36 +++++++++++++++-------- src/Views/FileHistories.xaml | 2 +- src/Views/Statistics.xaml.cs | 3 +- src/Views/Widgets/CommitDetail.xaml.cs | 16 +++++----- src/Views/Widgets/Histories.xaml | 2 +- src/Views/Widgets/Histories.xaml.cs | 2 +- src/Views/Widgets/RevisionCompare.xaml.cs | 4 +-- 14 files changed, 91 insertions(+), 41 deletions(-) diff --git a/src/Commands/Blame.cs b/src/Commands/Blame.cs index 4d364be3..571ff17e 100644 --- a/src/Commands/Blame.cs +++ b/src/Commands/Blame.cs @@ -8,6 +8,8 @@ namespace SourceGit.Commands { /// public class Blame : Command { private static readonly Regex REG_FORMAT = new Regex(@"^\^?([0-9a-f]+)\s+.*\((.*)\s+(\d+)\s+[\-\+]?\d+\s+\d+\) (.*)"); + private static readonly DateTime UTC_START = new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc).ToLocalTime(); + private Data data = new Data(); private bool needUnifyCommitSHA = false; private int minSHALen = 0; @@ -53,7 +55,7 @@ namespace SourceGit.Commands { var author = match.Groups[2].Value; var timestamp = int.Parse(match.Groups[3].Value); var content = match.Groups[4].Value; - var when = new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc).AddSeconds(timestamp).ToLocalTime().ToString("yyyy/MM/dd"); + var when = UTC_START.AddSeconds(timestamp).ToString("yyyy/MM/dd"); var blameLine = new Models.BlameLine() { LineNumber = $"{data.Lines.Count + 1}", diff --git a/src/Commands/Branches.cs b/src/Commands/Branches.cs index bb331615..88e767b3 100644 --- a/src/Commands/Branches.cs +++ b/src/Commands/Branches.cs @@ -61,9 +61,9 @@ namespace SourceGit.Commands { } private string ParseTrackStatus(string data) { - if (string.IsNullOrEmpty(data)) return ""; + if (string.IsNullOrEmpty(data)) return string.Empty; - string track = ""; + string track = string.Empty; var ahead = REG_AHEAD.Match(data); if (ahead.Success) { diff --git a/src/Commands/Command.cs b/src/Commands/Command.cs index f0ff0d72..3c22903b 100644 --- a/src/Commands/Command.cs +++ b/src/Commands/Command.cs @@ -145,7 +145,7 @@ namespace SourceGit.Commands { proc.Start(); } catch (Exception e) { return new ReadToEndResult() { - Output = "", + Output = string.Empty, Error = e.Message, IsSuccess = false, }; diff --git a/src/Commands/Commits.cs b/src/Commands/Commits.cs index 23dfe87e..b1b4c28f 100644 --- a/src/Commands/Commits.cs +++ b/src/Commands/Commits.cs @@ -75,9 +75,17 @@ namespace SourceGit.Commands { } else if (line.StartsWith("parent ", StringComparison.Ordinal)) { current.Parents.Add(line.Substring("parent ".Length)); } else if (line.StartsWith("author ", StringComparison.Ordinal)) { - current.Author.Parse(line); + Models.User user = Models.User.Invalid; + ulong time = 0; + Models.Commit.ParseUserAndTime(line, ref user, ref time); + current.Author = user; + current.AuthorTime = time; } else if (line.StartsWith("committer ", StringComparison.Ordinal)) { - current.Committer.Parse(line); + Models.User user = Models.User.Invalid; + ulong time = 0; + Models.Commit.ParseUserAndTime(line, ref user, ref time); + current.Committer = user; + current.CommitterTime = time; } else if (string.IsNullOrEmpty(current.Subject)) { current.Subject = line.Trim(); } else { @@ -129,7 +137,7 @@ namespace SourceGit.Commands { } private void MarkFirstMerged() { - Args = $"log --since=\"{commits.Last().Committer.Time}\" --format=\"%H\""; + Args = $"log --since=\"{commits.Last().CommitterTimeStr}\" --format=\"%H\""; var rs = ReadToEnd(); var shas = rs.Output.Split(new char[] { '\n' }, StringSplitOptions.RemoveEmptyEntries); diff --git a/src/Commands/Stashes.cs b/src/Commands/Stashes.cs index c40e256c..3e7d9713 100644 --- a/src/Commands/Stashes.cs +++ b/src/Commands/Stashes.cs @@ -37,7 +37,11 @@ namespace SourceGit.Commands { } else if (line.StartsWith("Reflog message: ", StringComparison.Ordinal)) { current.Message = line.Substring(16); } else if (line.StartsWith("author ", StringComparison.Ordinal)) { - current.Author.Parse(line); + Models.User user = Models.User.Invalid; + ulong time = 0; + Models.Commit.ParseUserAndTime(line, ref user, ref time); + current.Author = user; + current.Time = time; } } } diff --git a/src/Models/Commit.cs b/src/Models/Commit.cs index e78f655a..5467f3ec 100644 --- a/src/Models/Commit.cs +++ b/src/Models/Commit.cs @@ -1,4 +1,6 @@ +using System; using System.Collections.Generic; +using System.Text.RegularExpressions; using System.Windows; namespace SourceGit.Models { @@ -6,16 +8,32 @@ namespace SourceGit.Models { /// 提交记录 /// public class Commit { - public string SHA { get; set; } = ""; + private static readonly Regex REG_USER_FORMAT = new Regex(@"\w+ (.*) <(.*)> (\d{10}) [\+\-]\d+"); + private static readonly DateTime UTC_START = new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc).ToLocalTime(); + + public string SHA { get; set; } = string.Empty; public string ShortSHA => SHA.Substring(0, 8); - public User Author { get; set; } = new User(); - public User Committer { get; set; } = new User(); - public string Subject { get; set; } = ""; - public string Message { get; set; } = ""; + public User Author { get; set; } = User.Invalid; + public ulong AuthorTime { get; set; } = 0; + public User Committer { get; set; } = User.Invalid; + public ulong CommitterTime { get; set; } = 0; + public string Subject { get; set; } = string.Empty; + public string Message { get; set; } = string.Empty; public List Parents { get; set; } = new List(); public List Decorators { get; set; } = new List(); public bool HasDecorators => Decorators.Count > 0; public bool IsMerged { get; set; } = false; public Thickness Margin { get; set; } = new Thickness(0); + + public string AuthorTimeStr => UTC_START.AddSeconds(AuthorTime).ToString("yyyy-MM-dd HH:mm:ss"); + public string CommitterTimeStr => UTC_START.AddSeconds(CommitterTime).ToString("yyyy-MM-dd HH:mm:ss"); + + public static void ParseUserAndTime(string data, ref User user, ref ulong time) { + var match = REG_USER_FORMAT.Match(data); + if (!match.Success) return; + + user = User.FindOrAdd(match.Groups[1].Value, match.Groups[2].Value); + time = ulong.Parse(match.Groups[3].Value); + } } } diff --git a/src/Models/Stash.cs b/src/Models/Stash.cs index aece8d3d..f079277f 100644 --- a/src/Models/Stash.cs +++ b/src/Models/Stash.cs @@ -1,11 +1,18 @@ +using System; + namespace SourceGit.Models { /// /// 贮藏 /// public class Stash { + private static readonly DateTime UTC_START = new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc).ToLocalTime(); + public string Name { get; set; } = ""; public string SHA { get; set; } = ""; - public User Author { get; set; } = new User(); + public User Author { get; set; } = User.Invalid; + public ulong Time { get; set; } = 0; public string Message { get; set; } = ""; + + public string TimeStr => UTC_START.AddSeconds(Time).ToString("yyyy-MM-dd HH:mm:ss"); } } diff --git a/src/Models/User.cs b/src/Models/User.cs index 6eb358e0..bc3aeb3d 100644 --- a/src/Models/User.cs +++ b/src/Models/User.cs @@ -1,26 +1,36 @@ -using System; -using System.Text.RegularExpressions; +using System.Collections.Generic; namespace SourceGit.Models { /// /// Git用户 /// public class User { - private static readonly Regex REG_FORMAT = new Regex(@"\w+ (.*) <(.*)> (\d{10}) [\+\-]\d+"); + public static User Invalid = new User(); + public static Dictionary Caches = new Dictionary(); - public string Name { get; set; } = ""; - public string Email { get; set; } = ""; - public string Time { get; set; } = ""; + public string Name { get; set; } = string.Empty; + public string Email { get; set; } = string.Empty; - public void Parse(string data) { - var match = REG_FORMAT.Match(data); - if (!match.Success) return; + public override bool Equals(object obj) { + if (obj == null || !(obj is User)) return false; + + var other = obj as User; + return Name == other.Name && Email == other.Email; + } - var time = new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc).AddSeconds(int.Parse(match.Groups[3].Value)); + public override int GetHashCode() { + return base.GetHashCode(); + } - Name = match.Groups[1].Value; - Email = match.Groups[2].Value; - Time = time.ToLocalTime().ToString("yyyy-MM-dd HH:mm:ss"); + public static User FindOrAdd(string name, string email) { + string key = $"{name}#&#{email}"; + if (Caches.ContainsKey(key)) { + return Caches[key]; + } else { + User user = new User() { Name = name, Email = email }; + Caches.Add(key, user); + return user; + } } } } diff --git a/src/Views/FileHistories.xaml b/src/Views/FileHistories.xaml index 52f40a12..f205ee1e 100644 --- a/src/Views/FileHistories.xaml +++ b/src/Views/FileHistories.xaml @@ -105,7 +105,7 @@ diff --git a/src/Views/Statistics.xaml.cs b/src/Views/Statistics.xaml.cs index ad480931..7df3c86d 100644 --- a/src/Views/Statistics.xaml.cs +++ b/src/Views/Statistics.xaml.cs @@ -58,6 +58,7 @@ namespace SourceGit.Views { var weekStart = today.AddSeconds(-(int)today.DayOfWeek * 3600 * 24 - today.Hour * 3600 - today.Minute * 60 - today.Second); var weekEnd = weekStart.AddDays(7); var month = today.Month; + var utcStart = new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc).ToLocalTime(); var limits = $"--branches --remotes --since=\"{today.ToString("yyyy-01-01 00:00:00")}\""; var commits = new Commands.Commits(repo, limits).Result(); @@ -65,7 +66,7 @@ namespace SourceGit.Views { var totalCommitsMonth = 0; var totalCommitsYear = commits.Count; foreach (var c in commits) { - var commitTime = DateTime.Parse(c.Committer.Time); + var commitTime = utcStart.AddSeconds(c.CommitterTime); if (commitTime.CompareTo(weekStart) >= 0 && commitTime.CompareTo(weekEnd) < 0) { mapsWeek[(int)commitTime.DayOfWeek].Count++; totalCommitsWeek++; diff --git a/src/Views/Widgets/CommitDetail.xaml.cs b/src/Views/Widgets/CommitDetail.xaml.cs index d26af6e8..765b6d13 100644 --- a/src/Views/Widgets/CommitDetail.xaml.cs +++ b/src/Views/Widgets/CommitDetail.xaml.cs @@ -49,20 +49,20 @@ namespace SourceGit.Views.Widgets { avatarAuthor.FallbackLabel = commit.Author.Name; txtAuthorName.Text = commit.Author.Name; txtAuthorEmail.Text = commit.Author.Email; - txtAuthorTime.Text = commit.Author.Time; + txtAuthorTime.Text = commit.AuthorTimeStr; - avatarCommitter.Email = commit.Committer.Email; - avatarCommitter.FallbackLabel = commit.Committer.Name; - txtCommitterName.Text = commit.Committer.Name; - txtCommitterEmail.Text = commit.Committer.Email; - txtCommitterTime.Text = commit.Committer.Time; - - if (commit.Committer.Email == commit.Author.Email && commit.Committer.Time == commit.Author.Time) { + if (commit.Committer.Equals(commit.Author) && commit.CommitterTime == commit.AuthorTime) { avatarCommitter.Visibility = Visibility.Hidden; committerInfoPanel.Visibility = Visibility.Hidden; } else { avatarCommitter.Visibility = Visibility.Visible; committerInfoPanel.Visibility = Visibility.Visible; + + avatarCommitter.Email = commit.Committer.Email; + avatarCommitter.FallbackLabel = commit.Committer.Name; + txtCommitterName.Text = commit.Committer.Name; + txtCommitterEmail.Text = commit.Committer.Email; + txtCommitterTime.Text = commit.CommitterTimeStr; } if (commit.Parents.Count == 0) { diff --git a/src/Views/Widgets/Histories.xaml b/src/Views/Widgets/Histories.xaml index d0924e14..eb1190be 100644 --- a/src/Views/Widgets/Histories.xaml +++ b/src/Views/Widgets/Histories.xaml @@ -147,7 +147,7 @@ - + diff --git a/src/Views/Widgets/Histories.xaml.cs b/src/Views/Widgets/Histories.xaml.cs index c5008090..0eb0b8c7 100644 --- a/src/Views/Widgets/Histories.xaml.cs +++ b/src/Views/Widgets/Histories.xaml.cs @@ -404,7 +404,7 @@ namespace SourceGit.Views.Widgets { copyInfo.Click += (o, e) => { Clipboard.SetDataObject(string.Format( "SHA: {0}\nTITLE: {1}\nAUTHOR: {2} <{3}>\nTIME: {4}", - commit.SHA, commit.Subject, commit.Committer.Name, commit.Committer.Email, commit.Committer.Time), true); + commit.SHA, commit.Subject, commit.Committer.Name, commit.Committer.Email, commit.CommitterTime), true); }; menu.Items.Add(copyInfo); diff --git a/src/Views/Widgets/RevisionCompare.xaml.cs b/src/Views/Widgets/RevisionCompare.xaml.cs index 351f6c35..75ce957b 100644 --- a/src/Views/Widgets/RevisionCompare.xaml.cs +++ b/src/Views/Widgets/RevisionCompare.xaml.cs @@ -17,14 +17,14 @@ namespace SourceGit.Views.Widgets { avatarStart.FallbackLabel = start.Committer.Name; avatarStart.ToolTip = start.Committer.Name; txtStartSHA.Text = start.ShortSHA; - txtStartTime.Text = start.Committer.Time; + txtStartTime.Text = start.CommitterTimeStr; txtStartSubject.Text = start.Subject; avatarEnd.Email = end.Committer.Email; avatarEnd.FallbackLabel = end.Committer.Name; avatarEnd.ToolTip = end.Committer.Name; txtEndSHA.Text = end.ShortSHA; - txtEndTime.Text = end.Committer.Time; + txtEndTime.Text = end.CommitterTimeStr; txtEndSubject.Text = end.Subject; Task.Run(() => {