mirror of
https://github.com/sourcegit-scm/sourcegit.git
synced 2024-12-24 20:57:19 -08:00
enhance: improve commit and stash parsing time
This commit is contained in:
parent
57540b960a
commit
ce9a3dad2f
6 changed files with 117 additions and 230 deletions
|
@ -5,130 +5,82 @@ namespace SourceGit.Commands
|
||||||
{
|
{
|
||||||
public class QueryCommits : Command
|
public class QueryCommits : Command
|
||||||
{
|
{
|
||||||
private const string GPGSIG_START = "gpgsig -----BEGIN ";
|
|
||||||
private const string GPGSIG_END = " -----END ";
|
|
||||||
|
|
||||||
private readonly List<Models.Commit> commits = new List<Models.Commit>();
|
|
||||||
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)
|
public QueryCommits(string repo, string limits, bool needFindHead = true)
|
||||||
{
|
{
|
||||||
|
_endOfBodyToken = $"----- END OF BODY {Guid.NewGuid()} -----";
|
||||||
|
|
||||||
WorkingDirectory = repo;
|
WorkingDirectory = repo;
|
||||||
Context = repo;
|
Context = repo;
|
||||||
Args = "log --date-order --decorate=full --pretty=raw " + limits;
|
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;
|
_findFirstMerged = needFindHead;
|
||||||
}
|
}
|
||||||
|
|
||||||
public List<Models.Commit> Result()
|
public List<Models.Commit> Result()
|
||||||
{
|
{
|
||||||
Exec();
|
Exec();
|
||||||
|
|
||||||
if (current != null)
|
if (_findFirstMerged && !_isHeadFounded && _commits.Count > 0)
|
||||||
{
|
|
||||||
current.Message = current.Message.Trim();
|
|
||||||
commits.Add(current);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (findFirstMerged && !isHeadFounded && commits.Count > 0)
|
|
||||||
{
|
|
||||||
MarkFirstMerged();
|
MarkFirstMerged();
|
||||||
}
|
|
||||||
|
|
||||||
return commits;
|
return _commits;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override void OnReadline(string line)
|
protected override void OnReadline(string line)
|
||||||
{
|
{
|
||||||
if (isSkipingGpgsig)
|
switch (_nextPartIdx)
|
||||||
{
|
{
|
||||||
if (line.StartsWith(GPGSIG_END, StringComparison.Ordinal))
|
case 0:
|
||||||
isSkipingGpgsig = false;
|
_current = new Models.Commit() { SHA = line };
|
||||||
return;
|
_commits.Add(_current);
|
||||||
}
|
break;
|
||||||
else if (line.StartsWith(GPGSIG_START, StringComparison.Ordinal))
|
case 1:
|
||||||
{
|
if (!string.IsNullOrEmpty(line))
|
||||||
isSkipingGpgsig = true;
|
_current.Parents.AddRange(line.Split(' ', StringSplitOptions.RemoveEmptyEntries));
|
||||||
return;
|
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))
|
_nextPartIdx++;
|
||||||
{
|
|
||||||
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");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private bool ParseDecorators(List<Models.Decorator> decorators, string data)
|
private void ParseDecorators(string data)
|
||||||
{
|
{
|
||||||
bool isHeadOfCurrent = false;
|
var subs = data.Split(',', StringSplitOptions.RemoveEmptyEntries);
|
||||||
|
|
||||||
var subs = data.Split(new char[] { ',', ')', '(' }, StringSplitOptions.RemoveEmptyEntries);
|
|
||||||
foreach (var sub in subs)
|
foreach (var sub in subs)
|
||||||
{
|
{
|
||||||
var d = sub.Trim();
|
var d = sub.Trim();
|
||||||
if (d.StartsWith("tag: refs/tags/", StringComparison.Ordinal))
|
if (d.StartsWith("tag: refs/tags/", StringComparison.Ordinal))
|
||||||
{
|
{
|
||||||
decorators.Add(new Models.Decorator()
|
_current.Decorators.Add(new Models.Decorator()
|
||||||
{
|
{
|
||||||
Type = Models.DecoratorType.Tag,
|
Type = Models.DecoratorType.Tag,
|
||||||
Name = d.Substring(15).Trim(),
|
Name = d.Substring(15).Trim(),
|
||||||
|
@ -140,8 +92,8 @@ namespace SourceGit.Commands
|
||||||
}
|
}
|
||||||
else if (d.StartsWith("HEAD -> refs/heads/", StringComparison.Ordinal))
|
else if (d.StartsWith("HEAD -> refs/heads/", StringComparison.Ordinal))
|
||||||
{
|
{
|
||||||
isHeadOfCurrent = true;
|
_current.IsMerged = true;
|
||||||
decorators.Add(new Models.Decorator()
|
_current.Decorators.Add(new Models.Decorator()
|
||||||
{
|
{
|
||||||
Type = Models.DecoratorType.CurrentBranchHead,
|
Type = Models.DecoratorType.CurrentBranchHead,
|
||||||
Name = d.Substring(19).Trim(),
|
Name = d.Substring(19).Trim(),
|
||||||
|
@ -149,8 +101,8 @@ namespace SourceGit.Commands
|
||||||
}
|
}
|
||||||
else if (d.Equals("HEAD"))
|
else if (d.Equals("HEAD"))
|
||||||
{
|
{
|
||||||
isHeadOfCurrent = true;
|
_current.IsMerged = true;
|
||||||
decorators.Add(new Models.Decorator()
|
_current.Decorators.Add(new Models.Decorator()
|
||||||
{
|
{
|
||||||
Type = Models.DecoratorType.CurrentCommitHead,
|
Type = Models.DecoratorType.CurrentCommitHead,
|
||||||
Name = d.Trim(),
|
Name = d.Trim(),
|
||||||
|
@ -158,7 +110,7 @@ namespace SourceGit.Commands
|
||||||
}
|
}
|
||||||
else if (d.StartsWith("refs/heads/", StringComparison.Ordinal))
|
else if (d.StartsWith("refs/heads/", StringComparison.Ordinal))
|
||||||
{
|
{
|
||||||
decorators.Add(new Models.Decorator()
|
_current.Decorators.Add(new Models.Decorator()
|
||||||
{
|
{
|
||||||
Type = Models.DecoratorType.LocalBranchHead,
|
Type = Models.DecoratorType.LocalBranchHead,
|
||||||
Name = d.Substring(11).Trim(),
|
Name = d.Substring(11).Trim(),
|
||||||
|
@ -166,7 +118,7 @@ namespace SourceGit.Commands
|
||||||
}
|
}
|
||||||
else if (d.StartsWith("refs/remotes/", StringComparison.Ordinal))
|
else if (d.StartsWith("refs/remotes/", StringComparison.Ordinal))
|
||||||
{
|
{
|
||||||
decorators.Add(new Models.Decorator()
|
_current.Decorators.Add(new Models.Decorator()
|
||||||
{
|
{
|
||||||
Type = Models.DecoratorType.RemoteBranchHead,
|
Type = Models.DecoratorType.RemoteBranchHead,
|
||||||
Name = d.Substring(13).Trim(),
|
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)
|
if (l.Type != r.Type)
|
||||||
{
|
{
|
||||||
|
@ -186,12 +138,13 @@ namespace SourceGit.Commands
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
return isHeadOfCurrent;
|
if (_current.IsMerged && !_isHeadFounded)
|
||||||
|
_isHeadFounded = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void MarkFirstMerged()
|
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 rs = ReadToEnd();
|
||||||
var shas = rs.StdOut.Split(new char[] { '\n' }, StringSplitOptions.RemoveEmptyEntries);
|
var shas = rs.StdOut.Split(new char[] { '\n' }, StringSplitOptions.RemoveEmptyEntries);
|
||||||
|
@ -202,7 +155,7 @@ namespace SourceGit.Commands
|
||||||
foreach (var sha in shas)
|
foreach (var sha in shas)
|
||||||
set.Add(sha);
|
set.Add(sha);
|
||||||
|
|
||||||
foreach (var c in commits)
|
foreach (var c in _commits)
|
||||||
{
|
{
|
||||||
if (set.Contains(c.SHA))
|
if (set.Contains(c.SHA))
|
||||||
{
|
{
|
||||||
|
@ -211,5 +164,12 @@ namespace SourceGit.Commands
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private string _endOfBodyToken = string.Empty;
|
||||||
|
private List<Models.Commit> _commits = new List<Models.Commit>();
|
||||||
|
private Models.Commit _current = null;
|
||||||
|
private bool _isHeadFounded = false;
|
||||||
|
private readonly bool _findFirstMerged = true;
|
||||||
|
private int _nextPartIdx = 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,100 +1,57 @@
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
using System.Text;
|
||||||
|
|
||||||
namespace SourceGit.Commands
|
namespace SourceGit.Commands
|
||||||
{
|
{
|
||||||
public class QuerySingleCommit : Command
|
public class QuerySingleCommit : Command
|
||||||
{
|
{
|
||||||
private const string GPGSIG_START = "gpgsig -----BEGIN ";
|
|
||||||
private const string GPGSIG_END = " -----END ";
|
|
||||||
|
|
||||||
public QuerySingleCommit(string repo, string sha) {
|
public QuerySingleCommit(string repo, string sha) {
|
||||||
WorkingDirectory = repo;
|
WorkingDirectory = repo;
|
||||||
Context = 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()
|
public Models.Commit Result()
|
||||||
{
|
{
|
||||||
var succ = Exec();
|
var rs = ReadToEnd();
|
||||||
if (!succ)
|
if (rs.IsSuccess && !string.IsNullOrEmpty(rs.StdOut))
|
||||||
return null;
|
|
||||||
|
|
||||||
_commit.Message.Trim();
|
|
||||||
return _commit;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected override void OnReadline(string line)
|
|
||||||
{
|
|
||||||
if (isSkipingGpgsig)
|
|
||||||
{
|
{
|
||||||
if (line.StartsWith(GPGSIG_END, StringComparison.Ordinal))
|
var commit = new Models.Commit();
|
||||||
isSkipingGpgsig = false;
|
var lines = rs.StdOut.Split('\n');
|
||||||
return;
|
if (lines.Length < 8)
|
||||||
}
|
return null;
|
||||||
else if (line.StartsWith(GPGSIG_START, StringComparison.Ordinal))
|
|
||||||
{
|
|
||||||
isSkipingGpgsig = true;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (line.StartsWith("commit ", StringComparison.Ordinal))
|
commit.SHA = lines[0];
|
||||||
{
|
if (!string.IsNullOrEmpty(lines[1]))
|
||||||
line = line.Substring(7);
|
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 (lines.Length > 8)
|
||||||
if (decoratorStart < 0)
|
|
||||||
{
|
{
|
||||||
_commit.SHA = line.Trim();
|
StringBuilder builder = new StringBuilder();
|
||||||
}
|
for (int i = 8; i < lines.Length; i++)
|
||||||
else
|
builder.Append(lines[i]);
|
||||||
{
|
commit.Message = builder.ToString();
|
||||||
_commit.SHA = line.Substring(0, decoratorStart).Trim();
|
|
||||||
ParseDecorators(_commit.Decorators, line.Substring(decoratorStart + 1));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return;
|
return commit;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (line.StartsWith("tree ", StringComparison.Ordinal))
|
return null;
|
||||||
{
|
|
||||||
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");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private bool ParseDecorators(List<Models.Decorator> decorators, string data)
|
private bool ParseDecorators(List<Models.Decorator> decorators, string data)
|
||||||
{
|
{
|
||||||
bool isHeadOfCurrent = false;
|
bool isHeadOfCurrent = false;
|
||||||
|
|
||||||
var subs = data.Split(new char[] { ',', ')', '(' }, StringSplitOptions.RemoveEmptyEntries);
|
var subs = data.Split(',', StringSplitOptions.RemoveEmptyEntries);
|
||||||
foreach (var sub in subs)
|
foreach (var sub in subs)
|
||||||
{
|
{
|
||||||
var d = sub.Trim();
|
var d = sub.Trim();
|
||||||
|
@ -160,8 +117,5 @@ namespace SourceGit.Commands
|
||||||
|
|
||||||
return isHeadOfCurrent;
|
return isHeadOfCurrent;
|
||||||
}
|
}
|
||||||
|
|
||||||
private Models.Commit _commit = new Models.Commit();
|
|
||||||
private bool isSkipingGpgsig = false;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,64 +1,48 @@
|
||||||
using System;
|
using System.Collections.Generic;
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Text.RegularExpressions;
|
|
||||||
|
|
||||||
namespace SourceGit.Commands
|
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)
|
public QueryStashes(string repo)
|
||||||
{
|
{
|
||||||
WorkingDirectory = repo;
|
WorkingDirectory = repo;
|
||||||
Context = repo;
|
Context = repo;
|
||||||
Args = "stash list --pretty=raw";
|
Args = "stash list --pretty=format:%H%n%ct%n%gd%n%s";
|
||||||
}
|
}
|
||||||
|
|
||||||
public List<Models.Stash> Result()
|
public List<Models.Stash> Result()
|
||||||
{
|
{
|
||||||
Exec();
|
Exec();
|
||||||
if (_current != null)
|
|
||||||
_stashes.Add(_current);
|
|
||||||
return _stashes;
|
return _stashes;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override void OnReadline(string line)
|
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);
|
_stashes.Add(_current);
|
||||||
_current = new Models.Stash() { SHA = line.Substring(7, 8) };
|
break;
|
||||||
return;
|
case 1:
|
||||||
|
_current.Time = ulong.Parse(line);
|
||||||
|
break;
|
||||||
|
case 2:
|
||||||
|
_current.Name = line;
|
||||||
|
break;
|
||||||
|
case 3:
|
||||||
|
_current.Message = line;
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (_current == null)
|
_nextLineIdx++;
|
||||||
return;
|
if (_nextLineIdx > 3)
|
||||||
|
_nextLineIdx = 0;
|
||||||
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;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private readonly List<Models.Stash> _stashes = new List<Models.Stash>();
|
private readonly List<Models.Stash> _stashes = new List<Models.Stash>();
|
||||||
private Models.Stash _current = null;
|
private Models.Stash _current = null;
|
||||||
|
private int _nextLineIdx = 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -40,17 +40,6 @@ namespace SourceGit.Models
|
||||||
get => string.IsNullOrWhiteSpace(Message) ? Subject : $"{Subject}\n\n{Message}";
|
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();
|
private static readonly DateTime _utcStart = new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc).ToLocalTime();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,7 +8,6 @@ namespace SourceGit.Models
|
||||||
|
|
||||||
public string Name { get; set; } = "";
|
public string Name { get; set; } = "";
|
||||||
public string SHA { get; set; } = "";
|
public string SHA { get; set; } = "";
|
||||||
public User Author { get; set; } = User.Invalid;
|
|
||||||
public ulong Time { get; set; } = 0;
|
public ulong Time { get; set; } = 0;
|
||||||
public string Message { get; set; } = "";
|
public string Message { get; set; } = "";
|
||||||
|
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
using System.Collections.Concurrent;
|
using System;
|
||||||
|
using System.Collections.Concurrent;
|
||||||
|
|
||||||
namespace SourceGit.Models
|
namespace SourceGit.Models
|
||||||
{
|
{
|
||||||
|
@ -27,8 +28,8 @@ namespace SourceGit.Models
|
||||||
{
|
{
|
||||||
return _caches.GetOrAdd(data, key =>
|
return _caches.GetOrAdd(data, key =>
|
||||||
{
|
{
|
||||||
var nameEndIdx = key.IndexOf('<', System.StringComparison.Ordinal);
|
var nameEndIdx = key.IndexOf('±', StringComparison.Ordinal);
|
||||||
var name = nameEndIdx >= 2 ? key.Substring(0, nameEndIdx - 1) : string.Empty;
|
var name = nameEndIdx > 0 ? key.Substring(0, nameEndIdx) : string.Empty;
|
||||||
var email = key.Substring(nameEndIdx + 1);
|
var email = key.Substring(nameEndIdx + 1);
|
||||||
|
|
||||||
return new User() { Name = name, Email = email };
|
return new User() { Name = name, Email = email };
|
||||||
|
|
Loading…
Reference in a new issue