Skip to content
6 changes: 5 additions & 1 deletion src/Commands/QueryCommits.cs
Original file line number Diff line number Diff line change
@@ -1,18 +1,20 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace SourceGit.Commands
{
public class QueryCommits : Command
{
public QueryCommits(string repo, string limits, bool needFindHead = true)
public QueryCommits(string repo, string limits, bool needFindHead = true, List<string> patterns = null)
{
WorkingDirectory = repo;
Context = repo;
Args = $"log --no-show-signature --decorate=full --format=%H%n%P%n%D%n%aN±%aE%n%at%n%cN±%cE%n%ct%n%s {limits}";
_findFirstMerged = needFindHead;
_patterns = patterns ?? new List<string>();
}

public QueryCommits(string repo, string filter, Models.CommitSearchMethod method, bool onlyCurrentBranch)
Expand Down Expand Up @@ -70,6 +72,7 @@ public QueryCommits(string repo, string filter, Models.CommitSearchMethod method
{
case 0:
_current = new Models.Commit() { SHA = line };
_current.IsCommitFilterHead = _patterns.Count > 0 && _patterns.Any(f => line.StartsWith(f));
_commits.Add(_current);
break;
case 1:
Expand Down Expand Up @@ -143,6 +146,7 @@ private async Task MarkFirstMergedAsync()
}

private List<Models.Commit> _commits = new List<Models.Commit>();
private List<string> _patterns = new List<string>();
private Models.Commit _current = null;
private bool _findFirstMerged = false;
private bool _isHeadFound = false;
Expand Down
1 change: 1 addition & 0 deletions src/Models/Commit.cs
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ public static double OpacityForNotMerged
public string AuthorTimeShortStr => DateTime.UnixEpoch.AddSeconds(AuthorTime).ToLocalTime().ToString(DateTimeFormat.Active.DateOnly);
public string CommitterTimeShortStr => DateTime.UnixEpoch.AddSeconds(CommitterTime).ToLocalTime().ToString(DateTimeFormat.Active.DateOnly);

public bool IsCommitFilterHead { get; set; } = false;
public bool IsMerged { get; set; } = false;
public bool IsCommitterVisible => !Author.Equals(Committer) || AuthorTime != CommitterTime;
public bool IsCurrentHead => Decorators.Find(x => x.Type is DecoratorType.CurrentBranchHead or DecoratorType.CurrentCommitHead) != null;
Expand Down
5 changes: 4 additions & 1 deletion src/Models/CommitGraph.cs
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@ public enum DotType
Default,
Head,
Merge,
Filter,
}

public class Dot
Expand Down Expand Up @@ -157,7 +158,9 @@ public static CommitGraph Parse(List<Commit> commits, bool firstParentOnlyEnable
var position = new Point(major?.LastX ?? offsetX, offsetY);
var dotColor = major?.Path.Color ?? 0;
var anchor = new Dot() { Center = position, Color = dotColor, IsMerged = isMerged };
if (commit.IsCurrentHead)
if (commit.IsCommitFilterHead)
anchor.Type = DotType.Filter;
else if (commit.IsCurrentHead)
anchor.Type = DotType.Head;
else if (commit.Parents.Count > 1)
anchor.Type = DotType.Merge;
Expand Down
1 change: 1 addition & 0 deletions src/Models/Filter.cs
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ public enum FilterType
RemoteBranch,
RemoteBranchFolder,
Tag,
SoloCommits,
}

public enum FilterMode
Expand Down
30 changes: 29 additions & 1 deletion src/Models/RepositorySettings.cs
Original file line number Diff line number Diff line change
Expand Up @@ -306,7 +306,7 @@ public void RemoveChildrenBranchFilters(string pattern)
HistoriesFilters.Remove(filter);
}

public string BuildHistoriesFilter()
public string BuildHistoriesFilter(SourceGit.ViewModels.Repository repo)
{
var includedRefs = new List<string>();
var excludedBranches = new List<string>();
Expand Down Expand Up @@ -349,6 +349,34 @@ public string BuildHistoriesFilter()
else if (filter.Mode == FilterMode.Excluded)
excludedTags.Add($"--exclude=\"{filter.Pattern}\" --decorate-refs-exclude=\"refs/tags/{filter.Pattern}\"");
}
else if (filter.Type == FilterType.SoloCommits)
{
var allRefs = repo.GetRefsContainsThisCommit(filter.Pattern);
if (filter.Mode == FilterMode.Included)
{
foreach (var refItem in allRefs)
{
if (refItem.Type == Models.DecoratorType.LocalBranchHead)
includedRefs.Add($"{refItem.Name}");
else if (refItem.Type == Models.DecoratorType.RemoteBranchHead)
includedRefs.Add($"{refItem.Name}");
else if (refItem.Type == Models.DecoratorType.Tag)
includedRefs.Add($"refs/tags/{refItem.Name}");
}
}
else if (filter.Mode == FilterMode.Excluded)
{
foreach (var refItem in allRefs)
{
if (refItem.Type == Models.DecoratorType.LocalBranchHead)
excludedBranches.Add($"--exclude=\"{refItem.Name}\" --decorate-refs-exclude=\"refs/heads/{refItem.Name}\"");
else if (refItem.Type == Models.DecoratorType.RemoteBranchHead)
excludedRemotes.Add($"--exclude=\"{refItem.Name}\" --decorate-refs-exclude=\"refs/remotes/{refItem.Name}\"");
else if (refItem.Type == Models.DecoratorType.Tag)
excludedTags.Add($"--exclude=\"refs/tags/{refItem.Name}\" --decorate-refs-exclude=\"refs/tags/{refItem.Name}\"");
}
}
}
}

