enhance: only store subject in commits.

It has several advantages:

* reduce the memory costed by histories
* higher performance while parsing commits
* no need to calculate subject every time, which is invoked most frequently to render histories
This commit is contained in:
leo 2024-06-08 12:19:48 +08:00
parent 6426da3289
commit 9e45a8a77d
12 changed files with 67 additions and 65 deletions

View file

@ -0,0 +1,19 @@
namespace SourceGit.Commands
{
public class QueryCommitFullMessage : Command
{
public QueryCommitFullMessage(string repo, string sha)
{
WorkingDirectory = repo;
Context = repo;
Args = $"show --no-show-signature --pretty=format:%B -s {sha}";
}
public string Result()
{
var rs = ReadToEnd();
if (rs.IsSuccess) return rs.StdOut.TrimEnd();
return string.Empty;
}
}
}

View file

@ -7,11 +7,9 @@ namespace SourceGit.Commands
{ {
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 --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; 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%s " + limits;
_findFirstMerged = needFindHead; _findFirstMerged = needFindHead;
} }
@ -24,7 +22,6 @@ namespace SourceGit.Commands
var nextPartIdx = 0; var nextPartIdx = 0;
var start = 0; var start = 0;
var end = rs.StdOut.IndexOf('\n', start); var end = rs.StdOut.IndexOf('\n', start);
var max = rs.StdOut.Length;
while (end > 0) while (end > 0)
{ {
var line = rs.StdOut.Substring(start, end - start); var line = rs.StdOut.Substring(start, end - start);
@ -51,24 +48,17 @@ namespace SourceGit.Commands
break; break;
case 6: case 6:
_current.CommitterTime = ulong.Parse(line); _current.CommitterTime = ulong.Parse(line);
start = end + 1; break;
end = rs.StdOut.IndexOf(_endOfBodyToken, start, StringComparison.Ordinal); case 7:
if (end > 0) _current.Subject = line;
{ break;
if (end > start)
_current.Body = rs.StdOut.Substring(start, end - start).TrimEnd();
start = end + _endOfBodyToken.Length + 1;
end = start >= max ? -1 : rs.StdOut.IndexOf('\n', start);
}
nextPartIdx = 0;
continue;
default: default:
break; break;
} }
nextPartIdx++; nextPartIdx++;
if (nextPartIdx == 8) nextPartIdx = 0;
start = end + 1; start = end + 1;
end = rs.StdOut.IndexOf('\n', start); end = rs.StdOut.IndexOf('\n', start);
} }
@ -157,7 +147,7 @@ namespace SourceGit.Commands
if (l.Type != r.Type) if (l.Type != r.Type)
return (int)l.Type - (int)r.Type; return (int)l.Type - (int)r.Type;
else else
return l.Name.CompareTo(r.Name); return string.Compare(l.Name, r.Name, StringComparison.Ordinal);
}); });
if (_current.IsMerged && !_isHeadFounded) if (_current.IsMerged && !_isHeadFounded)
@ -187,7 +177,6 @@ namespace SourceGit.Commands
} }
} }
private string _endOfBodyToken = string.Empty;
private List<Models.Commit> _commits = new List<Models.Commit>(); private List<Models.Commit> _commits = new List<Models.Commit>();
private Models.Commit _current = null; private Models.Commit _current = null;
private bool _findFirstMerged = false; private bool _findFirstMerged = false;

View file

@ -10,7 +10,7 @@ namespace SourceGit.Commands
{ {
WorkingDirectory = repo; WorkingDirectory = repo;
Context = repo; Context = repo;
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}"; 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%s -s {sha}";
} }
public Models.Commit Result() public Models.Commit Result()
@ -32,11 +32,7 @@ namespace SourceGit.Commands
commit.AuthorTime = ulong.Parse(lines[4]); commit.AuthorTime = ulong.Parse(lines[4]);
commit.Committer = Models.User.FindOrAdd(lines[5]); commit.Committer = Models.User.FindOrAdd(lines[5]);
commit.CommitterTime = ulong.Parse(lines[6]); commit.CommitterTime = ulong.Parse(lines[6]);
commit.Subject = lines[7];
StringBuilder builder = new StringBuilder();
for (int i = 7; i < lines.Length; i++)
builder.AppendLine(lines[i]);
commit.Body = builder.ToString().TrimEnd();
return commit; return commit;
} }
@ -105,7 +101,7 @@ namespace SourceGit.Commands
if (l.Type != r.Type) if (l.Type != r.Type)
return (int)l.Type - (int)r.Type; return (int)l.Type - (int)r.Type;
else else
return l.Name.CompareTo(r.Name); return string.Compare(l.Name, r.Name, StringComparison.Ordinal);
}); });
return isHeadOfCurrent; return isHeadOfCurrent;

