From 6799111314c9664e218e27c9abdbc14700f77832 Mon Sep 17 00:00:00 2001 From: leo Date: Mon, 21 Jul 2025 10:43:52 +0800 Subject: [PATCH 001/591] revert: revert changes introduced by commit f487990bc0 Signed-off-by: leo --- src/Views/CommitMessageEditor.axaml.cs | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/src/Views/CommitMessageEditor.axaml.cs b/src/Views/CommitMessageEditor.axaml.cs index d15de55dd..868f95b5b 100644 --- a/src/Views/CommitMessageEditor.axaml.cs +++ b/src/Views/CommitMessageEditor.axaml.cs @@ -17,14 +17,23 @@ public void AsStandalone(string file) { _onSave = msg => File.WriteAllText(file, msg); _shouldExitApp = true; - Editor.Text = File.ReadAllText(file).ReplaceLineEndings("\n").Trim(); + + var content = File.ReadAllText(file).ReplaceLineEndings("\n").Trim(); + var parts = content.Split('\n', 2); + Editor.SubjectEditor.Text = parts[0]; + if (parts.Length > 1) + Editor.DescriptionEditor.Text = parts[1].Trim(); } public void AsBuiltin(string msg, Action onSave) { _onSave = onSave; _shouldExitApp = false; - Editor.Text = msg.Trim(); + + var parts = msg.Split('\n', 2); + Editor.SubjectEditor.Text = parts[0]; + if (parts.Length > 1) + Editor.DescriptionEditor.Text = parts[1].Trim(); } protected override void OnClosed(EventArgs e) From a39f2e2fee50c59438038c8340ff83832c9e117a Mon Sep 17 00:00:00 2001 From: heartacker <1876302+heartacker@users.noreply.github.com> Date: Mon, 21 Jul 2025 11:40:00 +0800 Subject: [PATCH 002/591] feature: add `gerrit` change-id rule (#1600) - git hash id length is 40 - gerrit change-id length is 41, and must startwith "I" --- src/Resources/Locales/en_US.axaml | 1 + src/ViewModels/RepositoryConfigure.cs | 5 +++++ src/Views/RepositoryConfigure.axaml | 1 + 3 files changed, 7 insertions(+) diff --git a/src/Resources/Locales/en_US.axaml b/src/Resources/Locales/en_US.axaml index 2166f05f4..6403c6652 100644 --- a/src/Resources/Locales/en_US.axaml +++ b/src/Resources/Locales/en_US.axaml @@ -201,6 +201,7 @@ Add Sample GitLab Issue Rule Add Sample GitLab Merge Request Rule Add Sample Jira Rule + Add Gerrit Change-Id Commit Rule New Rule Issue Regex Expression: Rule Name: diff --git a/src/ViewModels/RepositoryConfigure.cs b/src/ViewModels/RepositoryConfigure.cs index f118a66d0..72f7c2e89 100644 --- a/src/ViewModels/RepositoryConfigure.cs +++ b/src/ViewModels/RepositoryConfigure.cs @@ -213,6 +213,11 @@ public void AddSampleGitHubIssueTracker() SelectedIssueTrackerRule = _repo.Settings.AddIssueTracker("GitHub ISSUE", @"#(\d+)", link); } + public void AddSampleGerritChangeIdCommitTracker() + { + SelectedIssueTrackerRule = _repo.Settings.AddIssueTracker("Gerrit Change-Id", @"(I[A-Za-z0-9]{40})", "https://gerrit.yourcompany.com/q/$1"); + } + public void AddSampleJiraIssueTracker() { SelectedIssueTrackerRule = _repo.Settings.AddIssueTracker("Jira Tracker", @"PROJ-(\d+)", "https://jira.yourcompany.com/browse/PROJ-$1"); diff --git a/src/Views/RepositoryConfigure.axaml b/src/Views/RepositoryConfigure.axaml index f243540cb..f786a3d84 100644 --- a/src/Views/RepositoryConfigure.axaml +++ b/src/Views/RepositoryConfigure.axaml @@ -324,6 +324,7 @@ + From ca24a55644484842c3621b440bfbc2bfd739e9ff Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Mon, 21 Jul 2025 03:40:12 +0000 Subject: [PATCH 003/591] doc: Update translation status and sort locale files --- TRANSLATION.md | 63 +++++++++++++++++++++++++++++++++++++++++--------- 1 file changed, 52 insertions(+), 11 deletions(-) diff --git a/TRANSLATION.md b/TRANSLATION.md index 65f1b5142..c6125039e 100644 --- a/TRANSLATION.md +++ b/TRANSLATION.md @@ -6,11 +6,25 @@ This document shows the translation status of each locale file in the repository ### ![en_US](https://img.shields.io/badge/en__US-%E2%88%9A-brightgreen) -### ![de__DE](https://img.shields.io/badge/de__DE-%E2%88%9A-brightgreen) +### ![de__DE](https://img.shields.io/badge/de__DE-99.88%25-yellow) -### ![es__ES](https://img.shields.io/badge/es__ES-%E2%88%9A-brightgreen) +
+Missing keys in de_DE.axaml + +- Text.Configure.IssueTracker.AddSampleGerritChangeIdCommit + +
+ +### ![es__ES](https://img.shields.io/badge/es__ES-99.88%25-yellow) + +
+Missing keys in es_ES.axaml + +- Text.Configure.IssueTracker.AddSampleGerritChangeIdCommit + +
-### ![fr__FR](https://img.shields.io/badge/fr__FR-82.16%25-yellow) +### ![fr__FR](https://img.shields.io/badge/fr__FR-82.06%25-yellow)
Missing keys in fr_FR.axaml @@ -64,6 +78,7 @@ This document shows the translation status of each locale file in the repository - Text.Configure.CustomAction.InputControls.Tip - Text.Configure.CustomAction.Scope.Tag - Text.Configure.Git.PreferredMergeMode +- Text.Configure.IssueTracker.AddSampleGerritChangeIdCommit - Text.Configure.IssueTracker.Share - Text.ConfigureCustomActionControls - Text.ConfigureCustomActionControls.CheckedValue @@ -170,7 +185,7 @@ This document shows the translation status of each locale file in the repository
-### ![it__IT](https://img.shields.io/badge/it__IT-87.44%25-yellow) +### ![it__IT](https://img.shields.io/badge/it__IT-87.34%25-yellow)
Missing keys in it_IT.axaml @@ -211,6 +226,7 @@ This document shows the translation status of each locale file in the repository - Text.Configure.CustomAction.InputControls.Edit - Text.Configure.CustomAction.InputControls.Tip - Text.Configure.CustomAction.Scope.Tag +- Text.Configure.IssueTracker.AddSampleGerritChangeIdCommit - Text.Configure.IssueTracker.Share - Text.ConfigureCustomActionControls - Text.ConfigureCustomActionControls.CheckedValue @@ -285,7 +301,7 @@ This document shows the translation status of each locale file in the repository
-### ![ja__JP](https://img.shields.io/badge/ja__JP-82.16%25-yellow) +### ![ja__JP](https://img.shields.io/badge/ja__JP-82.06%25-yellow)
Missing keys in ja_JP.axaml @@ -340,6 +356,7 @@ This document shows the translation status of each locale file in the repository - Text.Configure.CustomAction.InputControls.Tip - Text.Configure.CustomAction.Scope.Tag - Text.Configure.Git.PreferredMergeMode +- Text.Configure.IssueTracker.AddSampleGerritChangeIdCommit - Text.Configure.IssueTracker.Share - Text.ConfigureCustomActionControls - Text.ConfigureCustomActionControls.CheckedValue @@ -445,7 +462,7 @@ This document shows the translation status of each locale file in the repository
-### ![pt__BR](https://img.shields.io/badge/pt__BR-75.12%25-yellow) +### ![pt__BR](https://img.shields.io/badge/pt__BR-75.03%25-yellow)
Missing keys in pt_BR.axaml @@ -517,6 +534,7 @@ This document shows the translation status of each locale file in the repository - Text.Configure.Git.PreferredMergeMode - Text.Configure.IssueTracker.AddSampleGiteeIssue - Text.Configure.IssueTracker.AddSampleGiteePullRequest +- Text.Configure.IssueTracker.AddSampleGerritChangeIdCommit - Text.Configure.IssueTracker.Share - Text.ConfigureCustomActionControls - Text.ConfigureCustomActionControls.CheckedValue @@ -665,9 +683,16 @@ This document shows the translation status of each locale file in the repository
-### ![ru__RU](https://img.shields.io/badge/ru__RU-%E2%88%9A-brightgreen) +### ![ru__RU](https://img.shields.io/badge/ru__RU-99.88%25-yellow) + +
+Missing keys in ru_RU.axaml + +- Text.Configure.IssueTracker.AddSampleGerritChangeIdCommit + +
-### ![ta__IN](https://img.shields.io/badge/ta__IN-82.28%25-yellow) +### ![ta__IN](https://img.shields.io/badge/ta__IN-82.18%25-yellow)
Missing keys in ta_IN.axaml @@ -722,6 +747,7 @@ This document shows the translation status of each locale file in the repository - Text.Configure.CustomAction.InputControls.Tip - Text.Configure.CustomAction.Scope.Tag - Text.Configure.Git.PreferredMergeMode +- Text.Configure.IssueTracker.AddSampleGerritChangeIdCommit - Text.Configure.IssueTracker.Share - Text.ConfigureCustomActionControls - Text.ConfigureCustomActionControls.CheckedValue @@ -826,7 +852,7 @@ This document shows the translation status of each locale file in the repository
-### ![uk__UA](https://img.shields.io/badge/uk__UA-83.45%25-yellow) +### ![uk__UA](https://img.shields.io/badge/uk__UA-83.35%25-yellow)
Missing keys in uk_UA.axaml @@ -879,6 +905,7 @@ This document shows the translation status of each locale file in the repository - Text.Configure.CustomAction.InputControls.Edit - Text.Configure.CustomAction.InputControls.Tip - Text.Configure.CustomAction.Scope.Tag +- Text.Configure.IssueTracker.AddSampleGerritChangeIdCommit - Text.Configure.IssueTracker.Share - Text.ConfigureCustomActionControls - Text.ConfigureCustomActionControls.CheckedValue @@ -975,6 +1002,20 @@ This document shows the translation status of each locale file in the repository
-### ![zh__CN](https://img.shields.io/badge/zh__CN-%E2%88%9A-brightgreen) +### ![zh__CN](https://img.shields.io/badge/zh__CN-99.88%25-yellow) + +
+Missing keys in zh_CN.axaml + +- Text.Configure.IssueTracker.AddSampleGerritChangeIdCommit + +
+ +### ![zh__TW](https://img.shields.io/badge/zh__TW-99.88%25-yellow) + +
+Missing keys in zh_TW.axaml + +- Text.Configure.IssueTracker.AddSampleGerritChangeIdCommit -### ![zh__TW](https://img.shields.io/badge/zh__TW-%E2%88%9A-brightgreen) \ No newline at end of file +
\ No newline at end of file From 47a184a07325590a9ad01b4c49021811f1da9d9e Mon Sep 17 00:00:00 2001 From: leo Date: Mon, 21 Jul 2025 11:42:47 +0800 Subject: [PATCH 004/591] code_review: PR #1600 Keeps translation keys in order and add missing translations for Chinese Signed-off-by: leo --- src/Resources/Locales/en_US.axaml | 2 +- src/Resources/Locales/zh_CN.axaml | 1 + src/Resources/Locales/zh_TW.axaml | 1 + 3 files changed, 3 insertions(+), 1 deletion(-) diff --git a/src/Resources/Locales/en_US.axaml b/src/Resources/Locales/en_US.axaml index 6403c6652..d45db601b 100644 --- a/src/Resources/Locales/en_US.axaml +++ b/src/Resources/Locales/en_US.axaml @@ -195,13 +195,13 @@ Preferred Merge Mode ISSUE TRACKER Add Sample Azure DevOps Rule + Add Gerrit Change-Id Commit Rule Add Sample Gitee Issue Rule Add Sample Gitee Pull Request Rule Add Sample GitHub Rule Add Sample GitLab Issue Rule Add Sample GitLab Merge Request Rule Add Sample Jira Rule - Add Gerrit Change-Id Commit Rule New Rule Issue Regex Expression: Rule Name: diff --git a/src/Resources/Locales/zh_CN.axaml b/src/Resources/Locales/zh_CN.axaml index 1f024429f..e97015e9c 100644 --- a/src/Resources/Locales/zh_CN.axaml +++ b/src/Resources/Locales/zh_CN.axaml @@ -199,6 +199,7 @@ 默认合并方式 ISSUE追踪 新增匹配Azure DevOps规则 + 新增匹配Gerrit Change-Id规则 新增匹配Gitee议题规则 新增匹配Gitee合并请求规则 新增匹配GitHub Issue规则 diff --git a/src/Resources/Locales/zh_TW.axaml b/src/Resources/Locales/zh_TW.axaml index dee236eb3..e5b04b8b6 100644 --- a/src/Resources/Locales/zh_TW.axaml +++ b/src/Resources/Locales/zh_TW.axaml @@ -199,6 +199,7 @@ 預設合併模式 Issue 追蹤 新增符合 Azure DevOps 規則 + 新增符合 Gerrit Change-Id 規則 新增符合 Gitee 議題規則 新增符合 Gitee 合併請求規則 新增符合 GitHub Issue 規則 From 64ec73abf23d9ae476db10fb40a5c804f44826d2 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Mon, 21 Jul 2025 03:43:07 +0000 Subject: [PATCH 005/591] doc: Update translation status and sort locale files --- TRANSLATION.md | 20 +++----------------- 1 file changed, 3 insertions(+), 17 deletions(-) diff --git a/TRANSLATION.md b/TRANSLATION.md index c6125039e..d44694c96 100644 --- a/TRANSLATION.md +++ b/TRANSLATION.md @@ -532,9 +532,9 @@ This document shows the translation status of each locale file in the repository - Text.Configure.CustomAction.Scope.Tag - Text.Configure.CustomAction.WaitForExit - Text.Configure.Git.PreferredMergeMode +- Text.Configure.IssueTracker.AddSampleGerritChangeIdCommit - Text.Configure.IssueTracker.AddSampleGiteeIssue - Text.Configure.IssueTracker.AddSampleGiteePullRequest -- Text.Configure.IssueTracker.AddSampleGerritChangeIdCommit - Text.Configure.IssueTracker.Share - Text.ConfigureCustomActionControls - Text.ConfigureCustomActionControls.CheckedValue @@ -1002,20 +1002,6 @@ This document shows the translation status of each locale file in the repository -### ![zh__CN](https://img.shields.io/badge/zh__CN-99.88%25-yellow) - -
-Missing keys in zh_CN.axaml - -- Text.Configure.IssueTracker.AddSampleGerritChangeIdCommit - -
- -### ![zh__TW](https://img.shields.io/badge/zh__TW-99.88%25-yellow) - -
-Missing keys in zh_TW.axaml - -- Text.Configure.IssueTracker.AddSampleGerritChangeIdCommit +### ![zh__CN](https://img.shields.io/badge/zh__CN-%E2%88%9A-brightgreen) -
\ No newline at end of file +### ![zh__TW](https://img.shields.io/badge/zh__TW-%E2%88%9A-brightgreen) \ No newline at end of file From e2aacf26e36f01403a42fcd9dc5914a589524cd8 Mon Sep 17 00:00:00 2001 From: leo Date: Mon, 21 Jul 2025 11:54:48 +0800 Subject: [PATCH 006/591] localization: update translations for sample issuetracker rules Signed-off-by: leo --- src/Resources/Locales/en_US.axaml | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/src/Resources/Locales/en_US.axaml b/src/Resources/Locales/en_US.axaml index d45db601b..a0485365c 100644 --- a/src/Resources/Locales/en_US.axaml +++ b/src/Resources/Locales/en_US.axaml @@ -194,14 +194,14 @@ Default Remote Preferred Merge Mode ISSUE TRACKER - Add Sample Azure DevOps Rule + Add Azure DevOps Rule Add Gerrit Change-Id Commit Rule - Add Sample Gitee Issue Rule - Add Sample Gitee Pull Request Rule - Add Sample GitHub Rule - Add Sample GitLab Issue Rule - Add Sample GitLab Merge Request Rule - Add Sample Jira Rule + Add Gitee Issue Rule + Add Gitee Pull Request Rule + Add GitHub Rule + Add GitLab Issue Rule + Add GitLab Merge Request Rule + Add Jira Rule New Rule Issue Regex Expression: Rule Name: From 2fa95c0dfc0880799cd3eea3a73d3878a16423b5 Mon Sep 17 00:00:00 2001 From: leo Date: Mon, 21 Jul 2025 12:23:28 +0800 Subject: [PATCH 007/591] enhance: do not trigger `OnPopupSure` when there's a `TextBox` focused (#1606) Signed-off-by: leo --- src/Resources/Locales/de_DE.axaml | 1 - src/Resources/Locales/en_US.axaml | 1 - src/Resources/Locales/es_ES.axaml | 1 - src/Resources/Locales/fr_FR.axaml | 1 - src/Resources/Locales/it_IT.axaml | 1 - src/Resources/Locales/ja_JP.axaml | 1 - src/Resources/Locales/pt_BR.axaml | 1 - src/Resources/Locales/ru_RU.axaml | 1 - src/Resources/Locales/ta_IN.axaml | 1 - src/Resources/Locales/uk_UA.axaml | 1 - src/Resources/Locales/zh_CN.axaml | 1 - src/Resources/Locales/zh_TW.axaml | 1 - src/Views/LauncherPage.axaml | 4 +++- src/Views/LauncherPage.axaml.cs | 28 ++++++++++++++++++++++++++++ src/Views/Reword.axaml | 5 ----- src/Views/Squash.axaml | 5 ----- src/Views/StashChanges.axaml | 15 ++++----------- 17 files changed, 35 insertions(+), 34 deletions(-) diff --git a/src/Resources/Locales/de_DE.axaml b/src/Resources/Locales/de_DE.axaml index 7bbeca503..bb78b8e7c 100644 --- a/src/Resources/Locales/de_DE.axaml +++ b/src/Resources/Locales/de_DE.axaml @@ -515,7 +515,6 @@ Vor {0} Monaten Vor {0} Jahren Gestern - Verwende 'Shift+Enter' um eine neue Zeile einzufügen. 'Enter' ist das Kürzel für den OK Button Einstellungen OPEN AI System-Prompt für Diff Analyse diff --git a/src/Resources/Locales/en_US.axaml b/src/Resources/Locales/en_US.axaml index a0485365c..655f6e976 100644 --- a/src/Resources/Locales/en_US.axaml +++ b/src/Resources/Locales/en_US.axaml @@ -512,7 +512,6 @@ {0} months ago {0} years ago Yesterday - Use 'Shift+Enter' to input a new line. 'Enter' is the hotkey of OK button Preferences AI Analyze Diff Prompt diff --git a/src/Resources/Locales/es_ES.axaml b/src/Resources/Locales/es_ES.axaml index 9e0c08de0..4ef06eae9 100644 --- a/src/Resources/Locales/es_ES.axaml +++ b/src/Resources/Locales/es_ES.axaml @@ -515,7 +515,6 @@ Hace {0} meses Hace {0} años Ayer - Usa 'Shift+Enter' para introducir una nueva línea. 'Enter' es el atajo del botón OK Preferencias OPEN AI Analizar Diff Prompt diff --git a/src/Resources/Locales/fr_FR.axaml b/src/Resources/Locales/fr_FR.axaml index e5e443ea1..919dfeac8 100644 --- a/src/Resources/Locales/fr_FR.axaml +++ b/src/Resources/Locales/fr_FR.axaml @@ -423,7 +423,6 @@ il y a {0} mois il y a {0} ans Hier - Utiliser 'Maj+Entrée' pour insérer une nouvelle ligne. 'Entrée' est la touche pour valider Préférences IA Analyser Diff Prompt diff --git a/src/Resources/Locales/it_IT.axaml b/src/Resources/Locales/it_IT.axaml index c83a8bdb9..c12e3e53c 100644 --- a/src/Resources/Locales/it_IT.axaml +++ b/src/Resources/Locales/it_IT.axaml @@ -443,7 +443,6 @@ {0} mesi fa {0} anni fa Ieri - Usa 'Shift+Enter' per inserire una nuova riga. 'Enter' è il tasto rapido per il pulsante OK Preferenze AI Analizza il Prompt Differenza diff --git a/src/Resources/Locales/ja_JP.axaml b/src/Resources/Locales/ja_JP.axaml index 63e4736d1..32c41f216 100644 --- a/src/Resources/Locales/ja_JP.axaml +++ b/src/Resources/Locales/ja_JP.axaml @@ -422,7 +422,6 @@ {0} ヶ月前 {0} 年前 昨日 - 改行には'Shift+Enter'キーを使用します。 'Enter"はOKボタンのホットキーとして機能します。 設定 AI 差分分析プロンプト diff --git a/src/Resources/Locales/pt_BR.axaml b/src/Resources/Locales/pt_BR.axaml index 87a14a3af..3fbac432f 100644 --- a/src/Resources/Locales/pt_BR.axaml +++ b/src/Resources/Locales/pt_BR.axaml @@ -385,7 +385,6 @@ {0} meses atrás {0} anos atrás Ontem - Use 'Shift+Enter' para inserir uma nova linha. 'Enter' é a tecla de atalho do botão OK Preferências INTELIGÊNCIA ARTIFICIAL Prompt para Analisar Diff diff --git a/src/Resources/Locales/ru_RU.axaml b/src/Resources/Locales/ru_RU.axaml index c5472492a..413b99e32 100644 --- a/src/Resources/Locales/ru_RU.axaml +++ b/src/Resources/Locales/ru_RU.axaml @@ -515,7 +515,6 @@ {0} месяцев назад {0} лет назад Вчера - Используйте «Shift+Enter» для ввода новой строки. «Enter» - это горячая клавиша кнопки «OK» Параметры ОТКРЫТЬ ИИ Запрос на анализ сравнения diff --git a/src/Resources/Locales/ta_IN.axaml b/src/Resources/Locales/ta_IN.axaml index 7604b3bd2..3a0c8e532 100644 --- a/src/Resources/Locales/ta_IN.axaml +++ b/src/Resources/Locales/ta_IN.axaml @@ -422,7 +422,6 @@ {0} திங்களுக்கு முன்பு {0} ஆண்டுகளுக்கு முன்பு நேற்று - புதிய வரியை உள்ளிட 'உயர்த்து+நுழை' ஐப் பயன்படுத்தவும். 'நுழை' என்பது சரி பொத்தானின் சூடானவிசை ஆகும் விருப்பத்தேர்வுகள் செநு வேறுபாடு உடனடியாக பகுப்பாய்வு செய் diff --git a/src/Resources/Locales/uk_UA.axaml b/src/Resources/Locales/uk_UA.axaml index b4b4ccbfd..46e42fcda 100644 --- a/src/Resources/Locales/uk_UA.axaml +++ b/src/Resources/Locales/uk_UA.axaml @@ -427,7 +427,6 @@ {0} місяців тому {0} років тому Вчора - Використовуйте 'Shift+Enter' для введення нового рядка. 'Enter' - гаряча клавіша кнопки OK Налаштування AI Промпт для аналізу різниці diff --git a/src/Resources/Locales/zh_CN.axaml b/src/Resources/Locales/zh_CN.axaml index e97015e9c..711ab12f3 100644 --- a/src/Resources/Locales/zh_CN.axaml +++ b/src/Resources/Locales/zh_CN.axaml @@ -516,7 +516,6 @@ {0}个月前 {0}年前 昨天 - 请使用Shift+Enter换行。Enter键已被【确 定】按钮占用。 偏好设置 AI Analyze Diff Prompt diff --git a/src/Resources/Locales/zh_TW.axaml b/src/Resources/Locales/zh_TW.axaml index e5b04b8b6..61956e97f 100644 --- a/src/Resources/Locales/zh_TW.axaml +++ b/src/Resources/Locales/zh_TW.axaml @@ -516,7 +516,6 @@ {0} 個月前 {0} 年前 昨天 - 請使用 Shift + Enter 換行。Enter 鍵已被 [確定] 按鈕佔用。 偏好設定 AI 分析變更差異提示詞 diff --git a/src/Views/LauncherPage.axaml b/src/Views/LauncherPage.axaml index d3e6a8070..82fa5efb8 100644 --- a/src/Views/LauncherPage.axaml +++ b/src/Views/LauncherPage.axaml @@ -76,6 +76,9 @@ Orientation="Horizontal" HorizontalAlignment="Right" IsVisible="{Binding InProgress, Converter={x:Static BoolConverters.Not}}"> + @@ -67,13 +69,8 @@ - - + + - - - - - From a6f29df3c23fef498159483f4303d8e23d08623f Mon Sep 17 00:00:00 2001 From: leo Date: Mon, 11 Aug 2025 09:35:49 +0800 Subject: [PATCH 086/591] ux: new style for `RadionButton.switch_button` (#1707) Signed-off-by: leo --- src/Resources/Styles.axaml | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/src/Resources/Styles.axaml b/src/Resources/Styles.axaml index a987a83c8..a886f0651 100644 --- a/src/Resources/Styles.axaml +++ b/src/Resources/Styles.axaml @@ -1096,16 +1096,22 @@ + - + From 730e54d90d1aadf4436dfcef6933ee056b0e617d Mon Sep 17 00:00:00 2001 From: Chiahong <36815907+ChiahongHong@users.noreply.github.com> Date: Mon, 11 Aug 2025 09:36:15 +0800 Subject: [PATCH 087/591] localization: update zh_TW.axaml (#1706) --- src/Resources/Locales/zh_TW.axaml | 56 +++++++++++++++---------------- 1 file changed, 28 insertions(+), 28 deletions(-) diff --git a/src/Resources/Locales/zh_TW.axaml b/src/Resources/Locales/zh_TW.axaml index b7b014767..73970aae8 100644 --- a/src/Resources/Locales/zh_TW.axaml +++ b/src/Resources/Locales/zh_TW.axaml @@ -7,8 +7,8 @@ 關於 SourceGit 開源免費的 Git 客戶端 新增忽略檔案 - 匹配模式 : - 儲存路徑 : + 比對模式: + 儲存路徑: 新增工作區 工作區路徑: 填寫該工作區的路徑。支援相對路徑。 @@ -98,9 +98,9 @@ 擱置變更並自動復原 同時更新所有子模組 目標分支: - 您目前的分離的 HEAD 包含與任何分支/標籤無關的提交!您要繼續嗎? + 您目前的分離的 HEAD 包含與任何分支/標籤無關的提交! 您要繼續嗎? 簽出分支並快轉 - 上游分支 : + 上游分支: 揀選提交 提交資訊中追加來源資訊 提交列表: @@ -114,7 +114,7 @@ 其他複製參數,選填。 本機存放庫名稱: 本機存放庫目錄的名稱,選填。 - 父級目錄: + 上層目錄: 初始化並更新子模組 遠端存放庫: 關閉 @@ -160,7 +160,7 @@ 檢視包含此提交的分支或標籤 本提交包含於以下分支或標籤 僅顯示前 100 項變更。請前往 [變更對比] 頁面以瀏覽所有變更。 - 簽名鑰匙: + 簽章金鑰: 提交訊息 前次提交 相關參照 @@ -172,16 +172,16 @@ 填寫提交訊息標題 存放庫設定 提交訊息範本 - 您可以使用 ${files_num}、${branch_name}、${files} 或 ${files:N} 其中 N 是要輸出的檔案路徑的最大數目。 + 您可以使用 ${files_num}、${branch_name}、${files} 或 ${files:N},其中 N 是要輸出的檔案路徑的最大數目。 範本內容: 範本名稱: 自訂動作 指令參數: 內建參數: ${REPO} 存放庫路徑、${BRANCH} 所選的分支、${SHA} 所選的提交編號、${TAG} 所選的標籤 可執行檔案路徑: - 輸入控件: + 輸入控制項: 編輯 - 請使用占位符如 $1, $2 來代表輸入控制項的值 + 請使用 $1、$2 等變數來代表輸入控制項的值 名稱: 執行範圍: 選取的分支 @@ -208,7 +208,7 @@ 新增自訂規則 符合 Issue 的正規表達式: 規則名稱: - 寫入 .issuetracker 檔案以分享此規則 + 寫入 .issuetracker 檔案以共用此規則 為 Issue 產生的網址連結: 可在網址中使用 $1、$2 等變數填入正規表達式相符的內容 AI @@ -218,9 +218,9 @@ HTTP 網路代理 使用者名稱 用於本存放庫的使用者名稱 - 編輯自訂動作輸入控件 - 啟用時的指令行參數: - 勾選 CheckBox 後,此值將用於命令列參數中 + 編輯自訂動作輸入控制項 + 啟用時的指令參數: + 勾選 CheckBox 後,此值將用於指令參數中 描述: 預設值: 目標路徑是否為資料夾: @@ -237,7 +237,7 @@ 自動暫存並提交 未包含任何檔案變更! 您是否仍要提交 (--allow-empty) 或者自動暫存全部變更並提交? 系統提示 - 您需要重新啟動此應用程式才能套用變更! + 您需要重新啟動此應用程式才能套用變更! 產生約定式提交訊息 破壞性變更: 關閉的 Issue: @@ -275,8 +275,8 @@ 按住 Ctrl 鍵將直接以預設參數執行 剪下 取消初始化子模組 - 強制取消,即使它包含本地變更 - 子模組 : + 強制取消,即使包含本機變更 + 子模組: 刪除分支確認 分支名稱: 您正在刪除遠端上的分支,請務必小心! @@ -302,7 +302,7 @@ 忽略空白符號變化 混合對比 並排對比 - 填充對比 + 滑桿對比 最後一個差異 LFS 物件變更 變更後 @@ -555,7 +555,7 @@ 電子郵件 預設 Git 使用者電子郵件 拉取變更時進行清理 (--prune) - 對比檔案時,預設忽略行尾的 CR 變更 (--ignore-cr-at-eol) + 對比檔案時,預設忽略行末的 CR 變更 (--ignore-cr-at-eol) 本軟體要求 Git 最低版本為 2.25.1 安裝路徑 啟用 HTTP SSL 驗證 @@ -634,14 +634,14 @@ 依建立時間 依名稱升序 清理本存放庫 (GC) - 本操作將執行 `git gc` 命令。 + 本操作將執行 `git gc` 指令。 清空篩選規則 清空 設定本存放庫 下一步 自訂動作 沒有自訂的動作 - 主頁 + 首頁 捨棄所有變更 在檔案瀏覽器中開啟 快速搜尋分支/標籤/子模組 @@ -676,7 +676,7 @@ 只顯示分支和標籤引用的提交 只顯示合併提交的第一父提交 顯示內容 - 顯示遺失參照的提交 + 顯示失去參照的提交 以樹型結構展示 以樹型結構展示 跳過此提交 @@ -702,8 +702,8 @@ 移至提交: 目前分支: 重設選取的分支(非目前分支) - 重設位置 : - 選取分支 : + 重設位置: + 選取分支: 在檔案瀏覽器中檢視 復原操作確認 目標提交: @@ -726,7 +726,7 @@ 子模組: 目前追蹤分支: 變更為: - 選購。為空時設定為預設值。 + 選填。留空時設定為預設值。 切換上游分支 本機分支: 取消設定上游分支 @@ -784,11 +784,11 @@ 更新 存放庫 確 定 - 創建者 - 創建時間 + 建立者 + 建立時間 標籤訊息 標籤名稱 - 創建者 + 建立者 複製標籤名稱 自訂動作 刪除 ${0}$... @@ -796,7 +796,7 @@ 推送 ${0}$... 更新子模組 更新所有子模組 - 如果子模組尚未初始化,則初始化它 + 如果子模組尚未初始化,則將其初始化 遞迴更新子孫模組 子模組: 更新至子模組的遠端追蹤分支 From d04bde91c1d4823c77636a3a29f44e4ec33bba66 Mon Sep 17 00:00:00 2001 From: AquariusStar <48148723+AquariusStar@users.noreply.github.com> Date: Mon, 11 Aug 2025 04:36:40 +0300 Subject: [PATCH 088/591] localization: update Russian translate (#1708) --- src/Resources/Locales/ru_RU.axaml | 1 + 1 file changed, 1 insertion(+) diff --git a/src/Resources/Locales/ru_RU.axaml b/src/Resources/Locales/ru_RU.axaml index 1842b1352..e1bd68748 100644 --- a/src/Resources/Locales/ru_RU.axaml +++ b/src/Resources/Locales/ru_RU.axaml @@ -789,6 +789,7 @@ Сообщение Имя Разметчик + Копировать имя метки Пользовательское действие Удалить ${0}$... Влить ${0}$ в ${1}$... From ec6b2d716d50425536c45cd0067dde7c7deff9fb Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Mon, 11 Aug 2025 01:36:57 +0000 Subject: [PATCH 089/591] doc: Update translation status and sort locale files --- TRANSLATION.md | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) diff --git a/TRANSLATION.md b/TRANSLATION.md index f41f87747..c9d299549 100644 --- a/TRANSLATION.md +++ b/TRANSLATION.md @@ -722,14 +722,7 @@ This document shows the translation status of each locale file in the repository -### ![ru__RU](https://img.shields.io/badge/ru__RU-99.88%25-yellow) - -
-Missing keys in ru_RU.axaml - -- Text.TagCM.CopyName - -
+### ![ru__RU](https://img.shields.io/badge/ru__RU-%E2%88%9A-brightgreen) ### ![ta__IN](https://img.shields.io/badge/ta__IN-81.35%25-yellow) From ceeca2d2c1edfb829ecea90fdfdaed882ddd6382 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Javier=20J=2E=20Mart=C3=ADnez=20M=2E?= <56406225+jjesus-dev@users.noreply.github.com> Date: Sun, 10 Aug 2025 19:38:22 -0600 Subject: [PATCH 090/591] localization: update spanish translations (#1709) * localization: update spanish translations Add missing translations. * doc: Update translation status and sort locale files --------- Co-authored-by: github-actions[bot] --- TRANSLATION.md | 9 +-------- src/Resources/Locales/es_ES.axaml | 1 + 2 files changed, 2 insertions(+), 8 deletions(-) diff --git a/TRANSLATION.md b/TRANSLATION.md index c9d299549..4eb6e8bdb 100644 --- a/TRANSLATION.md +++ b/TRANSLATION.md @@ -22,14 +22,7 @@ This document shows the translation status of each locale file in the repository -### ![es__ES](https://img.shields.io/badge/es__ES-99.88%25-yellow) - -
-Missing keys in es_ES.axaml - -- Text.TagCM.CopyName - -
+### ![es__ES](https://img.shields.io/badge/es__ES-%E2%88%9A-brightgreen) ### ![fr__FR](https://img.shields.io/badge/fr__FR-81.24%25-yellow) diff --git a/src/Resources/Locales/es_ES.axaml b/src/Resources/Locales/es_ES.axaml index b33330379..a6c995948 100644 --- a/src/Resources/Locales/es_ES.axaml +++ b/src/Resources/Locales/es_ES.axaml @@ -789,6 +789,7 @@ Mensaje Nombre Etiquetador + Copiar Nombre de la Etiqueta Acción Personalizada Eliminar ${0}$... Merge ${0}$ en ${1}$... From f6f7403fc116b5fbeb8c5902735f8d8a0e261146 Mon Sep 17 00:00:00 2001 From: leo Date: Mon, 11 Aug 2025 09:42:04 +0800 Subject: [PATCH 091/591] version: Release 2025.30 Signed-off-by: leo --- VERSION | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/VERSION b/VERSION index 9851c0cad..9b98ca385 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -2025.29 \ No newline at end of file +2025.30 \ No newline at end of file From 4dcfa352e5287b7993de8ebfebb8c971be6786b2 Mon Sep 17 00:00:00 2001 From: leo Date: Mon, 11 Aug 2025 13:23:15 +0800 Subject: [PATCH 092/591] ux: new branch tooltip style (#1693) Signed-off-by: leo --- src/Resources/Locales/en_US.axaml | 2 +- src/Resources/Locales/zh_CN.axaml | 1 + src/Resources/Locales/zh_TW.axaml | 1 + src/Views/BranchTree.axaml | 5 ++++- src/Views/FilterModeSwitchButton.axaml | 3 ++- 5 files changed, 9 insertions(+), 3 deletions(-) diff --git a/src/Resources/Locales/en_US.axaml b/src/Resources/Locales/en_US.axaml index 872ffe677..75adc24b2 100644 --- a/src/Resources/Locales/en_US.axaml +++ b/src/Resources/Locales/en_US.axaml @@ -70,10 +70,10 @@ Reset ${0}$ to ${1}$... Set Tracking Branch... Branch Compare + Invalid REMOTE TRACKING URL - Invalid upstream! CANCEL Reset to Parent Revision Reset to This Revision diff --git a/src/Resources/Locales/zh_CN.axaml b/src/Resources/Locales/zh_CN.axaml index 866a600f7..f9505b5ab 100644 --- a/src/Resources/Locales/zh_CN.axaml +++ b/src/Resources/Locales/zh_CN.axaml @@ -74,6 +74,7 @@ 重置 ${0}$ 到 ${1}$... 切换上游分支 ... 分支比较 + 不存在 远程 上游分支 远程地址 diff --git a/src/Resources/Locales/zh_TW.axaml b/src/Resources/Locales/zh_TW.axaml index 73970aae8..29f8d563a 100644 --- a/src/Resources/Locales/zh_TW.axaml +++ b/src/Resources/Locales/zh_TW.axaml @@ -74,6 +74,7 @@ 重設 ${0}$ 至 ${1}$... 切換上游分支... 分支比較 + 無效 遠端 上游分支 遠端網址 diff --git a/src/Views/BranchTree.axaml b/src/Views/BranchTree.axaml index c235b414c..94f0f6892 100644 --- a/src/Views/BranchTree.axaml +++ b/src/Views/BranchTree.axaml @@ -58,6 +58,10 @@ + + + + @@ -115,7 +119,6 @@ Width="12" Height="12" Margin="8,0" Background="Transparent" - ToolTip.Tip="{DynamicResource Text.BranchUpstreamInvalid}" IsVisible="{Binding ShowUpstreamGoneTip}"> diff --git a/src/Views/FilterModeSwitchButton.axaml b/src/Views/FilterModeSwitchButton.axaml index db2ca8004..7e3b21d73 100644 --- a/src/Views/FilterModeSwitchButton.axaml +++ b/src/Views/FilterModeSwitchButton.axaml @@ -11,7 +11,8 @@ Padding="0" Background="Transparent" VerticalContentAlignment="Center" - Click="OnChangeFilterModeButtonClicked"> + Click="OnChangeFilterModeButtonClicked" + ToolTip.Tip="{DynamicResource Text.Repository.FilterCommits}"> Date: Mon, 11 Aug 2025 05:23:58 +0000 Subject: [PATCH 093/591] doc: Update translation status and sort locale files --- TRANSLATION.md | 38 +++++++++++++++++++++++-------- src/Resources/Locales/de_DE.axaml | 1 - src/Resources/Locales/es_ES.axaml | 1 - src/Resources/Locales/fr_FR.axaml | 1 - src/Resources/Locales/it_IT.axaml | 1 - src/Resources/Locales/ja_JP.axaml | 1 - src/Resources/Locales/ru_RU.axaml | 1 - src/Resources/Locales/ta_IN.axaml | 1 - src/Resources/Locales/uk_UA.axaml | 1 - src/Resources/Locales/zh_CN.axaml | 1 - src/Resources/Locales/zh_TW.axaml | 1 - 11 files changed, 29 insertions(+), 19 deletions(-) diff --git a/TRANSLATION.md b/TRANSLATION.md index 4eb6e8bdb..8f9cc5638 100644 --- a/TRANSLATION.md +++ b/TRANSLATION.md @@ -6,11 +6,12 @@ This document shows the translation status of each locale file in the repository ### ![en_US](https://img.shields.io/badge/en__US-%E2%88%9A-brightgreen) -### ![de__DE](https://img.shields.io/badge/de__DE-99.07%25-yellow) +### ![de__DE](https://img.shields.io/badge/de__DE-98.95%25-yellow)
Missing keys in de_DE.axaml +- Text.BranchTree.InvalidUpstream - Text.Repository.Dashboard - Text.Repository.MoreOptions - Text.Tag.Tagger @@ -22,9 +23,16 @@ This document shows the translation status of each locale file in the repository
-### ![es__ES](https://img.shields.io/badge/es__ES-%E2%88%9A-brightgreen) +### ![es__ES](https://img.shields.io/badge/es__ES-99.88%25-yellow) -### ![fr__FR](https://img.shields.io/badge/fr__FR-81.24%25-yellow) +
+Missing keys in es_ES.axaml + +- Text.BranchTree.InvalidUpstream + +
+ +### ![fr__FR](https://img.shields.io/badge/fr__FR-81.12%25-yellow)
Missing keys in fr_FR.axaml @@ -42,6 +50,7 @@ This document shows the translation status of each locale file in the repository - Text.Bisect.Skip - Text.Bisect.WaitingForRange - Text.BranchCM.ResetToSelectedCommit +- Text.BranchTree.InvalidUpstream - Text.BranchTree.Remote - Text.BranchTree.Tracking - Text.BranchTree.URL @@ -193,7 +202,7 @@ This document shows the translation status of each locale file in the repository
-### ![it__IT](https://img.shields.io/badge/it__IT-86.48%25-yellow) +### ![it__IT](https://img.shields.io/badge/it__IT-86.36%25-yellow)
Missing keys in it_IT.axaml @@ -204,6 +213,7 @@ This document shows the translation status of each locale file in the repository - Text.Askpass.Passphrase - Text.Avatar.Load - Text.BranchCM.ResetToSelectedCommit +- Text.BranchTree.InvalidUpstream - Text.BranchTree.Remote - Text.BranchTree.Tracking - Text.BranchTree.URL @@ -317,7 +327,7 @@ This document shows the translation status of each locale file in the repository
-### ![ja__JP](https://img.shields.io/badge/ja__JP-81.24%25-yellow) +### ![ja__JP](https://img.shields.io/badge/ja__JP-81.12%25-yellow)
Missing keys in ja_JP.axaml @@ -336,6 +346,7 @@ This document shows the translation status of each locale file in the repository - Text.Bisect.WaitingForRange - Text.BranchCM.CompareWithCurrent - Text.BranchCM.ResetToSelectedCommit +- Text.BranchTree.InvalidUpstream - Text.BranchTree.Remote - Text.BranchTree.Tracking - Text.BranchTree.URL @@ -512,10 +523,10 @@ This document shows the translation status of each locale file in the repository - Text.BranchCM.CustomAction - Text.BranchCM.MergeMultiBranches - Text.BranchCM.ResetToSelectedCommit +- Text.BranchTree.InvalidUpstream - Text.BranchTree.Remote - Text.BranchTree.Tracking - Text.BranchTree.URL -- Text.BranchUpstreamInvalid - Text.ChangeSubmoduleUrl - Text.ChangeSubmoduleUrl.Submodule - Text.ChangeSubmoduleUrl.URL @@ -715,9 +726,16 @@ This document shows the translation status of each locale file in the repository
-### ![ru__RU](https://img.shields.io/badge/ru__RU-%E2%88%9A-brightgreen) +### ![ru__RU](https://img.shields.io/badge/ru__RU-99.88%25-yellow) + +
+Missing keys in ru_RU.axaml + +- Text.BranchTree.InvalidUpstream + +
-### ![ta__IN](https://img.shields.io/badge/ta__IN-81.35%25-yellow) +### ![ta__IN](https://img.shields.io/badge/ta__IN-81.24%25-yellow)
Missing keys in ta_IN.axaml @@ -736,6 +754,7 @@ This document shows the translation status of each locale file in the repository - Text.Bisect.WaitingForRange - Text.BranchCM.CompareWithCurrent - Text.BranchCM.ResetToSelectedCommit +- Text.BranchTree.InvalidUpstream - Text.BranchTree.Remote - Text.BranchTree.Tracking - Text.BranchTree.URL @@ -885,7 +904,7 @@ This document shows the translation status of each locale file in the repository
-### ![uk__UA](https://img.shields.io/badge/uk__UA-82.52%25-yellow) +### ![uk__UA](https://img.shields.io/badge/uk__UA-82.40%25-yellow)
Missing keys in uk_UA.axaml @@ -903,6 +922,7 @@ This document shows the translation status of each locale file in the repository - Text.Bisect.Skip - Text.Bisect.WaitingForRange - Text.BranchCM.ResetToSelectedCommit +- Text.BranchTree.InvalidUpstream - Text.BranchTree.Remote - Text.BranchTree.Tracking - Text.BranchTree.URL diff --git a/src/Resources/Locales/de_DE.axaml b/src/Resources/Locales/de_DE.axaml index 53cbce9d6..e29309a7c 100644 --- a/src/Resources/Locales/de_DE.axaml +++ b/src/Resources/Locales/de_DE.axaml @@ -77,7 +77,6 @@ REMOTE VERFOLGT URL - Ungültiger Upstream! ABBRECHEN Auf Vorgänger-Revision zurücksetzen Auf diese Revision zurücksetzen diff --git a/src/Resources/Locales/es_ES.axaml b/src/Resources/Locales/es_ES.axaml index a6c995948..1bbe1fbee 100644 --- a/src/Resources/Locales/es_ES.axaml +++ b/src/Resources/Locales/es_ES.axaml @@ -77,7 +77,6 @@ REMOTO SEGUIMIENTO URL - ¡Upstream inválido! CANCELAR Resetear a Revisión Padre Resetear a Esta Revisión diff --git a/src/Resources/Locales/fr_FR.axaml b/src/Resources/Locales/fr_FR.axaml index abb5d42a5..7359db96d 100644 --- a/src/Resources/Locales/fr_FR.axaml +++ b/src/Resources/Locales/fr_FR.axaml @@ -61,7 +61,6 @@ Renommer ${0}$... Définir la branche de suivi... Comparer les branches - Branche en amont invalide! ANNULER Réinitialiser à la révision parente Réinitialiser à cette révision diff --git a/src/Resources/Locales/it_IT.axaml b/src/Resources/Locales/it_IT.axaml index d83910fb1..731fb267f 100644 --- a/src/Resources/Locales/it_IT.axaml +++ b/src/Resources/Locales/it_IT.axaml @@ -68,7 +68,6 @@ Rinomina ${0}$... Imposta Branch di Tracciamento... Confronto Branch - Upstream non valido ANNULLA Ripristina la Revisione Padre Ripristina Questa Revisione diff --git a/src/Resources/Locales/ja_JP.axaml b/src/Resources/Locales/ja_JP.axaml index 52794f8a2..1d0f26bc5 100644 --- a/src/Resources/Locales/ja_JP.axaml +++ b/src/Resources/Locales/ja_JP.axaml @@ -60,7 +60,6 @@ ${0}$ をリネームする... トラッキングブランチを設定... ブランチの比較 - 無効な上流ブランチ! キャンセル 親リビジョンにリセット このリビジョンにリセット diff --git a/src/Resources/Locales/ru_RU.axaml b/src/Resources/Locales/ru_RU.axaml index e1bd68748..7024d3eb5 100644 --- a/src/Resources/Locales/ru_RU.axaml +++ b/src/Resources/Locales/ru_RU.axaml @@ -77,7 +77,6 @@ УДАЛЁННЫЙ ОТСЛЕЖИВАНИЕ URL-АДРЕС - Недопустимая основная ветка! ОТМЕНА Сбросить родительскую ревизию Сбросить эту ревизию diff --git a/src/Resources/Locales/ta_IN.axaml b/src/Resources/Locales/ta_IN.axaml index e76a85155..29150e14d 100644 --- a/src/Resources/Locales/ta_IN.axaml +++ b/src/Resources/Locales/ta_IN.axaml @@ -60,7 +60,6 @@ மறுபெயரிடு ${0}$... கண்காணிப்பு கிளையை அமை... கிளை ஒப்பிடு - தவறான மேல்ஓடை! விடு பெற்றோர் திருத்தத்திற்கு மீட்டமை இந்த திருத்தத்திற்கு மீட்டமை diff --git a/src/Resources/Locales/uk_UA.axaml b/src/Resources/Locales/uk_UA.axaml index ca5903dec..4880e02c3 100644 --- a/src/Resources/Locales/uk_UA.axaml +++ b/src/Resources/Locales/uk_UA.axaml @@ -61,7 +61,6 @@ Перейменувати ${0}$... Встановити відстежувану гілку... Порівняти гілки - Недійсний upstream! СКАСУВАТИ Скинути до батьківської ревізії Скинути до цієї ревізії diff --git a/src/Resources/Locales/zh_CN.axaml b/src/Resources/Locales/zh_CN.axaml index f9505b5ab..96606a8d2 100644 --- a/src/Resources/Locales/zh_CN.axaml +++ b/src/Resources/Locales/zh_CN.axaml @@ -78,7 +78,6 @@ 远程 上游分支 远程地址 - 跟踪的上游分支不存在或已删除! 取 消 重置文件到上一版本 重置文件到该版本 diff --git a/src/Resources/Locales/zh_TW.axaml b/src/Resources/Locales/zh_TW.axaml index 29f8d563a..5b05054dd 100644 --- a/src/Resources/Locales/zh_TW.axaml +++ b/src/Resources/Locales/zh_TW.axaml @@ -78,7 +78,6 @@ 遠端 上游分支 遠端網址 - 追蹤上游分支不存在或已刪除! 取 消 重設檔案到上一版本 重設檔案為此版本 From 6a8bde412a5dded1fbabb4e757bed21eb2c2be59 Mon Sep 17 00:00:00 2001 From: leo Date: Mon, 11 Aug 2025 19:06:31 +0800 Subject: [PATCH 094/591] fix: sometimes `%ct` of branch returns empty string (#1715) Signed-off-by: leo --- src/Commands/QueryBranches.cs | 5 ++++- src/Commands/QueryTags.cs | 5 ++++- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/src/Commands/QueryBranches.cs b/src/Commands/QueryBranches.cs index 0eaf2837f..9d89407f0 100644 --- a/src/Commands/QueryBranches.cs +++ b/src/Commands/QueryBranches.cs @@ -94,8 +94,11 @@ private Models.Branch ParseLine(string line) branch.IsLocal = true; } + ulong committerDate = 0; + ulong.TryParse(parts[1], out committerDate); + branch.FullName = refName; - branch.CommitterDate = ulong.Parse(parts[1]); + branch.CommitterDate = committerDate; branch.Head = parts[2]; branch.IsCurrent = parts[3] == "*"; branch.Upstream = parts[4]; diff --git a/src/Commands/QueryTags.cs b/src/Commands/QueryTags.cs index cf3d14585..1ca3bff17 100644 --- a/src/Commands/QueryTags.cs +++ b/src/Commands/QueryTags.cs @@ -34,13 +34,16 @@ public QueryTags(string repo) if (!string.IsNullOrEmpty(message) && message.Equals(name, StringComparison.Ordinal)) message = null; + ulong creactorDate = 0; + ulong.TryParse(subs[5], out creactorDate); + tags.Add(new Models.Tag() { Name = name, IsAnnotated = subs[1].Equals("tag", StringComparison.Ordinal), SHA = string.IsNullOrEmpty(subs[3]) ? subs[2] : subs[3], Creator = Models.User.FindOrAdd(subs[4]), - CreatorDate = string.IsNullOrEmpty(subs[5]) ? 0 : ulong.Parse(subs[5]), + CreatorDate = creactorDate, Message = message, }); } From b746e8ea551f1b853b583ee11e46504019d5e69b Mon Sep 17 00:00:00 2001 From: leo Date: Tue, 12 Aug 2025 14:23:26 +0800 Subject: [PATCH 095/591] feature: allow to select multiple tags and delete theme from context menu (#1717) Signed-off-by: leo --- src/Models/Tag.cs | 11 +- src/Resources/Locales/en_US.axaml | 4 + src/Resources/Locales/zh_CN.axaml | 4 + src/Resources/Locales/zh_TW.axaml | 4 + src/ViewModels/DeleteMultipleTags.cs | 56 +++++++ src/ViewModels/Repository.cs | 37 ++++- src/ViewModels/TagCollection.cs | 182 +++++++++++++++++++++- src/Views/BranchTree.axaml.cs | 11 +- src/Views/DeleteMultipleTags.axaml | 65 ++++++++ src/Views/DeleteMultipleTags.axaml.cs | 12 ++ src/Views/FilterModeSwitchButton.axaml.cs | 151 +++++++++--------- src/Views/TagsView.axaml | 172 ++++++++------------ src/Views/TagsView.axaml.cs | 131 +++++++++++----- 13 files changed, 595 insertions(+), 245 deletions(-) create mode 100644 src/ViewModels/DeleteMultipleTags.cs create mode 100644 src/Views/DeleteMultipleTags.axaml create mode 100644 src/Views/DeleteMultipleTags.axaml.cs diff --git a/src/Models/Tag.cs b/src/Models/Tag.cs index fc52fcb9a..9e1654575 100644 --- a/src/Models/Tag.cs +++ b/src/Models/Tag.cs @@ -1,5 +1,4 @@ using System; -using CommunityToolkit.Mvvm.ComponentModel; namespace SourceGit.Models { @@ -9,7 +8,7 @@ public enum TagSortMode Name, } - public class Tag : ObservableObject + public class Tag { public string Name { get; set; } = string.Empty; public bool IsAnnotated { get; set; } = false; @@ -22,13 +21,5 @@ public string CreatorDateStr { get => DateTime.UnixEpoch.AddSeconds(CreatorDate).ToLocalTime().ToString(DateTimeFormat.Active.DateTime); } - - public FilterMode FilterMode - { - get => _filterMode; - set => SetProperty(ref _filterMode, value); - } - - private FilterMode _filterMode = FilterMode.None; } } diff --git a/src/Resources/Locales/en_US.axaml b/src/Resources/Locales/en_US.axaml index 75adc24b2..c960f4920 100644 --- a/src/Resources/Locales/en_US.axaml +++ b/src/Resources/Locales/en_US.axaml @@ -279,6 +279,9 @@ Also delete remote branch ${0}$ Delete Multiple Branches You are trying to delete multiple branches at one time. Be sure to double-check before taking action! + Delete Multiple Tags + Delete them from remotes + You are trying to delete multiple tags at one time. Be sure to double-check before taking action! Delete Remote Remote: Path: @@ -788,6 +791,7 @@ Copy Tag Name Custom Action Delete ${0}$... + Delete selected {0} tags... Merge ${0}$ into ${1}$... Push ${0}$... Update Submodules diff --git a/src/Resources/Locales/zh_CN.axaml b/src/Resources/Locales/zh_CN.axaml index 96606a8d2..ac47d8ad2 100644 --- a/src/Resources/Locales/zh_CN.axaml +++ b/src/Resources/Locales/zh_CN.axaml @@ -283,6 +283,9 @@ 同时删除远程分支 ${0}$ 删除多个分支 您正在尝试一次性删除多个分支,请务必仔细检查后再执行操作! + 删除多个标签 + 同时在远程仓库中删除 + 您正在尝试一次性删除多个标签,请务必仔细检查后再执行操作! 删除远程确认 远程名 : 路径 : @@ -792,6 +795,7 @@ 复制标签名 自定义操作 删除 ${0}$... + 删除选中 {0} 个标签... 合并 ${0}$ 到 ${1}$... 推送 ${0}$... 更新子模块 diff --git a/src/Resources/Locales/zh_TW.axaml b/src/Resources/Locales/zh_TW.axaml index 5b05054dd..c3e0715a2 100644 --- a/src/Resources/Locales/zh_TW.axaml +++ b/src/Resources/Locales/zh_TW.axaml @@ -283,6 +283,9 @@ 同時刪除遠端分支 ${0}$ 刪除多個分支 您正在嘗試一次性刪除多個分支,請務必仔細檢查後再刪除! + 刪除多個標籤 + 同時刪除遠端存放庫中的這些標籤 + 您正在嘗試一次性刪除多個標籤,請務必仔細檢查後再刪除! 刪除遠端確認 遠端名稱: 路徑: @@ -792,6 +795,7 @@ 複製標籤名稱 自訂動作 刪除 ${0}$... + 刪除所選的 {0} 個標籤... 合併 ${0}$ 到 ${1}$... 推送 ${0}$... 更新子模組 diff --git a/src/ViewModels/DeleteMultipleTags.cs b/src/ViewModels/DeleteMultipleTags.cs new file mode 100644 index 000000000..3e13a0ee3 --- /dev/null +++ b/src/ViewModels/DeleteMultipleTags.cs @@ -0,0 +1,56 @@ +using System.Collections.Generic; +using System.Threading.Tasks; + +namespace SourceGit.ViewModels +{ + public class DeleteMultipleTags : Popup + { + public List Tags + { + get; + } + + public bool DeleteFromRemote + { + get; + set; + } = false; + + public DeleteMultipleTags(Repository repo, List tags) + { + _repo = repo; + Tags = tags; + } + + public override async Task Sure() + { + _repo.SetWatcherEnabled(false); + ProgressDescription = "Deleting multiple tags..."; + + var log = _repo.CreateLog("Delete Multiple Tags"); + Use(log); + + foreach (var tag in Tags) + { + var succ = await new Commands.Tag(_repo.FullPath, tag.Name) + .Use(log) + .DeleteAsync(); + + if (succ) + { + foreach (var r in _repo.Remotes) + await new Commands.Push(_repo.FullPath, r.Name, $"refs/tags/{tag.Name}", true) + .Use(log) + .RunAsync(); + } + } + + log.Complete(); + _repo.MarkTagsDirtyManually(); + _repo.SetWatcherEnabled(true); + return true; + } + + private readonly Repository _repo; + } +} diff --git a/src/ViewModels/Repository.cs b/src/ViewModels/Repository.cs index 9adbb1a8e..d7b87e7ba 100644 --- a/src/ViewModels/Repository.cs +++ b/src/ViewModels/Repository.cs @@ -1755,9 +1755,19 @@ private object BuildVisibleTags() UpdateTagFilterMode(historiesFilters); if (Preferences.Instance.ShowTagsAsTree) - return TagCollectionAsTree.Build(visible, _visibleTags as TagCollectionAsTree); + { + var tree = TagCollectionAsTree.Build(visible, _visibleTags as TagCollectionAsTree); + foreach (var node in tree.Tree) + node.UpdateFilterMode(historiesFilters); + return tree; + } else - return new TagCollectionAsList() { Tags = visible }; + { + var list = new TagCollectionAsList(visible); + foreach (var item in list.TagItems) + item.FilterMode = historiesFilters.GetValueOrDefault(item.Tag.Name, Models.FilterMode.None); + return list; + } } private object BuildVisibleSubmodules() @@ -1813,9 +1823,15 @@ private void UpdateBranchTreeFilterMode(List nodes, Dictionary filters) { - foreach (var tag in _tags) + if (VisibleTags is TagCollectionAsTree tree) + { + foreach (var node in tree.Tree) + node.UpdateFilterMode(filters); + } + else if (VisibleTags is TagCollectionAsList list) { - tag.FilterMode = filters.GetValueOrDefault(tag.Name, Models.FilterMode.None); + foreach (var item in list.TagItems) + item.FilterMode = filters.GetValueOrDefault(item.Tag.Name, Models.FilterMode.None); } } @@ -1831,8 +1847,17 @@ private void ResetBranchTreeFilterMode(List nodes) private void ResetTagFilterMode() { - foreach (var tag in _tags) - tag.FilterMode = Models.FilterMode.None; + if (VisibleTags is TagCollectionAsTree tree) + { + var filters = new Dictionary(); + foreach (var node in tree.Tree) + node.UpdateFilterMode(filters); + } + else if (VisibleTags is TagCollectionAsList list) + { + foreach (var item in list.TagItems) + item.FilterMode = Models.FilterMode.None; + } } private BranchTreeNode FindBranchNode(List nodes, string path) diff --git a/src/ViewModels/TagCollection.cs b/src/ViewModels/TagCollection.cs index 4c66f1e38..50503dc22 100644 --- a/src/ViewModels/TagCollection.cs +++ b/src/ViewModels/TagCollection.cs @@ -1,12 +1,14 @@ +using System.Collections; using System.Collections.Generic; +using Avalonia; using Avalonia.Collections; using CommunityToolkit.Mvvm.ComponentModel; namespace SourceGit.ViewModels { - public class TagTreeNodeToolTip + public class TagToolTip { public string Name { get; private set; } public bool IsAnnotated { get; private set; } @@ -14,7 +16,7 @@ public class TagTreeNodeToolTip public string CreatorDateStr { get; private set; } public string Message { get; private set; } - public TagTreeNodeToolTip(Models.Tag t) + public TagToolTip(Models.Tag t) { Name = t.Name; IsAnnotated = t.IsAnnotated; @@ -29,7 +31,7 @@ public class TagTreeNode : ObservableObject public string FullPath { get; private set; } public int Depth { get; private set; } = 0; public Models.Tag Tag { get; private set; } = null; - public TagTreeNodeToolTip ToolTip { get; private set; } = null; + public TagToolTip ToolTip { get; private set; } = null; public List Children { get; private set; } = []; public int Counter { get; set; } = 0; @@ -38,6 +40,24 @@ public bool IsFolder get => Tag == null; } + public bool IsSelected + { + get; + set; + } + + public Models.FilterMode FilterMode + { + get => _filterMode; + set => SetProperty(ref _filterMode, value); + } + + public CornerRadius CornerRadius + { + get => _cornerRadius; + set => SetProperty(ref _cornerRadius, value); + } + public bool IsExpanded { get => _isExpanded; @@ -54,7 +74,7 @@ public TagTreeNode(Models.Tag t, int depth) FullPath = t.Name; Depth = depth; Tag = t; - ToolTip = new TagTreeNodeToolTip(t); + ToolTip = new TagToolTip(t); IsExpanded = false; } @@ -66,6 +86,19 @@ public TagTreeNode(string path, bool isExpanded, int depth) Counter = 1; } + public void UpdateFilterMode(Dictionary filters) + { + if (Tag == null) + { + foreach (var child in Children) + child.UpdateFilterMode(filters); + } + else + { + FilterMode = filters.GetValueOrDefault(FullPath, Models.FilterMode.None); + } + } + public static List Build(List tags, HashSet expanded) { var nodes = new List(); @@ -131,16 +164,103 @@ private static void InsertFolder(List collection, TagTreeNode subFo collection.Add(subFolder); } + private Models.FilterMode _filterMode = Models.FilterMode.None; + private CornerRadius _cornerRadius = new CornerRadius(4); private bool _isExpanded = true; } + public class TagListItem : ObservableObject + { + public Models.Tag Tag + { + get; + set; + } + + public bool IsSelected + { + get; + set; + } + + public Models.FilterMode FilterMode + { + get => _filterMode; + set => SetProperty(ref _filterMode, value); + } + + public TagToolTip ToolTip + { + get; + set; + } + + public CornerRadius CornerRadius + { + get => _cornerRadius; + set => SetProperty(ref _cornerRadius, value); + } + + private Models.FilterMode _filterMode = Models.FilterMode.None; + private CornerRadius _cornerRadius = new CornerRadius(4); + } + public class TagCollectionAsList { - public List Tags + public List TagItems { get; set; } = []; + + public TagCollectionAsList(List tags) + { + foreach (var tag in tags) + TagItems.Add(new TagListItem() { Tag = tag, ToolTip = new TagToolTip(tag) }); + } + + public void ClearSelection() + { + foreach (var item in TagItems) + { + item.IsSelected = false; + item.CornerRadius = new CornerRadius(4); + } + } + + public void UpdateSelection(IList selectedItems) + { + var set = new HashSet(); + foreach (var item in selectedItems) + { + if (item is TagListItem tagItem) + set.Add(tagItem.Tag.Name); + } + + TagListItem last = null; + foreach (var item in TagItems) + { + item.IsSelected = set.Contains(item.Tag.Name); + if (item.IsSelected) + { + if (last is { IsSelected: true }) + { + last.CornerRadius = new CornerRadius(last.CornerRadius.TopLeft, 0); + item.CornerRadius = new CornerRadius(0, 4); + } + else + { + item.CornerRadius = new CornerRadius(4); + } + } + else + { + item.CornerRadius = new CornerRadius(4); + } + + last = item; + } + } } public class TagCollectionAsTree @@ -210,6 +330,46 @@ public void ToggleExpand(TagTreeNode node) } } + public void ClearSelection() + { + foreach (var node in Tree) + ClearSelectionRecursively(node); + } + + public void UpdateSelection(IList selectedItems) + { + var set = new HashSet(); + foreach (var item in selectedItems) + { + if (item is TagTreeNode node) + set.Add(node.FullPath); + } + + TagTreeNode last = null; + foreach (var row in Rows) + { + row.IsSelected = set.Contains(row.FullPath); + if (row.IsSelected) + { + if (last is { IsSelected: true }) + { + last.CornerRadius = new CornerRadius(last.CornerRadius.TopLeft, 0); + row.CornerRadius = new CornerRadius(0, 4); + } + else + { + row.CornerRadius = new CornerRadius(4); + } + } + else + { + row.CornerRadius = new CornerRadius(4); + } + + last = row; + } + } + private static void MakeTreeRows(List rows, List nodes) { foreach (var node in nodes) @@ -222,5 +382,17 @@ private static void MakeTreeRows(List rows, List nodes MakeTreeRows(rows, node.Children); } } + + private static void ClearSelectionRecursively(TagTreeNode node) + { + if (node.IsSelected) + { + node.IsSelected = false; + node.CornerRadius = new CornerRadius(4); + } + + foreach (var child in node.Children) + ClearSelectionRecursively(child); + } } } diff --git a/src/Views/BranchTree.axaml.cs b/src/Views/BranchTree.axaml.cs index 175c927b3..cca5defa9 100644 --- a/src/Views/BranchTree.axaml.cs +++ b/src/Views/BranchTree.axaml.cs @@ -352,6 +352,10 @@ protected override void OnPropertyChanged(AvaloniaPropertyChangedEventArgs chang private void OnNodePointerPressed(object sender, PointerPressedEventArgs e) { + var ctrl = OperatingSystem.IsMacOS() ? KeyModifiers.Meta : KeyModifiers.Control; + if (e.KeyModifiers.HasFlag(ctrl) || e.KeyModifiers.HasFlag(KeyModifiers.Shift)) + return; + var p = e.GetCurrentPoint(this); if (!p.Properties.IsLeftButtonPressed) return; @@ -365,13 +369,6 @@ private void OnNodePointerPressed(object sender, PointerPressedEventArgs e) if (node.Backend is not Models.Branch branch) return; - if (BranchesPresenter.SelectedItems is { Count: > 0 }) - { - var ctrl = OperatingSystem.IsMacOS() ? KeyModifiers.Meta : KeyModifiers.Control; - if (e.KeyModifiers.HasFlag(ctrl) || e.KeyModifiers.HasFlag(KeyModifiers.Shift)) - return; - } - repo.NavigateToCommit(branch.Head); } diff --git a/src/Views/DeleteMultipleTags.axaml b/src/Views/DeleteMultipleTags.axaml new file mode 100644 index 000000000..56ee70e7e --- /dev/null +++ b/src/Views/DeleteMultipleTags.axaml @@ -0,0 +1,65 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/Views/DeleteMultipleTags.axaml.cs b/src/Views/DeleteMultipleTags.axaml.cs new file mode 100644 index 000000000..5c0e1b9dd --- /dev/null +++ b/src/Views/DeleteMultipleTags.axaml.cs @@ -0,0 +1,12 @@ +using Avalonia.Controls; + +namespace SourceGit.Views +{ + public partial class DeleteMultipleTags : UserControl + { + public DeleteMultipleTags() + { + InitializeComponent(); + } + } +} diff --git a/src/Views/FilterModeSwitchButton.axaml.cs b/src/Views/FilterModeSwitchButton.axaml.cs index e0f117f75..97880eb12 100644 --- a/src/Views/FilterModeSwitchButton.axaml.cs +++ b/src/Views/FilterModeSwitchButton.axaml.cs @@ -64,98 +64,105 @@ private void OnChangeFilterModeButtonClicked(object sender, RoutedEventArgs e) return; var menu = new ContextMenu(); - var mode = Models.FilterMode.None; - if (DataContext is Models.Tag tag) - { - mode = tag.FilterMode; + if (DataContext is ViewModels.TagListItem tagItem) + FillContextMenuForTag(menu, repo, tagItem.Tag, tagItem.FilterMode); + else if (DataContext is ViewModels.TagTreeNode tagNode) + FillContextMenuForTag(menu, repo, tagNode.Tag, tagNode.FilterMode); + else if (DataContext is ViewModels.BranchTreeNode branchNode) + FillContextMenuForBranch(menu, repo, branchNode, branchNode.FilterMode); - if (mode != Models.FilterMode.None) - { - var unset = new MenuItem(); - unset.Header = App.Text("Repository.FilterCommits.Default"); - unset.Click += (_, ev) => - { - repo.SetTagFilterMode(tag, Models.FilterMode.None); - ev.Handled = true; - }; - - menu.Items.Add(unset); - menu.Items.Add(new MenuItem() { Header = "-" }); - } - - var include = new MenuItem(); - include.Icon = App.CreateMenuIcon("Icons.Filter"); - include.Header = App.Text("Repository.FilterCommits.Include"); - include.IsEnabled = mode != Models.FilterMode.Included; - include.Click += (_, ev) => - { - repo.SetTagFilterMode(tag, Models.FilterMode.Included); - ev.Handled = true; - }; + menu.Open(button); + e.Handled = true; + } - var exclude = new MenuItem(); - exclude.Icon = App.CreateMenuIcon("Icons.EyeClose"); - exclude.Header = App.Text("Repository.FilterCommits.Exclude"); - exclude.IsEnabled = mode != Models.FilterMode.Excluded; - exclude.Click += (_, ev) => + private void FillContextMenuForTag(ContextMenu menu, ViewModels.Repository repo, Models.Tag tag, Models.FilterMode current) + { + if (current != Models.FilterMode.None) + { + var unset = new MenuItem(); + unset.Header = App.Text("Repository.FilterCommits.Default"); + unset.Click += (_, ev) => { - repo.SetTagFilterMode(tag, Models.FilterMode.Excluded); + repo.SetTagFilterMode(tag, Models.FilterMode.None); ev.Handled = true; }; - menu.Items.Add(include); - menu.Items.Add(exclude); + menu.Items.Add(unset); + menu.Items.Add(new MenuItem() { Header = "-" }); } - else if (DataContext is ViewModels.BranchTreeNode node) + else { - mode = node.FilterMode; + IsContextMenuOpening = true; + menu.Closed += (_, _) => IsContextMenuOpening = false; + } - if (mode != Models.FilterMode.None) - { - var unset = new MenuItem(); - unset.Header = App.Text("Repository.FilterCommits.Default"); - unset.Click += (_, ev) => - { - repo.SetBranchFilterMode(node, Models.FilterMode.None, false, true); - ev.Handled = true; - }; - - menu.Items.Add(unset); - menu.Items.Add(new MenuItem() { Header = "-" }); - } - - var include = new MenuItem(); - include.Icon = App.CreateMenuIcon("Icons.Filter"); - include.Header = App.Text("Repository.FilterCommits.Include"); - include.IsEnabled = mode != Models.FilterMode.Included; - include.Click += (_, ev) => - { - repo.SetBranchFilterMode(node, Models.FilterMode.Included, false, true); - ev.Handled = true; - }; + var include = new MenuItem(); + include.Icon = App.CreateMenuIcon("Icons.Filter"); + include.Header = App.Text("Repository.FilterCommits.Include"); + include.IsEnabled = current != Models.FilterMode.Included; + include.Click += (_, ev) => + { + repo.SetTagFilterMode(tag, Models.FilterMode.Included); + ev.Handled = true; + }; + + var exclude = new MenuItem(); + exclude.Icon = App.CreateMenuIcon("Icons.EyeClose"); + exclude.Header = App.Text("Repository.FilterCommits.Exclude"); + exclude.IsEnabled = current != Models.FilterMode.Excluded; + exclude.Click += (_, ev) => + { + repo.SetTagFilterMode(tag, Models.FilterMode.Excluded); + ev.Handled = true; + }; - var exclude = new MenuItem(); - exclude.Icon = App.CreateMenuIcon("Icons.EyeClose"); - exclude.Header = App.Text("Repository.FilterCommits.Exclude"); - exclude.IsEnabled = mode != Models.FilterMode.Excluded; - exclude.Click += (_, ev) => + menu.Items.Add(include); + menu.Items.Add(exclude); + } + + private void FillContextMenuForBranch(ContextMenu menu, ViewModels.Repository repo, ViewModels.BranchTreeNode node, Models.FilterMode current) + { + if (current != Models.FilterMode.None) + { + var unset = new MenuItem(); + unset.Header = App.Text("Repository.FilterCommits.Default"); + unset.Click += (_, ev) => { - repo.SetBranchFilterMode(node, Models.FilterMode.Excluded, false, true); + repo.SetBranchFilterMode(node, Models.FilterMode.None, false, true); ev.Handled = true; }; - menu.Items.Add(include); - menu.Items.Add(exclude); + menu.Items.Add(unset); + menu.Items.Add(new MenuItem() { Header = "-" }); } - - if (mode == Models.FilterMode.None) + else { IsContextMenuOpening = true; menu.Closed += (_, _) => IsContextMenuOpening = false; } - menu.Open(button); - e.Handled = true; + var include = new MenuItem(); + include.Icon = App.CreateMenuIcon("Icons.Filter"); + include.Header = App.Text("Repository.FilterCommits.Include"); + include.IsEnabled = current != Models.FilterMode.Included; + include.Click += (_, ev) => + { + repo.SetBranchFilterMode(node, Models.FilterMode.Included, false, true); + ev.Handled = true; + }; + + var exclude = new MenuItem(); + exclude.Icon = App.CreateMenuIcon("Icons.EyeClose"); + exclude.Header = App.Text("Repository.FilterCommits.Exclude"); + exclude.IsEnabled = current != Models.FilterMode.Excluded; + exclude.Click += (_, ev) => + { + repo.SetBranchFilterMode(node, Models.FilterMode.Excluded, false, true); + ev.Handled = true; + }; + + menu.Items.Add(include); + menu.Items.Add(exclude); } } } diff --git a/src/Views/TagsView.axaml b/src/Views/TagsView.axaml index 03f4f2dae..bc1aee4e4 100644 --- a/src/Views/TagsView.axaml +++ b/src/Views/TagsView.axaml @@ -9,10 +9,6 @@ mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450" x:Class="SourceGit.Views.TagsView"> - - @@ -22,61 +18,21 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + SelectionChanged="OnSelectionChanged" + ContextRequested="OnTagsContextMenuRequested"> + + + - - - - - - - + + + @@ -115,60 +67,24 @@ + SelectionChanged="OnSelectionChanged" + ContextRequested="OnTagsContextMenuRequested"> + + + - + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - @@ -188,5 +104,47 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/Views/TagsView.axaml.cs b/src/Views/TagsView.axaml.cs index f77b7217e..a35fcfbb5 100644 --- a/src/Views/TagsView.axaml.cs +++ b/src/Views/TagsView.axaml.cs @@ -1,4 +1,5 @@ using System; +using System.Collections.Generic; using Avalonia; using Avalonia.Controls; @@ -143,7 +144,7 @@ protected override void OnPropertyChanged(AvaloniaPropertyChangedEventArgs chang if (Content is ViewModels.TagCollectionAsTree tree) Rows = tree.Rows.Count; else if (Content is ViewModels.TagCollectionAsList list) - Rows = list.Tags.Count; + Rows = list.TagItems.Count; else Rows = 0; @@ -164,10 +165,10 @@ private async void OnItemDoubleTapped(object sender, TappedEventArgs e) else if (DataContext is ViewModels.Repository repo) await repo.CheckoutTagAsync(node.Tag); } - else if (sender is Control { DataContext: Models.Tag tag }) + else if (sender is Control { DataContext: ViewModels.TagListItem item }) { if (DataContext is ViewModels.Repository repo) - await repo.CheckoutTagAsync(tag); + await repo.CheckoutTagAsync(item.Tag); } e.Handled = true; @@ -175,6 +176,10 @@ private async void OnItemDoubleTapped(object sender, TappedEventArgs e) private void OnItemPointerPressed(object sender, PointerPressedEventArgs e) { + var ctrl = OperatingSystem.IsMacOS() ? KeyModifiers.Meta : KeyModifiers.Control; + if (e.KeyModifiers.HasFlag(ctrl) || e.KeyModifiers.HasFlag(KeyModifiers.Shift)) + return; + var p = e.GetCurrentPoint(this); if (!p.Properties.IsLeftButtonPressed) return; @@ -182,55 +187,64 @@ private void OnItemPointerPressed(object sender, PointerPressedEventArgs e) if (DataContext is not ViewModels.Repository repo) return; - if (sender is Control { DataContext: Models.Tag tag }) - repo.NavigateToCommit(tag.SHA); - else if (sender is Control { DataContext: ViewModels.TagTreeNode { Tag: { } nodeTag } }) + if (sender is not Control control) + return; + + if (control.DataContext is ViewModels.TagListItem { Tag: { } itemTag }) + repo.NavigateToCommit(itemTag.SHA); + else if (control.DataContext is ViewModels.TagTreeNode { Tag: { } nodeTag }) repo.NavigateToCommit(nodeTag.SHA); } - private void OnItemContextRequested(object sender, ContextRequestedEventArgs e) + private void OnTagsContextMenuRequested(object sender, ContextRequestedEventArgs e) { - if (sender is not Control control) + if (sender is not ListBox { SelectedItems: { Count: > 0 } selectedItems } listBox) return; - Models.Tag selected; - if (control.DataContext is ViewModels.TagTreeNode node) - selected = node.Tag; - else if (control.DataContext is Models.Tag tag) - selected = tag; - else - selected = null; + if (DataContext is not ViewModels.Repository repo) + return; - if (selected != null && DataContext is ViewModels.Repository repo) + var selected = new List(); + foreach (var item in selectedItems) { + if (item is ViewModels.TagListItem i) + selected.Add(i.Tag); + else if (item is ViewModels.TagTreeNode n) + CollectTagsInNode(n, selected); + } + + if (selected.Count == 1) + { + var tag = selected[0]; + var createBranch = new MenuItem(); createBranch.Icon = App.CreateMenuIcon("Icons.Branch.Add"); createBranch.Header = App.Text("CreateBranch"); createBranch.Click += (_, ev) => { if (repo.CanCreatePopup()) - repo.ShowPopup(new ViewModels.CreateBranch(repo, selected)); + repo.ShowPopup(new ViewModels.CreateBranch(repo, tag)); ev.Handled = true; }; var pushTag = new MenuItem(); - pushTag.Header = App.Text("TagCM.Push", selected.Name); + pushTag.Header = App.Text("TagCM.Push", tag.Name); pushTag.Icon = App.CreateMenuIcon("Icons.Push"); pushTag.IsEnabled = repo.Remotes.Count > 0; pushTag.Click += (_, ev) => { if (repo.CanCreatePopup()) - repo.ShowPopup(new ViewModels.PushTag(repo, selected)); + repo.ShowPopup(new ViewModels.PushTag(repo, tag)); ev.Handled = true; }; var deleteTag = new MenuItem(); - deleteTag.Header = App.Text("TagCM.Delete", selected.Name); + deleteTag.Header = App.Text("TagCM.Delete", tag.Name); deleteTag.Icon = App.CreateMenuIcon("Icons.Clear"); deleteTag.Click += (_, ev) => { if (repo.CanCreatePopup()) - repo.ShowPopup(new ViewModels.DeleteTag(repo, selected)); + repo.ShowPopup(new ViewModels.DeleteTag(repo, tag)); ev.Handled = true; }; @@ -240,7 +254,7 @@ private void OnItemContextRequested(object sender, ContextRequestedEventArgs e) archive.Click += (_, ev) => { if (repo.CanCreatePopup()) - repo.ShowPopup(new ViewModels.Archive(repo, selected)); + repo.ShowPopup(new ViewModels.Archive(repo, tag)); ev.Handled = true; }; @@ -268,7 +282,7 @@ private void OnItemContextRequested(object sender, ContextRequestedEventArgs e) item.Header = label; item.Click += (_, e) => { - repo.ExecCustomAction(dup, selected); + repo.ExecCustomAction(dup, tag); e.Handled = true; }; @@ -288,38 +302,55 @@ private void OnItemContextRequested(object sender, ContextRequestedEventArgs e) copyName.Icon = App.CreateMenuIcon("Icons.Tag"); copyName.Click += async (_, ev) => { - await App.CopyTextAsync(selected.Name); + await App.CopyTextAsync(tag.Name); ev.Handled = true; }; var copyMessage = new MenuItem(); copyMessage.Header = App.Text("TagCM.Copy.Message"); copyMessage.Icon = App.CreateMenuIcon("Icons.Info"); - copyMessage.IsEnabled = !string.IsNullOrEmpty(selected.Message); + copyMessage.IsEnabled = !string.IsNullOrEmpty(tag.Message); copyMessage.Click += async (_, ev) => { - await App.CopyTextAsync(selected.Message); + await App.CopyTextAsync(tag.Message); ev.Handled = true; }; copy.Items.Add(copyName); copy.Items.Add(copyMessage); - if (selected.Creator is { Email: { Length: > 0 } }) + if (tag.Creator is { Email: { Length: > 0 } }) { var copyCreator = new MenuItem(); copyCreator.Header = App.Text("TagCM.Copy.Tagger"); copyCreator.Icon = App.CreateMenuIcon("Icons.User"); copyCreator.Click += async (_, ev) => { - await App.CopyTextAsync(selected.Creator.ToString()); + await App.CopyTextAsync(tag.Creator.ToString()); ev.Handled = true; }; copy.Items.Add(copyCreator); } menu.Items.Add(copy); - menu.Open(control); + menu.Open(listBox); + } + else if (selected.Count > 0) + { + var deleteMultiple = new MenuItem(); + deleteMultiple.Header = App.Text("TagCM.DeleteMultiple", selected.Count); + deleteMultiple.Icon = App.CreateMenuIcon("Icons.Clear"); + deleteMultiple.Click += (_, e) => + { + if (repo.CanCreatePopup()) + repo.ShowPopup(new ViewModels.DeleteMultipleTags(repo, selected)); + + e.Handled = true; + }; + + var menu = new ContextMenu(); + menu.Items.Add(deleteMultiple); + menu.Open(listBox); } e.Handled = true; @@ -327,15 +358,25 @@ private void OnItemContextRequested(object sender, ContextRequestedEventArgs e) private void OnSelectionChanged(object sender, SelectionChangedEventArgs _) { - var selected = (sender as ListBox)?.SelectedItem; - Models.Tag selectedTag = null; - if (selected is ViewModels.TagTreeNode node) - selectedTag = node.Tag; - else if (selected is Models.Tag tag) - selectedTag = tag; + if (sender is not ListBox listBox) + return; + + if (listBox.SelectedItems is { Count: 0 }) + { + if (Content is ViewModels.TagCollectionAsList list) + list.ClearSelection(); + else if (Content is ViewModels.TagCollectionAsTree tree) + tree.ClearSelection(); + } + else if (listBox.SelectedItems is { Count: > 0 }) + { + if (Content is ViewModels.TagCollectionAsList list) + list.UpdateSelection(listBox.SelectedItems); + else if (Content is ViewModels.TagCollectionAsTree tree) + tree.UpdateSelection(listBox.SelectedItems); - if (selectedTag != null) RaiseEvent(new RoutedEventArgs(SelectionChangedEvent)); + } } private void OnKeyDown(object sender, KeyEventArgs e) @@ -349,10 +390,24 @@ private void OnKeyDown(object sender, KeyEventArgs e) var selected = (sender as ListBox)?.SelectedItem; if (selected is ViewModels.TagTreeNode { Tag: { } tagInNode }) repo.DeleteTag(tagInNode); - else if (selected is Models.Tag tag) - repo.DeleteTag(tag); + else if (selected is ViewModels.TagListItem { Tag: { } tagInItem }) + repo.DeleteTag(tagInItem); e.Handled = true; } + + private void CollectTagsInNode(ViewModels.TagTreeNode node, List outs) + { + if (node.Tag is { } tag) + { + if (!outs.Contains(tag)) + outs.Add(tag); + + return; + } + + foreach (var child in node.Children) + CollectTagsInNode(child, outs); + } } } From 4b03afd8a29e01a68e7e10ae144dc4d809dc7ead Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Tue, 12 Aug 2025 06:24:07 +0000 Subject: [PATCH 096/591] doc: Update translation status and sort locale files --- TRANSLATION.md | 54 +++++++++++++++++++++++++++++++++++++++++--------- 1 file changed, 45 insertions(+), 9 deletions(-) diff --git a/TRANSLATION.md b/TRANSLATION.md index 8f9cc5638..e13f36e64 100644 --- a/TRANSLATION.md +++ b/TRANSLATION.md @@ -6,12 +6,15 @@ This document shows the translation status of each locale file in the repository ### ![en_US](https://img.shields.io/badge/en__US-%E2%88%9A-brightgreen) -### ![de__DE](https://img.shields.io/badge/de__DE-98.95%25-yellow) +### ![de__DE](https://img.shields.io/badge/de__DE-98.49%25-yellow)
Missing keys in de_DE.axaml - Text.BranchTree.InvalidUpstream +- Text.DeleteMultiTags +- Text.DeleteMultiTags.DeleteFromRemotes +- Text.DeleteMultiTags.Tip - Text.Repository.Dashboard - Text.Repository.MoreOptions - Text.Tag.Tagger @@ -20,19 +23,24 @@ This document shows the translation status of each locale file in the repository - Text.TagCM.Copy.Name - Text.TagCM.Copy.Tagger - Text.TagCM.CopyName +- Text.TagCM.DeleteMultiple
-### ![es__ES](https://img.shields.io/badge/es__ES-99.88%25-yellow) +### ![es__ES](https://img.shields.io/badge/es__ES-99.42%25-yellow)
Missing keys in es_ES.axaml - Text.BranchTree.InvalidUpstream +- Text.DeleteMultiTags +- Text.DeleteMultiTags.DeleteFromRemotes +- Text.DeleteMultiTags.Tip +- Text.TagCM.DeleteMultiple
-### ![fr__FR](https://img.shields.io/badge/fr__FR-81.12%25-yellow) +### ![fr__FR](https://img.shields.io/badge/fr__FR-80.74%25-yellow)
Missing keys in fr_FR.axaml @@ -108,6 +116,9 @@ This document shows the translation status of each locale file in the repository - Text.DeinitSubmodule - Text.DeinitSubmodule.Force - Text.DeinitSubmodule.Path +- Text.DeleteMultiTags +- Text.DeleteMultiTags.DeleteFromRemotes +- Text.DeleteMultiTags.Tip - Text.Diff.Image.Blend - Text.Diff.Image.SideBySide - Text.Diff.Image.Swipe @@ -186,6 +197,7 @@ This document shows the translation status of each locale file in the repository - Text.TagCM.Copy.Tagger - Text.TagCM.CopyName - Text.TagCM.CustomAction +- Text.TagCM.DeleteMultiple - Text.UpdateSubmodules.UpdateToRemoteTrackingBranch - Text.ViewLogs - Text.ViewLogs.Clear @@ -202,7 +214,7 @@ This document shows the translation status of each locale file in the repository
-### ![it__IT](https://img.shields.io/badge/it__IT-86.36%25-yellow) +### ![it__IT](https://img.shields.io/badge/it__IT-85.96%25-yellow)
Missing keys in it_IT.axaml @@ -261,6 +273,9 @@ This document shows the translation status of each locale file in the repository - Text.DeinitSubmodule - Text.DeinitSubmodule.Force - Text.DeinitSubmodule.Path +- Text.DeleteMultiTags +- Text.DeleteMultiTags.DeleteFromRemotes +- Text.DeleteMultiTags.Tip - Text.Diff.Image.Blend - Text.Diff.Image.SideBySide - Text.Diff.Image.Swipe @@ -320,6 +335,7 @@ This document shows the translation status of each locale file in the repository - Text.TagCM.Copy.Tagger - Text.TagCM.CopyName - Text.TagCM.CustomAction +- Text.TagCM.DeleteMultiple - Text.UpdateSubmodules.UpdateToRemoteTrackingBranch - Text.WorkingCopy.AddToGitIgnore.InFolder - Text.WorkingCopy.ConfirmCommitWithDetachedHead @@ -327,7 +343,7 @@ This document shows the translation status of each locale file in the repository
-### ![ja__JP](https://img.shields.io/badge/ja__JP-81.12%25-yellow) +### ![ja__JP](https://img.shields.io/badge/ja__JP-80.74%25-yellow)
Missing keys in ja_JP.axaml @@ -404,6 +420,9 @@ This document shows the translation status of each locale file in the repository - Text.DeinitSubmodule - Text.DeinitSubmodule.Force - Text.DeinitSubmodule.Path +- Text.DeleteMultiTags +- Text.DeleteMultiTags.DeleteFromRemotes +- Text.DeleteMultiTags.Tip - Text.Diff.Image.Blend - Text.Diff.Image.SideBySide - Text.Diff.Image.Swipe @@ -481,6 +500,7 @@ This document shows the translation status of each locale file in the repository - Text.TagCM.Copy.Tagger - Text.TagCM.CopyName - Text.TagCM.CustomAction +- Text.TagCM.DeleteMultiple - Text.UpdateSubmodules.UpdateToRemoteTrackingBranch - Text.ViewLogs - Text.ViewLogs.Clear @@ -497,7 +517,7 @@ This document shows the translation status of each locale file in the repository
-### ![pt__BR](https://img.shields.io/badge/pt__BR-74.24%25-red) +### ![pt__BR](https://img.shields.io/badge/pt__BR-73.90%25-red)
Missing keys in pt_BR.axaml @@ -592,6 +612,9 @@ This document shows the translation status of each locale file in the repository - Text.DeinitSubmodule - Text.DeinitSubmodule.Force - Text.DeinitSubmodule.Path +- Text.DeleteMultiTags +- Text.DeleteMultiTags.DeleteFromRemotes +- Text.DeleteMultiTags.Tip - Text.DeleteRepositoryNode.Path - Text.DeleteRepositoryNode.TipForGroup - Text.DeleteRepositoryNode.TipForRepository @@ -708,6 +731,7 @@ This document shows the translation status of each locale file in the repository - Text.TagCM.Copy.Tagger - Text.TagCM.CopyName - Text.TagCM.CustomAction +- Text.TagCM.DeleteMultiple - Text.UpdateSubmodules.UpdateToRemoteTrackingBranch - Text.ViewLogs - Text.ViewLogs.Clear @@ -726,16 +750,20 @@ This document shows the translation status of each locale file in the repository
-### ![ru__RU](https://img.shields.io/badge/ru__RU-99.88%25-yellow) +### ![ru__RU](https://img.shields.io/badge/ru__RU-99.42%25-yellow)
Missing keys in ru_RU.axaml - Text.BranchTree.InvalidUpstream +- Text.DeleteMultiTags +- Text.DeleteMultiTags.DeleteFromRemotes +- Text.DeleteMultiTags.Tip +- Text.TagCM.DeleteMultiple
-### ![ta__IN](https://img.shields.io/badge/ta__IN-81.24%25-yellow) +### ![ta__IN](https://img.shields.io/badge/ta__IN-80.86%25-yellow)
Missing keys in ta_IN.axaml @@ -812,6 +840,9 @@ This document shows the translation status of each locale file in the repository - Text.DeinitSubmodule - Text.DeinitSubmodule.Force - Text.DeinitSubmodule.Path +- Text.DeleteMultiTags +- Text.DeleteMultiTags.DeleteFromRemotes +- Text.DeleteMultiTags.Tip - Text.Diff.Image.Blend - Text.Diff.Image.SideBySide - Text.Diff.Image.Swipe @@ -888,6 +919,7 @@ This document shows the translation status of each locale file in the repository - Text.TagCM.Copy.Tagger - Text.TagCM.CopyName - Text.TagCM.CustomAction +- Text.TagCM.DeleteMultiple - Text.UpdateSubmodules.Target - Text.UpdateSubmodules.UpdateToRemoteTrackingBranch - Text.ViewLogs @@ -904,7 +936,7 @@ This document shows the translation status of each locale file in the repository
-### ![uk__UA](https://img.shields.io/badge/uk__UA-82.40%25-yellow) +### ![uk__UA](https://img.shields.io/badge/uk__UA-82.02%25-yellow)
Missing keys in uk_UA.axaml @@ -976,6 +1008,9 @@ This document shows the translation status of each locale file in the repository - Text.DeinitSubmodule - Text.DeinitSubmodule.Force - Text.DeinitSubmodule.Path +- Text.DeleteMultiTags +- Text.DeleteMultiTags.DeleteFromRemotes +- Text.DeleteMultiTags.Tip - Text.Diff.Image.Blend - Text.Diff.Image.SideBySide - Text.Diff.Image.Swipe @@ -1052,6 +1087,7 @@ This document shows the translation status of each locale file in the repository - Text.TagCM.Copy.Tagger - Text.TagCM.CopyName - Text.TagCM.CustomAction +- Text.TagCM.DeleteMultiple - Text.UpdateSubmodules.UpdateToRemoteTrackingBranch - Text.ViewLogs - Text.ViewLogs.Clear From e278f4a730165986c7eac60233cae5bd653279a1 Mon Sep 17 00:00:00 2001 From: Mingun Date: Tue, 12 Aug 2025 11:28:17 +0500 Subject: [PATCH 097/591] localization: update Russian translation on the `Welcome` page and in top-level menus (#1704) The following keys was reviewed: - Text.About.* - Text.Clone.* - Text.Hotkeys.* - Text.PageTabBar.* - Text.ScanRepositories.* - Text.SelfUpdate.* - Text.Welcome.* --- src/Resources/Locales/ru_RU.axaml | 52 +++++++++++++++---------------- 1 file changed, 26 insertions(+), 26 deletions(-) diff --git a/src/Resources/Locales/ru_RU.axaml b/src/Resources/Locales/ru_RU.axaml index 7024d3eb5..865a46158 100644 --- a/src/Resources/Locales/ru_RU.axaml +++ b/src/Resources/Locales/ru_RU.axaml @@ -13,7 +13,7 @@ Расположение: Путь к рабочему каталогу (поддерживается относительный путь) Имя ветки: - Имя целевого каталога по умолчанию. (необязательно) + Имя целевого каталога по умолчанию (необязательно) Отслеживание ветки: Отслеживание внешней ветки Переключиться на: @@ -109,12 +109,12 @@ Очистить отложенные Вы пытаетесь очистить все отложенные. Вы уверены, что хотите продолжить? Клонировать внешний репозиторий - Расширенные параметры: - Дополнительные аргументы для клонирования репозитория. (необязательно). + Параметры: + Аргументы git clone (необязательно) Локальное имя: - Имя репозитория. (необязательно). + Имя репозитория (необязательно) Родительский каталог: - Создать и обновить подмодуль + Создать и обновить подмодули Адрес репозитория: ЗАКРЫТЬ Редактор @@ -423,34 +423,34 @@ Удерживайте Ctrl или Shift, чтобы выбрать несколько ревизий. Удерживайте ⌘ или ⇧, чтобы выбрать несколько ревизий. ПОДСКАЗКИ: - Ссылка на сочетания клавиш - ОБЩЕЕ + Справка по сочетаниям клавиш + ГЛОБАЛЬНЫЕ Клонировать репозиторий - Закрыть вкладку + Закрыть текущую вкладку Перейти на следующую вкладку Перейти на предыдущую вкладку Создать новую вкладку Открыть диалоговое окно настроек - Переключить активное рабочее место - Переключить активную страницу + Переключиться на рабочее место + Переключиться на вкладку РЕПОЗИТОРИЙ Зафиксировать сформированные изменения Зафиксировать и выложить сформированные изменения Сформировать все изменения и зафиксировать - Извлечение, запускается сразу + Извлечь (fetch), запускается сразу Режим доски (по умолчанию) Режим поиска ревизий - Загрузить, запускается сразу - Выложить, запускается сразу - Принудительно перезагрузить репозиторий + Загрузить (pull), запускается сразу + Выложить (push), запускается сразу + Принудительно перечитать репозиторий Переключить на «Изменения» - Переключить на «Истории» + Переключить на «Историю» Переключить на «Отложенные» ТЕКСТОВЫЙ РЕДАКТОР Закрыть панель поиска Найти следующее совпадение Найти предыдущее совпадение - Открыть с внешним инструментом сравнения/слияния + Открыть во внешнем инструменте сравнения/слияния Открыть панель поиска Отклонить Сформировать @@ -475,7 +475,7 @@ ОШИБКА УВЕДОМЛЕНИЕ Рабочие места - Страницы + Вкладки Влить ветку Изменить сообщение слияния В: @@ -496,7 +496,7 @@ Открыть в инструменте слияния Окрыть с... Необязательно. - Создать новую страницу + Создать новую вкладку Закладка Закрыть вкладку Закрыть другие вкладки @@ -712,9 +712,9 @@ СОХРАНИТЬ Сохранить как... Заплатка успешно сохранена! - Сканирование репозиторий + Обнаружение репозиториев Корневой каталог: - Проверка для обновления... + Проверить обновления... Доступна новая версия программного обеспечения: Не удалось проверить наличие обновлений! Загрузка @@ -807,17 +807,17 @@ Предупреждение Приветствие Создать группу - Создать подгруппу + Создать подгруппу... Клонировать репозиторий - Удалить + Удалить... ПОДДЕРЖИВАЕТСЯ: ПЕРЕТАСКИВАНИЕ КАТАЛОГОВ, ПОЛЬЗОВАТЕЛЬСКАЯ ГРУППИРОВКА. - Редактировать - Перейти в другую группу + Редактировать... + Переместить в другую группу... Открыть все репозитории Открыть репозиторий Открыть терминал - Повторное сканирование репозиториев в каталоге клонирования по умолчанию - Поиск репозиториев... + Обнаружить репозитории в каталоге клонирования по умолчанию + Найти репозиторий... Изменения Игнорировать Git Игнорировать все *{0} файлы From 20ca646307a4bbe848262c7392b1bf418a63d1bd Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Tue, 12 Aug 2025 06:28:26 +0000 Subject: [PATCH 098/591] doc: Update translation status and sort locale files --- src/Resources/Locales/ru_RU.axaml | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/src/Resources/Locales/ru_RU.axaml b/src/Resources/Locales/ru_RU.axaml index 865a46158..611003c6f 100644 --- a/src/Resources/Locales/ru_RU.axaml +++ b/src/Resources/Locales/ru_RU.axaml @@ -13,7 +13,7 @@ Расположение: Путь к рабочему каталогу (поддерживается относительный путь) Имя ветки: - Имя целевого каталога по умолчанию (необязательно) + Имя целевого каталога по умолчанию (необязательно) Отслеживание ветки: Отслеживание внешней ветки Переключиться на: @@ -712,9 +712,9 @@ СОХРАНИТЬ Сохранить как... Заплатка успешно сохранена! - Обнаружение репозиториев + Обнаружение репозиториев Корневой каталог: - Проверить обновления... + Проверить обновления... Доступна новая версия программного обеспечения: Не удалось проверить наличие обновлений! Загрузка @@ -807,17 +807,17 @@ Предупреждение Приветствие Создать группу - Создать подгруппу... + Создать подгруппу... Клонировать репозиторий - Удалить... + Удалить... ПОДДЕРЖИВАЕТСЯ: ПЕРЕТАСКИВАНИЕ КАТАЛОГОВ, ПОЛЬЗОВАТЕЛЬСКАЯ ГРУППИРОВКА. - Редактировать... - Переместить в другую группу... + Редактировать... + Переместить в другую группу... Открыть все репозитории Открыть репозиторий Открыть терминал - Обнаружить репозитории в каталоге клонирования по умолчанию - Найти репозиторий... + Обнаружить репозитории в каталоге клонирования по умолчанию + Найти репозиторий... Изменения Игнорировать Git Игнорировать все *{0} файлы From bd81ccd94f80f75647a2f0f579ff09948dcf7fa7 Mon Sep 17 00:00:00 2001 From: leo Date: Tue, 12 Aug 2025 16:03:58 +0800 Subject: [PATCH 099/591] fix: main thread deadlock cause by calling `.Result` directly (#1720) Signed-off-by: leo --- src/App.axaml.cs | 2 +- src/Commands/Command.cs | 22 ++ src/Commands/Config.cs | 26 ++ src/Commands/IsBareRepository.cs | 11 + src/Commands/IsConflictResolved.cs | 5 + src/Commands/IsLFSFiltered.cs | 10 + src/Commands/QueryCommitFullMessage.cs | 6 + src/Commands/QueryCurrentBranch.cs | 5 + src/Commands/QueryGitCommonDir.cs | 5 +- src/Commands/QueryGitDir.cs | 10 + src/Commands/QueryRepositoryRootPath.cs | 5 + src/Commands/QueryRevisionByRefName.cs | 10 + src/Commands/QuerySingleCommit.cs | 47 +-- src/Commands/QueryStagedChangesWithAmend.cs | 5 +- src/Models/Watcher.cs | 23 +- src/ViewModels/Blame.cs | 5 +- src/ViewModels/Conflict.cs | 4 +- src/ViewModels/CreateTag.cs | 4 +- src/ViewModels/DirHistories.cs | 2 +- src/ViewModels/FileHistories.cs | 144 +++++---- src/ViewModels/InProgressContexts.cs | 34 +-- src/ViewModels/Launcher.cs | 6 +- src/ViewModels/Merge.cs | 2 +- src/ViewModels/Repository.cs | 307 +++++++++++--------- src/ViewModels/RepositoryConfigure.cs | 2 +- src/ViewModels/RevisionCompare.cs | 44 +-- src/ViewModels/WorkingCopy.cs | 53 ++-- src/Views/Preferences.axaml.cs | 2 +- src/Views/WorkingCopy.axaml.cs | 2 +- 29 files changed, 470 insertions(+), 333 deletions(-) diff --git a/src/App.axaml.cs b/src/App.axaml.cs index 65e38e419..54d4354c2 100644 --- a/src/App.axaml.cs +++ b/src/App.axaml.cs @@ -578,7 +578,7 @@ private void TryOpenRepository(string repo) { if (!string.IsNullOrEmpty(repo) && Directory.Exists(repo)) { - var test = new Commands.QueryRepositoryRootPath(repo).GetResultAsync().Result; + var test = new Commands.QueryRepositoryRootPath(repo).GetResult(); if (test.IsSuccess && !string.IsNullOrEmpty(test.StdOut)) { Dispatcher.UIThread.Invoke(() => diff --git a/src/Commands/Command.cs b/src/Commands/Command.cs index 11a25b369..d697efcc9 100644 --- a/src/Commands/Command.cs +++ b/src/Commands/Command.cs @@ -50,6 +50,28 @@ public void Exec() } } + protected Result ReadToEnd() + { + using var proc = new Process() { StartInfo = CreateGitStartInfo(true) }; + + try + { + proc.Start(); + } + catch (Exception e) + { + return Result.Failed(e.Message); + } + + var rs = new Result() { IsSuccess = true }; + rs.StdOut = proc.StandardOutput.ReadToEnd(); + rs.StdErr = proc.StandardError.ReadToEnd(); + proc.WaitForExit(); + + rs.IsSuccess = proc.ExitCode == 0; + return rs; + } + public async Task ExecAsync() { Log?.AppendLine($"$ git {Args}\n"); diff --git a/src/Commands/Config.cs b/src/Commands/Config.cs index 0348c5109..52fc021cb 100644 --- a/src/Commands/Config.cs +++ b/src/Commands/Config.cs @@ -20,6 +20,26 @@ public Config(string repository) } } + public Dictionary ReadAll() + { + Args = "config -l"; + + var output = ReadToEnd(); + var rs = new Dictionary(); + if (output.IsSuccess) + { + var lines = output.StdOut.Split(['\r', '\n'], StringSplitOptions.RemoveEmptyEntries); + foreach (var line in lines) + { + var parts = line.Split('=', 2); + if (parts.Length == 2) + rs[parts[0]] = parts[1]; + } + } + + return rs; + } + public async Task> ReadAllAsync() { Args = "config -l"; @@ -40,6 +60,12 @@ public async Task> ReadAllAsync() return rs; } + public string Get(string key) + { + Args = $"config {key}"; + return ReadToEnd().StdOut.Trim(); + } + public async Task GetAsync(string key) { Args = $"config {key}"; diff --git a/src/Commands/IsBareRepository.cs b/src/Commands/IsBareRepository.cs index 98b127ce6..03520131c 100644 --- a/src/Commands/IsBareRepository.cs +++ b/src/Commands/IsBareRepository.cs @@ -11,6 +11,17 @@ public IsBareRepository(string path) Args = "rev-parse --is-bare-repository"; } + public bool GetResult() + { + if (!Directory.Exists(Path.Combine(WorkingDirectory, "refs")) || + !Directory.Exists(Path.Combine(WorkingDirectory, "objects")) || + !File.Exists(Path.Combine(WorkingDirectory, "HEAD"))) + return false; + + var rs = ReadToEnd(); + return rs.IsSuccess && rs.StdOut.Trim() == "true"; + } + public async Task GetResultAsync() { if (!Directory.Exists(Path.Combine(WorkingDirectory, "refs")) || diff --git a/src/Commands/IsConflictResolved.cs b/src/Commands/IsConflictResolved.cs index 2d53766a2..d13fccf1e 100644 --- a/src/Commands/IsConflictResolved.cs +++ b/src/Commands/IsConflictResolved.cs @@ -13,6 +13,11 @@ public IsConflictResolved(string repo, Models.Change change) Args = $"diff -a --ignore-cr-at-eol --check {opt}"; } + public bool GetResult() + { + return ReadToEnd().IsSuccess; + } + public async Task GetResultAsync() { var rs = await ReadToEndAsync().ConfigureAwait(false); diff --git a/src/Commands/IsLFSFiltered.cs b/src/Commands/IsLFSFiltered.cs index e8e5513ca..5c45f8bc6 100644 --- a/src/Commands/IsLFSFiltered.cs +++ b/src/Commands/IsLFSFiltered.cs @@ -20,9 +20,19 @@ public IsLFSFiltered(string repo, string sha, string path) RaiseError = false; } + public bool GetResult() + { + return Parse(ReadToEnd()); + } + public async Task GetResultAsync() { var rs = await ReadToEndAsync().ConfigureAwait(false); + return Parse(rs); + } + + private bool Parse(Result rs) + { return rs.IsSuccess && rs.StdOut.Contains("filter\0lfs"); } } diff --git a/src/Commands/QueryCommitFullMessage.cs b/src/Commands/QueryCommitFullMessage.cs index 07b77f2e3..2e9db9298 100644 --- a/src/Commands/QueryCommitFullMessage.cs +++ b/src/Commands/QueryCommitFullMessage.cs @@ -11,6 +11,12 @@ public QueryCommitFullMessage(string repo, string sha) Args = $"show --no-show-signature --format=%B -s {sha}"; } + public string GetResult() + { + var rs = ReadToEnd(); + return rs.IsSuccess ? rs.StdOut.TrimEnd() : string.Empty; + } + public async Task GetResultAsync() { var rs = await ReadToEndAsync().ConfigureAwait(false); diff --git a/src/Commands/QueryCurrentBranch.cs b/src/Commands/QueryCurrentBranch.cs index c721d13b0..8b698d1e5 100644 --- a/src/Commands/QueryCurrentBranch.cs +++ b/src/Commands/QueryCurrentBranch.cs @@ -11,6 +11,11 @@ public QueryCurrentBranch(string repo) Args = "branch --show-current"; } + public string GetResult() + { + return ReadToEnd().StdOut.Trim(); + } + public async Task GetResultAsync() { var rs = await ReadToEndAsync().ConfigureAwait(false); diff --git a/src/Commands/QueryGitCommonDir.cs b/src/Commands/QueryGitCommonDir.cs index c5b9339d5..83b54e764 100644 --- a/src/Commands/QueryGitCommonDir.cs +++ b/src/Commands/QueryGitCommonDir.cs @@ -1,5 +1,4 @@ using System.IO; -using System.Threading.Tasks; namespace SourceGit.Commands { @@ -11,9 +10,9 @@ public QueryGitCommonDir(string workDir) Args = "rev-parse --git-common-dir"; } - public async Task GetResultAsync() + public string GetResult() { - var rs = await ReadToEndAsync().ConfigureAwait(false); + var rs = ReadToEnd(); if (!rs.IsSuccess) return null; diff --git a/src/Commands/QueryGitDir.cs b/src/Commands/QueryGitDir.cs index ce8bfee60..6bca314df 100644 --- a/src/Commands/QueryGitDir.cs +++ b/src/Commands/QueryGitDir.cs @@ -11,9 +11,19 @@ public QueryGitDir(string workDir) Args = "rev-parse --git-dir"; } + public string GetResult() + { + return Parse(ReadToEnd()); + } + public async Task GetResultAsync() { var rs = await ReadToEndAsync().ConfigureAwait(false); + return Parse(rs); + } + + private string Parse(Result rs) + { if (!rs.IsSuccess) return null; diff --git a/src/Commands/QueryRepositoryRootPath.cs b/src/Commands/QueryRepositoryRootPath.cs index f7e1eb637..89d259296 100644 --- a/src/Commands/QueryRepositoryRootPath.cs +++ b/src/Commands/QueryRepositoryRootPath.cs @@ -10,6 +10,11 @@ public QueryRepositoryRootPath(string path) Args = "rev-parse --show-toplevel"; } + public Result GetResult() + { + return ReadToEnd(); + } + public async Task GetResultAsync() { return await ReadToEndAsync().ConfigureAwait(false); diff --git a/src/Commands/QueryRevisionByRefName.cs b/src/Commands/QueryRevisionByRefName.cs index 64a03e9df..78104523a 100644 --- a/src/Commands/QueryRevisionByRefName.cs +++ b/src/Commands/QueryRevisionByRefName.cs @@ -11,9 +11,19 @@ public QueryRevisionByRefName(string repo, string refname) Args = $"rev-parse {refname}"; } + public string GetResult() + { + return Parse(ReadToEnd()); + } + public async Task GetResultAsync() { var rs = await ReadToEndAsync().ConfigureAwait(false); + return Parse(rs); + } + + private string Parse(Result rs) + { if (rs.IsSuccess && !string.IsNullOrEmpty(rs.StdOut)) return rs.StdOut.Trim(); diff --git a/src/Commands/QuerySingleCommit.cs b/src/Commands/QuerySingleCommit.cs index 897459f0d..822f5c4a5 100644 --- a/src/Commands/QuerySingleCommit.cs +++ b/src/Commands/QuerySingleCommit.cs @@ -12,31 +12,40 @@ public QuerySingleCommit(string repo, string sha) Args = $"show --no-show-signature --decorate=full --format=%H%n%P%n%D%n%aN±%aE%n%at%n%cN±%cE%n%ct%n%s -s {sha}"; } + public Models.Commit GetResult() + { + var rs = ReadToEnd(); + return Parse(rs); + } + public async Task GetResultAsync() { var rs = await ReadToEndAsync().ConfigureAwait(false); - if (rs.IsSuccess && !string.IsNullOrEmpty(rs.StdOut)) - { - var commit = new Models.Commit(); - var lines = rs.StdOut.Split('\n'); - if (lines.Length < 8) - return null; + return Parse(rs); + } + + private Models.Commit Parse(Result rs) + { + if (!rs.IsSuccess || string.IsNullOrEmpty(rs.StdOut)) + return null; - commit.SHA = lines[0]; - if (!string.IsNullOrEmpty(lines[1])) - commit.Parents.AddRange(lines[1].Split(' ', StringSplitOptions.RemoveEmptyEntries)); - if (!string.IsNullOrEmpty(lines[2])) - commit.ParseDecorators(lines[2]); - commit.Author = Models.User.FindOrAdd(lines[3]); - commit.AuthorTime = ulong.Parse(lines[4]); - commit.Committer = Models.User.FindOrAdd(lines[5]); - commit.CommitterTime = ulong.Parse(lines[6]); - commit.Subject = lines[7]; + var commit = new Models.Commit(); + var lines = rs.StdOut.Split('\n'); + if (lines.Length < 8) + return null; - return commit; - } + commit.SHA = lines[0]; + if (!string.IsNullOrEmpty(lines[1])) + commit.Parents.AddRange(lines[1].Split(' ', StringSplitOptions.RemoveEmptyEntries)); + if (!string.IsNullOrEmpty(lines[2])) + commit.ParseDecorators(lines[2]); + commit.Author = Models.User.FindOrAdd(lines[3]); + commit.AuthorTime = ulong.Parse(lines[4]); + commit.Committer = Models.User.FindOrAdd(lines[5]); + commit.CommitterTime = ulong.Parse(lines[6]); + commit.Subject = lines[7]; - return null; + return commit; } } } diff --git a/src/Commands/QueryStagedChangesWithAmend.cs b/src/Commands/QueryStagedChangesWithAmend.cs index bec033ff7..229d9e65e 100644 --- a/src/Commands/QueryStagedChangesWithAmend.cs +++ b/src/Commands/QueryStagedChangesWithAmend.cs @@ -1,7 +1,6 @@ using System; using System.Collections.Generic; using System.Text.RegularExpressions; -using System.Threading.Tasks; namespace SourceGit.Commands { @@ -20,9 +19,9 @@ public QueryStagedChangesWithAmend(string repo, string parent) _parent = parent; } - public async Task> GetResultAsync() + public List GetResult() { - var rs = await ReadToEndAsync().ConfigureAwait(false); + var rs = ReadToEnd(); if (!rs.IsSuccess) return []; diff --git a/src/Models/Watcher.cs b/src/Models/Watcher.cs index 9ba7ee9cb..23980399d 100644 --- a/src/Models/Watcher.cs +++ b/src/Models/Watcher.cs @@ -2,7 +2,6 @@ using System.Collections.Generic; using System.IO; using System.Threading; -using System.Threading.Tasks; namespace SourceGit.Models { @@ -125,44 +124,44 @@ private void Tick(object sender) if (_updateTags > 0) { _updateTags = 0; - Task.Run(_repo.RefreshTags); + _repo.RefreshTags(); } if (_updateSubmodules > 0 || _repo.MayHaveSubmodules()) { _updateSubmodules = 0; - Task.Run(_repo.RefreshSubmodules); + _repo.RefreshSubmodules(); } - Task.Run(_repo.RefreshBranches); - Task.Run(_repo.RefreshCommits); - Task.Run(_repo.RefreshWorkingCopyChanges); - Task.Run(_repo.RefreshWorktrees); + _repo.RefreshBranches(); + _repo.RefreshCommits(); + _repo.RefreshWorkingCopyChanges(); + _repo.RefreshWorktrees(); } if (_updateWC > 0 && now > _updateWC) { _updateWC = 0; - Task.Run(_repo.RefreshWorkingCopyChanges); + _repo.RefreshWorkingCopyChanges(); } if (_updateSubmodules > 0 && now > _updateSubmodules) { _updateSubmodules = 0; - Task.Run(_repo.RefreshSubmodules); + _repo.RefreshSubmodules(); } if (_updateStashes > 0 && now > _updateStashes) { _updateStashes = 0; - Task.Run(_repo.RefreshStashes); + _repo.RefreshStashes(); } if (_updateTags > 0 && now > _updateTags) { _updateTags = 0; - Task.Run(_repo.RefreshTags); - Task.Run(_repo.RefreshCommits); + _repo.RefreshTags(); + _repo.RefreshCommits(); } } diff --git a/src/ViewModels/Blame.cs b/src/ViewModels/Blame.cs index affe40d92..e71e672cc 100644 --- a/src/ViewModels/Blame.cs +++ b/src/ViewModels/Blame.cs @@ -63,10 +63,7 @@ public string GetCommitMessage(string sha) if (_commitMessages.TryGetValue(sha, out var msg)) return msg; - msg = new Commands.QueryCommitFullMessage(_repo, sha) - .GetResultAsync() - .Result; - + msg = new Commands.QueryCommitFullMessage(_repo, sha).GetResult(); _commitMessages[sha] = msg; return msg; } diff --git a/src/ViewModels/Conflict.cs b/src/ViewModels/Conflict.cs index 674d223f4..ccb8816d5 100644 --- a/src/ViewModels/Conflict.cs +++ b/src/ViewModels/Conflict.cs @@ -20,7 +20,7 @@ public ConflictSourceBranch(Repository repo, Models.Branch branch) { Name = branch.Name; Head = branch.Head; - Revision = new Commands.QuerySingleCommit(repo.FullPath, branch.Head).GetResultAsync().Result ?? new Models.Commit() { SHA = branch.Head }; + Revision = new Commands.QuerySingleCommit(repo.FullPath, branch.Head).GetResult() ?? new Models.Commit() { SHA = branch.Head }; } } @@ -69,7 +69,7 @@ public Conflict(Repository repo, WorkingCopy wc, Models.Change change) if (!isSubmodule && (_change.ConflictReason is Models.ConflictReason.BothAdded or Models.ConflictReason.BothModified)) { CanUseExternalMergeTool = true; - IsResolved = new Commands.IsConflictResolved(repo.FullPath, change).GetResultAsync().Result; + IsResolved = new Commands.IsConflictResolved(repo.FullPath, change).GetResult(); } switch (wc.InProgressContext) diff --git a/src/ViewModels/CreateTag.cs b/src/ViewModels/CreateTag.cs index d9113c132..a234594d9 100644 --- a/src/ViewModels/CreateTag.cs +++ b/src/ViewModels/CreateTag.cs @@ -51,7 +51,7 @@ public CreateTag(Repository repo, Models.Branch branch) _basedOn = branch.Head; BasedOn = branch; - SignTag = new Commands.Config(repo.FullPath).GetAsync("tag.gpgsign").Result.Equals("true", StringComparison.OrdinalIgnoreCase); + SignTag = new Commands.Config(repo.FullPath).Get("tag.gpgsign").Equals("true", StringComparison.OrdinalIgnoreCase); } public CreateTag(Repository repo, Models.Commit commit) @@ -60,7 +60,7 @@ public CreateTag(Repository repo, Models.Commit commit) _basedOn = commit.SHA; BasedOn = commit; - SignTag = new Commands.Config(repo.FullPath).GetAsync("tag.gpgsign").Result.Equals("true", StringComparison.OrdinalIgnoreCase); + SignTag = new Commands.Config(repo.FullPath).Get("tag.gpgsign").Equals("true", StringComparison.OrdinalIgnoreCase); } public static ValidationResult ValidateTagName(string name, ValidationContext ctx) diff --git a/src/ViewModels/DirHistories.cs b/src/ViewModels/DirHistories.cs index 04fee2b27..3dfcc68d1 100644 --- a/src/ViewModels/DirHistories.cs +++ b/src/ViewModels/DirHistories.cs @@ -87,7 +87,7 @@ public string GetCommitFullMessage(Models.Commit commit) if (_cachedCommitFullMessage.TryGetValue(sha, out var msg)) return msg; - msg = new Commands.QueryCommitFullMessage(_repo.FullPath, sha).GetResultAsync().Result; + msg = new Commands.QueryCommitFullMessage(_repo.FullPath, sha).GetResult(); _cachedCommitFullMessage[sha] = msg; return msg; } diff --git a/src/ViewModels/FileHistories.cs b/src/ViewModels/FileHistories.cs index 19066ca21..011adda94 100644 --- a/src/ViewModels/FileHistories.cs +++ b/src/ViewModels/FileHistories.cs @@ -74,89 +74,83 @@ await Commands.SaveRevisionFile private void RefreshViewContent() { if (_isDiffMode) + { SetViewContentAsDiff(); - else - SetViewContentAsRevisionFile(); + return; + } + + Task.Run(async () => + { + var objs = await new Commands.QueryRevisionObjects(_repo.FullPath, _revision.SHA, _file) + .GetResultAsync() + .ConfigureAwait(false); + + if (objs.Count == 0) + { + Dispatcher.UIThread.Post(() => ViewContent = new FileHistoriesRevisionFile(_file)); + return; + } + + var revisionContent = await GetRevisionFileContentAsync(objs[0]).ConfigureAwait(false); + Dispatcher.UIThread.Post(() => ViewContent = revisionContent); + }); } - private void SetViewContentAsRevisionFile() + private async Task GetRevisionFileContentAsync(Models.Object obj) { - var objs = new Commands.QueryRevisionObjects(_repo.FullPath, _revision.SHA, _file).GetResultAsync().Result; - if (objs.Count == 0) + if (obj.Type == Models.ObjectType.Blob) { - ViewContent = new FileHistoriesRevisionFile(_file); - return; + var isBinary = await new Commands.IsBinary(_repo.FullPath, _revision.SHA, _file).GetResultAsync().ConfigureAwait(false); + if (isBinary) + { + var imgDecoder = ImageSource.GetDecoder(_file); + if (imgDecoder != Models.ImageDecoder.None) + { + var source = await ImageSource.FromRevisionAsync(_repo.FullPath, _revision.SHA, _file, imgDecoder).ConfigureAwait(false); + var image = new Models.RevisionImageFile(_file, source.Bitmap, source.Size); + return new FileHistoriesRevisionFile(_file, image, true); + } + + var size = await new Commands.QueryFileSize(_repo.FullPath, _file, _revision.SHA).GetResultAsync().ConfigureAwait(false); + var binaryFile = new Models.RevisionBinaryFile() { Size = size }; + return new FileHistoriesRevisionFile(_file, binaryFile, true); + } + + var contentStream = await Commands.QueryFileContent.RunAsync(_repo.FullPath, _revision.SHA, _file).ConfigureAwait(false); + var content = await new StreamReader(contentStream).ReadToEndAsync(); + var lfs = Models.LFSObject.Parse(content); + if (lfs != null) + { + var imgDecoder = ImageSource.GetDecoder(_file); + if (imgDecoder != Models.ImageDecoder.None) + { + var combined = new RevisionLFSImage(_repo.FullPath, _file, lfs, imgDecoder); + return new FileHistoriesRevisionFile(_file, combined, true); + } + + var rlfs = new Models.RevisionLFSObject() { Object = lfs }; + return new FileHistoriesRevisionFile(_file, rlfs, true); + } + + var txt = new Models.RevisionTextFile() { FileName = obj.Path, Content = content }; + return new FileHistoriesRevisionFile(_file, txt, true); } - var obj = objs[0]; - switch (obj.Type) + if (obj.Type == Models.ObjectType.Commit) { - case Models.ObjectType.Blob: - Task.Run(async () => - { - var isBinary = await new Commands.IsBinary(_repo.FullPath, _revision.SHA, _file).GetResultAsync().ConfigureAwait(false); - if (isBinary) - { - var imgDecoder = ImageSource.GetDecoder(_file); - if (imgDecoder != Models.ImageDecoder.None) - { - var source = await ImageSource.FromRevisionAsync(_repo.FullPath, _revision.SHA, _file, imgDecoder).ConfigureAwait(false); - var image = new Models.RevisionImageFile(_file, source.Bitmap, source.Size); - Dispatcher.UIThread.Post(() => ViewContent = new FileHistoriesRevisionFile(_file, image, true)); - } - else - { - var size = await new Commands.QueryFileSize(_repo.FullPath, _file, _revision.SHA).GetResultAsync().ConfigureAwait(false); - var binaryFile = new Models.RevisionBinaryFile() { Size = size }; - Dispatcher.UIThread.Post(() => ViewContent = new FileHistoriesRevisionFile(_file, binaryFile, true)); - } - - return; - } - - var contentStream = await Commands.QueryFileContent.RunAsync(_repo.FullPath, _revision.SHA, _file).ConfigureAwait(false); - var content = await new StreamReader(contentStream).ReadToEndAsync(); - var lfs = Models.LFSObject.Parse(content); - if (lfs != null) - { - var imgDecoder = ImageSource.GetDecoder(_file); - if (imgDecoder != Models.ImageDecoder.None) - { - var combined = new RevisionLFSImage(_repo.FullPath, _file, lfs, imgDecoder); - Dispatcher.UIThread.Post(() => ViewContent = new FileHistoriesRevisionFile(_file, combined, true)); - } - else - { - var rlfs = new Models.RevisionLFSObject() { Object = lfs }; - Dispatcher.UIThread.Post(() => ViewContent = new FileHistoriesRevisionFile(_file, rlfs, true)); - } - } - else - { - var txt = new Models.RevisionTextFile() { FileName = obj.Path, Content = content }; - Dispatcher.UIThread.Post(() => ViewContent = new FileHistoriesRevisionFile(_file, txt, true)); - } - }); - break; - case Models.ObjectType.Commit: - Task.Run(async () => - { - var submoduleRoot = Path.Combine(_repo.FullPath, _file); - var commit = await new Commands.QuerySingleCommit(submoduleRoot, obj.SHA).GetResultAsync().ConfigureAwait(false); - var message = commit != null ? await new Commands.QueryCommitFullMessage(submoduleRoot, obj.SHA).GetResultAsync().ConfigureAwait(false) : null; - var module = new Models.RevisionSubmodule() - { - Commit = commit ?? new Models.Commit() { SHA = obj.SHA }, - FullMessage = new Models.CommitFullMessage { Message = message } - }; - - Dispatcher.UIThread.Post(() => ViewContent = new FileHistoriesRevisionFile(_file, module)); - }); - break; - default: - ViewContent = new FileHistoriesRevisionFile(_file); - break; + var submoduleRoot = Path.Combine(_repo.FullPath, _file); + var commit = await new Commands.QuerySingleCommit(submoduleRoot, obj.SHA).GetResultAsync().ConfigureAwait(false); + var message = commit != null ? await new Commands.QueryCommitFullMessage(submoduleRoot, obj.SHA).GetResultAsync().ConfigureAwait(false) : null; + var module = new Models.RevisionSubmodule() + { + Commit = commit ?? new Models.Commit() { SHA = obj.SHA }, + FullMessage = new Models.CommitFullMessage { Message = message } + }; + + return new FileHistoriesRevisionFile(_file, module); } + + return new FileHistoriesRevisionFile(_file); } private void SetViewContentAsDiff() @@ -326,7 +320,7 @@ public string GetCommitFullMessage(Models.Commit commit) if (_fullCommitMessages.TryGetValue(sha, out var msg)) return msg; - msg = new Commands.QueryCommitFullMessage(_repo.FullPath, sha).GetResultAsync().Result; + msg = new Commands.QueryCommitFullMessage(_repo.FullPath, sha).GetResult(); _fullCommitMessages[sha] = msg; return msg; } diff --git a/src/ViewModels/InProgressContexts.cs b/src/ViewModels/InProgressContexts.cs index 5104130ec..f816aab3e 100644 --- a/src/ViewModels/InProgressContexts.cs +++ b/src/ViewModels/InProgressContexts.cs @@ -11,9 +11,9 @@ protected InProgressContext(string repo, string cmd) _cmd = cmd; } - public Task AbortAsync() + public async Task AbortAsync() { - return new Commands.Command() + return await new Commands.Command() { WorkingDirectory = _repo, Context = _repo, @@ -21,9 +21,9 @@ public Task AbortAsync() }.ExecAsync(); } - public virtual Task SkipAsync() + public virtual async Task SkipAsync() { - return new Commands.Command() + return await new Commands.Command() { WorkingDirectory = _repo, Context = _repo, @@ -31,9 +31,9 @@ public virtual Task SkipAsync() }.ExecAsync(); } - public virtual Task ContinueAsync() + public virtual async Task ContinueAsync() { - return new Commands.Command() + return await new Commands.Command() { WorkingDirectory = _repo, Context = _repo, @@ -61,7 +61,7 @@ public string HeadName public CherryPickInProgress(Repository repo) : base(repo.FullPath, "cherry-pick") { var headSHA = File.ReadAllText(Path.Combine(repo.GitDir, "CHERRY_PICK_HEAD")).Trim(); - Head = new Commands.QuerySingleCommit(repo.FullPath, headSHA).GetResultAsync().Result ?? new Models.Commit() { SHA = headSHA }; + Head = new Commands.QuerySingleCommit(repo.FullPath, headSHA).GetResult() ?? new Models.Commit() { SHA = headSHA }; HeadName = Head.GetFriendlyName(); } } @@ -99,19 +99,19 @@ public RebaseInProgress(Repository repo) : base(repo.FullPath, "rebase") var stoppedSHAPath = Path.Combine(repo.GitDir, "rebase-merge", "stopped-sha"); var stoppedSHA = File.Exists(stoppedSHAPath) ? File.ReadAllText(stoppedSHAPath).Trim() - : new Commands.QueryRevisionByRefName(repo.FullPath, HeadName).GetResultAsync().Result; + : new Commands.QueryRevisionByRefName(repo.FullPath, HeadName).GetResult(); if (!string.IsNullOrEmpty(stoppedSHA)) - StoppedAt = new Commands.QuerySingleCommit(repo.FullPath, stoppedSHA).GetResultAsync().Result ?? new Models.Commit() { SHA = stoppedSHA }; + StoppedAt = new Commands.QuerySingleCommit(repo.FullPath, stoppedSHA).GetResult() ?? new Models.Commit() { SHA = stoppedSHA }; var ontoSHA = File.ReadAllText(Path.Combine(repo.GitDir, "rebase-merge", "onto")).Trim(); - Onto = new Commands.QuerySingleCommit(repo.FullPath, ontoSHA).GetResultAsync().Result ?? new Models.Commit() { SHA = ontoSHA }; + Onto = new Commands.QuerySingleCommit(repo.FullPath, ontoSHA).GetResult() ?? new Models.Commit() { SHA = ontoSHA }; BaseName = Onto.GetFriendlyName(); } - public override Task ContinueAsync() + public override async Task ContinueAsync() { - return new Commands.Command() + return await new Commands.Command() { WorkingDirectory = _repo, Context = _repo, @@ -131,7 +131,7 @@ public Models.Commit Head public RevertInProgress(Repository repo) : base(repo.FullPath, "revert") { var headSHA = File.ReadAllText(Path.Combine(repo.GitDir, "REVERT_HEAD")).Trim(); - Head = new Commands.QuerySingleCommit(repo.FullPath, headSHA).GetResultAsync().Result ?? new Models.Commit() { SHA = headSHA }; + Head = new Commands.QuerySingleCommit(repo.FullPath, headSHA).GetResult() ?? new Models.Commit() { SHA = headSHA }; } } @@ -154,16 +154,16 @@ public string SourceName public MergeInProgress(Repository repo) : base(repo.FullPath, "merge") { - Current = new Commands.QueryCurrentBranch(repo.FullPath).GetResultAsync().Result; + Current = new Commands.QueryCurrentBranch(repo.FullPath).GetResult(); var sourceSHA = File.ReadAllText(Path.Combine(repo.GitDir, "MERGE_HEAD")).Trim(); - Source = new Commands.QuerySingleCommit(repo.FullPath, sourceSHA).GetResultAsync().Result ?? new Models.Commit() { SHA = sourceSHA }; + Source = new Commands.QuerySingleCommit(repo.FullPath, sourceSHA).GetResult() ?? new Models.Commit() { SHA = sourceSHA }; SourceName = Source.GetFriendlyName(); } - public override Task SkipAsync() + public override async Task SkipAsync() { - return Task.FromResult(true); + return await Task.FromResult(true); } } } diff --git a/src/ViewModels/Launcher.cs b/src/ViewModels/Launcher.cs index 0febfdfc4..49bb7cef7 100644 --- a/src/ViewModels/Launcher.cs +++ b/src/ViewModels/Launcher.cs @@ -94,7 +94,7 @@ public Launcher(string startupRepo) foreach (var w in pref.Workspaces) w.IsActive = false; - var test = new Commands.QueryRepositoryRootPath(startupRepo).GetResultAsync().Result; + var test = new Commands.QueryRepositoryRootPath(startupRepo).GetResult(); if (!test.IsSuccess || string.IsNullOrEmpty(test.StdOut)) { Pages[0].Notifications.Add(new Models.Notification @@ -344,7 +344,7 @@ public void OpenRepositoryInTab(RepositoryNode node, LauncherPage page) return; } - var isBare = new Commands.IsBareRepository(node.Id).GetResultAsync().Result; + var isBare = new Commands.IsBareRepository(node.Id).GetResult(); var gitDir = isBare ? node.Id : GetRepositoryGitDir(node.Id); if (string.IsNullOrEmpty(gitDir)) { @@ -446,7 +446,7 @@ private string GetRepositoryGitDir(string repo) return null; } - return new Commands.QueryGitDir(repo).GetResultAsync().Result; + return new Commands.QueryGitDir(repo).GetResult(); } private void CloseRepositoryInTab(LauncherPage page, bool removeFromWorkspace = true) diff --git a/src/ViewModels/Merge.cs b/src/ViewModels/Merge.cs index 3ccf6a6fe..f893a9099 100644 --- a/src/ViewModels/Merge.cs +++ b/src/ViewModels/Merge.cs @@ -79,7 +79,7 @@ public override async Task Sure() private Models.MergeMode AutoSelectMergeMode() { - var config = new Commands.Config(_repo.FullPath).GetAsync($"branch.{Into}.mergeoptions").Result; + var config = new Commands.Config(_repo.FullPath).Get($"branch.{Into}.mergeoptions"); var mode = config switch { "--ff-only" => Models.MergeMode.FastForward, diff --git a/src/ViewModels/Repository.cs b/src/ViewModels/Repository.cs index d7b87e7ba..ffa985545 100644 --- a/src/ViewModels/Repository.cs +++ b/src/ViewModels/Repository.cs @@ -96,7 +96,7 @@ public bool EnableTopoOrderInHistories if (value != _settings.EnableTopoOrderInHistories) { _settings.EnableTopoOrderInHistories = value; - Task.Run(RefreshCommits); + RefreshCommits(); } } } @@ -109,7 +109,7 @@ public Models.HistoryShowFlags HistoryShowFlags if (value != _settings.HistoryShowFlags) { _settings.HistoryShowFlags = value; - Task.Run(RefreshCommits); + RefreshCommits(); } } } @@ -266,7 +266,7 @@ public bool IncludeUntracked { _settings.IncludeUntrackedInLocalChanges = value; OnPropertyChanged(); - Task.Run(RefreshWorkingCopyChanges); + RefreshWorkingCopyChanges(); } } } @@ -536,7 +536,7 @@ public void Open() var gitDirForWatcher = _gitDir; if (_gitDir.Replace('\\', '/').IndexOf("/worktrees/", StringComparison.Ordinal) > 0) { - var commonDir = new Commands.QueryGitCommonDir(_fullpath).GetResultAsync().Result; + var commonDir = new Commands.QueryGitCommonDir(_fullpath).GetResult(); if (!string.IsNullOrEmpty(commonDir)) gitDirForWatcher = commonDir; } @@ -762,13 +762,13 @@ public async Task ChangeIssueTrackerShareModeAsync(Models.IssueTracker rule) public void RefreshAll() { - Task.Run(RefreshCommits); - Task.Run(RefreshBranches); - Task.Run(RefreshTags); - Task.Run(RefreshSubmodules); - Task.Run(RefreshWorktrees); - Task.Run(RefreshWorkingCopyChanges); - Task.Run(RefreshStashes); + RefreshCommits(); + RefreshBranches(); + RefreshTags(); + RefreshSubmodules(); + RefreshWorktrees(); + RefreshWorkingCopyChanges(); + RefreshStashes(); Task.Run(async () => { @@ -959,10 +959,10 @@ public void MarkBranchesDirtyManually() { if (_watcher == null) { - Task.Run(RefreshBranches); - Task.Run(RefreshCommits); - Task.Run(RefreshWorkingCopyChanges); - Task.Run(RefreshWorktrees); + RefreshBranches(); + RefreshCommits(); + RefreshWorkingCopyChanges(); + RefreshWorktrees(); } else { @@ -974,8 +974,8 @@ public void MarkTagsDirtyManually() { if (_watcher == null) { - Task.Run(RefreshTags); - Task.Run(RefreshCommits); + RefreshTags(); + RefreshCommits(); } else { @@ -986,7 +986,7 @@ public void MarkTagsDirtyManually() public void MarkWorkingCopyDirtyManually() { if (_watcher == null) - Task.Run(RefreshWorkingCopyChanges); + RefreshWorkingCopyChanges(); else _watcher.MarkWorkingCopyDirtyManually(); } @@ -1028,7 +1028,7 @@ public void ClearHistoriesFilter() ResetBranchTreeFilterMode(LocalBranchTrees); ResetBranchTreeFilterMode(RemoteBranchTrees); ResetTagFilterMode(); - Task.Run(RefreshCommits); + RefreshCommits(); } public void RemoveHistoriesFilter(Models.Filter filter) @@ -1186,74 +1186,83 @@ public bool MayHaveSubmodules() public void RefreshBranches() { - var branches = new Commands.QueryBranches(_fullpath).GetResultAsync().Result; - var remotes = new Commands.QueryRemotes(_fullpath).GetResultAsync().Result; - var builder = BuildBranchTree(branches, remotes); - - Dispatcher.UIThread.Invoke(() => + Task.Run(async () => { - lock (_lockRemotes) - Remotes = remotes; - - Branches = branches; - CurrentBranch = branches.Find(x => x.IsCurrent); - LocalBranchTrees = builder.Locals; - RemoteBranchTrees = builder.Remotes; + var branches = await new Commands.QueryBranches(_fullpath).GetResultAsync().ConfigureAwait(false); + var remotes = await new Commands.QueryRemotes(_fullpath).GetResultAsync().ConfigureAwait(false); + var builder = BuildBranchTree(branches, remotes); - var localBranchesCount = 0; - foreach (var b in branches) + Dispatcher.UIThread.Invoke(() => { - if (b.IsLocal && !b.IsDetachedHead) - localBranchesCount++; - } - LocalBranchesCount = localBranchesCount; + lock (_lockRemotes) + Remotes = remotes; - if (_workingCopy != null) - _workingCopy.HasRemotes = remotes.Count > 0; + Branches = branches; + CurrentBranch = branches.Find(x => x.IsCurrent); + LocalBranchTrees = builder.Locals; + RemoteBranchTrees = builder.Remotes; - var hasPendingPullOrPush = CurrentBranch?.TrackStatus.IsVisible ?? false; - GetOwnerPage()?.ChangeDirtyState(Models.DirtyState.HasPendingPullOrPush, !hasPendingPullOrPush); + var localBranchesCount = 0; + foreach (var b in branches) + { + if (b.IsLocal && !b.IsDetachedHead) + localBranchesCount++; + } + LocalBranchesCount = localBranchesCount; + + if (_workingCopy != null) + _workingCopy.HasRemotes = remotes.Count > 0; + + var hasPendingPullOrPush = CurrentBranch?.TrackStatus.IsVisible ?? false; + GetOwnerPage()?.ChangeDirtyState(Models.DirtyState.HasPendingPullOrPush, !hasPendingPullOrPush); + }); }); } public void RefreshWorktrees() { - var worktrees = new Commands.Worktree(_fullpath).ReadAllAsync().Result; - if (worktrees.Count > 0) + Task.Run(async () => { - var cleaned = new List(); - var normalizedGitDir = _gitDir.Replace('\\', '/'); - - foreach (var worktree in worktrees) + var worktrees = await new Commands.Worktree(_fullpath).ReadAllAsync().ConfigureAwait(false); + if (worktrees.Count > 0) { - if (worktree.FullPath.Equals(_fullpath, StringComparison.Ordinal) || - worktree.FullPath.Equals(normalizedGitDir, StringComparison.Ordinal)) - continue; + var cleaned = new List(); + var normalizedGitDir = _gitDir.Replace('\\', '/'); - cleaned.Add(worktree); - } + foreach (var worktree in worktrees) + { + if (worktree.FullPath.Equals(_fullpath, StringComparison.Ordinal) || + worktree.FullPath.Equals(normalizedGitDir, StringComparison.Ordinal)) + continue; - Dispatcher.UIThread.Invoke(() => - { - Worktrees = cleaned; - }); - } - else - { - Dispatcher.UIThread.Invoke(() => + cleaned.Add(worktree); + } + + Dispatcher.UIThread.Invoke(() => + { + Worktrees = cleaned; + }); + } + else { - Worktrees = worktrees; - }); - } + Dispatcher.UIThread.Invoke(() => + { + Worktrees = worktrees; + }); + } + }); } public void RefreshTags() { - var tags = new Commands.QueryTags(_fullpath).GetResultAsync().Result; - Dispatcher.UIThread.Invoke(() => + Task.Run(async () => { - Tags = tags; - VisibleTags = BuildVisibleTags(); + var tags = await new Commands.QueryTags(_fullpath).GetResultAsync().ConfigureAwait(false); + Dispatcher.UIThread.Invoke(() => + { + Tags = tags; + VisibleTags = BuildVisibleTags(); + }); }); } @@ -1261,47 +1270,50 @@ public void RefreshCommits() { Dispatcher.UIThread.Invoke(() => _histories.IsLoading = true); - var builder = new StringBuilder(); - builder.Append($"-{Preferences.Instance.MaxHistoryCommits} "); + Task.Run(async () => + { + var builder = new StringBuilder(); + builder.Append($"-{Preferences.Instance.MaxHistoryCommits} "); - if (_settings.EnableTopoOrderInHistories) - builder.Append("--topo-order "); - else - builder.Append("--date-order "); + if (_settings.EnableTopoOrderInHistories) + builder.Append("--topo-order "); + else + builder.Append("--date-order "); - if (_settings.HistoryShowFlags.HasFlag(Models.HistoryShowFlags.Reflog)) - builder.Append("--reflog "); + if (_settings.HistoryShowFlags.HasFlag(Models.HistoryShowFlags.Reflog)) + builder.Append("--reflog "); - if (_settings.HistoryShowFlags.HasFlag(Models.HistoryShowFlags.FirstParentOnly)) - builder.Append("--first-parent "); + if (_settings.HistoryShowFlags.HasFlag(Models.HistoryShowFlags.FirstParentOnly)) + builder.Append("--first-parent "); - if (_settings.HistoryShowFlags.HasFlag(Models.HistoryShowFlags.SimplifyByDecoration)) - builder.Append("--simplify-by-decoration "); + if (_settings.HistoryShowFlags.HasFlag(Models.HistoryShowFlags.SimplifyByDecoration)) + builder.Append("--simplify-by-decoration "); - var filters = _settings.BuildHistoriesFilter(); - if (string.IsNullOrEmpty(filters)) - builder.Append("--branches --remotes --tags HEAD"); - else - builder.Append(filters); + var filters = _settings.BuildHistoriesFilter(); + 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 graph = Models.CommitGraph.Parse(commits, _settings.HistoryShowFlags.HasFlag(Models.HistoryShowFlags.FirstParentOnly)); + var commits = await new Commands.QueryCommits(_fullpath, builder.ToString()).GetResultAsync().ConfigureAwait(false); + var graph = Models.CommitGraph.Parse(commits, _settings.HistoryShowFlags.HasFlag(Models.HistoryShowFlags.FirstParentOnly)); - Dispatcher.UIThread.Invoke(() => - { - if (_histories != null) + Dispatcher.UIThread.Invoke(() => { - _histories.IsLoading = false; - _histories.Commits = commits; - _histories.Graph = graph; + if (_histories != null) + { + _histories.IsLoading = false; + _histories.Commits = commits; + _histories.Graph = graph; - BisectState = _histories.UpdateBisectInfo(); + BisectState = _histories.UpdateBisectInfo(); - if (!string.IsNullOrEmpty(_navigateToCommitDelayed)) - NavigateToCommit(_navigateToCommitDelayed); - } + if (!string.IsNullOrEmpty(_navigateToCommitDelayed)) + NavigateToCommit(_navigateToCommitDelayed); + } - _navigateToCommitDelayed = string.Empty; + _navigateToCommitDelayed = string.Empty; + }); }); } @@ -1321,41 +1333,44 @@ public void RefreshSubmodules() return; } - var submodules = new Commands.QuerySubmodules(_fullpath).GetResultAsync().Result; - _watcher?.SetSubmodules(submodules); - - Dispatcher.UIThread.Invoke(() => + Task.Run(async () => { - bool hasChanged = _submodules.Count != submodules.Count; - if (!hasChanged) - { - var old = new Dictionary(); - foreach (var module in _submodules) - old.Add(module.Path, module); + var submodules = await new Commands.QuerySubmodules(_fullpath).GetResultAsync().ConfigureAwait(false); + _watcher?.SetSubmodules(submodules); - foreach (var module in submodules) + Dispatcher.UIThread.Invoke(() => + { + bool hasChanged = _submodules.Count != submodules.Count; + if (!hasChanged) { - if (!old.TryGetValue(module.Path, out var exist)) + var old = new Dictionary(); + foreach (var module in _submodules) + old.Add(module.Path, module); + + foreach (var module in submodules) { - hasChanged = true; - break; + if (!old.TryGetValue(module.Path, out var exist)) + { + hasChanged = true; + break; + } + + hasChanged = !exist.SHA.Equals(module.SHA, StringComparison.Ordinal) || + !exist.Branch.Equals(module.Branch, StringComparison.Ordinal) || + !exist.URL.Equals(module.URL, StringComparison.Ordinal) || + exist.Status != module.Status; + + if (hasChanged) + break; } - - hasChanged = !exist.SHA.Equals(module.SHA, StringComparison.Ordinal) || - !exist.Branch.Equals(module.Branch, StringComparison.Ordinal) || - !exist.URL.Equals(module.URL, StringComparison.Ordinal) || - exist.Status != module.Status; - - if (hasChanged) - break; } - } - if (hasChanged) - { - Submodules = submodules; - VisibleSubmodules = BuildVisibleSubmodules(); - } + if (hasChanged) + { + Submodules = submodules; + VisibleSubmodules = BuildVisibleSubmodules(); + } + }); }); } @@ -1364,18 +1379,24 @@ public void RefreshWorkingCopyChanges() if (IsBare) return; - var changes = new Commands.QueryLocalChanges(_fullpath, _settings.IncludeUntrackedInLocalChanges).GetResultAsync().Result; - if (_workingCopy == null) - return; + Task.Run(async () => + { + var changes = await new Commands.QueryLocalChanges(_fullpath, _settings.IncludeUntrackedInLocalChanges) + .GetResultAsync() + .ConfigureAwait(false); - changes.Sort((l, r) => Models.NumericSort.Compare(l.Path, r.Path)); - _workingCopy.SetData(changes); + if (_workingCopy == null) + return; - Dispatcher.UIThread.Invoke(() => - { - LocalChangesCount = changes.Count; - OnPropertyChanged(nameof(InProgressContext)); - GetOwnerPage()?.ChangeDirtyState(Models.DirtyState.HasLocalChanges, changes.Count == 0); + changes.Sort((l, r) => Models.NumericSort.Compare(l.Path, r.Path)); + _workingCopy.SetData(changes); + + Dispatcher.UIThread.Invoke(() => + { + LocalChangesCount = changes.Count; + OnPropertyChanged(nameof(InProgressContext)); + GetOwnerPage()?.ChangeDirtyState(Models.DirtyState.HasLocalChanges, changes.Count == 0); + }); }); } @@ -1384,13 +1405,16 @@ public void RefreshStashes() if (IsBare) return; - var stashes = new Commands.QueryStashes(_fullpath).GetResultAsync().Result; - Dispatcher.UIThread.Invoke(() => + Task.Run(async () => { - if (_stashesPage != null) - _stashesPage.Stashes = stashes; + var stashes = await new Commands.QueryStashes(_fullpath).GetResultAsync().ConfigureAwait(false); + Dispatcher.UIThread.Invoke(() => + { + if (_stashesPage != null) + _stashesPage.Stashes = stashes; - StashesCount = stashes.Count; + StashesCount = stashes.Count; + }); }); } @@ -1806,8 +1830,7 @@ private void RefreshHistoriesFilters(bool refresh) UpdateBranchTreeFilterMode(LocalBranchTrees, filters); UpdateBranchTreeFilterMode(RemoteBranchTrees, filters); UpdateTagFilterMode(filters); - - Task.Run(RefreshCommits); + RefreshCommits(); } private void UpdateBranchTreeFilterMode(List nodes, Dictionary filters) diff --git a/src/ViewModels/RepositoryConfigure.cs b/src/ViewModels/RepositoryConfigure.cs index 738167ecb..83f22255b 100644 --- a/src/ViewModels/RepositoryConfigure.cs +++ b/src/ViewModels/RepositoryConfigure.cs @@ -161,7 +161,7 @@ public RepositoryConfigure(Repository repo) if (!AvailableOpenAIServices.Contains(PreferredOpenAIService)) PreferredOpenAIService = "---"; - _cached = new Commands.Config(repo.FullPath).ReadAllAsync().Result; + _cached = new Commands.Config(repo.FullPath).ReadAll(); if (_cached.TryGetValue("user.name", out var name)) UserName = name; if (_cached.TryGetValue("user.email", out var email)) diff --git a/src/ViewModels/RevisionCompare.cs b/src/ViewModels/RevisionCompare.cs index 1634a454e..4f79ae42a 100644 --- a/src/ViewModels/RevisionCompare.cs +++ b/src/ViewModels/RevisionCompare.cs @@ -81,8 +81,7 @@ public RevisionCompare(string repo, Models.Commit startPoint, Models.Commit endP _startPoint = (object)startPoint ?? new Models.Null(); _endPoint = (object)endPoint ?? new Models.Null(); CanSaveAsPatch = startPoint != null && endPoint != null; - - Task.Run(Refresh); + Refresh(); } public void Dispose() @@ -125,7 +124,7 @@ public void Swap() VisibleChanges = []; SelectedChanges = []; IsLoading = true; - Task.Run(Refresh); + Refresh(); } public void SaveAsPatch(string saveTo) @@ -167,28 +166,33 @@ private void RefreshVisible() private void Refresh() { - _changes = new Commands.CompareRevisions(_repo, GetSHA(_startPoint), GetSHA(_endPoint)).ReadAsync().Result; - - var visible = _changes; - if (!string.IsNullOrWhiteSpace(_searchFilter)) + Task.Run(async () => { - visible = []; - foreach (var c in _changes) + _changes = await new Commands.CompareRevisions(_repo, GetSHA(_startPoint), GetSHA(_endPoint)) + .ReadAsync() + .ConfigureAwait(false); + + var visible = _changes; + if (!string.IsNullOrWhiteSpace(_searchFilter)) { - if (c.Path.Contains(_searchFilter, StringComparison.OrdinalIgnoreCase)) - visible.Add(c); + visible = []; + foreach (var c in _changes) + { + if (c.Path.Contains(_searchFilter, StringComparison.OrdinalIgnoreCase)) + visible.Add(c); + } } - } - Dispatcher.UIThread.Post(() => - { - VisibleChanges = visible; - IsLoading = false; + Dispatcher.UIThread.Post(() => + { + VisibleChanges = visible; + IsLoading = false; - if (VisibleChanges.Count > 0) - SelectedChanges = [VisibleChanges[0]]; - else - SelectedChanges = []; + if (VisibleChanges.Count > 0) + SelectedChanges = [VisibleChanges[0]]; + else + SelectedChanges = []; + }); }); } diff --git a/src/ViewModels/WorkingCopy.cs b/src/ViewModels/WorkingCopy.cs index f784c8111..945b24e53 100644 --- a/src/ViewModels/WorkingCopy.cs +++ b/src/ViewModels/WorkingCopy.cs @@ -88,9 +88,7 @@ public bool UseAmend return; } - CommitMessage = new Commands.QueryCommitFullMessage(_repo.FullPath, currentBranch.Head) - .GetResultAsync() - .Result; + CommitMessage = new Commands.QueryCommitFullMessage(_repo.FullPath, currentBranch.Head).GetResult(); } else { @@ -670,13 +668,8 @@ public void CommitWithPush() { if (_useAmend) { - var head = new Commands.QuerySingleCommit(_repo.FullPath, "HEAD") - .GetResultAsync() - .Result; - - return new Commands.QueryStagedChangesWithAmend(_repo.FullPath, head.Parents.Count == 0 ? Models.Commit.EmptyTreeSHA1 : $"{head.SHA}^") - .GetResultAsync() - .Result; + var head = new Commands.QuerySingleCommit(_repo.FullPath, "HEAD").GetResult(); + return new Commands.QueryStagedChangesWithAmend(_repo.FullPath, head.Parents.Count == 0 ? Models.Commit.EmptyTreeSHA1 : $"{head.SHA}^").GetResult(); } var rs = new List(); @@ -722,7 +715,7 @@ private void UpdateInProgressState() if (File.Exists(rebaseMsgFile)) CommitMessage = File.ReadAllText(rebaseMsgFile); else if (rebasing.StoppedAt != null) - CommitMessage = new Commands.QueryCommitFullMessage(_repo.FullPath, rebasing.StoppedAt.SHA).GetResultAsync().Result; + CommitMessage = new Commands.QueryCommitFullMessage(_repo.FullPath, rebasing.StoppedAt.SHA).GetResult(); } } else if (File.Exists(Path.Combine(_repo.GitDir, "REVERT_HEAD"))) @@ -809,26 +802,14 @@ private void DoCommit(bool autoStage, bool autoPush, CommitCheckPassed checkPass log.Complete(); - Dispatcher.UIThread.Post(async () => + Dispatcher.UIThread.Post(() => { if (succ) { CommitMessage = string.Empty; UseAmend = false; - if (autoPush && _repo.Remotes.Count > 0) - { - if (_repo.CurrentBranch == null) - { - var currentBranchName = await new Commands.QueryCurrentBranch(_repo.FullPath).GetResultAsync(); - var tmp = new Models.Branch() { Name = currentBranchName }; - _repo.ShowAndStartPopup(new Push(_repo, tmp)); - } - else - { - _repo.ShowAndStartPopup(new Push(_repo, null)); - } - } + PushAfterCommit(); } _repo.MarkBranchesDirtyManually(); @@ -838,6 +819,28 @@ private void DoCommit(bool autoStage, bool autoPush, CommitCheckPassed checkPass }); } + private void PushAfterCommit() + { + if (_repo.CurrentBranch == null) + { + Task.Run(async () => + { + var currentBranchName = await new Commands.QueryCurrentBranch(_repo.FullPath).GetResultAsync(); + var tmp = new Models.Branch() { Name = currentBranchName }; + + Dispatcher.UIThread.Post(() => + { + if (_repo.CanCreatePopup()) + _repo.ShowAndStartPopup(new Push(_repo, tmp)); + }); + }); + } + else if (_repo.CanCreatePopup()) + { + _repo.ShowAndStartPopup(new Push(_repo, null)); + } + } + private bool IsChanged(List old, List cur) { if (old.Count != cur.Count) diff --git a/src/Views/Preferences.axaml.cs b/src/Views/Preferences.axaml.cs index ca9800725..08967d1c8 100644 --- a/src/Views/Preferences.axaml.cs +++ b/src/Views/Preferences.axaml.cs @@ -121,7 +121,7 @@ public Preferences() if (pref.IsGitConfigured()) { - var config = new Commands.Config(null).ReadAllAsync().Result; + var config = new Commands.Config(null).ReadAll(); if (config.TryGetValue("user.name", out var name)) DefaultUser = name; diff --git a/src/Views/WorkingCopy.axaml.cs b/src/Views/WorkingCopy.axaml.cs index 0c99e6147..a832af78d 100644 --- a/src/Views/WorkingCopy.axaml.cs +++ b/src/Views/WorkingCopy.axaml.cs @@ -457,7 +457,7 @@ private ContextMenu CreateContextMenuForUnstagedChanges(ViewModels.WorkingCopy v lfs.Header = App.Text("GitLFS"); lfs.Icon = App.CreateMenuIcon("Icons.LFS"); - var isLFSFiltered = new Commands.IsLFSFiltered(repo.FullPath, change.Path).GetResultAsync().Result; + var isLFSFiltered = new Commands.IsLFSFiltered(repo.FullPath, change.Path).GetResult(); if (!isLFSFiltered) { var filename = Path.GetFileName(change.Path); From c1343370775ddf8c9d370a7b29a0ce96cf84106a Mon Sep 17 00:00:00 2001 From: leo Date: Tue, 12 Aug 2025 20:15:14 +0800 Subject: [PATCH 100/591] refactor: rewrite the way to register git command log receiver Signed-off-by: leo --- src/Models/ICommandLog.cs | 5 +++++ src/ViewModels/CommandLog.cs | 25 +++++++++++++------------ src/ViewModels/LauncherPage.cs | 3 +++ src/ViewModels/Popup.cs | 25 ++++++++++++++++--------- src/Views/CommandLogContentPresenter.cs | 23 +++++++++++++---------- 5 files changed, 50 insertions(+), 31 deletions(-) diff --git a/src/Models/ICommandLog.cs b/src/Models/ICommandLog.cs index 34ec70316..28e1fcb39 100644 --- a/src/Models/ICommandLog.cs +++ b/src/Models/ICommandLog.cs @@ -1,5 +1,10 @@ namespace SourceGit.Models { + public interface ICommandLogReceiver + { + void OnReceiveCommandLog(string line); + } + public interface ICommandLog { void AppendLine(string line); diff --git a/src/ViewModels/CommandLog.cs b/src/ViewModels/CommandLog.cs index 96faaef7a..f12d27b45 100644 --- a/src/ViewModels/CommandLog.cs +++ b/src/ViewModels/CommandLog.cs @@ -1,4 +1,5 @@ using System; +using System.Collections.Generic; using System.Text; using Avalonia.Threading; using CommunityToolkit.Mvvm.ComponentModel; @@ -43,10 +44,14 @@ public CommandLog(string name) Name = name; } - public void Register(Action handler) + public void Subscribe(Models.ICommandLogReceiver receiver) { - if (!IsComplete) - _onNewLineReceived += handler; + _receivers.Add(receiver); + } + + public void Unsubscribe(Models.ICommandLogReceiver receiver) + { + _receivers.Remove(receiver); } public void AppendLine(string line = null) @@ -59,7 +64,9 @@ public void AppendLine(string line = null) { var newline = line ?? string.Empty; _builder.AppendLine(newline); - _onNewLineReceived?.Invoke(newline); + + foreach (var receiver in _receivers) + receiver.OnReceiveCommandLog(newline); } } @@ -76,20 +83,14 @@ public void Complete() _content = _builder.ToString(); _builder.Clear(); + _receivers.Clear(); _builder = null; OnPropertyChanged(nameof(IsComplete)); - - if (_onNewLineReceived != null) - { - var dumpHandlers = _onNewLineReceived.GetInvocationList(); - foreach (var d in dumpHandlers) - _onNewLineReceived -= (Action)d; - } } private string _content = string.Empty; private StringBuilder _builder = new StringBuilder(); - private event Action _onNewLineReceived; + private List _receivers = new List(); } } diff --git a/src/ViewModels/LauncherPage.cs b/src/ViewModels/LauncherPage.cs index 0b4b3d8ad..651574809 100644 --- a/src/ViewModels/LauncherPage.cs +++ b/src/ViewModels/LauncherPage.cs @@ -110,7 +110,10 @@ public async void ProcessPopup() { var finished = await dump.Sure(); if (finished) + { + dump.Cleanup(); Popup = null; + } } catch (Exception e) { diff --git a/src/ViewModels/Popup.cs b/src/ViewModels/Popup.cs index 942d33f9f..9d800c50c 100644 --- a/src/ViewModels/Popup.cs +++ b/src/ViewModels/Popup.cs @@ -4,7 +4,7 @@ namespace SourceGit.ViewModels { - public class Popup : ObservableValidator + public class Popup : ObservableValidator, Models.ICommandLogReceiver { public bool InProgress { @@ -27,6 +27,18 @@ public bool Check() return !HasErrors; } + public void OnReceiveCommandLog(string data) + { + var desc = data.Trim(); + if (!string.IsNullOrEmpty(desc)) + ProgressDescription = desc; + } + + public void Cleanup() + { + _log?.Unsubscribe(this); + } + public virtual bool CanStartDirectly() { return true; @@ -39,17 +51,12 @@ public virtual Task Sure() protected void Use(CommandLog log) { - log.Register(SetDescription); - } - - private void SetDescription(string data) - { - var desc = data.Trim(); - if (!string.IsNullOrEmpty(desc)) - ProgressDescription = desc; + _log = log; + _log.Subscribe(this); } private bool _inProgress = false; private string _progressDescription = string.Empty; + private CommandLog _log = null; } } diff --git a/src/Views/CommandLogContentPresenter.cs b/src/Views/CommandLogContentPresenter.cs index 7aa66575f..637a592f0 100644 --- a/src/Views/CommandLogContentPresenter.cs +++ b/src/Views/CommandLogContentPresenter.cs @@ -14,7 +14,7 @@ namespace SourceGit.Views { - public class CommandLogContentPresenter : TextEditor + public class CommandLogContentPresenter : TextEditor, Models.ICommandLogReceiver { public class LineStyleTransformer : DocumentColorizingTransformer { @@ -95,6 +95,12 @@ public string PureText TextArea.TextView.Options.EnableEmailHyperlinks = false; } + public void OnReceiveCommandLog(string line) + { + AppendText("\n"); + AppendText(line); + } + protected override void OnLoaded(RoutedEventArgs e) { base.OnLoaded(e); @@ -126,10 +132,13 @@ protected override void OnPropertyChanged(AvaloniaPropertyChangedEventArgs chang if (change.Property == LogProperty) { - if (change.NewValue is ViewModels.CommandLog log) + if (change.OldValue is ViewModels.CommandLog oldLog) + oldLog.Unsubscribe(this); + + if (change.NewValue is ViewModels.CommandLog newLog) { - Text = log.Content; - log.Register(OnLogLineReceived); + Text = newLog.Content; + newLog.Subscribe(this); } else { @@ -143,12 +152,6 @@ protected override void OnPropertyChanged(AvaloniaPropertyChangedEventArgs chang } } - private void OnLogLineReceived(string newline) - { - AppendText("\n"); - AppendText(newline); - } - private TextMate.Installation _textMate = null; } } From 33de0fe219316a93a4f62e203acaff9ed1b5173e Mon Sep 17 00:00:00 2001 From: leo Date: Wed, 13 Aug 2025 11:06:56 +0800 Subject: [PATCH 101/591] enhance: clean up popup before closing tab Signed-off-by: leo --- src/ViewModels/Launcher.cs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/ViewModels/Launcher.cs b/src/ViewModels/Launcher.cs index 49bb7cef7..8b1352d0e 100644 --- a/src/ViewModels/Launcher.cs +++ b/src/ViewModels/Launcher.cs @@ -265,6 +265,7 @@ public void CloseTab(LauncherPage page) Welcome.Instance.ClearSearchFilter(); last.Node = new RepositoryNode() { Id = Guid.NewGuid().ToString() }; last.Data = Welcome.Instance; + last.Popup?.Cleanup(); last.Popup = null; UpdateTitle(); @@ -459,6 +460,8 @@ private void CloseRepositoryInTab(LauncherPage page, bool removeFromWorkspace = repo.Close(); } + page.Popup?.Cleanup(); + page.Popup = null; page.Data = null; } From 5b6450d0269bcdf84c6c0ee7ae5498a1ea92a376 Mon Sep 17 00:00:00 2001 From: leo Date: Wed, 13 Aug 2025 11:14:41 +0800 Subject: [PATCH 102/591] code_style: move protected methods together Signed-off-by: leo --- src/Commands/Command.cs | 44 ++++++++++++++++++++--------------------- 1 file changed, 22 insertions(+), 22 deletions(-) diff --git a/src/Commands/Command.cs b/src/Commands/Command.cs index d697efcc9..43419b059 100644 --- a/src/Commands/Command.cs +++ b/src/Commands/Command.cs @@ -50,28 +50,6 @@ public void Exec() } } - protected Result ReadToEnd() - { - using var proc = new Process() { StartInfo = CreateGitStartInfo(true) }; - - try - { - proc.Start(); - } - catch (Exception e) - { - return Result.Failed(e.Message); - } - - var rs = new Result() { IsSuccess = true }; - rs.StdOut = proc.StandardOutput.ReadToEnd(); - rs.StdErr = proc.StandardError.ReadToEnd(); - proc.WaitForExit(); - - rs.IsSuccess = proc.ExitCode == 0; - return rs; - } - public async Task ExecAsync() { Log?.AppendLine($"$ git {Args}\n"); @@ -149,6 +127,28 @@ public async Task ExecAsync() return true; } + protected Result ReadToEnd() + { + using var proc = new Process() { StartInfo = CreateGitStartInfo(true) }; + + try + { + proc.Start(); + } + catch (Exception e) + { + return Result.Failed(e.Message); + } + + var rs = new Result() { IsSuccess = true }; + rs.StdOut = proc.StandardOutput.ReadToEnd(); + rs.StdErr = proc.StandardError.ReadToEnd(); + proc.WaitForExit(); + + rs.IsSuccess = proc.ExitCode == 0; + return rs; + } + protected async Task ReadToEndAsync() { using var proc = new Process() { StartInfo = CreateGitStartInfo(true) }; From 1eb313f7d9840724075ae62d1acf01161faa7144 Mon Sep 17 00:00:00 2001 From: leo Date: Wed, 13 Aug 2025 12:06:48 +0800 Subject: [PATCH 103/591] ux: style for `Visibility in Graph` Signed-off-by: leo --- src/Views/FilterModeInGraph.axaml | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/Views/FilterModeInGraph.axaml b/src/Views/FilterModeInGraph.axaml index b9e72b54d..d8d4b165e 100644 --- a/src/Views/FilterModeInGraph.axaml +++ b/src/Views/FilterModeInGraph.axaml @@ -8,7 +8,9 @@ x:DataType="vm:FilterModeInGraph"> + Text="{DynamicResource Text.Repository.FilterCommits}" + FontWeight="Bold" + Foreground="{DynamicResource MenuFlyoutItemKeyboardAcceleratorTextForeground}"/> From 48526a64682098a4a05233b9404453ed6b6b4600 Mon Sep 17 00:00:00 2001 From: leo Date: Wed, 13 Aug 2025 14:47:26 +0800 Subject: [PATCH 104/591] enhance: cleanup popup before cancel Signed-off-by: leo --- src/ViewModels/LauncherPage.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/ViewModels/LauncherPage.cs b/src/ViewModels/LauncherPage.cs index 651574809..a39fd7d30 100644 --- a/src/ViewModels/LauncherPage.cs +++ b/src/ViewModels/LauncherPage.cs @@ -126,10 +126,10 @@ public async void ProcessPopup() public void CancelPopup() { - if (_popup == null) - return; - if (_popup.InProgress) + if (_popup == null || _popup.InProgress) return; + + _popup?.Cleanup(); Popup = null; } From e64921b616686e7b43a673657ac15209d862e7bd Mon Sep 17 00:00:00 2001 From: leo Date: Wed, 13 Aug 2025 15:31:43 +0800 Subject: [PATCH 105/591] enhance: dispatch auto-fetch in `UIThread` Signed-off-by: leo --- src/ViewModels/Repository.cs | 49 ++++++++++++++++-------------------- 1 file changed, 21 insertions(+), 28 deletions(-) diff --git a/src/ViewModels/Repository.cs b/src/ViewModels/Repository.cs index ffa985545..cc4896145 100644 --- a/src/ViewModels/Repository.cs +++ b/src/ViewModels/Repository.cs @@ -560,7 +560,8 @@ public void Open() _selectedViewIndex = 0; _workingCopy.CommitMessage = _settings.LastCommitMessage; - _autoFetchTimer = new Timer(AutoFetchImpl, null, 5000, 5000); + _lastFetchTime = DateTime.Now; + _autoFetchTimer = new Timer(AutoFetchInBackground, null, 5000, 5000); RefreshAll(); } @@ -1194,9 +1195,7 @@ public void RefreshBranches() Dispatcher.UIThread.Invoke(() => { - lock (_lockRemotes) - Remotes = remotes; - + Remotes = remotes; Branches = branches; CurrentBranch = branches.Find(x => x.IsCurrent); LocalBranchTrees = builder.Locals; @@ -1960,13 +1959,19 @@ private void CalcMatchedFilesForSearching() MatchedFilesForSearching = matched; } - private async void AutoFetchImpl(object sender) + private void AutoFetchInBackground(object sender) { - try + Dispatcher.UIThread.Post(async () => { - if (!_settings.EnableAutoFetch || _isAutoFetching) + if (_settings == null || !_settings.EnableAutoFetch) return; + if (!CanCreatePopup()) + { + _lastFetchTime = DateTime.Now; + return; + } + var lockFile = Path.Combine(_gitDir, "index.lock"); if (File.Exists(lockFile)) return; @@ -1976,36 +1981,25 @@ private async void AutoFetchImpl(object sender) if (desire > now) return; - var remotes = new List(); - lock (_lockRemotes) - { - foreach (var remote in _remotes) - remotes.Add(remote.Name); - } - - Dispatcher.UIThread.Invoke(() => IsAutoFetching = true); + IsAutoFetching = true; if (_settings.FetchAllRemotes) { - foreach (var remote in remotes) - await new Commands.Fetch(_fullpath, remote, false, false) { RaiseError = false }.RunAsync(); + foreach (var remote in _remotes) + await new Commands.Fetch(_fullpath, remote.Name, false, false) { RaiseError = false }.RunAsync(); } - else if (remotes.Count > 0) + else if (_remotes.Count > 0) { var remote = string.IsNullOrEmpty(_settings.DefaultRemote) ? - remotes.Find(x => x.Equals(_settings.DefaultRemote, StringComparison.Ordinal)) : - remotes[0]; + _remotes.Find(x => x.Name.Equals(_settings.DefaultRemote, StringComparison.Ordinal)) : + _remotes[0]; - await new Commands.Fetch(_fullpath, remote, false, false) { RaiseError = false }.RunAsync(); + await new Commands.Fetch(_fullpath, remote.Name, false, false) { RaiseError = false }.RunAsync(); } _lastFetchTime = DateTime.Now; - Dispatcher.UIThread.Invoke(() => IsAutoFetching = false); - } - catch - { - // DO nothing, but prevent `System.AggregateException` - } + IsAutoFetching = false; + }); } private string _fullpath = string.Empty; @@ -2037,7 +2031,6 @@ private async void AutoFetchImpl(object sender) private List _matchedFilesForSearching = null; private string _filter = string.Empty; - private readonly Lock _lockRemotes = new(); private List _remotes = new List(); private List _branches = new List(); private Models.Branch _currentBranch = null; From 80ae34cb31f6f459befdf3c6c653416457e3a3f1 Mon Sep 17 00:00:00 2001 From: leo Date: Wed, 13 Aug 2025 15:51:54 +0800 Subject: [PATCH 106/591] fix: patch file line-ending Signed-off-by: leo --- src/Models/DiffResult.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/Models/DiffResult.cs b/src/Models/DiffResult.cs index 95ffa99f1..d80fa136a 100644 --- a/src/Models/DiffResult.cs +++ b/src/Models/DiffResult.cs @@ -148,7 +148,7 @@ public void GenerateNewPatchFromSelection(Change change, string fileBlobGuid, Te var isTracked = !string.IsNullOrEmpty(fileBlobGuid); var fileGuid = isTracked ? fileBlobGuid : "00000000"; - using var writer = new StreamWriter(output); + using var writer = new StreamWriter(output) { NewLine = "\n" }; writer.WriteLine($"diff --git a/{change.Path} b/{change.Path}"); if (!revert && !isTracked) writer.WriteLine("new file mode 100644"); @@ -192,7 +192,7 @@ public void GeneratePatchFromSelection(Change change, string fileTreeGuid, TextD { var orgFile = !string.IsNullOrEmpty(change.OriginalPath) ? change.OriginalPath : change.Path; - using var writer = new StreamWriter(output); + using var writer = new StreamWriter(output) { NewLine = "\n" }; writer.WriteLine($"diff --git a/{change.Path} b/{change.Path}"); writer.WriteLine($"index 00000000...{fileTreeGuid} 100644"); writer.WriteLine($"--- a/{orgFile}"); @@ -305,7 +305,7 @@ public void GeneratePatchFromSelectionSingleSide(Change change, string fileTreeG { var orgFile = !string.IsNullOrEmpty(change.OriginalPath) ? change.OriginalPath : change.Path; - using var writer = new StreamWriter(output); + using var writer = new StreamWriter(output) { NewLine = "\n" }; writer.WriteLine($"diff --git a/{change.Path} b/{change.Path}"); writer.WriteLine($"index 00000000...{fileTreeGuid} 100644"); writer.WriteLine($"--- a/{orgFile}"); From 29269b25284707b4cb45f555398e81864a8f1ebe Mon Sep 17 00:00:00 2001 From: leo Date: Wed, 13 Aug 2025 15:53:15 +0800 Subject: [PATCH 107/591] enhance: use `await Dispatcher.UIThread.InvokeAsync` instead of `Dispatcher.UIThread.Invoke` to make sure all code are running in order Signed-off-by: leo --- src/ViewModels/Repository.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/ViewModels/Repository.cs b/src/ViewModels/Repository.cs index cc4896145..5a24c84c6 100644 --- a/src/ViewModels/Repository.cs +++ b/src/ViewModels/Repository.cs @@ -1267,10 +1267,10 @@ public void RefreshTags() public void RefreshCommits() { - Dispatcher.UIThread.Invoke(() => _histories.IsLoading = true); - Task.Run(async () => { + await Dispatcher.UIThread.InvokeAsync(() => _histories.IsLoading = true); + var builder = new StringBuilder(); builder.Append($"-{Preferences.Instance.MaxHistoryCommits} "); From 2919433a9113307772c16825030b7b099992e8d4 Mon Sep 17 00:00:00 2001 From: leo Date: Wed, 13 Aug 2025 16:13:37 +0800 Subject: [PATCH 108/591] code_style: only `Commands.DiffTool` needs synchronous method `Exec` Signed-off-by: leo --- src/Commands/Command.cs | 15 +-------------- src/Commands/DiffTool.cs | 14 ++++++++++++-- 2 files changed, 13 insertions(+), 16 deletions(-) diff --git a/src/Commands/Command.cs b/src/Commands/Command.cs index 43419b059..0b3c890cb 100644 --- a/src/Commands/Command.cs +++ b/src/Commands/Command.cs @@ -37,19 +37,6 @@ public enum EditorType public bool RaiseError { get; set; } = true; public Models.ICommandLog Log { get; set; } = null; - public void Exec() - { - try - { - var start = CreateGitStartInfo(false); - Process.Start(start); - } - catch (Exception ex) - { - App.RaiseException(Context, ex.Message); - } - } - public async Task ExecAsync() { Log?.AppendLine($"$ git {Args}\n"); @@ -171,7 +158,7 @@ protected async Task ReadToEndAsync() return rs; } - private ProcessStartInfo CreateGitStartInfo(bool redirect) + protected ProcessStartInfo CreateGitStartInfo(bool redirect) { var start = new ProcessStartInfo(); start.FileName = Native.OS.GitExecutable; diff --git a/src/Commands/DiffTool.cs b/src/Commands/DiffTool.cs index 6fbb2c24b..a7dec1b41 100644 --- a/src/Commands/DiffTool.cs +++ b/src/Commands/DiffTool.cs @@ -1,4 +1,7 @@ -namespace SourceGit.Commands +using System; +using System.Diagnostics; + +namespace SourceGit.Commands { public class DiffTool : Command { @@ -28,7 +31,14 @@ public void Open() Args = $"-c difftool.sourcegit.cmd={cmd.Quoted()} difftool --tool=sourcegit --no-prompt {_option}"; } - Exec(); + try + { + Process.Start(CreateGitStartInfo(false)); + } + catch (Exception ex) + { + App.RaiseException(Context, ex.Message); + } } private Models.DiffOption _option; From ffa1a36e69ede5833b6503ea30a32f9debe4af17 Mon Sep 17 00:00:00 2001 From: leo Date: Wed, 13 Aug 2025 16:48:50 +0800 Subject: [PATCH 109/591] enhance: copy remote names before running auto-fetch Signed-off-by: leo --- src/ViewModels/Repository.cs | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/src/ViewModels/Repository.cs b/src/ViewModels/Repository.cs index 5a24c84c6..947d2b901 100644 --- a/src/ViewModels/Repository.cs +++ b/src/ViewModels/Repository.cs @@ -1983,18 +1983,22 @@ private void AutoFetchInBackground(object sender) IsAutoFetching = true; + var remotes = new List(); + foreach (var r in _remotes) + remotes.Add(r.Name); + if (_settings.FetchAllRemotes) { - foreach (var remote in _remotes) - await new Commands.Fetch(_fullpath, remote.Name, false, false) { RaiseError = false }.RunAsync(); + foreach (var remote in remotes) + await new Commands.Fetch(_fullpath, remote, false, false) { RaiseError = false }.RunAsync(); } - else if (_remotes.Count > 0) + else if (remotes.Count > 0) { var remote = string.IsNullOrEmpty(_settings.DefaultRemote) ? - _remotes.Find(x => x.Name.Equals(_settings.DefaultRemote, StringComparison.Ordinal)) : - _remotes[0]; + remotes.Find(x => x.Equals(_settings.DefaultRemote, StringComparison.Ordinal)) : + remotes[0]; - await new Commands.Fetch(_fullpath, remote.Name, false, false) { RaiseError = false }.RunAsync(); + await new Commands.Fetch(_fullpath, remote, false, false) { RaiseError = false }.RunAsync(); } _lastFetchTime = DateTime.Now; From e87eb8137bc043e0ca8568ce003fe22c4416dcb1 Mon Sep 17 00:00:00 2001 From: leo Date: Wed, 13 Aug 2025 16:53:54 +0800 Subject: [PATCH 110/591] fix: try to avoid asking ssh passphrase every time while communicating with remote (#1577) Signed-off-by: leo --- src/Commands/Command.cs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/Commands/Command.cs b/src/Commands/Command.cs index 0b3c890cb..44b651b20 100644 --- a/src/Commands/Command.cs +++ b/src/Commands/Command.cs @@ -176,8 +176,10 @@ protected ProcessStartInfo CreateGitStartInfo(bool redirect) // Force using this app as SSH askpass program var selfExecFile = Process.GetCurrentProcess().MainModule!.FileName; start.Environment.Add("SSH_ASKPASS", selfExecFile); // Can not use parameter here, because it invoked by SSH with `exec` - start.Environment.Add("SSH_ASKPASS_REQUIRE", "force"); + start.Environment.Add("SSH_ASKPASS_REQUIRE", "prefer"); start.Environment.Add("SOURCEGIT_LAUNCH_AS_ASKPASS", "TRUE"); + if (!OperatingSystem.IsLinux()) + start.Environment.Add("DISPLAY", "required"); // If an SSH private key was provided, sets the environment. if (!start.Environment.ContainsKey("GIT_SSH_COMMAND") && !string.IsNullOrEmpty(SSHKey)) From 9ce29bb9bc192e15974e96c19248984098ab6edf Mon Sep 17 00:00:00 2001 From: leo Date: Wed, 13 Aug 2025 21:13:50 +0800 Subject: [PATCH 111/591] feature: supports to configure `remote..pruneTags` while adding or editing remote (#1692) Signed-off-by: leo --- src/Resources/Locales/en_US.axaml | 2 ++ src/Resources/Locales/zh_CN.axaml | 2 ++ src/Resources/Locales/zh_TW.axaml | 2 ++ src/ViewModels/AddRemote.cs | 11 +++++++++++ src/ViewModels/EditRemote.cs | 26 ++++++++++++++------------ src/Views/AddRemote.axaml | 7 ++++++- src/Views/EditRemote.axaml | 7 ++++++- 7 files changed, 43 insertions(+), 14 deletions(-) diff --git a/src/Resources/Locales/en_US.axaml b/src/Resources/Locales/en_US.axaml index c960f4920..39561a730 100644 --- a/src/Resources/Locales/en_US.axaml +++ b/src/Resources/Locales/en_US.axaml @@ -612,6 +612,8 @@ Edit Remote Name: Remote name + Prune tags do not exists in this remote + Only works while fetching with `--prune` enabled Repository URL: Remote git repository URL Copy URL diff --git a/src/Resources/Locales/zh_CN.axaml b/src/Resources/Locales/zh_CN.axaml index ac47d8ad2..f51cc10bc 100644 --- a/src/Resources/Locales/zh_CN.axaml +++ b/src/Resources/Locales/zh_CN.axaml @@ -616,6 +616,8 @@ 编辑远程仓库 远程名 : 唯一远程名 + 自动清理该远程中不存在的标签 + 仅当启用修剪(--prune)后,拉取更新时作用 仓库地址 : 远程仓库的地址 复制远程地址 diff --git a/src/Resources/Locales/zh_TW.axaml b/src/Resources/Locales/zh_TW.axaml index c3e0715a2..e7e2db8fb 100644 --- a/src/Resources/Locales/zh_TW.axaml +++ b/src/Resources/Locales/zh_TW.axaml @@ -616,6 +616,8 @@ 編輯遠端存放庫 遠端名稱: 唯一遠端名稱 + 自動清除此遠端中不存在的標籤 + 僅在啟用自动清理 (--prune) 后,提取遠端變更時有效 存放庫網址: 遠端存放庫的網址 複製遠端網址 diff --git a/src/ViewModels/AddRemote.cs b/src/ViewModels/AddRemote.cs index 5a6c019fc..136a20c0a 100644 --- a/src/ViewModels/AddRemote.cs +++ b/src/ViewModels/AddRemote.cs @@ -44,6 +44,12 @@ public string SSHKey set => SetProperty(ref _sshkey, value, true); } + public bool PruneTagsOnFetch + { + get; + set; + } = false; + public AddRemote(Repository repo) { _repo = repo; @@ -105,6 +111,11 @@ public override async Task Sure() .Use(log) .SetAsync($"remote.{_name}.sshkey", _useSSH ? SSHKey : null); + if (PruneTagsOnFetch) + await new Commands.Config(_repo.FullPath) + .Use(log) + .SetAsync($"remote.{_name}.pruneTags", "true"); + await new Commands.Fetch(_repo.FullPath, _name, false, false) .Use(log) .RunAsync(); diff --git a/src/ViewModels/EditRemote.cs b/src/ViewModels/EditRemote.cs index d5fdf52ec..0f6127080 100644 --- a/src/ViewModels/EditRemote.cs +++ b/src/ViewModels/EditRemote.cs @@ -1,7 +1,7 @@ -using System.ComponentModel.DataAnnotations; +using System; +using System.ComponentModel.DataAnnotations; using System.IO; using System.Threading.Tasks; -using Avalonia.Threading; namespace SourceGit.ViewModels { @@ -45,6 +45,12 @@ public string SSHKey set => SetProperty(ref _sshkey, value, true); } + public bool PruneTagsOnFetch + { + get; + set; + } + public EditRemote(Repository repo, Models.Remote remote) { _repo = repo; @@ -53,17 +59,11 @@ public EditRemote(Repository repo, Models.Remote remote) _url = remote.URL; _useSSH = Models.Remote.IsSSH(remote.URL); + var config = new Commands.Config(repo.FullPath); if (_useSSH) - { - Task.Run(async () => - { - var sshKey = await new Commands.Config(repo.FullPath) - .GetAsync($"remote.{remote.Name}.sshkey") - .ConfigureAwait(false); + _sshkey = config.Get($"remote.{remote.Name}.sshkey"); - Dispatcher.UIThread.Post(() => SSHKey = sshKey); - }); - } + PruneTagsOnFetch = config.Get($"remote.{remote.Name}.pruneTags").Equals("true", StringComparison.OrdinalIgnoreCase); } public static ValidationResult ValidateRemoteName(string name, ValidationContext ctx) @@ -131,7 +131,9 @@ public override async Task Sure() if (pushURL != _url) await new Commands.Remote(_repo.FullPath).SetURLAsync(_name, _url, true); - await new Commands.Config(_repo.FullPath).SetAsync($"remote.{_name}.sshkey", _useSSH ? SSHKey : null); + var config = new Commands.Config(_repo.FullPath); + await config.SetAsync($"remote.{_name}.sshkey", _useSSH ? SSHKey : null); + await config.SetAsync($"remote.{_name}.pruneTags", PruneTagsOnFetch ? "true" : null); _repo.SetWatcherEnabled(true); return true; diff --git a/src/Views/AddRemote.axaml b/src/Views/AddRemote.axaml index f42fbf1ff..cacc66e60 100644 --- a/src/Views/AddRemote.axaml +++ b/src/Views/AddRemote.axaml @@ -12,7 +12,7 @@ Classes="bold" Text="{DynamicResource Text.Remote.AddTitle}"/> - + + + diff --git a/src/Views/EditRemote.axaml b/src/Views/EditRemote.axaml index 7d64a53a6..91d5bef5c 100644 --- a/src/Views/EditRemote.axaml +++ b/src/Views/EditRemote.axaml @@ -12,7 +12,7 @@ Classes="bold" Text="{DynamicResource Text.Remote.EditTitle}"/> - + + + From 21005cbbc113ca787608378f0c780872977f5e26 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Wed, 13 Aug 2025 13:14:37 +0000 Subject: [PATCH 112/591] doc: Update translation status and sort locale files --- TRANSLATION.md | 36 +++++++++++++++++++++++++++--------- 1 file changed, 27 insertions(+), 9 deletions(-) diff --git a/TRANSLATION.md b/TRANSLATION.md index e13f36e64..803153110 100644 --- a/TRANSLATION.md +++ b/TRANSLATION.md @@ -6,7 +6,7 @@ This document shows the translation status of each locale file in the repository ### ![en_US](https://img.shields.io/badge/en__US-%E2%88%9A-brightgreen) -### ![de__DE](https://img.shields.io/badge/de__DE-98.49%25-yellow) +### ![de__DE](https://img.shields.io/badge/de__DE-98.26%25-yellow)
Missing keys in de_DE.axaml @@ -15,6 +15,8 @@ This document shows the translation status of each locale file in the repository - Text.DeleteMultiTags - Text.DeleteMultiTags.DeleteFromRemotes - Text.DeleteMultiTags.Tip +- Text.Remote.PruneTagsOnFetch +- Text.Remote.PruneTagsOnFetch.Tip - Text.Repository.Dashboard - Text.Repository.MoreOptions - Text.Tag.Tagger @@ -27,7 +29,7 @@ This document shows the translation status of each locale file in the repository
-### ![es__ES](https://img.shields.io/badge/es__ES-99.42%25-yellow) +### ![es__ES](https://img.shields.io/badge/es__ES-99.19%25-yellow)
Missing keys in es_ES.axaml @@ -36,11 +38,13 @@ This document shows the translation status of each locale file in the repository - Text.DeleteMultiTags - Text.DeleteMultiTags.DeleteFromRemotes - Text.DeleteMultiTags.Tip +- Text.Remote.PruneTagsOnFetch +- Text.Remote.PruneTagsOnFetch.Tip - Text.TagCM.DeleteMultiple
-### ![fr__FR](https://img.shields.io/badge/fr__FR-80.74%25-yellow) +### ![fr__FR](https://img.shields.io/badge/fr__FR-80.56%25-yellow)
Missing keys in fr_FR.axaml @@ -147,6 +151,8 @@ This document shows the translation status of each locale file in the repository - Text.Push.New - Text.Push.Revision - Text.Push.Revision.Title +- Text.Remote.PruneTagsOnFetch +- Text.Remote.PruneTagsOnFetch.Tip - Text.Repository.BranchSort - Text.Repository.BranchSort.ByCommitterDate - Text.Repository.BranchSort.ByName @@ -214,7 +220,7 @@ This document shows the translation status of each locale file in the repository
-### ![it__IT](https://img.shields.io/badge/it__IT-85.96%25-yellow) +### ![it__IT](https://img.shields.io/badge/it__IT-85.76%25-yellow)
Missing keys in it_IT.axaml @@ -300,6 +306,8 @@ This document shows the translation status of each locale file in the repository - Text.Push.New - Text.Push.Revision - Text.Push.Revision.Title +- Text.Remote.PruneTagsOnFetch +- Text.Remote.PruneTagsOnFetch.Tip - Text.Repository.ClearStashes - Text.Repository.Dashboard - Text.Repository.MoreOptions @@ -343,7 +351,7 @@ This document shows the translation status of each locale file in the repository
-### ![ja__JP](https://img.shields.io/badge/ja__JP-80.74%25-yellow) +### ![ja__JP](https://img.shields.io/badge/ja__JP-80.56%25-yellow)
Missing keys in ja_JP.axaml @@ -451,6 +459,8 @@ This document shows the translation status of each locale file in the repository - Text.Push.New - Text.Push.Revision - Text.Push.Revision.Title +- Text.Remote.PruneTagsOnFetch +- Text.Remote.PruneTagsOnFetch.Tip - Text.Repository.BranchSort - Text.Repository.BranchSort.ByCommitterDate - Text.Repository.BranchSort.ByName @@ -517,7 +527,7 @@ This document shows the translation status of each locale file in the repository
-### ![pt__BR](https://img.shields.io/badge/pt__BR-73.90%25-red) +### ![pt__BR](https://img.shields.io/badge/pt__BR-73.73%25-red)
Missing keys in pt_BR.axaml @@ -667,6 +677,8 @@ This document shows the translation status of each locale file in the repository - Text.Push.New - Text.Push.Revision - Text.Push.Revision.Title +- Text.Remote.PruneTagsOnFetch +- Text.Remote.PruneTagsOnFetch.Tip - Text.Repository.BranchSort - Text.Repository.BranchSort.ByCommitterDate - Text.Repository.BranchSort.ByName @@ -750,7 +762,7 @@ This document shows the translation status of each locale file in the repository
-### ![ru__RU](https://img.shields.io/badge/ru__RU-99.42%25-yellow) +### ![ru__RU](https://img.shields.io/badge/ru__RU-99.19%25-yellow)
Missing keys in ru_RU.axaml @@ -759,11 +771,13 @@ This document shows the translation status of each locale file in the repository - Text.DeleteMultiTags - Text.DeleteMultiTags.DeleteFromRemotes - Text.DeleteMultiTags.Tip +- Text.Remote.PruneTagsOnFetch +- Text.Remote.PruneTagsOnFetch.Tip - Text.TagCM.DeleteMultiple
-### ![ta__IN](https://img.shields.io/badge/ta__IN-80.86%25-yellow) +### ![ta__IN](https://img.shields.io/badge/ta__IN-80.67%25-yellow)
Missing keys in ta_IN.axaml @@ -871,6 +885,8 @@ This document shows the translation status of each locale file in the repository - Text.Push.New - Text.Push.Revision - Text.Push.Revision.Title +- Text.Remote.PruneTagsOnFetch +- Text.Remote.PruneTagsOnFetch.Tip - Text.Repository.BranchSort - Text.Repository.BranchSort.ByCommitterDate - Text.Repository.BranchSort.ByName @@ -936,7 +952,7 @@ This document shows the translation status of each locale file in the repository
-### ![uk__UA](https://img.shields.io/badge/uk__UA-82.02%25-yellow) +### ![uk__UA](https://img.shields.io/badge/uk__UA-81.83%25-yellow)
Missing keys in uk_UA.axaml @@ -1039,6 +1055,8 @@ This document shows the translation status of each locale file in the repository - Text.Push.New - Text.Push.Revision - Text.Push.Revision.Title +- Text.Remote.PruneTagsOnFetch +- Text.Remote.PruneTagsOnFetch.Tip - Text.Repository.BranchSort - Text.Repository.BranchSort.ByCommitterDate - Text.Repository.BranchSort.ByName From b03784cc2661c6c11e957497c544e819fb87eae7 Mon Sep 17 00:00:00 2001 From: leo Date: Wed, 13 Aug 2025 21:21:52 +0800 Subject: [PATCH 113/591] ux: alignment and row height changes Signed-off-by: leo --- src/Views/AddRemote.axaml | 27 ++++++++++++++------------- src/Views/EditRemote.axaml | 27 ++++++++++++++------------- 2 files changed, 28 insertions(+), 26 deletions(-) diff --git a/src/Views/AddRemote.axaml b/src/Views/AddRemote.axaml index cacc66e60..a3bda1182 100644 --- a/src/Views/AddRemote.axaml +++ b/src/Views/AddRemote.axaml @@ -41,19 +41,20 @@ Margin="0,0,8,0" Text="{DynamicResource Text.SSHKey}" IsVisible="{Binding UseSSH}"/> - - - - - + + + + + + + - - - - - + + + + + + + Date: Thu, 14 Aug 2025 10:08:42 +0800 Subject: [PATCH 114/591] localization: update English translation for `Text.Remote.PruneTagsOnFetch` Signed-off-by: leo --- src/Resources/Locales/en_US.axaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Resources/Locales/en_US.axaml b/src/Resources/Locales/en_US.axaml index 39561a730..8e3136f94 100644 --- a/src/Resources/Locales/en_US.axaml +++ b/src/Resources/Locales/en_US.axaml @@ -612,7 +612,7 @@ Edit Remote Name: Remote name - Prune tags do not exists in this remote + Prune tags that do not exists in this remote Only works while fetching with `--prune` enabled Repository URL: Remote git repository URL From cf5b708e16552cad2d0e91f2d4d131b7499aa988 Mon Sep 17 00:00:00 2001 From: leo Date: Thu, 14 Aug 2025 10:21:35 +0800 Subject: [PATCH 115/591] revert: remove `remote..pruneTags` configuration support (#1692) Signed-off-by: leo --- src/Resources/Locales/en_US.axaml | 2 -- src/ViewModels/AddRemote.cs | 11 ----------- src/ViewModels/EditRemote.cs | 18 +++--------------- src/Views/AddRemote.axaml | 7 +------ src/Views/EditRemote.axaml | 7 +------ 5 files changed, 5 insertions(+), 40 deletions(-) diff --git a/src/Resources/Locales/en_US.axaml b/src/Resources/Locales/en_US.axaml index 8e3136f94..c960f4920 100644 --- a/src/Resources/Locales/en_US.axaml +++ b/src/Resources/Locales/en_US.axaml @@ -612,8 +612,6 @@ Edit Remote Name: Remote name - Prune tags that do not exists in this remote - Only works while fetching with `--prune` enabled Repository URL: Remote git repository URL Copy URL diff --git a/src/ViewModels/AddRemote.cs b/src/ViewModels/AddRemote.cs index 136a20c0a..5a6c019fc 100644 --- a/src/ViewModels/AddRemote.cs +++ b/src/ViewModels/AddRemote.cs @@ -44,12 +44,6 @@ public string SSHKey set => SetProperty(ref _sshkey, value, true); } - public bool PruneTagsOnFetch - { - get; - set; - } = false; - public AddRemote(Repository repo) { _repo = repo; @@ -111,11 +105,6 @@ public override async Task Sure() .Use(log) .SetAsync($"remote.{_name}.sshkey", _useSSH ? SSHKey : null); - if (PruneTagsOnFetch) - await new Commands.Config(_repo.FullPath) - .Use(log) - .SetAsync($"remote.{_name}.pruneTags", "true"); - await new Commands.Fetch(_repo.FullPath, _name, false, false) .Use(log) .RunAsync(); diff --git a/src/ViewModels/EditRemote.cs b/src/ViewModels/EditRemote.cs index 0f6127080..5d618478e 100644 --- a/src/ViewModels/EditRemote.cs +++ b/src/ViewModels/EditRemote.cs @@ -1,5 +1,4 @@ -using System; -using System.ComponentModel.DataAnnotations; +using System.ComponentModel.DataAnnotations; using System.IO; using System.Threading.Tasks; @@ -45,12 +44,6 @@ public string SSHKey set => SetProperty(ref _sshkey, value, true); } - public bool PruneTagsOnFetch - { - get; - set; - } - public EditRemote(Repository repo, Models.Remote remote) { _repo = repo; @@ -59,11 +52,8 @@ public EditRemote(Repository repo, Models.Remote remote) _url = remote.URL; _useSSH = Models.Remote.IsSSH(remote.URL); - var config = new Commands.Config(repo.FullPath); if (_useSSH) - _sshkey = config.Get($"remote.{remote.Name}.sshkey"); - - PruneTagsOnFetch = config.Get($"remote.{remote.Name}.pruneTags").Equals("true", StringComparison.OrdinalIgnoreCase); + _sshkey = new Commands.Config(repo.FullPath).Get($"remote.{remote.Name}.sshkey"); } public static ValidationResult ValidateRemoteName(string name, ValidationContext ctx) @@ -131,9 +121,7 @@ public override async Task Sure() if (pushURL != _url) await new Commands.Remote(_repo.FullPath).SetURLAsync(_name, _url, true); - var config = new Commands.Config(_repo.FullPath); - await config.SetAsync($"remote.{_name}.sshkey", _useSSH ? SSHKey : null); - await config.SetAsync($"remote.{_name}.pruneTags", PruneTagsOnFetch ? "true" : null); + await new Commands.Config(_repo.FullPath).SetAsync($"remote.{_name}.sshkey", _useSSH ? SSHKey : null); _repo.SetWatcherEnabled(true); return true; diff --git a/src/Views/AddRemote.axaml b/src/Views/AddRemote.axaml index a3bda1182..f4fe8f336 100644 --- a/src/Views/AddRemote.axaml +++ b/src/Views/AddRemote.axaml @@ -12,7 +12,7 @@ Classes="bold" Text="{DynamicResource Text.Remote.AddTitle}"/> - + - - diff --git a/src/Views/EditRemote.axaml b/src/Views/EditRemote.axaml index 85555f475..10e840198 100644 --- a/src/Views/EditRemote.axaml +++ b/src/Views/EditRemote.axaml @@ -12,7 +12,7 @@ Classes="bold" Text="{DynamicResource Text.Remote.EditTitle}"/> - + - - From bb082a30a4c78e78037cce38a56b2f61dcbd1770 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Thu, 14 Aug 2025 02:22:17 +0000 Subject: [PATCH 116/591] doc: Update translation status and sort locale files --- TRANSLATION.md | 36 ++++++++----------------------- src/Resources/Locales/zh_CN.axaml | 2 -- src/Resources/Locales/zh_TW.axaml | 2 -- 3 files changed, 9 insertions(+), 31 deletions(-) diff --git a/TRANSLATION.md b/TRANSLATION.md index 803153110..e13f36e64 100644 --- a/TRANSLATION.md +++ b/TRANSLATION.md @@ -6,7 +6,7 @@ This document shows the translation status of each locale file in the repository ### ![en_US](https://img.shields.io/badge/en__US-%E2%88%9A-brightgreen) -### ![de__DE](https://img.shields.io/badge/de__DE-98.26%25-yellow) +### ![de__DE](https://img.shields.io/badge/de__DE-98.49%25-yellow)
Missing keys in de_DE.axaml @@ -15,8 +15,6 @@ This document shows the translation status of each locale file in the repository - Text.DeleteMultiTags - Text.DeleteMultiTags.DeleteFromRemotes - Text.DeleteMultiTags.Tip -- Text.Remote.PruneTagsOnFetch -- Text.Remote.PruneTagsOnFetch.Tip - Text.Repository.Dashboard - Text.Repository.MoreOptions - Text.Tag.Tagger @@ -29,7 +27,7 @@ This document shows the translation status of each locale file in the repository
-### ![es__ES](https://img.shields.io/badge/es__ES-99.19%25-yellow) +### ![es__ES](https://img.shields.io/badge/es__ES-99.42%25-yellow)
Missing keys in es_ES.axaml @@ -38,13 +36,11 @@ This document shows the translation status of each locale file in the repository - Text.DeleteMultiTags - Text.DeleteMultiTags.DeleteFromRemotes - Text.DeleteMultiTags.Tip -- Text.Remote.PruneTagsOnFetch -- Text.Remote.PruneTagsOnFetch.Tip - Text.TagCM.DeleteMultiple
-### ![fr__FR](https://img.shields.io/badge/fr__FR-80.56%25-yellow) +### ![fr__FR](https://img.shields.io/badge/fr__FR-80.74%25-yellow)
Missing keys in fr_FR.axaml @@ -151,8 +147,6 @@ This document shows the translation status of each locale file in the repository - Text.Push.New - Text.Push.Revision - Text.Push.Revision.Title -- Text.Remote.PruneTagsOnFetch -- Text.Remote.PruneTagsOnFetch.Tip - Text.Repository.BranchSort - Text.Repository.BranchSort.ByCommitterDate - Text.Repository.BranchSort.ByName @@ -220,7 +214,7 @@ This document shows the translation status of each locale file in the repository
-### ![it__IT](https://img.shields.io/badge/it__IT-85.76%25-yellow) +### ![it__IT](https://img.shields.io/badge/it__IT-85.96%25-yellow)
Missing keys in it_IT.axaml @@ -306,8 +300,6 @@ This document shows the translation status of each locale file in the repository - Text.Push.New - Text.Push.Revision - Text.Push.Revision.Title -- Text.Remote.PruneTagsOnFetch -- Text.Remote.PruneTagsOnFetch.Tip - Text.Repository.ClearStashes - Text.Repository.Dashboard - Text.Repository.MoreOptions @@ -351,7 +343,7 @@ This document shows the translation status of each locale file in the repository
-### ![ja__JP](https://img.shields.io/badge/ja__JP-80.56%25-yellow) +### ![ja__JP](https://img.shields.io/badge/ja__JP-80.74%25-yellow)
Missing keys in ja_JP.axaml @@ -459,8 +451,6 @@ This document shows the translation status of each locale file in the repository - Text.Push.New - Text.Push.Revision - Text.Push.Revision.Title -- Text.Remote.PruneTagsOnFetch -- Text.Remote.PruneTagsOnFetch.Tip - Text.Repository.BranchSort - Text.Repository.BranchSort.ByCommitterDate - Text.Repository.BranchSort.ByName @@ -527,7 +517,7 @@ This document shows the translation status of each locale file in the repository
-### ![pt__BR](https://img.shields.io/badge/pt__BR-73.73%25-red) +### ![pt__BR](https://img.shields.io/badge/pt__BR-73.90%25-red)
Missing keys in pt_BR.axaml @@ -677,8 +667,6 @@ This document shows the translation status of each locale file in the repository - Text.Push.New - Text.Push.Revision - Text.Push.Revision.Title -- Text.Remote.PruneTagsOnFetch -- Text.Remote.PruneTagsOnFetch.Tip - Text.Repository.BranchSort - Text.Repository.BranchSort.ByCommitterDate - Text.Repository.BranchSort.ByName @@ -762,7 +750,7 @@ This document shows the translation status of each locale file in the repository
-### ![ru__RU](https://img.shields.io/badge/ru__RU-99.19%25-yellow) +### ![ru__RU](https://img.shields.io/badge/ru__RU-99.42%25-yellow)
Missing keys in ru_RU.axaml @@ -771,13 +759,11 @@ This document shows the translation status of each locale file in the repository - Text.DeleteMultiTags - Text.DeleteMultiTags.DeleteFromRemotes - Text.DeleteMultiTags.Tip -- Text.Remote.PruneTagsOnFetch -- Text.Remote.PruneTagsOnFetch.Tip - Text.TagCM.DeleteMultiple
-### ![ta__IN](https://img.shields.io/badge/ta__IN-80.67%25-yellow) +### ![ta__IN](https://img.shields.io/badge/ta__IN-80.86%25-yellow)
Missing keys in ta_IN.axaml @@ -885,8 +871,6 @@ This document shows the translation status of each locale file in the repository - Text.Push.New - Text.Push.Revision - Text.Push.Revision.Title -- Text.Remote.PruneTagsOnFetch -- Text.Remote.PruneTagsOnFetch.Tip - Text.Repository.BranchSort - Text.Repository.BranchSort.ByCommitterDate - Text.Repository.BranchSort.ByName @@ -952,7 +936,7 @@ This document shows the translation status of each locale file in the repository
-### ![uk__UA](https://img.shields.io/badge/uk__UA-81.83%25-yellow) +### ![uk__UA](https://img.shields.io/badge/uk__UA-82.02%25-yellow)
Missing keys in uk_UA.axaml @@ -1055,8 +1039,6 @@ This document shows the translation status of each locale file in the repository - Text.Push.New - Text.Push.Revision - Text.Push.Revision.Title -- Text.Remote.PruneTagsOnFetch -- Text.Remote.PruneTagsOnFetch.Tip - Text.Repository.BranchSort - Text.Repository.BranchSort.ByCommitterDate - Text.Repository.BranchSort.ByName diff --git a/src/Resources/Locales/zh_CN.axaml b/src/Resources/Locales/zh_CN.axaml index f51cc10bc..ac47d8ad2 100644 --- a/src/Resources/Locales/zh_CN.axaml +++ b/src/Resources/Locales/zh_CN.axaml @@ -616,8 +616,6 @@ 编辑远程仓库 远程名 : 唯一远程名 - 自动清理该远程中不存在的标签 - 仅当启用修剪(--prune)后,拉取更新时作用 仓库地址 : 远程仓库的地址 复制远程地址 diff --git a/src/Resources/Locales/zh_TW.axaml b/src/Resources/Locales/zh_TW.axaml index e7e2db8fb..c3e0715a2 100644 --- a/src/Resources/Locales/zh_TW.axaml +++ b/src/Resources/Locales/zh_TW.axaml @@ -616,8 +616,6 @@ 編輯遠端存放庫 遠端名稱: 唯一遠端名稱 - 自動清除此遠端中不存在的標籤 - 僅在啟用自动清理 (--prune) 后,提取遠端變更時有效 存放庫網址: 遠端存放庫的網址 複製遠端網址 From f206167f99b9fbde967555ace57a641e2d7a0faa Mon Sep 17 00:00:00 2001 From: leo Date: Thu, 14 Aug 2025 10:47:54 +0800 Subject: [PATCH 117/591] code_style: remove unnecessary empty line Signed-off-by: leo --- src/Commands/Fetch.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/src/Commands/Fetch.cs b/src/Commands/Fetch.cs index d25cc80c8..9ec27656a 100644 --- a/src/Commands/Fetch.cs +++ b/src/Commands/Fetch.cs @@ -21,7 +21,6 @@ public Fetch(string repo, string remote, bool noTags, bool force) Args += "--force "; Args += remote; - } public Fetch(string repo, Models.Branch local, Models.Branch remote) From 04055f66c48e1547b6f23ff24ffa37f18f59d9b3 Mon Sep 17 00:00:00 2001 From: leo Date: Thu, 14 Aug 2025 11:18:50 +0800 Subject: [PATCH 118/591] refactor!: move local issue tracker rule settings from `$GIT_DIR/sourcegit.issuetracker` to default repo's git config (#1725) Signed-off-by: leo --- src/Commands/IssueTracker.cs | 27 +++++++++++++++++++-------- src/ViewModels/Repository.cs | 3 +-- 2 files changed, 20 insertions(+), 10 deletions(-) diff --git a/src/Commands/IssueTracker.cs b/src/Commands/IssueTracker.cs index 8791714c6..6f714da6a 100644 --- a/src/Commands/IssueTracker.cs +++ b/src/Commands/IssueTracker.cs @@ -11,15 +11,25 @@ public IssueTracker(string repo, string storage) { WorkingDirectory = repo; Context = repo; - _storage = storage; + + if (string.IsNullOrEmpty(storage)) + { + _isStorageFileExists = true; + _baseArg = "config --local"; + } + else + { + _isStorageFileExists = File.Exists(storage); + _baseArg = $"config -f {storage.Quoted()}"; + } } public async Task ReadAllAsync(List outs, bool isShared) { - if (!File.Exists(_storage)) + if (!_isStorageFileExists) return; - Args = $"config -f {_storage.Quoted()} -l"; + Args = $"{_baseArg} -l"; var rs = await ReadToEndAsync().ConfigureAwait(false); if (rs.IsSuccess) @@ -57,12 +67,12 @@ public async Task ReadAllAsync(List outs, bool isShared) public async Task AddAsync(Models.IssueTracker rule) { - Args = $"config -f {_storage.Quoted()} issuetracker.{rule.Name.Quoted()}.regex {rule.RegexString.Quoted()}"; + Args = $"{_baseArg} issuetracker.{rule.Name.Quoted()}.regex {rule.RegexString.Quoted()}"; var succ = await ExecAsync().ConfigureAwait(false); if (succ) { - Args = $"config -f {_storage.Quoted()} issuetracker.{rule.Name.Quoted()}.url {rule.URLTemplate.Quoted()}"; + Args = $"{_baseArg} issuetracker.{rule.Name.Quoted()}.url {rule.URLTemplate.Quoted()}"; return await ExecAsync().ConfigureAwait(false); } @@ -71,10 +81,10 @@ public async Task AddAsync(Models.IssueTracker rule) public async Task RemoveAsync(Models.IssueTracker rule) { - if (!File.Exists(_storage)) + if (!_isStorageFileExists) return true; - Args = $"config -f {_storage.Quoted()} --remove-section issuetracker.{rule.Name.Quoted()}"; + Args = $"{_baseArg} --remove-section issuetracker.{rule.Name.Quoted()}"; return await ExecAsync().ConfigureAwait(false); } @@ -89,6 +99,7 @@ private Models.IssueTracker FindOrAdd(List rules, string ru return rule; } - private readonly string _storage; + private readonly bool _isStorageFileExists; + private readonly string _baseArg; } } diff --git a/src/ViewModels/Repository.cs b/src/ViewModels/Repository.cs index 947d2b901..93a0c5a45 100644 --- a/src/ViewModels/Repository.cs +++ b/src/ViewModels/Repository.cs @@ -1715,8 +1715,7 @@ private LauncherPage GetOwnerPage() private Commands.IssueTracker CreateIssueTrackerCommand(bool shared) { - var storage = shared ? $"{_fullpath}/.issuetracker" : $"{_gitDir}/sourcegit.issuetracker"; - return new Commands.IssueTracker(_fullpath, storage); + return new Commands.IssueTracker(_fullpath, shared ? $"{_fullpath}/.issuetracker" : null); } private BranchTreeNode.Builder BuildBranchTree(List branches, List remotes) From 3cce6c50a6bc5c814ee8ced8648a13c54276670d Mon Sep 17 00:00:00 2001 From: leo Date: Thu, 14 Aug 2025 12:16:49 +0800 Subject: [PATCH 119/591] feature: share settings between worktrees (#1665) NOTE: we can only share settings between worktrees for reading! It means that the repository settings only saved by the main repository not worktrees Signed-off-by: leo --- src/Commands/QueryGitCommonDir.cs | 26 -------------------- src/ViewModels/Repository.cs | 40 ++++++++++++++++++------------- 2 files changed, 23 insertions(+), 43 deletions(-) delete mode 100644 src/Commands/QueryGitCommonDir.cs diff --git a/src/Commands/QueryGitCommonDir.cs b/src/Commands/QueryGitCommonDir.cs deleted file mode 100644 index 83b54e764..000000000 --- a/src/Commands/QueryGitCommonDir.cs +++ /dev/null @@ -1,26 +0,0 @@ -using System.IO; - -namespace SourceGit.Commands -{ - public class QueryGitCommonDir : Command - { - public QueryGitCommonDir(string workDir) - { - WorkingDirectory = workDir; - Args = "rev-parse --git-common-dir"; - } - - public string GetResult() - { - var rs = ReadToEnd(); - if (!rs.IsSuccess) - return null; - - var stdout = rs.StdOut.Trim(); - if (string.IsNullOrEmpty(stdout)) - return null; - - return Path.IsPathRooted(stdout) ? stdout : Path.GetFullPath(Path.Combine(WorkingDirectory, stdout)); - } - } -} diff --git a/src/ViewModels/Repository.cs b/src/ViewModels/Repository.cs index 93a0c5a45..5b299b3e5 100644 --- a/src/ViewModels/Repository.cs +++ b/src/ViewModels/Repository.cs @@ -508,11 +508,28 @@ public Repository(bool isBare, string path, string gitDir) IsBare = isBare; FullPath = path; GitDir = gitDir; + + var commonDirFile = Path.Combine(_gitDir, "commondir"); + _isWorktree = _gitDir.Replace('\\', '/').IndexOf("/worktrees/", StringComparison.Ordinal) > 0 && + File.Exists(commonDirFile); + + if (_isWorktree) + { + var commonDir = File.ReadAllText(commonDirFile).Trim(); + if (!Path.IsPathRooted(commonDir)) + commonDir = new DirectoryInfo(Path.Combine(_gitDir, commonDir)).FullName; + + _gitCommonDir = commonDir; + } + else + { + _gitCommonDir = _gitDir; + } } public void Open() { - var settingsFile = Path.Combine(_gitDir, "sourcegit.settings"); + var settingsFile = Path.Combine(_gitCommonDir, "sourcegit.settings"); if (File.Exists(settingsFile)) { try @@ -532,16 +549,7 @@ public void Open() try { - // For worktrees, we need to watch the $GIT_COMMON_DIR instead of the $GIT_DIR. - var gitDirForWatcher = _gitDir; - if (_gitDir.Replace('\\', '/').IndexOf("/worktrees/", StringComparison.Ordinal) > 0) - { - var commonDir = new Commands.QueryGitCommonDir(_fullpath).GetResult(); - if (!string.IsNullOrEmpty(commonDir)) - gitDirForWatcher = commonDir; - } - - _watcher = new Models.Watcher(this, _fullpath, gitDirForWatcher); + _watcher = new Models.Watcher(this, _fullpath, _gitCommonDir); } catch (Exception ex) { @@ -570,16 +578,12 @@ public void Close() SelectedView = null; // Do NOT modify. Used to remove exists widgets for GC.Collect Logs.Clear(); - try + if (!_isWorktree) { _settings.LastCommitMessage = _workingCopy.CommitMessage; - using var stream = File.Create(Path.Combine(_gitDir, "sourcegit.settings")); + using var stream = File.Create(Path.Combine(_gitCommonDir, "sourcegit.settings")); JsonSerializer.Serialize(stream, _settings, JsonCodeGen.Default.RepositorySettings); } - catch - { - // Ignore - } _autoFetchTimer.Dispose(); _autoFetchTimer = null; @@ -2007,6 +2011,8 @@ private void AutoFetchInBackground(object sender) private string _fullpath = string.Empty; private string _gitDir = string.Empty; + private string _gitCommonDir = string.Empty; + private bool _isWorktree = false; private Models.RepositorySettings _settings = null; private Models.FilterMode _historiesFilterMode = Models.FilterMode.None; private bool _hasAllowedSignersFile = false; From ce23e8cb20f35c650a1018d8c6aed19c54bbf578 Mon Sep 17 00:00:00 2001 From: leo Date: Thu, 14 Aug 2025 12:53:16 +0800 Subject: [PATCH 120/591] refactor: use async methods to commit changes Signed-off-by: leo --- src/App.axaml.cs | 3 +- src/Models/Commit.cs | 8 + src/ViewModels/Checkout.cs | 2 +- src/ViewModels/CheckoutAndFastForward.cs | 2 +- src/ViewModels/CheckoutCommit.cs | 2 +- src/ViewModels/ConfirmEmptyCommit.cs | 22 +-- src/ViewModels/CreateBranch.cs | 2 +- src/ViewModels/WorkingCopy.cs | 191 +++++++++-------------- src/Views/Confirm.axaml.cs | 8 - src/Views/ConfirmEmptyCommit.axaml.cs | 12 +- src/Views/WorkingCopy.axaml | 8 +- src/Views/WorkingCopy.axaml.cs | 24 +++ 12 files changed, 138 insertions(+), 146 deletions(-) diff --git a/src/App.axaml.cs b/src/App.axaml.cs index 54d4354c2..b23c26266 100644 --- a/src/App.axaml.cs +++ b/src/App.axaml.cs @@ -158,13 +158,12 @@ public static void ShowWindow(object data) } } - public static async Task AskConfirmAsync(string message, Action onSure) + public static async Task AskConfirmAsync(string message) { if (Current?.ApplicationLifetime is IClassicDesktopStyleApplicationLifetime { MainWindow: { } owner }) { var confirm = new Views.Confirm(); confirm.Message.Text = message; - confirm.OnSure = onSure; return await confirm.ShowDialog(owner); } diff --git a/src/Models/Commit.cs b/src/Models/Commit.cs index 61438424a..4fb9f7d6d 100644 --- a/src/Models/Commit.cs +++ b/src/Models/Commit.cs @@ -16,6 +16,14 @@ public enum CommitSearchMethod ByContent, } + public enum CommitCheckPassed + { + None = 0, + DetachedHead, + Filter, + FileCount, + } + public class Commit { // As retrieved by: git mktree Sure() if (refs.Count == 0) { var msg = App.Text("Checkout.WarnLostCommits"); - var shouldContinue = await App.AskConfirmAsync(msg, null); + var shouldContinue = await App.AskConfirmAsync(msg); if (!shouldContinue) { _repo.SetWatcherEnabled(true); diff --git a/src/ViewModels/CheckoutAndFastForward.cs b/src/ViewModels/CheckoutAndFastForward.cs index 205bbbd7b..e3c89c7ba 100644 --- a/src/ViewModels/CheckoutAndFastForward.cs +++ b/src/ViewModels/CheckoutAndFastForward.cs @@ -52,7 +52,7 @@ public override async Task Sure() if (refs.Count == 0) { var msg = App.Text("Checkout.WarnLostCommits"); - var shouldContinue = await App.AskConfirmAsync(msg, null); + var shouldContinue = await App.AskConfirmAsync(msg); if (!shouldContinue) { _repo.SetWatcherEnabled(true); diff --git a/src/ViewModels/CheckoutCommit.cs b/src/ViewModels/CheckoutCommit.cs index a77a72356..5917b0f48 100644 --- a/src/ViewModels/CheckoutCommit.cs +++ b/src/ViewModels/CheckoutCommit.cs @@ -47,7 +47,7 @@ public override async Task Sure() if (refs.Count == 0) { var msg = App.Text("Checkout.WarnLostCommits"); - var shouldContinue = await App.AskConfirmAsync(msg, null); + var shouldContinue = await App.AskConfirmAsync(msg); if (!shouldContinue) { _repo.SetWatcherEnabled(true); diff --git a/src/ViewModels/ConfirmEmptyCommit.cs b/src/ViewModels/ConfirmEmptyCommit.cs index 87178b75e..95c7025c3 100644 --- a/src/ViewModels/ConfirmEmptyCommit.cs +++ b/src/ViewModels/ConfirmEmptyCommit.cs @@ -1,4 +1,4 @@ -using System; +using System.Threading.Tasks; namespace SourceGit.ViewModels { @@ -16,23 +16,25 @@ public string Message private set; } - public ConfirmEmptyCommit(bool hasLocalChanges, Action onSure) + public ConfirmEmptyCommit(WorkingCopy wc, bool autoPush, int unstagedCount) { - HasLocalChanges = hasLocalChanges; - Message = App.Text(hasLocalChanges ? "ConfirmEmptyCommit.WithLocalChanges" : "ConfirmEmptyCommit.NoLocalChanges"); - _onSure = onSure; + _wc = wc; + _autoPush = autoPush; + HasLocalChanges = unstagedCount > 0; + Message = App.Text(HasLocalChanges ? "ConfirmEmptyCommit.WithLocalChanges" : "ConfirmEmptyCommit.NoLocalChanges"); } - public void StageAllThenCommit() + public async Task StageAllThenCommitAsync() { - _onSure?.Invoke(true); + await _wc.CommitAsync(true, _autoPush, Models.CommitCheckPassed.FileCount); } - public void Continue() + public async Task ContinueAsync() { - _onSure?.Invoke(false); + await _wc.CommitAsync(false, _autoPush, Models.CommitCheckPassed.FileCount); } - private Action _onSure; + private readonly WorkingCopy _wc; + private readonly bool _autoPush; } } diff --git a/src/ViewModels/CreateBranch.cs b/src/ViewModels/CreateBranch.cs index 7e2662368..b79b17851 100644 --- a/src/ViewModels/CreateBranch.cs +++ b/src/ViewModels/CreateBranch.cs @@ -131,7 +131,7 @@ public override async Task Sure() if (refs.Count == 0) { var msg = App.Text("Checkout.WarnLostCommits"); - var shouldContinue = await App.AskConfirmAsync(msg, null); + var shouldContinue = await App.AskConfirmAsync(msg); if (!shouldContinue) { _repo.SetWatcherEnabled(true); diff --git a/src/ViewModels/WorkingCopy.cs b/src/ViewModels/WorkingCopy.cs index 945b24e53..4ff82332a 100644 --- a/src/ViewModels/WorkingCopy.cs +++ b/src/ViewModels/WorkingCopy.cs @@ -606,19 +606,86 @@ public void ApplyCommitMessageTemplate(Models.CommitTemplate tmpl) CommitMessage = tmpl.Apply(_repo.CurrentBranch, _staged); } - public void Commit() + public async Task CommitAsync(bool autoStage, bool autoPush, Models.CommitCheckPassed checkPassed = Models.CommitCheckPassed.None) { - DoCommit(false, false); - } + if (string.IsNullOrWhiteSpace(_commitMessage)) + return; - public void CommitWithAutoStage() - { - DoCommit(true, false); - } + if (!_repo.CanCreatePopup()) + { + App.RaiseException(_repo.FullPath, "Repository has an unfinished job! Please wait!"); + return; + } - public void CommitWithPush() - { - DoCommit(false, true); + if (autoStage && HasUnsolvedConflicts) + { + App.RaiseException(_repo.FullPath, "Repository has unsolved conflict(s). Auto-stage and commit is disabled!"); + return; + } + + if (_repo.CurrentBranch is { IsDetachedHead: true } && checkPassed < Models.CommitCheckPassed.DetachedHead) + { + var msg = App.Text("WorkingCopy.ConfirmCommitWithDetachedHead"); + var sure = await App.AskConfirmAsync(msg); + if (sure) + await CommitAsync(autoStage, autoPush, Models.CommitCheckPassed.DetachedHead); + return; + } + + if (!string.IsNullOrEmpty(_filter) && _staged.Count > _visibleStaged.Count && checkPassed < Models.CommitCheckPassed.Filter) + { + var msg = App.Text("WorkingCopy.ConfirmCommitWithFilter", _staged.Count, _visibleStaged.Count, _staged.Count - _visibleStaged.Count); + var sure = await App.AskConfirmAsync(msg); + if (sure) + await CommitAsync(autoStage, autoPush, Models.CommitCheckPassed.Filter); + return; + } + + if (checkPassed < Models.CommitCheckPassed.FileCount && !_useAmend) + { + if ((!autoStage && _staged.Count == 0) || (autoStage && _cached.Count == 0)) + { + await App.ShowDialog(new ConfirmEmptyCommit(this, autoPush, _cached.Count)); + return; + } + } + + IsCommitting = true; + _repo.Settings.PushCommitMessage(_commitMessage); + _repo.SetWatcherEnabled(false); + + var signOff = _repo.Settings.EnableSignOffForCommit; + var log = _repo.CreateLog("Commit"); + var succ = true; + if (autoStage && _unstaged.Count > 0) + succ = await new Commands.Add(_repo.FullPath, _repo.IncludeUntracked).Use(log).ExecAsync().ConfigureAwait(false); + + if (succ) + succ = await new Commands.Commit(_repo.FullPath, _commitMessage, signOff, _useAmend, _resetAuthor).Use(log).RunAsync().ConfigureAwait(false); + + log.Complete(); + + if (succ) + { + CommitMessage = string.Empty; + UseAmend = false; + if (autoPush && _repo.Remotes.Count > 0) + { + Models.Branch pushBranch = null; + if (_repo.CurrentBranch == null) + { + var currentBranchName = await new Commands.QueryCurrentBranch(_repo.FullPath).GetResultAsync(); + pushBranch = new Models.Branch() { Name = currentBranchName }; + } + + if (_repo.CanCreatePopup()) + _repo.ShowAndStartPopup(new Push(_repo, pushBranch)); + } + } + + _repo.MarkBranchesDirtyManually(); + _repo.SetWatcherEnabled(true); + IsCommitting = false; } private List GetVisibleChanges(List changes) @@ -745,102 +812,6 @@ private void SetDetail(Models.Change change, bool isUnstaged) DetailContext = new DiffContext(_repo.FullPath, new Models.DiffOption(change, isUnstaged), _detailContext as DiffContext); } - private void DoCommit(bool autoStage, bool autoPush, CommitCheckPassed checkPassed = CommitCheckPassed.None) - { - if (string.IsNullOrWhiteSpace(_commitMessage)) - return; - - if (!_repo.CanCreatePopup()) - { - App.RaiseException(_repo.FullPath, "Repository has an unfinished job! Please wait!"); - return; - } - - if (autoStage && HasUnsolvedConflicts) - { - App.RaiseException(_repo.FullPath, "Repository has unsolved conflict(s). Auto-stage and commit is disabled!"); - return; - } - - if (_repo.CurrentBranch is { IsDetachedHead: true } && checkPassed < CommitCheckPassed.DetachedHead) - { - var msg = App.Text("WorkingCopy.ConfirmCommitWithDetachedHead"); - _ = App.AskConfirmAsync(msg, () => DoCommit(autoStage, autoPush, CommitCheckPassed.DetachedHead)); - return; - } - - if (!string.IsNullOrEmpty(_filter) && _staged.Count > _visibleStaged.Count && checkPassed < CommitCheckPassed.Filter) - { - var msg = App.Text("WorkingCopy.ConfirmCommitWithFilter", _staged.Count, _visibleStaged.Count, _staged.Count - _visibleStaged.Count); - _ = App.AskConfirmAsync(msg, () => DoCommit(autoStage, autoPush, CommitCheckPassed.Filter)); - return; - } - - if (checkPassed < CommitCheckPassed.FileCount && !_useAmend) - { - if ((!autoStage && _staged.Count == 0) || (autoStage && _cached.Count == 0)) - { - _ = App.ShowDialog(new ConfirmEmptyCommit(_cached.Count > 0, stageAll => DoCommit(stageAll, autoPush, CommitCheckPassed.FileCount))); - return; - } - } - - IsCommitting = true; - _repo.Settings.PushCommitMessage(_commitMessage); - _repo.SetWatcherEnabled(false); - - var signOff = _repo.Settings.EnableSignOffForCommit; - var log = _repo.CreateLog("Commit"); - Task.Run(async () => - { - var succ = true; - if (autoStage && _unstaged.Count > 0) - succ = await new Commands.Add(_repo.FullPath, _repo.IncludeUntracked).Use(log).ExecAsync().ConfigureAwait(false); - - if (succ) - succ = await new Commands.Commit(_repo.FullPath, _commitMessage, signOff, _useAmend, _resetAuthor).Use(log).RunAsync().ConfigureAwait(false); - - log.Complete(); - - Dispatcher.UIThread.Post(() => - { - if (succ) - { - CommitMessage = string.Empty; - UseAmend = false; - if (autoPush && _repo.Remotes.Count > 0) - PushAfterCommit(); - } - - _repo.MarkBranchesDirtyManually(); - _repo.SetWatcherEnabled(true); - IsCommitting = false; - }); - }); - } - - private void PushAfterCommit() - { - if (_repo.CurrentBranch == null) - { - Task.Run(async () => - { - var currentBranchName = await new Commands.QueryCurrentBranch(_repo.FullPath).GetResultAsync(); - var tmp = new Models.Branch() { Name = currentBranchName }; - - Dispatcher.UIThread.Post(() => - { - if (_repo.CanCreatePopup()) - _repo.ShowAndStartPopup(new Push(_repo, tmp)); - }); - }); - } - else if (_repo.CanCreatePopup()) - { - _repo.ShowAndStartPopup(new Push(_repo, null)); - } - } - private bool IsChanged(List old, List cur) { if (old.Count != cur.Count) @@ -857,14 +828,6 @@ private bool IsChanged(List old, List cur) return false; } - private enum CommitCheckPassed - { - None = 0, - DetachedHead, - Filter, - FileCount, - } - private Repository _repo = null; private bool _isLoadingData = false; private bool _isStaging = false; diff --git a/src/Views/Confirm.axaml.cs b/src/Views/Confirm.axaml.cs index f214da23e..3756fa3a6 100644 --- a/src/Views/Confirm.axaml.cs +++ b/src/Views/Confirm.axaml.cs @@ -1,16 +1,9 @@ -using System; using Avalonia.Interactivity; namespace SourceGit.Views { public partial class Confirm : ChromelessWindow { - public Action OnSure - { - get; - set; - } - public Confirm() { InitializeComponent(); @@ -18,7 +11,6 @@ public Confirm() private void Sure(object _1, RoutedEventArgs _2) { - OnSure?.Invoke(); Close(true); } diff --git a/src/Views/ConfirmEmptyCommit.axaml.cs b/src/Views/ConfirmEmptyCommit.axaml.cs index 6668ee2c1..101d77d4d 100644 --- a/src/Views/ConfirmEmptyCommit.axaml.cs +++ b/src/Views/ConfirmEmptyCommit.axaml.cs @@ -9,15 +9,19 @@ public ConfirmEmptyCommit() InitializeComponent(); } - private void StageAllThenCommit(object _1, RoutedEventArgs _2) + private async void StageAllThenCommit(object _1, RoutedEventArgs _2) { - (DataContext as ViewModels.ConfirmEmptyCommit)?.StageAllThenCommit(); + if (DataContext is ViewModels.ConfirmEmptyCommit vm) + await vm.StageAllThenCommitAsync(); + Close(); } - private void Continue(object _1, RoutedEventArgs _2) + private async void Continue(object _1, RoutedEventArgs _2) { - (DataContext as ViewModels.ConfirmEmptyCommit)?.Continue(); + if (DataContext is ViewModels.ConfirmEmptyCommit vm) + await vm.ContinueAsync(); + Close(); } diff --git a/src/Views/WorkingCopy.axaml b/src/Views/WorkingCopy.axaml index 99505c9fb..a57cc3c29 100644 --- a/src/Views/WorkingCopy.axaml +++ b/src/Views/WorkingCopy.axaml @@ -295,7 +295,7 @@ @@ -307,7 +307,7 @@ Height="28" Margin="8,0,0,0" Padding="8,0" - Command="{Binding Commit}" + Click="OnCommit" HotKey="{OnPlatform Ctrl+Enter, macOS=⌘+Enter}" IsVisible="{Binding InProgressContext, Converter={x:Static ObjectConverters.IsNull}}" ToolTip.Placement="Top" @@ -337,7 +337,7 @@ - diff --git a/src/Views/RepositoryToolbar.axaml.cs b/src/Views/RepositoryToolbar.axaml.cs index 027e64584..d25780069 100644 --- a/src/Views/RepositoryToolbar.axaml.cs +++ b/src/Views/RepositoryToolbar.axaml.cs @@ -122,65 +122,65 @@ private async void OpenConfigure(object sender, RoutedEventArgs e) } } - private void Fetch(object sender, TappedEventArgs e) + private async void Fetch(object sender, TappedEventArgs e) { if (DataContext is ViewModels.Repository repo) { - repo.Fetch(e.KeyModifiers is KeyModifiers.Control); + await repo.FetchAsync(e.KeyModifiers is KeyModifiers.Control); e.Handled = true; } } - private void FetchDirectlyByHotKey(object sender, RoutedEventArgs e) + private async void FetchDirectlyByHotKey(object sender, RoutedEventArgs e) { if (DataContext is ViewModels.Repository repo) { - repo.Fetch(true); + await repo.FetchAsync(true); e.Handled = true; } } - private void Pull(object sender, TappedEventArgs e) + private async void Pull(object sender, TappedEventArgs e) { if (DataContext is ViewModels.Repository repo) { - repo.Pull(e.KeyModifiers is KeyModifiers.Control); + await repo.PullAsync(e.KeyModifiers is KeyModifiers.Control); e.Handled = true; } } - private void PullDirectlyByHotKey(object sender, RoutedEventArgs e) + private async void PullDirectlyByHotKey(object sender, RoutedEventArgs e) { if (DataContext is ViewModels.Repository repo) { - repo.Pull(true); + await repo.PullAsync(true); e.Handled = true; } } - private void Push(object sender, TappedEventArgs e) + private async void Push(object sender, TappedEventArgs e) { if (DataContext is ViewModels.Repository repo) { - repo.Push(e.KeyModifiers is KeyModifiers.Control); + await repo.PushAsync(e.KeyModifiers is KeyModifiers.Control); e.Handled = true; } } - private void PushDirectlyByHotKey(object sender, RoutedEventArgs e) + private async void PushDirectlyByHotKey(object sender, RoutedEventArgs e) { if (DataContext is ViewModels.Repository repo) { - repo.Push(true); + await repo.PushAsync(true); e.Handled = true; } } - private void StashAll(object _, TappedEventArgs e) + private async void StashAll(object _, TappedEventArgs e) { if (DataContext is ViewModels.Repository repo) { - repo.StashAll(e.KeyModifiers is KeyModifiers.Control); + await repo.StashAllAsync(e.KeyModifiers is KeyModifiers.Control); e.Handled = true; } } @@ -277,12 +277,12 @@ private void OpenGitLFSMenu(object sender, RoutedEventArgs e) fetch.Header = App.Text("GitLFS.Fetch"); fetch.Icon = App.CreateMenuIcon("Icons.Fetch"); fetch.IsEnabled = repo.Remotes.Count > 0; - fetch.Click += (_, e) => + fetch.Click += async (_, e) => { if (repo.CanCreatePopup()) { if (repo.Remotes.Count == 1) - repo.ShowAndStartPopup(new ViewModels.LFSFetch(repo)); + await repo.ShowAndStartPopupAsync(new ViewModels.LFSFetch(repo)); else repo.ShowPopup(new ViewModels.LFSFetch(repo)); } @@ -295,12 +295,12 @@ private void OpenGitLFSMenu(object sender, RoutedEventArgs e) pull.Header = App.Text("GitLFS.Pull"); pull.Icon = App.CreateMenuIcon("Icons.Pull"); pull.IsEnabled = repo.Remotes.Count > 0; - pull.Click += (_, e) => + pull.Click += async (_, e) => { if (repo.CanCreatePopup()) { if (repo.Remotes.Count == 1) - repo.ShowAndStartPopup(new ViewModels.LFSPull(repo)); + await repo.ShowAndStartPopupAsync(new ViewModels.LFSPull(repo)); else repo.ShowPopup(new ViewModels.LFSPull(repo)); } @@ -313,12 +313,12 @@ private void OpenGitLFSMenu(object sender, RoutedEventArgs e) push.Header = App.Text("GitLFS.Push"); push.Icon = App.CreateMenuIcon("Icons.Push"); push.IsEnabled = repo.Remotes.Count > 0; - push.Click += (_, e) => + push.Click += async (_, e) => { if (repo.CanCreatePopup()) { if (repo.Remotes.Count == 1) - repo.ShowAndStartPopup(new ViewModels.LFSPush(repo)); + await repo.ShowAndStartPopupAsync(new ViewModels.LFSPush(repo)); else repo.ShowPopup(new ViewModels.LFSPush(repo)); } @@ -330,10 +330,10 @@ private void OpenGitLFSMenu(object sender, RoutedEventArgs e) var prune = new MenuItem(); prune.Header = App.Text("GitLFS.Prune"); prune.Icon = App.CreateMenuIcon("Icons.Clean"); - prune.Click += (_, e) => + prune.Click += async (_, e) => { if (repo.CanCreatePopup()) - repo.ShowAndStartPopup(new ViewModels.LFSPrune(repo)); + await repo.ShowAndStartPopupAsync(new ViewModels.LFSPrune(repo)); e.Handled = true; }; @@ -406,6 +406,15 @@ private async void StartBisect(object sender, RoutedEventArgs e) e.Handled = true; } + private async void Cleanup(object sender, RoutedEventArgs e) + { + if (DataContext is ViewModels.Repository repo) + { + await repo.CleanupAsync(); + e.Handled = true; + } + } + private void OpenCustomActionMenu(object sender, RoutedEventArgs e) { if (DataContext is ViewModels.Repository repo && sender is Control control) @@ -422,9 +431,9 @@ private void OpenCustomActionMenu(object sender, RoutedEventArgs e) var item = new MenuItem(); item.Icon = App.CreateMenuIcon("Icons.Action"); item.Header = label; - item.Click += (_, e) => + item.Click += async (_, e) => { - repo.ExecCustomAction(dup, null); + await repo.ExecCustomActionAsync(dup, null); e.Handled = true; }; diff --git a/src/Views/TagsView.axaml.cs b/src/Views/TagsView.axaml.cs index a35fcfbb5..943619860 100644 --- a/src/Views/TagsView.axaml.cs +++ b/src/Views/TagsView.axaml.cs @@ -280,9 +280,9 @@ private void OnTagsContextMenuRequested(object sender, ContextRequestedEventArgs var item = new MenuItem(); item.Icon = App.CreateMenuIcon("Icons.Action"); item.Header = label; - item.Click += (_, e) => + item.Click += async (_, e) => { - repo.ExecCustomAction(dup, tag); + await repo.ExecCustomActionAsync(dup, tag); e.Handled = true; }; From c4ae21ea715e52553c5b1dd60883e67872f6aa76 Mon Sep 17 00:00:00 2001 From: leo Date: Thu, 14 Aug 2025 16:18:38 +0800 Subject: [PATCH 124/591] code_style: async method names Signed-off-by: leo --- src/ViewModels/Repository.cs | 4 ++-- src/Views/BranchTree.axaml.cs | 4 ++-- src/Views/RepositoryToolbar.axaml.cs | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/ViewModels/Repository.cs b/src/ViewModels/Repository.cs index 2701a4fb8..bd6d2c08d 100644 --- a/src/ViewModels/Repository.cs +++ b/src/ViewModels/Repository.cs @@ -677,7 +677,7 @@ public bool IsLFSEnabled() return content.Contains("git lfs pre-push"); } - public async Task InstallLFS() + public async Task InstallLFSAsync() { var log = CreateLog("Install LFS"); var succ = await new Commands.LFS(_fullpath).Use(log).InstallAsync(); @@ -1498,7 +1498,7 @@ public async Task CheckoutTagAsync(Models.Tag tag) await _histories?.CheckoutBranchByCommitAsync(c); } - public async Task CompareBranchWithWorktree(Models.Branch branch) + public async Task CompareBranchWithWorktreeAsync(Models.Branch branch) { if (_histories != null) { diff --git a/src/Views/BranchTree.axaml.cs b/src/Views/BranchTree.axaml.cs index aca4212ca..8c0b63108 100644 --- a/src/Views/BranchTree.axaml.cs +++ b/src/Views/BranchTree.axaml.cs @@ -741,7 +741,7 @@ private ContextMenu CreateContextMenuForLocalBranch(ViewModels.Repository repo, compareWithWorktree.Icon = App.CreateMenuIcon("Icons.Compare"); compareWithWorktree.Click += async (_, e) => { - await repo.CompareBranchWithWorktree(branch); + await repo.CompareBranchWithWorktreeAsync(branch); e.Handled = true; }; menu.Items.Add(compareWithWorktree); @@ -1014,7 +1014,7 @@ public ContextMenu CreateContextMenuForRemoteBranch(ViewModels.Repository repo, compareWithWorktree.Icon = App.CreateMenuIcon("Icons.Compare"); compareWithWorktree.Click += async (_, e) => { - await repo.CompareBranchWithWorktree(branch); + await repo.CompareBranchWithWorktreeAsync(branch); e.Handled = true; }; menu.Items.Add(compareWithWorktree); diff --git a/src/Views/RepositoryToolbar.axaml.cs b/src/Views/RepositoryToolbar.axaml.cs index d25780069..b8944ace6 100644 --- a/src/Views/RepositoryToolbar.axaml.cs +++ b/src/Views/RepositoryToolbar.axaml.cs @@ -378,7 +378,7 @@ private void OpenGitLFSMenu(object sender, RoutedEventArgs e) install.Icon = App.CreateMenuIcon("Icons.Init"); install.Click += async (_, e) => { - await repo.InstallLFS(); + await repo.InstallLFSAsync(); e.Handled = true; }; menu.Items.Add(install); From d66a083113e2ec21d9f600fe9982d028a38fa704 Mon Sep 17 00:00:00 2001 From: leo Date: Thu, 14 Aug 2025 17:53:11 +0800 Subject: [PATCH 125/591] feature: support creating custom action for selected remote (#1726) Signed-off-by: leo --- src/Models/CustomAction.cs | 1 + src/Resources/Locales/en_US.axaml | 4 +++- src/Resources/Locales/zh_CN.axaml | 4 +++- src/Resources/Locales/zh_TW.axaml | 4 +++- src/ViewModels/ExecuteCustomAction.cs | 31 ++++----------------------- src/ViewModels/Repository.cs | 11 ++-------- src/Views/BranchTree.axaml.cs | 30 ++++++++++++++++++++++++++ src/Views/ExecuteCustomAction.axaml | 7 ++++++ src/Views/Preferences.axaml | 1 + src/Views/RepositoryConfigure.axaml | 1 + 10 files changed, 55 insertions(+), 39 deletions(-) diff --git a/src/Models/CustomAction.cs b/src/Models/CustomAction.cs index 1ed65b8b3..33d7e77e2 100644 --- a/src/Models/CustomAction.cs +++ b/src/Models/CustomAction.cs @@ -9,6 +9,7 @@ public enum CustomActionScope Commit, Branch, Tag, + Remote, } public enum CustomActionControlType diff --git a/src/Resources/Locales/en_US.axaml b/src/Resources/Locales/en_US.axaml index de16b5b7f..ea7a338a5 100644 --- a/src/Resources/Locales/en_US.axaml +++ b/src/Resources/Locales/en_US.axaml @@ -173,7 +173,7 @@ Template Name: CUSTOM ACTION Arguments: - Built-in parameters: ${REPO} - repository's path; ${BRANCH} - selected branch; ${SHA} - selected commit's hash; ${TAG} - selected tag + Built-in parameters: ${REPO} - repository's path; ${REMOTE} - selected remote or selected branch's remote; ${BRANCH} - selected branch; ${SHA} - selected commit's hash; ${TAG} - selected tag Executable File: Input Controls: Edit @@ -182,6 +182,7 @@ Scope: Branch Commit + Remote Repository Tag Wait for action exit @@ -616,6 +617,7 @@ Repository URL: Remote git repository URL Copy URL + Custom Action Delete... Edit... Fetch diff --git a/src/Resources/Locales/zh_CN.axaml b/src/Resources/Locales/zh_CN.axaml index 15af237c3..aeb6e040c 100644 --- a/src/Resources/Locales/zh_CN.axaml +++ b/src/Resources/Locales/zh_CN.axaml @@ -177,7 +177,7 @@ 模板名 : 自定义操作 命令行参数 : - 内置变量:${REPO} 仓库路径、${BRANCH} 选中的分支、${SHA} 选中的提交哈希,${TAG} 选中的标签 + 内置变量:${REPO} 仓库路径、${REMOTE} 选中的远程仓库或选中分支所属的远程仓库、${BRANCH} 选中的分支、${SHA} 选中的提交哈希,${TAG} 选中的标签 可执行文件路径 : 输入控件 : 编辑 @@ -186,6 +186,7 @@ 作用目标 : 选中的分支 选中的提交 + 远程仓库 仓库 选中的标签 等待操作执行完成 @@ -620,6 +621,7 @@ 仓库地址 : 远程仓库的地址 复制远程地址 + 自定义操作 删除 ... 编辑 ... 拉取(fetch)更新 diff --git a/src/Resources/Locales/zh_TW.axaml b/src/Resources/Locales/zh_TW.axaml index d217ac51b..be23c8e04 100644 --- a/src/Resources/Locales/zh_TW.axaml +++ b/src/Resources/Locales/zh_TW.axaml @@ -177,7 +177,7 @@ 範本名稱: 自訂動作 指令參數: - 內建參數: ${REPO} 存放庫路徑、${BRANCH} 所選的分支、${SHA} 所選的提交編號、${TAG} 所選的標籤 + 內建參數: ${REPO} 存放庫路徑、${REMOTE} 所選的遠端存放庫或所選分支的遠端、${BRANCH} 所選的分支、${SHA} 所選的提交編號、${TAG} 所選的標籤 可執行檔案路徑: 輸入控制項: 編輯 @@ -186,6 +186,7 @@ 執行範圍: 選取的分支 選取的提交 + 遠端存放庫 存放庫 選取的標籤 等待自訂動作執行結束 @@ -620,6 +621,7 @@ 存放庫網址: 遠端存放庫的網址 複製遠端網址 + 自訂動作 刪除... 編輯... 提取 (fetch) 更新 diff --git a/src/ViewModels/ExecuteCustomAction.cs b/src/ViewModels/ExecuteCustomAction.cs index fb0f2f819..837259a7c 100644 --- a/src/ViewModels/ExecuteCustomAction.cs +++ b/src/ViewModels/ExecuteCustomAction.cs @@ -119,35 +119,11 @@ public List ControlParameters get; } = []; - public ExecuteCustomAction(Repository repo, Models.CustomAction action) + public ExecuteCustomAction(Repository repo, Models.CustomAction action, object scopeTarget) { _repo = repo; CustomAction = action; - Target = new Models.Null(); - PrepareControlParameters(); - } - - public ExecuteCustomAction(Repository repo, Models.CustomAction action, Models.Branch branch) - { - _repo = repo; - CustomAction = action; - Target = branch; - PrepareControlParameters(); - } - - public ExecuteCustomAction(Repository repo, Models.CustomAction action, Models.Commit commit) - { - _repo = repo; - CustomAction = action; - Target = commit; - PrepareControlParameters(); - } - - public ExecuteCustomAction(Repository repo, Models.CustomAction action, Models.Tag tag) - { - _repo = repo; - CustomAction = action; - Target = tag; + Target = scopeTarget ?? new Models.Null(); PrepareControlParameters(); } @@ -206,9 +182,10 @@ private string PrepareStringByTarget(string org) return Target switch { - Models.Branch b => org.Replace("${BRANCH}", b.FriendlyName), + Models.Branch b => org.Replace("${BRANCH}", b.FriendlyName).Replace("${REMOTE}", b.Remote), Models.Commit c => org.Replace("${SHA}", c.SHA), Models.Tag t => org.Replace("${TAG}", t.Name), + Models.Remote r => org.Replace("${REMOTE}", r.Name), _ => org }; } diff --git a/src/ViewModels/Repository.cs b/src/ViewModels/Repository.cs index bd6d2c08d..9ba559ca4 100644 --- a/src/ViewModels/Repository.cs +++ b/src/ViewModels/Repository.cs @@ -876,19 +876,12 @@ public void ApplyPatch() ShowPopup(new Apply(this)); } - public async Task ExecCustomActionAsync(Models.CustomAction action, object scope) + public async Task ExecCustomActionAsync(Models.CustomAction action, object scopeTarget) { if (!CanCreatePopup()) return; - var popup = scope switch - { - Models.Branch b => new ExecuteCustomAction(this, action, b), - Models.Commit c => new ExecuteCustomAction(this, action, c), - Models.Tag t => new ExecuteCustomAction(this, action, t), - _ => new ExecuteCustomAction(this, action) - }; - + var popup = new ExecuteCustomAction(this, action, scopeTarget); if (action.Controls.Count == 0) await ShowAndStartPopupAsync(popup); else diff --git a/src/Views/BranchTree.axaml.cs b/src/Views/BranchTree.axaml.cs index 8c0b63108..8217b49f7 100644 --- a/src/Views/BranchTree.axaml.cs +++ b/src/Views/BranchTree.axaml.cs @@ -940,6 +940,7 @@ private ContextMenu CreateContextMenuForRemote(ViewModels.Repository repo, Model menu.Items.Add(edit); menu.Items.Add(delete); menu.Items.Add(new MenuItem() { Header = "-" }); + TryToAddCustomActionsToRemoteContextMenu(repo, menu, remote); menu.Items.Add(copy); return menu; } @@ -1112,6 +1113,35 @@ private void TryToAddCustomActionsToBranchContextMenu(ViewModels.Repository repo menu.Items.Add(new MenuItem() { Header = "-" }); } + private void TryToAddCustomActionsToRemoteContextMenu(ViewModels.Repository repo, ContextMenu menu, Models.Remote remote) + { + var actions = repo.GetCustomActions(Models.CustomActionScope.Remote); + if (actions.Count == 0) + return; + + var custom = new MenuItem(); + custom.Header = App.Text("RemoteCM.CustomAction"); + custom.Icon = App.CreateMenuIcon("Icons.Action"); + + foreach (var action in actions) + { + var (dup, label) = action; + var item = new MenuItem(); + item.Icon = App.CreateMenuIcon("Icons.Action"); + item.Header = label; + item.Click += async (_, e) => + { + await repo.ExecCustomActionAsync(dup, remote); + e.Handled = true; + }; + + custom.Items.Add(item); + } + + menu.Items.Add(custom); + menu.Items.Add(new MenuItem() { Header = "-" }); + } + private bool _disableSelectionChangingEvent = false; } } diff --git a/src/Views/ExecuteCustomAction.axaml b/src/Views/ExecuteCustomAction.axaml index 4f0c6c6d9..5329794f3 100644 --- a/src/Views/ExecuteCustomAction.axaml +++ b/src/Views/ExecuteCustomAction.axaml @@ -50,6 +50,13 @@ + + + + + + + diff --git a/src/Views/Preferences.axaml b/src/Views/Preferences.axaml index a01b10800..90df4323a 100644 --- a/src/Views/Preferences.axaml +++ b/src/Views/Preferences.axaml @@ -656,6 +656,7 @@ + diff --git a/src/Views/RepositoryConfigure.axaml b/src/Views/RepositoryConfigure.axaml index a99b717fa..14dc3bc12 100644 --- a/src/Views/RepositoryConfigure.axaml +++ b/src/Views/RepositoryConfigure.axaml @@ -480,6 +480,7 @@ + From 431532851226cf2ecf08239a32629b6b6140f295 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Thu, 14 Aug 2025 09:53:34 +0000 Subject: [PATCH 126/591] doc: Update translation status and sort locale files --- TRANSLATION.md | 36 +++++++++++++++++++++++++++--------- 1 file changed, 27 insertions(+), 9 deletions(-) diff --git a/TRANSLATION.md b/TRANSLATION.md index 4bf1aa759..2689d0249 100644 --- a/TRANSLATION.md +++ b/TRANSLATION.md @@ -6,16 +6,18 @@ This document shows the translation status of each locale file in the repository ### ![en_US](https://img.shields.io/badge/en__US-%E2%88%9A-brightgreen) -### ![de__DE](https://img.shields.io/badge/de__DE-98.38%25-yellow) +### ![de__DE](https://img.shields.io/badge/de__DE-98.15%25-yellow)
Missing keys in de_DE.axaml - Text.BranchTree.InvalidUpstream +- Text.Configure.CustomAction.Scope.Remote - Text.DeleteMultiTags - Text.DeleteMultiTags.DeleteFromRemotes - Text.DeleteMultiTags.Tip - Text.Preferences.Appearance.EnableCompactFolders +- Text.RemoteCM.CustomAction - Text.Repository.Dashboard - Text.Repository.MoreOptions - Text.Tag.Tagger @@ -28,21 +30,23 @@ This document shows the translation status of each locale file in the repository
-### ![es__ES](https://img.shields.io/badge/es__ES-99.30%25-yellow) +### ![es__ES](https://img.shields.io/badge/es__ES-99.08%25-yellow)
Missing keys in es_ES.axaml - Text.BranchTree.InvalidUpstream +- Text.Configure.CustomAction.Scope.Remote - Text.DeleteMultiTags - Text.DeleteMultiTags.DeleteFromRemotes - Text.DeleteMultiTags.Tip - Text.Preferences.Appearance.EnableCompactFolders +- Text.RemoteCM.CustomAction - Text.TagCM.DeleteMultiple
-### ![fr__FR](https://img.shields.io/badge/fr__FR-80.65%25-yellow) +### ![fr__FR](https://img.shields.io/badge/fr__FR-80.46%25-yellow)
Missing keys in fr_FR.axaml @@ -94,6 +98,7 @@ This document shows the translation status of each locale file in the repository - Text.Configure.CustomAction.InputControls - Text.Configure.CustomAction.InputControls.Edit - Text.Configure.CustomAction.InputControls.Tip +- Text.Configure.CustomAction.Scope.Remote - Text.Configure.CustomAction.Scope.Tag - Text.Configure.Git.PreferredMergeMode - Text.Configure.IssueTracker.AddSampleGerritChangeIdCommit @@ -150,6 +155,7 @@ This document shows the translation status of each locale file in the repository - Text.Push.New - Text.Push.Revision - Text.Push.Revision.Title +- Text.RemoteCM.CustomAction - Text.Repository.BranchSort - Text.Repository.BranchSort.ByCommitterDate - Text.Repository.BranchSort.ByName @@ -217,7 +223,7 @@ This document shows the translation status of each locale file in the repository
-### ![it__IT](https://img.shields.io/badge/it__IT-85.86%25-yellow) +### ![it__IT](https://img.shields.io/badge/it__IT-85.66%25-yellow)
Missing keys in it_IT.axaml @@ -257,6 +263,7 @@ This document shows the translation status of each locale file in the repository - Text.Configure.CustomAction.InputControls - Text.Configure.CustomAction.InputControls.Edit - Text.Configure.CustomAction.InputControls.Tip +- Text.Configure.CustomAction.Scope.Remote - Text.Configure.CustomAction.Scope.Tag - Text.Configure.IssueTracker.AddSampleGerritChangeIdCommit - Text.Configure.IssueTracker.Share @@ -304,6 +311,7 @@ This document shows the translation status of each locale file in the repository - Text.Push.New - Text.Push.Revision - Text.Push.Revision.Title +- Text.RemoteCM.CustomAction - Text.Repository.ClearStashes - Text.Repository.Dashboard - Text.Repository.MoreOptions @@ -347,7 +355,7 @@ This document shows the translation status of each locale file in the repository
-### ![ja__JP](https://img.shields.io/badge/ja__JP-80.65%25-yellow) +### ![ja__JP](https://img.shields.io/badge/ja__JP-80.46%25-yellow)
Missing keys in ja_JP.axaml @@ -400,6 +408,7 @@ This document shows the translation status of each locale file in the repository - Text.Configure.CustomAction.InputControls - Text.Configure.CustomAction.InputControls.Edit - Text.Configure.CustomAction.InputControls.Tip +- Text.Configure.CustomAction.Scope.Remote - Text.Configure.CustomAction.Scope.Tag - Text.Configure.Git.PreferredMergeMode - Text.Configure.IssueTracker.AddSampleGerritChangeIdCommit @@ -456,6 +465,7 @@ This document shows the translation status of each locale file in the repository - Text.Push.New - Text.Push.Revision - Text.Push.Revision.Title +- Text.RemoteCM.CustomAction - Text.Repository.BranchSort - Text.Repository.BranchSort.ByCommitterDate - Text.Repository.BranchSort.ByName @@ -522,7 +532,7 @@ This document shows the translation status of each locale file in the repository
-### ![pt__BR](https://img.shields.io/badge/pt__BR-73.81%25-red) +### ![pt__BR](https://img.shields.io/badge/pt__BR-73.64%25-red)
Missing keys in pt_BR.axaml @@ -588,6 +598,7 @@ This document shows the translation status of each locale file in the repository - Text.Configure.CustomAction.InputControls.Edit - Text.Configure.CustomAction.InputControls.Tip - Text.Configure.CustomAction.Scope.Branch +- Text.Configure.CustomAction.Scope.Remote - Text.Configure.CustomAction.Scope.Tag - Text.Configure.CustomAction.WaitForExit - Text.Configure.Git.PreferredMergeMode @@ -673,6 +684,7 @@ This document shows the translation status of each locale file in the repository - Text.Push.New - Text.Push.Revision - Text.Push.Revision.Title +- Text.RemoteCM.CustomAction - Text.Repository.BranchSort - Text.Repository.BranchSort.ByCommitterDate - Text.Repository.BranchSort.ByName @@ -756,21 +768,23 @@ This document shows the translation status of each locale file in the repository
-### ![ru__RU](https://img.shields.io/badge/ru__RU-99.30%25-yellow) +### ![ru__RU](https://img.shields.io/badge/ru__RU-99.08%25-yellow)
Missing keys in ru_RU.axaml - Text.BranchTree.InvalidUpstream +- Text.Configure.CustomAction.Scope.Remote - Text.DeleteMultiTags - Text.DeleteMultiTags.DeleteFromRemotes - Text.DeleteMultiTags.Tip - Text.Preferences.Appearance.EnableCompactFolders +- Text.RemoteCM.CustomAction - Text.TagCM.DeleteMultiple
-### ![ta__IN](https://img.shields.io/badge/ta__IN-80.76%25-yellow) +### ![ta__IN](https://img.shields.io/badge/ta__IN-80.58%25-yellow)
Missing keys in ta_IN.axaml @@ -823,6 +837,7 @@ This document shows the translation status of each locale file in the repository - Text.Configure.CustomAction.InputControls - Text.Configure.CustomAction.InputControls.Edit - Text.Configure.CustomAction.InputControls.Tip +- Text.Configure.CustomAction.Scope.Remote - Text.Configure.CustomAction.Scope.Tag - Text.Configure.Git.PreferredMergeMode - Text.Configure.IssueTracker.AddSampleGerritChangeIdCommit @@ -879,6 +894,7 @@ This document shows the translation status of each locale file in the repository - Text.Push.New - Text.Push.Revision - Text.Push.Revision.Title +- Text.RemoteCM.CustomAction - Text.Repository.BranchSort - Text.Repository.BranchSort.ByCommitterDate - Text.Repository.BranchSort.ByName @@ -944,7 +960,7 @@ This document shows the translation status of each locale file in the repository
-### ![uk__UA](https://img.shields.io/badge/uk__UA-81.92%25-yellow) +### ![uk__UA](https://img.shields.io/badge/uk__UA-81.73%25-yellow)
Missing keys in uk_UA.axaml @@ -996,6 +1012,7 @@ This document shows the translation status of each locale file in the repository - Text.Configure.CustomAction.InputControls - Text.Configure.CustomAction.InputControls.Edit - Text.Configure.CustomAction.InputControls.Tip +- Text.Configure.CustomAction.Scope.Remote - Text.Configure.CustomAction.Scope.Tag - Text.Configure.IssueTracker.AddSampleGerritChangeIdCommit - Text.Configure.IssueTracker.Share @@ -1048,6 +1065,7 @@ This document shows the translation status of each locale file in the repository - Text.Push.New - Text.Push.Revision - Text.Push.Revision.Title +- Text.RemoteCM.CustomAction - Text.Repository.BranchSort - Text.Repository.BranchSort.ByCommitterDate - Text.Repository.BranchSort.ByName From 8fdacd1f82af1000cbd90eda9067380d97b088cc Mon Sep 17 00:00:00 2001 From: leo Date: Fri, 15 Aug 2025 10:50:05 +0800 Subject: [PATCH 127/591] fix: sync-scroll in side-by-side text diff view does not work while draging mouse out of the scrollbar Signed-off-by: leo --- src/Views/TextDiffView.axaml.cs | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/src/Views/TextDiffView.axaml.cs b/src/Views/TextDiffView.axaml.cs index 6b65dba29..57e0630fb 100644 --- a/src/Views/TextDiffView.axaml.cs +++ b/src/Views/TextDiffView.axaml.cs @@ -1495,9 +1495,15 @@ protected override void OnDataContextChanged(EventArgs e) private void OnTextViewScrollChanged(object sender, ScrollChangedEventArgs e) { - if (IsPointerOver && DataContext is ViewModels.TwoSideTextDiff diff) + if (_scrollViewer == null || DataContext is not ViewModels.TwoSideTextDiff diff) + return; + + if (diff.SyncScrollOffset.NearlyEquals(_scrollViewer.Offset)) + return; + + if (IsPointerOver || !e.OffsetDelta.NearlyEquals(Vector.Zero)) { - diff.SyncScrollOffset = _scrollViewer?.Offset ?? Vector.Zero; + diff.SyncScrollOffset = _scrollViewer.Offset; if (!TextArea.TextView.IsPointerOver) TrySetChunk(null); @@ -1507,7 +1513,7 @@ private void OnTextViewScrollChanged(object sender, ScrollChangedEventArgs e) private void DirectSyncScrollOffset() { if (_scrollViewer is not null && DataContext is ViewModels.TwoSideTextDiff diff) - diff.SyncScrollOffset = _scrollViewer?.Offset ?? Vector.Zero; + diff.SyncScrollOffset = _scrollViewer.Offset; } private ScrollViewer _scrollViewer = null; From e87205494321042484be25c1d88358818691cffc Mon Sep 17 00:00:00 2001 From: AquariusStar <48148723+AquariusStar@users.noreply.github.com> Date: Fri, 15 Aug 2025 06:06:59 +0300 Subject: [PATCH 128/591] localization: update `Russian` translation (#1731) --- src/Resources/Locales/ru_RU.axaml | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/Resources/Locales/ru_RU.axaml b/src/Resources/Locales/ru_RU.axaml index 611003c6f..08c9d9828 100644 --- a/src/Resources/Locales/ru_RU.axaml +++ b/src/Resources/Locales/ru_RU.axaml @@ -74,6 +74,7 @@ Сбросить ${0}$ к ${1}$... Отслеживать ветку... Сравнение веток + Неверно УДАЛЁННЫЙ ОТСЛЕЖИВАНИЕ URL-АДРЕС @@ -185,6 +186,7 @@ Диапазон: Ветка Ревизия + Удалённый Репозиторий Метка Ждать для выполения выхода @@ -282,6 +284,9 @@ Также удалите внешнюю ветку ${0}$ Удаление нескольких веток Вы пытаетесь удалить несколько веток одновременно. Обязательно перепроверьте, прежде чем предпринимать какие-либо действия! + Удалить несколько меток + Удалить их с удалённого + Вы пытаетесь удалить сразу несколько меток. Перепроверьте обязательно перед выполнением! Удалить внешний репозиторий Внешний репозиторий: Путь: @@ -526,6 +531,7 @@ ВИД Шрифт по умолчанию Редактировать ширину вкладки + Включить компактные каталоги в дереве изменений Размер шрифта По умолчанию Редактор @@ -615,6 +621,7 @@ Адрес: Адрес внешнего репозитория git Копировать адрес + Пользовательские действия Удалить... Редактировать... Извлечь @@ -791,6 +798,7 @@ Копировать имя метки Пользовательское действие Удалить ${0}$... + Удалить выбранные метки ({0})... Влить ${0}$ в ${1}$... Выложить ${0}$... Обновление подмодулей From 154f3e7241b4dfc5952648526955afec6050039e Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Fri, 15 Aug 2025 03:07:07 +0000 Subject: [PATCH 129/591] doc: Update translation status and sort locale files --- TRANSLATION.md | 16 +--------------- 1 file changed, 1 insertion(+), 15 deletions(-) diff --git a/TRANSLATION.md b/TRANSLATION.md index 2689d0249..cc00dae20 100644 --- a/TRANSLATION.md +++ b/TRANSLATION.md @@ -768,21 +768,7 @@ This document shows the translation status of each locale file in the repository
-### ![ru__RU](https://img.shields.io/badge/ru__RU-99.08%25-yellow) - -
-Missing keys in ru_RU.axaml - -- Text.BranchTree.InvalidUpstream -- Text.Configure.CustomAction.Scope.Remote -- Text.DeleteMultiTags -- Text.DeleteMultiTags.DeleteFromRemotes -- Text.DeleteMultiTags.Tip -- Text.Preferences.Appearance.EnableCompactFolders -- Text.RemoteCM.CustomAction -- Text.TagCM.DeleteMultiple - -
+### ![ru__RU](https://img.shields.io/badge/ru__RU-%E2%88%9A-brightgreen) ### ![ta__IN](https://img.shields.io/badge/ta__IN-80.58%25-yellow) From f44e8352f4b1bd2621596c7ba9a7f9b9392c8a50 Mon Sep 17 00:00:00 2001 From: leo Date: Fri, 15 Aug 2025 12:58:46 +0800 Subject: [PATCH 130/591] fix: single-clicking titlebar should not maximize/restore window on macOS Signed-off-by: leo --- src/Views/ChromelessWindow.cs | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/Views/ChromelessWindow.cs b/src/Views/ChromelessWindow.cs index 24c652dcf..1abb06c97 100644 --- a/src/Views/ChromelessWindow.cs +++ b/src/Views/ChromelessWindow.cs @@ -30,7 +30,12 @@ public ChromelessWindow() public void BeginMoveWindow(object _, PointerPressedEventArgs e) { if (e.ClickCount == 1) + { + if (OperatingSystem.IsMacOS()) + e.Pointer.Capture(this); + BeginMoveDrag(e); + } e.Handled = true; } From e5c3ed5b2b25a2d597843459f6e68c8692628140 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Javier=20J=2E=20Mart=C3=ADnez=20M=2E?= <56406225+jjesus-dev@users.noreply.github.com> Date: Thu, 14 Aug 2025 22:59:25 -0600 Subject: [PATCH 131/591] localization: update `Spanish` translations (#1733) Add missing translations. --- src/Resources/Locales/es_ES.axaml | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/src/Resources/Locales/es_ES.axaml b/src/Resources/Locales/es_ES.axaml index 1bbe1fbee..8fd80ac78 100644 --- a/src/Resources/Locales/es_ES.axaml +++ b/src/Resources/Locales/es_ES.axaml @@ -74,6 +74,7 @@ Resetear ${0}$ a ${1}$... Establecer Rama de Seguimiento... Comparar Ramas + Inválido REMOTO SEGUIMIENTO URL @@ -185,6 +186,7 @@ Alcance: Rama Commit + Remoto Repositorio Etiqueta Esperar la acción de salida @@ -281,7 +283,10 @@ ¡Estás a punto de eliminar una rama remota! También eliminar la rama remota ${0}$ Eliminar Múltiples Ramas - Estás intentando eliminar múltiples ramas a la vez. ¡Asegúrate de revisar antes de tomar acción! + Estás intentando eliminar múltiples ramas a la vez. ¡Asegúrate de comprobar dos veces antes de realizar esta acción! + Eliminar Múltiples Etiquetas + Eliminarlas de los remotos + Estás intentando eliminar múltiples etiquetas a la vez. ¡Asegúrate de comprobar dos veces antes de realizar esta acción! Eliminar Remoto Remoto: Ruta: @@ -522,10 +527,11 @@ Modelo Nombre Servidor - Activar Transmisión + Habilitar Transmisión APARIENCIA Fuente por defecto Ancho de la Pestaña del Editor + Habilitar carpetas compactas en el árbol de cambios Tamaño de fuente Por defecto Editor @@ -615,6 +621,7 @@ URL del Repositorio: URL del repositorio git remoto Copiar URL + Acción Personalizada Borrar... Editar... Fetch @@ -791,6 +798,7 @@ Copiar Nombre de la Etiqueta Acción Personalizada Eliminar ${0}$... + Eliminar {0} etiquetas seleccionadas... Merge ${0}$ en ${1}$... Push ${0}$... Actualizar Submódulos From 91a0d2559fca294769b19e37183daf2854c24586 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Fri, 15 Aug 2025 04:59:34 +0000 Subject: [PATCH 132/591] doc: Update translation status and sort locale files --- TRANSLATION.md | 16 +--------------- 1 file changed, 1 insertion(+), 15 deletions(-) diff --git a/TRANSLATION.md b/TRANSLATION.md index cc00dae20..3c865b4d7 100644 --- a/TRANSLATION.md +++ b/TRANSLATION.md @@ -30,21 +30,7 @@ This document shows the translation status of each locale file in the repository
-### ![es__ES](https://img.shields.io/badge/es__ES-99.08%25-yellow) - -
-Missing keys in es_ES.axaml - -- Text.BranchTree.InvalidUpstream -- Text.Configure.CustomAction.Scope.Remote -- Text.DeleteMultiTags -- Text.DeleteMultiTags.DeleteFromRemotes -- Text.DeleteMultiTags.Tip -- Text.Preferences.Appearance.EnableCompactFolders -- Text.RemoteCM.CustomAction -- Text.TagCM.DeleteMultiple - -
+### ![es__ES](https://img.shields.io/badge/es__ES-%E2%88%9A-brightgreen) ### ![fr__FR](https://img.shields.io/badge/fr__FR-80.46%25-yellow) From c8da655a51a270e298f20e43addd05eba7473115 Mon Sep 17 00:00:00 2001 From: leo Date: Fri, 15 Aug 2025 13:00:23 +0800 Subject: [PATCH 133/591] ux: change blame window layout Signed-off-by: leo --- src/Views/Blame.axaml.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Views/Blame.axaml.cs b/src/Views/Blame.axaml.cs index 734160e53..a3288851c 100644 --- a/src/Views/Blame.axaml.cs +++ b/src/Views/Blame.axaml.cs @@ -288,10 +288,10 @@ public int TabWidth _textMate = Models.TextMateHelper.CreateForEditor(this); - TextArea.LeftMargins.Add(new LineNumberMargin() { Margin = new Thickness(8, 0) }); - TextArea.LeftMargins.Add(new VerticalSeparatorMargin(this)); TextArea.LeftMargins.Add(new CommitInfoMargin(this) { Margin = new Thickness(8, 0) }); TextArea.LeftMargins.Add(new VerticalSeparatorMargin(this)); + TextArea.LeftMargins.Add(new LineNumberMargin() { Margin = new Thickness(8, 0) }); + TextArea.LeftMargins.Add(new VerticalSeparatorMargin(this)); TextArea.Caret.PositionChanged += OnTextAreaCaretPositionChanged; TextArea.TextView.ContextRequested += OnTextViewContextRequested; TextArea.TextView.VisualLinesChanged += OnTextViewVisualLinesChanged; From 4e01270cc08702e6743d968f14f2df7b1178ffeb Mon Sep 17 00:00:00 2001 From: leo Date: Fri, 15 Aug 2025 16:50:21 +0800 Subject: [PATCH 134/591] feature!: add built-in parameter `${BRANCH_FRIENDLY_NAME}` for custom action - BREAKING CHANGE: Change `${BRANCH}` to pure branch name, which means it not contains the `${REMOTE}` part for remote branch - Re-design UI/UX for custom action Signed-off-by: leo --- src/Resources/Locales/de_DE.axaml | 2 -- src/Resources/Locales/en_US.axaml | 11 +++++++-- src/Resources/Locales/es_ES.axaml | 2 -- src/Resources/Locales/ru_RU.axaml | 2 -- src/Resources/Locales/zh_CN.axaml | 11 +++++++-- src/Resources/Locales/zh_TW.axaml | 11 +++++++-- src/ViewModels/ExecuteCustomAction.cs | 2 +- src/Views/Preferences.axaml | 35 +++++++++++++++------------ src/Views/RepositoryConfigure.axaml | 33 +++++++++++++------------ 9 files changed, 65 insertions(+), 44 deletions(-) diff --git a/src/Resources/Locales/de_DE.axaml b/src/Resources/Locales/de_DE.axaml index e29309a7c..89b377aa6 100644 --- a/src/Resources/Locales/de_DE.axaml +++ b/src/Resources/Locales/de_DE.axaml @@ -176,11 +176,9 @@ Template Name: BENUTZERDEFINIERTE AKTION Argumente: - Vordefinierte Parameter: ${REPO} - Repository Pfad; ${BRANCH} - selektierter Branch; ${SHA} - Hash des selektierten Commits; ${TAG} - selektiertes Tag Ausführbare Datei: Eingabe-Steuerelemente: Bearbeiten - $1, $2 ... können als Argumente in Eingabe-Steuerelementen benutzt werden Name: Geltungsbereich: Branch diff --git a/src/Resources/Locales/en_US.axaml b/src/Resources/Locales/en_US.axaml index ea7a338a5..0fa9c8473 100644 --- a/src/Resources/Locales/en_US.axaml +++ b/src/Resources/Locales/en_US.axaml @@ -173,11 +173,18 @@ Template Name: CUSTOM ACTION Arguments: - Built-in parameters: ${REPO} - repository's path; ${REMOTE} - selected remote or selected branch's remote; ${BRANCH} - selected branch; ${SHA} - selected commit's hash; ${TAG} - selected tag + Built-in parameters: + + ${REPO} Repository's path + ${REMOTE} Selected remote or selected branch's remote + ${BRANCH} Selected branch, without ${REMOTE} part for remote branches + ${BRANCH_FRIENDLY_NAME} Firendly name of selected branch, contains ${REMOTE} part for remote branches + ${SHA} Selected commit's hash + ${TAG} Selected tag + $1, $2 ... Input control values Executable File: Input Controls: Edit - You can use $1, $2 ... in arguments for input control values Name: Scope: Branch diff --git a/src/Resources/Locales/es_ES.axaml b/src/Resources/Locales/es_ES.axaml index 8fd80ac78..9c9aae18a 100644 --- a/src/Resources/Locales/es_ES.axaml +++ b/src/Resources/Locales/es_ES.axaml @@ -177,11 +177,9 @@ Nombre de la Plantilla: ACCIÓN PERSONALIZADA Argumentos: - Parámetros incorporados: ${REPO} - ruta del repositorio; ${BRANCH} - rama seleccionada; ${SHA} - hash del commit seleccionado; ${TAG} - etiqueta seleccionada Archivo Ejecutable: Controles de entrada: Editar - Puedes usar $1, $2 ... en argumentos, para valores de los controles de entrada Nombre: Alcance: Rama diff --git a/src/Resources/Locales/ru_RU.axaml b/src/Resources/Locales/ru_RU.axaml index 08c9d9828..8a4f48a9d 100644 --- a/src/Resources/Locales/ru_RU.axaml +++ b/src/Resources/Locales/ru_RU.axaml @@ -177,11 +177,9 @@ Название: ПОЛЬЗОВАТЕЛЬСКОЕ ДЕЙСТВИЕ Аргументы: - Встроенные параметры: ${REPO} — путь к репозиторию; ${BRANCH} — выбранная ветка; ${SHA} — хеш выбранной ревизии; ${TAG} — выбранная метка Исполняемый файл: Элементы управления вводом: Редактор - Вы можете использовать $1, $2 ... в аргументах для значений элемента управления вводом Имя: Диапазон: Ветка diff --git a/src/Resources/Locales/zh_CN.axaml b/src/Resources/Locales/zh_CN.axaml index aeb6e040c..d9cc3cd1d 100644 --- a/src/Resources/Locales/zh_CN.axaml +++ b/src/Resources/Locales/zh_CN.axaml @@ -177,11 +177,18 @@ 模板名 : 自定义操作 命令行参数 : - 内置变量:${REPO} 仓库路径、${REMOTE} 选中的远程仓库或选中分支所属的远程仓库、${BRANCH} 选中的分支、${SHA} 选中的提交哈希,${TAG} 选中的标签 + 内置变量: + + ${REPO} 仓库路径 + ${REMOTE} 选中的远程仓库或选中分支所属的远程仓库 + ${BRANCH} 选中的分支,对于远程分支不包含远程名 + ${BRANCH_FRIENDLY_NAME} 选中的分支,对于远程分支包含远程名 + ${SHA} 选中的提交哈希 + ${TAG} 选中的标签 + $1, $2 ... 输入控件中填写的值 可执行文件路径 : 输入控件 : 编辑 - 请在命令行参数中使用 $1, $2 等占位符表示输入控件的值 名称 : 作用目标 : 选中的分支 diff --git a/src/Resources/Locales/zh_TW.axaml b/src/Resources/Locales/zh_TW.axaml index be23c8e04..f7f4697b6 100644 --- a/src/Resources/Locales/zh_TW.axaml +++ b/src/Resources/Locales/zh_TW.axaml @@ -177,11 +177,18 @@ 範本名稱: 自訂動作 指令參數: - 內建參數: ${REPO} 存放庫路徑、${REMOTE} 所選的遠端存放庫或所選分支的遠端、${BRANCH} 所選的分支、${SHA} 所選的提交編號、${TAG} 所選的標籤 + 內建參數: + + ${REPO} 存放庫路徑 + ${REMOTE} 所選的遠端存放庫或所選分支的遠端 + ${BRANCH} 所選的分支。對於遠端分支,不包含遠端名稱 + ${BRANCH_FRIENDLY_NAME} 所選的分支。對於遠端分支,不包含遠端名稱 + ${SHA} 所選的提交編號 + ${TAG} 所選的標籤 + $1, $2 ... 輸入控制項中的值 可執行檔案路徑: 輸入控制項: 編輯 - 請使用 $1、$2 等變數來代表輸入控制項的值 名稱: 執行範圍: 選取的分支 diff --git a/src/ViewModels/ExecuteCustomAction.cs b/src/ViewModels/ExecuteCustomAction.cs index 837259a7c..e08bb3c09 100644 --- a/src/ViewModels/ExecuteCustomAction.cs +++ b/src/ViewModels/ExecuteCustomAction.cs @@ -182,7 +182,7 @@ private string PrepareStringByTarget(string org) return Target switch { - Models.Branch b => org.Replace("${BRANCH}", b.FriendlyName).Replace("${REMOTE}", b.Remote), + Models.Branch b => org.Replace("${BRANCH_FRIENDLY_NAME}", b.FriendlyName).Replace("${BRANCH}", b.Name).Replace("${REMOTE}", b.Remote), Models.Commit c => org.Replace("${SHA}", c.SHA), Models.Tag t => org.Replace("${TAG}", t.Name), Models.Remote r => org.Replace("${REMOTE}", r.Name), diff --git a/src/Views/Preferences.axaml b/src/Views/Preferences.axaml index 90df4323a..cdb0940ae 100644 --- a/src/Views/Preferences.axaml +++ b/src/Views/Preferences.axaml @@ -559,10 +559,10 @@ - + - + - - - - - - - - - - - - + + + + + + + + + + + + + + + diff --git a/src/Views/RepositoryConfigure.axaml b/src/Views/RepositoryConfigure.axaml index 14dc3bc12..167d6815c 100644 --- a/src/Views/RepositoryConfigure.axaml +++ b/src/Views/RepositoryConfigure.axaml @@ -388,7 +388,7 @@ - + @@ -483,19 +483,6 @@ - - - - - - - - - - - - + + + + + + + + + + + + + + + From a5afe0ad41649196b0215abaca2ac9d7b6923301 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Fri, 15 Aug 2025 08:51:04 +0000 Subject: [PATCH 135/591] doc: Update translation status and sort locale files --- TRANSLATION.md | 39 ++++++++++++++++++++++++--------------- 1 file changed, 24 insertions(+), 15 deletions(-) diff --git a/TRANSLATION.md b/TRANSLATION.md index 3c865b4d7..5305b74c0 100644 --- a/TRANSLATION.md +++ b/TRANSLATION.md @@ -6,12 +6,13 @@ This document shows the translation status of each locale file in the repository ### ![en_US](https://img.shields.io/badge/en__US-%E2%88%9A-brightgreen) -### ![de__DE](https://img.shields.io/badge/de__DE-98.15%25-yellow) +### ![de__DE](https://img.shields.io/badge/de__DE-98.03%25-yellow)
Missing keys in de_DE.axaml - Text.BranchTree.InvalidUpstream +- Text.Configure.CustomAction.Arguments.Tip - Text.Configure.CustomAction.Scope.Remote - Text.DeleteMultiTags - Text.DeleteMultiTags.DeleteFromRemotes @@ -30,9 +31,16 @@ This document shows the translation status of each locale file in the repository
-### ![es__ES](https://img.shields.io/badge/es__ES-%E2%88%9A-brightgreen) +### ![es__ES](https://img.shields.io/badge/es__ES-99.88%25-yellow) + +
+Missing keys in es_ES.axaml + +- Text.Configure.CustomAction.Arguments.Tip + +
-### ![fr__FR](https://img.shields.io/badge/fr__FR-80.46%25-yellow) +### ![fr__FR](https://img.shields.io/badge/fr__FR-80.56%25-yellow)
Missing keys in fr_FR.axaml @@ -83,7 +91,6 @@ This document shows the translation status of each locale file in the repository - Text.Configure.CustomAction.Arguments.Tip - Text.Configure.CustomAction.InputControls - Text.Configure.CustomAction.InputControls.Edit -- Text.Configure.CustomAction.InputControls.Tip - Text.Configure.CustomAction.Scope.Remote - Text.Configure.CustomAction.Scope.Tag - Text.Configure.Git.PreferredMergeMode @@ -209,7 +216,7 @@ This document shows the translation status of each locale file in the repository
-### ![it__IT](https://img.shields.io/badge/it__IT-85.66%25-yellow) +### ![it__IT](https://img.shields.io/badge/it__IT-85.76%25-yellow)
Missing keys in it_IT.axaml @@ -248,7 +255,6 @@ This document shows the translation status of each locale file in the repository - Text.Configure.CustomAction.Arguments.Tip - Text.Configure.CustomAction.InputControls - Text.Configure.CustomAction.InputControls.Edit -- Text.Configure.CustomAction.InputControls.Tip - Text.Configure.CustomAction.Scope.Remote - Text.Configure.CustomAction.Scope.Tag - Text.Configure.IssueTracker.AddSampleGerritChangeIdCommit @@ -341,7 +347,7 @@ This document shows the translation status of each locale file in the repository
-### ![ja__JP](https://img.shields.io/badge/ja__JP-80.46%25-yellow) +### ![ja__JP](https://img.shields.io/badge/ja__JP-80.56%25-yellow)
Missing keys in ja_JP.axaml @@ -393,7 +399,6 @@ This document shows the translation status of each locale file in the repository - Text.Configure.CustomAction.Arguments.Tip - Text.Configure.CustomAction.InputControls - Text.Configure.CustomAction.InputControls.Edit -- Text.Configure.CustomAction.InputControls.Tip - Text.Configure.CustomAction.Scope.Remote - Text.Configure.CustomAction.Scope.Tag - Text.Configure.Git.PreferredMergeMode @@ -518,7 +523,7 @@ This document shows the translation status of each locale file in the repository
-### ![pt__BR](https://img.shields.io/badge/pt__BR-73.64%25-red) +### ![pt__BR](https://img.shields.io/badge/pt__BR-73.73%25-red)
Missing keys in pt_BR.axaml @@ -582,7 +587,6 @@ This document shows the translation status of each locale file in the repository - Text.Configure.CustomAction.Arguments.Tip - Text.Configure.CustomAction.InputControls - Text.Configure.CustomAction.InputControls.Edit -- Text.Configure.CustomAction.InputControls.Tip - Text.Configure.CustomAction.Scope.Branch - Text.Configure.CustomAction.Scope.Remote - Text.Configure.CustomAction.Scope.Tag @@ -754,9 +758,16 @@ This document shows the translation status of each locale file in the repository
-### ![ru__RU](https://img.shields.io/badge/ru__RU-%E2%88%9A-brightgreen) +### ![ru__RU](https://img.shields.io/badge/ru__RU-99.88%25-yellow) + +
+Missing keys in ru_RU.axaml + +- Text.Configure.CustomAction.Arguments.Tip + +
-### ![ta__IN](https://img.shields.io/badge/ta__IN-80.58%25-yellow) +### ![ta__IN](https://img.shields.io/badge/ta__IN-80.67%25-yellow)
Missing keys in ta_IN.axaml @@ -808,7 +819,6 @@ This document shows the translation status of each locale file in the repository - Text.Configure.CustomAction.Arguments.Tip - Text.Configure.CustomAction.InputControls - Text.Configure.CustomAction.InputControls.Edit -- Text.Configure.CustomAction.InputControls.Tip - Text.Configure.CustomAction.Scope.Remote - Text.Configure.CustomAction.Scope.Tag - Text.Configure.Git.PreferredMergeMode @@ -932,7 +942,7 @@ This document shows the translation status of each locale file in the repository
-### ![uk__UA](https://img.shields.io/badge/uk__UA-81.73%25-yellow) +### ![uk__UA](https://img.shields.io/badge/uk__UA-81.83%25-yellow)
Missing keys in uk_UA.axaml @@ -983,7 +993,6 @@ This document shows the translation status of each locale file in the repository - Text.Configure.CustomAction.Arguments.Tip - Text.Configure.CustomAction.InputControls - Text.Configure.CustomAction.InputControls.Edit -- Text.Configure.CustomAction.InputControls.Tip - Text.Configure.CustomAction.Scope.Remote - Text.Configure.CustomAction.Scope.Tag - Text.Configure.IssueTracker.AddSampleGerritChangeIdCommit From 68716eb103d7e4ae96020aa25632dfc6bc83d676 Mon Sep 17 00:00:00 2001 From: leo Date: Fri, 15 Aug 2025 18:29:01 +0800 Subject: [PATCH 136/591] =?UTF-8?q?feature:=20supports=20to=20use=20`?= =?UTF-8?q?=E2=8C=98+H`=20to=20hide=20this=20app=20on=20macOS=20(#1716)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: leo --- src/App.Commands.cs | 14 ++++++++++++++ src/App.axaml | 3 +++ src/Resources/Locales/en_US.axaml | 2 ++ src/Resources/Locales/zh_CN.axaml | 2 ++ src/Resources/Locales/zh_TW.axaml | 2 ++ 5 files changed, 23 insertions(+) diff --git a/src/App.Commands.cs b/src/App.Commands.cs index f26919c73..a88826ac3 100644 --- a/src/App.Commands.cs +++ b/src/App.Commands.cs @@ -1,6 +1,8 @@ using System; using System.Windows.Input; + using Avalonia.Controls; +using Avalonia.Controls.ApplicationLifetimes; namespace SourceGit { @@ -53,5 +55,17 @@ public static bool IsCheckForUpdateCommandVisible else if (!string.IsNullOrEmpty(textBlock.Text)) await CopyTextAsync(textBlock.Text); }); + + public static readonly Command HideAppCommand = new Command(_ => + { + if (Current is App app && app.TryGetFeature(typeof(IActivatableLifetime)) is IActivatableLifetime lifetime) + lifetime.TryEnterBackground(); + }); + + public static readonly Command ShowAppCommand = new Command(_ => + { + if (Current is App app && app.TryGetFeature(typeof(IActivatableLifetime)) is IActivatableLifetime lifetime) + lifetime.TryLeaveBackground(); + }); } } diff --git a/src/App.axaml b/src/App.axaml index f4dc3d893..9b8b4686f 100644 --- a/src/App.axaml +++ b/src/App.axaml @@ -42,6 +42,9 @@ + + + diff --git a/src/Resources/Locales/en_US.axaml b/src/Resources/Locales/en_US.axaml index 0fa9c8473..be137b917 100644 --- a/src/Resources/Locales/en_US.axaml +++ b/src/Resources/Locales/en_US.axaml @@ -19,6 +19,8 @@ RE-GENERATE Use AI to generate commit message APPLY AS COMMIT MESSAGE + Hide SourceGit + Show All Patch Patch File: Select .patch file to apply diff --git a/src/Resources/Locales/zh_CN.axaml b/src/Resources/Locales/zh_CN.axaml index d9cc3cd1d..0f2fe026d 100644 --- a/src/Resources/Locales/zh_CN.axaml +++ b/src/Resources/Locales/zh_CN.axaml @@ -23,6 +23,8 @@ 重新生成 使用AI助手生成提交信息 应用本次生成 + 隐藏 SourceGit + 显示所有窗口 应用补丁(apply) 补丁文件 : 选择补丁文件 diff --git a/src/Resources/Locales/zh_TW.axaml b/src/Resources/Locales/zh_TW.axaml index f7f4697b6..068a2fdbe 100644 --- a/src/Resources/Locales/zh_TW.axaml +++ b/src/Resources/Locales/zh_TW.axaml @@ -23,6 +23,8 @@ 重新產生 使用 AI 產生提交訊息 套用為提交訊息 + 隱藏 SourceGit + 顯示所有 套用修補檔 (apply patch) 修補檔: 選擇修補檔 From 84eca5ece333f88bf6fac1b58863627a2c093f3b Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Fri, 15 Aug 2025 10:29:17 +0000 Subject: [PATCH 137/591] doc: Update translation status and sort locale files --- TRANSLATION.md | 36 +++++++++++++++++++++++++++--------- 1 file changed, 27 insertions(+), 9 deletions(-) diff --git a/TRANSLATION.md b/TRANSLATION.md index 5305b74c0..eadeb391a 100644 --- a/TRANSLATION.md +++ b/TRANSLATION.md @@ -6,11 +6,13 @@ This document shows the translation status of each locale file in the repository ### ![en_US](https://img.shields.io/badge/en__US-%E2%88%9A-brightgreen) -### ![de__DE](https://img.shields.io/badge/de__DE-98.03%25-yellow) +### ![de__DE](https://img.shields.io/badge/de__DE-97.81%25-yellow)
Missing keys in de_DE.axaml +- Text.App.Hide +- Text.App.ShowAll - Text.BranchTree.InvalidUpstream - Text.Configure.CustomAction.Arguments.Tip - Text.Configure.CustomAction.Scope.Remote @@ -31,16 +33,18 @@ This document shows the translation status of each locale file in the repository
-### ![es__ES](https://img.shields.io/badge/es__ES-99.88%25-yellow) +### ![es__ES](https://img.shields.io/badge/es__ES-99.65%25-yellow)
Missing keys in es_ES.axaml +- Text.App.Hide +- Text.App.ShowAll - Text.Configure.CustomAction.Arguments.Tip
-### ![fr__FR](https://img.shields.io/badge/fr__FR-80.56%25-yellow) +### ![fr__FR](https://img.shields.io/badge/fr__FR-80.37%25-yellow)
Missing keys in fr_FR.axaml @@ -48,6 +52,8 @@ This document shows the translation status of each locale file in the repository - Text.AddToIgnore - Text.AddToIgnore.Pattern - Text.AddToIgnore.Storage +- Text.App.Hide +- Text.App.ShowAll - Text.Askpass.Passphrase - Text.Avatar.Load - Text.Bisect @@ -216,7 +222,7 @@ This document shows the translation status of each locale file in the repository
-### ![it__IT](https://img.shields.io/badge/it__IT-85.76%25-yellow) +### ![it__IT](https://img.shields.io/badge/it__IT-85.57%25-yellow)
Missing keys in it_IT.axaml @@ -224,6 +230,8 @@ This document shows the translation status of each locale file in the repository - Text.AddToIgnore - Text.AddToIgnore.Pattern - Text.AddToIgnore.Storage +- Text.App.Hide +- Text.App.ShowAll - Text.Askpass.Passphrase - Text.Avatar.Load - Text.BranchCM.ResetToSelectedCommit @@ -347,7 +355,7 @@ This document shows the translation status of each locale file in the repository
-### ![ja__JP](https://img.shields.io/badge/ja__JP-80.56%25-yellow) +### ![ja__JP](https://img.shields.io/badge/ja__JP-80.37%25-yellow)
Missing keys in ja_JP.axaml @@ -355,6 +363,8 @@ This document shows the translation status of each locale file in the repository - Text.AddToIgnore - Text.AddToIgnore.Pattern - Text.AddToIgnore.Storage +- Text.App.Hide +- Text.App.ShowAll - Text.Askpass.Passphrase - Text.Avatar.Load - Text.Bisect @@ -523,7 +533,7 @@ This document shows the translation status of each locale file in the repository
-### ![pt__BR](https://img.shields.io/badge/pt__BR-73.73%25-red) +### ![pt__BR](https://img.shields.io/badge/pt__BR-73.56%25-red)
Missing keys in pt_BR.axaml @@ -533,6 +543,8 @@ This document shows the translation status of each locale file in the repository - Text.AddToIgnore.Storage - Text.AIAssistant.Regen - Text.AIAssistant.Use +- Text.App.Hide +- Text.App.ShowAll - Text.ApplyStash - Text.ApplyStash.DropAfterApply - Text.ApplyStash.RestoreIndex @@ -758,16 +770,18 @@ This document shows the translation status of each locale file in the repository
-### ![ru__RU](https://img.shields.io/badge/ru__RU-99.88%25-yellow) +### ![ru__RU](https://img.shields.io/badge/ru__RU-99.65%25-yellow)
Missing keys in ru_RU.axaml +- Text.App.Hide +- Text.App.ShowAll - Text.Configure.CustomAction.Arguments.Tip
-### ![ta__IN](https://img.shields.io/badge/ta__IN-80.67%25-yellow) +### ![ta__IN](https://img.shields.io/badge/ta__IN-80.48%25-yellow)
Missing keys in ta_IN.axaml @@ -775,6 +789,8 @@ This document shows the translation status of each locale file in the repository - Text.AddToIgnore - Text.AddToIgnore.Pattern - Text.AddToIgnore.Storage +- Text.App.Hide +- Text.App.ShowAll - Text.Askpass.Passphrase - Text.Avatar.Load - Text.Bisect @@ -942,7 +958,7 @@ This document shows the translation status of each locale file in the repository
-### ![uk__UA](https://img.shields.io/badge/uk__UA-81.83%25-yellow) +### ![uk__UA](https://img.shields.io/badge/uk__UA-81.64%25-yellow)
Missing keys in uk_UA.axaml @@ -950,6 +966,8 @@ This document shows the translation status of each locale file in the repository - Text.AddToIgnore - Text.AddToIgnore.Pattern - Text.AddToIgnore.Storage +- Text.App.Hide +- Text.App.ShowAll - Text.Askpass.Passphrase - Text.Avatar.Load - Text.Bisect From 19286942ddc5990765b20ceb98f7864d0efa62cb Mon Sep 17 00:00:00 2001 From: leo Date: Fri, 15 Aug 2025 18:33:40 +0800 Subject: [PATCH 138/591] code_style: remove unnecessary whitespaces Signed-off-by: leo --- src/App.Commands.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/App.Commands.cs b/src/App.Commands.cs index a88826ac3..194adc07c 100644 --- a/src/App.Commands.cs +++ b/src/App.Commands.cs @@ -55,13 +55,13 @@ public static bool IsCheckForUpdateCommandVisible else if (!string.IsNullOrEmpty(textBlock.Text)) await CopyTextAsync(textBlock.Text); }); - + public static readonly Command HideAppCommand = new Command(_ => { if (Current is App app && app.TryGetFeature(typeof(IActivatableLifetime)) is IActivatableLifetime lifetime) lifetime.TryEnterBackground(); }); - + public static readonly Command ShowAppCommand = new Command(_ => { if (Current is App app && app.TryGetFeature(typeof(IActivatableLifetime)) is IActivatableLifetime lifetime) From 2a9ad941783190577b28b6f091e16db01bf87a10 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Javier=20J=2E=20Mart=C3=ADnez=20M=2E?= <56406225+jjesus-dev@users.noreply.github.com> Date: Sun, 17 Aug 2025 20:33:16 -0600 Subject: [PATCH 139/591] localization: update spanish translations (#1736) Add missing translations. --- src/Resources/Locales/es_ES.axaml | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/src/Resources/Locales/es_ES.axaml b/src/Resources/Locales/es_ES.axaml index 9c9aae18a..71cd212d1 100644 --- a/src/Resources/Locales/es_ES.axaml +++ b/src/Resources/Locales/es_ES.axaml @@ -23,6 +23,8 @@ RE-GENERAR Usar OpenAI para generar mensaje de commit APLICAR COMO MENSAJE DE COMMIT + Ocultar SourceGit + Mostrar Todo Aplicar Parche Archivo del Parche: Seleccionar archivo .patch para aplicar @@ -177,6 +179,15 @@ Nombre de la Plantilla: ACCIÓN PERSONALIZADA Argumentos: + Parámetros incorporados: + + ${REPO} Ruta del repositorio + ${REMOTE} Remoto seleccionado o Remoto de la rama seleccionada + ${BRANCH} Rama seleccionada, sin la parte ${REMOTE} para ramas remotas + ${BRANCH_FRIENDLY_NAME} Nombre amigable de la rama seleccionada, contiene la parte ${REMOTE} para ramas remotas + ${SHA} Hash del commit seleccionado + ${TAG} Etiqueta seleccionada + $1, $2 ... Valores de control de entrada Archivo Ejecutable: Controles de entrada: Editar From f0540a6b53f603d540c9eb4ba9d2d3ff0b77d1f3 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Mon, 18 Aug 2025 02:33:29 +0000 Subject: [PATCH 140/591] doc: Update translation status and sort locale files --- TRANSLATION.md | 11 +---------- 1 file changed, 1 insertion(+), 10 deletions(-) diff --git a/TRANSLATION.md b/TRANSLATION.md index eadeb391a..c298386d5 100644 --- a/TRANSLATION.md +++ b/TRANSLATION.md @@ -33,16 +33,7 @@ This document shows the translation status of each locale file in the repository
-### ![es__ES](https://img.shields.io/badge/es__ES-99.65%25-yellow) - -
-Missing keys in es_ES.axaml - -- Text.App.Hide -- Text.App.ShowAll -- Text.Configure.CustomAction.Arguments.Tip - -
+### ![es__ES](https://img.shields.io/badge/es__ES-%E2%88%9A-brightgreen) ### ![fr__FR](https://img.shields.io/badge/fr__FR-80.37%25-yellow) From c363a783d2e80eb8f4667f50c427786342af4728 Mon Sep 17 00:00:00 2001 From: Sina Hinderks Date: Mon, 18 Aug 2025 04:33:32 +0200 Subject: [PATCH 141/591] localization: update German translations (#1737) --- src/Resources/Locales/de_DE.axaml | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/src/Resources/Locales/de_DE.axaml b/src/Resources/Locales/de_DE.axaml index 89b377aa6..183310566 100644 --- a/src/Resources/Locales/de_DE.axaml +++ b/src/Resources/Locales/de_DE.axaml @@ -23,6 +23,8 @@ Neu generieren Verwende OpenAI, um Commit-Nachrichten zu generieren Als Commit-Nachricht verwenden + SourceGit minimieren + Alles anzeigen Patch Patch-Datei: Wähle die anzuwendende .patch-Datei @@ -74,6 +76,7 @@ Setze ${0}$ zurück auf ${1}$... Setze verfolgten Branch... Branch Vergleich + Ungültig REMOTE VERFOLGT URL @@ -176,6 +179,16 @@ Template Name: BENUTZERDEFINIERTE AKTION Argumente: + Vordefinierte Parameter: + + ${REPO} Repository-Pfad + ${REMOTE} selektierter Remote oder selektierter Branch-Remote + ${BRANCH} selektierter Branch, ohne ${REMOTE}-Teil für Remote-Branches + ${BRANCH_FRIENDLY_NAME} Freundlicher Name des selektierten Branches, enthält ${REMOTE}-Teil für Remote-Branches + ${SHA} Hash des selektierten Commits + ${TAG} selektiertes Tag + $1, $2 ... Werte der Eingabe-Steuerelemente + Ausführbare Datei: Eingabe-Steuerelemente: Bearbeiten @@ -183,6 +196,7 @@ Geltungsbereich: Branch Commit + Remote Repository Tag Auf Beenden der Aktion warten @@ -280,6 +294,9 @@ Auch Remote-Branch ${0}$ löschen Mehrere Branches löschen Du versuchst mehrere Branches auf einmal zu löschen. Kontrolliere noch einmal vor dem Fortfahren! + Mehrere Tags löschen + von Remotes löschen + Du versuchst, mehrere Tags gleichzeitig zu löschen. Sei dir sicher, das doppelt zu prüfen, bevor du loslegst! Remote löschen Remote: Pfad: @@ -524,6 +541,7 @@ DARSTELLUNG Standardschriftart Editor Tab Breite + Aktiviere kompakte Ordner im Änderungsbaum Schriftgröße Standard Texteditor @@ -613,6 +631,7 @@ Repository URL: Remote Git Repository URL URL kopieren + Benutzerdefinierte Aktion Löschen... Bearbeiten... Fetch @@ -638,6 +657,7 @@ WEITER Benutzerdefinierte Aktionen Keine benutzerdefinierten Aktionen + Dashboard Alle Änderungen verwerfen Öffne im Datei-Browser Suche Branches/Tags/Submodule @@ -652,6 +672,7 @@ Commit Zeitpunkt Topologie LOKALE BRANCHES + Mehr Optionen... Zum HEAD wechseln Erstelle Branch BENACHRICHTIGUNGEN LÖSCHEN @@ -779,8 +800,15 @@ Update URL OK + TAGGER + TIME + Nachricht + Name + Tagger + Name des Tags kopieren Benutzerdefinierte Aktion Lösche ${0}$... + Selektierte {0} Tags löschen... Merge ${0}$ in ${1}$ hinein... Pushe ${0}$... Submodule aktualisieren From 179ae2651980570eeaf691c29cce4d0ad5fa8b96 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Mon, 18 Aug 2025 02:33:41 +0000 Subject: [PATCH 142/591] doc: Update translation status and sort locale files --- TRANSLATION.md | 27 +-------------------------- 1 file changed, 1 insertion(+), 26 deletions(-) diff --git a/TRANSLATION.md b/TRANSLATION.md index c298386d5..13efcafaa 100644 --- a/TRANSLATION.md +++ b/TRANSLATION.md @@ -6,32 +6,7 @@ This document shows the translation status of each locale file in the repository ### ![en_US](https://img.shields.io/badge/en__US-%E2%88%9A-brightgreen) -### ![de__DE](https://img.shields.io/badge/de__DE-97.81%25-yellow) - -
-Missing keys in de_DE.axaml - -- Text.App.Hide -- Text.App.ShowAll -- Text.BranchTree.InvalidUpstream -- Text.Configure.CustomAction.Arguments.Tip -- Text.Configure.CustomAction.Scope.Remote -- Text.DeleteMultiTags -- Text.DeleteMultiTags.DeleteFromRemotes -- Text.DeleteMultiTags.Tip -- Text.Preferences.Appearance.EnableCompactFolders -- Text.RemoteCM.CustomAction -- Text.Repository.Dashboard -- Text.Repository.MoreOptions -- Text.Tag.Tagger -- Text.Tag.Time -- Text.TagCM.Copy.Message -- Text.TagCM.Copy.Name -- Text.TagCM.Copy.Tagger -- Text.TagCM.CopyName -- Text.TagCM.DeleteMultiple - -
+### ![de__DE](https://img.shields.io/badge/de__DE-%E2%88%9A-brightgreen) ### ![es__ES](https://img.shields.io/badge/es__ES-%E2%88%9A-brightgreen) From 5008bf6bff6a25a31184f1d65a077eec7e3a0d16 Mon Sep 17 00:00:00 2001 From: Sina Hinderks Date: Mon, 18 Aug 2025 04:33:52 +0200 Subject: [PATCH 143/591] localization: fix typos for English translations (#1738) `Friendly` and `delimiter` had typos. --- src/Resources/Locales/en_US.axaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Resources/Locales/en_US.axaml b/src/Resources/Locales/en_US.axaml index be137b917..794ecab64 100644 --- a/src/Resources/Locales/en_US.axaml +++ b/src/Resources/Locales/en_US.axaml @@ -180,7 +180,7 @@ ${REPO} Repository's path ${REMOTE} Selected remote or selected branch's remote ${BRANCH} Selected branch, without ${REMOTE} part for remote branches - ${BRANCH_FRIENDLY_NAME} Firendly name of selected branch, contains ${REMOTE} part for remote branches + ${BRANCH_FRIENDLY_NAME} Friendly name of selected branch, contains ${REMOTE} part for remote branches ${SHA} Selected commit's hash ${TAG} Selected tag $1, $2 ... Input control values @@ -232,7 +232,7 @@ Is Folder: Label: Options: - Use '|' as delimitter for options + Use '|' as delimiter for options Type: Workspaces Color From 8a0c2f11ef93af61c9c555c11c59ac36197bf255 Mon Sep 17 00:00:00 2001 From: Sina Hinderks Date: Mon, 18 Aug 2025 04:34:10 +0200 Subject: [PATCH 144/591] localization: fix typos in German translations (#1739) --- src/Resources/Locales/de_DE.axaml | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/src/Resources/Locales/de_DE.axaml b/src/Resources/Locales/de_DE.axaml index 183310566..689567a12 100644 --- a/src/Resources/Locales/de_DE.axaml +++ b/src/Resources/Locales/de_DE.axaml @@ -107,7 +107,7 @@ Quelle an Commit-Nachricht anhängen Commit(s): Alle Änderungen committen - Hautplinie: + Hauptlinie: Normalerweise ist es nicht möglich einen Merge zu cherry-picken, da unklar ist welche Seite des Merges die Hauptlinie ist. Diese Option ermöglicht es die Änderungen relativ zum ausgewählten Vorgänger zu wiederholen. Stashes löschen Du versuchst alle Stashes zu löschen. Möchtest du wirklich fortfahren? @@ -132,7 +132,7 @@ SHA Betreff Benutzerdefinierte Aktion - Interakives Rebase + Interaktives Rebase Entfernen... Bearbeiten... Fixup in den Vorgänger... @@ -224,7 +224,7 @@ Verwende bitte $1, $2 um auf Regex-Gruppenwerte zuzugreifen. OPEN AI Bevorzugter Service: - Der ausgewählte 'Bevorzugte Service' wird nur in diesem Repository gesetzt und verwendet. Wenn keiner gesetzt ist und mehrere Servies verfügbar sind wird ein Kontextmenü zur Auswahl angezeigt. + Der ausgewählte 'Bevorzugte Service' wird nur in diesem Repository gesetzt und verwendet. Wenn keiner gesetzt ist und mehrere Services verfügbar sind wird ein Kontextmenü zur Auswahl angezeigt. HTTP Proxy HTTP Proxy für dieses Repository Benutzername @@ -420,7 +420,7 @@ Prune Führt `git lfs prune` aus um alte LFS Dateien vom lokalen Speicher zu löschen Pull - Führt `git lfs pull` aus um alle Git LFS Dasteien für aktuellen Ref & Checkout herunterzuladen + Führt `git lfs pull` aus um alle Git LFS-Dateien für aktuellen Ref & Checkout herunterzuladen LFS Objekte pullen Push Pushe große Dateien in der Warteschlange zum Git LFS Endpunkt @@ -524,7 +524,7 @@ Vor {0} Stunden Gerade eben Letzter Monat - Leztes Jahr + Letztes Jahr Vor {0} Minuten Vor {0} Monaten Vor {0} Jahren @@ -760,7 +760,7 @@ Optional. Informationen zu diesem Stash Modus: Nur gestagte Änderungen - Gestagte und unstagte Änderungen der ausgewähleten Datei(en) werden gestasht!!! + Gestagte und ungestagte Änderungen der ausgewählten Datei(en) werden gestasht!!! Lokale Änderungen stashen Anwenden Kopiere Nachricht @@ -839,7 +839,7 @@ Änderungen Git Ignore Ignoriere alle *{0} Dateien - Ignoriere *{0} Datein im selben Ordner + Ignoriere *{0} Dateien im selben Ordner Ignoriere nicht-verfolgte Dateien in diesem Ordner Ignoriere nur diese Datei Amend From 79cf73e8af0b686b869479beb306ca364e18bf2b Mon Sep 17 00:00:00 2001 From: Sina Hinderks Date: Mon, 18 Aug 2025 04:36:17 +0200 Subject: [PATCH 145/591] fix: Open Terminal on Welcome Page crashed (Windows) (#1740) Trying to open the Windows Terminal on the Welcome Page crashed with a `NullReferenceException`. The code for Windows after the fix is similar to the corresponding code for Linux. --- src/Native/Windows.cs | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/src/Native/Windows.cs b/src/Native/Windows.cs index 5ba027bc1..d8b7286e9 100644 --- a/src/Native/Windows.cs +++ b/src/Native/Windows.cs @@ -203,19 +203,23 @@ public void OpenBrowser(string url) public void OpenTerminal(string workdir) { - if (!File.Exists(OS.ShellOrTerminal)) + var home = Environment.GetFolderPath(Environment.SpecialFolder.UserProfile); + var cwd = string.IsNullOrEmpty(workdir) ? home : workdir; + var terminal = OS.ShellOrTerminal; + + if (!File.Exists(terminal)) { App.RaiseException(workdir, "Terminal is not specified! Please confirm that the correct shell/terminal has been configured."); return; } var startInfo = new ProcessStartInfo(); - startInfo.WorkingDirectory = workdir; - startInfo.FileName = OS.ShellOrTerminal; + startInfo.WorkingDirectory = cwd; + startInfo.FileName = terminal; // Directly launching `Windows Terminal` need to specify the `-d` parameter - if (OS.ShellOrTerminal.EndsWith("wt.exe", StringComparison.OrdinalIgnoreCase)) - startInfo.Arguments = $"-d {workdir.Quoted()}"; + if (terminal.EndsWith("wt.exe", StringComparison.OrdinalIgnoreCase)) + startInfo.Arguments = $"-d {cwd.Quoted()}"; Process.Start(startInfo); } From 0526ce2777ce22086a1498202835457662905af1 Mon Sep 17 00:00:00 2001 From: leo Date: Mon, 18 Aug 2025 10:50:18 +0800 Subject: [PATCH 146/591] project: upgrade `AvaloniaUI` to `11.3.4` Signed-off-by: leo --- src/SourceGit.csproj | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/SourceGit.csproj b/src/SourceGit.csproj index 7b2f998c8..7c55d9301 100644 --- a/src/SourceGit.csproj +++ b/src/SourceGit.csproj @@ -39,12 +39,12 @@ - - - - - - + + + + + + From 6f7c695058bec453e6fe62a15ad24cde6e008366 Mon Sep 17 00:00:00 2001 From: leo Date: Mon, 18 Aug 2025 10:52:46 +0800 Subject: [PATCH 147/591] version: Release 2025.31 Signed-off-by: leo --- VERSION | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/VERSION b/VERSION index 9b98ca385..154883650 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -2025.30 \ No newline at end of file +2025.31 \ No newline at end of file From 5c33198fc71c1ee163f174ffb0b6b22096da2d1e Mon Sep 17 00:00:00 2001 From: leo Date: Mon, 18 Aug 2025 11:53:58 +0800 Subject: [PATCH 148/591] enhance: remove unnecessary code for moving window on macOS This has been fix in`AvaloniaUI`. See https://github.com/AvaloniaUI/Avalonia/pull/19447 Signed-off-by: leo --- src/Views/ChromelessWindow.cs | 5 ----- 1 file changed, 5 deletions(-) diff --git a/src/Views/ChromelessWindow.cs b/src/Views/ChromelessWindow.cs index 1abb06c97..24c652dcf 100644 --- a/src/Views/ChromelessWindow.cs +++ b/src/Views/ChromelessWindow.cs @@ -30,12 +30,7 @@ public ChromelessWindow() public void BeginMoveWindow(object _, PointerPressedEventArgs e) { if (e.ClickCount == 1) - { - if (OperatingSystem.IsMacOS()) - e.Pointer.Capture(this); - BeginMoveDrag(e); - } e.Handled = true; } From 6511d15c01ca77fb7e477428e058397421dc4e4e Mon Sep 17 00:00:00 2001 From: leo Date: Mon, 18 Aug 2025 18:12:13 +0800 Subject: [PATCH 149/591] refactor: rewrite text diff view - Move some data-only code from `Views` to `ViewModels` - Remove unnecessary attributes - This commit also contains a feature request #1722 Signed-off-by: leo --- src/Commands/Diff.cs | 6 +- src/Models/DiffResult.cs | 7 +- src/ViewModels/BlockNavigation.cs | 44 ++- src/ViewModels/DiffContext.cs | 115 +++++-- src/ViewModels/TextDiffContext.cs | 291 +++++++++++++++++ src/ViewModels/TwoSideTextDiff.cs | 109 ------- src/Views/DiffView.axaml | 40 ++- src/Views/DiffView.axaml.cs | 40 +-- src/Views/TextDiffView.axaml | 36 +- src/Views/TextDiffView.axaml.cs | 525 +++++++----------------------- 10 files changed, 586 insertions(+), 627 deletions(-) create mode 100644 src/ViewModels/TextDiffContext.cs delete mode 100644 src/ViewModels/TwoSideTextDiff.cs diff --git a/src/Commands/Diff.cs b/src/Commands/Diff.cs index 3eae1b54b..ee5433acc 100644 --- a/src/Commands/Diff.cs +++ b/src/Commands/Diff.cs @@ -20,11 +20,7 @@ public partial class Diff : Command public Diff(string repo, Models.DiffOption opt, int unified, bool ignoreWhitespace) { - _result.TextDiff = new Models.TextDiff() - { - Repo = repo, - Option = opt, - }; + _result.TextDiff = new Models.TextDiff() { Option = opt }; WorkingDirectory = repo; Context = repo; diff --git a/src/Models/DiffResult.cs b/src/Models/DiffResult.cs index d80fa136a..9b21e6c96 100644 --- a/src/Models/DiffResult.cs +++ b/src/Models/DiffResult.cs @@ -1,8 +1,6 @@ using System.Collections.Generic; using System.IO; using System.Text.RegularExpressions; - -using Avalonia; using Avalonia.Media.Imaging; namespace SourceGit.Models @@ -62,13 +60,10 @@ public bool IsInRange(int idx) public partial class TextDiff { public string File { get; set; } = string.Empty; + public DiffOption Option { get; set; } = null; public List Lines { get; set; } = new List(); - public Vector ScrollOffset { get; set; } = Vector.Zero; public int MaxLineNumber = 0; - public string Repo { get; set; } = null; - public DiffOption Option { get; set; } = null; - public TextDiffSelection MakeSelection(int startLine, int endLine, bool isCombined, bool isOldSide) { var rs = new TextDiffSelection(); diff --git a/src/ViewModels/BlockNavigation.cs b/src/ViewModels/BlockNavigation.cs index 4a51244cb..bf5e23145 100644 --- a/src/ViewModels/BlockNavigation.cs +++ b/src/ViewModels/BlockNavigation.cs @@ -48,17 +48,11 @@ public string Indicator } } - public BlockNavigation(object context) + public BlockNavigation(List lines) { Blocks.Clear(); Current = -1; - var lines = new List(); - if (context is Models.TextDiff combined) - lines = combined.Lines; - else if (context is TwoSideTextDiff twoSide) - lines = twoSide.Old; - if (lines.Count == 0) return; @@ -96,7 +90,10 @@ public BlockNavigation(object context) public Block GetCurrentBlock() { - return (_current >= 0 && _current < Blocks.Count) ? Blocks[_current] : null; + if (_current >= 0 && _current < Blocks.Count) + return Blocks[_current]; + + return Blocks.Count > 0 ? Blocks[0] : null; } public Block GotoFirst() @@ -105,6 +102,7 @@ public Block GotoFirst() return null; Current = 0; + OnPropertyChanged(nameof(Indicator)); return Blocks[_current]; } @@ -117,6 +115,8 @@ public Block GotoPrev() Current = 0; else if (_current > 0) Current = _current - 1; + + OnPropertyChanged(nameof(Indicator)); return Blocks[_current]; } @@ -127,6 +127,8 @@ public Block GotoNext() if (_current < Blocks.Count - 1) Current = _current + 1; + + OnPropertyChanged(nameof(Indicator)); return Blocks[_current]; } @@ -136,9 +138,35 @@ public Block GotoLast() return null; Current = Blocks.Count - 1; + OnPropertyChanged(nameof(Indicator)); return Blocks[_current]; } + public void AutoUpdate(int start, int end) + { + if (_current >= 0 && _current < Blocks.Count) + { + var block = Blocks[_current]; + if ((block.Start >= start && block.Start <= end) || + (block.End >= start && block.End <= end) || + (block.Start <= start && block.End >= end)) + return; + } + + for (var i = 0; i < Blocks.Count; i++) + { + var block = Blocks[i]; + if ((block.Start >= start && block.Start <= end) || + (block.End >= start && block.End <= end) || + (block.Start <= start && block.End >= end)) + { + Current = i; + OnPropertyChanged(nameof(Indicator)); + return; + } + } + } + private int _current = -1; } } diff --git a/src/ViewModels/DiffContext.cs b/src/ViewModels/DiffContext.cs index e5ee18ca3..f499654b6 100644 --- a/src/ViewModels/DiffContext.cs +++ b/src/ViewModels/DiffContext.cs @@ -1,9 +1,7 @@ using System; using System.IO; using System.Threading.Tasks; - using Avalonia.Threading; - using CommunityToolkit.Mvvm.ComponentModel; namespace SourceGit.ViewModels @@ -24,7 +22,58 @@ public bool IgnoreWhitespace { Preferences.Instance.IgnoreWhitespaceChangesInDiff = value; OnPropertyChanged(); - LoadDiffContent(); + + if (_isTextDiff) + LoadContent(); + } + } + } + + public bool ShowEntireFile + { + get => Preferences.Instance.UseFullTextDiff; + set + { + if (value != Preferences.Instance.UseFullTextDiff) + { + Preferences.Instance.UseFullTextDiff = value; + OnPropertyChanged(); + + if (Content is TextDiffContext ctx) + { + ctx.Data.File = string.Empty; // Just to ignore both previous `ScrollOffset` and `BlockNavigation` + LoadContent(); + } + } + } + } + + public bool UseBlockNavigation + { + get => Preferences.Instance.UseBlockNavigationInDiffView; + set + { + if (value != Preferences.Instance.UseBlockNavigationInDiffView) + { + Preferences.Instance.UseBlockNavigationInDiffView = value; + OnPropertyChanged(); + (Content as TextDiffContext)?.ResetBlockNavigation(value); + } + } + } + + public bool UseSideBySide + { + get => Preferences.Instance.UseSideBySideDiff; + set + { + if (value != Preferences.Instance.UseSideBySideDiff) + { + Preferences.Instance.UseSideBySideDiff = value; + OnPropertyChanged(); + + if (Content is TextDiffContext ctx && ctx.IsSideBySide() != value) + Content = ctx.SwitchMode(); } } } @@ -72,25 +121,19 @@ public DiffContext(string repo, Models.DiffOption option, DiffContext previous = else Title = $"{_option.OrgPath} → {_option.Path}"; - LoadDiffContent(); - } - - public void ToggleFullTextDiff() - { - Preferences.Instance.UseFullTextDiff = !Preferences.Instance.UseFullTextDiff; - LoadDiffContent(); + LoadContent(); } public void IncrUnified() { UnifiedLines = _unifiedLines + 1; - LoadDiffContent(); + LoadContent(); } public void DecrUnified() { UnifiedLines = Math.Max(4, _unifiedLines - 1); - LoadDiffContent(); + LoadContent(); } public void OpenExternalMergeTool() @@ -98,7 +141,29 @@ public void OpenExternalMergeTool() new Commands.DiffTool(_repo, _option).Open(); } - private void LoadDiffContent() + public void CheckSettings() + { + if (Content is TextDiffContext ctx) + { + if ((ShowEntireFile && _info.UnifiedLines != _entireFileLine) || + (!ShowEntireFile && _info.UnifiedLines == _entireFileLine) || + (IgnoreWhitespace != _info.IgnoreWhitespace)) + { + LoadContent(); + return; + } + + if (ctx.IsSideBySide() != UseSideBySide) + { + ctx = ctx.SwitchMode(); + Content = ctx; + } + + ctx.ResetBlockNavigation(UseBlockNavigation); + } + } + + private void LoadContent() { if (_option.Path.EndsWith('/')) { @@ -109,7 +174,7 @@ private void LoadDiffContent() Task.Run(async () => { - var numLines = Preferences.Instance.UseFullTextDiff ? 999999999 : _unifiedLines; + var numLines = Preferences.Instance.UseFullTextDiff ? _entireFileLine : _unifiedLines; var ignoreWhitespace = Preferences.Instance.IgnoreWhitespaceChangesInDiff; var latest = await new Commands.Diff(_repo, _option, numLines, ignoreWhitespace) @@ -228,12 +293,23 @@ private void LoadDiffContent() Dispatcher.UIThread.Post(() => { - if (_content is Models.TextDiff old && rs is Models.TextDiff cur && old.File == cur.File) - cur.ScrollOffset = old.ScrollOffset; - FileModeChange = latest.FileModeChange; - Content = rs; - IsTextDiff = rs is Models.TextDiff; + + if (rs is Models.TextDiff cur) + { + IsTextDiff = true; + + var hasBlockNavigation = Preferences.Instance.UseBlockNavigationInDiffView; + if (Preferences.Instance.UseSideBySideDiff) + Content = new TwoSideTextDiff(cur, hasBlockNavigation, _content as TwoSideTextDiff); + else + Content = new CombinedTextDiff(cur, hasBlockNavigation, _content as CombinedTextDiff); + } + else + { + IsTextDiff = false; + Content = rs; + } }); }); } @@ -279,6 +355,7 @@ public bool IsSame(Info other) } } + private readonly int _entireFileLine = 999999999; private readonly string _repo; private readonly Models.DiffOption _option = null; private string _fileModeChange = string.Empty; diff --git a/src/ViewModels/TextDiffContext.cs b/src/ViewModels/TextDiffContext.cs new file mode 100644 index 000000000..97aed9d33 --- /dev/null +++ b/src/ViewModels/TextDiffContext.cs @@ -0,0 +1,291 @@ +using System; +using System.Collections.Generic; +using Avalonia; +using CommunityToolkit.Mvvm.ComponentModel; + +namespace SourceGit.ViewModels +{ + public record TextDiffSelectedChunk(double y, double h, int start, int end, bool combined, bool isOldSide) + { + public double Y { get; set; } = y; + public double Height { get; set; } = h; + public int StartIdx { get; set; } = start; + public int EndIdx { get; set; } = end; + public bool Combined { get; set; } = combined; + public bool IsOldSide { get; set; } = isOldSide; + + public static bool IsChanged(TextDiffSelectedChunk oldValue, TextDiffSelectedChunk newValue) + { + if (newValue == null) + return oldValue != null; + + if (oldValue == null) + return true; + + return Math.Abs(newValue.Y - oldValue.Y) > 0.001 || + Math.Abs(newValue.Height - oldValue.Height) > 0.001 || + newValue.StartIdx != oldValue.StartIdx || + newValue.EndIdx != oldValue.EndIdx || + newValue.Combined != oldValue.Combined || + newValue.IsOldSide != oldValue.IsOldSide; + } + } + + public record TextDiffDisplayRange(int start, int end) + { + public int Start { get; set; } = start; + public int End { get; set; } = end; + } + + public class TextDiffContext : ObservableObject + { + public Models.TextDiff Data => _data; + public string File => _data.File; + public bool IsUnstaged => _data.Option.IsUnstaged; + public bool EnableChunkOption => _data.Option.WorkingCopyChange != null; + + public Vector ScrollOffset + { + get => _scrollOffset; + set => SetProperty(ref _scrollOffset, value); + } + + public BlockNavigation BlockNavigation + { + get => _blockNavigation; + set => SetProperty(ref _blockNavigation, value); + } + + public TextDiffDisplayRange DisplayRange + { + get => _displayRange; + set => SetProperty(ref _displayRange, value); + } + + public TextDiffSelectedChunk SelectedChunk + { + get => _selectedChunk; + set => SetProperty(ref _selectedChunk, value); + } + + public void ResetBlockNavigation(bool enabled) + { + if (!enabled) + BlockNavigation = null; + else if (_blockNavigation == null) + BlockNavigation = CreateBlockNavigation(); + } + + public (int, int) FindRangeByIndex(List lines, int lineIdx) + { + var startIdx = -1; + var endIdx = -1; + + var normalLineCount = 0; + var modifiedLineCount = 0; + + for (int i = lineIdx; i >= 0; i--) + { + var line = lines[i]; + if (line.Type == Models.TextDiffLineType.Indicator) + { + startIdx = i; + break; + } + + if (line.Type == Models.TextDiffLineType.Normal) + { + normalLineCount++; + if (normalLineCount >= 2) + { + startIdx = i; + break; + } + } + else + { + normalLineCount = 0; + modifiedLineCount++; + } + } + + normalLineCount = lines[lineIdx].Type == Models.TextDiffLineType.Normal ? 1 : 0; + for (int i = lineIdx + 1; i < lines.Count; i++) + { + var line = lines[i]; + if (line.Type == Models.TextDiffLineType.Indicator) + { + endIdx = i; + break; + } + + if (line.Type == Models.TextDiffLineType.Normal) + { + normalLineCount++; + if (normalLineCount >= 2) + { + endIdx = i; + break; + } + } + else + { + normalLineCount = 0; + modifiedLineCount++; + } + } + + if (endIdx == -1) + endIdx = lines.Count - 1; + + return modifiedLineCount > 0 ? (startIdx, endIdx) : (-1, -1); + } + + public virtual bool IsSideBySide() + { + return false; + } + + public virtual TextDiffContext SwitchMode() + { + return null; + } + + public virtual BlockNavigation CreateBlockNavigation() + { + return new BlockNavigation(_data.Lines); + } + + protected Models.TextDiff _data = null; + protected Vector _scrollOffset = Vector.Zero; + protected BlockNavigation _blockNavigation = null; + protected TextDiffDisplayRange _displayRange = null; + protected TextDiffSelectedChunk _selectedChunk = null; + } + + public class CombinedTextDiff : TextDiffContext + { + public CombinedTextDiff(Models.TextDiff diff, bool hasBlockNavigation, CombinedTextDiff previous = null) + { + _data = diff; + + if (previous != null && previous.File.Equals(File, StringComparison.Ordinal)) + _scrollOffset = previous.ScrollOffset; + + if (hasBlockNavigation) + _blockNavigation = CreateBlockNavigation(); + } + + public override TextDiffContext SwitchMode() + { + return new TwoSideTextDiff(_data, _blockNavigation != null); + } + } + + public class TwoSideTextDiff : TextDiffContext + { + public List Old { get; } = new List(); + public List New { get; } = new List(); + + public TwoSideTextDiff(Models.TextDiff diff, bool hasBlockNavigation, TwoSideTextDiff previous = null) + { + _data = diff; + + foreach (var line in diff.Lines) + { + switch (line.Type) + { + case Models.TextDiffLineType.Added: + New.Add(line); + break; + case Models.TextDiffLineType.Deleted: + Old.Add(line); + break; + default: + FillEmptyLines(); + Old.Add(line); + New.Add(line); + break; + } + } + + FillEmptyLines(); + + if (previous != null && previous.File.Equals(File, StringComparison.Ordinal)) + _scrollOffset = previous._scrollOffset; + + if (hasBlockNavigation) + _blockNavigation = CreateBlockNavigation(); + } + + public override bool IsSideBySide() + { + return true; + } + + public override TextDiffContext SwitchMode() + { + return new CombinedTextDiff(_data, _blockNavigation != null); + } + + public override BlockNavigation CreateBlockNavigation() + { + return new BlockNavigation(Old); + } + + public void ConvertsToCombinedRange(ref int startLine, ref int endLine, bool isOldSide) + { + endLine = Math.Min(endLine, _data.Lines.Count - 1); + + var oneSide = isOldSide ? Old : New; + var firstContentLine = -1; + for (int i = startLine; i <= endLine; i++) + { + var line = oneSide[i]; + if (line.Type != Models.TextDiffLineType.None) + { + firstContentLine = i; + break; + } + } + + if (firstContentLine < 0) + return; + + var endContentLine = -1; + for (int i = Math.Min(endLine, oneSide.Count - 1); i >= startLine; i--) + { + var line = oneSide[i]; + if (line.Type != Models.TextDiffLineType.None) + { + endContentLine = i; + break; + } + } + + if (endContentLine < 0) + return; + + var firstContent = oneSide[firstContentLine]; + var endContent = oneSide[endContentLine]; + startLine = _data.Lines.IndexOf(firstContent); + endLine = _data.Lines.IndexOf(endContent); + } + + private void FillEmptyLines() + { + if (Old.Count < New.Count) + { + int diff = New.Count - Old.Count; + for (int i = 0; i < diff; i++) + Old.Add(new Models.TextDiffLine()); + } + else if (Old.Count > New.Count) + { + int diff = Old.Count - New.Count; + for (int i = 0; i < diff; i++) + New.Add(new Models.TextDiffLine()); + } + } + } +} diff --git a/src/ViewModels/TwoSideTextDiff.cs b/src/ViewModels/TwoSideTextDiff.cs deleted file mode 100644 index 3fb1e63b3..000000000 --- a/src/ViewModels/TwoSideTextDiff.cs +++ /dev/null @@ -1,109 +0,0 @@ -using System; -using System.Collections.Generic; - -using Avalonia; - -using CommunityToolkit.Mvvm.ComponentModel; - -namespace SourceGit.ViewModels -{ - public class TwoSideTextDiff : ObservableObject - { - public string File { get; set; } - public List Old { get; set; } = new List(); - public List New { get; set; } = new List(); - public int MaxLineNumber = 0; - - public Vector SyncScrollOffset - { - get => _syncScrollOffset; - set => SetProperty(ref _syncScrollOffset, value); - } - - public TwoSideTextDiff(Models.TextDiff diff, TwoSideTextDiff previous = null) - { - File = diff.File; - MaxLineNumber = diff.MaxLineNumber; - - foreach (var line in diff.Lines) - { - switch (line.Type) - { - case Models.TextDiffLineType.Added: - New.Add(line); - break; - case Models.TextDiffLineType.Deleted: - Old.Add(line); - break; - default: - FillEmptyLines(); - Old.Add(line); - New.Add(line); - break; - } - } - - FillEmptyLines(); - - if (previous != null && previous.File == File) - _syncScrollOffset = previous._syncScrollOffset; - } - - public void ConvertsToCombinedRange(Models.TextDiff combined, ref int startLine, ref int endLine, bool isOldSide) - { - endLine = Math.Min(endLine, combined.Lines.Count - 1); - - var oneSide = isOldSide ? Old : New; - var firstContentLine = -1; - for (int i = startLine; i <= endLine; i++) - { - var line = oneSide[i]; - if (line.Type != Models.TextDiffLineType.None) - { - firstContentLine = i; - break; - } - } - - if (firstContentLine < 0) - return; - - var endContentLine = -1; - for (int i = Math.Min(endLine, oneSide.Count - 1); i >= startLine; i--) - { - var line = oneSide[i]; - if (line.Type != Models.TextDiffLineType.None) - { - endContentLine = i; - break; - } - } - - if (endContentLine < 0) - return; - - var firstContent = oneSide[firstContentLine]; - var endContent = oneSide[endContentLine]; - startLine = combined.Lines.IndexOf(firstContent); - endLine = combined.Lines.IndexOf(endContent); - } - - private void FillEmptyLines() - { - if (Old.Count < New.Count) - { - int diff = New.Count - Old.Count; - for (int i = 0; i < diff; i++) - Old.Add(new Models.TextDiffLine()); - } - else if (Old.Count > New.Count) - { - int diff = Old.Count - New.Count; - for (int i = 0; i < diff; i++) - New.Add(new Models.TextDiffLine()); - } - } - - private Vector _syncScrollOffset = Vector.Zero; - } -} diff --git a/src/Views/DiffView.axaml b/src/Views/DiffView.axaml index ee8070f9d..25a6dc87f 100644 --- a/src/Views/DiffView.axaml +++ b/src/Views/DiffView.axaml @@ -48,7 +48,7 @@ - + @@ -73,11 +73,20 @@ - + - + + + + + + + + + + @@ -142,7 +149,7 @@ - + @@ -150,8 +157,7 @@ @@ -172,8 +178,8 @@ ToolTip.Tip="{DynamicResource Text.Diff.ToggleWordWrap}"> - - + + @@ -197,7 +203,7 @@ @@ -361,10 +367,8 @@ - - + + diff --git a/src/Views/DiffView.axaml.cs b/src/Views/DiffView.axaml.cs index 53fe08030..b67a6f2df 100644 --- a/src/Views/DiffView.axaml.cs +++ b/src/Views/DiffView.axaml.cs @@ -1,4 +1,3 @@ -using Avalonia; using Avalonia.Controls; using Avalonia.Interactivity; using Avalonia.VisualTree; @@ -12,50 +11,35 @@ public DiffView() InitializeComponent(); } + protected override void OnLoaded(RoutedEventArgs e) + { + base.OnLoaded(e); + + if (DataContext is ViewModels.DiffContext vm) + vm.CheckSettings(); + } + private void OnGotoFirstChange(object _, RoutedEventArgs e) { - this.FindDescendantOfType()?.GotoFirstChange(); + this.FindDescendantOfType()?.GotoFirstChange(); e.Handled = true; } private void OnGotoPrevChange(object _, RoutedEventArgs e) { - this.FindDescendantOfType()?.GotoPrevChange(); + this.FindDescendantOfType()?.GotoPrevChange(); e.Handled = true; } private void OnGotoNextChange(object _, RoutedEventArgs e) { - this.FindDescendantOfType()?.GotoNextChange(); + this.FindDescendantOfType()?.GotoNextChange(); e.Handled = true; } private void OnGotoLastChange(object _, RoutedEventArgs e) { - this.FindDescendantOfType()?.GotoLastChange(); - e.Handled = true; - } - - private void OnBlockNavigationChanged(object sender, RoutedEventArgs e) - { - if (sender is TextDiffView textDiff) - BlockNavigationIndicator.Text = textDiff.BlockNavigation?.Indicator ?? string.Empty; - } - - private void OnUseFullTextDiffClicked(object sender, RoutedEventArgs e) - { - var textDiffView = this.FindDescendantOfType(); - - var presenter = textDiffView?.FindDescendantOfType(); - if (presenter == null) - return; - - if (presenter.DataContext is Models.TextDiff combined) - combined.ScrollOffset = Vector.Zero; - else if (presenter.DataContext is ViewModels.TwoSideTextDiff twoSides) - twoSides.File = string.Empty; // Just to reset `SyncScrollOffset` without affect UI refresh. - - (DataContext as ViewModels.DiffContext)?.ToggleFullTextDiff(); + this.FindDescendantOfType()?.GotoLastChange(); e.Handled = true; } } diff --git a/src/Views/TextDiffView.axaml b/src/Views/TextDiffView.axaml index 45745d384..135d5711e 100644 --- a/src/Views/TextDiffView.axaml +++ b/src/Views/TextDiffView.axaml @@ -7,15 +7,14 @@ xmlns:v="using:SourceGit.Views" mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450" x:Class="SourceGit.Views.TextDiffView" - x:Name="ThisControl" + x:DataType="vm:TextDiffContext" Background="{DynamicResource Brush.Contents}"> - + - + + EnableChunkSelection="{Binding EnableChunkOption}" + SelectedChunk="{Binding SelectedChunk, Mode=TwoWay}" + BlockNavigation="{Binding BlockNavigation, Mode=OneWay}"/> @@ -47,7 +46,6 @@ + EnableChunkSelection="{Binding EnableChunkOption, Mode=OneWay}" + SelectedChunk="{Binding SelectedChunk, Mode=TwoWay}" + BlockNavigation="{Binding BlockNavigation, Mode=OneWay}"/> @@ -87,14 +85,14 @@ UseSyntaxHighlighting="{Binding Source={x:Static vm:Preferences.Instance}, Path=UseSyntaxHighlighting}" WordWrap="False" ShowHiddenSymbols="{Binding Source={x:Static vm:Preferences.Instance}, Path=ShowHiddenSymbolsInDiffView}" - EnableChunkSelection="{Binding #ThisControl.EnableChunkSelection}" - SelectedChunk="{Binding #ThisControl.SelectedChunk, Mode=TwoWay}" - BlockNavigation="{Binding #ThisControl.BlockNavigation, Mode=TwoWay}"/> + EnableChunkSelection="{Binding EnableChunkOption, Mode=OneWay}" + SelectedChunk="{Binding SelectedChunk, Mode=TwoWay}" + BlockNavigation="{Binding BlockNavigation, Mode=OneWay}"/> @@ -103,7 +101,7 @@ - - - + + + - + - + diff --git a/src/Views/Launcher.axaml.cs b/src/Views/Launcher.axaml.cs index 2f2f8bee6..dcf4533ce 100644 --- a/src/Views/Launcher.axaml.cs +++ b/src/Views/Launcher.axaml.cs @@ -317,6 +317,8 @@ private void OnOpenWorkspaceMenu(object sender, RoutedEventArgs e) { var pref = ViewModels.Preferences.Instance; var menu = new ContextMenu(); + menu.Placement = PlacementMode.BottomEdgeAlignedLeft; + menu.VerticalOffset = -6; for (var i = 0; i < pref.Workspaces.Count; i++) { diff --git a/src/Views/LauncherPageSwitcher.axaml b/src/Views/LauncherPageSwitcher.axaml index b52feef0c..3f3eab242 100644 --- a/src/Views/LauncherPageSwitcher.axaml +++ b/src/Views/LauncherPageSwitcher.axaml @@ -83,7 +83,7 @@ - + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -