Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions src/Commands/QueryCommits.cs
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,10 @@ public QueryCommits(string repo, string filter, Models.CommitSearchMethod method
{
builder.Append("-i --author=").Append(filter.Quoted());
}
else if (method == Models.CommitSearchMethod.ByCommitter)
{
builder.Append("-i --committer=").Append(filter.Quoted());
}
else if (method == Models.CommitSearchMethod.ByMessage)
{
var words = filter.Split([' ', '\t', '\r'], StringSplitOptions.RemoveEmptyEntries);
Expand Down
3 changes: 3 additions & 0 deletions src/Converters/IntConverters.cs
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,9 @@ public static class IntConverters
public static readonly FuncValueConverter<int, bool> IsZero =
new(v => v == 0);

public static readonly FuncValueConverter<int, bool> IsNotOne =
new(v => v != 1);

public static readonly FuncValueConverter<int, Thickness> ToTreeMargin =
new(v => new Thickness(v * 16, 0, 0, 0));

Expand Down
7 changes: 6 additions & 1 deletion src/Models/Commit.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,18 @@

namespace SourceGit.Models
{
// Bound by index to the search-method ComboBox in Views/CommitSearchView.axaml,
// so UI-selectable methods must stay first and in dropdown order. ByCommitter
// is internal-only (used by the fulltext `All` search).
public enum CommitSearchMethod
{
BySHA = 0,
All = 0,
BySHA,
ByAuthor,
ByMessage,
ByPath,
ByContent,
ByCommitter,
}

public class Commit : ObservableObject
Expand Down
1 change: 1 addition & 0 deletions src/Resources/Locales/en_US.axaml
Original file line number Diff line number Diff line change
Expand Up @@ -839,6 +839,7 @@
<x:String x:Key="Text.Repository.Remotes.Add" xml:space="preserve">Add Remote</x:String>
<x:String x:Key="Text.Repository.Resolve" xml:space="preserve">RESOLVE</x:String>
<x:String x:Key="Text.Repository.Search" xml:space="preserve">SEARCH COMMITS</x:String>
<x:String x:Key="Text.Repository.Search.ByAll" xml:space="preserve">SHA/Author/Message</x:String>
<x:String x:Key="Text.Repository.Search.ByAuthor" xml:space="preserve">Author</x:String>
<x:String x:Key="Text.Repository.Search.ByContent" xml:space="preserve">Content</x:String>
<x:String x:Key="Text.Repository.Search.ByMessage" xml:space="preserve">Message</x:String>
Expand Down
85 changes: 71 additions & 14 deletions src/ViewModels/SearchCommitContext.cs
Original file line number Diff line number Diff line change
Expand Up @@ -129,22 +129,21 @@ public void StartSearch()
result.Add(commit);
}
}
else if (_onlySearchCurrentBranch)
{
result = await new Commands.QueryCommits(_repo, _filter, method, true)
.GetResultAsync()
.ConfigureAwait(false);

foreach (var c in result)
c.IsMerged = true;
}
else
{
result = await new Commands.QueryCommits(_repo, _filter, method, false)
.GetResultAsync()
.ConfigureAwait(false);
if (method == Models.CommitSearchMethod.All)
result = await SearchAllFieldsAsync().ConfigureAwait(false);
else
result = await new Commands.QueryCommits(_repo, _filter, method, _onlySearchCurrentBranch)
.GetResultAsync()
.ConfigureAwait(false);

if (result.Count > 0)
if (_onlySearchCurrentBranch)
{
foreach (var c in result)
c.IsMerged = true;
}
else if (result.Count > 0)
{
var set = await new Commands.QueryCurrentBranchCommitHashes(_repo, result[^1].CommitterTime)
.GetResultAsync()
Expand All @@ -171,6 +170,64 @@ public void StartSearch()
}, token);
}

