feature: supports sort branches by committer date (#1192)

Signed-off-by: leo <longshuang@msn.cn>
This commit is contained in:
leo 2025-05-06 18:15:11 +08:00
parent b7fa04d141
commit e45e37d305
No known key found for this signature in database
10 changed files with 217 additions and 20 deletions

View file

@ -14,7 +14,7 @@ namespace SourceGit.Commands
{ {
WorkingDirectory = repo; WorkingDirectory = repo;
Context = repo; Context = repo;
Args = "branch -l --all -v --format=\"%(refname)%00%(objectname)%00%(HEAD)%00%(upstream)%00%(upstream:trackshort)\""; Args = "branch -l --all -v --format=\"%(refname)%00%(committerdate:unix)%00%(objectname)%00%(HEAD)%00%(upstream)%00%(upstream:trackshort)\"";
} }
public List<Models.Branch> Result() public List<Models.Branch> Result()
@ -49,7 +49,7 @@ namespace SourceGit.Commands
private Models.Branch ParseLine(string line) private Models.Branch ParseLine(string line)
{ {
var parts = line.Split('\0'); var parts = line.Split('\0');
if (parts.Length != 5) if (parts.Length != 6)
return null; return null;
var branch = new Models.Branch(); var branch = new Models.Branch();
@ -83,12 +83,13 @@ namespace SourceGit.Commands
} }
branch.FullName = refName; branch.FullName = refName;
branch.Head = parts[1]; branch.CommitterDate = ulong.Parse(parts[1]);
branch.IsCurrent = parts[2] == "*"; branch.Head = parts[2];
branch.Upstream = parts[3]; branch.IsCurrent = parts[3] == "*";
branch.Upstream = parts[4];
branch.IsUpstreamGone = false; branch.IsUpstreamGone = false;
if (branch.IsLocal && !string.IsNullOrEmpty(parts[4]) && !parts[4].Equals("=", StringComparison.Ordinal)) if (branch.IsLocal && !string.IsNullOrEmpty(parts[5]) && !parts[5].Equals("=", StringComparison.Ordinal))
branch.TrackStatus = new QueryTrackStatus(WorkingDirectory, branch.Name, branch.Upstream).Result(); branch.TrackStatus = new QueryTrackStatus(WorkingDirectory, branch.Name, branch.Upstream).Result();
else else
branch.TrackStatus = new Models.BranchTrackStatus(); branch.TrackStatus = new Models.BranchTrackStatus();

View file

@ -23,10 +23,17 @@ namespace SourceGit.Models
} }
} }
public enum BranchSortMode
{
Name = 0,
CommitterDate,
}
public class Branch public class Branch
{ {
public string Name { get; set; } public string Name { get; set; }
public string FullName { get; set; } public string FullName { get; set; }
public ulong CommitterDate { get; set; }
public string Head { get; set; } public string Head { get; set; }
public bool IsLocal { get; set; } public bool IsLocal { get; set; }
public bool IsCurrent { get; set; } public bool IsCurrent { get; set; }

View file

@ -38,6 +38,18 @@ namespace SourceGit.Models
set; set;
} = false; } = false;
public BranchSortMode LocalBranchSortMode
{
get;
set;
} = BranchSortMode.Name;
public BranchSortMode RemoteBranchSortMode
{
get;
set;
} = BranchSortMode.Name;
public TagSortMode TagSortMode public TagSortMode TagSortMode
{ {
get; get;

View file

@ -572,6 +572,9 @@
<x:String x:Key="Text.RenameBranch.Target" xml:space="preserve">Branch:</x:String> <x:String x:Key="Text.RenameBranch.Target" xml:space="preserve">Branch:</x:String>
<x:String x:Key="Text.Repository.Abort" xml:space="preserve">ABORT</x:String> <x:String x:Key="Text.Repository.Abort" xml:space="preserve">ABORT</x:String>
<x:String x:Key="Text.Repository.AutoFetching" xml:space="preserve">Auto fetching changes from remotes...</x:String> <x:String x:Key="Text.Repository.AutoFetching" xml:space="preserve">Auto fetching changes from remotes...</x:String>
<x:String x:Key="Text.Repository.BranchSort" xml:space="preserve">Sort</x:String>
<x:String x:Key="Text.Repository.BranchSort.ByCommitterDate" xml:space="preserve">By Committer Date</x:String>
<x:String x:Key="Text.Repository.BranchSort.ByName" xml:space="preserve">By Name</x:String>
<x:String x:Key="Text.Repository.Clean" xml:space="preserve">Cleanup(GC &amp; Prune)</x:String> <x:String x:Key="Text.Repository.Clean" xml:space="preserve">Cleanup(GC &amp; Prune)</x:String>
<x:String x:Key="Text.Repository.CleanTips" xml:space="preserve">Run `git gc` command for this repository.</x:String> <x:String x:Key="Text.Repository.CleanTips" xml:space="preserve">Run `git gc` command for this repository.</x:String>
<x:String x:Key="Text.Repository.ClearAllCommitsFilter" xml:space="preserve">Clear all</x:String> <x:String x:Key="Text.Repository.ClearAllCommitsFilter" xml:space="preserve">Clear all</x:String>
@ -602,7 +605,7 @@
<x:String x:Key="Text.Repository.OpenWithExternalTools" xml:space="preserve">Open in External Tools</x:String> <x:String x:Key="Text.Repository.OpenWithExternalTools" xml:space="preserve">Open in External Tools</x:String>
<x:String x:Key="Text.Repository.Refresh" xml:space="preserve">Refresh</x:String> <x:String x:Key="Text.Repository.Refresh" xml:space="preserve">Refresh</x:String>
<x:String x:Key="Text.Repository.Remotes" xml:space="preserve">REMOTES</x:String> <x:String x:Key="Text.Repository.Remotes" xml:space="preserve">REMOTES</x:String>
<x:String x:Key="Text.Repository.Remotes.Add" xml:space="preserve">ADD REMOTE</x:String> <x:String x:Key="Text.Repository.Remotes.Add" xml:space="preserve">Add Remote</x:String>
<x:String x:Key="Text.Repository.Search" xml:space="preserve">Search Commit</x:String> <x:String x:Key="Text.Repository.Search" xml:space="preserve">Search Commit</x:String>
<x:String x:Key="Text.Repository.Search.ByAuthor" xml:space="preserve">Author</x:String> <x:String x:Key="Text.Repository.Search.ByAuthor" xml:space="preserve">Author</x:String>
<x:String x:Key="Text.Repository.Search.ByCommitter" xml:space="preserve">Committer</x:String> <x:String x:Key="Text.Repository.Search.ByCommitter" xml:space="preserve">Committer</x:String>
@ -615,10 +618,10 @@
<x:String x:Key="Text.Repository.Skip" xml:space="preserve">SKIP</x:String> <x:String x:Key="Text.Repository.Skip" xml:space="preserve">SKIP</x:String>
<x:String x:Key="Text.Repository.Statistics" xml:space="preserve">Statistics</x:String> <x:String x:Key="Text.Repository.Statistics" xml:space="preserve">Statistics</x:String>
<x:String x:Key="Text.Repository.Submodules" xml:space="preserve">SUBMODULES</x:String> <x:String x:Key="Text.Repository.Submodules" xml:space="preserve">SUBMODULES</x:String>
<x:String x:Key="Text.Repository.Submodules.Add" xml:space="preserve">ADD SUBMODULE</x:String> <x:String x:Key="Text.Repository.Submodules.Add" xml:space="preserve">Add Submodule</x:String>
<x:String x:Key="Text.Repository.Submodules.Update" xml:space="preserve">UPDATE SUBMODULE</x:String> <x:String x:Key="Text.Repository.Submodules.Update" xml:space="preserve">Update Submodule</x:String>
<x:String x:Key="Text.Repository.Tags" xml:space="preserve">TAGS</x:String> <x:String x:Key="Text.Repository.Tags" xml:space="preserve">TAGS</x:String>
<x:String x:Key="Text.Repository.Tags.Add" xml:space="preserve">NEW TAG</x:String> <x:String x:Key="Text.Repository.Tags.Add" xml:space="preserve">New Tag</x:String>
<x:String x:Key="Text.Repository.Tags.OrderByCreatorDate" xml:space="preserve">By Creator Date</x:String> <x:String x:Key="Text.Repository.Tags.OrderByCreatorDate" xml:space="preserve">By Creator Date</x:String>
<x:String x:Key="Text.Repository.Tags.OrderByNameAsc" xml:space="preserve">By Name (Ascending)</x:String> <x:String x:Key="Text.Repository.Tags.OrderByNameAsc" xml:space="preserve">By Name (Ascending)</x:String>
<x:String x:Key="Text.Repository.Tags.OrderByNameDes" xml:space="preserve">By Name (Descending)</x:String> <x:String x:Key="Text.Repository.Tags.OrderByNameDes" xml:space="preserve">By Name (Descending)</x:String>
@ -628,8 +631,8 @@
<x:String x:Key="Text.Repository.ViewLogs" xml:space="preserve">View Logs</x:String> <x:String x:Key="Text.Repository.ViewLogs" xml:space="preserve">View Logs</x:String>
<x:String x:Key="Text.Repository.Visit" xml:space="preserve">Visit '{0}' in Browser</x:String> <x:String x:Key="Text.Repository.Visit" xml:space="preserve">Visit '{0}' in Browser</x:String>
<x:String x:Key="Text.Repository.Worktrees" xml:space="preserve">WORKTREES</x:String> <x:String x:Key="Text.Repository.Worktrees" xml:space="preserve">WORKTREES</x:String>
<x:String x:Key="Text.Repository.Worktrees.Add" xml:space="preserve">ADD WORKTREE</x:String> <x:String x:Key="Text.Repository.Worktrees.Add" xml:space="preserve">Add Worktree</x:String>
<x:String x:Key="Text.Repository.Worktrees.Prune" xml:space="preserve">PRUNE</x:String> <x:String x:Key="Text.Repository.Worktrees.Prune" xml:space="preserve">Prune</x:String>
<x:String x:Key="Text.RepositoryURL" xml:space="preserve">Git Repository URL</x:String> <x:String x:Key="Text.RepositoryURL" xml:space="preserve">Git Repository URL</x:String>
<x:String x:Key="Text.Reset" xml:space="preserve">Reset Current Branch To Revision</x:String> <x:String x:Key="Text.Reset" xml:space="preserve">Reset Current Branch To Revision</x:String>
<x:String x:Key="Text.Reset.Mode" xml:space="preserve">Reset Mode:</x:String> <x:String x:Key="Text.Reset.Mode" xml:space="preserve">Reset Mode:</x:String>

View file

@ -576,6 +576,9 @@
<x:String x:Key="Text.RenameBranch.Target" xml:space="preserve">分支 </x:String> <x:String x:Key="Text.RenameBranch.Target" xml:space="preserve">分支 </x:String>
<x:String x:Key="Text.Repository.Abort" xml:space="preserve">终止合并</x:String> <x:String x:Key="Text.Repository.Abort" xml:space="preserve">终止合并</x:String>
<x:String x:Key="Text.Repository.AutoFetching" xml:space="preserve">自动拉取远端变更中...</x:String> <x:String x:Key="Text.Repository.AutoFetching" xml:space="preserve">自动拉取远端变更中...</x:String>
<x:String x:Key="Text.Repository.BranchSort" xml:space="preserve">排序方式</x:String>
<x:String x:Key="Text.Repository.BranchSort.ByCommitterDate" xml:space="preserve">按提交时间</x:String>
<x:String x:Key="Text.Repository.BranchSort.ByName" xml:space="preserve">按名称</x:String>
<x:String x:Key="Text.Repository.Clean" xml:space="preserve">清理本仓库(GC)</x:String> <x:String x:Key="Text.Repository.Clean" xml:space="preserve">清理本仓库(GC)</x:String>
<x:String x:Key="Text.Repository.CleanTips" xml:space="preserve">本操作将执行`git gc`命令。</x:String> <x:String x:Key="Text.Repository.CleanTips" xml:space="preserve">本操作将执行`git gc`命令。</x:String>
<x:String x:Key="Text.Repository.ClearAllCommitsFilter" xml:space="preserve">清空过滤规则</x:String> <x:String x:Key="Text.Repository.ClearAllCommitsFilter" xml:space="preserve">清空过滤规则</x:String>

View file

@ -576,6 +576,9 @@
<x:String x:Key="Text.RenameBranch.Target" xml:space="preserve">分支:</x:String> <x:String x:Key="Text.RenameBranch.Target" xml:space="preserve">分支:</x:String>
<x:String x:Key="Text.Repository.Abort" xml:space="preserve">中止</x:String> <x:String x:Key="Text.Repository.Abort" xml:space="preserve">中止</x:String>
<x:String x:Key="Text.Repository.AutoFetching" xml:space="preserve">自動提取遠端變更中...</x:String> <x:String x:Key="Text.Repository.AutoFetching" xml:space="preserve">自動提取遠端變更中...</x:String>
<x:String x:Key="Text.Repository.BranchSort" xml:space="preserve">排序</x:String>
<x:String x:Key="Text.Repository.BranchSort.ByCommitterDate" xml:space="preserve">依建立時間</x:String>
<x:String x:Key="Text.Repository.BranchSort.ByName" xml:space="preserve">依名稱升序</x:String>
<x:String x:Key="Text.Repository.Clean" xml:space="preserve">清理本存放庫 (GC)</x:String> <x:String x:Key="Text.Repository.Clean" xml:space="preserve">清理本存放庫 (GC)</x:String>
<x:String x:Key="Text.Repository.CleanTips" xml:space="preserve">本操作將執行 `git gc` 命令。</x:String> <x:String x:Key="Text.Repository.CleanTips" xml:space="preserve">本操作將執行 `git gc` 命令。</x:String>
<x:String x:Key="Text.Repository.ClearAllCommitsFilter" xml:space="preserve">清空篩選規則</x:String> <x:String x:Key="Text.Repository.ClearAllCommitsFilter" xml:space="preserve">清空篩選規則</x:String>

View file

@ -10,6 +10,7 @@ namespace SourceGit.ViewModels
public string Name { get; private set; } = string.Empty; public string Name { get; private set; } = string.Empty;
public string Path { get; private set; } = string.Empty; public string Path { get; private set; } = string.Empty;
public object Backend { get; private set; } = null; public object Backend { get; private set; } = null;
public ulong TimeToSort { get; private set; } = 0;
public int Depth { get; set; } = 0; public int Depth { get; set; } = 0;
public bool IsSelected { get; set; } = false; public bool IsSelected { get; set; } = false;
public List<BranchTreeNode> Children { get; private set; } = new List<BranchTreeNode>(); public List<BranchTreeNode> Children { get; private set; } = new List<BranchTreeNode>();
@ -62,6 +63,12 @@ namespace SourceGit.ViewModels
public List<BranchTreeNode> Remotes => _remotes; public List<BranchTreeNode> Remotes => _remotes;
public List<string> InvalidExpandedNodes => _invalidExpandedNodes; public List<string> InvalidExpandedNodes => _invalidExpandedNodes;
public Builder(Models.BranchSortMode localSortMode, Models.BranchSortMode remoteSortMode)
{
_localSortMode = localSortMode;
_remoteSortMode = remoteSortMode;
}
public void SetExpandedNodes(List<string> expanded) public void SetExpandedNodes(List<string> expanded)
{ {
foreach (var node in expanded) foreach (var node in expanded)
@ -72,6 +79,7 @@ namespace SourceGit.ViewModels
{ {
var folders = new Dictionary<string, BranchTreeNode>(); var folders = new Dictionary<string, BranchTreeNode>();
var fakeRemoteTime = (ulong)remotes.Count;
foreach (var remote in remotes) foreach (var remote in remotes)
{ {
var path = $"refs/remotes/{remote.Name}"; var path = $"refs/remotes/{remote.Name}";
@ -81,8 +89,10 @@ namespace SourceGit.ViewModels
Path = path, Path = path,
Backend = remote, Backend = remote,
IsExpanded = bForceExpanded || _expanded.Contains(path), IsExpanded = bForceExpanded || _expanded.Contains(path),
TimeToSort = fakeRemoteTime,
}; };
fakeRemoteTime--;
folders.Add(path, node); folders.Add(path, node);
_remotes.Add(node); _remotes.Add(node);
} }
@ -108,8 +118,26 @@ namespace SourceGit.ViewModels
} }
folders.Clear(); folders.Clear();
SortNodes(_locals);
SortNodes(_remotes); if (_localSortMode == Models.BranchSortMode.Name)
{
SortNodesByName(_locals);
}
else
{
SetTimeToSortRecusive(_locals);
SortNodesByTime(_locals);
}
if (_remoteSortMode == Models.BranchSortMode.Name)
{
SortNodesByName(_remotes);
}
else
{
SetTimeToSortRecusive(_remotes);
SortNodesByTime(_remotes);
}
} }
private void MakeBranchNode(Models.Branch branch, List<BranchTreeNode> roots, Dictionary<string, BranchTreeNode> folders, string prefix, bool bForceExpanded) private void MakeBranchNode(Models.Branch branch, List<BranchTreeNode> roots, Dictionary<string, BranchTreeNode> folders, string prefix, bool bForceExpanded)
@ -124,6 +152,7 @@ namespace SourceGit.ViewModels
Path = fullpath, Path = fullpath,
Backend = branch, Backend = branch,
IsExpanded = false, IsExpanded = false,
TimeToSort = branch.CommitterDate,
}); });
return; return;
} }
@ -175,10 +204,11 @@ namespace SourceGit.ViewModels
Path = fullpath, Path = fullpath,
Backend = branch, Backend = branch,
IsExpanded = false, IsExpanded = false,
TimeToSort = branch.CommitterDate,
}); });
} }
private void SortNodes(List<BranchTreeNode> nodes) private void SortNodesByName(List<BranchTreeNode> nodes)
{ {
nodes.Sort((l, r) => nodes.Sort((l, r) =>
{ {
@ -192,9 +222,61 @@ namespace SourceGit.ViewModels
}); });
foreach (var node in nodes) foreach (var node in nodes)
SortNodes(node.Children); SortNodesByName(node.Children);
} }
private void SortNodesByTime(List<BranchTreeNode> nodes)
{
nodes.Sort((l, r) =>
{
if (l.Backend is Models.Branch { IsDetachedHead: true })
return -1;
if (l.Backend is Models.Branch)
{
if (r.Backend is Models.Branch)
return r.TimeToSort == l.TimeToSort ? Models.NumericSort.Compare(l.Name, r.Name) : r.TimeToSort.CompareTo(l.TimeToSort);
else
return 1;
}
if (r.Backend is Models.Branch)
return -1;
if (r.TimeToSort == l.TimeToSort)
return Models.NumericSort.Compare(l.Name, r.Name);
return r.TimeToSort.CompareTo(l.TimeToSort);
});
foreach (var node in nodes)
SortNodesByTime(node.Children);
}
private ulong SetTimeToSortRecusive(List<BranchTreeNode> nodes)
{
var recent = (ulong)0;
foreach (var node in nodes)
{
if (node.Backend is Models.Branch)
{
recent = Math.Max(recent, node.TimeToSort);
continue;
}
var time = SetTimeToSortRecusive(node.Children);
recent = Math.Max(recent, time);
if (node.Backend is not Models.Remote)
node.TimeToSort = time;
}
return recent;
}
private readonly Models.BranchSortMode _localSortMode = Models.BranchSortMode.Name;
private readonly Models.BranchSortMode _remoteSortMode = Models.BranchSortMode.Name;
private readonly List<BranchTreeNode> _locals = new List<BranchTreeNode>(); private readonly List<BranchTreeNode> _locals = new List<BranchTreeNode>();
private readonly List<BranchTreeNode> _remotes = new List<BranchTreeNode>(); private readonly List<BranchTreeNode> _remotes = new List<BranchTreeNode>();
private readonly List<string> _invalidExpandedNodes = new List<string>(); private readonly List<string> _invalidExpandedNodes = new List<string>();

