From c0348b73bd1df091489df2fa6569542030028556 Mon Sep 17 00:00:00 2001 From: ghiboz Date: Fri, 5 Jul 2024 14:02:30 +0200 Subject: [PATCH 01/40] set pen thickness "General": { "Pen.Thickness": "4.4" } --- src/App.axaml.cs | 17 +++++++++++++++++ src/Models/CommitGraph.cs | 10 ++++++++++ src/Models/CustomColorSchema.cs | 1 + src/Views/Histories.axaml.cs | 2 ++ 4 files changed, 30 insertions(+) diff --git a/src/App.axaml.cs b/src/App.axaml.cs index f989a325..785b511c 100644 --- a/src/App.axaml.cs +++ b/src/App.axaml.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.Globalization; using System.IO; using System.Net.Http; using System.Reflection; @@ -188,6 +189,22 @@ namespace SourceGit Models.CommitGraph.SetPenColors(penColors); } + foreach (var kv in schema.General) + { + if (kv.Key.Equals("Pen.Thickness", StringComparison.Ordinal)) + { + double thick = Models.CommitGraph.GetPenThickness(); + try + { + thick = double.Parse(kv.Value, CultureInfo.InvariantCulture); + } + catch + { + } + Models.CommitGraph.SetPenThickness(thick); + } + } + app.Resources.MergedDictionaries.Add(resDic); app._colorOverrides = resDic; } diff --git a/src/Models/CommitGraph.cs b/src/Models/CommitGraph.cs index bc0ea8e1..c0ffe820 100644 --- a/src/Models/CommitGraph.cs +++ b/src/Models/CommitGraph.cs @@ -123,6 +123,15 @@ namespace SourceGit.Models _penCount = colors.Count; } + public static void SetPenThickness(double value) + { + _penThickness = value; + } + public static double GetPenThickness() + { + return _penThickness; + } + public static CommitGraph Parse(List commits) { double UNIT_WIDTH = 12; @@ -268,6 +277,7 @@ namespace SourceGit.Models return temp; } + private static double _penThickness = 1; private static int _penCount = 0; private static readonly List _defaultPenColors = [ Colors.Orange, diff --git a/src/Models/CustomColorSchema.cs b/src/Models/CustomColorSchema.cs index 4266b98e..943b916e 100644 --- a/src/Models/CustomColorSchema.cs +++ b/src/Models/CustomColorSchema.cs @@ -6,5 +6,6 @@ namespace SourceGit.Models { public Dictionary Basic { get; set; } = new Dictionary(); public List Graph { get; set; } = new List(); + public Dictionary General { get; set; } = new Dictionary(); } } diff --git a/src/Views/Histories.axaml.cs b/src/Views/Histories.axaml.cs index 37ad441d..6ac67d0b 100644 --- a/src/Views/Histories.axaml.cs +++ b/src/Views/Histories.axaml.cs @@ -158,6 +158,8 @@ namespace SourceGit.Views var geo = new StreamGeometry(); var pen = Models.CommitGraph.Pens[line.Color]; + pen.Thickness = Models.CommitGraph.GetPenThickness(); + using (var ctx = geo.Open()) { var started = false; From 16d9b627f0399374182a315086be7aa786528f8c Mon Sep 17 00:00:00 2001 From: leo Date: Mon, 8 Jul 2024 16:21:57 +0800 Subject: [PATCH 02/40] refactor: rename `Models.CustomColorSchema` to `Models.ThemeOverrides` because it do NOT contains only colors currently. --- src/App.JsonCodeGen.cs | 2 +- src/App.axaml.cs | 52 +++++++++++-------------------- src/Models/CommitGraph.cs | 18 +++-------- src/Models/CustomColorSchema.cs | 11 ------- src/Models/ThemeOverrides.cs | 11 +++++++ src/Resources/Locales/en_US.axaml | 2 +- src/Resources/Locales/zh_CN.axaml | 2 +- src/Resources/Locales/zh_TW.axaml | 2 +- src/ViewModels/Preference.cs | 10 +++--- src/Views/Histories.axaml.cs | 1 - src/Views/Preference.axaml | 6 ++-- src/Views/Preference.axaml.cs | 6 ++-- 12 files changed, 49 insertions(+), 74 deletions(-) delete mode 100644 src/Models/CustomColorSchema.cs create mode 100644 src/Models/ThemeOverrides.cs diff --git a/src/App.JsonCodeGen.cs b/src/App.JsonCodeGen.cs index 901a9b5b..ba6bd96c 100644 --- a/src/App.JsonCodeGen.cs +++ b/src/App.JsonCodeGen.cs @@ -6,8 +6,8 @@ namespace SourceGit [JsonSourceGenerationOptions(WriteIndented = true, IgnoreReadOnlyFields = true, IgnoreReadOnlyProperties = true)] [JsonSerializable(typeof(List))] [JsonSerializable(typeof(Models.JetBrainsState))] + [JsonSerializable(typeof(Models.ThemeOverrides))] [JsonSerializable(typeof(Models.Version))] - [JsonSerializable(typeof(Models.CustomColorSchema))] [JsonSerializable(typeof(ViewModels.Preference))] [JsonSerializable(typeof(ViewModels.RepositorySettings))] internal partial class JsonCodeGen : JsonSerializerContext { } diff --git a/src/App.axaml.cs b/src/App.axaml.cs index 785b511c..8c9738fa 100644 --- a/src/App.axaml.cs +++ b/src/App.axaml.cs @@ -1,6 +1,5 @@ using System; using System.Collections.Generic; -using System.Globalization; using System.IO; using System.Net.Http; using System.Reflection; @@ -144,7 +143,7 @@ namespace SourceGit app._activeLocale = targetLocale; } - public static void SetTheme(string theme, string colorsFile) + public static void SetTheme(string theme, string themeOverridesFile) { var app = Current as App; @@ -155,63 +154,50 @@ namespace SourceGit else app.RequestedThemeVariant = ThemeVariant.Default; - if (app._colorOverrides != null) + if (app._themeOverrides != null) { - app.Resources.MergedDictionaries.Remove(app._colorOverrides); - app._colorOverrides = null; + app.Resources.MergedDictionaries.Remove(app._themeOverrides); + app._themeOverrides = null; } - Models.CommitGraph.SetDefaultPens(); - - if (!string.IsNullOrEmpty(colorsFile) && File.Exists(colorsFile)) + if (!string.IsNullOrEmpty(themeOverridesFile) && File.Exists(themeOverridesFile)) { try { var resDic = new ResourceDictionary(); - - var schema = JsonSerializer.Deserialize(File.ReadAllText(colorsFile), JsonCodeGen.Default.CustomColorSchema); - foreach (var kv in schema.Basic) + var overrides = JsonSerializer.Deserialize(File.ReadAllText(themeOverridesFile), JsonCodeGen.Default.ThemeOverrides); + foreach (var kv in overrides.BasicColors) { if (kv.Key.Equals("SystemAccentColor", StringComparison.Ordinal)) resDic["SystemAccentColor"] = Color.Parse(kv.Value); else resDic[$"Color.{kv.Key}"] = Color.Parse(kv.Value); } - - if (schema.Graph.Count > 0) + if (overrides.GraphColors.Count > 0) { var penColors = new List(); - - foreach (var c in schema.Graph) + foreach (var c in overrides.GraphColors) penColors.Add(Color.Parse(c)); - Models.CommitGraph.SetPenColors(penColors); + Models.CommitGraph.SetPens(penColors, overrides.GraphPenThickness); } - - foreach (var kv in schema.General) + else { - if (kv.Key.Equals("Pen.Thickness", StringComparison.Ordinal)) - { - double thick = Models.CommitGraph.GetPenThickness(); - try - { - thick = double.Parse(kv.Value, CultureInfo.InvariantCulture); - } - catch - { - } - Models.CommitGraph.SetPenThickness(thick); - } + Models.CommitGraph.SetDefaultPens(overrides.GraphPenThickness); } app.Resources.MergedDictionaries.Add(resDic); - app._colorOverrides = resDic; + app._themeOverrides = resDic; } catch { } } + else + { + Models.CommitGraph.SetDefaultPens(); + } } public static async void CopyText(string data) @@ -338,7 +324,7 @@ namespace SourceGit var pref = ViewModels.Preference.Instance; SetLocale(pref.Locale); - SetTheme(pref.Theme, pref.ColorOverrides); + SetTheme(pref.Theme, pref.ThemeOverrides); } public override void OnFrameworkInitializationCompleted() @@ -383,6 +369,6 @@ namespace SourceGit private ViewModels.Launcher _launcher = null; private ResourceDictionary _activeLocale = null; - private ResourceDictionary _colorOverrides = null; + private ResourceDictionary _themeOverrides = null; } } diff --git a/src/Models/CommitGraph.cs b/src/Models/CommitGraph.cs index c0ffe820..9ed99904 100644 --- a/src/Models/CommitGraph.cs +++ b/src/Models/CommitGraph.cs @@ -108,30 +108,21 @@ namespace SourceGit.Models private set; } = new List(); - public static void SetDefaultPens() + public static void SetDefaultPens(double thickness = 1.5) { - SetPenColors(_defaultPenColors); + SetPens(_defaultPenColors, thickness); } - public static void SetPenColors(List colors) + public static void SetPens(List colors, double thickness) { Pens.Clear(); foreach (var c in colors) - Pens.Add(new Pen(c.ToUInt32(), 2)); + Pens.Add(new Pen(c.ToUInt32(), thickness)); _penCount = colors.Count; } - public static void SetPenThickness(double value) - { - _penThickness = value; - } - public static double GetPenThickness() - { - return _penThickness; - } - public static CommitGraph Parse(List commits) { double UNIT_WIDTH = 12; @@ -277,7 +268,6 @@ namespace SourceGit.Models return temp; } - private static double _penThickness = 1; private static int _penCount = 0; private static readonly List _defaultPenColors = [ Colors.Orange, diff --git a/src/Models/CustomColorSchema.cs b/src/Models/CustomColorSchema.cs deleted file mode 100644 index 943b916e..00000000 --- a/src/Models/CustomColorSchema.cs +++ /dev/null @@ -1,11 +0,0 @@ -using System.Collections.Generic; - -namespace SourceGit.Models -{ - public class CustomColorSchema - { - public Dictionary Basic { get; set; } = new Dictionary(); - public List Graph { get; set; } = new List(); - public Dictionary General { get; set; } = new Dictionary(); - } -} diff --git a/src/Models/ThemeOverrides.cs b/src/Models/ThemeOverrides.cs new file mode 100644 index 00000000..96e48f82 --- /dev/null +++ b/src/Models/ThemeOverrides.cs @@ -0,0 +1,11 @@ +using System.Collections.Generic; + +namespace SourceGit.Models +{ + public class ThemeOverrides + { + public Dictionary BasicColors { get; set; } = new Dictionary(); + public double GraphPenThickness { get; set; } = 1.5; + public List GraphColors { get; set; } = new List(); + } +} diff --git a/src/Resources/Locales/en_US.axaml b/src/Resources/Locales/en_US.axaml index 55b05a01..271a52dd 100644 --- a/src/Resources/Locales/en_US.axaml +++ b/src/Resources/Locales/en_US.axaml @@ -337,11 +337,11 @@ Paste Preference APPEARANCE - Custom Color Schema Default Font Default Font Size Monospace Font Theme + Theme Overrides GENERAL Avatar Server Check for updates on startup diff --git a/src/Resources/Locales/zh_CN.axaml b/src/Resources/Locales/zh_CN.axaml index 58a72d47..adf03079 100644 --- a/src/Resources/Locales/zh_CN.axaml +++ b/src/Resources/Locales/zh_CN.axaml @@ -340,11 +340,11 @@ 粘贴 偏好设置 外观配置 - 自定义配色文件 缺省字体 默认字体大小 等宽字体 主题 + 主题自定义 通用配置 头像服务 启动时检测软件更新 diff --git a/src/Resources/Locales/zh_TW.axaml b/src/Resources/Locales/zh_TW.axaml index 73176126..d8d22032 100644 --- a/src/Resources/Locales/zh_TW.axaml +++ b/src/Resources/Locales/zh_TW.axaml @@ -340,11 +340,11 @@ 貼上 偏好設定 外觀配置 - 自訂配色檔 預設字型 預設字型大小 等寬字型 主題 + 主題自訂 通用配置 頭像服務 啟動時檢測軟體更新 diff --git a/src/ViewModels/Preference.cs b/src/ViewModels/Preference.cs index 5198a8b8..431fd8d8 100644 --- a/src/ViewModels/Preference.cs +++ b/src/ViewModels/Preference.cs @@ -66,16 +66,16 @@ namespace SourceGit.ViewModels set { if (SetProperty(ref _theme, value)) - App.SetTheme(_theme, _colorOverrides); + App.SetTheme(_theme, _themeOverrides); } } - public string ColorOverrides + public string ThemeOverrides { - get => _colorOverrides; + get => _themeOverrides; set { - if (SetProperty(ref _colorOverrides, value)) + if (SetProperty(ref _themeOverrides, value)) App.SetTheme(_theme, value); } } @@ -487,7 +487,7 @@ namespace SourceGit.ViewModels private string _locale = "en_US"; private string _theme = "Default"; - private string _colorOverrides = string.Empty; + private string _themeOverrides = string.Empty; private FontFamily _defaultFont = null; private FontFamily _monospaceFont = null; private double _defaultFontSize = 13; diff --git a/src/Views/Histories.axaml.cs b/src/Views/Histories.axaml.cs index 6ac67d0b..05fe1a81 100644 --- a/src/Views/Histories.axaml.cs +++ b/src/Views/Histories.axaml.cs @@ -158,7 +158,6 @@ namespace SourceGit.Views var geo = new StreamGeometry(); var pen = Models.CommitGraph.Pens[line.Color]; - pen.Thickness = Models.CommitGraph.GetPenThickness(); using (var ctx = geo.Open()) { diff --git a/src/Views/Preference.axaml b/src/Views/Preference.axaml index 56561c46..5ca3b83f 100644 --- a/src/Views/Preference.axaml +++ b/src/Views/Preference.axaml @@ -229,15 +229,15 @@ Value="{Binding DefaultFontSize, Mode=TwoWay}"/> + Text="{Binding ThemeOverrides, Mode=TwoWay}"> - diff --git a/src/Views/Preference.axaml.cs b/src/Views/Preference.axaml.cs index 0bc477f3..d483659c 100644 --- a/src/Views/Preference.axaml.cs +++ b/src/Views/Preference.axaml.cs @@ -210,18 +210,18 @@ namespace SourceGit.Views Close(); } - private async void SelectColorSchemaFile(object sender, RoutedEventArgs e) + private async void SelectThemeOverrideFile(object sender, RoutedEventArgs e) { var options = new FilePickerOpenOptions() { - FileTypeFilter = [new FilePickerFileType("Theme Color Schema File") { Patterns = ["*.json"] }], + FileTypeFilter = [new FilePickerFileType("Theme Overrides File") { Patterns = ["*.json"] }], AllowMultiple = false, }; var selected = await StorageProvider.OpenFilePickerAsync(options); if (selected.Count == 1) { - ViewModels.Preference.Instance.ColorOverrides = selected[0].Path.LocalPath; + ViewModels.Preference.Instance.ThemeOverrides = selected[0].Path.LocalPath; } e.Handled = true; From 7ee3db500abb1524df079d5e78753029305bb18b Mon Sep 17 00:00:00 2001 From: leo Date: Mon, 8 Jul 2024 16:45:51 +0800 Subject: [PATCH 03/40] refactor: json serialization * move all converters to `App.JsonCodeGen.cs` * use `ColorConverter` instead of parsing colors manually --- src/App.JsonCodeGen.cs | 59 ++++++++++++++++++++++++++++++++++-- src/App.axaml.cs | 14 ++------- src/Models/ThemeOverrides.cs | 6 ++-- src/ViewModels/LayoutInfo.cs | 19 ------------ src/ViewModels/Preference.cs | 16 ---------- 5 files changed, 64 insertions(+), 50 deletions(-) diff --git a/src/App.JsonCodeGen.cs b/src/App.JsonCodeGen.cs index ba6bd96c..fd4d274e 100644 --- a/src/App.JsonCodeGen.cs +++ b/src/App.JsonCodeGen.cs @@ -1,9 +1,64 @@ -using System.Collections.Generic; +using System; +using System.Collections.Generic; +using System.Text.Json; using System.Text.Json.Serialization; +using Avalonia.Controls; +using Avalonia.Media; + namespace SourceGit { - [JsonSourceGenerationOptions(WriteIndented = true, IgnoreReadOnlyFields = true, IgnoreReadOnlyProperties = true)] + public class ColorConverter : JsonConverter + { + public override Color Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) + { + return Color.Parse(reader.GetString()); + } + + public override void Write(Utf8JsonWriter writer, Color value, JsonSerializerOptions options) + { + writer.WriteStringValue(value.ToString()); + } + } + + public class FontFamilyConverter : JsonConverter + { + public override FontFamily Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) + { + var name = reader.GetString(); + return new FontFamily(name); + } + + public override void Write(Utf8JsonWriter writer, FontFamily value, JsonSerializerOptions options) + { + writer.WriteStringValue(value.ToString()); + } + } + + public class GridLengthConverter : JsonConverter + { + public override GridLength Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) + { + var size = reader.GetDouble(); + return new GridLength(size, GridUnitType.Pixel); + } + + public override void Write(Utf8JsonWriter writer, GridLength value, JsonSerializerOptions options) + { + writer.WriteNumberValue(value.Value); + } + } + + [JsonSourceGenerationOptions( + WriteIndented = true, + IgnoreReadOnlyFields = true, + IgnoreReadOnlyProperties = true, + Converters = [ + typeof(ColorConverter), + typeof(FontFamilyConverter), + typeof(GridLengthConverter), + ] + )] [JsonSerializable(typeof(List))] [JsonSerializable(typeof(Models.JetBrainsState))] [JsonSerializable(typeof(Models.ThemeOverrides))] diff --git a/src/App.axaml.cs b/src/App.axaml.cs index 8c9738fa..b03e4f03 100644 --- a/src/App.axaml.cs +++ b/src/App.axaml.cs @@ -169,23 +169,15 @@ namespace SourceGit foreach (var kv in overrides.BasicColors) { if (kv.Key.Equals("SystemAccentColor", StringComparison.Ordinal)) - resDic["SystemAccentColor"] = Color.Parse(kv.Value); + resDic["SystemAccentColor"] = kv.Value; else - resDic[$"Color.{kv.Key}"] = Color.Parse(kv.Value); + resDic[$"Color.{kv.Key}"] = kv.Value; } if (overrides.GraphColors.Count > 0) - { - var penColors = new List(); - foreach (var c in overrides.GraphColors) - penColors.Add(Color.Parse(c)); - - Models.CommitGraph.SetPens(penColors, overrides.GraphPenThickness); - } + Models.CommitGraph.SetPens(overrides.GraphColors, overrides.GraphPenThickness); else - { Models.CommitGraph.SetDefaultPens(overrides.GraphPenThickness); - } app.Resources.MergedDictionaries.Add(resDic); app._themeOverrides = resDic; diff --git a/src/Models/ThemeOverrides.cs b/src/Models/ThemeOverrides.cs index 96e48f82..b231353c 100644 --- a/src/Models/ThemeOverrides.cs +++ b/src/Models/ThemeOverrides.cs @@ -1,11 +1,13 @@ using System.Collections.Generic; +using Avalonia.Media; + namespace SourceGit.Models { public class ThemeOverrides { - public Dictionary BasicColors { get; set; } = new Dictionary(); + public Dictionary BasicColors { get; set; } = new Dictionary(); public double GraphPenThickness { get; set; } = 1.5; - public List GraphColors { get; set; } = new List(); + public List GraphColors { get; set; } = new List(); } } diff --git a/src/ViewModels/LayoutInfo.cs b/src/ViewModels/LayoutInfo.cs index e78ad2cf..30be27de 100644 --- a/src/ViewModels/LayoutInfo.cs +++ b/src/ViewModels/LayoutInfo.cs @@ -28,35 +28,30 @@ namespace SourceGit.ViewModels set; } = WindowState.Normal; - [JsonConverter(typeof(GridLengthConverter))] public GridLength RepositorySidebarWidth { get => _repositorySidebarWidth; set => SetProperty(ref _repositorySidebarWidth, value); } - [JsonConverter(typeof(GridLengthConverter))] public GridLength WorkingCopyLeftWidth { get => _workingCopyLeftWidth; set => SetProperty(ref _workingCopyLeftWidth, value); } - [JsonConverter(typeof(GridLengthConverter))] public GridLength StashesLeftWidth { get => _stashesLeftWidth; set => SetProperty(ref _stashesLeftWidth, value); } - [JsonConverter(typeof(GridLengthConverter))] public GridLength CommitDetailChangesLeftWidth { get => _commitDetailChangesLeftWidth; set => SetProperty(ref _commitDetailChangesLeftWidth, value); } - [JsonConverter(typeof(GridLengthConverter))] public GridLength CommitDetailFilesLeftWidth { get => _commitDetailFilesLeftWidth; @@ -69,18 +64,4 @@ namespace SourceGit.ViewModels private GridLength _commitDetailChangesLeftWidth = new GridLength(256, GridUnitType.Pixel); private GridLength _commitDetailFilesLeftWidth = new GridLength(256, GridUnitType.Pixel); } - - public class GridLengthConverter : JsonConverter - { - public override GridLength Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) - { - var size = reader.GetDouble(); - return new GridLength(size, GridUnitType.Pixel); - } - - public override void Write(Utf8JsonWriter writer, GridLength value, JsonSerializerOptions options) - { - writer.WriteNumberValue(value.Value); - } - } } diff --git a/src/ViewModels/Preference.cs b/src/ViewModels/Preference.cs index 431fd8d8..00808a48 100644 --- a/src/ViewModels/Preference.cs +++ b/src/ViewModels/Preference.cs @@ -80,14 +80,12 @@ namespace SourceGit.ViewModels } } - [JsonConverter(typeof(FontFamilyConverter))] public FontFamily DefaultFont { get => _defaultFont; set => SetProperty(ref _defaultFont, value); } - [JsonConverter(typeof(FontFamilyConverter))] public FontFamily MonospaceFont { get => _monospaceFont; @@ -517,18 +515,4 @@ namespace SourceGit.ViewModels private AvaloniaList _repositoryNodes = new AvaloniaList(); } - - public class FontFamilyConverter : JsonConverter - { - public override FontFamily Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) - { - var name = reader.GetString(); - return new FontFamily(name); - } - - public override void Write(Utf8JsonWriter writer, FontFamily value, JsonSerializerOptions options) - { - writer.WriteStringValue(value.ToString()); - } - } } From 8fa19ecd0c9e02443c3df089018d1f04800ba882 Mon Sep 17 00:00:00 2001 From: leo Date: Mon, 8 Jul 2024 18:10:26 +0800 Subject: [PATCH 04/40] enhance: better commit graph --- src/Models/CommitGraph.cs | 9 +++++++-- src/Views/Histories.axaml.cs | 2 +- 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/src/Models/CommitGraph.cs b/src/Models/CommitGraph.cs index 9ed99904..deda6c04 100644 --- a/src/Models/CommitGraph.cs +++ b/src/Models/CommitGraph.cs @@ -61,8 +61,13 @@ namespace SourceGit.Models } else if (x < LastX) { - if (y > LastY + halfHeight) - Add(new Point(LastX, LastY + halfHeight)); + var testY = LastY + halfHeight; + if (y > testY) + Add(new Point(LastX, testY)); + + if (!isEnd) + y += halfHeight; + Add(new Point(x, y)); } else if (isEnd) diff --git a/src/Views/Histories.axaml.cs b/src/Views/Histories.axaml.cs index 05fe1a81..5d63cfe4 100644 --- a/src/Views/Histories.axaml.cs +++ b/src/Views/Histories.axaml.cs @@ -193,7 +193,7 @@ namespace SourceGit.Views if (i < size - 1) { var midY = (last.Y + cur.Y) / 2; - ctx.CubicBezierTo(new Point(last.X, midY + 2), new Point(cur.X, midY - 2), cur); + ctx.CubicBezierTo(new Point(last.X, midY + 4), new Point(cur.X, midY - 4), cur); } else { From cbe4c365259e6ef57bd6a4a3196a99ea97ae46ff Mon Sep 17 00:00:00 2001 From: leo Date: Mon, 8 Jul 2024 22:07:00 +0800 Subject: [PATCH 05/40] feature: support git.core.askpass (#239) --- src/App.axaml.cs | 43 +++++++++++++--- src/Commands/Branch.cs | 10 ++-- src/Commands/Clone.cs | 6 +-- src/Commands/Command.cs | 20 +++++++- src/Commands/Fetch.cs | 20 +++----- src/Commands/Pull.cs | 10 ++-- src/Commands/Push.cs | 20 +++----- src/Resources/Icons.axaml | 1 + src/Resources/Locales/en_US.axaml | 1 + src/Resources/Locales/zh_CN.axaml | 1 + src/Resources/Locales/zh_TW.axaml | 1 + src/ViewModels/Launcher.cs | 5 +- src/Views/Askpass.axaml | 81 +++++++++++++++++++++++++++++++ src/Views/Askpass.axaml.cs | 52 ++++++++++++++++++++ 14 files changed, 211 insertions(+), 60 deletions(-) create mode 100644 src/Views/Askpass.axaml create mode 100644 src/Views/Askpass.axaml.cs diff --git a/src/App.axaml.cs b/src/App.axaml.cs index b03e4f03..b21953a2 100644 --- a/src/App.axaml.cs +++ b/src/App.axaml.cs @@ -5,6 +5,7 @@ using System.Net.Http; using System.Reflection; using System.Text; using System.Text.Json; +using System.Text.RegularExpressions; using System.Threading.Tasks; using System.Windows.Input; @@ -324,16 +325,25 @@ namespace SourceGit if (ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktop) { BindingPlugins.DataValidators.RemoveAt(0); - Native.OS.SetupEnternalTools(); - _launcher = new ViewModels.Launcher(); - desktop.MainWindow = new Views.Launcher() { DataContext = _launcher }; - - var pref = ViewModels.Preference.Instance; - if (pref.ShouldCheck4UpdateOnStartup) + var commandlines = Environment.GetCommandLineArgs(); + if (TryParseAskpass(commandlines, out var keyname)) { - pref.Save(); - Check4Update(); + desktop.MainWindow = new Views.Askpass(Path.GetFileName(keyname)); + } + else + { + Native.OS.SetupEnternalTools(); + + _launcher = new ViewModels.Launcher(commandlines); + desktop.MainWindow = new Views.Launcher() { DataContext = _launcher }; + + var pref = ViewModels.Preference.Instance; + if (pref.ShouldCheck4UpdateOnStartup) + { + pref.Save(); + Check4Update(); + } } } @@ -359,6 +369,23 @@ namespace SourceGit }); } + private static bool TryParseAskpass(string[] args, out string keyname) + { + keyname = string.Empty; + + if (args.Length != 2) + return false; + + var match = REG_ASKPASS().Match(args[1]); + if (match.Success) + keyname = match.Groups[1].Value; + + return match.Success; + } + + [GeneratedRegex(@"Enter\s+passphrase\s*for\s*key\s*['""]([^'""]+)['""]\:\s*", RegexOptions.IgnoreCase)] + private static partial Regex REG_ASKPASS(); + private ViewModels.Launcher _launcher = null; private ResourceDictionary _activeLocale = null; private ResourceDictionary _themeOverrides = null; diff --git a/src/Commands/Branch.cs b/src/Commands/Branch.cs index 660a5daa..cd4ec599 100644 --- a/src/Commands/Branch.cs +++ b/src/Commands/Branch.cs @@ -52,14 +52,10 @@ cmd.Context = repo; var sshKey = new Config(repo).Get($"remote.{remote}.sshkey"); - if (!string.IsNullOrEmpty(sshKey)) - { - cmd.Args = $"-c core.sshCommand=\"ssh -i '{sshKey}'\" "; - } - else - { + if (string.IsNullOrEmpty(sshKey)) cmd.Args = "-c credential.helper=manager "; - } + else + cmd.UseSSHKey(sshKey); cmd.Args += $"push {remote} --delete {name}"; return cmd.Exec(); diff --git a/src/Commands/Clone.cs b/src/Commands/Clone.cs index 80e0df50..cdefbb23 100644 --- a/src/Commands/Clone.cs +++ b/src/Commands/Clone.cs @@ -13,13 +13,9 @@ namespace SourceGit.Commands TraitErrorAsOutput = true; if (string.IsNullOrEmpty(sshKey)) - { Args = "-c credential.helper=manager "; - } else - { - Args = $"-c core.sshCommand=\"ssh -i '{sshKey}'\" "; - } + UseSSHKey(sshKey); Args += "clone --progress --verbose --recurse-submodules "; diff --git a/src/Commands/Command.cs b/src/Commands/Command.cs index 00f36c85..3e440a13 100644 --- a/src/Commands/Command.cs +++ b/src/Commands/Command.cs @@ -28,6 +28,15 @@ namespace SourceGit.Commands public string Args { get; set; } = string.Empty; public bool RaiseError { get; set; } = true; public bool TraitErrorAsOutput { get; set; } = false; + public Dictionary Envs { get; set; } = new Dictionary(); + + public void UseSSHKey(string key) + { + Envs.Add("DISPLAY", "required"); + Envs.Add("SSH_ASKPASS", Process.GetCurrentProcess().MainModule.FileName); + Envs.Add("SSH_ASKPASS_REQUIRE", "prefer"); + Envs.Add("GIT_SSH_COMMAND", $"ssh -i '{key}'"); + } public bool Exec() { @@ -41,6 +50,10 @@ namespace SourceGit.Commands start.StandardOutputEncoding = Encoding.UTF8; start.StandardErrorEncoding = Encoding.UTF8; + // User environment overrides. + foreach (var kv in Envs) + start.Environment.Add(kv.Key, kv.Value); + // Force using en_US.UTF-8 locale to avoid GCM crash if (OperatingSystem.IsLinux()) start.Environment.Add("LANG", "en_US.UTF-8"); @@ -94,7 +107,7 @@ namespace SourceGit.Commands return; if (e.Data.StartsWith("Filtering content:", StringComparison.Ordinal)) return; - if (_progressRegex().IsMatch(e.Data)) + if (REG_PROGRESS().IsMatch(e.Data)) return; errs.Add(e.Data); }; @@ -185,6 +198,9 @@ namespace SourceGit.Commands protected virtual void OnReadline(string line) { } [GeneratedRegex(@"\d+%")] - private static partial Regex _progressRegex(); + private static partial Regex REG_PROGRESS(); + + [GeneratedRegex(@"Enter\s+passphrase\s*for\s*key\s*['""]([^'""]+)['""]\:\s*", RegexOptions.IgnoreCase)] + private static partial Regex REG_ASKPASS(); } } diff --git a/src/Commands/Fetch.cs b/src/Commands/Fetch.cs index ca1d83d6..c169645d 100644 --- a/src/Commands/Fetch.cs +++ b/src/Commands/Fetch.cs @@ -15,14 +15,10 @@ namespace SourceGit.Commands TraitErrorAsOutput = true; var sshKey = new Config(repo).Get($"remote.{remote}.sshkey"); - if (!string.IsNullOrEmpty(sshKey)) - { - Args = $"-c core.sshCommand=\"ssh -i '{sshKey}'\" "; - } - else - { + if (string.IsNullOrEmpty(sshKey)) Args = "-c credential.helper=manager "; - } + else + UseSSHKey(sshKey); Args += "fetch --progress --verbose "; if (prune) @@ -46,14 +42,10 @@ namespace SourceGit.Commands TraitErrorAsOutput = true; var sshKey = new Config(repo).Get($"remote.{remote}.sshkey"); - if (!string.IsNullOrEmpty(sshKey)) - { - Args = $"-c core.sshCommand=\"ssh -i '{sshKey}'\" "; - } - else - { + if (string.IsNullOrEmpty(sshKey)) Args = "-c credential.helper=manager "; - } + else + UseSSHKey(sshKey); Args += $"fetch --progress --verbose {remote} {remoteBranch}:{localBranch}"; } diff --git a/src/Commands/Pull.cs b/src/Commands/Pull.cs index 43418825..5ed81603 100644 --- a/src/Commands/Pull.cs +++ b/src/Commands/Pull.cs @@ -12,14 +12,10 @@ namespace SourceGit.Commands TraitErrorAsOutput = true; var sshKey = new Config(repo).Get($"remote.{remote}.sshkey"); - if (!string.IsNullOrEmpty(sshKey)) - { - Args = $"-c core.sshCommand=\"ssh -i '{sshKey}'\" "; - } - else - { + if (string.IsNullOrEmpty(sshKey)) Args = "-c credential.helper=manager "; - } + else + UseSSHKey(sshKey); Args += "pull --verbose --progress --tags "; if (useRebase) diff --git a/src/Commands/Push.cs b/src/Commands/Push.cs index 0aac37a5..80ad10e6 100644 --- a/src/Commands/Push.cs +++ b/src/Commands/Push.cs @@ -12,14 +12,10 @@ namespace SourceGit.Commands _outputHandler = onProgress; var sshKey = new Config(repo).Get($"remote.{remote}.sshkey"); - if (!string.IsNullOrEmpty(sshKey)) - { - Args = $"-c core.sshCommand=\"ssh -i '{sshKey}'\" "; - } - else - { + if (string.IsNullOrEmpty(sshKey)) Args = "-c credential.helper=manager "; - } + else + UseSSHKey(sshKey); Args += "push --progress --verbose "; @@ -39,14 +35,10 @@ namespace SourceGit.Commands Context = repo; var sshKey = new Config(repo).Get($"remote.{remote}.sshkey"); - if (!string.IsNullOrEmpty(sshKey)) - { - Args = $"-c core.sshCommand=\"ssh -i '{sshKey}'\" "; - } - else - { + if (string.IsNullOrEmpty(sshKey)) Args = "-c credential.helper=manager "; - } + else + UseSSHKey(sshKey); Args += "push "; if (isDelete) diff --git a/src/Resources/Icons.axaml b/src/Resources/Icons.axaml index 1af75c64..00f7fc48 100644 --- a/src/Resources/Icons.axaml +++ b/src/Resources/Icons.axaml @@ -50,6 +50,7 @@ M512 0C229 0 0 229 0 512s229 512 512 512 512-229 512-512S795 0 512 0zM512 928c-230 0-416-186-416-416S282 96 512 96s416 186 416 416S742 928 512 928zM538 343c47 0 83-38 83-78 0-32-21-61-62-61-55 0-82 45-82 77C475 320 498 343 538 343zM533 729c-8 0-11-10-3-40l43-166c16-61 11-100-22-100-39 0-131 40-211 108l16 27c25-17 68-35 78-35 8 0 7 10 0 36l-38 158c-23 89 1 110 34 110 33 0 118-30 196-110l-19-25C575 717 543 729 533 729z M412 66C326 132 271 233 271 347c0 17 1 34 4 50-41-48-98-79-162-83a444 444 0 00-46 196c0 207 142 382 337 439h2c19 0 34 15 34 33 0 11-6 21-14 26l1 14C183 973 0 763 0 511 0 272 166 70 393 7A35 35 0 01414 0c19 0 34 15 34 33a33 33 0 01-36 33zm200 893c86-66 141-168 141-282 0-17-1-34-4-50 41 48 98 79 162 83a444 444 0 0046-196c0-207-142-382-337-439h-2a33 33 0 01-34-33c0-11 6-21 14-26L596 0C841 51 1024 261 1024 513c0 239-166 441-393 504A35 35 0 01610 1024a33 33 0 01-34-33 33 33 0 0136-33zM512 704a192 192 0 110-384 192 192 0 010 384z M512 64A447 447 0 0064 512c0 248 200 448 448 448s448-200 448-448S760 64 512 64zM218 295h31c54 0 105 19 145 55 13 12 13 31 3 43a35 35 0 01-22 10 36 36 0 01-21-7 155 155 0 00-103-39h-31a32 32 0 01-31-31c0-18 13-31 30-31zm31 433h-31a32 32 0 01-31-31c0-16 13-31 31-31h31A154 154 0 00403 512 217 217 0 01620 295h75l-93-67a33 33 0 01-7-43 33 33 0 0143-7l205 148-205 148a29 29 0 01-18 6 32 32 0 01-31-31c0-10 4-19 13-25l93-67H620a154 154 0 00-154 154c0 122-97 220-217 220zm390 118a29 29 0 01-18 6 32 32 0 01-31-31c0-10 4-19 13-25l93-67h-75c-52 0-103-19-143-54-12-12-13-31-1-43a30 30 0 0142-3 151 151 0 00102 39h75L602 599a33 33 0 01-7-43 33 33 0 0143-7l205 148-203 151z + M640 96c-158 0-288 130-288 288 0 17 3 31 5 46L105 681 96 691V928h224v-96h96v-96h96v-95c38 18 82 31 128 31 158 0 288-130 288-288s-130-288-288-288zm0 64c123 0 224 101 224 224s-101 224-224 224a235 235 0 01-109-28l-8-4H448v96h-96v96H256v96H160v-146l253-254 12-11-3-17C419 417 416 400 416 384c0-123 101-224 224-224zm64 96a64 64 0 100 128 64 64 0 100-128z M875 117H149C109 117 75 151 75 192v640c0 41 34 75 75 75h725c41 0 75-34 75-75V192c0-41-34-75-75-75zM139 832V192c0-6 4-11 11-11h331v661H149c-6 0-11-4-11-11zm747 0c0 6-4 11-11 11H544v-661H875c6 0 11 4 11 11v640z M875 117H149C109 117 75 151 75 192v640c0 41 34 75 75 75h725c41 0 75-34 75-75V192c0-41-34-75-75-75zm-725 64h725c6 0 11 4 11 11v288h-747V192c0-6 4-11 11-11zm725 661H149c-6 0-11-4-11-11V544h747V832c0 6-4 11-11 11z M40 9 15 23 15 31 9 28 9 20 34 5 24 0 0 14 0 34 25 48 25 28 49 14zM26 29 26 48 49 34 49 15z diff --git a/src/Resources/Locales/en_US.axaml b/src/Resources/Locales/en_US.axaml index 271a52dd..3a6ef2bd 100644 --- a/src/Resources/Locales/en_US.axaml +++ b/src/Resources/Locales/en_US.axaml @@ -36,6 +36,7 @@ Select archive file path Revision: Archive + SourceGit Askpass FILES ASSUME UNCHANGED NO FILES ASSUMED AS UNCHANGED REMOVE diff --git a/src/Resources/Locales/zh_CN.axaml b/src/Resources/Locales/zh_CN.axaml index adf03079..51ece14d 100644 --- a/src/Resources/Locales/zh_CN.axaml +++ b/src/Resources/Locales/zh_CN.axaml @@ -39,6 +39,7 @@ 选择存档文件的存放路径 指定的提交: 存档 + SourceGit Askpass 不跟踪更改的文件 没有不跟踪更改的文件 移除 diff --git a/src/Resources/Locales/zh_TW.axaml b/src/Resources/Locales/zh_TW.axaml index d8d22032..bb5bd996 100644 --- a/src/Resources/Locales/zh_TW.axaml +++ b/src/Resources/Locales/zh_TW.axaml @@ -39,6 +39,7 @@ 選擇存檔檔案的存放路徑 指定的提交: 存檔 + SourceGit Askpass 不跟蹤更改的檔案 沒有不跟蹤更改的檔案 移除 diff --git a/src/ViewModels/Launcher.cs b/src/ViewModels/Launcher.cs index df5cf641..c0b376ba 100644 --- a/src/ViewModels/Launcher.cs +++ b/src/ViewModels/Launcher.cs @@ -28,12 +28,11 @@ namespace SourceGit.ViewModels } } - public Launcher() + public Launcher(string[] commandlines) { Pages = new AvaloniaList(); AddNewTab(); - - var commandlines = Environment.GetCommandLineArgs(); + if (commandlines.Length == 2) { var path = commandlines[1]; diff --git a/src/Views/Askpass.axaml b/src/Views/Askpass.axaml new file mode 100644 index 00000000..11d87a45 --- /dev/null +++ b/src/Views/Askpass.axaml @@ -0,0 +1,81 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +