feature: add a check icon for commits contained in current branch in search results (#1773)

Signed-off-by: leo <longshuang@msn.cn>
This commit is contained in:
leo
2025-09-01 21:15:50 +08:00
parent be915a2390
commit 3056287cde
5 changed files with 117 additions and 38 deletions

View File

@@ -0,0 +1,20 @@
using System.Threading.Tasks;
namespace SourceGit.Commands
{
public class IsAncestor : Command
{
public IsAncestor(string repo, string checkPoint, string endPoint)
{
WorkingDirectory = repo;
Context = repo;
Args = $"merge-base --is-ancestor {checkPoint} {endPoint}";
}
public async Task<bool> GetResultAsync()
{
var rs = await ReadToEndAsync().ConfigureAwait(false);
return rs.IsSuccess;
}
}
}

View File

@@ -18,40 +18,39 @@ namespace SourceGit.Commands
public QueryCommits(string repo, string filter, Models.CommitSearchMethod method, bool onlyCurrentBranch)
{
string search = onlyCurrentBranch ? string.Empty : "--branches --remotes ";
var builder = new StringBuilder();
builder.Append("log -1000 --date-order --no-show-signature --decorate=full --format=%H%x00%P%x00%D%x00%aN±%aE%x00%at%x00%cN±%cE%x00%ct%x00%s ");
if (!onlyCurrentBranch)
builder.Append("--branches --remotes ");
if (method == Models.CommitSearchMethod.ByAuthor)
{
search += $"-i --author={filter.Quoted()}";
builder.Append("-i --author=").Append(filter.Quoted());
}
else if (method == Models.CommitSearchMethod.ByCommitter)
{
search += $"-i --committer={filter.Quoted()}";
builder.Append("-i --committer=").Append(filter.Quoted());
}
else if (method == Models.CommitSearchMethod.ByMessage)
{
var argsBuilder = new StringBuilder();
argsBuilder.Append(search);
var words = filter.Split([' ', '\t', '\r'], StringSplitOptions.RemoveEmptyEntries);
foreach (var word in words)
argsBuilder.Append("--grep=").Append(word.Trim().Quoted()).Append(' ');
argsBuilder.Append("--all-match -i");
search = argsBuilder.ToString();
builder.Append("--grep=").Append(word.Trim().Quoted()).Append(' ');
builder.Append("--all-match -i");
}
else if (method == Models.CommitSearchMethod.ByPath)
{
search += $"-- {filter.Quoted()}";
builder.Append("-- ").Append(filter.Quoted());
}
else
{
search = $"-G{filter.Quoted()}";
builder.Append("-G").Append(filter.Quoted());
}
WorkingDirectory = repo;
Context = repo;
Args = $"log -1000 --date-order --no-show-signature --decorate=full --format=%H%x00%P%x00%D%x00%aN±%aE%x00%at%x00%cN±%cE%x00%ct%x00%s {search}";
Args = builder.ToString();
_findFirstMerged = false;
}
@@ -87,7 +86,20 @@ namespace SourceGit.Commands
await proc.WaitForExitAsync().ConfigureAwait(false);
if (_findFirstMerged && !_isHeadFound && commits.Count > 0)
await MarkFirstMergedAsync(commits).ConfigureAwait(false);
{
var set = await new QueryCurrentBranchCommitHashes(WorkingDirectory, commits[^1].CommitterTime)
.GetResultAsync()
.ConfigureAwait(false);
foreach (var c in commits)
{
if (set.Contains(c.SHA))
{
c.IsMerged = true;
break;
}
}
}
}
catch (Exception e)
{
@@ -97,27 +109,6 @@ namespace SourceGit.Commands
return commits;
}
private async Task MarkFirstMergedAsync(List<Models.Commit> commits)
{
Args = $"log --since={commits[^1].CommitterTimeStr.Quoted()} --format=\"%H\"";
var rs = await ReadToEndAsync().ConfigureAwait(false);
var shas = rs.StdOut.Split(['\r', '\n'], StringSplitOptions.RemoveEmptyEntries);
if (shas.Length == 0)
return;
var set = new HashSet<string>(shas);
foreach (var c in commits)
{
if (set.Contains(c.SHA))
{
c.IsMerged = true;
break;
}
}
}
private bool _findFirstMerged = false;
private bool _isHeadFound = false;
}