private async Task<List<Models.Commit>> SearchAllFieldsAsync()
{
// Git ANDs `--grep`, `--author` and `--committer` within a single
// invocation, so every field needs its own `git log`. Each of those is
// a full history walk on large repositories, so they run concurrently
// instead of one after another.
var bySHA = SearchExactSHAAsync();
var byMessage = new Commands.QueryCommits(_repo, _filter, Models.CommitSearchMethod.ByMessage, _onlySearchCurrentBranch)
.GetResultAsync();
var byAuthor = new Commands.QueryCommits(_repo, _filter, Models.CommitSearchMethod.ByAuthor, _onlySearchCurrentBranch)
.GetResultAsync();
var byCommitter = new Commands.QueryCommits(_repo, _filter, Models.CommitSearchMethod.ByCommitter, _onlySearchCurrentBranch)
.GetResultAsync();

await Task.WhenAll(bySHA, byMessage, byAuthor, byCommitter).ConfigureAwait(false);

var result = new List<Models.Commit>();
var seen = new HashSet<string>();

if (bySHA.Result is { } exact && seen.Add(exact.SHA))
result.Add(exact);

foreach (var matched in new[] { byMessage.Result, byAuthor.Result, byCommitter.Result })
{
foreach (var c in matched)
{
if (seen.Add(c.SHA))
result.Add(c);
}
}

result.Sort((l, r) => r.CommitterTime.CompareTo(l.CommitterTime));
return result;
}

private async Task<Models.Commit> SearchExactSHAAsync()
{
var isCommitSHA = await new Commands.IsCommitSHA(_repo, _filter)
.GetResultAsync()
.ConfigureAwait(false);

if (!isCommitSHA)
return null;

var commit = await new Commands.QuerySingleCommit(_repo, _filter)
.GetResultAsync()
.ConfigureAwait(false);

if (commit == null || !_onlySearchCurrentBranch)
return commit;

var isInCurrentBranch = await new Commands.IsAncestor(_repo, commit.SHA, "HEAD")
.GetResultAsync()
.ConfigureAwait(false);

return isInCurrentBranch ? commit : null;
}

public void EndSearch()
{
if (_cancellation is { IsCancellationRequested: false })
Expand Down Expand Up @@ -288,7 +345,7 @@ private void UpdateSuggestions()
private string _repo = null;
private Histories _histories = null;
private CancellationTokenSource _cancellation = null;
private int _method = (int)Models.CommitSearchMethod.ByMessage;
private int _method = (int)Models.CommitSearchMethod.All;
private string _filter = string.Empty;
private bool _onlySearchCurrentBranch = false;
private bool _isQuerying = false;
Expand Down
3 changes: 2 additions & 1 deletion src/Views/CommitSearchView.axaml
Original file line number Diff line number Diff line change
Expand Up @@ -145,6 +145,7 @@
BorderBrush="{DynamicResource Brush.Border2}"
SelectedIndex="{Binding Method, Mode=TwoWay}">
<ComboBox.Items>
<TextBlock Text="{DynamicResource Text.Repository.Search.ByAll}"/>
<TextBlock Text="{DynamicResource Text.Repository.Search.BySHA}"/>
<TextBlock Text="{DynamicResource Text.Repository.Search.ByAuthor}"/>
<TextBlock Text="{DynamicResource Text.Repository.Search.ByMessage}"/>
Expand All @@ -156,7 +157,7 @@
<CheckBox Grid.Row="1" Grid.Column="1"
Height="26"
IsChecked="{Binding OnlySearchCurrentBranch, Mode=TwoWay}"
IsVisible="{Binding Method, Converter={x:Static c:IntConverters.IsGreaterThanZero}}">
IsVisible="{Binding Method, Converter={x:Static c:IntConverters.IsNotOne}}">
<TextBlock Text="{DynamicResource Text.Repository.Search.InCurrentBranch}"/>
</CheckBox>
</Grid>
Expand Down