View file

@ -12,31 +12,13 @@ namespace SourceGit.Models
public ulong AuthorTime { get; set; } = 0; public ulong AuthorTime { get; set; } = 0;
public User Committer { get; set; } = User.Invalid; public User Committer { get; set; } = User.Invalid;
public ulong CommitterTime { get; set; } = 0; public ulong CommitterTime { get; set; } = 0;
public string Body { get; set; } = string.Empty; public string Subject { get; set; } = string.Empty;
public List<string> Parents { get; set; } = new List<string>(); public List<string> Parents { get; set; } = new List<string>();
public List<Decorator> Decorators { get; set; } = new List<Decorator>(); public List<Decorator> Decorators { get; set; } = new List<Decorator>();
public bool HasDecorators => Decorators.Count > 0; public bool HasDecorators => Decorators.Count > 0;
public bool IsMerged { get; set; } = false; public bool IsMerged { get; set; } = false;
public Thickness Margin { get; set; } = new Thickness(0); public Thickness Margin { get; set; } = new Thickness(0);
public string Subject
{
get
{
var end = Body.IndexOf("\r\n\r\n", StringComparison.Ordinal);
if (end == -1)
{
end = Body.IndexOf("\n\n", StringComparison.Ordinal);
if (end > 0)
return Body.Substring(0, end).Replace("\n", "", StringComparison.Ordinal);
return Body.Replace("\n", " ", StringComparison.Ordinal);
}
return Body.Substring(0, end).Replace("\r\n", " ", StringComparison.Ordinal);
}
}
public string AuthorTimeStr => _utcStart.AddSeconds(AuthorTime).ToString("yyyy/MM/dd HH:mm:ss"); public string AuthorTimeStr => _utcStart.AddSeconds(AuthorTime).ToString("yyyy/MM/dd HH:mm:ss");
public string CommitterTimeStr => _utcStart.AddSeconds(CommitterTime).ToString("yyyy/MM/dd HH:mm:ss"); public string CommitterTimeStr => _utcStart.AddSeconds(CommitterTime).ToString("yyyy/MM/dd HH:mm:ss");
public string AuthorTimeShortStr => _utcStart.AddSeconds(AuthorTime).ToString("yyyy/MM/dd"); public string AuthorTimeShortStr => _utcStart.AddSeconds(AuthorTime).ToString("yyyy/MM/dd");

View file