var builder = new StringBuilder();
Expand Down
2 changes: 2 additions & 0 deletions src/Resources/Locales/en_US.axaml
Original file line number Diff line number Diff line change
Expand Up @@ -126,6 +126,7 @@
<x:String x:Key="Text.CommitCM.CopySHA" xml:space="preserve">SHA</x:String>
<x:String x:Key="Text.CommitCM.CopySubject" xml:space="preserve">Subject</x:String>
<x:String x:Key="Text.CommitCM.CustomAction" xml:space="preserve">Custom Action</x:String>
<x:String x:Key="Text.CommitCM.SoloCommits" xml:space="preserve">Solo on Current Commit</x:String>
<x:String x:Key="Text.CommitCM.InteractiveRebase" xml:space="preserve">Interactive Rebase</x:String>
<x:String x:Key="Text.CommitCM.InteractiveRebase.Drop" xml:space="preserve">Drop...</x:String>
<x:String x:Key="Text.CommitCM.InteractiveRebase.Edit" xml:space="preserve">Edit...</x:String>
Expand Down Expand Up @@ -654,6 +655,7 @@
<x:String x:Key="Text.Repository.LocalBranches" xml:space="preserve">LOCAL BRANCHES</x:String>
<x:String x:Key="Text.Repository.MoreOptions" xml:space="preserve">More options...</x:String>
<x:String x:Key="Text.Repository.NavigateToCurrentHead" xml:space="preserve">Navigate to HEAD</x:String>
<x:String x:Key="Text.Repository.SoloModeOnCurrentHead" xml:space="preserve">Solo On HEAD</x:String>
<x:String x:Key="Text.Repository.NewBranch" xml:space="preserve">Create Branch</x:String>
<x:String x:Key="Text.Repository.Notifications.Clear" xml:space="preserve">CLEAR NOTIFICATIONS</x:String>
<x:String x:Key="Text.Repository.OnlyHighlightCurrentBranchInGraph" xml:space="preserve">Only highlight current branch</x:String>
Expand Down
6 changes: 4 additions & 2 deletions src/ViewModels/Checkout.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using System.Threading.Tasks;
using System.Linq;
using System.Threading.Tasks;

namespace SourceGit.ViewModels
{
Expand Down Expand Up @@ -102,7 +103,8 @@ public override async Task<bool> Sure()
log.Complete();

var b = _repo.Branches.Find(x => x.IsLocal && x.Name == Branch);
if (b != null && _repo.HistoriesFilterMode == Models.FilterMode.Included)
if (b != null && _repo.HistoriesFilterMode == Models.FilterMode.Included
&& !_repo.Settings.HistoriesFilters.Any(f => f.Pattern == "HEAD"))
_repo.SetBranchFilterMode(b, Models.FilterMode.Included, true, false);

_repo.MarkBranchesDirtyManually();
Expand Down
15 changes: 15 additions & 0 deletions src/ViewModels/Histories.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
using System.Collections;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Threading.Tasks;

using Avalonia.Controls;
Expand Down Expand Up @@ -44,6 +45,12 @@ public Models.Commit AutoSelectedCommit
set => SetProperty(ref _autoSelectedCommit, value);
}

