From ce9a3dad2faab6ccf4d3499a1a4e1f8daa8a98d9 Mon Sep 17 00:00:00 2001 From: leo Date: Wed, 5 Jun 2024 11:46:31 +0800 Subject: [PATCH] enhance: improve commit and stash parsing time --- src/Commands/QueryCommits.cs | 174 ++++++++++++------------------ src/Commands/QuerySingleCommit.cs | 98 +++++------------ src/Commands/QueryStashes.cs | 56 ++++------ src/Models/Commit.cs | 11 -- src/Models/Stash.cs | 1 - src/Models/User.cs | 7 +- 6 files changed, 117 insertions(+), 230 deletions(-) diff --git a/src/Commands/QueryCommits.cs b/src/Commands/QueryCommits.cs index 6788884c..0bfb132c 100644 --- a/src/Commands/QueryCommits.cs +++ b/src/Commands/QueryCommits.cs @@ -5,130 +5,82 @@ namespace SourceGit.Commands { public class QueryCommits : Command { - private const string GPGSIG_START = "gpgsig -----BEGIN "; - private const string GPGSIG_END = " -----END "; - - private readonly List commits = new List(); - private Models.Commit current = null; - private bool isSkipingGpgsig = false; - private bool isHeadFounded = false; - private readonly bool findFirstMerged = true; - public QueryCommits(string repo, string limits, bool needFindHead = true) { + _endOfBodyToken = $"----- END OF BODY {Guid.NewGuid()} -----"; + WorkingDirectory = repo; Context = repo; - Args = "log --date-order --decorate=full --pretty=raw " + limits; - findFirstMerged = needFindHead; + Args = $"log --date-order --no-show-signature --decorate=full --pretty=format:\"%H%n%P%n%D%n%aN±%aE%n%at%n%cN±%cE%n%ct%n%B%n{_endOfBodyToken}\" " + limits; + _findFirstMerged = needFindHead; } public List Result() { Exec(); - if (current != null) - { - current.Message = current.Message.Trim(); - commits.Add(current); - } - - if (findFirstMerged && !isHeadFounded && commits.Count > 0) - { + if (_findFirstMerged && !_isHeadFounded && _commits.Count > 0) MarkFirstMerged(); - } - return commits; + return _commits; } protected override void OnReadline(string line) { - if (isSkipingGpgsig) + switch (_nextPartIdx) { - if (line.StartsWith(GPGSIG_END, StringComparison.Ordinal)) - isSkipingGpgsig = false; - return; - } - else if (line.StartsWith(GPGSIG_START, StringComparison.Ordinal)) - { - isSkipingGpgsig = true; - return; + case 0: + _current = new Models.Commit() { SHA = line }; + _commits.Add(_current); + break; + case 1: + if (!string.IsNullOrEmpty(line)) + _current.Parents.AddRange(line.Split(' ', StringSplitOptions.RemoveEmptyEntries)); + break; + case 2: + if (!string.IsNullOrEmpty(line)) + ParseDecorators(line); + break; + case 3: + _current.Author = Models.User.FindOrAdd(line); + break; + case 4: + _current.AuthorTime = ulong.Parse(line); + break; + case 5: + _current.Committer = Models.User.FindOrAdd(line); + break; + case 6: + _current.CommitterTime = ulong.Parse(line); + break; + default: + if (line.Equals(_endOfBodyToken, StringComparison.Ordinal)) + { + _nextPartIdx = 0; + if (!string.IsNullOrEmpty(_current.Message)) _current.Message = _current.Message.Trim(); + } + else + { + if (string.IsNullOrEmpty(_current.Subject)) + _current.Subject = line; + else + _current.Message += (line + "\n"); + } + return; } - if (line.StartsWith("commit ", StringComparison.Ordinal)) - { - if (current != null) - { - current.Message = current.Message.Trim(); - commits.Add(current); - } - - current = new Models.Commit(); - line = line.Substring(7); - - var decoratorStart = line.IndexOf('(', StringComparison.Ordinal); - if (decoratorStart < 0) - { - current.SHA = line.Trim(); - } - else - { - current.SHA = line.Substring(0, decoratorStart).Trim(); - current.IsMerged = ParseDecorators(current.Decorators, line.Substring(decoratorStart + 1)); - if (!isHeadFounded) - isHeadFounded = current.IsMerged; - } - - return; - } - - if (current == null) - return; - - if (line.StartsWith("tree ", StringComparison.Ordinal)) - { - return; - } - else if (line.StartsWith("parent ", StringComparison.Ordinal)) - { - current.Parents.Add(line.Substring("parent ".Length)); - } - else if (line.StartsWith("author ", StringComparison.Ordinal)) - { - Models.User user = Models.User.Invalid; - ulong time = 0; - Models.Commit.ParseUserAndTime(line.Substring(7), ref user, ref time); - current.Author = user; - current.AuthorTime = time; - } - else if (line.StartsWith("committer ", StringComparison.Ordinal)) - { - Models.User user = Models.User.Invalid; - ulong time = 0; - Models.Commit.ParseUserAndTime(line.Substring(10), ref user, ref time); - current.Committer = user; - current.CommitterTime = time; - } - else if (string.IsNullOrEmpty(current.Subject)) - { - current.Subject = line.Trim(); - } - else - { - current.Message += (line.Trim() + "\n"); - } + _nextPartIdx++; } - private bool ParseDecorators(List decorators, string data) + private void ParseDecorators(string data) { - bool isHeadOfCurrent = false; - - var subs = data.Split(new char[] { ',', ')', '(' }, StringSplitOptions.RemoveEmptyEntries); + var subs = data.Split(',', StringSplitOptions.RemoveEmptyEntries); foreach (var sub in subs) { var d = sub.Trim(); if (d.StartsWith("tag: refs/tags/", StringComparison.Ordinal)) { - decorators.Add(new Models.Decorator() + _current.Decorators.Add(new Models.Decorator() { Type = Models.DecoratorType.Tag, Name = d.Substring(15).Trim(), @@ -140,8 +92,8 @@ namespace SourceGit.Commands } else if (d.StartsWith("HEAD -> refs/heads/", StringComparison.Ordinal)) { - isHeadOfCurrent = true; - decorators.Add(new Models.Decorator() + _current.IsMerged = true; + _current.Decorators.Add(new Models.Decorator() { Type = Models.DecoratorType.CurrentBranchHead, Name = d.Substring(19).Trim(), @@ -149,8 +101,8 @@ namespace SourceGit.Commands } else if (d.Equals("HEAD")) { - isHeadOfCurrent = true; - decorators.Add(new Models.Decorator() + _current.IsMerged = true; + _current.Decorators.Add(new Models.Decorator() { Type = Models.DecoratorType.CurrentCommitHead, Name = d.Trim(), @@ -158,7 +110,7 @@ namespace SourceGit.Commands } else if (d.StartsWith("refs/heads/", StringComparison.Ordinal)) { - decorators.Add(new Models.Decorator() + _current.Decorators.Add(new Models.Decorator() { Type = Models.DecoratorType.LocalBranchHead, Name = d.Substring(11).Trim(), @@ -166,7 +118,7 @@ namespace SourceGit.Commands } else if (d.StartsWith("refs/remotes/", StringComparison.Ordinal)) { - decorators.Add(new Models.Decorator() + _current.Decorators.Add(new Models.Decorator() { Type = Models.DecoratorType.RemoteBranchHead, Name = d.Substring(13).Trim(), @@ -174,7 +126,7 @@ namespace SourceGit.Commands } } - decorators.Sort((l, r) => + _current.Decorators.Sort((l, r) => { if (l.Type != r.Type) { @@ -186,12 +138,13 @@ namespace SourceGit.Commands } }); - return isHeadOfCurrent; + if (_current.IsMerged && !_isHeadFounded) + _isHeadFounded = true; } private void MarkFirstMerged() { - Args = $"log --since=\"{commits[commits.Count - 1].CommitterTimeStr}\" --format=\"%H\""; + Args = $"log --since=\"{_commits[_commits.Count - 1].CommitterTimeStr}\" --format=\"%H\""; var rs = ReadToEnd(); var shas = rs.StdOut.Split(new char[] { '\n' }, StringSplitOptions.RemoveEmptyEntries); @@ -202,7 +155,7 @@ namespace SourceGit.Commands foreach (var sha in shas) set.Add(sha); - foreach (var c in commits) + foreach (var c in _commits) { if (set.Contains(c.SHA)) { @@ -211,5 +164,12 @@ namespace SourceGit.Commands } } } + + private string _endOfBodyToken = string.Empty; + private List _commits = new List(); + private Models.Commit _current = null; + private bool _isHeadFounded = false; + private readonly bool _findFirstMerged = true; + private int _nextPartIdx = 0; } } diff --git a/src/Commands/QuerySingleCommit.cs b/src/Commands/QuerySingleCommit.cs index 0213061d..3ea7fd08 100644 --- a/src/Commands/QuerySingleCommit.cs +++ b/src/Commands/QuerySingleCommit.cs @@ -1,100 +1,57 @@ using System; using System.Collections.Generic; +using System.Text; namespace SourceGit.Commands { public class QuerySingleCommit : Command { - private const string GPGSIG_START = "gpgsig -----BEGIN "; - private const string GPGSIG_END = " -----END "; - public QuerySingleCommit(string repo, string sha) { WorkingDirectory = repo; Context = repo; - Args = $"show --pretty=raw --decorate=full -s {sha}"; + Args = $"show --no-show-signature --decorate=full --pretty=format:%H%n%P%n%D%n%aN±%aE%n%at%n%cN±%cE%n%ct%n%B -s {sha}"; } public Models.Commit Result() { - var succ = Exec(); - if (!succ) - return null; - - _commit.Message.Trim(); - return _commit; - } - - protected override void OnReadline(string line) - { - if (isSkipingGpgsig) + var rs = ReadToEnd(); + if (rs.IsSuccess && !string.IsNullOrEmpty(rs.StdOut)) { - if (line.StartsWith(GPGSIG_END, StringComparison.Ordinal)) - isSkipingGpgsig = false; - return; - } - else if (line.StartsWith(GPGSIG_START, StringComparison.Ordinal)) - { - isSkipingGpgsig = true; - return; - } + var commit = new Models.Commit(); + var lines = rs.StdOut.Split('\n'); + if (lines.Length < 8) + return null; - if (line.StartsWith("commit ", StringComparison.Ordinal)) - { - line = line.Substring(7); + commit.SHA = lines[0]; + if (!string.IsNullOrEmpty(lines[1])) + commit.Parents.AddRange(lines[1].Split(' ', StringSplitOptions.RemoveEmptyEntries)); + if (!string.IsNullOrEmpty(lines[2])) + commit.IsMerged = ParseDecorators(commit.Decorators, lines[2]); + commit.Author = Models.User.FindOrAdd(lines[3]); + commit.AuthorTime = ulong.Parse(lines[4]); + commit.Committer = Models.User.FindOrAdd(lines[5]); + commit.CommitterTime = ulong.Parse(lines[6]); + commit.Subject = lines[7]; - var decoratorStart = line.IndexOf('(', StringComparison.Ordinal); - if (decoratorStart < 0) + if (lines.Length > 8) { - _commit.SHA = line.Trim(); - } - else - { - _commit.SHA = line.Substring(0, decoratorStart).Trim(); - ParseDecorators(_commit.Decorators, line.Substring(decoratorStart + 1)); + StringBuilder builder = new StringBuilder(); + for (int i = 8; i < lines.Length; i++) + builder.Append(lines[i]); + commit.Message = builder.ToString(); } - return; + return commit; } - if (line.StartsWith("tree ", StringComparison.Ordinal)) - { - return; - } - else if (line.StartsWith("parent ", StringComparison.Ordinal)) - { - _commit.Parents.Add(line.Substring("parent ".Length)); - } - else if (line.StartsWith("author ", StringComparison.Ordinal)) - { - Models.User user = Models.User.Invalid; - ulong time = 0; - Models.Commit.ParseUserAndTime(line.Substring(7), ref user, ref time); - _commit.Author = user; - _commit.AuthorTime = time; - } - else if (line.StartsWith("committer ", StringComparison.Ordinal)) - { - Models.User user = Models.User.Invalid; - ulong time = 0; - Models.Commit.ParseUserAndTime(line.Substring(10), ref user, ref time); - _commit.Committer = user; - _commit.CommitterTime = time; - } - else if (string.IsNullOrEmpty(_commit.Subject)) - { - _commit.Subject = line.Trim(); - } - else - { - _commit.Message += (line.Trim() + "\n"); - } + return null; } private bool ParseDecorators(List decorators, string data) { bool isHeadOfCurrent = false; - var subs = data.Split(new char[] { ',', ')', '(' }, StringSplitOptions.RemoveEmptyEntries); + var subs = data.Split(',', StringSplitOptions.RemoveEmptyEntries); foreach (var sub in subs) { var d = sub.Trim(); @@ -160,8 +117,5 @@ namespace SourceGit.Commands return isHeadOfCurrent; } - - private Models.Commit _commit = new Models.Commit(); - private bool isSkipingGpgsig = false; } } diff --git a/src/Commands/QueryStashes.cs b/src/Commands/QueryStashes.cs index 5362f87b..6d089f8e 100644 --- a/src/Commands/QueryStashes.cs +++ b/src/Commands/QueryStashes.cs @@ -1,64 +1,48 @@ -using System; -using System.Collections.Generic; -using System.Text.RegularExpressions; +using System.Collections.Generic; namespace SourceGit.Commands { - public partial class QueryStashes : Command + public class QueryStashes : Command { - - [GeneratedRegex(@"^Reflog: refs/(stash@\{\d+\}).*$")] - private static partial Regex REG_STASH(); - public QueryStashes(string repo) { WorkingDirectory = repo; Context = repo; - Args = "stash list --pretty=raw"; + Args = "stash list --pretty=format:%H%n%ct%n%gd%n%s"; } public List Result() { Exec(); - if (_current != null) - _stashes.Add(_current); return _stashes; } protected override void OnReadline(string line) { - if (line.StartsWith("commit ", StringComparison.Ordinal)) + switch (_nextLineIdx) { - if (_current != null && !string.IsNullOrEmpty(_current.Name)) + case 0: + _current = new Models.Stash() { SHA = line }; _stashes.Add(_current); - _current = new Models.Stash() { SHA = line.Substring(7, 8) }; - return; + break; + case 1: + _current.Time = ulong.Parse(line); + break; + case 2: + _current.Name = line; + break; + case 3: + _current.Message = line; + break; } - if (_current == null) - return; - - if (line.StartsWith("Reflog: refs/stash@", StringComparison.Ordinal)) - { - var match = REG_STASH().Match(line); - if (match.Success) - _current.Name = match.Groups[1].Value; - } - else if (line.StartsWith("Reflog message: ", StringComparison.Ordinal)) - { - _current.Message = line.Substring(16); - } - else if (line.StartsWith("author ", StringComparison.Ordinal)) - { - Models.User user = Models.User.Invalid; - ulong time = 0; - Models.Commit.ParseUserAndTime(line.Substring(7), ref user, ref time); - _current.Author = user; - _current.Time = time; - } + _nextLineIdx++; + if (_nextLineIdx > 3) + _nextLineIdx = 0; } private readonly List _stashes = new List(); private Models.Stash _current = null; + private int _nextLineIdx = 0; } } diff --git a/src/Models/Commit.cs b/src/Models/Commit.cs index d8355e81..4a7313f0 100644 --- a/src/Models/Commit.cs +++ b/src/Models/Commit.cs @@ -40,17 +40,6 @@ namespace SourceGit.Models get => string.IsNullOrWhiteSpace(Message) ? Subject : $"{Subject}\n\n{Message}"; } - public static void ParseUserAndTime(string data, ref User user, ref ulong time) - { - var userEndIdx = data.IndexOf('>', StringComparison.Ordinal); - if (userEndIdx < 0) - return; - - var timeEndIdx = data.IndexOf(' ', userEndIdx + 2); - user = User.FindOrAdd(data.Substring(0, userEndIdx)); - time = timeEndIdx < 0 ? 0 : ulong.Parse(data.Substring(userEndIdx + 2, timeEndIdx - userEndIdx - 2)); - } - private static readonly DateTime _utcStart = new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc).ToLocalTime(); } } diff --git a/src/Models/Stash.cs b/src/Models/Stash.cs index 2376959a..2fab0f2f 100644 --- a/src/Models/Stash.cs +++ b/src/Models/Stash.cs @@ -8,7 +8,6 @@ namespace SourceGit.Models public string Name { get; set; } = ""; public string SHA { get; set; } = ""; - public User Author { get; set; } = User.Invalid; public ulong Time { get; set; } = 0; public string Message { get; set; } = ""; diff --git a/src/Models/User.cs b/src/Models/User.cs index 5a93e135..cb0d21cd 100644 --- a/src/Models/User.cs +++ b/src/Models/User.cs @@ -1,4 +1,5 @@ -using System.Collections.Concurrent; +using System; +using System.Collections.Concurrent; namespace SourceGit.Models { @@ -27,8 +28,8 @@ namespace SourceGit.Models { return _caches.GetOrAdd(data, key => { - var nameEndIdx = key.IndexOf('<', System.StringComparison.Ordinal); - var name = nameEndIdx >= 2 ? key.Substring(0, nameEndIdx - 1) : string.Empty; + var nameEndIdx = key.IndexOf('±', StringComparison.Ordinal); + var name = nameEndIdx > 0 ? key.Substring(0, nameEndIdx) : string.Empty; var email = key.Substring(nameEndIdx + 1); return new User() { Name = name, Email = email };