Skip to content

Commit b3fa69e

Browse files
committed
feature: add total added/removed line counts to changes views
1 parent 9d01953 commit b3fa69e

9 files changed

Lines changed: 273 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: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -97,6 +97,18 @@ public List<Models.Change> VisibleChanges
9797
set => SetProperty(ref _visibleChanges, value);
9898
}
9999

100+
public int TotalAddedLines
101+
{
102+
get => _totalAddedLines;
103+
private set => SetProperty(ref _totalAddedLines, value);
104+
}
105+
106+
public int TotalDeletedLines
107+
{
108+
get => _totalDeletedLines;
109+
private set => SetProperty(ref _totalDeletedLines, value);
110+
}
111+
100112
public List<Models.Change> SelectedChanges
101113
{
102114
get => _selectedChanges;
@@ -534,6 +546,9 @@ private void Refresh()
534546
{
535547
var cmd = new Commands.CompareRevisions(_repo.FullPath, _commit.FirstParentToCompare, _commit.SHA) { CancellationToken = token };
536548
var changes = await cmd.ReadAsync().ConfigureAwait(false);
549+
var pref = Preferences.Instance;
550+
var statsCmd = new Commands.QueryDiffLineStats(_repo.FullPath, _commit.FirstParentToCompare, _commit.SHA, pref.IgnoreWhitespaceChangesInDiff, pref.IgnoreCRAtEOLInDiff) { CancellationToken = token };
551+
var stats = await statsCmd.GetResultAsync().ConfigureAwait(false);
537552
var visible = changes;
538553
if (!string.IsNullOrWhiteSpace(_searchChangeFilter))
539554
{
@@ -551,6 +566,8 @@ private void Refresh()
551566
{
552567
Changes = changes;
553568
VisibleChanges = visible;
569+
TotalAddedLines = stats.added;
570+
TotalDeletedLines = stats.deleted;
554571

555572
if (visible.Count == 0)
556573
SelectedChanges = null;
@@ -768,6 +785,8 @@ private async Task SetViewingCommitAsync(Models.Object file)
768785
private List<Models.Change> _changes = [];
769786
private List<Models.Change> _visibleChanges = [];
770787
private List<Models.Change> _selectedChanges = null;
788+
private int _totalAddedLines = 0;
789+
private int _totalDeletedLines = 0;
771790
private string _searchChangeFilter = string.Empty;
772791
private DiffContext _diffContext = null;
773792
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

@@ -398,6 +421,8 @@ private string GetSHA(object obj)
398421
private Models.Commit _baseHead = null;
399422
private Models.Commit _toHead = null;
400423
private int _totalChanges = 0;
424+
private int _totalAddedLines = 0;
425+
private int _totalDeletedLines = 0;
401426
private List<Models.Change> _changes = null;
402427
private List<Models.Change> _visibleChanges = null;
403428
private List<Models.Change> _selectedChanges = 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;
@@ -350,10 +362,21 @@ private void Refresh()
350362
{
351363
Task.Run(async () =>
352364
{
353-
_changes = await new Commands.CompareRevisions(_repo.FullPath, GetSHA(_startPoint), GetSHA(_endPoint))
365+
var startSHA = GetSHA(_startPoint);
366+
var endSHA = GetSHA(_endPoint);
367+
_changes = await new Commands.CompareRevisions(_repo.FullPath, startSHA, endSHA)
354368
.ReadAsync()
355369
.ConfigureAwait(false);
356370

371+
var pref = Preferences.Instance;
372+
var stats = await new Commands.QueryDiffLineStats(
373+
_repo.FullPath,
374+
startSHA,
375+
endSHA,
376+
pref.IgnoreWhitespaceChangesInDiff,
377+
pref.IgnoreCRAtEOLInDiff)
378+
.GetResultAsync().ConfigureAwait(false);
379+
357380
var visible = _changes;
358381
if (!string.IsNullOrWhiteSpace(_searchFilter))
359382
{
@@ -368,6 +391,8 @@ private void Refresh()
368391
Dispatcher.UIThread.Post(() =>
369392
{
370393
TotalChanges = _changes.Count;
394+
TotalAddedLines = stats.added;
395+
TotalDeletedLines = stats.deleted;
371396
VisibleChanges = visible;
372397
IsLoading = false;
373398

@@ -394,6 +419,8 @@ private string GetDesc(object obj)
394419
private object _startPoint = null;
395420
private object _endPoint = null;
396421
private int _totalChanges = 0;
422+
private int _totalAddedLines = 0;
423+
private int _totalDeletedLines = 0;
397424
private List<Models.Change> _changes = null;
398425
private List<Models.Change> _visibleChanges = null;
399426
private List<Models.Change> _selectedChanges = 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
@@ -160,6 +161,30 @@ public List<Models.Change> VisibleStaged
160161
private set => SetProperty(ref _visibleStaged, value);
161162
}
162163

164+
public int UnstagedAddedLines
165+
{
166+
get => _unstagedAddedLines;
167+
private set => SetProperty(ref _unstagedAddedLines, value);
168+
}
169+
170+
public int UnstagedDeletedLines
171+
{
172+
get => _unstagedDeletedLines;
173+
private set => SetProperty(ref _unstagedDeletedLines, value);
174+
}
175+
176+
public int StagedAddedLines
177+
{
178+
get => _stagedAddedLines;
179+
private set => SetProperty(ref _stagedAddedLines, value);
180+
}
181+
182+
public int StagedDeletedLines
183+
{
184+
get => _stagedDeletedLines;
185+
private set => SetProperty(ref _stagedDeletedLines, value);
186+
}
187+
163188
public List<Models.Change> SelectedUnstaged
164189
{
165190
get => _selectedUnstaged;
@@ -312,6 +337,29 @@ public void SetData(List<Models.Change> changes)
312337

313338
UpdateInProgressState();
314339
UpdateDetail();
340+
341+
_ = Task.Run(async () =>
342+
{
343+
var pref = Preferences.Instance;
344+
var unstagedStats = await new Commands.QueryDiffLineStats(
345+
_repo.FullPath, false, pref.IgnoreWhitespaceChangesInDiff, pref.IgnoreCRAtEOLInDiff)
346+
.GetResultAsync().ConfigureAwait(false);
347+
var stagedStats = await new Commands.QueryDiffLineStats(
348+
_repo.FullPath, true, pref.IgnoreWhitespaceChangesInDiff, pref.IgnoreCRAtEOLInDiff)
349+
.GetResultAsync().ConfigureAwait(false);
350+
351+
Dispatcher.UIThread.Post(() =>
352+
{
353+
// A newer SetData may have run while fetching stats; drop this stale update.
354+
if (!ReferenceEquals(_cached, changes))
355+
return;
356+
357+
UnstagedAddedLines = unstagedStats.added;
358+
UnstagedDeletedLines = unstagedStats.deleted;
359+
StagedAddedLines = stagedStats.added;
360+
StagedDeletedLines = stagedStats.deleted;
361+
});
362+
});
315363
}
316364

317365
public async Task StageChangesAsync(List<Models.Change> changes, Models.Change next)
@@ -821,6 +869,10 @@ private bool IsChanged(List<Models.Change> old, List<Models.Change> cur)
821869
private List<Models.Change> _visibleStaged = [];
822870
private List<Models.Change> _selectedUnstaged = [];
823871
private List<Models.Change> _selectedStaged = [];
872+
private int _unstagedAddedLines = 0;
873+
private int _unstagedDeletedLines = 0;
874+
private int _stagedAddedLines = 0;
875+
private int _stagedDeletedLines = 0;
824876
private object _detailContext = null;
825877
private string _filter = string.Empty;
826878
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">
@@ -56,12 +57,24 @@
5657

5758
<!-- Summary -->
5859
<Border Grid.Row="2" BorderBrush="{DynamicResource Brush.Border2}" BorderThickness="1,0,1,1" Background="Transparent">
59-
<TextBlock Margin="4,0,0,0"
60-
Foreground="{DynamicResource Brush.FG2}"
61-
HorizontalAlignment="Left" VerticalAlignment="Center">
62-
<Run Text="{Binding Changes.Count, Mode=OneWay}" FontWeight="Bold"/>
63-
<Run Text="{DynamicResource Text.CommitDetail.Changes.Count}"/>
64-
</TextBlock>
60+
<StackPanel Orientation="Horizontal" Margin="4,0,0,0" VerticalAlignment="Center">
61+
<TextBlock Foreground="{DynamicResource Brush.FG2}" HorizontalAlignment="Left" VerticalAlignment="Center">
62+
<Run Text="{Binding Changes.Count, Mode=OneWay}" FontWeight="Bold"/>
63+
<Run Text="{DynamicResource Text.CommitDetail.Changes.Count}"/>
64+
</TextBlock>
65+
<TextBlock FontSize="11"
66+
Margin="12,0,0,0"
67+
Foreground="Green"
68+
Text="{Binding TotalAddedLines, StringFormat=+{0}}"
69+
IsVisible="{Binding TotalAddedLines, Converter={x:Static c:IntConverters.IsGreaterThanZero}}"
70+
VerticalAlignment="Center"/>
71+
<TextBlock FontSize="11"
72+
Margin="4,0,0,0"
73+
Foreground="Red"
74+
Text="{Binding TotalDeletedLines, StringFormat=-{0}}"
75+
IsVisible="{Binding TotalDeletedLines, Converter={x:Static c:IntConverters.IsGreaterThanZero}}"
76+
VerticalAlignment="Center"/>
77+
</StackPanel>
6578
</Border>
6679
</Grid>
6780

src/Views/Compare.axaml

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

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

0 commit comments

Comments
 (0)