public List<Models.Commit> LastSelectedCommits
{
get => _lastSelectedCommits;
private set => SetProperty(ref _lastSelectedCommits, value);
}

public long NavigationId
{
get => _navigationId;
Expand Down Expand Up @@ -191,6 +198,13 @@ public void Select(IList commits)
_repo.SelectedSearchedCommit = null;
DetailContext = new Models.Count(commits.Count);
}

_repo.SelectedCommits = commits.Cast<Models.Commit>().ToList();
}

public void MarkCommitsAsSelected(IList<Models.Commit> commits)
{
LastSelectedCommits = _commits.Where(x => commits.Any(y => y.SHA == x.SHA)).ToList();
}

public bool CheckoutBranchByDecorator(Models.Decorator decorator)
Expand Down Expand Up @@ -402,6 +416,7 @@ private void NavigateTo(Models.Commit commit)
private Repository _repo = null;
private bool _isLoading = true;
private List<Models.Commit> _commits = new List<Models.Commit>();
private List<Models.Commit> _lastSelectedCommits = [];
private Models.CommitGraph _graph = null;
private Models.Commit _autoSelectedCommit = null;
private Models.Bisect _bisect = null;
Expand Down
71 changes: 67 additions & 4 deletions src/ViewModels/Repository.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Text.Json;
using System.Threading;
Expand Down Expand Up @@ -348,6 +349,12 @@ public List<Models.Commit> SearchedCommits
set => SetProperty(ref _searchedCommits, value);
}

public List<Models.Commit> SelectedCommits
{
get => _selectCommits;
set => SetProperty(ref _selectCommits, value);
}

public Models.Commit SelectedSearchedCommit
{
get => _selectedSearchedCommit;
Expand Down Expand Up @@ -1005,10 +1012,26 @@ public void NavigateToCommit(string sha, bool isDelayMode = false)
else if (_histories != null)
{
SelectedViewIndex = 0;
if (sha == "HEAD")
sha = _currentBranch.Head;
_histories.NavigateTo(sha);
}
}

public void NavigateToBranch(string branch, bool isDelayMode = false)
{
var b = _branches.Find(b => b.FullName.Equals(branch, StringComparison.Ordinal));
if (b != null)
NavigateToCommit(b.Head);
}

public void NavigateToTag(string tag, bool isDelayMode = false)
{
var t = _tags.Find(t => t.Name.Equals(tag, StringComparison.Ordinal));
if (t != null)
NavigateToCommit(t.SHA);
}

public void ClearCommitMessage()
{
if (_workingCopy is not null)
Expand Down Expand Up @@ -1063,6 +1086,30 @@ public void SetTagFilterMode(Models.Tag tag, Models.FilterMode mode)
RefreshHistoriesFilters(true);
}

public void SetSoloCommitFilterMode(Models.Commit commit, Models.FilterMode mode)
=> SetSoloCommitFilterMode(commit.SHA[..10], mode);


public void SetSoloCommitFilterMode(IEnumerable<Models.Commit> commits, Models.FilterMode mode)
=> SetSoloCommitFilterMode(commits.Select(x => x.SHA[..10]).ToList(), mode);

public void SetSoloCommitFilterMode(string sha, Models.FilterMode mode)
{
var changed = _settings.UpdateHistoriesFilter(sha, Models.FilterType.SoloCommits, mode);
if (changed)
RefreshHistoriesFilters(true);
}

public void SetSoloCommitFilterMode(IEnumerable<string> shas, Models.FilterMode mode)
{
bool changed = false;
foreach (var sha in shas)
changed |= _settings.UpdateHistoriesFilter(sha, Models.FilterType.SoloCommits, mode);

if (changed)
RefreshHistoriesFilters(true);
}