View File

@@ -0,0 +1,41 @@
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Threading.Tasks;
namespace SourceGit.Commands
{
public class QueryCurrentBranchCommitHashes : Command
{
public QueryCurrentBranchCommitHashes(string repo, ulong sinceTimestamp)
{
var since = DateTime.UnixEpoch.AddSeconds(sinceTimestamp).ToLocalTime().ToString("yyyy/MM/dd HH:mm:ss");
WorkingDirectory = repo;
Context = repo;
Args = $"log --since={since.Quoted()} --format=%H";
}
public async Task<HashSet<string>> GetResultAsync()
{
var outs = new HashSet<string>();
try
{
using var proc = new Process();
proc.StartInfo = CreateGitStartInfo(true);
proc.Start();
while (await proc.StandardOutput.ReadLineAsync() is { Length: > 8 } line)
outs.Add(line);
await proc.WaitForExitAsync().ConfigureAwait(false);
}
catch
{
// Ignore exceptions;
}
return outs;
}
}
}

View File

@@ -905,14 +905,38 @@ namespace SourceGit.ViewModels
var commit = await new Commands.QuerySingleCommit(FullPath, _searchCommitFilter)
.GetResultAsync()
.ConfigureAwait(false);
commit.IsMerged = await new Commands.IsAncestor(FullPath, commit.SHA, "HEAD")
.GetResultAsync()
.ConfigureAwait(false);
visible.Add(commit);
}
}
else
else if (_onlySearchCommitsInCurrentBranch)
{
visible = await new Commands.QueryCommits(FullPath, _searchCommitFilter, method, _onlySearchCommitsInCurrentBranch)
visible = await new Commands.QueryCommits(FullPath, _searchCommitFilter, method, true)
.GetResultAsync()
.ConfigureAwait(false);
foreach (var c in visible)
c.IsMerged = true;
}
else
{
visible = await new Commands.QueryCommits(FullPath, _searchCommitFilter, method, false)
.GetResultAsync()
.ConfigureAwait(false);
if (visible.Count > 0)
{
var set = await new Commands.QueryCurrentBranchCommitHashes(FullPath, visible[^1].CommitterTime)
.GetResultAsync()
.ConfigureAwait(false);
foreach (var c in visible)
c.IsMerged = set.Contains(c.SHA);
}
}
Dispatcher.UIThread.Post(() =>

View File

@@ -581,7 +581,10 @@
<ColumnDefinition Width="Auto" SharedSizeGroup="SearchCommitTimeColumn"/>
</Grid.ColumnDefinitions>
<v:Avatar Grid.Column="0" Width="16" Height="16" HorizontalAlignment="Center" VerticalAlignment="Center" IsHitTestVisible="False" User="{Binding Author}"/>
<TextBlock Grid.Column="1" Classes="primary" Text="{Binding Subject}" Margin="2,0,0,0" VerticalAlignment="Center" TextTrimming="CharacterEllipsis"/>
<Grid Grid.Column="1" ColumnDefinitions="Auto,*">
<Path Grid.Column="0" Width="14" Height="14" Margin="0,2,2,0" Data="{StaticResource Icons.Check}" Fill="Green" IsVisible="{Binding IsMerged, Mode=OneWay}"/>
<TextBlock Grid.Column="1" Classes="primary" Text="{Binding Subject}" Margin="2,0,0,0" VerticalAlignment="Center" TextTrimming="CharacterEllipsis"/>
</Grid>
<TextBlock Grid.Column="2"
Classes="primary"
Margin="4,0"