Skip to content

Commit 13b12de

Browse files
committed
feature: add total added/removed line counts to changes views
1 parent 18d8d7f commit 13b12de

9 files changed

Lines changed: 280 additions & 21 deletions

File tree

‎src/Commands/QueryDiffLineStats.cs‎

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
using System;
2+
using System.Text;
3+
using System.Threading.Tasks;
4+
5+
namespace SourceGit.Commands
6+
{
7+
public class QueryDiffLineStats : Command
8+
{
9+
public QueryDiffLineStats(string repo, string start, string end, bool ignoreWhitespace, bool ignoreCRAtEOL)
10+
: this(repo, ignoreWhitespace, ignoreCRAtEOL, $"{(string.IsNullOrEmpty(start) ? "-R" : start)} {end}")
11+
{
12+
}
13+
14+
public QueryDiffLineStats(string repo, bool staged, bool ignoreWhitespace, bool ignoreCRAtEOL)
15+
: this(repo, ignoreWhitespace, ignoreCRAtEOL, staged ? "--cached" : string.Empty)
16+
{
17+
}
18+
19+
private QueryDiffLineStats(string repo, bool ignoreWhitespace, bool ignoreCRAtEOL, string suffix)
20+
{
21+
WorkingDirectory = repo;
22+
Context = repo;
23+
RaiseError = false;
24+
25+
var builder = new StringBuilder();
26+
builder.Append("diff --no-color --no-ext-diff --numstat -z ");
27+
if (ignoreCRAtEOL)
28+
builder.Append("--ignore-cr-at-eol ");
29+
if (ignoreWhitespace)
30+
builder.Append("--ignore-space-change --ignore-blank-lines ");
31+
builder.Append(suffix);
32+
Args = builder.ToString().TrimEnd();
33+
}
34+
35+
public async Task<(int added, int deleted)> GetResultAsync()
36+
{
37+
try
38+
{
39+
var rs = await ReadToEndAsync().ConfigureAwait(false);
40+
if (string.IsNullOrWhiteSpace(rs.StdOut))
41+
return (0, 0);
42+
43+
int added = 0, deleted = 0;
44+
foreach (var token in rs.StdOut.Split('\0', StringSplitOptions.RemoveEmptyEntries))
45+
{
46+
var parts = token.Split('\t', 3);
47+
if (parts.Length < 2)
48+
continue;
49+
50+
added += ParseCount(parts[0]);
51+
deleted += ParseCount(parts[1]);
52+
}
53+
54+
return (added, deleted);
55+
}
56+
catch
57+
{
58+
return (0, 0);
59+
}
60+
}
61+
62+
private static int ParseCount(string value) => int.TryParse(value, out var parsed) ? parsed : 0;
63+
}
64+
}

‎src/ViewModels/CommitDetail.cs‎

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -91,6 +91,18 @@ public List<Models.Change> VisibleChanges
9191
set => SetProperty(ref _visibleChanges, value);
9292
}
9393