@ -36,6 +36,12 @@ namespace SourceGit.ViewModels
} }
} }
public string FullMessage
{
get => _fullMessage;
private set => SetProperty(ref _fullMessage, value);
}
public List<Models.Change> Changes public List<Models.Change> Changes
{ {
get => _changes; get => _changes;
@ -376,6 +382,7 @@ namespace SourceGit.ViewModels
private void Refresh() private void Refresh()
{ {
_changes = null; _changes = null;
FullMessage = string.Empty;
VisibleChanges = null; VisibleChanges = null;
SelectedChanges = null; SelectedChanges = null;
@ -389,6 +396,7 @@ namespace SourceGit.ViewModels
Task.Run(() => Task.Run(() =>
{ {
var fullMessage = new Commands.QueryCommitFullMessage(_repo, _commit.SHA).Result();
var parent = _commit.Parents.Count == 0 ? "4b825dc642cb6eb9a060e54bf8d69288fbee4904" : _commit.Parents[0]; var parent = _commit.Parents.Count == 0 ? "4b825dc642cb6eb9a060e54bf8d69288fbee4904" : _commit.Parents[0];
var cmdChanges = new Commands.CompareRevisions(_repo, parent, _commit.SHA) { Cancel = _cancelToken }; var cmdChanges = new Commands.CompareRevisions(_repo, parent, _commit.SHA) { Cancel = _cancelToken };
var changes = cmdChanges.Result(); var changes = cmdChanges.Result();
@ -407,6 +415,7 @@ namespace SourceGit.ViewModels
{ {
Dispatcher.UIThread.Post(() => Dispatcher.UIThread.Post(() =>
{ {
FullMessage = fullMessage;
Changes = changes; Changes = changes;
VisibleChanges = visible; VisibleChanges = visible;
}); });
@ -444,6 +453,7 @@ namespace SourceGit.ViewModels
private string _repo = string.Empty; private string _repo = string.Empty;
private int _activePageIndex = 0; private int _activePageIndex = 0;
private Models.Commit _commit = null; private Models.Commit _commit = null;
private string _fullMessage = string.Empty;
private List<Models.Change> _changes = null; private List<Models.Change> _changes = null;
private List<Models.Change> _visibleChanges = null; private List<Models.Change> _visibleChanges = null;
private List<Models.Change> _selectedChanges = null; private List<Models.Change> _selectedChanges = null;

View file

@ -429,7 +429,7 @@ namespace SourceGit.ViewModels
foreach (var c in _histories.Commits) foreach (var c in _histories.Commits)
{ {
if (c.SHA.Contains(_searchCommitFilter, StringComparison.OrdinalIgnoreCase) if (c.SHA.Contains(_searchCommitFilter, StringComparison.OrdinalIgnoreCase)
|| c.Body.Contains(_searchCommitFilter, StringComparison.OrdinalIgnoreCase) || c.Subject.Contains(_searchCommitFilter, StringComparison.OrdinalIgnoreCase)
|| c.Author.Name.Contains(_searchCommitFilter, StringComparison.OrdinalIgnoreCase) || c.Author.Name.Contains(_searchCommitFilter, StringComparison.OrdinalIgnoreCase)
|| c.Committer.Name.Contains(_searchCommitFilter, StringComparison.OrdinalIgnoreCase) || c.Committer.Name.Contains(_searchCommitFilter, StringComparison.OrdinalIgnoreCase)
|| c.Author.Email.Contains(_searchCommitFilter, StringComparison.OrdinalIgnoreCase) || c.Author.Email.Contains(_searchCommitFilter, StringComparison.OrdinalIgnoreCase)

View file

@ -1,4 +1,5 @@
using System.ComponentModel.DataAnnotations; using System;
using System.ComponentModel.DataAnnotations;
using System.Threading.Tasks; using System.Threading.Tasks;
namespace SourceGit.ViewModels namespace SourceGit.ViewModels
@ -21,14 +22,16 @@ namespace SourceGit.ViewModels
public Reword(Repository repo, Models.Commit head) public Reword(Repository repo, Models.Commit head)
{ {
_repo = repo; _repo = repo;
_oldMessage = new Commands.QueryCommitFullMessage(_repo.FullPath, head.SHA).Result();
_message = _oldMessage;
Head = head; Head = head;
Message = head.Body;
View = new Views.Reword() { DataContext = this }; View = new Views.Reword() { DataContext = this };
} }
public override Task<bool> Sure() public override Task<bool> Sure()
{ {
if (_message == Head.Body) if (string.Compare(_message, _oldMessage, StringComparison.Ordinal) == 0)
return null; return null;
_repo.SetWatcherEnabled(false); _repo.SetWatcherEnabled(false);
@ -44,5 +47,6 @@ namespace SourceGit.ViewModels
private readonly Repository _repo = null; private readonly Repository _repo = null;
private string _message = string.Empty; private string _message = string.Empty;
private string _oldMessage = string.Empty;
} }
} }

View file

@ -27,7 +27,8 @@ namespace SourceGit.ViewModels
public Squash(Repository repo, Models.Commit head, Models.Commit parent) public Squash(Repository repo, Models.Commit head, Models.Commit parent)
{ {
_repo = repo; _repo = repo;
_message = parent.Body; _message = new Commands.QueryCommitFullMessage(_repo.FullPath, parent.SHA).Result();
Head = head; Head = head;
Parent = parent; Parent = parent;
View = new Views.Squash() { DataContext = this }; View = new Views.Squash() { DataContext = this };

View file

@ -93,16 +93,7 @@ namespace SourceGit.ViewModels
return; return;
} }
var head = new Commands.QuerySingleCommit(_repo.FullPath, currentBranch.Head).Result(); CommitMessage = new Commands.QueryCommitFullMessage(_repo.FullPath, currentBranch.Head).Result();
if (head == null)
{
App.RaiseException(_repo.FullPath, "No commits to amend!!!");
_useAmend = false;
OnPropertyChanged();
return;
}
CommitMessage = head.Body;
} }
OnPropertyChanged(nameof(IsCommitWithPushVisible)); OnPropertyChanged(nameof(IsCommitWithPushVisible));

View file

@ -7,14 +7,15 @@
xmlns:v="using:SourceGit.Views" xmlns:v="using:SourceGit.Views"
xmlns:c="using:SourceGit.Converters" xmlns:c="using:SourceGit.Converters"
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450" mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
x:Class="SourceGit.Views.CommitBaseInfo"> x:Class="SourceGit.Views.CommitBaseInfo"
x:Name="ThisControl">
<UserControl.DataTemplates> <UserControl.DataTemplates>
<DataTemplate DataType="m:Commit"> <DataTemplate DataType="m:Commit">
<StackPanel Orientation="Vertical"> <StackPanel Orientation="Vertical">
<!-- Author & Committer --> <!-- Author & Committer -->
<UniformGrid Rows="1" Margin="0,8"> <UniformGrid Rows="1" Margin="0,8">
<!-- Author --> <!-- Author -->
<Grid Grid.Column="0" ColumnDefinitions="96,*"> <Grid ColumnDefinitions="96,*">
<v:Avatar Grid.Column="0" Width="64" Height="64" HorizontalAlignment="Right" User="{Binding Author}"/> <v:Avatar Grid.Column="0" Width="64" Height="64" HorizontalAlignment="Right" User="{Binding Author}"/>
<StackPanel Grid.Column="1" Margin="16,0,8,0" Orientation="Vertical"> <StackPanel Grid.Column="1" Margin="16,0,8,0" Orientation="Vertical">
<TextBlock Classes="group_header_label" Margin="0" Text="{DynamicResource Text.CommitDetail.Info.Author}"/> <TextBlock Classes="group_header_label" Margin="0" Text="{DynamicResource Text.CommitDetail.Info.Author}"/>
@ -30,7 +31,7 @@
</Grid> </Grid>
<!-- Committer --> <!-- Committer -->
<Grid Grid.Column="1" ColumnDefinitions="96,*" IsVisible="{Binding IsCommitterVisible}"> <Grid ColumnDefinitions="96,*" IsVisible="{Binding IsCommitterVisible}">
<v:Avatar Grid.Column="0" Width="64" Height="64" HorizontalAlignment="Right" User="{Binding Committer}"/> <v:Avatar Grid.Column="0" Width="64" Height="64" HorizontalAlignment="Right" User="{Binding Committer}"/>
<StackPanel Grid.Column="1" Margin="16,0,8,0" Orientation="Vertical"> <StackPanel Grid.Column="1" Margin="16,0,8,0" Orientation="Vertical">
<TextBlock Classes="group_header_label" Margin="0" Text="{DynamicResource Text.CommitDetail.Info.Committer}"/> <TextBlock Classes="group_header_label" Margin="0" Text="{DynamicResource Text.CommitDetail.Info.Committer}"/>
@ -104,7 +105,7 @@
<!-- Messages --> <!-- Messages -->
<TextBlock Grid.Row="3" Grid.Column="0" Classes="info_label" Text="{DynamicResource Text.CommitDetail.Info.Message}" VerticalAlignment="Top" Margin="0,4,0,0" /> <TextBlock Grid.Row="3" Grid.Column="0" Classes="info_label" Text="{DynamicResource Text.CommitDetail.Info.Message}" VerticalAlignment="Top" Margin="0,4,0,0" />
<ScrollViewer Grid.Row="3" Grid.Column="1" Margin="12,5,8,0" MaxHeight="64" HorizontalScrollBarVisibility="Disabled" VerticalScrollBarVisibility="Auto"> <ScrollViewer Grid.Row="3" Grid.Column="1" Margin="12,5,8,0" MaxHeight="64" HorizontalScrollBarVisibility="Disabled" VerticalScrollBarVisibility="Auto">
<SelectableTextBlock Text="{Binding Body}" FontFamily="{Binding Source={x:Static vm:Preference.Instance}, Path=MonospaceFont}" TextWrapping="Wrap"/> <SelectableTextBlock Text="{Binding #ThisControl.Message}" FontFamily="{Binding Source={x:Static vm:Preference.Instance}, Path=MonospaceFont}" TextWrapping="Wrap"/>
</ScrollViewer> </ScrollViewer>
</Grid> </Grid>
</StackPanel> </StackPanel>

View file

@ -14,6 +14,15 @@ namespace SourceGit.Views
get => GetValue(CanNavigateProperty); get => GetValue(CanNavigateProperty);
set => SetValue(CanNavigateProperty, value); set => SetValue(CanNavigateProperty, value);
} }
public static readonly StyledProperty<string> MessageProperty =
AvaloniaProperty.Register<CommitBaseInfo, string>(nameof(Message), string.Empty);
public string Message
{
get => GetValue(MessageProperty);
set => SetValue(MessageProperty, value);
}
public CommitBaseInfo() public CommitBaseInfo()
{ {

View file

@ -19,7 +19,7 @@
<Grid RowDefinitions="Auto,1,*"> <Grid RowDefinitions="Auto,1,*">
<!-- Base Information --> <!-- Base Information -->
<v:CommitBaseInfo Grid.Row="0" Content="{Binding Commit}"/> <v:CommitBaseInfo Grid.Row="0" Content="{Binding Commit}" Message="{Binding FullMessage}"/>
<!-- Line --> <!-- Line -->
<Rectangle Grid.Row="1" Height=".65" Margin="8" Fill="{DynamicResource Brush.Border2}" VerticalAlignment="Center"/> <Rectangle Grid.Row="1" Height=".65" Margin="8" Fill="{DynamicResource Brush.Border2}" VerticalAlignment="Center"/>