diff --git a/README.md b/README.md
index e2cf9da2..1f69cccf 100644
--- a/README.md
+++ b/README.md
@@ -39,8 +39,8 @@ This software creates a folder `$"{System.Environment.SpecialFolder.ApplicationD
| OS | PATH |
| --- | --- |
| Windows | `C:\Users\USER_NAME\AppData\Roaming\SourceGit` |
-| Linux | `/home/USER_NAME/.config/SourceGit` |
-| macOS | `/Users/USER_NAME/.config/SourceGit` |
+| Linux | `${HOME}/.config/SourceGit` |
+| macOS | `${HOME}/Library/Application Support/SourceGit` |
For **Windows** users:
diff --git a/VERSION b/VERSION
index 9c57ca32..90c5b336 100644
--- a/VERSION
+++ b/VERSION
@@ -1 +1 @@
-8.12
\ No newline at end of file
+8.13
\ No newline at end of file
diff --git a/build/build.osx.command b/build/build.osx.command
old mode 100644
new mode 100755
diff --git a/build/resources/rpm/SPECS/build.spec b/build/resources/rpm/SPECS/build.spec
index 2e6875a6..6025acac 100644
--- a/build/resources/rpm/SPECS/build.spec
+++ b/build/resources/rpm/SPECS/build.spec
@@ -24,7 +24,7 @@ chmod 755 -R $RPM_BUILD_ROOT
%files
/opt
-/usr/bin
+%attr(555,root,root)/usr/bin
/usr/share
%changelog
diff --git a/screenshots/theme_dark.png b/screenshots/theme_dark.png
index 4a9646dc..cfd94d8f 100644
Binary files a/screenshots/theme_dark.png and b/screenshots/theme_dark.png differ
diff --git a/screenshots/theme_light.png b/screenshots/theme_light.png
index 669b4d18..c4044d49 100644
Binary files a/screenshots/theme_light.png and b/screenshots/theme_light.png differ
diff --git a/src/App.axaml b/src/App.axaml
index ffb634c7..7129fd7e 100644
--- a/src/App.axaml
+++ b/src/App.axaml
@@ -26,12 +26,12 @@
-
+
-
+
diff --git a/src/App.axaml.cs b/src/App.axaml.cs
index ed92b621..151c6e92 100644
--- a/src/App.axaml.cs
+++ b/src/App.axaml.cs
@@ -266,6 +266,7 @@ namespace SourceGit
if (ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktop)
{
BindingPlugins.DataValidators.RemoveAt(0);
+ Native.OS.SetupEnternalTools();
var launcher = new Views.Launcher();
_notificationReceiver = launcher;
diff --git a/src/Models/ExternalTool.cs b/src/Models/ExternalTool.cs
index 1489e1cf..98351cd6 100644
--- a/src/Models/ExternalTool.cs
+++ b/src/Models/ExternalTool.cs
@@ -12,34 +12,23 @@ namespace SourceGit.Models
{
public class ExternalTool
{
- public string Name { get; set; } = string.Empty;
- public string Icon { get; set; } = string.Empty;
- public string Executable { get; set; } = string.Empty;
- public string OpenCmdArgs { get; set; } = string.Empty;
+ public string Name { get; private set; } = string.Empty;
+ public string Executable { get; private set; } = string.Empty;
+ public string OpenCmdArgs { get; private set; } = string.Empty;
+ public Bitmap IconImage { get; private set; } = null;
- public Bitmap IconImage
+ public ExternalTool(string name, string icon, string executable, string openCmdArgs)
{
- get
+ Name = name;
+ Executable = executable;
+ OpenCmdArgs = openCmdArgs;
+
+ try
{
- if (_isFirstTimeGetIcon)
- {
- _isFirstTimeGetIcon = false;
-
- if (!string.IsNullOrWhiteSpace(Icon))
- {
- try
- {
- var icon = AssetLoader.Open(new Uri($"avares://SourceGit/Resources/ExternalToolIcons/{Icon}.png", UriKind.RelativeOrAbsolute));
- _iconImage = new Bitmap(icon);
- }
- catch
- {
- }
- }
- }
-
- return _iconImage;
+ var asset = AssetLoader.Open(new Uri($"avares://SourceGit/Resources/ExternalToolIcons/{icon}.png", UriKind.RelativeOrAbsolute));
+ IconImage = new Bitmap(asset);
}
+ catch { }
}
public void Open(string repo)
@@ -52,9 +41,6 @@ namespace SourceGit.Models
UseShellExecute = false,
});
}
-
- private bool _isFirstTimeGetIcon = true;
- private Bitmap _iconImage = null;
}
public class JetBrainsState
@@ -107,13 +93,7 @@ namespace SourceGit.Models
return;
}
- Founded.Add(new ExternalTool
- {
- Name = name,
- Icon = icon,
- OpenCmdArgs = args,
- Executable = path
- });
+ Founded.Add(new ExternalTool(name, icon, path, args));
}
public void VSCode(Func platformFinder)
@@ -154,13 +134,11 @@ namespace SourceGit.Models
if (exclude.Contains(tool.ToolId.ToLowerInvariant()))
continue;
- Founded.Add(new ExternalTool
- {
- Name = $"{tool.DisplayName} {tool.DisplayVersion}",
- Icon = supported_icons.Contains(tool.ProductCode) ? $"JetBrains/{tool.ProductCode}" : $"JetBrains/JB",
- OpenCmdArgs = "\"{0}\"",
- Executable = Path.Combine(tool.InstallLocation, tool.LaunchCommand),
- });
+ Founded.Add(new ExternalTool(
+ $"{tool.DisplayName} {tool.DisplayVersion}",
+ supported_icons.Contains(tool.ProductCode) ? $"JetBrains/{tool.ProductCode}" : "JetBrains/JB",
+ Path.Combine(tool.InstallLocation, tool.LaunchCommand),
+ "\"{0}\""));
}
}
}
diff --git a/src/Models/User.cs b/src/Models/User.cs
index ba1bebd6..5a93e135 100644
--- a/src/Models/User.cs
+++ b/src/Models/User.cs
@@ -25,20 +25,14 @@ namespace SourceGit.Models
public static User FindOrAdd(string data)
{
- if (_caches.TryGetValue(data, out var value))
+ return _caches.GetOrAdd(data, key =>
{
- return value;
- }
- else
- {
- var nameEndIdx = data.IndexOf('<', System.StringComparison.Ordinal);
- var name = nameEndIdx >= 2 ? data.Substring(0, nameEndIdx - 1) : string.Empty;
- var email = data.Substring(nameEndIdx + 1);
+ var nameEndIdx = key.IndexOf('<', System.StringComparison.Ordinal);
+ var name = nameEndIdx >= 2 ? key.Substring(0, nameEndIdx - 1) : string.Empty;
+ var email = key.Substring(nameEndIdx + 1);
- User user = new User() { Name = name, Email = email };
- _caches.TryAdd(data, user);
- return user;
- }
+ return new User() { Name = name, Email = email };
+ });
}
private static ConcurrentDictionary _caches = new ConcurrentDictionary();
diff --git a/src/Native/Linux.cs b/src/Native/Linux.cs
index b34943a4..5474c5c5 100644
--- a/src/Native/Linux.cs
+++ b/src/Native/Linux.cs
@@ -43,6 +43,11 @@ namespace SourceGit.Native
DefaultFamilyName = "fonts:SourceGit#JetBrains Mono",
});
+ builder.With(new X11PlatformOptions()
+ {
+ EnableIme = true,
+ });
+
// Free-desktop file picker has an extra black background panel.
builder.UseManagedSystemDialogs();
}
diff --git a/src/Native/OS.cs b/src/Native/OS.cs
index c3a94037..46a49e6a 100644
--- a/src/Native/OS.cs
+++ b/src/Native/OS.cs
@@ -41,8 +41,6 @@ namespace SourceGit.Native
{
throw new Exception("Platform unsupported!!!");
}
-
- ExternalTools = _backend.FindExternalTools();
}
public static Models.Shell GetShell()
@@ -77,6 +75,11 @@ namespace SourceGit.Native
_backend.SetupApp(builder);
}
+ public static void SetupEnternalTools()
+ {
+ ExternalTools = _backend.FindExternalTools();
+ }
+
public static string FindGitExecutable()
{
return _backend.FindGitExecutable();
diff --git a/src/Resources/Locales/en_US.axaml b/src/Resources/Locales/en_US.axaml
index b095a6a9..bf7d660e 100644
--- a/src/Resources/Locales/en_US.axaml
+++ b/src/Resources/Locales/en_US.axaml
@@ -210,8 +210,7 @@
SEARCH SHA/SUBJECT/AUTHOR. PRESS ENTER TO SEARCH, ESC TO QUIT
CLEAR
SELECTED {0} COMMITS
- HotKeys
- About HotKeys
+ Keyboard Shortcuts Reference
GLOBAL
Cancel current popup
Close current page
@@ -343,6 +342,7 @@
Configure this repository
CONTINUE
Open In File Browser
+ Filter Branches
LOCAL BRANCHES
Navigate To HEAD
Create Branch
diff --git a/src/Resources/Locales/zh_CN.axaml b/src/Resources/Locales/zh_CN.axaml
index 1c972de5..9f464b79 100644
--- a/src/Resources/Locales/zh_CN.axaml
+++ b/src/Resources/Locales/zh_CN.axaml
@@ -210,8 +210,7 @@
查询提交指纹、信息、作者。回车键开始,ESC键取消
清空
已选中 {0} 项提交
- 快捷键
- 显示快捷键
+ 快捷键参考
全局快捷键
取消弹出面板
关闭当前页面
@@ -343,6 +342,7 @@
配置本仓库
下一步
在文件浏览器中打开
+ 过滤显示分支
本地分支
定位HEAD
新建分支
diff --git a/src/Models/BranchTreeNode.cs b/src/ViewModels/BranchTreeNode.cs
similarity index 67%
rename from src/Models/BranchTreeNode.cs
rename to src/ViewModels/BranchTreeNode.cs
index 66296519..b6b88b5d 100644
--- a/src/Models/BranchTreeNode.cs
+++ b/src/ViewModels/BranchTreeNode.cs
@@ -1,9 +1,9 @@
using System;
using System.Collections.Generic;
-
+using System.IO;
using Avalonia.Collections;
-namespace SourceGit.Models
+namespace SourceGit.ViewModels
{
public enum BranchTreeNodeType
{
@@ -23,12 +23,12 @@ namespace SourceGit.Models
public bool IsUpstreamTrackStatusVisible
{
- get => IsBranch && !string.IsNullOrEmpty((Backend as Branch).UpstreamTrackStatus);
+ get => IsBranch && !string.IsNullOrEmpty((Backend as Models.Branch).UpstreamTrackStatus);
}
public string UpstreamTrackStatus
{
- get => Type == BranchTreeNodeType.Branch ? (Backend as Branch).UpstreamTrackStatus : "";
+ get => Type == BranchTreeNodeType.Branch ? (Backend as Models.Branch).UpstreamTrackStatus : "";
}
public bool IsRemote
@@ -48,7 +48,7 @@ namespace SourceGit.Models
public bool IsCurrent
{
- get => IsBranch && (Backend as Branch).IsCurrent;
+ get => IsBranch && (Backend as Models.Branch).IsCurrent;
}
public class Builder
@@ -56,8 +56,10 @@ namespace SourceGit.Models
public List Locals => _locals;
public List Remotes => _remotes;
- public void Run(List branches, List remotes)
+ public void Run(List branches, List remotes, bool bForceExpanded)
{
+ var folders = new Dictionary();
+
foreach (var remote in remotes)
{
var path = $"remote/{remote.Name}";
@@ -66,10 +68,10 @@ namespace SourceGit.Models
Name = remote.Name,
Type = BranchTreeNodeType.Remote,
Backend = remote,
- IsExpanded = _expanded.Contains(path),
+ IsExpanded = bForceExpanded || _expanded.Contains(path),
};
- _maps.Add(path, node);
+ folders.Add(path, node);
_remotes.Add(node);
}
@@ -78,16 +80,17 @@ namespace SourceGit.Models
var isFiltered = _filters.Contains(branch.FullName);
if (branch.IsLocal)
{
- MakeBranchNode(branch, _locals, "local", isFiltered);
+ MakeBranchNode(branch, _locals, folders, "local", isFiltered, bForceExpanded);
}
else
{
var remote = _remotes.Find(x => x.Name == branch.Remote);
if (remote != null)
- MakeBranchNode(branch, remote.Children, $"remote/{remote.Name}", isFiltered);
+ MakeBranchNode(branch, remote.Children, folders, $"remote/{remote.Name}", isFiltered, bForceExpanded);
}
}
+ folders.Clear();
SortNodes(_locals);
SortNodes(_remotes);
}
@@ -113,67 +116,69 @@ namespace SourceGit.Models
}
}
- private void MakeBranchNode(Branch branch, List roots, string prefix, bool isFiltered)
+ private void MakeBranchNode(Models.Branch branch, List roots, Dictionary folders, string prefix, bool isFiltered, bool bForceExpanded)
{
- var subs = branch.Name.Split(new char[] { '/' }, StringSplitOptions.RemoveEmptyEntries);
-
- if (subs.Length == 1)
+ var sepIdx = branch.Name.IndexOf('/', StringComparison.Ordinal);
+ if (sepIdx == -1)
{
- var node = new BranchTreeNode()
+ roots.Add(new BranchTreeNode()
{
- Name = subs[0],
+ Name = branch.Name,
Type = BranchTreeNodeType.Branch,
Backend = branch,
IsExpanded = false,
IsFiltered = isFiltered,
- };
- roots.Add(node);
+ });
return;
}
- BranchTreeNode lastFolder = null;
- string path = prefix;
- for (int i = 0; i < subs.Length - 1; i++)
+ var lastFolder = null as BranchTreeNode;
+ var start = 0;
+
+ while (sepIdx != -1)
{
- path = string.Concat(path, "/", subs[i]);
- if (_maps.TryGetValue(path, out var value))
+ var folder = string.Concat(prefix, "/", branch.Name.Substring(0, sepIdx));
+ var name = branch.Name.Substring(start, sepIdx - start);
+ if (folders.TryGetValue(folder, out var val))
{
- lastFolder = value;
+ lastFolder = val;
}
else if (lastFolder == null)
{
lastFolder = new BranchTreeNode()
{
- Name = subs[i],
+ Name = name,
Type = BranchTreeNodeType.Folder,
- IsExpanded = branch.IsCurrent || _expanded.Contains(path),
+ IsExpanded = bForceExpanded || branch.IsCurrent || _expanded.Contains(folder),
};
roots.Add(lastFolder);
- _maps.Add(path, lastFolder);
+ folders.Add(folder, lastFolder);
}
else
{
- var folder = new BranchTreeNode()
+ var cur = new BranchTreeNode()
{
- Name = subs[i],
+ Name = name,
Type = BranchTreeNodeType.Folder,
- IsExpanded = branch.IsCurrent || _expanded.Contains(path),
+ IsExpanded = bForceExpanded || branch.IsCurrent || _expanded.Contains(folder),
};
- _maps.Add(path, folder);
- lastFolder.Children.Add(folder);
- lastFolder = folder;
+ lastFolder.Children.Add(cur);
+ folders.Add(folder, cur);
+ lastFolder = cur;
}
+
+ start = sepIdx + 1;
+ sepIdx = branch.Name.IndexOf('/', start);
}
- var last = new BranchTreeNode()
+ lastFolder.Children.Add(new BranchTreeNode()
{
- Name = subs[subs.Length - 1],
+ Name = Path.GetFileName(branch.Name),
Type = BranchTreeNodeType.Branch,
Backend = branch,
IsExpanded = false,
IsFiltered = isFiltered,
- };
- lastFolder.Children.Add(last);
+ });
}
private void SortNodes(List nodes)
@@ -186,7 +191,7 @@ namespace SourceGit.Models
}
else
{
- return (int)(l.Type) - (int)(r.Type);
+ return (int)l.Type - (int)r.Type;
}
});
@@ -198,7 +203,6 @@ namespace SourceGit.Models
private readonly List _remotes = new List();
private readonly HashSet _expanded = new HashSet();
private readonly List _filters = new List();
- private readonly Dictionary _maps = new Dictionary();
}
}
}
diff --git a/src/ViewModels/Repository.cs b/src/ViewModels/Repository.cs
index 0e67f11b..a0f84a15 100644
--- a/src/ViewModels/Repository.cs
+++ b/src/ViewModels/Repository.cs
@@ -89,6 +89,21 @@ namespace SourceGit.ViewModels
set => SetProperty(ref _selectedView, value);
}
+ [JsonIgnore]
+ public string SearchBranchFilter
+ {
+ get => _searchBranchFilter;
+ set
+ {
+ if (SetProperty(ref _searchBranchFilter, value))
+ {
+ var builder = BuildBranchTree(_branches, _remotes);
+ LocalBranchTrees = builder.Locals;
+ RemoteBranchTrees = builder.Remotes;
+ }
+ }
+ }
+
[JsonIgnore]
public List Remotes
{
@@ -104,14 +119,14 @@ namespace SourceGit.ViewModels
}
[JsonIgnore]
- public List LocalBranchTrees
+ public List LocalBranchTrees
{
get => _localBranchTrees;
private set => SetProperty(ref _localBranchTrees, value);
}
[JsonIgnore]
- public List RemoteBranchTrees
+ public List RemoteBranchTrees
{
get => _remoteBranchTrees;
private set => SetProperty(ref _remoteBranchTrees, value);
@@ -221,6 +236,7 @@ namespace SourceGit.ViewModels
private set => SetProperty(ref _hasUnsolvedConflicts, value);
}
+ [JsonIgnore]
public Models.Commit SearchResultSelectedCommit
{
get => _searchResultSelectedCommit;
@@ -422,6 +438,11 @@ namespace SourceGit.ViewModels
SearchedCommits = visible;
}
+ public void ClearSearchBranchFilter()
+ {
+ SearchBranchFilter = string.Empty;
+ }
+
public void SetWatcherEnabled(bool enabled)
{
if (_watcher != null)
@@ -533,12 +554,7 @@ namespace SourceGit.ViewModels
{
var branches = new Commands.QueryBranches(FullPath).Result();
var remotes = new Commands.QueryRemotes(FullPath).Result();
-
- var builder = new Models.BranchTreeNode.Builder();
- builder.SetFilters(Filters);
- builder.CollectExpandedNodes(_localBranchTrees, true);
- builder.CollectExpandedNodes(_remoteBranchTrees, false);
- builder.Run(branches, remotes);
+ var builder = BuildBranchTree(branches, remotes);
Dispatcher.UIThread.Invoke(() =>
{
@@ -1354,6 +1370,32 @@ namespace SourceGit.ViewModels
return menu;
}
+ private BranchTreeNode.Builder BuildBranchTree(List branches, List remotes)
+ {
+ var builder = new BranchTreeNode.Builder();
+ builder.SetFilters(Filters);
+
+ if (string.IsNullOrEmpty(_searchBranchFilter))
+ {
+ builder.CollectExpandedNodes(_localBranchTrees, true);
+ builder.CollectExpandedNodes(_remoteBranchTrees, false);
+ builder.Run(branches, remotes, false);
+ }
+ else
+ {
+ var visibles = new List();
+ foreach (var b in branches)
+ {
+ if (b.FullName.Contains(_searchBranchFilter, StringComparison.OrdinalIgnoreCase))
+ visibles.Add(b);
+ }
+
+ builder.Run(visibles, remotes, visibles.Count <= 20);
+ }
+
+ return builder;
+ }
+
private string _fullpath = string.Empty;
private string _gitDir = string.Empty;
private Models.GitFlow _gitflow = new Models.GitFlow();
@@ -1372,10 +1414,12 @@ namespace SourceGit.ViewModels
private bool _isTagGroupExpanded = false;
private bool _isSubmoduleGroupExpanded = false;
+ private string _searchBranchFilter = string.Empty;
+
private List _remotes = new List();
private List _branches = new List();
- private List _localBranchTrees = new List();
- private List _remoteBranchTrees = new List();
+ private List _localBranchTrees = new List();
+ private List _remoteBranchTrees = new List();
private List _tags = new List();
private List _submodules = new List();
private bool _canCommitWithPush = false;
diff --git a/src/Views/Hotkeys.axaml b/src/Views/Hotkeys.axaml
index 79a06cb7..fb067d15 100644
--- a/src/Views/Hotkeys.axaml
+++ b/src/Views/Hotkeys.axaml
@@ -68,7 +68,7 @@
Margin="0,0,0,8"/>
-
+
diff --git a/src/Views/Launcher.axaml.cs b/src/Views/Launcher.axaml.cs
index 17a5e78a..fd6fd690 100644
--- a/src/Views/Launcher.axaml.cs
+++ b/src/Views/Launcher.axaml.cs
@@ -223,7 +223,10 @@ namespace SourceGit.Views
private void BeginMoveWindow(object sender, PointerPressedEventArgs e)
{
- BeginMoveDrag(e);
+ if (e.ClickCount != 2)
+ {
+ BeginMoveDrag(e);
+ }
}
private void ScrollTabs(object sender, PointerWheelEventArgs e)
diff --git a/src/Views/Repository.axaml b/src/Views/Repository.axaml
index c854faa2..6b62a0b1 100644
--- a/src/Views/Repository.axaml
+++ b/src/Views/Repository.axaml
@@ -109,7 +109,7 @@
-
+
@@ -159,9 +159,43 @@
+
+
+
+
+
+
+
+
+
+
+
+
-
-
+
-
-
+
@@ -208,13 +242,13 @@
-
+
-
-
-
+
@@ -250,7 +284,7 @@
-
+
@@ -259,7 +293,7 @@
-
-
+
@@ -328,7 +362,7 @@
-