View file

@ -2229,6 +2229,51 @@ namespace SourceGit.ViewModels
return menu; return menu;
} }
public ContextMenu CreateContextMenuForBranchSortMode(bool local)
{
var mode = local ? _settings.LocalBranchSortMode : _settings.RemoteBranchSortMode;
var changeMode = new Action<Models.BranchSortMode>(m =>
{
if (local)
_settings.LocalBranchSortMode = m;
else
_settings.RemoteBranchSortMode = m;
var builder = BuildBranchTree(_branches, _remotes);
LocalBranchTrees = builder.Locals;
RemoteBranchTrees = builder.Remotes;
});
var byNameAsc = new MenuItem();
byNameAsc.Header = App.Text("Repository.BranchSort.ByName");
if (mode == Models.BranchSortMode.Name)
byNameAsc.Icon = App.CreateMenuIcon("Icons.Check");
byNameAsc.Click += (_, ev) =>
{
if (mode != Models.BranchSortMode.Name)
changeMode(Models.BranchSortMode.Name);
ev.Handled = true;
};
var byCommitterDate = new MenuItem();
byCommitterDate.Header = App.Text("Repository.BranchSort.ByCommitterDate");
if (mode == Models.BranchSortMode.CommitterDate)
byCommitterDate.Icon = App.CreateMenuIcon("Icons.Check");
byCommitterDate.Click += (_, ev) =>
{
if (mode != Models.BranchSortMode.CommitterDate)
changeMode(Models.BranchSortMode.CommitterDate);
ev.Handled = true;
};
var menu = new ContextMenu();
menu.Items.Add(byNameAsc);
menu.Items.Add(byCommitterDate);
return menu;
}
public ContextMenu CreateContextMenuForTagSortMode() public ContextMenu CreateContextMenuForTagSortMode()
{ {
var mode = _settings.TagSortMode; var mode = _settings.TagSortMode;
@ -2398,7 +2443,7 @@ namespace SourceGit.ViewModels
private BranchTreeNode.Builder BuildBranchTree(List<Models.Branch> branches, List<Models.Remote> remotes) private BranchTreeNode.Builder BuildBranchTree(List<Models.Branch> branches, List<Models.Remote> remotes)
{ {
var builder = new BranchTreeNode.Builder(); var builder = new BranchTreeNode.Builder(_settings.LocalBranchSortMode, _settings.RemoteBranchSortMode);
if (string.IsNullOrEmpty(_filter)) if (string.IsNullOrEmpty(_filter))
{ {
builder.SetExpandedNodes(_settings.ExpandedBranchNodesInSideBar); builder.SetExpandedNodes(_settings.ExpandedBranchNodesInSideBar);

View file

@ -202,9 +202,20 @@
<Grid Grid.Row="2" x:Name="LeftSidebarGroups" Margin="0,4,0,0" RowDefinitions="28,Auto,28,Auto,28,Auto,28,Auto,28,Auto" SizeChanged="OnLeftSidebarSizeChanged"> <Grid Grid.Row="2" x:Name="LeftSidebarGroups" Margin="0,4,0,0" RowDefinitions="28,Auto,28,Auto,28,Auto,28,Auto,28,Auto" SizeChanged="OnLeftSidebarSizeChanged">
<!-- Local Branches --> <!-- Local Branches -->
<ToggleButton Grid.Row="0" Classes="group_expander" IsChecked="{Binding IsLocalBranchGroupExpanded, Mode=TwoWay}"> <ToggleButton Grid.Row="0" Classes="group_expander" IsChecked="{Binding IsLocalBranchGroupExpanded, Mode=TwoWay}">
<Grid ColumnDefinitions="16,*"> <Grid ColumnDefinitions="16,*,Auto,Auto">
<Path Grid.Column="0" Width="11" Height="11" HorizontalAlignment="Left" Data="{StaticResource Icons.Local}" Fill="{DynamicResource Brush.FG2}"/> <Path Grid.Column="0" Width="11" Height="11" HorizontalAlignment="Left" Data="{StaticResource Icons.Local}" Fill="{DynamicResource Brush.FG2}"/>
<TextBlock Grid.Column="1" Classes="group_header_label" Margin="0" Text="{DynamicResource Text.Repository.LocalBranches}"/> <TextBlock Grid.Column="1" Classes="group_header_label" Margin="0" Text="{DynamicResource Text.Repository.LocalBranches}"/>
<Button Grid.Column="2"
Classes="icon_button"
Width="14"
Margin="8,0,0,0"
Click="OnOpenSortLocalBranchMenu"
ToolTip.Tip="{DynamicResource Text.Repository.BranchSort}">
<Path Width="12" Height="12" Data="{StaticResource Icons.Order}"/>
</Button>
<Button Grid.Column="3" Classes="icon_button" Width="14" Margin="8,0" Command="{Binding CreateNewBranch}" ToolTip.Tip="{DynamicResource Text.Repository.NewBranch}">
<Path Width="12" Height="12" Data="{StaticResource Icons.Branch.Add}"/>
</Button>
</Grid> </Grid>
</ToggleButton> </ToggleButton>
<v:BranchTree Grid.Row="1" <v:BranchTree Grid.Row="1"
@ -218,10 +229,18 @@
<!-- Remotes --> <!-- Remotes -->
<ToggleButton Grid.Row="2" Classes="group_expander" IsChecked="{Binding IsRemoteGroupExpanded, Mode=TwoWay}"> <ToggleButton Grid.Row="2" Classes="group_expander" IsChecked="{Binding IsRemoteGroupExpanded, Mode=TwoWay}">
<Grid ColumnDefinitions="16,*,Auto"> <Grid ColumnDefinitions="16,*,Auto,Auto">
<Path Grid.Column="0" Width="12" Height="12" HorizontalAlignment="Left" Data="{StaticResource Icons.Remotes}" Fill="{DynamicResource Brush.FG2}"/> <Path Grid.Column="0" Width="12" Height="12" HorizontalAlignment="Left" Data="{StaticResource Icons.Remotes}" Fill="{DynamicResource Brush.FG2}"/>
<TextBlock Grid.Column="1" Classes="group_header_label" Margin="0" Text="{DynamicResource Text.Repository.Remotes}"/> <TextBlock Grid.Column="1" Classes="group_header_label" Margin="0" Text="{DynamicResource Text.Repository.Remotes}"/>
<Button Grid.Column="2" Classes="icon_button" Width="14" Margin="8,0" Command="{Binding AddRemote}" ToolTip.Tip="{DynamicResource Text.Repository.Remotes.Add}"> <Button Grid.Column="2"
Classes="icon_button"
Width="14"
Margin="8,0,0,0"
Click="OnOpenSortRemoteBranchMenu"
ToolTip.Tip="{DynamicResource Text.Repository.BranchSort}">
<Path Width="12" Height="12" Data="{StaticResource Icons.Order}"/>
</Button>
<Button Grid.Column="3" Classes="icon_button" Width="14" Margin="8,0" Command="{Binding AddRemote}" ToolTip.Tip="{DynamicResource Text.Repository.Remotes.Add}">
<Path Width="12" Height="12" Data="{StaticResource Icons.Remote.Add}"/> <Path Width="12" Height="12" Data="{StaticResource Icons.Remote.Add}"/>
</Button> </Button>
</Grid> </Grid>

View file

@ -403,6 +403,28 @@ namespace SourceGit.Views
e.Handled = true; e.Handled = true;
} }
private void OnOpenSortLocalBranchMenu(object sender, RoutedEventArgs e)
{
if (sender is Button button && DataContext is ViewModels.Repository repo)
{
var menu = repo.CreateContextMenuForBranchSortMode(true);
menu?.Open(button);
}
e.Handled = true;
}
private void OnOpenSortRemoteBranchMenu(object sender, RoutedEventArgs e)
{
if (sender is Button button && DataContext is ViewModels.Repository repo)
{
var menu = repo.CreateContextMenuForBranchSortMode(false);
menu?.Open(button);
}
e.Handled = true;
}
private void OnOpenSortTagMenu(object sender, RoutedEventArgs e) private void OnOpenSortTagMenu(object sender, RoutedEventArgs e)
{ {
if (sender is Button button && DataContext is ViewModels.Repository repo) if (sender is Button button && DataContext is ViewModels.Repository repo)