94+
public int TotalAddedLines
95+
{
96+
get => _totalAddedLines;
97+
private set => SetProperty(ref _totalAddedLines, value);
98+
}
99+
100+
public int TotalDeletedLines
101+
{
102+
get => _totalDeletedLines;
103+
private set => SetProperty(ref _totalDeletedLines, value);
104+
}
105+
94106
public ChangeSelection ChangeSelection
95107
{
96108
get => _changeSelection;
@@ -512,6 +524,16 @@ private void Refresh()
512524
.ReadAsync()
513525
.ConfigureAwait(false);
514526

527+
var pref = Preferences.Instance;
528+
var stats = await new Commands.QueryDiffLineStats(
529+
_repo.FullPath,
530+
_commit.FirstParentToCompare,
531+
_commit.SHA,
532+
pref.IgnoreWhitespaceChangesInDiff,
533+
pref.IgnoreCRAtEOLInDiff)
534+
.GetResultAsync()
535+
.ConfigureAwait(false);
536+
515537
var visible = changes;
516538
if (!string.IsNullOrWhiteSpace(_searchChangeFilter))
517539
{
@@ -529,6 +551,8 @@ private void Refresh()
529551
{
530552
Changes = changes;
531553
VisibleChanges = visible;
554+
TotalAddedLines = stats.added;
555+
TotalDeletedLines = stats.deleted;
532556

533557
if (visible.Count == 0)
534558
ChangeSelection = new(null);
@@ -736,6 +760,8 @@ private async Task SetViewingCommitAsync(Models.Object file)
736760
private List<Models.Change> _changes = [];
737761
private List<Models.Change> _visibleChanges = [];
738762
private ChangeSelection _changeSelection = new(null);
763+
private int _totalAddedLines = 0;
764+
private int _totalDeletedLines = 0;
739765
private string _searchChangeFilter = string.Empty;
740766
private DiffContext _diffContext = null;
741767
private string _viewRevisionFilePath = string.Empty;

‎src/ViewModels/Compare.cs‎

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,18 @@ public int TotalChanges
6262
private set => SetProperty(ref _totalChanges, value);
6363
}
6464

65+
public int TotalAddedLines
66+
{
67+
get => _totalAddedLines;
68+
private set => SetProperty(ref _totalAddedLines, value);
69+
}
70+
71+
public int TotalDeletedLines
72+
{
73+
get => _totalDeletedLines;
74+
private set => SetProperty(ref _totalDeletedLines, value);
75+
}
76+
6577
public List<Models.Change> VisibleChanges
6678
{
6779
get => _visibleChanges;
@@ -317,6 +329,15 @@ private void UpdateChanges()
317329
.ReadAsync()
318330
.ConfigureAwait(false);
319331

332+
var pref = Preferences.Instance;
333+
var stats = await new Commands.QueryDiffLineStats(
334+
_repo.FullPath,
335+
_based,
336+
_to,
337+
pref.IgnoreWhitespaceChangesInDiff,
338+
pref.IgnoreCRAtEOLInDiff)
339+
.GetResultAsync().ConfigureAwait(false);
340+
320341
var visible = _changes;
321342
if (!string.IsNullOrWhiteSpace(_searchFilter))
322343
{
@@ -331,6 +352,8 @@ private void UpdateChanges()
331352
Dispatcher.UIThread.Post(() =>
332353
{
333354
TotalChanges = _changes.Count;
355+
TotalAddedLines = stats.added;
356+
TotalDeletedLines = stats.deleted;
334357
VisibleChanges = visible;
335358
IsLoadingChanges = false;
336359

@@ -396,6 +419,8 @@ private string GetSHA(object obj)
396419
private Models.Commit _baseHead = null;
397420
private Models.Commit _toHead = null;
398421
private int _totalChanges = 0;
422+
private int _totalAddedLines = 0;
423+
private int _totalDeletedLines = 0;
399424
private List<Models.Change> _changes = null;
400425
private List<Models.Change> _visibleChanges = null;
401426
private ChangeSelection _changeSelection = new(null);

‎src/ViewModels/RevisionCompare.cs‎

Lines changed: 28 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,18 @@ public int TotalChanges
5959
private set => SetProperty(ref _totalChanges, value);
6060
}
6161

62+
public int TotalAddedLines
63+
{
64+
get => _totalAddedLines;
65+
private set => SetProperty(ref _totalAddedLines, value);
66+
}
67+
68+
public int TotalDeletedLines
69+
{
70+
get => _totalDeletedLines;
71+
private set => SetProperty(ref _totalDeletedLines, value);
72+
}
73+
6274
public List<Models.Change> VisibleChanges
6375
{
6476
get => _visibleChanges;
@@ -345,10 +357,21 @@ private void Refresh()
345357
{
346358
Task.Run(async () =>
347359
{
348-
_changes = await new Commands.CompareRevisions(_repo.FullPath, GetSHA(_startPoint), GetSHA(_endPoint))
360+
var startSHA = GetSHA(_startPoint);
361+
var endSHA = GetSHA(_endPoint);
362+
_changes = await new Commands.CompareRevisions(_repo.FullPath, startSHA, endSHA)
349363
.ReadAsync()
350364
.ConfigureAwait(false);
351365

366+
var pref = Preferences.Instance;
367+
var stats = await new Commands.QueryDiffLineStats(
368+
_repo.FullPath,
369+
startSHA,
370+
endSHA,
371+
pref.IgnoreWhitespaceChangesInDiff,
372+
pref.IgnoreCRAtEOLInDiff)
373+
.GetResultAsync().ConfigureAwait(false);
374+
352375
var visible = _changes;
353376
if (!string.IsNullOrWhiteSpace(_searchFilter))
354377
{
@@ -363,6 +386,8 @@ private void Refresh()
363386
Dispatcher.UIThread.Post(() =>
364387
{
365388
TotalChanges = _changes.Count;
389+
TotalAddedLines = stats.added;
390+
TotalDeletedLines = stats.deleted;
366391
VisibleChanges = visible;
367392
IsLoading = false;
368393

@@ -389,6 +414,8 @@ private string GetDesc(object obj)
389414
private object _startPoint = null;
390415
private object _endPoint = null;
391416
private int _totalChanges = 0;
417+
private int _totalAddedLines = 0;
418+
private int _totalDeletedLines = 0;
392419
private List<Models.Change> _changes = null;
393420
private List<Models.Change> _visibleChanges = null;
394421
private ChangeSelection _changeSelection = new(null);

‎src/ViewModels/WorkingCopy.cs‎

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
using System.Collections.Generic;
33
using System.IO;
44
using System.Threading.Tasks;
5+
using Avalonia.Threading;
56
using CommunityToolkit.Mvvm.ComponentModel;
67

78
namespace SourceGit.ViewModels
@@ -191,6 +192,30 @@ public string StagedCountInfo
191192
get => string.IsNullOrEmpty(_filter) ? $"({_staged.Count})" : $"({_visibleStaged.Count}/{_staged.Count})";
192193
}
193194

195+
public int UnstagedAddedLines
196+
{
197+
get => _unstagedAddedLines;
198+
private set => SetProperty(ref _unstagedAddedLines, value);
199+
}
200+
201+
public int UnstagedDeletedLines
202+
{
203+
get => _unstagedDeletedLines;
204+
private set => SetProperty(ref _unstagedDeletedLines, value);
205+
}
206+
207+
public int StagedAddedLines
208+
{
209+
get => _stagedAddedLines;
210+
private set => SetProperty(ref _stagedAddedLines, value);
211+
}
212+
213+
public int StagedDeletedLines
214+
{
215+
get => _stagedDeletedLines;
216+
private set => SetProperty(ref _stagedDeletedLines, value);
217+
}
218+
194219
public ChangeSelection SelectedUnstaged
195220
{
196221
get => _selectedUnstaged;
@@ -364,6 +389,29 @@ public void SetData(List<Models.Change> changes)
364389

365390
UpdateInProgressState();
366391
UpdateDetail();
392+
393+
_ = Task.Run(async () =>
394+
{
395+
var pref = Preferences.Instance;
396+
var unstagedStats = await new Commands.QueryDiffLineStats(
397+
_repo.FullPath, false, pref.IgnoreWhitespaceChangesInDiff, pref.IgnoreCRAtEOLInDiff)
398+
.GetResultAsync().ConfigureAwait(false);
399+
var stagedStats = await new Commands.QueryDiffLineStats(
400+
_repo.FullPath, true, pref.IgnoreWhitespaceChangesInDiff, pref.IgnoreCRAtEOLInDiff)
401+
.GetResultAsync().ConfigureAwait(false);
402+
403+
Dispatcher.UIThread.Post(() =>
404+
{
405+
// A newer SetData may have run while fetching stats; drop this stale update.
406+
if (!ReferenceEquals(_cached, changes))
407+
return;
408+
409+
UnstagedAddedLines = unstagedStats.added;
410+
UnstagedDeletedLines = unstagedStats.deleted;
411+
StagedAddedLines = stagedStats.added;
412+
StagedDeletedLines = stagedStats.deleted;
413+
});
414+
});
367415
}
368416

369417
public async Task StageChangesAsync(List<Models.Change> changes, Models.Change next)
@@ -958,6 +1006,10 @@ public ChangeInTree(Models.Change change)
9581006
private List<Models.Change> _visibleStaged = [];
9591007
private ChangeSelection _selectedUnstaged = new(null);
9601008
private ChangeSelection _selectedStaged = new(null);
1009+
private int _unstagedAddedLines = 0;
1010+
private int _unstagedDeletedLines = 0;
1011+
private int _stagedAddedLines = 0;
1012+
private int _stagedDeletedLines = 0;
9611013
private object _detailContext = null;
9621014
private string _filter = string.Empty;
9631015
private string _commitMessage = string.Empty;

‎src/Views/CommitChanges.axaml‎

Lines changed: 19 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
55
xmlns:vm="using:SourceGit.ViewModels"
66
xmlns:v="using:SourceGit.Views"
7+
xmlns:c="using:SourceGit.Converters"
78
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
89
x:Class="SourceGit.Views.CommitChanges"
910
x:DataType="vm:CommitDetail">
@@ -65,12 +66,24 @@
6566

6667
<!-- Summary -->
6768
<Border Grid.Row="2" CornerRadius="0,0,4,4" BorderBrush="{DynamicResource Brush.Border2}" BorderThickness="1,0,1,1" Background="Transparent">
68-
<TextBlock Margin="4,0,0,0"
69-
Foreground="{DynamicResource Brush.FG2}"
70-
HorizontalAlignment="Left" VerticalAlignment="Center">
71-
<Run Text="{Binding Changes.Count, Mode=OneWay}" FontWeight="Bold"/>
72-
<Run Text="{DynamicResource Text.CommitDetail.Changes.Count}"/>
73-
</TextBlock>
69+
<StackPanel Orientation="Horizontal" Margin="4,0,0,0" VerticalAlignment="Center">
70+
<TextBlock Foreground="{DynamicResource Brush.FG2}" HorizontalAlignment="Left" VerticalAlignment="Center">
71+
<Run Text="{Binding Changes.Count, Mode=OneWay}" FontWeight="Bold"/>
72+
<Run Text="{DynamicResource Text.CommitDetail.Changes.Count}"/>
73+
</TextBlock>
74+
<TextBlock FontSize="11"
75+
Margin="12,0,0,0"
76+
Foreground="Green"
77+
Text="{Binding TotalAddedLines, StringFormat=+{0}}"
78+
IsVisible="{Binding TotalAddedLines, Converter={x:Static c:IntConverters.IsGreaterThanZero}}"
79+
VerticalAlignment="Center"/>
80+
<TextBlock FontSize="11"
81+
Margin="4,0,0,0"
82+
Foreground="Red"
83+
Text="{Binding TotalDeletedLines, StringFormat=-{0}}"
84+
IsVisible="{Binding TotalDeletedLines, Converter={x:Static c:IntConverters.IsGreaterThanZero}}"
85+
VerticalAlignment="Center"/>
86+
</StackPanel>
7487
</Border>
7588
</Grid>
7689

‎src/Views/Compare.axaml‎

Lines changed: 18 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -195,12 +195,24 @@
195195

196196
<!-- Summary -->
197197
<Border Grid.Row="2" CornerRadius="0,0,4,4" BorderBrush="{DynamicResource Brush.Border2}" BorderThickness="1,0,1,1" Background="Transparent">
198-
<TextBlock Margin="4,0,0,0"
199-
Foreground="{DynamicResource Brush.FG2}"
200-
HorizontalAlignment="Left" VerticalAlignment="Center">
201-
<Run Text="{Binding TotalChanges, Mode=OneWay}" FontWeight="Bold"/>
202-
<Run Text="{DynamicResource Text.CommitDetail.Changes.Count}"/>
203-
</TextBlock>
198+
<StackPanel Orientation="Horizontal" Margin="4,0,0,0" VerticalAlignment="Center">
199+
<TextBlock Foreground="{DynamicResource Brush.FG2}" HorizontalAlignment="Left" VerticalAlignment="Center">
200+
<Run Text="{Binding TotalChanges, Mode=OneWay}" FontWeight="Bold"/>
201+
<Run Text="{DynamicResource Text.CommitDetail.Changes.Count}"/>
202+
</TextBlock>
203+
<TextBlock FontSize="11"
204+
Margin="12,0,0,0"
205+
Foreground="Green"
206+
Text="{Binding TotalAddedLines, StringFormat=+{0}}"
207+
IsVisible="{Binding TotalAddedLines, Converter={x:Static c:IntConverters.IsGreaterThanZero}}"
208+
VerticalAlignment="Center"/>
209+
<TextBlock FontSize="11"
210+
Margin="4,0,0,0"
211+
Foreground="Red"
212+
Text="{Binding TotalDeletedLines, StringFormat=-{0}}"
213+
IsVisible="{Binding TotalDeletedLines, Converter={x:Static c:IntConverters.IsGreaterThanZero}}"
214+
VerticalAlignment="Center"/>
215+
</StackPanel>
204216
</Border>
205217
</Grid>
206218

0 commit comments

Comments
 (0)