public void SetBranchFilterMode(Models.Branch branch, Models.FilterMode mode, bool clearExists, bool refresh)
{
var node = FindBranchNode(branch.IsLocal ? _localBranchTrees : _remoteBranchTrees, branch.FullName);
Expand All @@ -1077,8 +1124,10 @@ public void SetBranchFilterMode(BranchTreeNode node, Models.FilterMode mode, boo

if (clearExists)
{
_settings.HistoriesFilters.Clear();
HistoriesFilterMode = Models.FilterMode.None;
_settings.HistoriesFilters.RemoveAll(_settings.HistoriesFilters
.Where(f => f.Type != Models.FilterType.SoloCommits).ToArray());
if (_settings.HistoriesFilters.Count <= 0)
HistoriesFilterMode = Models.FilterMode.None;
}

if (node.Backend is Models.Branch branch)
Expand Down Expand Up @@ -1257,8 +1306,17 @@ public void RefreshTags()
});
}

public List<Models.Decorator> GetRefsContainsThisCommit(string hash = null)
{
var a = new Commands.QueryRefsContainsCommit(FullPath, hash ?? "HEAD")
.GetResultAsync();
return a.Result;
}

public void RefreshCommits()
{
var oldSelectedCommits = _selectCommits.ToList();

Dispatcher.UIThread.Invoke(() => _histories.IsLoading = true);

var builder = new StringBuilder();
Expand All @@ -1278,13 +1336,15 @@ public void RefreshCommits()
if (_settings.HistoryShowFlags.HasFlag(Models.HistoryShowFlags.SimplifyByDecoration))
builder.Append("--simplify-by-decoration ");

var filters = _settings.BuildHistoriesFilter();
var filters = _settings.BuildHistoriesFilter(this);
if (string.IsNullOrEmpty(filters))
builder.Append("--branches --remotes --tags HEAD");
else
builder.Append(filters);

var commits = new Commands.QueryCommits(_fullpath, builder.ToString()).GetResultAsync().Result;
var patterns = _settings.HistoriesFilters.Where(f => f.Type == Models.FilterType.SoloCommits).Select(f => f.Pattern).ToList();

var commits = new Commands.QueryCommits(_fullpath, builder.ToString(), true, patterns).GetResultAsync().Result;
var graph = Models.CommitGraph.Parse(commits, _settings.HistoryShowFlags.HasFlag(Models.HistoryShowFlags.FirstParentOnly));

Dispatcher.UIThread.Invoke(() =>
Expand All @@ -1297,6 +1357,8 @@ public void RefreshCommits()

BisectState = _histories.UpdateBisectInfo();

_histories.MarkCommitsAsSelected(oldSelectedCommits);

if (!string.IsNullOrEmpty(_navigateToCommitDelayed))
NavigateToCommit(_navigateToCommitDelayed);
}
Expand Down Expand Up @@ -1983,6 +2045,7 @@ private async void AutoFetchImpl(object sender)
private bool _onlySearchCommitsInCurrentBranch = false;
private string _searchCommitFilter = string.Empty;
private List<Models.Commit> _searchedCommits = new List<Models.Commit>();
private List<Models.Commit> _selectCommits = new List<Models.Commit>();
private Models.Commit _selectedSearchedCommit = null;
private bool _requestingWorktreeFiles = false;
private List<string> _worktreeFiles = null;
Expand Down
6 changes: 6 additions & 0 deletions src/Views/CommitGraph.cs
Original file line number Diff line number Diff line change
Expand Up @@ -230,6 +230,12 @@ private void DrawAnchors(DrawingContext context, Models.CommitGraph graph, doubl
context.DrawLine(dotFillPen, new Point(center.X, center.Y - 3), new Point(center.X, center.Y + 3));
context.DrawLine(dotFillPen, new Point(center.X - 3, center.Y), new Point(center.X + 3, center.Y));
break;
case Models.CommitGraph.DotType.Filter:
context.DrawEllipse(pen.Brush, null, center, 7, 7);
context.DrawLine(dotFillPen, new Point(center.X, center.Y - 5), new Point(center.X, center.Y + 5));
context.DrawLine(dotFillPen, new Point(center.X - 4, center.Y - 3), new Point(center.X + 4, center.Y + 3));
context.DrawLine(dotFillPen, new Point(center.X + 4, center.Y - 3), new Point(center.X - 4, center.Y + 3));
break;
default:
context.DrawEllipse(dotFill, pen, center, 3, 3);
break;
Expand Down
Loading
Loading