From c6ece81b0761e3e74448a493cef22269dc1e7efd Mon Sep 17 00:00:00 2001 From: leo Date: Mon, 22 Sep 2025 21:29:42 +0800 Subject: [PATCH 0001/1230] ux: add tooltip for release notes Signed-off-by: leo --- src/Resources/Locales/en_US.axaml | 1 + src/Resources/Locales/zh_CN.axaml | 1 + src/Resources/Locales/zh_TW.axaml | 1 + src/Views/About.axaml | 3 ++- 4 files changed, 5 insertions(+), 1 deletion(-) diff --git a/src/Resources/Locales/en_US.axaml b/src/Resources/Locales/en_US.axaml index cf2d1ea91..bfeb261c4 100644 --- a/src/Resources/Locales/en_US.axaml +++ b/src/Resources/Locales/en_US.axaml @@ -1,6 +1,7 @@ About About SourceGit + Release Notes Opensource & Free Git GUI Client Add File(s) To Ignore Pattern: diff --git a/src/Resources/Locales/zh_CN.axaml b/src/Resources/Locales/zh_CN.axaml index b96f736ca..18606ff82 100644 --- a/src/Resources/Locales/zh_CN.axaml +++ b/src/Resources/Locales/zh_CN.axaml @@ -5,6 +5,7 @@ 关于软件 关于本软件 + 浏览版本更新说明 开源免费的Git客户端 新增忽略文件 匹配模式 : diff --git a/src/Resources/Locales/zh_TW.axaml b/src/Resources/Locales/zh_TW.axaml index 605be607c..11a4e5aec 100644 --- a/src/Resources/Locales/zh_TW.axaml +++ b/src/Resources/Locales/zh_TW.axaml @@ -5,6 +5,7 @@ 關於 關於 SourceGit + 版本說明 開源免費的 Git 客戶端 新增忽略檔案 比對模式: diff --git a/src/Views/About.axaml b/src/Views/About.axaml index 02199a765..92053fe52 100644 --- a/src/Views/About.axaml +++ b/src/Views/About.axaml @@ -50,7 +50,8 @@ Background="{DynamicResource Brush.Accent}" Effect="drop-shadow(0 0 6 #40000000)" Cursor="Hand" - PointerPressed="OnVisitReleaseNotes"> + PointerPressed="OnVisitReleaseNotes" + ToolTip.Tip="{DynamicResource Text.About.ReleaseNotes}"> From a335a561b48e2e97ed56b9023b703b93c5a808f7 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Mon, 22 Sep 2025 13:29:57 +0000 Subject: [PATCH 0002/1230] doc: Update translation status and sort locale files --- TRANSLATION.md | 39 ++++++++++++++++++++++++++++++--------- 1 file changed, 30 insertions(+), 9 deletions(-) diff --git a/TRANSLATION.md b/TRANSLATION.md index ee141a937..b2aabb45b 100644 --- a/TRANSLATION.md +++ b/TRANSLATION.md @@ -6,22 +6,31 @@ 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.89%25-yellow) -### ![es__ES](https://img.shields.io/badge/es__ES-99.89%25-yellow) +
+Missing keys in de_DE.axaml + +- Text.About.ReleaseNotes + +
+ +### ![es__ES](https://img.shields.io/badge/es__ES-99.78%25-yellow)
Missing keys in es_ES.axaml +- Text.About.ReleaseNotes - Text.Preferences.General.UseGitHubStyleAvatar
-### ![fr__FR](https://img.shields.io/badge/fr__FR-78.15%25-yellow) +### ![fr__FR](https://img.shields.io/badge/fr__FR-78.07%25-yellow)
Missing keys in fr_FR.axaml +- Text.About.ReleaseNotes - Text.AddToIgnore - Text.AddToIgnore.Pattern - Text.AddToIgnore.Storage @@ -219,11 +228,12 @@ This document shows the translation status of each locale file in the repository
-### ![it__IT](https://img.shields.io/badge/it__IT-97.18%25-yellow) +### ![it__IT](https://img.shields.io/badge/it__IT-97.08%25-yellow)
Missing keys in it_IT.axaml +- Text.About.ReleaseNotes - Text.BranchCM.SwitchToWorktree - Text.BranchTree.Ahead - Text.BranchTree.AheadBehind @@ -252,11 +262,12 @@ This document shows the translation status of each locale file in the repository
-### ![ja__JP](https://img.shields.io/badge/ja__JP-78.15%25-yellow) +### ![ja__JP](https://img.shields.io/badge/ja__JP-78.07%25-yellow)
Missing keys in ja_JP.axaml +- Text.About.ReleaseNotes - Text.AddToIgnore - Text.AddToIgnore.Pattern - Text.AddToIgnore.Storage @@ -454,11 +465,12 @@ This document shows the translation status of each locale file in the repository
-### ![pt__BR](https://img.shields.io/badge/pt__BR-71.73%25-red) +### ![pt__BR](https://img.shields.io/badge/pt__BR-71.65%25-red)
Missing keys in pt_BR.axaml +- Text.About.ReleaseNotes - Text.AddToIgnore - Text.AddToIgnore.Pattern - Text.AddToIgnore.Storage @@ -713,13 +725,21 @@ 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.89%25-yellow) + +
+Missing keys in ru_RU.axaml + +- Text.About.ReleaseNotes + +
-### ![ta__IN](https://img.shields.io/badge/ta__IN-78.27%25-yellow) +### ![ta__IN](https://img.shields.io/badge/ta__IN-78.18%25-yellow)
Missing keys in ta_IN.axaml +- Text.About.ReleaseNotes - Text.AddToIgnore - Text.AddToIgnore.Pattern - Text.AddToIgnore.Storage @@ -916,11 +936,12 @@ This document shows the translation status of each locale file in the repository
-### ![uk__UA](https://img.shields.io/badge/uk__UA-79.39%25-yellow) +### ![uk__UA](https://img.shields.io/badge/uk__UA-79.30%25-yellow)
Missing keys in uk_UA.axaml +- Text.About.ReleaseNotes - Text.AddToIgnore - Text.AddToIgnore.Pattern - Text.AddToIgnore.Storage From c15048b6463fbade80ffb4431c5310e7c727fe8b Mon Sep 17 00:00:00 2001 From: leo Date: Tue, 23 Sep 2025 10:51:21 +0800 Subject: [PATCH 0003/1230] feature: supports to revert non-ancestor commit (#1800) Signed-off-by: leo --- src/Views/Histories.axaml.cs | 22 ++++++++++------------ 1 file changed, 10 insertions(+), 12 deletions(-) diff --git a/src/Views/Histories.axaml.cs b/src/Views/Histories.axaml.cs index 6c24ac162..e6cca699e 100644 --- a/src/Views/Histories.axaml.cs +++ b/src/Views/Histories.axaml.cs @@ -598,19 +598,17 @@ private ContextMenu CreateContextMenuForSingleCommit(ViewModels.Repository repo, }; menu.Items.Add(cherryPick); } - else + + var revert = new MenuItem(); + revert.Header = App.Text("CommitCM.Revert"); + revert.Icon = App.CreateMenuIcon("Icons.Undo"); + revert.Click += (_, e) => { - var revert = new MenuItem(); - revert.Header = App.Text("CommitCM.Revert"); - revert.Icon = App.CreateMenuIcon("Icons.Undo"); - revert.Click += (_, e) => - { - if (repo.CanCreatePopup()) - repo.ShowPopup(new ViewModels.Revert(repo, commit)); - e.Handled = true; - }; - menu.Items.Add(revert); - } + if (repo.CanCreatePopup()) + repo.ShowPopup(new ViewModels.Revert(repo, commit)); + e.Handled = true; + }; + menu.Items.Add(revert); if (!isHead) { From 545a8a276647559847f97b1cca3343522fdd2849 Mon Sep 17 00:00:00 2001 From: leo Date: Tue, 23 Sep 2025 10:58:55 +0800 Subject: [PATCH 0004/1230] fix: do not call `ConfigureAwait(false)` since we need go back to `UIThread` (#1718) Signed-off-by: leo --- src/ViewModels/ScanRepositories.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/ViewModels/ScanRepositories.cs b/src/ViewModels/ScanRepositories.cs index 95a57fbb4..538fe244a 100644 --- a/src/ViewModels/ScanRepositories.cs +++ b/src/ViewModels/ScanRepositories.cs @@ -129,7 +129,7 @@ private async Task GetUnmanagedRepositoriesAsync(DirectoryInfo dir, List var gitDir = Path.Combine(subdir.FullName, ".git"); if (Directory.Exists(gitDir) || File.Exists(gitDir)) { - var test = await new Commands.QueryRepositoryRootPath(subdir.FullName).GetResultAsync().ConfigureAwait(false); + var test = await new Commands.QueryRepositoryRootPath(subdir.FullName).GetResultAsync(); if (test.IsSuccess && !string.IsNullOrEmpty(test.StdOut)) { var normalized = test.StdOut.Trim().Replace('\\', '/').TrimEnd('/'); @@ -140,7 +140,7 @@ private async Task GetUnmanagedRepositoriesAsync(DirectoryInfo dir, List continue; } - var isBare = await new Commands.IsBareRepository(subdir.FullName).GetResultAsync().ConfigureAwait(false); + var isBare = await new Commands.IsBareRepository(subdir.FullName).GetResultAsync(); if (isBare) { outs.Add(normalizedSelf); From 494eb137be45ddac15bcf97fe3d65e17a816fdd7 Mon Sep 17 00:00:00 2001 From: leo Date: Tue, 23 Sep 2025 11:12:29 +0800 Subject: [PATCH 0005/1230] enhance: better undo/redo history in blame (#1686) Signed-off-by: leo --- src/ViewModels/Blame.cs | 20 +++++++++++++------- src/Views/Blame.axaml.cs | 2 +- 2 files changed, 14 insertions(+), 8 deletions(-) diff --git a/src/ViewModels/Blame.cs b/src/ViewModels/Blame.cs index e71e672cc..e8b01e412 100644 --- a/src/ViewModels/Blame.cs +++ b/src/ViewModels/Blame.cs @@ -76,7 +76,7 @@ public void Back() _navigationActiveIndex--; OnPropertyChanged(nameof(CanBack)); OnPropertyChanged(nameof(CanForward)); - NavigateToCommit(_navigationHistory[_navigationActiveIndex]); + NavigateToCommit(_navigationHistory[_navigationActiveIndex], true); } public void Forward() @@ -87,21 +87,27 @@ public void Forward() _navigationActiveIndex++; OnPropertyChanged(nameof(CanBack)); OnPropertyChanged(nameof(CanForward)); - NavigateToCommit(_navigationHistory[_navigationActiveIndex]); + NavigateToCommit(_navigationHistory[_navigationActiveIndex], true); } - public void NavigateToCommit(string commitSHA) + public void NavigateToCommit(string commitSHA, bool isBackOrForward) { - if (!_navigationHistory[_navigationActiveIndex].Equals(commitSHA, StringComparison.Ordinal)) + if (Revision.SHA.StartsWith(commitSHA, StringComparison.Ordinal)) + return; + + if (!isBackOrForward) { + var count = _navigationHistory.Count; + if (_navigationActiveIndex < count - 1) + _navigationHistory.RemoveRange(_navigationActiveIndex + 1, count - _navigationActiveIndex - 1); + _navigationHistory.Add(commitSHA); - _navigationActiveIndex = _navigationHistory.Count - 1; + _navigationActiveIndex++; OnPropertyChanged(nameof(CanBack)); OnPropertyChanged(nameof(CanForward)); } - if (!Revision.SHA.StartsWith(commitSHA, StringComparison.Ordinal)) - SetBlameData(commitSHA); + SetBlameData(commitSHA); if (App.GetLauncher() is { Pages: { } pages }) { diff --git a/src/Views/Blame.axaml.cs b/src/Views/Blame.axaml.cs index a3288851c..0533beef8 100644 --- a/src/Views/Blame.axaml.cs +++ b/src/Views/Blame.axaml.cs @@ -223,7 +223,7 @@ protected override void OnPointerPressed(PointerPressedEventArgs e) if (rect.Contains(pos)) { if (DataContext is ViewModels.Blame blame) - blame.NavigateToCommit(info.CommitSHA); + blame.NavigateToCommit(info.CommitSHA, false); e.Handled = true; break; From b73d2b4ec38d224ed13215aa3e8773116863e566 Mon Sep 17 00:00:00 2001 From: AquariusStar <48148723+AquariusStar@users.noreply.github.com> Date: Sun, 28 Sep 2025 04:26:15 +0300 Subject: [PATCH 0006/1230] localization: update Russian translate (#1830) --- 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 91f6afdf7..f454e5f46 100644 --- a/src/Resources/Locales/ru_RU.axaml +++ b/src/Resources/Locales/ru_RU.axaml @@ -5,6 +5,7 @@ О программе О SourceGit + Примечания выпуска Бесплатный графический клиент Git с исходным кодом Добавить файл(ы) к игнорируемым Шаблон: From 93edb8ec0ac17561c77f9fcd4921e23aaac94f56 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Sun, 28 Sep 2025 01:26:26 +0000 Subject: [PATCH 0007/1230] 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 b2aabb45b..1a416bfe5 100644 --- a/TRANSLATION.md +++ b/TRANSLATION.md @@ -725,14 +725,7 @@ This document shows the translation status of each locale file in the repository
-### ![ru__RU](https://img.shields.io/badge/ru__RU-99.89%25-yellow) - -
-Missing keys in ru_RU.axaml - -- Text.About.ReleaseNotes - -
+### ![ru__RU](https://img.shields.io/badge/ru__RU-%E2%88%9A-brightgreen) ### ![ta__IN](https://img.shields.io/badge/ta__IN-78.18%25-yellow) From 77b0fde60322b8019be75171d39903bfe204402f Mon Sep 17 00:00:00 2001 From: leo Date: Mon, 29 Sep 2025 11:38:37 +0800 Subject: [PATCH 0008/1230] feature: supports to drop HEAD commit (reset to its first parent) (#1832) Signed-off-by: leo --- src/Resources/Locales/en_US.axaml | 4 ++ src/Resources/Locales/zh_CN.axaml | 4 ++ src/Resources/Locales/zh_TW.axaml | 4 ++ src/ViewModels/DropHead.cs | 63 +++++++++++++++++++++++++++++++ src/ViewModels/Histories.cs | 10 +++++ src/Views/DropHead.axaml | 37 ++++++++++++++++++ src/Views/DropHead.axaml.cs | 12 ++++++ src/Views/Histories.axaml.cs | 14 ++++++- 8 files changed, 147 insertions(+), 1 deletion(-) create mode 100644 src/ViewModels/DropHead.cs create mode 100644 src/Views/DropHead.axaml create mode 100644 src/Views/DropHead.axaml.cs diff --git a/src/Resources/Locales/en_US.axaml b/src/Resources/Locales/en_US.axaml index bfeb261c4..7884a14d2 100644 --- a/src/Resources/Locales/en_US.axaml +++ b/src/Resources/Locales/en_US.axaml @@ -135,6 +135,7 @@ SHA Subject Custom Action + Drop Commit Interactive Rebase Drop... Edit... @@ -355,6 +356,9 @@ Include untracked files {0} changes will be discarded You can't undo this action!!! + Drop Commit + Commit: + New HEAD: Bookmark: New Name: Target: diff --git a/src/Resources/Locales/zh_CN.axaml b/src/Resources/Locales/zh_CN.axaml index 18606ff82..cc3b04549 100644 --- a/src/Resources/Locales/zh_CN.axaml +++ b/src/Resources/Locales/zh_CN.axaml @@ -139,6 +139,7 @@ 提交指纹 主题 自定义操作 + 丢弃此提交 交互式变基(rebase -i) 丢弃... 编辑... @@ -359,6 +360,9 @@ 包括未跟踪的文件 总计{0}项选中更改 本操作不支持回退,请确认后继续!!! + 丢弃提交 + 提交 : + 丢弃后 HEAD : 书签 : 名称 : 目标 : diff --git a/src/Resources/Locales/zh_TW.axaml b/src/Resources/Locales/zh_TW.axaml index 11a4e5aec..14c7509cd 100644 --- a/src/Resources/Locales/zh_TW.axaml +++ b/src/Resources/Locales/zh_TW.axaml @@ -139,6 +139,7 @@ 提交編號 標題 自訂動作 + 捨棄此提交 互動式重定基底 (rebase -i) 捨棄... 編輯... @@ -359,6 +360,9 @@ 包含未追蹤檔案 將捨棄總計 {0} 項已選取的變更 您無法復原此操作,請確認後再繼續! + 捨棄提交 + 提交 : + 捨棄後新的 HEAD : 書籤: 名稱: 目標: diff --git a/src/ViewModels/DropHead.cs b/src/ViewModels/DropHead.cs new file mode 100644 index 000000000..b5baed06f --- /dev/null +++ b/src/ViewModels/DropHead.cs @@ -0,0 +1,63 @@ +using System.Threading.Tasks; + +namespace SourceGit.ViewModels +{ + public class DropHead : Popup + { + public Models.Commit Target + { + get; + } + + public Models.Commit NewHead + { + get; + } + + public DropHead(Repository repo, Models.Commit target, Models.Commit parent) + { + _repo = repo; + Target = target; + NewHead = parent; + } + + public override async Task Sure() + { + using var lockWatcher = _repo.LockWatcher(); + ProgressDescription = $"Drop HEAD '{Target.SHA}' ..."; + + var log = _repo.CreateLog($"Drop '{Target.SHA}'"); + Use(log); + + var changes = await new Commands.QueryLocalChanges(_repo.FullPath, true).GetResultAsync(); + var needAutoStash = changes.Count > 0; + var succ = false; + + if (needAutoStash) + { + succ = await new Commands.Stash(_repo.FullPath) + .Use(log) + .PushAsync("DROP_HEAD_AUTO_STASH", true); + if (!succ) + { + log.Complete(); + return false; + } + } + + succ = await new Commands.Reset(_repo.FullPath, NewHead.SHA, "--hard") + .Use(log) + .ExecAsync(); + + if (succ && needAutoStash) + await new Commands.Stash(_repo.FullPath) + .Use(log) + .PopAsync("stash@{0}"); + + log.Complete(); + return succ; + } + + private readonly Repository _repo; + } +} diff --git a/src/ViewModels/Histories.cs b/src/ViewModels/Histories.cs index 7f9e540a0..3ad355966 100644 --- a/src/ViewModels/Histories.cs +++ b/src/ViewModels/Histories.cs @@ -332,6 +332,16 @@ public async Task SquashHeadAsync(Models.Commit head) } } + public async Task DropHeadAsync(Models.Commit head) + { + var parent = _commits.Find(x => x.SHA.Equals(head.Parents[0])); + if (parent == null) + parent = await new Commands.QuerySingleCommit(_repo.FullPath, head.Parents[0]).GetResultAsync(); + + if (parent != null && _repo.CanCreatePopup()) + _repo.ShowPopup(new DropHead(_repo, head, parent)); + } + public async Task InteractiveRebaseAsync(Models.Commit commit, Models.InteractiveRebaseAction act) { var prefill = new InteractiveRebasePrefill(commit.SHA, act); diff --git a/src/Views/DropHead.axaml b/src/Views/DropHead.axaml new file mode 100644 index 000000000..a7f9fd144 --- /dev/null +++ b/src/Views/DropHead.axaml @@ -0,0 +1,37 @@ + + + + + + + + + + + + + + + + + + + + diff --git a/src/Views/DropHead.axaml.cs b/src/Views/DropHead.axaml.cs new file mode 100644 index 000000000..5dc43cf14 --- /dev/null +++ b/src/Views/DropHead.axaml.cs @@ -0,0 +1,12 @@ +using Avalonia.Controls; + +namespace SourceGit.Views +{ + public partial class DropHead : UserControl + { + public DropHead() + { + InitializeComponent(); + } + } +} diff --git a/src/Views/Histories.axaml.cs b/src/Views/Histories.axaml.cs index e6cca699e..b0cef55d7 100644 --- a/src/Views/Histories.axaml.cs +++ b/src/Views/Histories.axaml.cs @@ -610,7 +610,19 @@ private ContextMenu CreateContextMenuForSingleCommit(ViewModels.Repository repo, }; menu.Items.Add(revert); - if (!isHead) + if (isHead) + { + var dropHead = new MenuItem(); + dropHead.Header = App.Text("CommitCM.Drop"); + dropHead.Icon = App.CreateMenuIcon("Icons.Clear"); + dropHead.Click += async (_, e) => + { + await vm.DropHeadAsync(commit); + e.Handled = true; + }; + menu.Items.Add(dropHead); + } + else { var checkoutCommit = new MenuItem(); checkoutCommit.Header = App.Text("CommitCM.Checkout"); From 50421181d9fa0031909b240e153019ec3503c366 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Mon, 29 Sep 2025 03:39:57 +0000 Subject: [PATCH 0009/1230] doc: Update translation status and sort locale files --- TRANSLATION.md | 60 ++++++++++++++++++++++++++++++++++++++++++-------- 1 file changed, 51 insertions(+), 9 deletions(-) diff --git a/TRANSLATION.md b/TRANSLATION.md index 1a416bfe5..f87f071d2 100644 --- a/TRANSLATION.md +++ b/TRANSLATION.md @@ -6,26 +6,34 @@ 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.89%25-yellow) +### ![de__DE](https://img.shields.io/badge/de__DE-99.44%25-yellow)
Missing keys in de_DE.axaml - Text.About.ReleaseNotes +- Text.CommitCM.Drop +- Text.DropHead +- Text.DropHead.Commit +- Text.DropHead.NewHead
-### ![es__ES](https://img.shields.io/badge/es__ES-99.78%25-yellow) +### ![es__ES](https://img.shields.io/badge/es__ES-99.33%25-yellow)
Missing keys in es_ES.axaml - Text.About.ReleaseNotes +- Text.CommitCM.Drop +- Text.DropHead +- Text.DropHead.Commit +- Text.DropHead.NewHead - Text.Preferences.General.UseGitHubStyleAvatar
-### ![fr__FR](https://img.shields.io/badge/fr__FR-78.07%25-yellow) +### ![fr__FR](https://img.shields.io/badge/fr__FR-77.72%25-yellow)
Missing keys in fr_FR.axaml @@ -67,6 +75,7 @@ This document shows the translation status of each locale file in the repository - Text.CommitCM.CopyCommitMessage - Text.CommitCM.CopyCommitter - Text.CommitCM.CopySubject +- Text.CommitCM.Drop - Text.CommitCM.InteractiveRebase - Text.CommitCM.InteractiveRebase.Drop - Text.CommitCM.InteractiveRebase.Edit @@ -129,6 +138,9 @@ This document shows the translation status of each locale file in the repository - Text.DirtyState.HasPendingPullOrPush - Text.DirtyState.UpToDate - Text.Discard.IncludeUntracked +- Text.DropHead +- Text.DropHead.Commit +- Text.DropHead.NewHead - Text.ExecuteCustomAction.Target - Text.ExecuteCustomAction.Repository - Text.GitFlow.FinishWithPush @@ -228,7 +240,7 @@ This document shows the translation status of each locale file in the repository
-### ![it__IT](https://img.shields.io/badge/it__IT-97.08%25-yellow) +### ![it__IT](https://img.shields.io/badge/it__IT-96.64%25-yellow)
Missing keys in it_IT.axaml @@ -240,6 +252,7 @@ This document shows the translation status of each locale file in the repository - Text.BranchTree.Behind - Text.BranchTree.Status - Text.BranchTree.Worktree +- Text.CommitCM.Drop - Text.CommitDetail.Info.CopyEmail - Text.CommitDetail.Info.CopyName - Text.CommitDetail.Info.CopyNameAndEmail @@ -248,6 +261,9 @@ This document shows the translation status of each locale file in the repository - Text.DirtyState.HasLocalChanges - Text.DirtyState.HasPendingPullOrPush - Text.DirtyState.UpToDate +- Text.DropHead +- Text.DropHead.Commit +- Text.DropHead.NewHead - Text.Preferences.AI.ReadApiKeyFromEnv - Text.Preferences.Appearance.UseAutoHideScrollBars - Text.Preferences.General.EnableCompactFolders @@ -262,7 +278,7 @@ This document shows the translation status of each locale file in the repository
-### ![ja__JP](https://img.shields.io/badge/ja__JP-78.07%25-yellow) +### ![ja__JP](https://img.shields.io/badge/ja__JP-77.72%25-yellow)
Missing keys in ja_JP.axaml @@ -305,6 +321,7 @@ This document shows the translation status of each locale file in the repository - Text.CommitCM.CopyCommitMessage - Text.CommitCM.CopyCommitter - Text.CommitCM.CopySubject +- Text.CommitCM.Drop - Text.CommitCM.InteractiveRebase - Text.CommitCM.InteractiveRebase.Drop - Text.CommitCM.InteractiveRebase.Edit @@ -367,6 +384,9 @@ This document shows the translation status of each locale file in the repository - Text.DirtyState.HasPendingPullOrPush - Text.DirtyState.UpToDate - Text.Discard.IncludeUntracked +- Text.DropHead +- Text.DropHead.Commit +- Text.DropHead.NewHead - Text.ExecuteCustomAction.Target - Text.ExecuteCustomAction.Repository - Text.GitFlow.FinishWithPush @@ -465,7 +485,7 @@ This document shows the translation status of each locale file in the repository
-### ![pt__BR](https://img.shields.io/badge/pt__BR-71.65%25-red) +### ![pt__BR](https://img.shields.io/badge/pt__BR-71.33%25-red)
Missing keys in pt_BR.axaml @@ -516,6 +536,7 @@ This document shows the translation status of each locale file in the repository - Text.CommitCM.CopyCommitMessage - Text.CommitCM.CopyCommitter - Text.CommitCM.CopySubject +- Text.CommitCM.Drop - Text.CommitCM.InteractiveRebase - Text.CommitCM.InteractiveRebase.Drop - Text.CommitCM.InteractiveRebase.Edit @@ -592,6 +613,9 @@ This document shows the translation status of each locale file in the repository - Text.DirtyState.HasPendingPullOrPush - Text.DirtyState.UpToDate - Text.Discard.IncludeUntracked +- Text.DropHead +- Text.DropHead.Commit +- Text.DropHead.NewHead - Text.ExecuteCustomAction.Target - Text.ExecuteCustomAction.Repository - Text.Fetch.Force @@ -725,9 +749,19 @@ 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.55%25-yellow) -### ![ta__IN](https://img.shields.io/badge/ta__IN-78.18%25-yellow) +
+Missing keys in ru_RU.axaml + +- Text.CommitCM.Drop +- Text.DropHead +- Text.DropHead.Commit +- Text.DropHead.NewHead + +
+ +### ![ta__IN](https://img.shields.io/badge/ta__IN-77.83%25-yellow)
Missing keys in ta_IN.axaml @@ -770,6 +804,7 @@ This document shows the translation status of each locale file in the repository - Text.CommitCM.CopyCommitMessage - Text.CommitCM.CopyCommitter - Text.CommitCM.CopySubject +- Text.CommitCM.Drop - Text.CommitCM.InteractiveRebase - Text.CommitCM.InteractiveRebase.Drop - Text.CommitCM.InteractiveRebase.Edit @@ -832,6 +867,9 @@ This document shows the translation status of each locale file in the repository - Text.DirtyState.HasPendingPullOrPush - Text.DirtyState.UpToDate - Text.Discard.IncludeUntracked +- Text.DropHead +- Text.DropHead.Commit +- Text.DropHead.NewHead - Text.ExecuteCustomAction.Target - Text.ExecuteCustomAction.Repository - Text.GitFlow.FinishWithPush @@ -929,7 +967,7 @@ This document shows the translation status of each locale file in the repository
-### ![uk__UA](https://img.shields.io/badge/uk__UA-79.30%25-yellow) +### ![uk__UA](https://img.shields.io/badge/uk__UA-78.95%25-yellow)
Missing keys in uk_UA.axaml @@ -971,6 +1009,7 @@ This document shows the translation status of each locale file in the repository - Text.CommitCM.CopyCommitMessage - Text.CommitCM.CopyCommitter - Text.CommitCM.CopySubject +- Text.CommitCM.Drop - Text.CommitCM.InteractiveRebase - Text.CommitCM.InteractiveRebase.Drop - Text.CommitCM.InteractiveRebase.Edit @@ -1029,6 +1068,9 @@ This document shows the translation status of each locale file in the repository - Text.DirtyState.HasPendingPullOrPush - Text.DirtyState.UpToDate - Text.Discard.IncludeUntracked +- Text.DropHead +- Text.DropHead.Commit +- Text.DropHead.NewHead - Text.ExecuteCustomAction.Target - Text.ExecuteCustomAction.Repository - Text.GitFlow.FinishWithPush From e31846c08facd0060d5ac84eceb289da66c47082 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: Wed, 8 Oct 2025 20:12:36 -0600 Subject: [PATCH 0010/1230] localization: update Spanish translation (#1837) * localization: update Spanish translation Add missing strings. * doc: Update translation status and sort locale files --------- Co-authored-by: github-actions[bot] --- TRANSLATION.md | 14 +------------- src/Resources/Locales/es_ES.axaml | 6 ++++++ 2 files changed, 7 insertions(+), 13 deletions(-) diff --git a/TRANSLATION.md b/TRANSLATION.md index f87f071d2..6e38feba2 100644 --- a/TRANSLATION.md +++ b/TRANSLATION.md @@ -19,19 +19,7 @@ This document shows the translation status of each locale file in the repository
-### ![es__ES](https://img.shields.io/badge/es__ES-99.33%25-yellow) - -
-Missing keys in es_ES.axaml - -- Text.About.ReleaseNotes -- Text.CommitCM.Drop -- Text.DropHead -- Text.DropHead.Commit -- Text.DropHead.NewHead -- Text.Preferences.General.UseGitHubStyleAvatar - -
+### ![es__ES](https://img.shields.io/badge/es__ES-%E2%88%9A-brightgreen) ### ![fr__FR](https://img.shields.io/badge/fr__FR-77.72%25-yellow) diff --git a/src/Resources/Locales/es_ES.axaml b/src/Resources/Locales/es_ES.axaml index 292705a30..30e793376 100644 --- a/src/Resources/Locales/es_ES.axaml +++ b/src/Resources/Locales/es_ES.axaml @@ -5,6 +5,7 @@ Acerca de Acerca de SourceGit + Notas de la versión (Release) Cliente Git GUI de código abierto y gratuito Agregar Archivo(s) Para Ignorar Patrón: @@ -138,6 +139,7 @@ SHA Asunto Acción personalizada + Eliminar Commit Rebase interactivo Eliminar... Editar... @@ -358,6 +360,9 @@ Incluir archivos no rastreados Total {0} cambios serán descartados ¡No puedes deshacer esta acción! + Eliminar Commit + Commit: + Nuevo HEAD: Marcador: Nuevo Nombre: Destino: @@ -579,6 +584,7 @@ Mostrar hijos en los detalles de commit Mostrar etiquetas en el gráfico de commit Longitud de la guía del asunto + Generar avatar con estilo por defecto de Github GIT Habilitar Auto CRLF Directorio de clonado por defecto From 3b7995455158bd69a650065a1db82d3d3e86adf1 Mon Sep 17 00:00:00 2001 From: AquariusStar <48148723+AquariusStar@users.noreply.github.com> Date: Thu, 9 Oct 2025 05:13:03 +0300 Subject: [PATCH 0011/1230] localization: update Russian translation (#1840) --- src/Resources/Locales/ru_RU.axaml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/Resources/Locales/ru_RU.axaml b/src/Resources/Locales/ru_RU.axaml index f454e5f46..776bb7816 100644 --- a/src/Resources/Locales/ru_RU.axaml +++ b/src/Resources/Locales/ru_RU.axaml @@ -139,6 +139,7 @@ SHA Субъект Пользовательское действие + Бросить ревизию Интерактивное перемещение Бросить... Редактировать... @@ -359,6 +360,9 @@ Включить неотслеживаемые файлы {0} изменений будут отменены Вы не можете отменить это действие!!! + Бросить ревизию + Ревизия: + Новая ГОЛОВА: Закладка: Новое имя: Цель: From 050e058f7d14df557db4cdf7a9825ce653400cbc Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Thu, 9 Oct 2025 02:13:21 +0000 Subject: [PATCH 0012/1230] doc: Update translation status and sort locale files --- TRANSLATION.md | 12 +----------- 1 file changed, 1 insertion(+), 11 deletions(-) diff --git a/TRANSLATION.md b/TRANSLATION.md index 6e38feba2..73827f23c 100644 --- a/TRANSLATION.md +++ b/TRANSLATION.md @@ -737,17 +737,7 @@ This document shows the translation status of each locale file in the repository -### ![ru__RU](https://img.shields.io/badge/ru__RU-99.55%25-yellow) - -
-Missing keys in ru_RU.axaml - -- Text.CommitCM.Drop -- Text.DropHead -- Text.DropHead.Commit -- Text.DropHead.NewHead - -
+### ![ru__RU](https://img.shields.io/badge/ru__RU-%E2%88%9A-brightgreen) ### ![ta__IN](https://img.shields.io/badge/ta__IN-77.83%25-yellow) From 501aae9867a1f7022d1448c447aff413406516ca Mon Sep 17 00:00:00 2001 From: leo Date: Thu, 9 Oct 2025 11:54:27 +0800 Subject: [PATCH 0013/1230] project: upgrade dependencies Signed-off-by: leo --- THIRD-PARTY-LICENSES.md | 6 +- src/App.axaml.cs | 3 +- src/SourceGit.csproj | 16 ++--- src/ViewModels/Welcome.cs | 16 +++++ src/Views/BranchOrTagNameTextBox.cs | 3 +- src/Views/InteractiveRebase.axaml | 5 +- src/Views/InteractiveRebase.axaml.cs | 79 +++++++++++---------- src/Views/LauncherTabBar.axaml | 5 +- src/Views/LauncherTabBar.axaml.cs | 48 ++++++++----- src/Views/Welcome.axaml | 12 ++-- src/Views/Welcome.axaml.cs | 100 ++++++++++----------------- 11 files changed, 155 insertions(+), 138 deletions(-) diff --git a/THIRD-PARTY-LICENSES.md b/THIRD-PARTY-LICENSES.md index 48cf38b1c..4734482e6 100644 --- a/THIRD-PARTY-LICENSES.md +++ b/THIRD-PARTY-LICENSES.md @@ -7,7 +7,7 @@ The project uses the following third-party libraries or assets ### AvaloniaUI - **Source**: https://github.com/AvaloniaUI/Avalonia -- **Version**: 11.3.6 +- **Version**: 11.3.7 - **License**: MIT License - **License Link**: https://github.com/AvaloniaUI/Avalonia/blob/master/licence.md @@ -35,14 +35,14 @@ The project uses the following third-party libraries or assets ### OpenAI .NET SDK - **Source**: https://github.com/openai/openai-dotnet -- **Version**: 2.4.0 +- **Version**: 2.5.0 - **License**: MIT License - **License Link**: https://github.com/openai/openai-dotnet/blob/main/LICENSE ### Azure.AI.OpenAI - **Source**: https://github.com/Azure/azure-sdk-for-net -- **Version**: 2.3.0-beta.2 +- **Version**: 2.5.0-beta.1 - **License**: MIT License - **License Link**: https://github.com/Azure/azure-sdk-for-net/blob/main/LICENSE.txt diff --git a/src/App.axaml.cs b/src/App.axaml.cs index de32508cb..242a5f13c 100644 --- a/src/App.axaml.cs +++ b/src/App.axaml.cs @@ -14,6 +14,7 @@ using Avalonia.Controls; using Avalonia.Controls.ApplicationLifetimes; using Avalonia.Data.Core.Plugins; +using Avalonia.Input.Platform; using Avalonia.Markup.Xaml; using Avalonia.Media; using Avalonia.Media.Fonts; @@ -324,7 +325,7 @@ public static async Task CopyTextAsync(string data) public static async Task GetClipboardTextAsync() { if (Current?.ApplicationLifetime is IClassicDesktopStyleApplicationLifetime { MainWindow.Clipboard: { } clipboard }) - return await clipboard.GetTextAsync(); + return await clipboard.TryGetTextAsync(); return null; } diff --git a/src/SourceGit.csproj b/src/SourceGit.csproj index 60f9868c2..a2e15c7c9 100644 --- a/src/SourceGit.csproj +++ b/src/SourceGit.csproj @@ -39,19 +39,19 @@ - - - - - - + + + + + + - + - + diff --git a/src/ViewModels/Welcome.cs b/src/ViewModels/Welcome.cs index e18581ad4..2e6b3a6a3 100644 --- a/src/ViewModels/Welcome.cs +++ b/src/ViewModels/Welcome.cs @@ -176,6 +176,22 @@ public void AddRootNode() activePage.Popup = new CreateGroup(null); } + public RepositoryNode FindNodeById(string id, RepositoryNode root = null) + { + var collection = (root == null) ? Preferences.Instance.RepositoryNodes : root.SubNodes; + foreach (var node in collection) + { + if (node.Id.Equals(id, StringComparison.Ordinal)) + return node; + + var sub = FindNodeById(id, node); + if (sub != null) + return sub; + } + + return null; + } + public RepositoryNode FindParentGroup(RepositoryNode node, RepositoryNode group = null) { var collection = (group == null) ? Preferences.Instance.RepositoryNodes : group.SubNodes; diff --git a/src/Views/BranchOrTagNameTextBox.cs b/src/Views/BranchOrTagNameTextBox.cs index 1e834118e..e7078b17c 100644 --- a/src/Views/BranchOrTagNameTextBox.cs +++ b/src/Views/BranchOrTagNameTextBox.cs @@ -3,6 +3,7 @@ using Avalonia.Controls; using Avalonia.Input; +using Avalonia.Input.Platform; using Avalonia.Interactivity; namespace SourceGit.Views @@ -51,7 +52,7 @@ private async void OnPastingFromClipboard(object sender, RoutedEventArgs e) var clipboard = TopLevel.GetTopLevel(this)?.Clipboard; if (clipboard != null) { - var text = await clipboard.GetTextAsync(); + var text = await clipboard.TryGetTextAsync(); if (!string.IsNullOrEmpty(text)) OnTextInput(new TextInputEventArgs() { Text = text }); } diff --git a/src/Views/InteractiveRebase.axaml b/src/Views/InteractiveRebase.axaml index 0f706ae3d..eba78b502 100644 --- a/src/Views/InteractiveRebase.axaml +++ b/src/Views/InteractiveRebase.axaml @@ -94,8 +94,9 @@ + PointerPressed="OnRowHeaderPointerPressed" + DragDrop.AllowDrop="True" + DragDrop.DragOver="OnRowHeaderDragOver"> diff --git a/src/Views/InteractiveRebase.axaml.cs b/src/Views/InteractiveRebase.axaml.cs index c43acae37..b2f5fd9c3 100644 --- a/src/Views/InteractiveRebase.axaml.cs +++ b/src/Views/InteractiveRebase.axaml.cs @@ -136,54 +136,62 @@ private void OnRowsSelectionChanged(object _, SelectionChangedEventArgs e) OpenCommitMessageEditor(items[0]); } - private void OnSetupRowHeaderDragDrop(object sender, RoutedEventArgs e) - { - if (sender is Border border) - { - DragDrop.SetAllowDrop(border, true); - border.AddHandler(DragDrop.DragOverEvent, OnRowHeaderDragOver); - } - } - - private void OnRowHeaderPointerPressed(object sender, PointerPressedEventArgs e) + private async void OnRowHeaderPointerPressed(object sender, PointerPressedEventArgs e) { if (sender is Border { DataContext: ViewModels.InteractiveRebaseItem item }) { - var data = new DataObject(); - data.Set("InteractiveRebaseItem", item); - DragDrop.DoDragDrop(e, data, DragDropEffects.Move | DragDropEffects.Copy | DragDropEffects.Link); + var data = new DataTransfer(); + data.Add(DataTransferItem.Create(_dndItemFormat, item.Commit.SHA)); + await DragDrop.DoDragDropAsync(e, data, DragDropEffects.Move); } } private void OnRowHeaderDragOver(object sender, DragEventArgs e) { - if (DataContext is ViewModels.InteractiveRebase vm && - e.Data.Contains("InteractiveRebaseItem") && - e.Data.Get("InteractiveRebaseItem") is ViewModels.InteractiveRebaseItem src && - sender is Border { DataContext: ViewModels.InteractiveRebaseItem dst } border && - src != dst) - { - e.DragEffects = DragDropEffects.Move | DragDropEffects.Copy | DragDropEffects.Link; + if (DataContext is not ViewModels.InteractiveRebase vm) + return; - var p = e.GetPosition(border); - if (p.Y > border.Bounds.Height * 0.33 && p.Y < border.Bounds.Height * 0.67) + if (e.DataTransfer.TryGetValue(_dndItemFormat) is not { Length: > 6 } sha) + return; + + ViewModels.InteractiveRebaseItem src = null; + foreach (var item in vm.Items) + { + if (item.Commit.SHA.Equals(sha, StringComparison.Ordinal)) { - var srcIdx = vm.Items.IndexOf(src); - var dstIdx = vm.Items.IndexOf(dst); - if (srcIdx < dstIdx) - { - for (var i = srcIdx; i < dstIdx; i++) - vm.MoveItemDown(src); - } - else - { - for (var i = srcIdx; i > dstIdx; i--) - vm.MoveItemUp(src); - } + src = item; + break; } + } - e.Handled = true; + if (src == null) + return; + + if (sender is not Border { DataContext: ViewModels.InteractiveRebaseItem dst } border) + return; + + if (src == dst) + return; + + var p = e.GetPosition(border); + if (p.Y > border.Bounds.Height * 0.33 && p.Y < border.Bounds.Height * 0.67) + { + var srcIdx = vm.Items.IndexOf(src); + var dstIdx = vm.Items.IndexOf(dst); + if (srcIdx < dstIdx) + { + for (var i = srcIdx; i < dstIdx; i++) + vm.MoveItemDown(src); + } + else + { + for (var i = srcIdx; i > dstIdx; i--) + vm.MoveItemUp(src); + } } + + e.DragEffects = DragDropEffects.Move; + e.Handled = true; } private void OnButtonActionClicked(object sender, RoutedEventArgs e) @@ -293,5 +301,6 @@ private void ChangeItemsAction(ViewModels.InteractiveRebaseItem target, Models.I } private bool _firstSelectionChangedHandled = false; + private readonly DataFormat _dndItemFormat = DataFormat.CreateStringApplicationFormat("sourcegit-dnd-ir-item"); } } diff --git a/src/Views/LauncherTabBar.axaml b/src/Views/LauncherTabBar.axaml index 244424b24..7ea6bd400 100644 --- a/src/Views/LauncherTabBar.axaml +++ b/src/Views/LauncherTabBar.axaml @@ -47,8 +47,9 @@ PointerPressed="OnPointerPressedTab" PointerMoved="OnPointerMovedOverTab" PointerReleased="OnPointerReleasedTab" - Loaded="SetupDragAndDrop" - ContextRequested="OnTabContextRequested"> + ContextRequested="OnTabContextRequested" + DragDrop.AllowDrop="True" + DragDrop.Drop="DropTab"> diff --git a/src/Views/LauncherTabBar.axaml.cs b/src/Views/LauncherTabBar.axaml.cs index 546463436..be91f7296 100644 --- a/src/Views/LauncherTabBar.axaml.cs +++ b/src/Views/LauncherTabBar.axaml.cs @@ -161,16 +161,6 @@ private void OnTabsSelectionChanged(object _1, SelectionChangedEventArgs _2) InvalidateVisual(); } - private void SetupDragAndDrop(object sender, RoutedEventArgs e) - { - if (sender is Border border) - { - DragDrop.SetAllowDrop(border, true); - border.AddHandler(DragDrop.DropEvent, DropTab); - } - e.Handled = true; - } - private void OnPointerPressedTab(object sender, PointerPressedEventArgs e) { if (sender is Border border) @@ -196,7 +186,7 @@ private void OnPointerReleasedTab(object _1, PointerReleasedEventArgs _2) _startDragTab = false; } - private void OnPointerMovedOverTab(object sender, PointerEventArgs e) + private async void OnPointerMovedOverTab(object sender, PointerEventArgs e) { if (_pressedTab && !_startDragTab && sender is Border { DataContext: ViewModels.LauncherPage page } border) { @@ -207,23 +197,42 @@ private void OnPointerMovedOverTab(object sender, PointerEventArgs e) _startDragTab = true; - var data = new DataObject(); - data.Set("MovedTab", page); - DragDrop.DoDragDrop(e, data, DragDropEffects.Move); + var data = new DataTransfer(); + data.Add(DataTransferItem.Create(_dndMainTabFormat, page.Node.Id)); + await DragDrop.DoDragDropAsync(e, data, DragDropEffects.Move); } e.Handled = true; } private void DropTab(object sender, DragEventArgs e) { - if (e.Data.Contains("MovedTab") && - e.Data.Get("MovedTab") is ViewModels.LauncherPage moved && - sender is Border { DataContext: ViewModels.LauncherPage to } && - to != moved) + if (e.DataTransfer.TryGetValue(_dndMainTabFormat) is not { Length: > 0 } id) + return; + + if (DataContext is not ViewModels.Launcher launcher) + return; + + ViewModels.LauncherPage target = null; + foreach (var page in launcher.Pages) { - (DataContext as ViewModels.Launcher)?.MoveTab(moved, to); + if (page.Node.Id.Equals(id, StringComparison.Ordinal)) + { + target = page; + break; + } } + if (target == null) + return; + + if (sender is not Border { DataContext: ViewModels.LauncherPage to }) + return; + + if (target == to) + return; + + launcher.MoveTab(target, to); + _pressedTab = false; _startDragTab = false; e.Handled = true; @@ -317,5 +326,6 @@ private void OnCloseTab(object sender, RoutedEventArgs e) private bool _pressedTab = false; private Point _pressedTabPosition = new(); private bool _startDragTab = false; + private readonly DataFormat _dndMainTabFormat = DataFormat.CreateStringApplicationFormat("sourcegit-dnd-main-tab"); } } diff --git a/src/Views/Welcome.axaml b/src/Views/Welcome.axaml index 935569ca2..771073bd0 100644 --- a/src/Views/Welcome.axaml +++ b/src/Views/Welcome.axaml @@ -60,8 +60,10 @@ ScrollViewer.VerticalScrollBarVisibility="Auto" ItemsSource="{Binding Rows}" SelectionMode="Single" - Loaded="SetupTreeViewDragAndDrop" - LostFocus="OnTreeViewLostFocus"> + LostFocus="OnTreeViewLostFocus" + DragDrop.AllowDrop="True" + DragDrop.DragOver="DragOverTreeView" + DragDrop.Drop="DropOnTreeView"> - - - - - - - - - - - - - - - - - - - - - - - diff --git a/src/Views/WorkspaceSwitcher.axaml.cs b/src/Views/WorkspaceSwitcher.axaml.cs deleted file mode 100644 index 7b708dd91..000000000 --- a/src/Views/WorkspaceSwitcher.axaml.cs +++ /dev/null @@ -1,62 +0,0 @@ -using Avalonia.Controls; -using Avalonia.Input; - -namespace SourceGit.Views -{ - public partial class WorkspaceSwitcher : UserControl - { - public WorkspaceSwitcher() - { - InitializeComponent(); - } - - protected override void OnKeyDown(KeyEventArgs e) - { - base.OnKeyDown(e); - - if (e.Key == Key.Enter && DataContext is ViewModels.WorkspaceSwitcher switcher) - { - switcher.Switch(); - e.Handled = true; - } - } - - private void OnItemTapped(object sender, TappedEventArgs e) - { - if (DataContext is ViewModels.WorkspaceSwitcher switcher) - { - switcher.Switch(); - e.Handled = true; - } - } - - private void OnSearchBoxKeyDown(object sender, KeyEventArgs e) - { - if (WorkspaceListBox.ItemCount == 0) - return; - - if (e.Key == Key.Down) - { - WorkspaceListBox.Focus(NavigationMethod.Directional); - - if (WorkspaceListBox.SelectedIndex < WorkspaceListBox.ItemCount - 1) - WorkspaceListBox.SelectedIndex++; - else - WorkspaceListBox.SelectedIndex = 0; - - e.Handled = true; - } - else if (e.Key == Key.Up) - { - WorkspaceListBox.Focus(NavigationMethod.Directional); - - if (WorkspaceListBox.SelectedIndex > 0) - WorkspaceListBox.SelectedIndex--; - else - WorkspaceListBox.SelectedIndex = WorkspaceListBox.ItemCount - 1; - - e.Handled = true; - } - } - } -} From 91d769cfe2a1768aa528c5cc22ed911ff55828c5 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Thu, 16 Oct 2025 09:50:30 +0000 Subject: [PATCH 0040/1230] doc: Update translation status and sort locale files --- TRANSLATION.md | 15 +++++---------- src/Resources/Locales/de_DE.axaml | 1 - src/Resources/Locales/es_ES.axaml | 1 - src/Resources/Locales/id_ID.axaml | 1 - src/Resources/Locales/it_IT.axaml | 1 - src/Resources/Locales/ru_RU.axaml | 1 - src/Resources/Locales/zh_CN.axaml | 1 - src/Resources/Locales/zh_TW.axaml | 1 - 8 files changed, 5 insertions(+), 17 deletions(-) diff --git a/TRANSLATION.md b/TRANSLATION.md index d38bb5ce1..596b406a9 100644 --- a/TRANSLATION.md +++ b/TRANSLATION.md @@ -32,7 +32,7 @@ This document shows the translation status of each locale file in the repository -### ![fr__FR](https://img.shields.io/badge/fr__FR-77.32%25-yellow) +### ![fr__FR](https://img.shields.io/badge/fr__FR-77.40%25-yellow)
Missing keys in fr_FR.axaml @@ -146,7 +146,6 @@ This document shows the translation status of each locale file in the repository - Text.ExecuteCustomAction.Repository - Text.GitFlow.FinishWithPush - Text.GitFlow.FinishWithSquash -- Text.Hotkeys.Global.SwitchWorkspace - Text.Hotkeys.Global.SwitchTab - Text.Hotkeys.TextEditor.OpenExternalMergeTool - Text.InteractiveRebase.ReorderTip @@ -304,7 +303,7 @@ This document shows the translation status of each locale file in the repository
-### ![ja__JP](https://img.shields.io/badge/ja__JP-77.32%25-yellow) +### ![ja__JP](https://img.shields.io/badge/ja__JP-77.40%25-yellow)
Missing keys in ja_JP.axaml @@ -419,7 +418,6 @@ This document shows the translation status of each locale file in the repository - Text.ExecuteCustomAction.Repository - Text.GitFlow.FinishWithPush - Text.GitFlow.FinishWithSquash -- Text.Hotkeys.Global.SwitchWorkspace - Text.Hotkeys.Global.SwitchTab - Text.Hotkeys.TextEditor.OpenExternalMergeTool - Text.InteractiveRebase.ReorderTip @@ -515,7 +513,7 @@ This document shows the translation status of each locale file in the repository
-### ![pt__BR](https://img.shields.io/badge/pt__BR-70.95%25-red) +### ![pt__BR](https://img.shields.io/badge/pt__BR-71.03%25-red)
Missing keys in pt_BR.axaml @@ -655,7 +653,6 @@ This document shows the translation status of each locale file in the repository - Text.GitFlow.FinishWithPush - Text.GitFlow.FinishWithSquash - Text.Hotkeys.Global.Clone -- Text.Hotkeys.Global.SwitchWorkspace - Text.Hotkeys.Global.SwitchTab - Text.Hotkeys.TextEditor.OpenExternalMergeTool - Text.InProgress.CherryPick.Head @@ -796,7 +793,7 @@ This document shows the translation status of each locale file in the repository
-### ![ta__IN](https://img.shields.io/badge/ta__IN-77.43%25-yellow) +### ![ta__IN](https://img.shields.io/badge/ta__IN-77.52%25-yellow)
Missing keys in ta_IN.axaml @@ -911,7 +908,6 @@ This document shows the translation status of each locale file in the repository - Text.ExecuteCustomAction.Repository - Text.GitFlow.FinishWithPush - Text.GitFlow.FinishWithSquash -- Text.Hotkeys.Global.SwitchWorkspace - Text.Hotkeys.Global.SwitchTab - Text.Hotkeys.TextEditor.OpenExternalMergeTool - Text.InteractiveRebase.ReorderTip @@ -1006,7 +1002,7 @@ This document shows the translation status of each locale file in the repository
-### ![uk__UA](https://img.shields.io/badge/uk__UA-78.55%25-yellow) +### ![uk__UA](https://img.shields.io/badge/uk__UA-78.64%25-yellow)
Missing keys in uk_UA.axaml @@ -1116,7 +1112,6 @@ This document shows the translation status of each locale file in the repository - Text.ExecuteCustomAction.Repository - Text.GitFlow.FinishWithPush - Text.GitFlow.FinishWithSquash -- Text.Hotkeys.Global.SwitchWorkspace - Text.Hotkeys.Global.SwitchTab - Text.Hotkeys.TextEditor.OpenExternalMergeTool - Text.InteractiveRebase.ReorderTip diff --git a/src/Resources/Locales/de_DE.axaml b/src/Resources/Locales/de_DE.axaml index a5f25ec49..c770bdcf5 100644 --- a/src/Resources/Locales/de_DE.axaml +++ b/src/Resources/Locales/de_DE.axaml @@ -462,7 +462,6 @@ Zum vorherigen Tab wechseln Neuen Tab erstellen Einstellungen öffnen - Aktiven Arbeitsplatz wechseln Aktiven Tab wechseln REPOSITORY Gestagte Änderungen committen diff --git a/src/Resources/Locales/es_ES.axaml b/src/Resources/Locales/es_ES.axaml index 5a47584bb..76c6748ea 100644 --- a/src/Resources/Locales/es_ES.axaml +++ b/src/Resources/Locales/es_ES.axaml @@ -461,7 +461,6 @@ Ir a la página anterior Crear nueva página Abrir diálogo de preferencias - Cambiar espacio de trabajo activo Cambiar página activa REPOSITORIO Commit cambios staged diff --git a/src/Resources/Locales/id_ID.axaml b/src/Resources/Locales/id_ID.axaml index 4a784d948..19601f7c7 100644 --- a/src/Resources/Locales/id_ID.axaml +++ b/src/Resources/Locales/id_ID.axaml @@ -456,7 +456,6 @@ Ke tab sebelumnya Buat tab baru Buka dialog Preferensi - Ganti workspace aktif Ganti tab aktif REPOSITORI Commit perubahan yang di-stage diff --git a/src/Resources/Locales/it_IT.axaml b/src/Resources/Locales/it_IT.axaml index b95b3d33d..f726f5984 100644 --- a/src/Resources/Locales/it_IT.axaml +++ b/src/Resources/Locales/it_IT.axaml @@ -442,7 +442,6 @@ Vai alla pagina precedente Crea una nuova pagina Apri la finestra delle preferenze - Cambia workspace attivo Cambia scheda attiva REPOSITORY Committa le modifiche in tsage diff --git a/src/Resources/Locales/ru_RU.axaml b/src/Resources/Locales/ru_RU.axaml index 3a0d458a8..d73d336dc 100644 --- a/src/Resources/Locales/ru_RU.axaml +++ b/src/Resources/Locales/ru_RU.axaml @@ -461,7 +461,6 @@ Перейти на предыдущую вкладку Создать новую вкладку Открыть диалоговое окно настроек - Переключиться на рабочее место Переключиться на вкладку РЕПОЗИТОРИЙ Зафиксировать сформированные изменения diff --git a/src/Resources/Locales/zh_CN.axaml b/src/Resources/Locales/zh_CN.axaml index f511682f4..94a2d5da8 100644 --- a/src/Resources/Locales/zh_CN.axaml +++ b/src/Resources/Locales/zh_CN.axaml @@ -471,7 +471,6 @@ 切换到上一个页面 新建页面 打开偏好设置面板 - 切换工作区 切换显示页面 仓库页面快捷键 提交暂存区更改 diff --git a/src/Resources/Locales/zh_TW.axaml b/src/Resources/Locales/zh_TW.axaml index 46d2ee76e..9aa98f5c5 100644 --- a/src/Resources/Locales/zh_TW.axaml +++ b/src/Resources/Locales/zh_TW.axaml @@ -471,7 +471,6 @@ 切換到上一個頁面 新增頁面 開啟偏好設定面板 - 切換工作區 切換目前頁面 存放庫頁面快速鍵 提交暫存區變更 From 9193e17ed6f9c4087140b28fa25dfb34950b23cd Mon Sep 17 00:00:00 2001 From: leo Date: Thu, 16 Oct 2025 18:02:05 +0800 Subject: [PATCH 0041/1230] refactor: use `git-bash.exe` instead of `bin/bash.exe` to make sure bash-profiles work (#1856) Signed-off-by: leo --- src/Native/Windows.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Native/Windows.cs b/src/Native/Windows.cs index aa1b42c86..8439d702a 100644 --- a/src/Native/Windows.cs +++ b/src/Native/Windows.cs @@ -144,7 +144,7 @@ public string FindTerminal(Models.ShellOrTerminal shell) break; var binDir = Path.GetDirectoryName(OS.GitExecutable)!; - var bash = Path.Combine(binDir, "bash.exe"); + var bash = Path.GetFullPath(Path.Combine(binDir, "..", "git-bash.exe")); if (!File.Exists(bash)) break; From 8846bfe4a7fe4b08b62b1f6be7f027dc44e652f9 Mon Sep 17 00:00:00 2001 From: leo Date: Thu, 16 Oct 2025 18:08:58 +0800 Subject: [PATCH 0042/1230] enhance: temp ignore `Ctrl+Shift+P` Signed-off-by: leo --- src/Views/Launcher.axaml.cs | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/Views/Launcher.axaml.cs b/src/Views/Launcher.axaml.cs index a712c24b0..b1a3a8b00 100644 --- a/src/Views/Launcher.axaml.cs +++ b/src/Views/Launcher.axaml.cs @@ -174,6 +174,12 @@ protected override async void OnKeyDown(KeyEventArgs e) if (e.KeyModifiers.HasFlag(OperatingSystem.IsMacOS() ? KeyModifiers.Meta : KeyModifiers.Control)) { + if (e.Key == Key.P && e.KeyModifiers.HasFlag(KeyModifiers.Shift)) + { + // TODO: Command Palette + return; + } + if (e.Key == Key.P) { vm.OpenTabSwitcher(); From 0b4be6e22630df98d56674a774ec17bea76203f1 Mon Sep 17 00:00:00 2001 From: leo Date: Fri, 17 Oct 2025 10:42:23 +0800 Subject: [PATCH 0043/1230] refactor: global hotkeys Signed-off-by: leo --- src/Views/Launcher.axaml | 9 +++++-- src/Views/Launcher.axaml.cs | 47 ++++++++++------------------------ src/Views/LauncherTabBar.axaml | 2 +- 3 files changed, 22 insertions(+), 36 deletions(-) diff --git a/src/Views/Launcher.axaml b/src/Views/Launcher.axaml index cecf34a4d..c45947b80 100644 --- a/src/Views/Launcher.axaml +++ b/src/Views/Launcher.axaml @@ -72,11 +72,16 @@ -
-### ![ru__RU](https://img.shields.io/badge/ru__RU-99.44%25-yellow) - -
-Missing keys in ru_RU.axaml - -- Text.BranchCM.CreatePR -- Text.BranchCM.CreatePRForUpstream -- Text.Configure.CommitMessageTemplate.BuiltinVars -- Text.PushToNewBranch -- Text.PushToNewBranch.Title - -
+### ![ru__RU](https://img.shields.io/badge/ru__RU-%E2%88%9A-brightgreen) ### ![ta__IN](https://img.shields.io/badge/ta__IN-77.52%25-yellow) From 56a332f64ae3f5913e3ad8b7c1773ee9af65967a Mon Sep 17 00:00:00 2001 From: leo Date: Mon, 20 Oct 2025 11:56:51 +0800 Subject: [PATCH 0050/1230] feature: supports to goto previous revision (not in undo/redo history, but the file itself) in `Blame` view (#1686) Signed-off-by: leo --- src/Resources/Icons.axaml | 1 + src/Resources/Locales/de_DE.axaml | 2 +- src/Resources/Locales/en_US.axaml | 3 +- src/Resources/Locales/es_ES.axaml | 2 +- src/Resources/Locales/fr_FR.axaml | 2 +- src/Resources/Locales/id_ID.axaml | 2 +- src/Resources/Locales/it_IT.axaml | 2 +- src/Resources/Locales/ja_JP.axaml | 2 +- src/Resources/Locales/pt_BR.axaml | 2 +- src/Resources/Locales/ru_RU.axaml | 2 +- src/Resources/Locales/ta_IN.axaml | 2 +- src/Resources/Locales/uk_UA.axaml | 2 +- src/Resources/Locales/zh_CN.axaml | 3 +- src/Resources/Locales/zh_TW.axaml | 3 +- src/ViewModels/Blame.cs | 57 +++++++++++++++++++------------ src/Views/Blame.axaml | 13 +++++-- 16 files changed, 64 insertions(+), 36 deletions(-) diff --git a/src/Resources/Icons.axaml b/src/Resources/Icons.axaml index 3103e68f7..ea1530a5a 100644 --- a/src/Resources/Icons.axaml +++ b/src/Resources/Icons.axaml @@ -59,6 +59,7 @@ M884 159l-18-18a43 43 0 00-38-12l-235 43a166 166 0 00-101 60L400 349a128 128 0 00-148 47l-120 171a21 21 0 005 29l17 12a128 128 0 00178-32l27-38 124 124-38 27a128 128 0 00-32 178l12 17a21 21 0 0029 5l171-120a128 128 0 0047-148l117-92A166 166 0 00853 431l43-235a43 43 0 00-12-38zm-177 249a64 64 0 110-90 64 64 0 010 90zm-373 312a21 21 0 010 30l-139 139a21 21 0 01-30 0l-30-30a21 21 0 010-30l139-139a21 21 0 0130 0z M525 0C235 0 0 235 0 525c0 232 150 429 359 498 26 5 36-11 36-25 0-12-1-54-1-97-146 31-176-63-176-63-23-61-58-76-58-76-48-32 3-32 3-32 53 3 81 54 81 54 47 80 123 57 153 43 4-34 18-57 33-70-116-12-239-57-239-259 0-57 21-104 54-141-5-13-23-67 5-139 0 0 44-14 144 54 42-11 87-17 131-17s90 6 131 17C756 203 801 217 801 217c29 72 10 126 5 139 34 37 54 83 54 141 0 202-123 246-240 259 19 17 36 48 36 97 0 70-1 127-1 144 0 14 10 30 36 25 209-70 359-266 359-498C1050 235 814 0 525 0z M590 74 859 342V876c0 38-31 68-68 68H233c-38 0-68-31-68-68V142c0-38 31-68 68-68h357zm-12 28H233a40 40 0 00-40 38L193 142v734a40 40 0 0038 40L233 916h558a40 40 0 0040-38L831 876V354L578 102zM855 371h-215c-46 0-83-36-84-82l0-2V74h28v213c0 30 24 54 54 55l2 0h215v28zM57 489m28 0 853 0q28 0 28 28l0 284q0 28-28 28l-853 0q-28 0-28-28l0-284q0-28 28-28ZM157 717c15 0 29-6 37-13v-51h-41v22h17v18c-2 2-6 3-10 3-21 0-30-13-30-34 0-21 12-34 28-34 9 0 15 4 20 9l14-17C184 610 172 603 156 603c-29 0-54 21-54 57 0 37 24 56 54 56zM245 711v-108h-34v108h34zm69 0v-86H341V603H262v22h28V711h24zM393 711v-108h-34v108h34zm66 6c15 0 29-6 37-13v-51h-41v22h17v18c-2 2-6 3-10 3-21 0-30-13-30-34 0-21 12-34 28-34 9 0 15 4 20 9l14-17C485 610 474 603 458 603c-29 0-54 21-54 57 0 37 24 56 54 56zm88-6v-36c0-13-2-28-3-40h1l10 24 25 52H603v-108h-23v36c0 13 2 28 3 40h-1l-10-24L548 603H523v108h23zM677 717c30 0 51-22 51-57 0-36-21-56-51-56-30 0-51 20-51 56 0 36 21 57 51 57zm3-23c-16 0-26-12-26-32 0-19 10-31 26-31 16 0 26 11 26 31S696 694 680 694zm93 17v-38h13l21 38H836l-25-43c12-5 19-15 19-31 0-26-20-34-44-34H745v108h27zm16-51H774v-34h15c16 0 25 4 25 16s-9 18-25 18zM922 711v-22h-43v-23h35v-22h-35V625h41V603H853v108h68z + M727 641c-78 0-142 55-157 128H256V320h251c16 108 108 192 221 192 124 0 224-100 224-224S851 64 727 64c-113 0-205 84-221 192H96c-18 0-32 14-32 32s14 32 32 32h96v482c0 18 14 32 32 32h347c15 73 79 128 157 128 88 0 160-72 160-160s-72-160-160-160zm0 256c-53 0-96-43-96-96s43-96 96-96 96 43 96 96-43 96-96 96z M30 271l241 0 0-241-241 0 0 241zM392 271l241 0 0-241-241 0 0 241zM753 30l0 241 241 0 0-241-241 0zM30 632l241 0 0-241-241 0 0 241zM392 632l241 0 0-241-241 0 0 241zM753 632l241 0 0-241-241 0 0 241zM30 994l241 0 0-241-241 0 0 241zM392 994l241 0 0-241-241 0 0 241zM753 994l241 0 0-241-241 0 0 241z M566 585l37-146-145 0-37 146 145 0zM1005 297l-32 128q-4 14-18 14l-187 0-37 146 178 0q9 0 14 7 6 8 3 16l-32 128q-3 14-18 14l-187 0-46 187q-4 14-18 14l-128 0q-9 0-15-7-5-7-3-16l45-178-145 0-46 187q-4 14-18 14l-129 0q-9 0-14-7-5-7-3-16l45-178-178 0q-9 0-14-7-5-7-3-16l32-128q4-14 18-14l187 0 37-146-178 0q-9 0-14-7-6-8-3-16l32-128q3-14 18-14l187 0 46-187q4-14 18-14l128 0q9 0 14 7 5 7 3 16l-45 178 145 0 46-187q4-14 18-14l128 0q9 0 14 7 5 7 3 16l-45 178 178 0q9 0 14 7 5 7 3 16z M0 512M1024 512M512 0M512 1024M955 323q0 23-16 39l-414 414-78 78q-16 16-39 16t-39-16l-78-78-207-207q-16-16-16-39t16-39l78-78q16-16 39-16t39 16l168 169 375-375q16-16 39-16t39 16l78 78q16 16 16 39z diff --git a/src/Resources/Locales/de_DE.axaml b/src/Resources/Locales/de_DE.axaml index c770bdcf5..0eb6a90fc 100644 --- a/src/Resources/Locales/de_DE.axaml +++ b/src/Resources/Locales/de_DE.axaml @@ -56,7 +56,7 @@ Überspringen Bisecting. Aktuellen Commit als gut oder schlecht markieren und einen anderen auschecken. Blame - BLAME WIRD BEI DIESER DATEI NICHT UNTERSTÜTZT!!! + BLAME WIRD BEI DIESER DATEI NICHT UNTERSTÜTZT!!! Auschecken von ${0}$... Mit ${0}$ vergleichen Mit Worktree vergleichen diff --git a/src/Resources/Locales/en_US.axaml b/src/Resources/Locales/en_US.axaml index c0c465aeb..1b978bc56 100644 --- a/src/Resources/Locales/en_US.axaml +++ b/src/Resources/Locales/en_US.axaml @@ -52,7 +52,8 @@ Skip Bisecting. Mark current commit as good or bad and checkout another one. Blame - BLAME ON THIS FILE IS NOT SUPPORTED!!! + Blame on Previous Revision + BLAME ON THIS FILE IS NOT SUPPORTED!!! Checkout ${0}$... Compare with ${0}$ Compare with Worktree diff --git a/src/Resources/Locales/es_ES.axaml b/src/Resources/Locales/es_ES.axaml index 76c6748ea..16131682a 100644 --- a/src/Resources/Locales/es_ES.axaml +++ b/src/Resources/Locales/es_ES.axaml @@ -56,7 +56,7 @@ Saltar Bisecting. Marcar el commit actual como bueno o malo y revisar otro. Blame - ¡BLAME EN ESTE ARCHIVO NO SOPORTADO! + ¡BLAME EN ESTE ARCHIVO NO SOPORTADO! Checkout ${0}$... Comparar con ${0}$ Comparar con Worktree diff --git a/src/Resources/Locales/fr_FR.axaml b/src/Resources/Locales/fr_FR.axaml index 2052f93ac..ceebf7363 100644 --- a/src/Resources/Locales/fr_FR.axaml +++ b/src/Resources/Locales/fr_FR.axaml @@ -41,7 +41,7 @@ Rafraîchir FICHIER BINAIRE NON SUPPORTÉ !!! Blâme - LE BLÂME SUR CE FICHIER N'EST PAS SUPPORTÉ!!! + LE BLÂME SUR CE FICHIER N'EST PAS SUPPORTÉ!!! Récupérer ${0}$... Comparer avec ${0}$ Comparer avec le worktree diff --git a/src/Resources/Locales/id_ID.axaml b/src/Resources/Locales/id_ID.axaml index 19601f7c7..1e8a6c697 100644 --- a/src/Resources/Locales/id_ID.axaml +++ b/src/Resources/Locales/id_ID.axaml @@ -55,7 +55,7 @@ Lewati Bisect berjalan. Tandai commit saat ini sebagai baik atau buruk dan checkout yang lain. Blame - BLAME PADA BERKAS INI TIDAK DIDUKUNG!!! + BLAME PADA BERKAS INI TIDAK DIDUKUNG!!! Checkout ${0}$... Bandingkan dengan ${0}$ Bandingkan dengan Worktree diff --git a/src/Resources/Locales/it_IT.axaml b/src/Resources/Locales/it_IT.axaml index f726f5984..3ddab9feb 100644 --- a/src/Resources/Locales/it_IT.axaml +++ b/src/Resources/Locales/it_IT.axaml @@ -55,7 +55,7 @@ Salta Bisecando. Marca il commit corrente come buono o cattivo e fai checkout di un altro. Attribuisci - L'ATTRIBUZIONE SU QUESTO FILE NON È SUPPORTATA!!! + L'ATTRIBUZIONE SU QUESTO FILE NON È SUPPORTATA!!! Checkout ${0}$... Confronta con ${0}$ Confronta con Worktree diff --git a/src/Resources/Locales/ja_JP.axaml b/src/Resources/Locales/ja_JP.axaml index 958ceb9f9..6d7723083 100644 --- a/src/Resources/Locales/ja_JP.axaml +++ b/src/Resources/Locales/ja_JP.axaml @@ -41,7 +41,7 @@ 更新 バイナリファイルはサポートされていません!!! Blame - BLAMEではこのファイルはサポートされていません!!! + BLAMEではこのファイルはサポートされていません!!! ${0}$ をチェックアウトする... ワークツリーと比較 ブランチ名をコピー diff --git a/src/Resources/Locales/pt_BR.axaml b/src/Resources/Locales/pt_BR.axaml index 24569a93f..04ececee5 100644 --- a/src/Resources/Locales/pt_BR.axaml +++ b/src/Resources/Locales/pt_BR.axaml @@ -35,7 +35,7 @@ Atualizar ARQUIVO BINÁRIO NÃO SUPORTADO!!! Blame - BLAME NESTE ARQUIVO NÃO É SUPORTADO!!! + BLAME NESTE ARQUIVO NÃO É SUPORTADO!!! Checkout ${0}$... Comparar com ${0}$ Comparar com Worktree diff --git a/src/Resources/Locales/ru_RU.axaml b/src/Resources/Locales/ru_RU.axaml index 9d0eb5b1b..56db54577 100644 --- a/src/Resources/Locales/ru_RU.axaml +++ b/src/Resources/Locales/ru_RU.axaml @@ -56,7 +56,7 @@ Пропустить Раздвоение. Сделать текущую ревизию хорошей или плохой и переключиться на другой. Расследование - РАССЛЕДОВАНИЕ В ЭТОМ ФАЙЛЕ НЕ ПОДДЕРЖИВАЕТСЯ!!! + РАССЛЕДОВАНИЕ В ЭТОМ ФАЙЛЕ НЕ ПОДДЕРЖИВАЕТСЯ!!! Переключиться на ${0}$... Сравнить с ${0}$ Сравнить с рабочим каталогом diff --git a/src/Resources/Locales/ta_IN.axaml b/src/Resources/Locales/ta_IN.axaml index e98dfc058..8c1029345 100644 --- a/src/Resources/Locales/ta_IN.axaml +++ b/src/Resources/Locales/ta_IN.axaml @@ -41,7 +41,7 @@ புதுப்பி இருமம் கோப்பு ஆதரிக்கப்படவில்லை!!! குற்றச்சாட்டு - இந்த கோப்பில் குற்றம் சாட்ட ஆதரிக்கப்படவில்லை!!! + இந்த கோப்பில் குற்றம் சாட்ட ஆதரிக்கப்படவில்லை!!! ${0}$ சரிபார்... பணிமரத்துடன் ஒப்பிடுக கிளை பெயரை நகலெடு diff --git a/src/Resources/Locales/uk_UA.axaml b/src/Resources/Locales/uk_UA.axaml index 6b91933e1..7c5814ebe 100644 --- a/src/Resources/Locales/uk_UA.axaml +++ b/src/Resources/Locales/uk_UA.axaml @@ -41,7 +41,7 @@ Оновити БІНАРНИЙ ФАЙЛ НЕ ПІДТРИМУЄТЬСЯ!!! Автор рядка - ПОШУК АВТОРА РЯДКА ДЛЯ ЦЬОГО ФАЙЛУ НЕ ПІДТРИМУЄТЬСЯ!!! + ПОШУК АВТОРА РЯДКА ДЛЯ ЦЬОГО ФАЙЛУ НЕ ПІДТРИМУЄТЬСЯ!!! Перейти на ${0}$... Порівняти з ${0}$ Порівняти з робочим деревом diff --git a/src/Resources/Locales/zh_CN.axaml b/src/Resources/Locales/zh_CN.axaml index 94a2d5da8..8700104d8 100644 --- a/src/Resources/Locales/zh_CN.axaml +++ b/src/Resources/Locales/zh_CN.axaml @@ -56,7 +56,8 @@ 无法判定 二分定位进行中。请标记当前的提交是 '正确' 还是 '错误',然后检出另一个提交。 逐行追溯(blame) - 选中文件不支持该操作!!! + 对当前版本的前一版本执行逐行追溯操作 + 选中文件不支持该操作!!! 检出(checkout) ${0}$... 与当前 ${0}$ 比较 与本地工作树比较 diff --git a/src/Resources/Locales/zh_TW.axaml b/src/Resources/Locales/zh_TW.axaml index 9aa98f5c5..29cd3807c 100644 --- a/src/Resources/Locales/zh_TW.axaml +++ b/src/Resources/Locales/zh_TW.axaml @@ -56,7 +56,8 @@ 無法確認 二分搜尋進行中。請標記目前的提交為「良好」或「錯誤」,然後簽出另一個提交。 逐行溯源 (blame) - 所選擇的檔案不支援該操作! + 對當前版本的先前版本執行逐行溯源操作。 + 所選擇的檔案不支援該操作! 簽出 (checkout) ${0}$... 與目前 ${0}$ 比較 與本機工作區比較 diff --git a/src/ViewModels/Blame.cs b/src/ViewModels/Blame.cs index e8b01e412..fda29964c 100644 --- a/src/ViewModels/Blame.cs +++ b/src/ViewModels/Blame.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.Text; using System.Threading; using System.Threading.Tasks; using Avalonia.Threading; @@ -20,6 +21,12 @@ public Models.Commit Revision private set => SetProperty(ref _revision, value); } + public Models.Commit PrevRevision + { + get => _prevRevision; + private set => SetProperty(ref _prevRevision, value); + } + public Models.BlameData Data { get => _data; @@ -51,10 +58,10 @@ public Blame(string repo, string file, Models.Commit commit) FilePath = file; Revision = commit; + PrevRevision = null; _repo = repo; _navigationHistory.Add(sha); - _commits.Add(sha, commit); SetBlameData(sha); } @@ -90,6 +97,14 @@ public void Forward() NavigateToCommit(_navigationHistory[_navigationActiveIndex], true); } + public void GotoPrevRevision() + { + if (_prevRevision == null) + return; + + NavigateToCommit(_prevRevision.SHA, false); + } + public void NavigateToCommit(string commitSHA, bool isBackOrForward) { if (Revision.SHA.StartsWith(commitSHA, StringComparison.Ordinal)) @@ -130,28 +145,28 @@ private void SetBlameData(string commitSHA) _cancellationSource = new CancellationTokenSource(); var token = _cancellationSource.Token; - if (_commits.TryGetValue(commitSHA, out var c)) - { - Revision = c; - } - else + Task.Run(async () => { - Task.Run(async () => - { - var result = await new Commands.QuerySingleCommit(_repo, commitSHA) - .GetResultAsync() - .ConfigureAwait(false); + var argsBuilder = new StringBuilder(); + argsBuilder + .Append("--date-order -n 2 ") + .Append(commitSHA ?? string.Empty) + .Append(" -- ") + .Append(FilePath.Quoted()); + + var commits = await new Commands.QueryCommits(_repo, argsBuilder.ToString(), false) + .GetResultAsync() + .ConfigureAwait(false); - Dispatcher.UIThread.Post(() => + Dispatcher.UIThread.Post(() => + { + if (!token.IsCancellationRequested) { - if (!token.IsCancellationRequested) - { - _commits.Add(commitSHA, result); - Revision = result ?? new Models.Commit() { SHA = commitSHA }; - } - }); - }, token); - } + Revision = commits.Count > 0 ? commits[0] : null; + PrevRevision = commits.Count == 2 ? commits[1] : null; + } + }); + }); Task.Run(async () => { @@ -169,11 +184,11 @@ private void SetBlameData(string commitSHA) private string _repo; private Models.Commit _revision; + private Models.Commit _prevRevision; private CancellationTokenSource _cancellationSource = null; private int _navigationActiveIndex = 0; private List _navigationHistory = []; private Models.BlameData _data = null; - private Dictionary _commits = new(); private Dictionary _commitMessages = new(); } } diff --git a/src/Views/Blame.axaml b/src/Views/Blame.axaml index bd8a2f811..af27d3563 100644 --- a/src/Views/Blame.axaml +++ b/src/Views/Blame.axaml @@ -38,7 +38,7 @@ - + @@ -76,6 +76,15 @@ TextTrimming="CharacterEllipsis"/> + +
@@ -107,7 +116,7 @@ HorizontalAlignment="Center" VerticalAlignment="Center" IsVisible="{Binding IsBinary}"> - + From 6e70b7421277ef87707eca31c493a812be63e595 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Mon, 20 Oct 2025 03:57:13 +0000 Subject: [PATCH 0051/1230] doc: Update translation status and sort locale files --- TRANSLATION.md | 36 ++++++++++++++++++++++++++---------- 1 file changed, 26 insertions(+), 10 deletions(-) diff --git a/TRANSLATION.md b/TRANSLATION.md index dff27b046..ca9c88be8 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.44%25-yellow) +### ![de__DE](https://img.shields.io/badge/de__DE-99.33%25-yellow)
Missing keys in de_DE.axaml +- Text.Blame.BlameOnPreviousRevision - Text.BranchCM.CreatePR - Text.BranchCM.CreatePRForUpstream - Text.Configure.CommitMessageTemplate.BuiltinVars @@ -19,11 +20,12 @@ This document shows the translation status of each locale file in the repository
-### ![es__ES](https://img.shields.io/badge/es__ES-99.44%25-yellow) +### ![es__ES](https://img.shields.io/badge/es__ES-99.33%25-yellow)
Missing keys in es_ES.axaml +- Text.Blame.BlameOnPreviousRevision - Text.BranchCM.CreatePR - Text.BranchCM.CreatePRForUpstream - Text.Configure.CommitMessageTemplate.BuiltinVars @@ -32,7 +34,7 @@ This document shows the translation status of each locale file in the repository
-### ![fr__FR](https://img.shields.io/badge/fr__FR-77.40%25-yellow) +### ![fr__FR](https://img.shields.io/badge/fr__FR-77.32%25-yellow)
Missing keys in fr_FR.axaml @@ -52,6 +54,7 @@ This document shows the translation status of each locale file in the repository - Text.Bisect.Good - Text.Bisect.Skip - Text.Bisect.WaitingForRange +- Text.Blame.BlameOnPreviousRevision - Text.BranchCM.CreatePR - Text.BranchCM.CreatePRForUpstream - Text.BranchCM.ResetToSelectedCommit @@ -242,12 +245,13 @@ This document shows the translation status of each locale file in the repository
-### ![id__ID](https://img.shields.io/badge/id__ID-98.88%25-yellow) +### ![id__ID](https://img.shields.io/badge/id__ID-98.77%25-yellow)
Missing keys in id_ID.axaml - Text.About.ReleaseNotes +- Text.Blame.BlameOnPreviousRevision - Text.BranchCM.CreatePR - Text.BranchCM.CreatePRForUpstream - Text.CommitCM.Drop @@ -260,12 +264,13 @@ This document shows the translation status of each locale file in the repository
-### ![it__IT](https://img.shields.io/badge/it__IT-96.09%25-yellow) +### ![it__IT](https://img.shields.io/badge/it__IT-95.98%25-yellow)
Missing keys in it_IT.axaml - Text.About.ReleaseNotes +- Text.Blame.BlameOnPreviousRevision - Text.BranchCM.CreatePR - Text.BranchCM.CreatePRForUpstream - Text.BranchCM.SwitchToWorktree @@ -303,7 +308,7 @@ This document shows the translation status of each locale file in the repository
-### ![ja__JP](https://img.shields.io/badge/ja__JP-77.40%25-yellow) +### ![ja__JP](https://img.shields.io/badge/ja__JP-77.32%25-yellow)
Missing keys in ja_JP.axaml @@ -323,6 +328,7 @@ This document shows the translation status of each locale file in the repository - Text.Bisect.Good - Text.Bisect.Skip - Text.Bisect.WaitingForRange +- Text.Blame.BlameOnPreviousRevision - Text.BranchCM.CompareWithCurrent - Text.BranchCM.CreatePR - Text.BranchCM.CreatePRForUpstream @@ -513,7 +519,7 @@ This document shows the translation status of each locale file in the repository
-### ![pt__BR](https://img.shields.io/badge/pt__BR-71.03%25-red) +### ![pt__BR](https://img.shields.io/badge/pt__BR-70.95%25-red)
Missing keys in pt_BR.axaml @@ -539,6 +545,7 @@ This document shows the translation status of each locale file in the repository - Text.Bisect.Good - Text.Bisect.Skip - Text.Bisect.WaitingForRange +- Text.Blame.BlameOnPreviousRevision - Text.BranchCM.CreatePR - Text.BranchCM.CreatePRForUpstream - Text.BranchCM.CustomAction @@ -780,9 +787,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.89%25-yellow) -### ![ta__IN](https://img.shields.io/badge/ta__IN-77.52%25-yellow) +
+Missing keys in ru_RU.axaml + +- Text.Blame.BlameOnPreviousRevision + +
+ +### ![ta__IN](https://img.shields.io/badge/ta__IN-77.43%25-yellow)
Missing keys in ta_IN.axaml @@ -802,6 +816,7 @@ This document shows the translation status of each locale file in the repository - Text.Bisect.Good - Text.Bisect.Skip - Text.Bisect.WaitingForRange +- Text.Blame.BlameOnPreviousRevision - Text.BranchCM.CompareWithCurrent - Text.BranchCM.CreatePR - Text.BranchCM.CreatePRForUpstream @@ -991,7 +1006,7 @@ This document shows the translation status of each locale file in the repository
-### ![uk__UA](https://img.shields.io/badge/uk__UA-78.64%25-yellow) +### ![uk__UA](https://img.shields.io/badge/uk__UA-78.55%25-yellow)
Missing keys in uk_UA.axaml @@ -1011,6 +1026,7 @@ This document shows the translation status of each locale file in the repository - Text.Bisect.Good - Text.Bisect.Skip - Text.Bisect.WaitingForRange +- Text.Blame.BlameOnPreviousRevision - Text.BranchCM.CreatePR - Text.BranchCM.CreatePRForUpstream - Text.BranchCM.ResetToSelectedCommit From 0c34c54e5c563342ba4d8de942a2475b8922f9b8 Mon Sep 17 00:00:00 2001 From: Junhyung Choi Date: Mon, 20 Oct 2025 14:55:16 +0900 Subject: [PATCH 0052/1230] localization: add Korean(ko_KR) support (#1864) * localization: add Korean(ko_KR) support * [FIX] typo --- src/App.axaml | 1 + src/Models/Locales.cs | 1 + src/Resources/Locales/ko_KR.axaml | 903 ++++++++++++++++++++++++++++++ 3 files changed, 905 insertions(+) create mode 100644 src/Resources/Locales/ko_KR.axaml diff --git a/src/App.axaml b/src/App.axaml index 36c17decd..a7a0c17f2 100644 --- a/src/App.axaml +++ b/src/App.axaml @@ -24,6 +24,7 @@ + diff --git a/src/Models/Locales.cs b/src/Models/Locales.cs index 42b878208..027433336 100644 --- a/src/Models/Locales.cs +++ b/src/Models/Locales.cs @@ -21,6 +21,7 @@ public class Locale new Locale("繁體中文", "zh_TW"), new Locale("日本語", "ja_JP"), new Locale("தமிழ் (Tamil)", "ta_IN"), + new Locale("한국어", "ko_KR"), }; public Locale(string name, string key) diff --git a/src/Resources/Locales/ko_KR.axaml b/src/Resources/Locales/ko_KR.axaml new file mode 100644 index 000000000..ea262f0b0 --- /dev/null +++ b/src/Resources/Locales/ko_KR.axaml @@ -0,0 +1,903 @@ + +정보 + SourceGit 정보 + 릴리스 노트 + 오픈소스 & 무료 Git GUI 클라이언트 + 무시할 파일 추가 + 패턴: + 저장 파일: + 워크트리 추가 + 위치: + 이 워크트리의 경로입니다. 상대 경로를 지원합니다. + 브랜치 이름: + 선택 사항. 기본값은 대상 폴더 이름입니다. + 추적할 브랜치: + 원격 브랜치 추적 + 체크아웃할 대상: + 새 브랜치 생성 + 기존 브랜치 + AI 어시스턴트 + 재생성 + AI를 사용하여 커밋 메시지 생성 + 커밋 메시지로 적용 + SourceGit 숨기기 + 모두 보기 + 패치 + 패치 파일: + 적용할 .patch 파일을 선택하세요 + 공백 변경 사항 무시 + 패치 적용 + 공백: + 스태시 적용 + 적용 후 삭제 + 인덱스의 변경 사항 복원 + 스태시: + 아카이브... + 아카이브 저장 위치: + 아카이브 파일 경로 선택 + 리비전: + 아카이브 + SourceGit Askpass + 암호 입력: + 변경되지 않음으로 간주된 파일 + 변경되지 않음으로 간주된 파일 없음 + 이미지 불러오기... + 새로 고침 + 바이너리 파일은 지원되지 않습니다!!! + 이진 탐색 + 중단 + 나쁨 + 이진 탐색 중. 현재 HEAD가 '좋음' 상태입니까, '나쁨' 상태입니까? + 좋음 + 건너뛰기 + 이진 탐색 중. 현재 커밋을 '좋음' 또는 '나쁨'으로 표시하고 다른 커밋을 체크아웃하세요. + 블레임 + 이 파일은 블레임을 지원하지 않습니다!!! + ${0}$ 체크아웃... + ${0}$와(과) 비교 + 워크트리와 비교 + 브랜치 이름 복사 + 사용자 지정 작업 + ${0}$ 삭제... + 선택한 {0}개의 브랜치 삭제 + ${0}$(으)로 Fast-Forward + ${0}$에서 ${1}$(으)로 Fetch... + Git Flow - ${0}$ 완료 + ${0}$을(를) ${1}$(으)로 병합... + 선택한 {0}개의 브랜치를 현재 브랜치로 병합 + ${0}$ Pull + ${0}$에서 ${1}$(으)로 Pull... + ${0}$ Push + ${1}$을(를) 기반으로 ${0}$ 리베이스... + ${0}$ 이름 바꾸기... + ${0}$을(를) ${1}$(으)로 리셋... + ${0}$(워크트리)로 전환 + 추적 브랜치 설정... + 브랜치 비교 + {0}개 커밋 앞섬 + {0}개 커밋 앞섬, {1}개 커밋 뒤처짐 + {0}개 커밋 뒤처짐 + 유효하지 않음 + 원격 + 상태 + 추적 중 + URL + 워크트리 + 취소 + 부모 리비전으로 리셋 + 이 리비전으로 리셋 + 커밋 메시지 생성 + 표시 모드 변경 + 파일 및 디렉터리 목록으로 보기 + 경로 목록으로 보기 + 파일 시스템 트리로 보기 + 서브모듈 URL 변경 + 서브모듈: + URL: + 브랜치 체크아웃 + 커밋 체크아웃 + 커밋: + 경고: 커밋 체크아웃을 하면, HEAD가 분리됩니다(detached) + 로컬 변경 사항: + 폐기 + 스태시 & 재적용 + 모든 서브모듈 업데이트 + 브랜치: + 현재 HEAD에 브랜치/태그에 연결되지 않은 커밋이 있습니다! 계속하시겠습니까? + 체크아웃 & Fast-Forward + Fast-Forward 대상: + 체리픽 + 커밋 메시지에 원본 추가 + 커밋: + 모든 변경 사항 커밋 + 메인라인: + 어느 쪽을 메인라인으로 간주해야 할지 알 수 없기 때문에 일반적으로 병합(merge)을 체리픽할 수 없습니다. 이 옵션을 사용하면 지정된 부모를 기준으로 변경 사항을 다시 적용할 수 있습니다. + 모든 스태시 지우기 + 모든 스태시를 지우려고 합니다. 계속하시겠습니까? + 원격 저장소 복제 + 추가 파라미터: + 저장소 복제 시 추가 인수. 선택 사항. + 로컬 이름: + 저장소 이름. 선택 사항. + 상위 폴더: + 서브모듈 초기화 & 업데이트 + 저장소 URL: + 닫기 + 에디터 + 커밋 체크아웃 + 커밋 체리픽 + 체리픽... + HEAD와 비교 + 워크트리와 비교 + 작성자 + 메시지 + 커밋터 + SHA + 제목 + 사용자 지정 작업 + 커밋 삭제 + 대화형 리베이스 + 삭제(Drop)... + 수정(Edit)... + 부모에 합치기(Fixup)... + ${1}$을(를) 기반으로 ${0}$ 대화형 리베이스 + 메시지 수정(Reword)... + 부모에 합치기(Squash)... + ${0}$(으)로 병합 + 병합... + ${0}$을(를) ${1}$(으)로 푸시 + ${1}$을(를) 기반으로 ${0}$ 리베이스 + ${0}$을(를) ${1}$(으)로 리셋 + 커밋 되돌리기 + 메시지 수정 + 패치로 저장... + 부모에 합치기 + 변경 사항 + 변경된 파일 + 변경 사항 검색... + 파일 + LFS 파일 + 파일 검색... + 서브모듈 + 정보 + 작성자 + 자식 + 커밋터 + 이 커밋을 포함하는 ref 확인 + 커밋 포함 REF + 이메일 복사 + 이름 복사 + 이름 & 이메일 복사 + 처음 100개의 변경 사항만 표시합니다. 모든 변경 사항은 '변경 사항' 탭에서 확인하세요. + 키: + 메시지 + 부모 + REFS + SHA + 서명자: + 브라우저에서 열기 + 설명 + 붙여넣기 (모두 바꾸기) + 제목 + 커밋 제목 입력 + 저장소 설정 + 커밋 템플릿 + ${files_num}, ${branch_name}, ${files} 및 ${files:N} (N은 출력할 최대 파일 경로 수)을(를) 사용할 수 있습니다. + 템플릿 내용: + 템플릿 이름: + 사용자 지정 작업 + 인수: + 내장 파라미터: + + ${REPO} 저장소 경로 + ${REMOTE} 선택한 원격 또는 선택한 브랜치의 원격 + ${BRANCH} 선택한 브랜치 (원격 브랜치의 경우 ${REMOTE} 부분 제외) + ${BRANCH_FRIENDLY_NAME} 선택한 브랜치의 식별하기 쉬운 이름 (원격 브랜치의 경우 ${REMOTE} 부분 포함) + ${SHA} 선택한 커밋의 해시 + ${TAG} 선택한 태그 + $1, $2 ... 입력 컨트롤 값 + 실행 파일: + 입력 컨트롤: + 편집 + 이름: + 범위: + 브랜치 + 커밋 + 원격 + 저장소 + 태그 + 작업이 끝날 때까지 대기 + 이메일 주소 + 이메일 주소 + GIT + 원격 자동 Fetch + + 기본 원격 + 선호하는 병합 모드 + 이슈 트래커 + Azure DevOps 규칙 추가 + Gerrit Change-Id 커밋 규칙 추가 + Gitee 이슈 규칙 추가 + Gitee Pull Request 규칙 추가 + GitHub 규칙 추가 + GitLab 이슈 규칙 추가 + GitLab Merge Request 규칙 추가 + Jira 규칙 추가 + 새 규칙 + 이슈 정규식: + 규칙 이름: + .issuetracker 파일에 이 규칙 공유 + 결과 URL: + 정규식 그룹 값에 접근하려면 $1, $2를 사용하세요. + AI + 선호하는 서비스: + '선호하는 서비스'가 설정되면, SourceGit은 이 저장소에서 해당 서비스만 사용합니다. 그렇지 않고 사용 가능한 서비스가 두 개 이상인 경우, 하나를 선택할 수 있는 컨텍스트 메뉴가 표시됩니다. + HTTP 프록시 + 이 저장소에서 사용하는 HTTP 프록시 + 사용자 이름 + 이 저장소의 사용자 이름 + 사용자 지정 작업 컨트롤 편집 + 선택 시 값: + 선택 시, 이 값이 명령줄 인수로 사용됩니다 + 설명: + 기본값: + 폴더 여부: + 레이블: + 옵션: + 옵션 구분자로 '|'를 사용하세요 + 유형: + 작업 공간 + 색상 + 이름 + 시작 시 탭 복원 + 계속 + 빈 커밋이 감지되었습니다! 계속하시겠습니까 (--allow-empty)? + 모두 스테이징 & 커밋 + 빈 커밋이 감지되었습니다! 계속하시겠습니까 (--allow-empty) 아니면 모두 스테이징 후 커밋하시겠습니까? + 재시작 필요 + 변경 사항을 적용하려면 앱을 다시 시작해야 합니다. + Conventional Commit 도우미 + 주요 변경 사항(Breaking Change): + 종료된 이슈: + 상세 변경 내역: + 범위: + 간단한 설명: + 변경 유형: + 복사 + 전체 텍스트 복사 + 전체 경로 복사 + 경로 복사 + 브랜치 생성... + 기준: + 생성된 브랜치로 체크아웃 + 로컬 변경 사항: + 폐기 + 스태시 & 재적용 + 새 브랜치 이름: + 브랜치 이름을 입력하세요. + 로컬 브랜치 생성 + 기존 브랜치 덮어쓰기 + 태그 생성... + 태그 생성 위치: + GPG 서명 + 태그 메시지: + 선택 사항. + 태그 이름: + 권장 형식: v1.0.0-alpha + 생성 후 모든 원격에 푸시 + 새 태그 생성 + 종류: + 주석 태그 + 경량 태그 + Ctrl을 누른 채 클릭하면 바로 시작합니다 + 잘라내기 + 서브모듈 초기화 해제 + 로컬 변경 사항이 있어도 강제로 초기화 해제합니다. + 서브모듈: + 브랜치 삭제 + 브랜치: + 원격 브랜치를 삭제하려고 합니다!!! + 원격 브랜치 ${0}$도 함께 삭제 + 여러 브랜치 삭제 + 한 번에 여러 브랜치를 삭제하려고 합니다. 실행하기 전에 다시 한번 확인하세요! + 여러 태그 삭제 + 원격 저장소에서도 삭제 + 한 번에 여러 태그를 삭제하려고 합니다. 실행하기 전에 다시 한번 확인하세요! + 원격 삭제 + 원격: + 경로: + 대상: + 모든 하위 항목이 목록에서 제거됩니다. + 목록에서만 제거되며, 디스크에서 삭제되지 않습니다! + 그룹 삭제 확인 + 저장소 삭제 확인 + 서브모듈 삭제 + 서브모듈 경로: + 태그 삭제 + 태그: + 원격 저장소에서도 삭제 + 바이너리 비교 + 파일 모드 변경됨 + 첫 번째 차이점 + 모든 공백 변경 사항 무시 + 혼합 + 차이점 + 나란히 보기 + 스와이프 + 마지막 차이점 + LFS 객체 변경 + 신규 + 다음 차이점 + 변경 사항 없음 또는 줄바꿈(EOL) 변경만 있음 + 기존 + 이전 차이점 + 패치로 저장 + 숨겨진 기호 표시 + 나란히 비교 + 서브모듈 + 삭제됨 + 신규 + 전환 + 구문 강조 + 줄 바꿈 + 병합 도구에서 열기 + 모든 줄 표시 + 표시 줄 수 줄이기 + 표시 줄 수 늘리기 + 파일을 선택하여 변경 사항 보기 + 디렉터리 히스토리 + 로컬 변경 사항 있음 + 업스트림과 불일치 + 이미 최신 상태 + 변경 사항 폐기 + 작업 사본의 모든 로컬 변경 사항. + 변경 사항: + 무시된 파일 포함 + 추적하지 않는 파일 포함 + {0}개의 변경 사항이 폐기됩니다 + 이 작업은 되돌릴 수 없습니다!!! + 커밋 삭제 + 커밋: + 새 HEAD: + 북마크: + 새 이름: + 대상: + 선택한 그룹 편집 + 선택한 저장소 편집 + 대상: + 이 저장소 + Fetch + 모든 원격 Fetch + 로컬 ref 강제 덮어쓰기 + 태그 없이 Fetch + 원격: + 원격 변경 사항 Fetch + 변경되지 않음으로 간주 + 폐기... + {0}개 파일 폐기... + ${0}$을(를) 사용하여 해결 + 패치로 저장... + 스테이지 + {0}개 파일 스테이지 + 스태시... + {0}개 파일 스태시... + 언스테이지 + {0}개 파일 언스테이지 + 내 것 사용 (checkout --ours) + 상대방 것 사용 (checkout --theirs) + 파일 히스토리 + 변경 사항 + 내용 + Git-Flow + 개발 브랜치: + Feature: + Feature 접두사: + FLOW - Feature 완료 + FLOW - Hotfix 완료 + FLOW - Release 완료 + 대상: + 완료 후 원격(들)에 푸시 + 병합 시 스쿼시 + 핫픽스: + Hotfix 접두사: + Git-Flow 초기화 + 브랜치 유지 + 운영 브랜치: + 릴리스: + Release 접두사: + Feature 시작... + FLOW - Feature 시작 + Hotfix 시작... + FLOW - Hotfix 시작 + 이름 입력 + Release 시작... + FLOW - Release 시작 + 버전 태그 접두사: + Git LFS + 추적 패턴 추가... + 패턴이 파일 이름임 + 사용자 정의 패턴: + Git LFS에 추적 패턴 추가 + Fetch + Git LFS 객체를 다운로드하려면 `git lfs fetch`를 실행하세요. 이 작업은 작업 사본을 업데이트하지 않습니다. + LFS 객체 Fetch + Git LFS 훅(hook) 설치 + 잠금 보기 + 잠긴 파일 없음 + 잠금 + 내 잠금만 보기 + LFS 잠금 + 잠금 해제 + 강제 잠금 해제 + 정리 + 로컬 저장소에서 오래된 LFS 파일을 삭제하려면 `git lfs prune`을 실행하세요 + Pull + 현재 ref 및 체크아웃에 대한 모든 Git LFS 파일을 다운로드하려면 `git lfs pull`을 실행하세요 + LFS 객체 Pull + 푸시 + 대기 중인 대용량 파일을 Git LFS 엔드포인트로 푸시합니다 + LFS 객체 푸시 + 원격: + '{0}' 이름의 파일 추적 + 모든 *{0} 파일 추적 + 히스토리 + 작성자 + 작성 시간 + 그래프 & 제목 + SHA + 커밋 시간 + {0}개 커밋 선택됨 + 'Ctrl' 또는 'Shift' 키를 누른 채로 여러 커밋을 선택하세요. + ⌘ 또는 ⇧ 키를 누른 채로 여러 커밋을 선택하세요. + 팁: + 키보드 단축키 참조 + 전역 + 새 저장소 복제 + 현재 탭 닫기 + 다음 탭으로 이동 + 이전 탭으로 이동 + 새 탭 만들기 + 환경설정 대화상자 열기 + 활성 작업 공간 전환 + 활성 탭 전환 + 저장소 + 스테이징된 변경 사항 커밋 + 스테이징된 변경 사항 커밋 및 푸시 + 모든 변경 사항 스테이징 후 커밋 + Fetch (바로 시작) + 대시보드 모드 (기본) + 커밋 검색 모드 열기 + Pull (바로 시작) + 푸시 (바로 시작) + 이 저장소 강제 새로고침 + '변경 사항'으로 전환 + '히스토리'로 전환 + '스태시'로 전환 + 텍스트 에디터 + 검색 패널 닫기 + 다음 일치 항목 찾기 + 이전 일치 항목 찾기 + 외부 diff/merge 도구로 열기 + 검색 패널 열기 + 폐기 + 스테이지 + 언스테이지 + 저장소 초기화 + 경로: + 체리픽 진행 중. + 커밋 처리 중 + 병합 진행 중. + 병합 중 + 리베이스 진행 중. + 중단 지점 + 되돌리기 진행 중. + 커밋 되돌리는 중 + 대화형 리베이스 + 로컬 변경 사항 스태시 & 재적용 + 기준: + 드래그 앤 드롭으로 커밋 순서 변경 + 대상 브랜치: + 링크 복사 + 브라우저에서 열기 + 오류 + 알림 + + 작업 공간 + 브랜치 병합 + 병합 메시지 수정 + 대상: + 병합 옵션: + 소스: + 병합 (다중) + 모든 변경 사항 커밋 + 전략: + 대상: + 서브모듈 이동 + 이동 위치: + 서브모듈: + 저장소 노드 이동 + 상위 노드 선택: + 이름: + Git이 구성되지 않았습니다. [환경설정]으로 이동하여 먼저 구성하세요. + 데이터 저장 디렉터리 열기 + 병합 도구에서 열기 + 다음으로 열기... + 선택 사항. + 새 탭 만들기 + 북마크 + 탭 닫기 + 다른 탭 닫기 + 오른쪽 탭 닫기 + 저장소 경로 복사 + 저장소 + 붙여넣기 + {0}일 전 + 1시간 전 + {0}시간 전 + 방금 전 + 지난 달 + 작년 + {0}분 전 + {0}개월 전 + {0}년 전 + 어제 + 환경설정 + AI + Diff 분석 프롬프트 + API 키 + 제목 생성 프롬프트 + 모델 + 이름 + 입력된 값은 환경변수(ENV)에서 API 키를 불러올 이름입니다 + 서버 + 스트리밍 활성화 + 모양 + 기본 글꼴 + 에디터 탭 너비 + 글꼴 크기 + 기본 + 에디터 + 고정폭 글꼴 + 텍스트 에디터에서만 고정폭 글꼴 사용 + 테마 + 테마 재정의 + 스크롤바 자동 숨기기 사용 + 제목 표시줄에 고정폭 탭 사용 + 네이티브 윈도우 프레임 사용 + DIFF/MERGE 도구 + 설치 경로 + diff/merge 도구 경로 입력 + 도구 + 일반 + 시작 시 업데이트 확인 + 날짜 형식 + 변경 사항 트리에서 폴더 압축 활성화 + 언어 + 히스토리 커밋 수 + 그래프에 커밋 시간 대신 작성자 시간 표시 + 기본으로 `로컬 변경 사항` 페이지 표시 + 커밋 세부 정보에서 기본으로 `변경 사항` 탭 표시 + 커밋 세부 정보에 자식 커밋 표시 + 커밋 그래프에 태그 표시 + 제목 가이드 길이 + GitHub 스타일 기본 아바타 생성 + GIT + 자동 CRLF 활성화 + 기본 복제 디렉터리 + 사용자 이메일 + 전역 git 사용자 이메일 + Fetch 시 --prune 활성화 + diff 시 --ignore-cr-at-eol 활성화 + 이 앱은 Git (>= 2.25.1)을(를) 필요로 합니다 + 설치 경로 + HTTP SSL 검증 활성화 + git-credential-manager 대신 git-credential-libsecret 사용 + 사용자 이름 + 전역 git 사용자 이름 + Git 버전 + GPG 서명 + 커밋 GPG 서명 + GPG 형식 + 프로그램 설치 경로 + 설치된 gpg 프로그램 경로 입력 + 태그 GPG 서명 + 사용자 서명 키 + 사용자의 gpg 서명 키 + 연동 + 셸/터미널 + 경로 + 셸/터미널 + 원격 정리 + 대상: + 워크트리 정리 + `$GIT_COMMON_DIR/worktrees`의 워크트리 정보 정리 + Pull + 원격 브랜치: + 대상: + 로컬 변경 사항: + 폐기 + 스태시 & 재적용 + 모든 서브모듈 업데이트 + 원격: + Pull (Fetch & 병합) + 병합 대신 리베이스 사용 + 푸시 + 서브모듈이 푸시되었는지 확인 + 강제 푸시 + 로컬 브랜치: + 신규 + 원격: + 리비전: + 리비전을 원격에 푸시 + 변경 사항을 원격에 푸시 + 원격 브랜치: + 추적 브랜치로 설정 + 모든 태그 푸시 + 태그를 원격에 푸시 + 모든 원격에 푸시 + 원격: + 태그: + 종료 + 현재 브랜치 리베이스 + 로컬 변경 사항 스태시 & 재적용 + 기준: + 원격 추가 + 원격 편집 + 이름: + 원격 이름 + 저장소 URL: + 원격 git 저장소 URL + URL 복사 + 사용자 지정 작업 + 삭제... + 편집... + Fetch + 브라우저에서 열기 + 정리 + 워크트리 제거 확인 + `--force` 옵션 활성화 + 대상: + 브랜치 이름 변경 + 새 이름: + 이 브랜치의 고유한 이름 + 브랜치: + 중단 + 원격에서 변경 사항 자동 Fetch 중... + 정렬 + 커밋 날짜 순 + 이름 순 + 정리 (GC & Prune) + 이 저장소에 대해 `git gc` 명령을 실행합니다. + 모두 지우기 + 지우기 + 이 저장소 설정 + 계속 + 사용자 지정 작업 + 사용자 지정 작업 없음 + 대시보드 + 모든 변경 사항 폐기 + 파일 탐색기에서 열기 + 브랜치/태그/서브모듈 검색 + 그래프에 표시 여부 + 설정 안 함 + 커밋 그래프에서 숨기기 + 커밋 그래프에서 필터링 + 레이아웃 + 수평 + 수직 + 커밋 순서 + 커밋 날짜 + 위상 정렬 + 로컬 브랜치 + 추가 옵션... + HEAD로 이동 + 브랜치 생성 + 알림 지우기 + 현재 브랜치만 강조 + {0}에서 열기 + 외부 도구에서 열기 + 원격 + 원격 추가 + 커밋 검색 + 작성자 + 커밋터 + 내용 + 메시지 + 경로 + SHA + 현재 브랜치 + 장식된(Decorated) 커밋만 + 첫 번째 부모만 + 플래그 표시 + 유실된(Lost) 커밋 표시 + 서브모듈을 트리로 표시 + 태그를 트리로 표시 + 건너뛰기 + 통계 + 서브모듈 + 서브모듈 추가 + 서브모듈 업데이트 + 태그 + 새 태그 + 생성 날짜 순 + 이름 순 + 정렬 + 터미널에서 열기 + 상대 시간 사용 + 로그 보기 + 브라우저에서 '{0}' 방문 + 워크트리 + 워크트리 추가 + 정리 + Git 저장소 URL + 현재 브랜치를 리비전으로 리셋 + 리셋 모드: + 이동 대상: + 현재 브랜치: + 브랜치 리셋 (체크아웃 없음) + 이동 대상: + 브랜치: + 파일 탐색기에서 보기 + 커밋 되돌리기 + 커밋: + 되돌린 변경 사항 커밋 + 커밋 메시지 수정 + 실행 중. 잠시만 기다려주세요... + 저장 + 다른 이름으로 저장... + 패치가 성공적으로 저장되었습니다! + 저장소 스캔 + 루트 디렉터리: + 다른 사용자 정의 디렉터리 스캔 + 업데이트 확인... + 이 소프트웨어의 새 버전을 사용할 수 있습니다: + 업데이트 확인 실패! + 다운로드 + 이 버전 건너뛰기 + 소프트웨어 업데이트 + 현재 사용 가능한 업데이트가 없습니다. + 서브모듈 브랜치 설정 + 서브모듈: + 현재: + 변경: + 선택 사항. 비어 있으면 기본값으로 설정됩니다. + 추적 브랜치 설정 + 브랜치: + 업스트림 설정 해제 + 업스트림: + SHA 복사 + 이동 + 커밋 스쿼시 + 대상: + SSH 개인 키: + 개인 SSH 키 저장 경로 + 시작 + 스태시 + 추적하지 않는 파일 포함 + 메시지: + 선택 사항. 이 스태시의 메시지 + 모드: + 스테이징된 변경 사항만 + 선택한 파일의 스테이징된 변경 사항과 스테이징되지 않은 변경 사항이 모두 스태시됩니다!!! + 로컬 변경 사항 스태시 + 적용 + 메시지 복사 + 삭제 + 패치로 저장... + 스태시 삭제 + 삭제: + 스태시 + 변경 사항 + 스태시 + 통계 + 개요 + 이번 달 + 이번 주 + 작성자: + 커밋: + 서브모듈 + 서브모듈 추가 + 브랜치 + 브랜치 + 상대 경로 + 초기화 해제 + 중첩된 서브모듈 Fetch + 히스토리 + 이동 + 저장소 열기 + 상대 경로: + 이 모듈을 저장할 상대 폴더입니다. + 삭제 + 브랜치 설정 + URL 변경 + 상태 + 수정됨 + 초기화 안 됨 + 리비전 변경됨 + 병합 안 됨 + 업데이트 + URL + 확인 + 태그 생성자 + 시간 + 메시지 + 이름 + 태그 생성자 + 태그 이름 복사 + 사용자 지정 작업 + ${0}$ 삭제... + 선택한 {0}개의 태그 삭제... + ${0}$을(를) ${1}$(으)로 병합... + ${0}$ 푸시... + 서브모듈 업데이트 + 모든 서브모듈 + 필요시 초기화 + 서브모듈 재귀적으로 탐색 + 서브모듈: + 서브모듈의 원격 추적 브랜치로 업데이트 + URL: + 로그 + 모두 지우기 + 복사 + 삭제 + 경고 + 시작 페이지 + 그룹 생성 + 하위 그룹 생성 + 저장소 복제 + 삭제 + 폴더 끌어다 놓기 지원. 사용자 정의 그룹화 지원. + 편집 + 다른 그룹으로 이동 + 모든 저장소 열기 + 저장소 열기 + 터미널 열기 + 기본 복제 디렉터리의 저장소 다시 스캔 + 저장소 검색... + 로컬 변경 사항 + Git 무시 + 모든 *{0} 파일 무시 + 같은 폴더의 *{0} 파일 무시 + 이 폴더의 추적하지 않는 파일 무시 + 이 파일만 무시 + 수정 + 이제 이 파일을 스테이징할 수 있습니다. + 히스토리 지우기 + 모든 커밋 메시지 히스토리를 지우시겠습니까? 이 작업은 되돌릴 수 없습니다. + 커밋 + 커밋 & 푸시 + 템플릿/히스토리 + 클릭 이벤트 트리거 + 커밋 (수정) + 모든 변경 사항 스테이징 후 커밋 + 분리된(detached) HEAD에 커밋을 생성하고 있습니다. 계속하시겠습니까? + {0}개의 파일을 스테이징했지만 {1}개의 파일만 표시됩니다 ({2}개의 파일은 필터링됨). 계속하시겠습니까? + 충돌 감지됨 + 외부 병합 도구 열기 + 모든 충돌을 외부 병합 도구에서 열기 + 파일 충돌 해결됨 + 내 것 사용 + 상대방 것 사용 + 추적하지 않는 파일 포함 + 최근 입력한 메시지 없음 + 커밋 템플릿 없음 + 검증 안 함 + 작성자 리셋 + 서명(SignOff) + 스테이징됨 + 언스테이지 + 모두 언스테이지 + 스테이징 안 됨 + 스테이지 + 모두 스테이지 + 변경되지 않음으로 간주된 파일 보기 + 템플릿: ${0}$ + 작업 공간: + 작업 공간 설정... + 워크트리 + 경로 복사 + 잠금 + 열기 + 제거 + 잠금 해제 + From c963473219a0de60f62fcaececc985aa9f422171 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Mon, 20 Oct 2025 05:55:36 +0000 Subject: [PATCH 0053/1230] doc: Update translation status and sort locale files --- TRANSLATION.md | 15 +++++++++++++++ src/Resources/Locales/ko_KR.axaml | 8 ++------ 2 files changed, 17 insertions(+), 6 deletions(-) diff --git a/TRANSLATION.md b/TRANSLATION.md index ca9c88be8..b91e2c84d 100644 --- a/TRANSLATION.md +++ b/TRANSLATION.md @@ -519,6 +519,21 @@ This document shows the translation status of each locale file in the repository
+### ![ko__KR](https://img.shields.io/badge/ko__KR-99.22%25-yellow) + +
+Missing keys in ko_KR.axaml + +- Text.Blame.BlameOnPreviousRevision +- Text.Blame.TypeNotSupported +- Text.BranchCM.CreatePR +- Text.BranchCM.CreatePRForUpstream +- Text.PushToNewBranch +- Text.PushToNewBranch.Title +- Text.Submodule.Status.Unmerged + +
+ ### ![pt__BR](https://img.shields.io/badge/pt__BR-70.95%25-red)
diff --git a/src/Resources/Locales/ko_KR.axaml b/src/Resources/Locales/ko_KR.axaml index ea262f0b0..35769b8bf 100644 --- a/src/Resources/Locales/ko_KR.axaml +++ b/src/Resources/Locales/ko_KR.axaml @@ -1,5 +1,6 @@ -정보 + + 정보 SourceGit 정보 릴리스 노트 오픈소스 & 무료 Git GUI 클라이언트 @@ -52,7 +53,6 @@ 건너뛰기 이진 탐색 중. 현재 커밋을 '좋음' 또는 '나쁨'으로 표시하고 다른 커밋을 체크아웃하세요. 블레임 - 이 파일은 블레임을 지원하지 않습니다!!! ${0}$ 체크아웃... ${0}$와(과) 비교 워크트리와 비교 @@ -458,7 +458,6 @@ 이전 탭으로 이동 새 탭 만들기 환경설정 대화상자 열기 - 활성 작업 공간 전환 활성 탭 전환 저장소 스테이징된 변경 사항 커밋 @@ -558,11 +557,9 @@ 기본 에디터 고정폭 글꼴 - 텍스트 에디터에서만 고정폭 글꼴 사용 테마 테마 재정의 스크롤바 자동 숨기기 사용 - 제목 표시줄에 고정폭 탭 사용 네이티브 윈도우 프레임 사용 DIFF/MERGE 도구 설치 경로 @@ -814,7 +811,6 @@ 수정됨 초기화 안 됨 리비전 변경됨 - 병합 안 됨 업데이트 URL 확인 From 6907e2220cc4c225120b78572e35dfdd9fbe540a Mon Sep 17 00:00:00 2001 From: leo Date: Mon, 20 Oct 2025 13:58:11 +0800 Subject: [PATCH 0054/1230] =?UTF-8?q?doc:=20add=20`=ED=95=9C=EA=B5=AD?= =?UTF-8?q?=EC=96=B4`=20translations?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: leo --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 5f0c037bd..25de6dd87 100644 --- a/README.md +++ b/README.md @@ -11,7 +11,7 @@ * Supports Windows/macOS/Linux * Opensource/Free * Fast -* Deutsch/English/Español/Bahasa Indonesia/Français/Italiano/Português/Русский/Українська/简体中文/繁體中文/日本語/தமிழ் (Tamil) +* Deutsch/English/Español/Bahasa Indonesia/Français/Italiano/Português/Русский/Українська/简体中文/繁體中文/日本語/தமிழ் (Tamil)/한국어 * Built-in light/dark themes * Customize theme * Visual commit graph From 11d66c67118b84b541c74931b999e8444f2bdbca Mon Sep 17 00:00:00 2001 From: leo Date: Mon, 20 Oct 2025 15:06:09 +0800 Subject: [PATCH 0055/1230] ux: main tab close icon style Signed-off-by: leo --- src/Views/LauncherTabBar.axaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Views/LauncherTabBar.axaml b/src/Views/LauncherTabBar.axaml index c82656edc..d3f23b91b 100644 --- a/src/Views/LauncherTabBar.axaml +++ b/src/Views/LauncherTabBar.axaml @@ -107,7 +107,7 @@ + + + + - - - + diff --git a/src/Views/RepositoryConfigure.axaml.cs b/src/Views/RepositoryConfigure.axaml.cs index 3ce581d76..2c5622df1 100644 --- a/src/Views/RepositoryConfigure.axaml.cs +++ b/src/Views/RepositoryConfigure.axaml.cs @@ -21,6 +21,21 @@ protected override async void OnClosing(WindowClosingEventArgs e) await configure.SaveAsync(); } + private async void SelectConventionalTypesFile(object sender, RoutedEventArgs e) + { + var options = new FilePickerOpenOptions() + { + FileTypeFilter = [new FilePickerFileType("Conventional Commit Types") { Patterns = ["*.json"] }], + AllowMultiple = false, + }; + + var selected = await StorageProvider.OpenFilePickerAsync(options); + if (selected.Count == 1 && DataContext is ViewModels.RepositoryConfigure vm) + vm.ConventionalTypesOverride = selected[0].Path.LocalPath; + + e.Handled = true; + } + private async void SelectExecutableForCustomAction(object sender, RoutedEventArgs e) { var options = new FilePickerOpenOptions() From 33c6d9d0ef29b21635f276da0d0dccd3fc4f06d1 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Mon, 20 Oct 2025 13:24:57 +0000 Subject: [PATCH 0057/1230] doc: Update translation status and sort locale files --- TRANSLATION.md | 33 ++++++++++++++++++++++----------- 1 file changed, 22 insertions(+), 11 deletions(-) diff --git a/TRANSLATION.md b/TRANSLATION.md index b91e2c84d..ca6bac8d0 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-99.33%25-yellow) +### ![de__DE](https://img.shields.io/badge/de__DE-99.22%25-yellow)
Missing keys in de_DE.axaml @@ -15,12 +15,13 @@ This document shows the translation status of each locale file in the repository - Text.BranchCM.CreatePR - Text.BranchCM.CreatePRForUpstream - Text.Configure.CommitMessageTemplate.BuiltinVars +- Text.Configure.Git.ConventionalTypesOverride - Text.PushToNewBranch - Text.PushToNewBranch.Title
-### ![es__ES](https://img.shields.io/badge/es__ES-99.33%25-yellow) +### ![es__ES](https://img.shields.io/badge/es__ES-99.22%25-yellow)
Missing keys in es_ES.axaml @@ -29,12 +30,13 @@ This document shows the translation status of each locale file in the repository - Text.BranchCM.CreatePR - Text.BranchCM.CreatePRForUpstream - Text.Configure.CommitMessageTemplate.BuiltinVars +- Text.Configure.Git.ConventionalTypesOverride - Text.PushToNewBranch - Text.PushToNewBranch.Title
-### ![fr__FR](https://img.shields.io/badge/fr__FR-77.32%25-yellow) +### ![fr__FR](https://img.shields.io/badge/fr__FR-77.23%25-yellow)
Missing keys in fr_FR.axaml @@ -104,6 +106,7 @@ This document shows the translation status of each locale file in the repository - Text.Configure.CustomAction.InputControls.Edit - Text.Configure.CustomAction.Scope.Remote - Text.Configure.CustomAction.Scope.Tag +- Text.Configure.Git.ConventionalTypesOverride - Text.Configure.Git.PreferredMergeMode - Text.Configure.IssueTracker.AddSampleGerritChangeIdCommit - Text.Configure.IssueTracker.Share @@ -245,7 +248,7 @@ This document shows the translation status of each locale file in the repository
-### ![id__ID](https://img.shields.io/badge/id__ID-98.77%25-yellow) +### ![id__ID](https://img.shields.io/badge/id__ID-98.66%25-yellow)
Missing keys in id_ID.axaml @@ -256,6 +259,7 @@ This document shows the translation status of each locale file in the repository - Text.BranchCM.CreatePRForUpstream - Text.CommitCM.Drop - Text.Configure.CommitMessageTemplate.BuiltinVars +- Text.Configure.Git.ConventionalTypesOverride - Text.DropHead - Text.DropHead.Commit - Text.DropHead.NewHead @@ -264,7 +268,7 @@ This document shows the translation status of each locale file in the repository
-### ![it__IT](https://img.shields.io/badge/it__IT-95.98%25-yellow) +### ![it__IT](https://img.shields.io/badge/it__IT-95.87%25-yellow)
Missing keys in it_IT.axaml @@ -285,6 +289,7 @@ This document shows the translation status of each locale file in the repository - Text.CommitDetail.Info.CopyNameAndEmail - Text.CommitMessageTextBox.PasteAndReplaceAll - Text.Configure.CommitMessageTemplate.BuiltinVars +- Text.Configure.Git.ConventionalTypesOverride - Text.Diff.Image.Difference - Text.DirtyState.HasLocalChanges - Text.DirtyState.HasPendingPullOrPush @@ -308,7 +313,7 @@ This document shows the translation status of each locale file in the repository
-### ![ja__JP](https://img.shields.io/badge/ja__JP-77.32%25-yellow) +### ![ja__JP](https://img.shields.io/badge/ja__JP-77.23%25-yellow)
Missing keys in ja_JP.axaml @@ -379,6 +384,7 @@ This document shows the translation status of each locale file in the repository - Text.Configure.CustomAction.InputControls.Edit - Text.Configure.CustomAction.Scope.Remote - Text.Configure.CustomAction.Scope.Tag +- Text.Configure.Git.ConventionalTypesOverride - Text.Configure.Git.PreferredMergeMode - Text.Configure.IssueTracker.AddSampleGerritChangeIdCommit - Text.Configure.IssueTracker.Share @@ -519,7 +525,7 @@ This document shows the translation status of each locale file in the repository
-### ![ko__KR](https://img.shields.io/badge/ko__KR-99.22%25-yellow) +### ![ko__KR](https://img.shields.io/badge/ko__KR-99.11%25-yellow)
Missing keys in ko_KR.axaml @@ -528,13 +534,14 @@ This document shows the translation status of each locale file in the repository - Text.Blame.TypeNotSupported - Text.BranchCM.CreatePR - Text.BranchCM.CreatePRForUpstream +- Text.Configure.Git.ConventionalTypesOverride - Text.PushToNewBranch - Text.PushToNewBranch.Title - Text.Submodule.Status.Unmerged
-### ![pt__BR](https://img.shields.io/badge/pt__BR-70.95%25-red) +### ![pt__BR](https://img.shields.io/badge/pt__BR-70.87%25-red)
Missing keys in pt_BR.axaml @@ -619,6 +626,7 @@ This document shows the translation status of each locale file in the repository - Text.Configure.CustomAction.Scope.Remote - Text.Configure.CustomAction.Scope.Tag - Text.Configure.CustomAction.WaitForExit +- Text.Configure.Git.ConventionalTypesOverride - Text.Configure.Git.PreferredMergeMode - Text.Configure.IssueTracker.AddSampleGerritChangeIdCommit - Text.Configure.IssueTracker.AddSampleGiteeIssue @@ -802,16 +810,17 @@ This document shows the translation status of each locale file in the repository
-### ![ru__RU](https://img.shields.io/badge/ru__RU-99.89%25-yellow) +### ![ru__RU](https://img.shields.io/badge/ru__RU-99.78%25-yellow)
Missing keys in ru_RU.axaml - Text.Blame.BlameOnPreviousRevision +- Text.Configure.Git.ConventionalTypesOverride
-### ![ta__IN](https://img.shields.io/badge/ta__IN-77.43%25-yellow) +### ![ta__IN](https://img.shields.io/badge/ta__IN-77.34%25-yellow)
Missing keys in ta_IN.axaml @@ -882,6 +891,7 @@ This document shows the translation status of each locale file in the repository - Text.Configure.CustomAction.InputControls.Edit - Text.Configure.CustomAction.Scope.Remote - Text.Configure.CustomAction.Scope.Tag +- Text.Configure.Git.ConventionalTypesOverride - Text.Configure.Git.PreferredMergeMode - Text.Configure.IssueTracker.AddSampleGerritChangeIdCommit - Text.Configure.IssueTracker.Share @@ -1021,7 +1031,7 @@ This document shows the translation status of each locale file in the repository
-### ![uk__UA](https://img.shields.io/badge/uk__UA-78.55%25-yellow) +### ![uk__UA](https://img.shields.io/badge/uk__UA-78.46%25-yellow)
Missing keys in uk_UA.axaml @@ -1091,6 +1101,7 @@ This document shows the translation status of each locale file in the repository - Text.Configure.CustomAction.InputControls.Edit - Text.Configure.CustomAction.Scope.Remote - Text.Configure.CustomAction.Scope.Tag +- Text.Configure.Git.ConventionalTypesOverride - Text.Configure.IssueTracker.AddSampleGerritChangeIdCommit - Text.Configure.IssueTracker.Share - Text.ConfigureCustomActionControls From d9b2b45922ef53ba09e357ec7db08b45d6f192a3 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: Mon, 20 Oct 2025 20:31:25 -0600 Subject: [PATCH 0058/1230] localization: update Spanish translation (#1863) Add missing strings. --- src/Resources/Locales/es_ES.axaml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/Resources/Locales/es_ES.axaml b/src/Resources/Locales/es_ES.axaml index 16131682a..44634488a 100644 --- a/src/Resources/Locales/es_ES.axaml +++ b/src/Resources/Locales/es_ES.axaml @@ -61,6 +61,8 @@ Comparar con ${0}$ Comparar con Worktree Copiar Nombre de la Rama + Crear PR... + Crear PR para upstream ${0}$... Acción personalizada Eliminar ${0}$... Eliminar {0} ramas seleccionadas @@ -637,6 +639,8 @@ Push a todos los remotos Remoto: Etiqueta: + Push a una NUEVA rama + Nombre de entrada de la nueva rama remota: Salir Rebase Rama Actual Stash & reaplicar cambios locales From 2925ae06066545f6cc98c81ebdeb515e3188d5bc Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Tue, 21 Oct 2025 02:31:49 +0000 Subject: [PATCH 0059/1230] doc: Update translation status and sort locale files --- TRANSLATION.md | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/TRANSLATION.md b/TRANSLATION.md index ca6bac8d0..b0bd02cf1 100644 --- a/TRANSLATION.md +++ b/TRANSLATION.md @@ -21,18 +21,14 @@ This document shows the translation status of each locale file in the repository
-### ![es__ES](https://img.shields.io/badge/es__ES-99.22%25-yellow) +### ![es__ES](https://img.shields.io/badge/es__ES-99.67%25-yellow)
Missing keys in es_ES.axaml - Text.Blame.BlameOnPreviousRevision -- Text.BranchCM.CreatePR -- Text.BranchCM.CreatePRForUpstream - Text.Configure.CommitMessageTemplate.BuiltinVars - Text.Configure.Git.ConventionalTypesOverride -- Text.PushToNewBranch -- Text.PushToNewBranch.Title
From 27e3a97d7e93f0631efbae17d1c9b9969f3ec8db Mon Sep 17 00:00:00 2001 From: leo Date: Tue, 21 Oct 2025 10:49:48 +0800 Subject: [PATCH 0060/1230] ux: use another label for string values in custom input control because $1, $2... is not available here Signed-off-by: leo --- src/Resources/Locales/en_US.axaml | 1 + src/Resources/Locales/zh_CN.axaml | 1 + src/Resources/Locales/zh_TW.axaml | 1 + src/Views/ConfigureCustomActionControls.axaml | 2 +- 4 files changed, 4 insertions(+), 1 deletion(-) diff --git a/src/Resources/Locales/en_US.axaml b/src/Resources/Locales/en_US.axaml index 0e4e87330..011f26c7f 100644 --- a/src/Resources/Locales/en_US.axaml +++ b/src/Resources/Locales/en_US.axaml @@ -256,6 +256,7 @@ Label: Options: Use '|' as delimiter for options + The built-in variables ${REPO}, ${REMOTE}, ${BRANCH}, ${BRANCH_FRIENDLY_NAME}, ${SHA}, and ${TAG} remain available here Type: Workspaces Color diff --git a/src/Resources/Locales/zh_CN.axaml b/src/Resources/Locales/zh_CN.axaml index 8a31a35a6..88f020e4a 100644 --- a/src/Resources/Locales/zh_CN.axaml +++ b/src/Resources/Locales/zh_CN.axaml @@ -260,6 +260,7 @@ 名称 : 选项列表 : 选项之间请使用英文 '|' 作为分隔符 + 内置变量 ${REPO}, ${REMOTE}, ${BRANCH}, ${BRANCH_FRIENDLY_NAME}, ${SHA} 与 ${TAG} 在这里仍然可用 类型 : 工作区 颜色 diff --git a/src/Resources/Locales/zh_TW.axaml b/src/Resources/Locales/zh_TW.axaml index 9944c41d0..e329f3525 100644 --- a/src/Resources/Locales/zh_TW.axaml +++ b/src/Resources/Locales/zh_TW.axaml @@ -260,6 +260,7 @@ 名稱: 選項列表: 請使用英文「|」符號分隔選項 + 內建變數 ${REPO}、${REMOTE}、${BRANCH}、${BRANCH_FRIENDLY_NAME}、${SHA} 及 ${TAG} 在此處仍可使用 類型: 工作區 顏色 diff --git a/src/Views/ConfigureCustomActionControls.axaml b/src/Views/ConfigureCustomActionControls.axaml index 1e7c9fa83..e24bc0101 100644 --- a/src/Views/ConfigureCustomActionControls.axaml +++ b/src/Views/ConfigureCustomActionControls.axaml @@ -177,7 +177,7 @@ From a70182e7b7d59a0d6e3dbbaefdd50aee1737ca2f Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Tue, 21 Oct 2025 02:50:11 +0000 Subject: [PATCH 0061/1230] doc: Update translation status and sort locale files --- TRANSLATION.md | 33 ++++++++++++++++++++++----------- 1 file changed, 22 insertions(+), 11 deletions(-) diff --git a/TRANSLATION.md b/TRANSLATION.md index b0bd02cf1..c02202014 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-99.22%25-yellow) +### ![de__DE](https://img.shields.io/badge/de__DE-99.11%25-yellow)
Missing keys in de_DE.axaml @@ -16,12 +16,13 @@ This document shows the translation status of each locale file in the repository - Text.BranchCM.CreatePRForUpstream - Text.Configure.CommitMessageTemplate.BuiltinVars - Text.Configure.Git.ConventionalTypesOverride +- Text.ConfigureCustomActionControls.StringValue.Tip - Text.PushToNewBranch - Text.PushToNewBranch.Title
-### ![es__ES](https://img.shields.io/badge/es__ES-99.67%25-yellow) +### ![es__ES](https://img.shields.io/badge/es__ES-99.55%25-yellow)
Missing keys in es_ES.axaml @@ -29,10 +30,11 @@ This document shows the translation status of each locale file in the repository - Text.Blame.BlameOnPreviousRevision - Text.Configure.CommitMessageTemplate.BuiltinVars - Text.Configure.Git.ConventionalTypesOverride +- Text.ConfigureCustomActionControls.StringValue.Tip
-### ![fr__FR](https://img.shields.io/badge/fr__FR-77.23%25-yellow) +### ![fr__FR](https://img.shields.io/badge/fr__FR-77.15%25-yellow)
Missing keys in fr_FR.axaml @@ -115,6 +117,7 @@ This document shows the translation status of each locale file in the repository - Text.ConfigureCustomActionControls.Label - Text.ConfigureCustomActionControls.Options - Text.ConfigureCustomActionControls.Options.Tip +- Text.ConfigureCustomActionControls.StringValue.Tip - Text.ConfigureCustomActionControls.Type - Text.ConfirmEmptyCommit.Continue - Text.ConfirmEmptyCommit.NoLocalChanges @@ -244,7 +247,7 @@ This document shows the translation status of each locale file in the repository
-### ![id__ID](https://img.shields.io/badge/id__ID-98.66%25-yellow) +### ![id__ID](https://img.shields.io/badge/id__ID-98.55%25-yellow)
Missing keys in id_ID.axaml @@ -256,6 +259,7 @@ This document shows the translation status of each locale file in the repository - Text.CommitCM.Drop - Text.Configure.CommitMessageTemplate.BuiltinVars - Text.Configure.Git.ConventionalTypesOverride +- Text.ConfigureCustomActionControls.StringValue.Tip - Text.DropHead - Text.DropHead.Commit - Text.DropHead.NewHead @@ -264,7 +268,7 @@ This document shows the translation status of each locale file in the repository
-### ![it__IT](https://img.shields.io/badge/it__IT-95.87%25-yellow) +### ![it__IT](https://img.shields.io/badge/it__IT-95.76%25-yellow)
Missing keys in it_IT.axaml @@ -286,6 +290,7 @@ This document shows the translation status of each locale file in the repository - Text.CommitMessageTextBox.PasteAndReplaceAll - Text.Configure.CommitMessageTemplate.BuiltinVars - Text.Configure.Git.ConventionalTypesOverride +- Text.ConfigureCustomActionControls.StringValue.Tip - Text.Diff.Image.Difference - Text.DirtyState.HasLocalChanges - Text.DirtyState.HasPendingPullOrPush @@ -309,7 +314,7 @@ This document shows the translation status of each locale file in the repository
-### ![ja__JP](https://img.shields.io/badge/ja__JP-77.23%25-yellow) +### ![ja__JP](https://img.shields.io/badge/ja__JP-77.15%25-yellow)
Missing keys in ja_JP.axaml @@ -393,6 +398,7 @@ This document shows the translation status of each locale file in the repository - Text.ConfigureCustomActionControls.Label - Text.ConfigureCustomActionControls.Options - Text.ConfigureCustomActionControls.Options.Tip +- Text.ConfigureCustomActionControls.StringValue.Tip - Text.ConfigureCustomActionControls.Type - Text.ConfirmEmptyCommit.Continue - Text.ConfirmEmptyCommit.NoLocalChanges @@ -521,7 +527,7 @@ This document shows the translation status of each locale file in the repository
-### ![ko__KR](https://img.shields.io/badge/ko__KR-99.11%25-yellow) +### ![ko__KR](https://img.shields.io/badge/ko__KR-99.00%25-yellow)
Missing keys in ko_KR.axaml @@ -531,13 +537,14 @@ This document shows the translation status of each locale file in the repository - Text.BranchCM.CreatePR - Text.BranchCM.CreatePRForUpstream - Text.Configure.Git.ConventionalTypesOverride +- Text.ConfigureCustomActionControls.StringValue.Tip - Text.PushToNewBranch - Text.PushToNewBranch.Title - Text.Submodule.Status.Unmerged
-### ![pt__BR](https://img.shields.io/badge/pt__BR-70.87%25-red) +### ![pt__BR](https://img.shields.io/badge/pt__BR-70.79%25-red)
Missing keys in pt_BR.axaml @@ -637,6 +644,7 @@ This document shows the translation status of each locale file in the repository - Text.ConfigureCustomActionControls.Label - Text.ConfigureCustomActionControls.Options - Text.ConfigureCustomActionControls.Options.Tip +- Text.ConfigureCustomActionControls.StringValue.Tip - Text.ConfigureCustomActionControls.Type - Text.ConfirmEmptyCommit.Continue - Text.ConfirmEmptyCommit.NoLocalChanges @@ -806,17 +814,18 @@ This document shows the translation status of each locale file in the repository
-### ![ru__RU](https://img.shields.io/badge/ru__RU-99.78%25-yellow) +### ![ru__RU](https://img.shields.io/badge/ru__RU-99.67%25-yellow)
Missing keys in ru_RU.axaml - Text.Blame.BlameOnPreviousRevision - Text.Configure.Git.ConventionalTypesOverride +- Text.ConfigureCustomActionControls.StringValue.Tip
-### ![ta__IN](https://img.shields.io/badge/ta__IN-77.34%25-yellow) +### ![ta__IN](https://img.shields.io/badge/ta__IN-77.26%25-yellow)
Missing keys in ta_IN.axaml @@ -900,6 +909,7 @@ This document shows the translation status of each locale file in the repository - Text.ConfigureCustomActionControls.Label - Text.ConfigureCustomActionControls.Options - Text.ConfigureCustomActionControls.Options.Tip +- Text.ConfigureCustomActionControls.StringValue.Tip - Text.ConfigureCustomActionControls.Type - Text.ConfirmEmptyCommit.Continue - Text.ConfirmEmptyCommit.NoLocalChanges @@ -1027,7 +1037,7 @@ This document shows the translation status of each locale file in the repository
-### ![uk__UA](https://img.shields.io/badge/uk__UA-78.46%25-yellow) +### ![uk__UA](https://img.shields.io/badge/uk__UA-78.37%25-yellow)
Missing keys in uk_UA.axaml @@ -1109,6 +1119,7 @@ This document shows the translation status of each locale file in the repository - Text.ConfigureCustomActionControls.Label - Text.ConfigureCustomActionControls.Options - Text.ConfigureCustomActionControls.Options.Tip +- Text.ConfigureCustomActionControls.StringValue.Tip - Text.ConfigureCustomActionControls.Type - Text.ConfigureWorkspace.Name - Text.ConfirmRestart.Title From fe471ac89d5cb2efea6851e2850a86c96b749731 Mon Sep 17 00:00:00 2001 From: leo Date: Tue, 21 Oct 2025 16:05:11 +0800 Subject: [PATCH 0062/1230] feature: enable multiple-selection for changes collection view in `Changes Detail`/`Stash Changes`/`Branch Compare`/`Revision Compare` (#1826) Signed-off-by: leo --- src/Commands/Checkout.cs | 15 ++ src/ViewModels/BranchCompare.cs | 7 + src/ViewModels/CommitDetail.cs | 37 ++++- src/ViewModels/RevisionCompare.cs | 11 +- src/ViewModels/StashesPage.cs | 38 ++++- src/Views/BranchCompare.axaml.cs | 169 +++++++++++++++------- src/Views/ChangeCollectionView.axaml | 6 +- src/Views/ChangeCollectionView.axaml.cs | 9 -- src/Views/CommitChanges.axaml | 3 +- src/Views/CommitChanges.axaml.cs | 42 +++--- src/Views/CommitDetail.axaml.cs | 93 ++++++++++++ src/Views/RevisionCompare.axaml.cs | 175 ++++++++++++++++------- src/Views/StashesPage.axaml.cs | 179 ++++++++++++++++-------- src/Views/WorkingCopy.axaml | 2 - 14 files changed, 585 insertions(+), 201 deletions(-) diff --git a/src/Commands/Checkout.cs b/src/Commands/Checkout.cs index 23cbd413b..024636bf9 100644 --- a/src/Commands/Checkout.cs +++ b/src/Commands/Checkout.cs @@ -72,5 +72,20 @@ public async Task FileWithRevisionAsync(string file, string revision) Args = $"checkout --no-overlay {revision} -- {file.Quoted()}"; return await ExecAsync().ConfigureAwait(false); } + + public async Task MultipleFilesWithRevisionAsync(List files, string revision) + { + var builder = new StringBuilder(); + builder + .Append("checkout --no-overlay ") + .Append(revision) + .Append(" --"); + + foreach (var f in files) + builder.Append(' ').Append(f.Quoted()); + + Args = builder.ToString(); + return await ExecAsync().ConfigureAwait(false); + } } } diff --git a/src/ViewModels/BranchCompare.cs b/src/ViewModels/BranchCompare.cs index bbe26390e..e7f17e490 100644 --- a/src/ViewModels/BranchCompare.cs +++ b/src/ViewModels/BranchCompare.cs @@ -128,6 +128,13 @@ public string GetAbsPath(string path) return Native.OS.GetAbsPath(_repo, path); } + public async Task SaveChangesAsPatchAsync(List changes, string saveTo) + { + var succ = await Commands.SaveChangesAsPatch.ProcessRevisionCompareChangesAsync(_repo, changes, _based.Head, _to.Head, saveTo); + if (succ) + App.SendNotification(_repo, App.Text("SaveAsPatchSuccess")); + } + private void Refresh() { IsLoading = true; diff --git a/src/ViewModels/CommitDetail.cs b/src/ViewModels/CommitDetail.cs index 1501f6b4d..c272689dc 100644 --- a/src/ViewModels/CommitDetail.cs +++ b/src/ViewModels/CommitDetail.cs @@ -239,7 +239,7 @@ public async Task SaveChangesAsPatchAsync(List changes, string sa public async Task ResetToThisRevisionAsync(string path) { var log = _repo.CreateLog($"Reset File to '{_commit.SHA}'"); - await new Commands.Checkout(_repo.FullPath).Use(log).FileWithRevisionAsync(path, $"{_commit.SHA}"); + await new Commands.Checkout(_repo.FullPath).Use(log).FileWithRevisionAsync(path, _commit.SHA); log.Complete(); } @@ -254,6 +254,41 @@ public async Task ResetToParentRevisionAsync(Models.Change change) log.Complete(); } + public async Task ResetMultipleToThisRevisionAsync(List changes) + { + var files = new List(); + foreach (var c in changes) + files.Add(c.Path); + + var log = _repo.CreateLog($"Reset Files to '{_commit.SHA}'"); + await new Commands.Checkout(_repo.FullPath).Use(log).MultipleFilesWithRevisionAsync(files, _commit.SHA); + log.Complete(); + } + + public async Task ResetMultipleToParentRevisionAsync(List changes) + { + var renamed = new List(); + var modified = new List(); + + foreach (var c in changes) + { + if (c.Index == Models.ChangeState.Renamed) + renamed.Add(c.OriginalPath); + else + modified.Add(c.Path); + } + + var log = _repo.CreateLog($"Reset Files to '{_commit.SHA}~1'"); + + if (modified.Count > 0) + await new Commands.Checkout(_repo.FullPath).Use(log).MultipleFilesWithRevisionAsync(modified, $"{_commit.SHA}~1"); + + if (renamed.Count > 0) + await new Commands.Checkout(_repo.FullPath).Use(log).MultipleFilesWithRevisionAsync(renamed, $"{_commit.SHA}~1"); + + log.Complete(); + } + public async Task> GetRevisionFilesUnderFolderAsync(string parentFolder) { return await new Commands.QueryRevisionObjects(_repo.FullPath, _commit.SHA, parentFolder) diff --git a/src/ViewModels/RevisionCompare.cs b/src/ViewModels/RevisionCompare.cs index ca81a0dc7..4de7b16d6 100644 --- a/src/ViewModels/RevisionCompare.cs +++ b/src/ViewModels/RevisionCompare.cs @@ -127,14 +127,11 @@ public string GetAbsPath(string path) return Native.OS.GetAbsPath(_repo, path); } - public void SaveAsPatch(string saveTo) + public async Task SaveChangesAsPatchAsync(List changes, string saveTo) { - Task.Run(async () => - { - var succ = await Commands.SaveChangesAsPatch.ProcessRevisionCompareChangesAsync(_repo, _changes, GetSHA(_startPoint), GetSHA(_endPoint), saveTo); - if (succ) - App.SendNotification(_repo, App.Text("SaveAsPatchSuccess")); - }); + var succ = await Commands.SaveChangesAsPatch.ProcessRevisionCompareChangesAsync(_repo, changes ?? _changes, GetSHA(_startPoint), GetSHA(_endPoint), saveTo); + if (succ) + App.SendNotification(_repo, App.Text("SaveAsPatchSuccess")); } public void ClearSearchFilter() diff --git a/src/ViewModels/StashesPage.cs b/src/ViewModels/StashesPage.cs index de34ecab0..96f01c7a0 100644 --- a/src/ViewModels/StashesPage.cs +++ b/src/ViewModels/StashesPage.cs @@ -158,7 +158,7 @@ public void Drop(Models.Stash stash) _repo.ShowPopup(new DropStash(_repo, stash)); } - public async Task SaveStashAsPathAsync(Models.Stash stash, string saveTo) + public async Task SaveStashAsPatchAsync(Models.Stash stash, string saveTo) { var opts = new List(); var changes = await new Commands.CompareRevisions(_repo.FullPath, $"{stash.SHA}^", stash.SHA) @@ -211,6 +211,42 @@ public async Task CheckoutSingleFileAsync(Models.Change change) log.Complete(); } + public async Task CheckoutMultipleFileAsync(List changes) + { + var untracked = new List(); + var added = new List(); + var modified = new List(); + + foreach (var c in changes) + { + if (_untracked.Contains(c) && _selectedStash.Parents.Count == 3) + untracked.Add(c.Path); + else if (c.Index == Models.ChangeState.Added && _selectedStash.Parents.Count > 1) + added.Add(c.Path); + else + modified.Add(c.Path); + } + + var log = _repo.CreateLog($"Reset File to '{_selectedStash.Name}'"); + + if (untracked.Count > 0) + await new Commands.Checkout(_repo.FullPath) + .Use(log) + .MultipleFilesWithRevisionAsync(untracked, _selectedStash.Parents[2]); + + if (added.Count > 0) + await new Commands.Checkout(_repo.FullPath) + .Use(log) + .MultipleFilesWithRevisionAsync(added, _selectedStash.Parents[1]); + + if (modified.Count > 0) + await new Commands.Checkout(_repo.FullPath) + .Use(log) + .MultipleFilesWithRevisionAsync(modified, _selectedStash.SHA); + + log.Complete(); + } + private void RefreshVisible() { if (string.IsNullOrEmpty(_searchFilter)) diff --git a/src/Views/BranchCompare.axaml.cs b/src/Views/BranchCompare.axaml.cs index 886825b14..08083ccca 100644 --- a/src/Views/BranchCompare.axaml.cs +++ b/src/Views/BranchCompare.axaml.cs @@ -1,8 +1,10 @@ using System; using System.IO; +using System.Text; using Avalonia.Controls; using Avalonia.Input; +using Avalonia.Platform.Storage; namespace SourceGit.Views { @@ -15,61 +17,127 @@ public BranchCompare() private void OnChangeContextRequested(object sender, ContextRequestedEventArgs e) { - if (DataContext is ViewModels.BranchCompare { SelectedChanges: { Count: 1 } selected } vm && + if (DataContext is ViewModels.BranchCompare { SelectedChanges: { Count: > 0 } selected } vm && sender is ChangeCollectionView view) { - var repo = vm.RepositoryPath; - var change = selected[0]; var menu = new ContextMenu(); + var repo = vm.RepositoryPath; - var openWithMerger = new MenuItem(); - openWithMerger.Header = App.Text("OpenInExternalMergeTool"); - openWithMerger.Icon = App.CreateMenuIcon("Icons.OpenWith"); - openWithMerger.Tag = OperatingSystem.IsMacOS() ? "⌘+⇧+D" : "Ctrl+Shift+D"; - openWithMerger.Click += (_, ev) => + var patch = new MenuItem(); + patch.Header = App.Text("FileCM.SaveAsPatch"); + patch.Icon = App.CreateMenuIcon("Icons.Diff"); + patch.Click += async (_, e) => { - new Commands.DiffTool(repo, new Models.DiffOption(vm.Base.Head, vm.To.Head, change)).Open(); - ev.Handled = true; + var storageProvider = this.StorageProvider; + if (storageProvider == null) + return; + + var options = new FilePickerSaveOptions(); + options.Title = App.Text("FileCM.SaveAsPatch"); + options.DefaultExtension = ".patch"; + options.FileTypeChoices = [new FilePickerFileType("Patch File") { Patterns = ["*.patch"] }]; + + var storageFile = await storageProvider.SaveFilePickerAsync(options); + if (storageFile != null) + { + var saveTo = storageFile.Path.LocalPath; + await vm.SaveChangesAsPatchAsync(selected, saveTo); + } + + e.Handled = true; }; - menu.Items.Add(openWithMerger); - if (change.Index != Models.ChangeState.Deleted) + if (selected.Count == 1) { - var full = Path.GetFullPath(Path.Combine(repo, change.Path)); - var explore = new MenuItem(); - explore.Header = App.Text("RevealFile"); - explore.Icon = App.CreateMenuIcon("Icons.Explore"); - explore.IsEnabled = File.Exists(full); - explore.Click += (_, ev) => + var change = selected[0]; + var openWithMerger = new MenuItem(); + openWithMerger.Header = App.Text("OpenInExternalMergeTool"); + openWithMerger.Icon = App.CreateMenuIcon("Icons.OpenWith"); + openWithMerger.Tag = OperatingSystem.IsMacOS() ? "⌘+⇧+D" : "Ctrl+Shift+D"; + openWithMerger.Click += (_, ev) => { - Native.OS.OpenInFileManager(full, true); + new Commands.DiffTool(repo, new Models.DiffOption(vm.Base.Head, vm.To.Head, change)).Open(); ev.Handled = true; }; - menu.Items.Add(explore); - } + menu.Items.Add(openWithMerger); - var copyPath = new MenuItem(); - copyPath.Header = App.Text("CopyPath"); - copyPath.Icon = App.CreateMenuIcon("Icons.Copy"); - copyPath.Tag = OperatingSystem.IsMacOS() ? "⌘+C" : "Ctrl+C"; - copyPath.Click += async (_, ev) => - { - await App.CopyTextAsync(change.Path); - ev.Handled = true; - }; - menu.Items.Add(new MenuItem() { Header = "-" }); - menu.Items.Add(copyPath); - - var copyFullPath = new MenuItem(); - copyFullPath.Header = App.Text("CopyFullPath"); - copyFullPath.Icon = App.CreateMenuIcon("Icons.Copy"); - copyFullPath.Tag = OperatingSystem.IsMacOS() ? "⌘+⇧+C" : "Ctrl+Shift+C"; - copyFullPath.Click += async (_, ev) => + if (change.Index != Models.ChangeState.Deleted) + { + var full = Path.GetFullPath(Path.Combine(repo, change.Path)); + var explore = new MenuItem(); + explore.Header = App.Text("RevealFile"); + explore.Icon = App.CreateMenuIcon("Icons.Explore"); + explore.IsEnabled = File.Exists(full); + explore.Click += (_, ev) => + { + Native.OS.OpenInFileManager(full, true); + ev.Handled = true; + }; + menu.Items.Add(explore); + } + + var copyPath = new MenuItem(); + copyPath.Header = App.Text("CopyPath"); + copyPath.Icon = App.CreateMenuIcon("Icons.Copy"); + copyPath.Tag = OperatingSystem.IsMacOS() ? "⌘+C" : "Ctrl+C"; + copyPath.Click += async (_, ev) => + { + await App.CopyTextAsync(change.Path); + ev.Handled = true; + }; + + var copyFullPath = new MenuItem(); + copyFullPath.Header = App.Text("CopyFullPath"); + copyFullPath.Icon = App.CreateMenuIcon("Icons.Copy"); + copyFullPath.Tag = OperatingSystem.IsMacOS() ? "⌘+⇧+C" : "Ctrl+Shift+C"; + copyFullPath.Click += async (_, ev) => + { + await App.CopyTextAsync(Native.OS.GetAbsPath(repo, change.Path)); + ev.Handled = true; + }; + + menu.Items.Add(new MenuItem() { Header = "-" }); + menu.Items.Add(patch); + menu.Items.Add(new MenuItem() { Header = "-" }); + menu.Items.Add(copyPath); + menu.Items.Add(copyFullPath); + } + else { - await App.CopyTextAsync(Native.OS.GetAbsPath(repo, change.Path)); - ev.Handled = true; - }; - menu.Items.Add(copyFullPath); + var copyPath = new MenuItem(); + copyPath.Header = App.Text("CopyPath"); + copyPath.Icon = App.CreateMenuIcon("Icons.Copy"); + copyPath.Tag = OperatingSystem.IsMacOS() ? "⌘+C" : "Ctrl+C"; + copyPath.Click += async (_, ev) => + { + var builder = new StringBuilder(); + foreach (var c in selected) + builder.AppendLine(c.Path); + + await App.CopyTextAsync(builder.ToString()); + ev.Handled = true; + }; + + var copyFullPath = new MenuItem(); + copyFullPath.Header = App.Text("CopyFullPath"); + copyFullPath.Icon = App.CreateMenuIcon("Icons.Copy"); + copyFullPath.Tag = OperatingSystem.IsMacOS() ? "⌘+⇧+C" : "Ctrl+Shift+C"; + copyFullPath.Click += async (_, ev) => + { + var builder = new StringBuilder(); + foreach (var c in selected) + builder.AppendLine(Native.OS.GetAbsPath(repo, c.Path)); + + await App.CopyTextAsync(builder.ToString()); + ev.Handled = true; + }; + + menu.Items.Add(patch); + menu.Items.Add(new MenuItem() { Header = "-" }); + menu.Items.Add(copyPath); + menu.Items.Add(copyFullPath); + } + menu.Open(view); } @@ -89,17 +157,24 @@ private async void OnChangeCollectionViewKeyDown(object sender, KeyEventArgs e) if (DataContext is not ViewModels.BranchCompare vm) return; - if (sender is not ChangeCollectionView { SelectedChanges: { Count: 1 } selectedChanges }) + if (sender is not ChangeCollectionView { SelectedChanges: { Count: > 0 } selectedChanges }) return; - var change = selectedChanges[0]; if (e.KeyModifiers.HasFlag(OperatingSystem.IsMacOS() ? KeyModifiers.Meta : KeyModifiers.Control) && e.Key == Key.C) { - if (e.KeyModifiers.HasFlag(KeyModifiers.Shift)) - await App.CopyTextAsync(vm.GetAbsPath(change.Path)); + var builder = new StringBuilder(); + var copyAbsPath = e.KeyModifiers.HasFlag(KeyModifiers.Shift); + if (selectedChanges.Count == 1) + { + builder.Append(copyAbsPath ? vm.GetAbsPath(selectedChanges[0].Path) : selectedChanges[0].Path); + } else - await App.CopyTextAsync(change.Path); + { + foreach (var c in selectedChanges) + builder.AppendLine(copyAbsPath ? vm.GetAbsPath(c.Path) : c.Path); + } + await App.CopyTextAsync(builder.ToString()); e.Handled = true; } } diff --git a/src/Views/ChangeCollectionView.axaml b/src/Views/ChangeCollectionView.axaml index e7cf63882..a00570f50 100644 --- a/src/Views/ChangeCollectionView.axaml +++ b/src/Views/ChangeCollectionView.axaml @@ -33,7 +33,7 @@ @@ -78,7 +78,7 @@ @@ -110,7 +110,7 @@ diff --git a/src/Views/ChangeCollectionView.axaml.cs b/src/Views/ChangeCollectionView.axaml.cs index 4e3092fa8..237a69da4 100644 --- a/src/Views/ChangeCollectionView.axaml.cs +++ b/src/Views/ChangeCollectionView.axaml.cs @@ -61,15 +61,6 @@ public bool IsUnstagedChange set => SetValue(IsUnstagedChangeProperty, value); } - public static readonly StyledProperty SelectionModeProperty = - AvaloniaProperty.Register(nameof(SelectionMode)); - - public SelectionMode SelectionMode - { - get => GetValue(SelectionModeProperty); - set => SetValue(SelectionModeProperty, value); - } - public static readonly StyledProperty ViewModeProperty = AvaloniaProperty.Register(nameof(ViewMode), Models.ChangeViewMode.Tree); diff --git a/src/Views/CommitChanges.axaml b/src/Views/CommitChanges.axaml index 4656b6b48..e03a50ad2 100644 --- a/src/Views/CommitChanges.axaml +++ b/src/Views/CommitChanges.axaml @@ -45,8 +45,7 @@ - 0 } changes } view) return; var detailView = this.FindAncestorOfType(); if (detailView == null) return; - var changes = view.SelectedChanges ?? []; var container = view.FindDescendantOfType(); if (container is { SelectedItems.Count: 1, SelectedItem: ViewModels.ChangeTreeNode { IsFolder: true } node }) - { - var menu = detailView.CreateChangeContextMenuByFolder(node, changes); - menu.Open(view); - return; - } - - if (changes.Count == 1) - { - var menu = detailView.CreateChangeContextMenu(changes[0]); - menu.Open(view); - } + detailView.CreateChangeContextMenuByFolder(node, changes)?.Open(view); + else if (changes.Count > 1) + detailView.CreateMultipleChangesContextMenu(changes)?.Open(view); + else + detailView.CreateChangeContextMenu(changes[0])?.Open(view); } private async void OnChangeCollectionViewKeyDown(object sender, KeyEventArgs e) @@ -45,18 +39,30 @@ private async void OnChangeCollectionViewKeyDown(object sender, KeyEventArgs e) if (DataContext is not ViewModels.CommitDetail vm) return; - if (sender is not ChangeCollectionView { SelectedChanges: { Count: 1 } selectedChanges }) + if (sender is not ChangeCollectionView { SelectedChanges: { Count: > 0 } selectedChanges } view) return; - var change = selectedChanges[0]; if (e.Key == Key.C && e.KeyModifiers.HasFlag(OperatingSystem.IsMacOS() ? KeyModifiers.Meta : KeyModifiers.Control)) { - if (e.KeyModifiers.HasFlag(KeyModifiers.Shift)) - await App.CopyTextAsync(vm.GetAbsPath(change.Path)); + var builder = new StringBuilder(); + var copyAbsPath = e.KeyModifiers.HasFlag(KeyModifiers.Shift); + var container = view.FindDescendantOfType(); + if (container is { SelectedItems.Count: 1, SelectedItem: ViewModels.ChangeTreeNode { IsFolder: true } node }) + { + builder.Append(copyAbsPath ? vm.GetAbsPath(node.FullPath) : node.FullPath); + } + else if (selectedChanges.Count == 1) + { + builder.Append(copyAbsPath ? vm.GetAbsPath(selectedChanges[0].Path) : selectedChanges[0].Path); + } else - await App.CopyTextAsync(change.Path); + { + foreach (var c in selectedChanges) + builder.AppendLine(copyAbsPath ? vm.GetAbsPath(c.Path) : c.Path); + } + await App.CopyTextAsync(builder.ToString()); e.Handled = true; } } diff --git a/src/Views/CommitDetail.axaml.cs b/src/Views/CommitDetail.axaml.cs index 1d028ca8b..ee570e64f 100644 --- a/src/Views/CommitDetail.axaml.cs +++ b/src/Views/CommitDetail.axaml.cs @@ -1,6 +1,7 @@ using System; using System.Collections.Generic; using System.IO; +using System.Text; using Avalonia.Controls; using Avalonia.Input; @@ -97,6 +98,98 @@ public ContextMenu CreateChangeContextMenuByFolder(ViewModels.ChangeTreeNode nod return menu; } + public ContextMenu CreateMultipleChangesContextMenu(List changes) + { + if (DataContext is not ViewModels.CommitDetail { Repository: { } repo, Commit: { } commit } vm) + return null; + + var patch = new MenuItem(); + patch.Header = App.Text("FileCM.SaveAsPatch"); + patch.Icon = App.CreateMenuIcon("Icons.Diff"); + patch.Click += async (_, e) => + { + var storageProvider = TopLevel.GetTopLevel(this)?.StorageProvider; + if (storageProvider == null) + return; + + var options = new FilePickerSaveOptions(); + options.Title = App.Text("FileCM.SaveAsPatch"); + options.DefaultExtension = ".patch"; + options.FileTypeChoices = [new FilePickerFileType("Patch File") { Patterns = ["*.patch"] }]; + + var storageFile = await storageProvider.SaveFilePickerAsync(options); + if (storageFile != null) + { + var saveTo = storageFile.Path.LocalPath; + await vm.SaveChangesAsPatchAsync(changes, saveTo); + } + + e.Handled = true; + }; + + var menu = new ContextMenu(); + menu.Items.Add(patch); + menu.Items.Add(new MenuItem() { Header = "-" }); + + if (!repo.IsBare) + { + var resetToThisRevision = new MenuItem(); + resetToThisRevision.Header = App.Text("ChangeCM.CheckoutThisRevision"); + resetToThisRevision.Icon = App.CreateMenuIcon("Icons.File.Checkout"); + resetToThisRevision.Click += async (_, ev) => + { + await vm.ResetMultipleToThisRevisionAsync(changes); + ev.Handled = true; + }; + + var resetToFirstParent = new MenuItem(); + resetToFirstParent.Header = App.Text("ChangeCM.CheckoutFirstParentRevision"); + resetToFirstParent.Icon = App.CreateMenuIcon("Icons.File.Checkout"); + resetToFirstParent.IsEnabled = commit.Parents.Count > 0; + resetToFirstParent.Click += async (_, ev) => + { + await vm.ResetMultipleToParentRevisionAsync(changes); + ev.Handled = true; + }; + + menu.Items.Add(resetToThisRevision); + menu.Items.Add(resetToFirstParent); + menu.Items.Add(new MenuItem { Header = "-" }); + } + + var copyPath = new MenuItem(); + copyPath.Header = App.Text("CopyPath"); + copyPath.Icon = App.CreateMenuIcon("Icons.Copy"); + copyPath.Tag = OperatingSystem.IsMacOS() ? "⌘+C" : "Ctrl+C"; + copyPath.Click += async (_, ev) => + { + var builder = new StringBuilder(); + foreach (var c in changes) + builder.AppendLine(c.Path); + + await App.CopyTextAsync(builder.ToString()); + ev.Handled = true; + }; + + var copyFullPath = new MenuItem(); + copyFullPath.Header = App.Text("CopyFullPath"); + copyFullPath.Icon = App.CreateMenuIcon("Icons.Copy"); + copyFullPath.Tag = OperatingSystem.IsMacOS() ? "⌘+⇧+C" : "Ctrl+Shift+C"; + copyFullPath.Click += async (_, e) => + { + var builder = new StringBuilder(); + foreach (var c in changes) + builder.AppendLine(Native.OS.GetAbsPath(repo.FullPath, c.Path)); + + await App.CopyTextAsync(builder.ToString()); + e.Handled = true; + }; + + menu.Items.Add(copyPath); + menu.Items.Add(copyFullPath); + return menu; + } + public ContextMenu CreateChangeContextMenu(Models.Change change) { if (DataContext is not ViewModels.CommitDetail { Repository: { } repo, Commit: { } commit } vm) diff --git a/src/Views/RevisionCompare.axaml.cs b/src/Views/RevisionCompare.axaml.cs index bb27e7095..3a84baca1 100644 --- a/src/Views/RevisionCompare.axaml.cs +++ b/src/Views/RevisionCompare.axaml.cs @@ -1,5 +1,6 @@ using System; using System.IO; +using System.Text; using Avalonia.Controls; using Avalonia.Input; @@ -17,59 +18,126 @@ public RevisionCompare() private void OnChangeContextRequested(object sender, ContextRequestedEventArgs e) { - if (DataContext is ViewModels.RevisionCompare { SelectedChanges: { Count: 1 } selected } vm && + if (DataContext is ViewModels.RevisionCompare { SelectedChanges: { Count: > 0 } selected } vm && sender is ChangeCollectionView view) { - var change = selected[0]; - var changeFullPath = vm.GetAbsPath(change.Path); - var menu = new ContextMenu(); - - var openWithMerger = new MenuItem(); - openWithMerger.Header = App.Text("OpenInExternalMergeTool"); - openWithMerger.Icon = App.CreateMenuIcon("Icons.OpenWith"); - openWithMerger.Tag = OperatingSystem.IsMacOS() ? "⌘+⇧+D" : "Ctrl+Shift+D"; - openWithMerger.Click += (_, ev) => + var patch = new MenuItem(); + patch.Header = App.Text("FileCM.SaveAsPatch"); + patch.Icon = App.CreateMenuIcon("Icons.Diff"); + patch.Click += async (_, e) => { - vm.OpenChangeWithExternalDiffTool(change); - ev.Handled = true; + var storageProvider = TopLevel.GetTopLevel(this)?.StorageProvider; + if (storageProvider == null) + return; + + var options = new FilePickerSaveOptions(); + options.Title = App.Text("FileCM.SaveAsPatch"); + options.DefaultExtension = ".patch"; + options.FileTypeChoices = [new FilePickerFileType("Patch File") { Patterns = ["*.patch"] }]; + + var storageFile = await storageProvider.SaveFilePickerAsync(options); + if (storageFile != null) + { + var saveTo = storageFile.Path.LocalPath; + await vm.SaveChangesAsPatchAsync(selected, saveTo); + } + + e.Handled = true; }; - menu.Items.Add(openWithMerger); - if (change.Index != Models.ChangeState.Deleted) + var menu = new ContextMenu(); + if (selected.Count == 1) { - var explore = new MenuItem(); - explore.Header = App.Text("RevealFile"); - explore.Icon = App.CreateMenuIcon("Icons.Explore"); - explore.IsEnabled = File.Exists(changeFullPath); - explore.Click += (_, ev) => + var change = selected[0]; + var changeFullPath = vm.GetAbsPath(change.Path); + + var openWithMerger = new MenuItem(); + openWithMerger.Header = App.Text("OpenInExternalMergeTool"); + openWithMerger.Icon = App.CreateMenuIcon("Icons.OpenWith"); + openWithMerger.Tag = OperatingSystem.IsMacOS() ? "⌘+⇧+D" : "Ctrl+Shift+D"; + openWithMerger.Click += (_, ev) => { - Native.OS.OpenInFileManager(changeFullPath, true); + vm.OpenChangeWithExternalDiffTool(change); ev.Handled = true; }; - menu.Items.Add(explore); - } + menu.Items.Add(openWithMerger); - var copyPath = new MenuItem(); - copyPath.Header = App.Text("CopyPath"); - copyPath.Icon = App.CreateMenuIcon("Icons.Copy"); - copyPath.Tag = OperatingSystem.IsMacOS() ? "⌘+C" : "Ctrl+C"; - copyPath.Click += async (_, ev) => - { - await App.CopyTextAsync(change.Path); - ev.Handled = true; - }; - menu.Items.Add(copyPath); + if (change.Index != Models.ChangeState.Deleted) + { + var explore = new MenuItem(); + explore.Header = App.Text("RevealFile"); + explore.Icon = App.CreateMenuIcon("Icons.Explore"); + explore.IsEnabled = File.Exists(changeFullPath); + explore.Click += (_, ev) => + { + Native.OS.OpenInFileManager(changeFullPath, true); + ev.Handled = true; + }; + menu.Items.Add(explore); + } + + var copyPath = new MenuItem(); + copyPath.Header = App.Text("CopyPath"); + copyPath.Icon = App.CreateMenuIcon("Icons.Copy"); + copyPath.Tag = OperatingSystem.IsMacOS() ? "⌘+C" : "Ctrl+C"; + copyPath.Click += async (_, ev) => + { + await App.CopyTextAsync(change.Path); + ev.Handled = true; + }; - var copyFullPath = new MenuItem(); - copyFullPath.Header = App.Text("CopyFullPath"); - copyFullPath.Icon = App.CreateMenuIcon("Icons.Copy"); - copyFullPath.Tag = OperatingSystem.IsMacOS() ? "⌘+⇧+C" : "Ctrl+Shift+C"; - copyFullPath.Click += async (_, ev) => + var copyFullPath = new MenuItem(); + copyFullPath.Header = App.Text("CopyFullPath"); + copyFullPath.Icon = App.CreateMenuIcon("Icons.Copy"); + copyFullPath.Tag = OperatingSystem.IsMacOS() ? "⌘+⇧+C" : "Ctrl+Shift+C"; + copyFullPath.Click += async (_, ev) => + { + await App.CopyTextAsync(changeFullPath); + ev.Handled = true; + }; + + menu.Items.Add(new MenuItem() { Header = "-" }); + menu.Items.Add(patch); + menu.Items.Add(new MenuItem() { Header = "-" }); + menu.Items.Add(copyPath); + menu.Items.Add(copyFullPath); + } + else { - await App.CopyTextAsync(changeFullPath); - ev.Handled = true; - }; - menu.Items.Add(copyFullPath); + var copyPath = new MenuItem(); + copyPath.Header = App.Text("CopyPath"); + copyPath.Icon = App.CreateMenuIcon("Icons.Copy"); + copyPath.Tag = OperatingSystem.IsMacOS() ? "⌘+C" : "Ctrl+C"; + copyPath.Click += async (_, ev) => + { + var builder = new StringBuilder(); + foreach (var c in selected) + builder.AppendLine(c.Path); + + await App.CopyTextAsync(builder.ToString()); + ev.Handled = true; + }; + + var copyFullPath = new MenuItem(); + copyFullPath.Header = App.Text("CopyFullPath"); + copyFullPath.Icon = App.CreateMenuIcon("Icons.Copy"); + copyFullPath.Tag = OperatingSystem.IsMacOS() ? "⌘+⇧+C" : "Ctrl+Shift+C"; + copyFullPath.Click += async (_, ev) => + { + var builder = new StringBuilder(); + foreach (var c in selected) + builder.AppendLine(vm.GetAbsPath(c.Path)); + + await App.CopyTextAsync(builder.ToString()); + ev.Handled = true; + }; + + menu.Items.Add(patch); + menu.Items.Add(new MenuItem() { Header = "-" }); + menu.Items.Add(copyPath); + menu.Items.Add(copyFullPath); + } + menu.Open(view); } @@ -86,8 +154,8 @@ private void OnPressedSHA(object sender, PointerPressedEventArgs e) private async void OnSaveAsPatch(object sender, RoutedEventArgs e) { - var topLevel = TopLevel.GetTopLevel(this); - if (topLevel == null) + var storage = TopLevel.GetTopLevel(this)?.StorageProvider; + if (storage == null) return; if (DataContext is not ViewModels.RevisionCompare vm) @@ -98,9 +166,9 @@ private async void OnSaveAsPatch(object sender, RoutedEventArgs e) options.DefaultExtension = ".patch"; options.FileTypeChoices = [new FilePickerFileType("Patch File") { Patterns = ["*.patch"] }]; - var storageFile = await topLevel.StorageProvider.SaveFilePickerAsync(options); + var storageFile = await storage.SaveFilePickerAsync(options); if (storageFile != null) - vm.SaveAsPatch(storageFile.Path.LocalPath); + await vm.SaveChangesAsPatchAsync(null, storageFile.Path.LocalPath); e.Handled = true; } @@ -110,17 +178,24 @@ private async void OnChangeCollectionViewKeyDown(object sender, KeyEventArgs e) if (DataContext is not ViewModels.RevisionCompare vm) return; - if (sender is not ChangeCollectionView { SelectedChanges: { Count: 1 } selectedChanges }) + if (sender is not ChangeCollectionView { SelectedChanges: { Count: > 0 } selectedChanges }) return; - var change = selectedChanges[0]; if (e.KeyModifiers.HasFlag(OperatingSystem.IsMacOS() ? KeyModifiers.Meta : KeyModifiers.Control) && e.Key == Key.C) { - if (e.KeyModifiers.HasFlag(KeyModifiers.Shift)) - await App.CopyTextAsync(vm.GetAbsPath(change.Path)); + var builder = new StringBuilder(); + var copyAbsPath = e.KeyModifiers.HasFlag(KeyModifiers.Shift); + if (selectedChanges.Count == 1) + { + builder.Append(copyAbsPath ? vm.GetAbsPath(selectedChanges[0].Path) : selectedChanges[0].Path); + } else - await App.CopyTextAsync(change.Path); + { + foreach (var c in selectedChanges) + builder.AppendLine(copyAbsPath ? vm.GetAbsPath(c.Path) : c.Path); + } + await App.CopyTextAsync(builder.ToString()); e.Handled = true; } } diff --git a/src/Views/StashesPage.axaml.cs b/src/Views/StashesPage.axaml.cs index 6dc74f174..ec19e27ae 100644 --- a/src/Views/StashesPage.axaml.cs +++ b/src/Views/StashesPage.axaml.cs @@ -1,5 +1,6 @@ using System; using System.IO; +using System.Text; using Avalonia.Controls; using Avalonia.Input; @@ -84,7 +85,7 @@ private void OnStashContextRequested(object sender, ContextRequestedEventArgs e) var storageFile = await storageProvider.SaveFilePickerAsync(options); if (storageFile != null) - await vm.SaveStashAsPathAsync(stash, storageFile.Path.LocalPath); + await vm.SaveStashAsPatchAsync(stash, storageFile.Path.LocalPath); ev.Handled = true; }; @@ -123,70 +124,119 @@ private void OnStashDoubleTapped(object sender, TappedEventArgs e) private void OnChangeContextRequested(object sender, ContextRequestedEventArgs e) { - if (DataContext is ViewModels.StashesPage { SelectedChanges: { Count: 1 } selected } vm && + if (DataContext is ViewModels.StashesPage { SelectedChanges: { Count: > 0 } selected } vm && sender is ChangeCollectionView view) { - var change = selected[0]; - var fullPath = vm.GetAbsPath(change.Path); - - var openWithMerger = new MenuItem(); - openWithMerger.Header = App.Text("OpenInExternalMergeTool"); - openWithMerger.Icon = App.CreateMenuIcon("Icons.OpenWith"); - openWithMerger.Tag = OperatingSystem.IsMacOS() ? "⌘+⇧+D" : "Ctrl+Shift+D"; - openWithMerger.Click += (_, ev) => + if (selected.Count == 1) { - vm.OpenChangeWithExternalDiffTool(change); - ev.Handled = true; - }; + var change = selected[0]; + var fullPath = vm.GetAbsPath(change.Path); - var explore = new MenuItem(); - explore.Header = App.Text("RevealFile"); - explore.Icon = App.CreateMenuIcon("Icons.Explore"); - explore.IsEnabled = File.Exists(fullPath); - explore.Click += (_, ev) => - { - Native.OS.OpenInFileManager(fullPath, true); - ev.Handled = true; - }; + var openWithMerger = new MenuItem(); + openWithMerger.Header = App.Text("OpenInExternalMergeTool"); + openWithMerger.Icon = App.CreateMenuIcon("Icons.OpenWith"); + openWithMerger.Tag = OperatingSystem.IsMacOS() ? "⌘+⇧+D" : "Ctrl+Shift+D"; + openWithMerger.Click += (_, ev) => + { + vm.OpenChangeWithExternalDiffTool(change); + ev.Handled = true; + }; - var resetToThisRevision = new MenuItem(); - resetToThisRevision.Header = App.Text("ChangeCM.CheckoutThisRevision"); - resetToThisRevision.Icon = App.CreateMenuIcon("Icons.File.Checkout"); - resetToThisRevision.Click += async (_, ev) => - { - await vm.CheckoutSingleFileAsync(change); - ev.Handled = true; - }; + var explore = new MenuItem(); + explore.Header = App.Text("RevealFile"); + explore.Icon = App.CreateMenuIcon("Icons.Explore"); + explore.IsEnabled = File.Exists(fullPath); + explore.Click += (_, ev) => + { + Native.OS.OpenInFileManager(fullPath, true); + ev.Handled = true; + }; - var copyPath = new MenuItem(); - copyPath.Header = App.Text("CopyPath"); - copyPath.Icon = App.CreateMenuIcon("Icons.Copy"); - copyPath.Tag = OperatingSystem.IsMacOS() ? "⌘+C" : "Ctrl+C"; - copyPath.Click += async (_, ev) => - { - await App.CopyTextAsync(change.Path); - ev.Handled = true; - }; + var resetToThisRevision = new MenuItem(); + resetToThisRevision.Header = App.Text("ChangeCM.CheckoutThisRevision"); + resetToThisRevision.Icon = App.CreateMenuIcon("Icons.File.Checkout"); + resetToThisRevision.Click += async (_, ev) => + { + await vm.CheckoutSingleFileAsync(change); + ev.Handled = true; + }; + + var copyPath = new MenuItem(); + copyPath.Header = App.Text("CopyPath"); + copyPath.Icon = App.CreateMenuIcon("Icons.Copy"); + copyPath.Tag = OperatingSystem.IsMacOS() ? "⌘+C" : "Ctrl+C"; + copyPath.Click += async (_, ev) => + { + await App.CopyTextAsync(change.Path); + ev.Handled = true; + }; - var copyFullPath = new MenuItem(); - copyFullPath.Header = App.Text("CopyFullPath"); - copyFullPath.Icon = App.CreateMenuIcon("Icons.Copy"); - copyFullPath.Tag = OperatingSystem.IsMacOS() ? "⌘+⇧+C" : "Ctrl+Shift+C"; - copyFullPath.Click += async (_, ev) => + var copyFullPath = new MenuItem(); + copyFullPath.Header = App.Text("CopyFullPath"); + copyFullPath.Icon = App.CreateMenuIcon("Icons.Copy"); + copyFullPath.Tag = OperatingSystem.IsMacOS() ? "⌘+⇧+C" : "Ctrl+Shift+C"; + copyFullPath.Click += async (_, ev) => + { + await App.CopyTextAsync(fullPath); + ev.Handled = true; + }; + + var menu = new ContextMenu(); + menu.Items.Add(openWithMerger); + menu.Items.Add(explore); + menu.Items.Add(new MenuItem { Header = "-" }); + menu.Items.Add(resetToThisRevision); + menu.Items.Add(new MenuItem { Header = "-" }); + menu.Items.Add(copyPath); + menu.Items.Add(copyFullPath); + menu.Open(view); + } + else { - await App.CopyTextAsync(fullPath); - ev.Handled = true; - }; + var resetToThisRevision = new MenuItem(); + resetToThisRevision.Header = App.Text("ChangeCM.CheckoutThisRevision"); + resetToThisRevision.Icon = App.CreateMenuIcon("Icons.File.Checkout"); + resetToThisRevision.Click += async (_, ev) => + { + await vm.CheckoutMultipleFileAsync(selected); + ev.Handled = true; + }; - var menu = new ContextMenu(); - menu.Items.Add(openWithMerger); - menu.Items.Add(explore); - menu.Items.Add(new MenuItem { Header = "-" }); - menu.Items.Add(resetToThisRevision); - menu.Items.Add(new MenuItem { Header = "-" }); - menu.Items.Add(copyPath); - menu.Items.Add(copyFullPath); - menu.Open(view); + var copyPath = new MenuItem(); + copyPath.Header = App.Text("CopyPath"); + copyPath.Icon = App.CreateMenuIcon("Icons.Copy"); + copyPath.Tag = OperatingSystem.IsMacOS() ? "⌘+C" : "Ctrl+C"; + copyPath.Click += async (_, ev) => + { + var builder = new StringBuilder(); + foreach (var c in selected) + builder.AppendLine(c.Path); + + await App.CopyTextAsync(builder.ToString()); + ev.Handled = true; + }; + + var copyFullPath = new MenuItem(); + copyFullPath.Header = App.Text("CopyFullPath"); + copyFullPath.Icon = App.CreateMenuIcon("Icons.Copy"); + copyFullPath.Tag = OperatingSystem.IsMacOS() ? "⌘+⇧+C" : "Ctrl+Shift+C"; + copyFullPath.Click += async (_, ev) => + { + var builder = new StringBuilder(); + foreach (var c in selected) + builder.AppendLine(vm.GetAbsPath(c.Path)); + + await App.CopyTextAsync(builder.ToString()); + ev.Handled = true; + }; + + var menu = new ContextMenu(); + menu.Items.Add(resetToThisRevision); + menu.Items.Add(new MenuItem { Header = "-" }); + menu.Items.Add(copyPath); + menu.Items.Add(copyFullPath); + menu.Open(view); + } } e.Handled = true; @@ -197,17 +247,24 @@ private async void OnChangeCollectionViewKeyDown(object sender, KeyEventArgs e) if (DataContext is not ViewModels.StashesPage vm) return; - if (sender is not ChangeCollectionView { SelectedChanges: { Count: 1 } selectedChanges }) + if (sender is not ChangeCollectionView { SelectedChanges: { Count: > 0 } selectedChanges }) return; - var change = selectedChanges[0]; if (e.KeyModifiers.HasFlag(OperatingSystem.IsMacOS() ? KeyModifiers.Meta : KeyModifiers.Control) && e.Key == Key.C) { - if (e.KeyModifiers.HasFlag(KeyModifiers.Shift)) - await App.CopyTextAsync(vm.GetAbsPath(change.Path)); + var builder = new StringBuilder(); + var copyAbsPath = e.KeyModifiers.HasFlag(KeyModifiers.Shift); + if (selectedChanges.Count == 1) + { + builder.Append(copyAbsPath ? vm.GetAbsPath(selectedChanges[0].Path) : selectedChanges[0].Path); + } else - await App.CopyTextAsync(change.Path); + { + foreach (var c in selectedChanges) + builder.AppendLine(copyAbsPath ? vm.GetAbsPath(c.Path) : c.Path); + } + await App.CopyTextAsync(builder.ToString()); e.Handled = true; } } diff --git a/src/Views/WorkingCopy.axaml b/src/Views/WorkingCopy.axaml index e5eed5e71..ce940299f 100644 --- a/src/Views/WorkingCopy.axaml +++ b/src/Views/WorkingCopy.axaml @@ -126,7 +126,6 @@ Date: Wed, 22 Oct 2025 15:45:42 +0800 Subject: [PATCH 0063/1230] feature: show repo status in `Welcome` page (#1867) Signed-off-by: leo --- ...thoutUntracked.cs => CountLocalChanges.cs} | 7 ++- src/ViewModels/Checkout.cs | 2 +- src/ViewModels/CheckoutAndFastForward.cs | 2 +- src/ViewModels/CheckoutCommit.cs | 2 +- src/ViewModels/CreateBranch.cs | 2 +- src/ViewModels/Pull.cs | 2 +- src/ViewModels/RepositoryNode.cs | 49 ++++++++++++++- src/ViewModels/Welcome.cs | 6 ++ src/Views/Welcome.axaml | 59 ++++++++++++++++--- src/Views/Welcome.axaml.cs | 6 ++ 10 files changed, 121 insertions(+), 16 deletions(-) rename src/Commands/{CountLocalChangesWithoutUntracked.cs => CountLocalChanges.cs} (66%) diff --git a/src/Commands/CountLocalChangesWithoutUntracked.cs b/src/Commands/CountLocalChanges.cs similarity index 66% rename from src/Commands/CountLocalChangesWithoutUntracked.cs rename to src/Commands/CountLocalChanges.cs index 769d732e9..17916926d 100644 --- a/src/Commands/CountLocalChangesWithoutUntracked.cs +++ b/src/Commands/CountLocalChanges.cs @@ -3,13 +3,14 @@ namespace SourceGit.Commands { - public class CountLocalChangesWithoutUntracked : Command + public class CountLocalChanges : Command { - public CountLocalChangesWithoutUntracked(string repo) + public CountLocalChanges(string repo, bool includeUntracked) { + var option = includeUntracked ? "-uall" : "-uno"; WorkingDirectory = repo; Context = repo; - Args = "--no-optional-locks status -uno --ignore-submodules=all --porcelain"; + Args = $"--no-optional-locks status {option} --ignore-submodules=all --porcelain"; } public async Task GetResultAsync() diff --git a/src/ViewModels/Checkout.cs b/src/ViewModels/Checkout.cs index 98b2a21ff..25f3e05db 100644 --- a/src/ViewModels/Checkout.cs +++ b/src/ViewModels/Checkout.cs @@ -58,7 +58,7 @@ public override async Task Sure() if (!DiscardLocalChanges) { - var changes = await new Commands.CountLocalChangesWithoutUntracked(_repo.FullPath).GetResultAsync(); + var changes = await new Commands.CountLocalChanges(_repo.FullPath, false).GetResultAsync(); if (changes > 0) { succ = await new Commands.Stash(_repo.FullPath) diff --git a/src/ViewModels/CheckoutAndFastForward.cs b/src/ViewModels/CheckoutAndFastForward.cs index 550245fe7..32ce28798 100644 --- a/src/ViewModels/CheckoutAndFastForward.cs +++ b/src/ViewModels/CheckoutAndFastForward.cs @@ -63,7 +63,7 @@ public override async Task Sure() if (!DiscardLocalChanges) { - var changes = await new Commands.CountLocalChangesWithoutUntracked(_repo.FullPath).GetResultAsync(); + var changes = await new Commands.CountLocalChanges(_repo.FullPath, false).GetResultAsync(); if (changes > 0) { succ = await new Commands.Stash(_repo.FullPath) diff --git a/src/ViewModels/CheckoutCommit.cs b/src/ViewModels/CheckoutCommit.cs index 459fba4dd..b4d4438c7 100644 --- a/src/ViewModels/CheckoutCommit.cs +++ b/src/ViewModels/CheckoutCommit.cs @@ -58,7 +58,7 @@ public override async Task Sure() if (!DiscardLocalChanges) { - var changes = await new Commands.CountLocalChangesWithoutUntracked(_repo.FullPath).GetResultAsync(); + var changes = await new Commands.CountLocalChanges(_repo.FullPath, false).GetResultAsync(); if (changes > 0) { succ = await new Commands.Stash(_repo.FullPath) diff --git a/src/ViewModels/CreateBranch.cs b/src/ViewModels/CreateBranch.cs index d213f1ecb..3958e0d56 100644 --- a/src/ViewModels/CreateBranch.cs +++ b/src/ViewModels/CreateBranch.cs @@ -142,7 +142,7 @@ public override async Task Sure() var needPopStash = false; if (!DiscardLocalChanges) { - var changes = await new Commands.CountLocalChangesWithoutUntracked(_repo.FullPath).GetResultAsync(); + var changes = await new Commands.CountLocalChanges(_repo.FullPath, false).GetResultAsync(); if (changes > 0) { succ = await new Commands.Stash(_repo.FullPath) diff --git a/src/ViewModels/Pull.cs b/src/ViewModels/Pull.cs index 2a976a060..378d63f40 100644 --- a/src/ViewModels/Pull.cs +++ b/src/ViewModels/Pull.cs @@ -119,7 +119,7 @@ public override async Task Sure() Use(log); var updateSubmodules = IsRecurseSubmoduleVisible && RecurseSubmodules; - var changes = await new Commands.CountLocalChangesWithoutUntracked(_repo.FullPath).GetResultAsync(); + var changes = await new Commands.CountLocalChanges(_repo.FullPath, false).GetResultAsync(); var needPopStash = false; if (changes > 0) { diff --git a/src/ViewModels/RepositoryNode.cs b/src/ViewModels/RepositoryNode.cs index d69f9d9dc..ea9c3fbe3 100644 --- a/src/ViewModels/RepositoryNode.cs +++ b/src/ViewModels/RepositoryNode.cs @@ -1,7 +1,7 @@ using System.Collections.Generic; using System.IO; using System.Text.Json.Serialization; - +using System.Threading.Tasks; using CommunityToolkit.Mvvm.ComponentModel; namespace SourceGit.ViewModels @@ -62,6 +62,20 @@ public int Depth set; } = 0; + [JsonIgnore] + public Models.Branch CurrentBranch + { + get => _currentBranch; + private set => SetProperty(ref _currentBranch, value); + } + + [JsonIgnore] + public int LocalChanges + { + get => _localChanges; + private set => SetProperty(ref _localChanges, value); + } + public List SubNodes { get; @@ -122,11 +136,44 @@ public void Delete() activePage.Popup = new DeleteRepositoryNode(this); } + public async Task UpdateStatusAsync() + { + if (!_isRepository || !Directory.Exists(_id)) + { + CurrentBranch = null; + LocalChanges = 0; + + if (SubNodes.Count > 0) + { + foreach (var subNode in SubNodes) + await subNode.UpdateStatusAsync(); + } + + return; + } + + LocalChanges = await new Commands.CountLocalChanges(_id, true) { RaiseError = false }.GetResultAsync(); + + var branches = await new Commands.QueryBranches(_id) { RaiseError = false }.GetResultAsync(); + foreach (var branch in branches) + { + if (branch.IsCurrent) + { + CurrentBranch = branch; + return; + } + } + + CurrentBranch = null; + } + private string _id = string.Empty; private string _name = string.Empty; private bool _isRepository = false; private int _bookmark = 0; private bool _isExpanded = false; private bool _isVisible = true; + private Models.Branch _currentBranch = null; + private int _localChanges = 0; } } diff --git a/src/ViewModels/Welcome.cs b/src/ViewModels/Welcome.cs index 2e6b3a6a3..c020cdda5 100644 --- a/src/ViewModels/Welcome.cs +++ b/src/ViewModels/Welcome.cs @@ -52,6 +52,12 @@ public void Refresh() Rows.AddRange(rows); } + public async Task UpdateStatusAsync() + { + foreach (var node in Preferences.Instance.RepositoryNodes) + await node.UpdateStatusAsync(); + } + public void ToggleNodeIsExpanded(RepositoryNode node) { node.IsExpanded = !node.IsExpanded; diff --git a/src/Views/Welcome.axaml b/src/Views/Welcome.axaml index 0f625e7af..d31bdb55e 100644 --- a/src/Views/Welcome.axaml +++ b/src/Views/Welcome.axaml @@ -3,6 +3,7 @@ xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:c="using:SourceGit.Converters" + xmlns:m="using:SourceGit.Models" xmlns:vm="using:SourceGit.ViewModels" xmlns:v="using:SourceGit.Views" mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450" @@ -106,7 +107,7 @@ + + + + + + + + - - - + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/Views/Welcome.axaml.cs b/src/Views/Welcome.axaml.cs index 365a7cbee..b9010bfe2 100644 --- a/src/Views/Welcome.axaml.cs +++ b/src/Views/Welcome.axaml.cs @@ -61,6 +61,12 @@ public Welcome() InitializeComponent(); } + protected override async void OnLoaded(RoutedEventArgs e) + { + base.OnLoaded(e); + await ViewModels.Welcome.Instance.UpdateStatusAsync(); + } + protected override void OnKeyDown(KeyEventArgs e) { base.OnKeyDown(e); From f88f920f57f62916e3cbe2a37348d342fd586744 Mon Sep 17 00:00:00 2001 From: leo Date: Wed, 22 Oct 2025 16:11:05 +0800 Subject: [PATCH 0064/1230] enhance: limit the rate to call `UpdateStatusAsync` and add hotkey `F5` to call it forcely Signed-off-by: leo --- src/ViewModels/Welcome.cs | 11 ++++++++++- src/Views/Launcher.axaml.cs | 6 ++++++ src/Views/Welcome.axaml.cs | 2 +- 3 files changed, 17 insertions(+), 2 deletions(-) diff --git a/src/ViewModels/Welcome.cs b/src/ViewModels/Welcome.cs index c020cdda5..3cb83b6ba 100644 --- a/src/ViewModels/Welcome.cs +++ b/src/ViewModels/Welcome.cs @@ -52,8 +52,16 @@ public void Refresh() Rows.AddRange(rows); } - public async Task UpdateStatusAsync() + public async Task UpdateStatusAsync(bool force) { + if (!force) + { + var passed = DateTime.Now - _lastUpdateStatus; + if (passed.TotalSeconds < 10.0) + return; + } + + _lastUpdateStatus = DateTime.Now; foreach (var node in Preferences.Instance.RepositoryNodes) await node.UpdateStatusAsync(); } @@ -275,6 +283,7 @@ private void MakeTreeRows(List rows, List nodes, } } + private DateTime _lastUpdateStatus = DateTime.UnixEpoch.ToLocalTime(); private string _searchFilter = string.Empty; } } diff --git a/src/Views/Launcher.axaml.cs b/src/Views/Launcher.axaml.cs index a8329dd01..008b98f72 100644 --- a/src/Views/Launcher.axaml.cs +++ b/src/Views/Launcher.axaml.cs @@ -266,6 +266,12 @@ protected override async void OnKeyDown(KeyEventArgs e) e.Handled = true; return; } + else if (vm.ActivePage.Data is ViewModels.Welcome welcome) + { + e.Handled = true; + await welcome.UpdateStatusAsync(true); + return; + } } base.OnKeyDown(e); diff --git a/src/Views/Welcome.axaml.cs b/src/Views/Welcome.axaml.cs index b9010bfe2..fd00a8995 100644 --- a/src/Views/Welcome.axaml.cs +++ b/src/Views/Welcome.axaml.cs @@ -64,7 +64,7 @@ public Welcome() protected override async void OnLoaded(RoutedEventArgs e) { base.OnLoaded(e); - await ViewModels.Welcome.Instance.UpdateStatusAsync(); + await ViewModels.Welcome.Instance.UpdateStatusAsync(false); } protected override void OnKeyDown(KeyEventArgs e) From c651e71229347d8c926f855cbee439c69aa2a8e5 Mon Sep 17 00:00:00 2001 From: leo Date: Wed, 22 Oct 2025 16:34:32 +0800 Subject: [PATCH 0065/1230] refactor: move rate limitation from `Welcome` to `RepositoryNode` Signed-off-by: leo --- src/ViewModels/RepositoryNode.cs | 16 +++++++++++++--- src/ViewModels/Welcome.cs | 11 +---------- 2 files changed, 14 insertions(+), 13 deletions(-) diff --git a/src/ViewModels/RepositoryNode.cs b/src/ViewModels/RepositoryNode.cs index ea9c3fbe3..eb813d830 100644 --- a/src/ViewModels/RepositoryNode.cs +++ b/src/ViewModels/RepositoryNode.cs @@ -1,4 +1,5 @@ -using System.Collections.Generic; +using System; +using System.Collections.Generic; using System.IO; using System.Text.Json.Serialization; using System.Threading.Tasks; @@ -136,7 +137,7 @@ public void Delete() activePage.Popup = new DeleteRepositoryNode(this); } - public async Task UpdateStatusAsync() + public async Task UpdateStatusAsync(bool force) { if (!_isRepository || !Directory.Exists(_id)) { @@ -146,12 +147,20 @@ public async Task UpdateStatusAsync() if (SubNodes.Count > 0) { foreach (var subNode in SubNodes) - await subNode.UpdateStatusAsync(); + await subNode.UpdateStatusAsync(force); } return; } + if (!force) + { + var passed = DateTime.Now - _lastUpdateStatus; + if (passed.TotalSeconds < 10.0) + return; + } + + _lastUpdateStatus = DateTime.Now; LocalChanges = await new Commands.CountLocalChanges(_id, true) { RaiseError = false }.GetResultAsync(); var branches = await new Commands.QueryBranches(_id) { RaiseError = false }.GetResultAsync(); @@ -175,5 +184,6 @@ public async Task UpdateStatusAsync() private bool _isVisible = true; private Models.Branch _currentBranch = null; private int _localChanges = 0; + private DateTime _lastUpdateStatus = DateTime.UnixEpoch.ToLocalTime(); } } diff --git a/src/ViewModels/Welcome.cs b/src/ViewModels/Welcome.cs index 3cb83b6ba..014f748fb 100644 --- a/src/ViewModels/Welcome.cs +++ b/src/ViewModels/Welcome.cs @@ -54,16 +54,8 @@ public void Refresh() public async Task UpdateStatusAsync(bool force) { - if (!force) - { - var passed = DateTime.Now - _lastUpdateStatus; - if (passed.TotalSeconds < 10.0) - return; - } - - _lastUpdateStatus = DateTime.Now; foreach (var node in Preferences.Instance.RepositoryNodes) - await node.UpdateStatusAsync(); + await node.UpdateStatusAsync(force); } public void ToggleNodeIsExpanded(RepositoryNode node) @@ -283,7 +275,6 @@ private void MakeTreeRows(List rows, List nodes, } } - private DateTime _lastUpdateStatus = DateTime.UnixEpoch.ToLocalTime(); private string _searchFilter = string.Empty; } } From 02ecd581c75485b75b6ac36518edd82a849ec5a0 Mon Sep 17 00:00:00 2001 From: leo Date: Wed, 22 Oct 2025 17:42:51 +0800 Subject: [PATCH 0066/1230] refactor: use new command `QueryRepositoryStatus` to update repository status in `Welcome` page Signed-off-by: leo --- src/Commands/QueryRepositoryStatus.cs | 59 ++++++++++++++++++++ src/Models/Branch.cs | 2 +- src/Models/RepositoryStatus.cs | 29 ++++++++++ src/ViewModels/RepositoryNode.cs | 33 +++--------- src/Views/Welcome.axaml | 78 +++++++++++++-------------- 5 files changed, 133 insertions(+), 68 deletions(-) create mode 100644 src/Commands/QueryRepositoryStatus.cs create mode 100644 src/Models/RepositoryStatus.cs diff --git a/src/Commands/QueryRepositoryStatus.cs b/src/Commands/QueryRepositoryStatus.cs new file mode 100644 index 000000000..32c2489a6 --- /dev/null +++ b/src/Commands/QueryRepositoryStatus.cs @@ -0,0 +1,59 @@ +using System; +using System.Text.RegularExpressions; +using System.Threading.Tasks; + +namespace SourceGit.Commands +{ + public partial class QueryRepositoryStatus : Command + { + [GeneratedRegex(@"ahead\s(\d+)")] + private static partial Regex REG_AHEAD(); + + [GeneratedRegex(@"behind\s(\d+)")] + private static partial Regex REG_BEHIND(); + + public QueryRepositoryStatus(string repo) + { + WorkingDirectory = repo; + RaiseError = false; + } + + public async Task GetResultAsync() + { + Args = "branch -l -v --format=\"%(refname:short)%00%(HEAD)%00%(upstream:track,nobracket)\""; + var rs = await ReadToEndAsync().ConfigureAwait(false); + if (!rs.IsSuccess) + return null; + + var status = new Models.RepositoryStatus(); + var lines = rs.StdOut.Split(['\r', '\n'], StringSplitOptions.RemoveEmptyEntries); + foreach (var line in lines) + { + var parts = line.Split('\0'); + if (parts.Length != 3 || !parts[1].Equals("*", StringComparison.Ordinal)) + continue; + + status.CurrentBranch = parts[0]; + if (!string.IsNullOrEmpty(parts[2])) + ParseTrackStatus(status, parts[2]); + } + + status.LocalChanges = await new CountLocalChanges(WorkingDirectory, true) { RaiseError = false } + .GetResultAsync() + .ConfigureAwait(false); + + return status; + } + + private void ParseTrackStatus(Models.RepositoryStatus status, string input) + { + var aheadMatch = REG_AHEAD().Match(input); + if (aheadMatch.Success) + status.Ahead = int.Parse(aheadMatch.Groups[1].Value); + + var behindMatch = REG_BEHIND().Match(input); + if (behindMatch.Success) + status.Behind = int.Parse(behindMatch.Groups[1].Value); + } + } +} diff --git a/src/Models/Branch.cs b/src/Models/Branch.cs index f2c593d31..47aa2153a 100644 --- a/src/Models/Branch.cs +++ b/src/Models/Branch.cs @@ -26,7 +26,7 @@ public class Branch public bool HasWorktree => !IsCurrent && !string.IsNullOrEmpty(WorktreePath); public string FriendlyName => IsLocal ? Name : $"{Remote}/{Name}"; - public bool IsTrackStatusVisible => Ahead.Count + Behind.Count > 0; + public bool IsTrackStatusVisible => Ahead.Count > 0 || Behind.Count > 0; public string TrackStatusDescription { diff --git a/src/Models/RepositoryStatus.cs b/src/Models/RepositoryStatus.cs new file mode 100644 index 000000000..c7c498ae5 --- /dev/null +++ b/src/Models/RepositoryStatus.cs @@ -0,0 +1,29 @@ +namespace SourceGit.Models +{ + public class RepositoryStatus + { + public string CurrentBranch { get; set; } = string.Empty; + public int Ahead { get; set; } = 0; + public int Behind { get; set; } = 0; + public int LocalChanges { get; set; } = 0; + + public bool IsTrackingStatusVisible + { + get + { + return Ahead > 0 || Behind > 0; + } + } + + public string TrackingDescription + { + get + { + if (Ahead > 0) + return Behind > 0 ? $"{Ahead}↑ {Behind}↓" : $"{Ahead}↑"; + + return Behind > 0 ? $"{Behind}↓" : string.Empty; + } + } + } +} diff --git a/src/ViewModels/RepositoryNode.cs b/src/ViewModels/RepositoryNode.cs index eb813d830..5dc23ed3f 100644 --- a/src/ViewModels/RepositoryNode.cs +++ b/src/ViewModels/RepositoryNode.cs @@ -64,17 +64,10 @@ public int Depth } = 0; [JsonIgnore] - public Models.Branch CurrentBranch + public Models.RepositoryStatus Status { - get => _currentBranch; - private set => SetProperty(ref _currentBranch, value); - } - - [JsonIgnore] - public int LocalChanges - { - get => _localChanges; - private set => SetProperty(ref _localChanges, value); + get => _status; + private set => SetProperty(ref _status, value); } public List SubNodes @@ -141,8 +134,7 @@ public async Task UpdateStatusAsync(bool force) { if (!_isRepository || !Directory.Exists(_id)) { - CurrentBranch = null; - LocalChanges = 0; + Status = null; if (SubNodes.Count > 0) { @@ -161,19 +153,7 @@ public async Task UpdateStatusAsync(bool force) } _lastUpdateStatus = DateTime.Now; - LocalChanges = await new Commands.CountLocalChanges(_id, true) { RaiseError = false }.GetResultAsync(); - - var branches = await new Commands.QueryBranches(_id) { RaiseError = false }.GetResultAsync(); - foreach (var branch in branches) - { - if (branch.IsCurrent) - { - CurrentBranch = branch; - return; - } - } - - CurrentBranch = null; + Status = await new Commands.QueryRepositoryStatus(_id).GetResultAsync(); } private string _id = string.Empty; @@ -182,8 +162,7 @@ public async Task UpdateStatusAsync(bool force) private int _bookmark = 0; private bool _isExpanded = false; private bool _isVisible = true; - private Models.Branch _currentBranch = null; - private int _localChanges = 0; + private Models.RepositoryStatus _status = null; private DateTime _lastUpdateStatus = DateTime.UnixEpoch.ToLocalTime(); } } diff --git a/src/Views/Welcome.axaml b/src/Views/Welcome.axaml index d31bdb55e..8bff914eb 100644 --- a/src/Views/Welcome.axaml +++ b/src/Views/Welcome.axaml @@ -158,48 +158,46 @@ IsVisible="{Binding IsInvalid}"/> - - - - - - - - - - + + + + + + FontSize="11" + Foreground="{DynamicResource Brush.Window}"/> + - - - - - - - - - - - + + + + + + + + + + + + From b412c9a13f0996515a003e8188dcd61feb07d0a3 Mon Sep 17 00:00:00 2001 From: leo Date: Wed, 22 Oct 2025 17:53:22 +0800 Subject: [PATCH 0067/1230] enhance: cancel updating repository status when `Welcome` page is unloaded Signed-off-by: leo --- src/ViewModels/RepositoryNode.cs | 8 ++++++-- src/ViewModels/Welcome.cs | 5 +++-- src/Views/Launcher.axaml.cs | 2 +- src/Views/Welcome.axaml.cs | 10 +++++++++- 4 files changed, 19 insertions(+), 6 deletions(-) diff --git a/src/ViewModels/RepositoryNode.cs b/src/ViewModels/RepositoryNode.cs index 5dc23ed3f..24f5f66e2 100644 --- a/src/ViewModels/RepositoryNode.cs +++ b/src/ViewModels/RepositoryNode.cs @@ -2,6 +2,7 @@ using System.Collections.Generic; using System.IO; using System.Text.Json.Serialization; +using System.Threading; using System.Threading.Tasks; using CommunityToolkit.Mvvm.ComponentModel; @@ -130,8 +131,11 @@ public void Delete() activePage.Popup = new DeleteRepositoryNode(this); } - public async Task UpdateStatusAsync(bool force) + public async Task UpdateStatusAsync(bool force, CancellationToken? token) { + if (token is { IsCancellationRequested: true }) + return; + if (!_isRepository || !Directory.Exists(_id)) { Status = null; @@ -139,7 +143,7 @@ public async Task UpdateStatusAsync(bool force) if (SubNodes.Count > 0) { foreach (var subNode in SubNodes) - await subNode.UpdateStatusAsync(force); + await subNode.UpdateStatusAsync(force, token); } return; diff --git a/src/ViewModels/Welcome.cs b/src/ViewModels/Welcome.cs index 014f748fb..d3416788e 100644 --- a/src/ViewModels/Welcome.cs +++ b/src/ViewModels/Welcome.cs @@ -1,6 +1,7 @@ using System; using System.Collections.Generic; using System.IO; +using System.Threading; using System.Threading.Tasks; using Avalonia.Collections; @@ -52,10 +53,10 @@ public void Refresh() Rows.AddRange(rows); } - public async Task UpdateStatusAsync(bool force) + public async Task UpdateStatusAsync(bool force, CancellationToken? token) { foreach (var node in Preferences.Instance.RepositoryNodes) - await node.UpdateStatusAsync(force); + await node.UpdateStatusAsync(force, token); } public void ToggleNodeIsExpanded(RepositoryNode node) diff --git a/src/Views/Launcher.axaml.cs b/src/Views/Launcher.axaml.cs index 008b98f72..6998fb8ae 100644 --- a/src/Views/Launcher.axaml.cs +++ b/src/Views/Launcher.axaml.cs @@ -269,7 +269,7 @@ protected override async void OnKeyDown(KeyEventArgs e) else if (vm.ActivePage.Data is ViewModels.Welcome welcome) { e.Handled = true; - await welcome.UpdateStatusAsync(true); + await welcome.UpdateStatusAsync(true, null); return; } } diff --git a/src/Views/Welcome.axaml.cs b/src/Views/Welcome.axaml.cs index fd00a8995..6a5ea2df1 100644 --- a/src/Views/Welcome.axaml.cs +++ b/src/Views/Welcome.axaml.cs @@ -1,4 +1,5 @@ using System; +using System.Threading; using Avalonia; using Avalonia.Controls; using Avalonia.Controls.Primitives; @@ -64,7 +65,13 @@ public Welcome() protected override async void OnLoaded(RoutedEventArgs e) { base.OnLoaded(e); - await ViewModels.Welcome.Instance.UpdateStatusAsync(false); + await ViewModels.Welcome.Instance.UpdateStatusAsync(false, _cancellation.Token); + } + + protected override void OnUnloaded(RoutedEventArgs e) + { + _cancellation.Cancel(); + base.OnUnloaded(e); } protected override void OnKeyDown(KeyEventArgs e) @@ -361,5 +368,6 @@ private void OnDoubleTappedTreeNode(object sender, TappedEventArgs e) private Point _pressedTreeNodePosition = new Point(); private bool _startDragTreeNode = false; private readonly DataFormat _dndRepoNode = DataFormat.CreateStringApplicationFormat("sourcegit-dnd-repo-node"); + private CancellationTokenSource _cancellation = new CancellationTokenSource(); } } From fb046d551f52837544d1d5ece77c64c0e99267ae Mon Sep 17 00:00:00 2001 From: leo Date: Wed, 22 Oct 2025 17:59:21 +0800 Subject: [PATCH 0068/1230] enhance: cache last status of repository for better experience Signed-off-by: leo --- src/ViewModels/RepositoryNode.cs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/ViewModels/RepositoryNode.cs b/src/ViewModels/RepositoryNode.cs index 24f5f66e2..770e1b86e 100644 --- a/src/ViewModels/RepositoryNode.cs +++ b/src/ViewModels/RepositoryNode.cs @@ -64,11 +64,10 @@ public int Depth set; } = 0; - [JsonIgnore] public Models.RepositoryStatus Status { get => _status; - private set => SetProperty(ref _status, value); + set => SetProperty(ref _status, value); } public List SubNodes From b15d714b849e679121e7d7de2299f7fc20ce36d9 Mon Sep 17 00:00:00 2001 From: leo Date: Wed, 22 Oct 2025 18:15:50 +0800 Subject: [PATCH 0069/1230] enhance: update repository status immediately on the first time that it is added Signed-off-by: leo --- src/ViewModels/Clone.cs | 2 ++ src/ViewModels/Init.cs | 4 +++- src/ViewModels/RepositoryNode.cs | 17 ++++++++++++++--- src/ViewModels/ScanRepositories.cs | 6 ++++-- src/ViewModels/Welcome.cs | 18 ++++++++++++++++-- src/Views/Welcome.axaml.cs | 4 ++-- src/Views/WelcomeToolbar.axaml.cs | 2 +- 7 files changed, 42 insertions(+), 11 deletions(-) diff --git a/src/ViewModels/Clone.cs b/src/ViewModels/Clone.cs index cfcf51bf2..73033682e 100644 --- a/src/ViewModels/Clone.cs +++ b/src/ViewModels/Clone.cs @@ -150,6 +150,8 @@ public override async Task Sure() log.Complete(); var node = Preferences.Instance.FindOrAddNodeByRepositoryPath(path, null, true); + await node.UpdateStatusAsync(false, null); + var launcher = App.GetLauncher(); LauncherPage page = null; foreach (var one in launcher.Pages) diff --git a/src/ViewModels/Init.cs b/src/ViewModels/Init.cs index 7f349917f..f6740d0f6 100644 --- a/src/ViewModels/Init.cs +++ b/src/ViewModels/Init.cs @@ -39,7 +39,9 @@ public override async Task Sure() if (succ) { - Preferences.Instance.FindOrAddNodeByRepositoryPath(_targetPath, _parentNode, true); + var node = Preferences.Instance.FindOrAddNodeByRepositoryPath(_targetPath, _parentNode, true); + await node.UpdateStatusAsync(false, null); + Welcome.Instance.Refresh(); } return succ; diff --git a/src/ViewModels/RepositoryNode.cs b/src/ViewModels/RepositoryNode.cs index 770e1b86e..1f5515b9d 100644 --- a/src/ViewModels/RepositoryNode.cs +++ b/src/ViewModels/RepositoryNode.cs @@ -135,19 +135,30 @@ public async Task UpdateStatusAsync(bool force, CancellationToken? token) if (token is { IsCancellationRequested: true }) return; - if (!_isRepository || !Directory.Exists(_id)) + if (!_isRepository) { Status = null; if (SubNodes.Count > 0) { - foreach (var subNode in SubNodes) - await subNode.UpdateStatusAsync(force, token); + // avoid collection was modified while enumerating. + var nodes = new List(); + nodes.AddRange(SubNodes); + + foreach (var node in nodes) + await node.UpdateStatusAsync(force, token); } return; } + if (!Directory.Exists(_id)) + { + _lastUpdateStatus = DateTime.Now; + Status = null; + return; + } + if (!force) { var passed = DateTime.Now - _lastUpdateStatus; diff --git a/src/ViewModels/ScanRepositories.cs b/src/ViewModels/ScanRepositories.cs index 6802897aa..e7316dba5 100644 --- a/src/ViewModels/ScanRepositories.cs +++ b/src/ViewModels/ScanRepositories.cs @@ -97,13 +97,15 @@ public override async Task Sure() var parent = new DirectoryInfo(f).Parent!.FullName.Replace('\\', '/').TrimEnd('/'); if (parent.Equals(normalizedRoot, StringComparison.Ordinal)) { - Preferences.Instance.FindOrAddNodeByRepositoryPath(f, null, false, false); + var node = Preferences.Instance.FindOrAddNodeByRepositoryPath(f, null, false, false); + await node.UpdateStatusAsync(false, null); } else if (parent.StartsWith(normalizedRoot, StringComparison.Ordinal)) { var relative = parent.Substring(normalizedRoot.Length).TrimStart('/'); var group = FindOrCreateGroupRecursive(Preferences.Instance.RepositoryNodes, relative); - Preferences.Instance.FindOrAddNodeByRepositoryPath(f, group, false, false); + var node = Preferences.Instance.FindOrAddNodeByRepositoryPath(f, group, false, false); + await node.UpdateStatusAsync(false, null); } } diff --git a/src/ViewModels/Welcome.cs b/src/ViewModels/Welcome.cs index d3416788e..1fa1797a8 100644 --- a/src/ViewModels/Welcome.cs +++ b/src/ViewModels/Welcome.cs @@ -55,8 +55,19 @@ public void Refresh() public async Task UpdateStatusAsync(bool force, CancellationToken? token) { - foreach (var node in Preferences.Instance.RepositoryNodes) + if (_isUpdatingStatus) + return; + + _isUpdatingStatus = true; + + // avoid collection was modified while enumerating. + var nodes = new List(); + nodes.AddRange(Preferences.Instance.RepositoryNodes); + + foreach (var node in nodes) await node.UpdateStatusAsync(force, token); + + _isUpdatingStatus = false; } public void ToggleNodeIsExpanded(RepositoryNode node) @@ -130,9 +141,11 @@ public void InitRepository(string path, RepositoryNode parent, string reason) activePage.Popup = new Init(activePage.Node.Id, path, parent, reason); } - public void AddRepository(string path, RepositoryNode parent, bool moveNode, bool open) + public async Task AddRepositoryAsync(string path, RepositoryNode parent, bool moveNode, bool open) { var node = Preferences.Instance.FindOrAddNodeByRepositoryPath(path, parent, moveNode); + await node.UpdateStatusAsync(false, null); + if (open) node.Open(); } @@ -277,5 +290,6 @@ private void MakeTreeRows(List rows, List nodes, } private string _searchFilter = string.Empty; + private bool _isUpdatingStatus = false; } } diff --git a/src/Views/Welcome.axaml.cs b/src/Views/Welcome.axaml.cs index 6a5ea2df1..7a3565c02 100644 --- a/src/Views/Welcome.axaml.cs +++ b/src/Views/Welcome.axaml.cs @@ -279,7 +279,7 @@ private async void DropOnTreeView(object sender, DragEventArgs e) var path = await ViewModels.Welcome.Instance.GetRepositoryRootAsync(item.Path.LocalPath); if (!string.IsNullOrEmpty(path)) { - ViewModels.Welcome.Instance.AddRepository(path, null, true, false); + await ViewModels.Welcome.Instance.AddRepositoryAsync(path, null, true, false); refresh = true; } } @@ -338,7 +338,7 @@ private async void DropOnTreeNode(object sender, DragEventArgs e) var path = await ViewModels.Welcome.Instance.GetRepositoryRootAsync(item.Path.LocalPath); if (!string.IsNullOrEmpty(path)) { - ViewModels.Welcome.Instance.AddRepository(path, to, true, false); + await ViewModels.Welcome.Instance.AddRepositoryAsync(path, to, true, false); refresh = true; } } diff --git a/src/Views/WelcomeToolbar.axaml.cs b/src/Views/WelcomeToolbar.axaml.cs index 65b4ca129..7d901e35c 100644 --- a/src/Views/WelcomeToolbar.axaml.cs +++ b/src/Views/WelcomeToolbar.axaml.cs @@ -47,7 +47,7 @@ private async void OpenLocalRepository(object _1, RoutedEventArgs e) var repoPath = await ViewModels.Welcome.Instance.GetRepositoryRootAsync(folderPath); if (!string.IsNullOrEmpty(repoPath)) { - ViewModels.Welcome.Instance.AddRepository(repoPath, null, false, true); + await ViewModels.Welcome.Instance.AddRepositoryAsync(repoPath, null, false, true); ViewModels.Welcome.Instance.Refresh(); } else if (Directory.Exists(folderPath)) From c4929dddc86ea24213f46cf91ca748c855db12b7 Mon Sep 17 00:00:00 2001 From: leo Date: Wed, 22 Oct 2025 19:46:48 +0800 Subject: [PATCH 0070/1230] refactor: remove unused `ConterPresenter` Signed-off-by: leo --- src/Views/Repository.axaml | 42 ++++++++++------ src/Views/Repository.axaml.cs | 93 ----------------------------------- 2 files changed, 26 insertions(+), 109 deletions(-) diff --git a/src/Views/Repository.axaml b/src/Views/Repository.axaml index 18c5c06a3..59512aba2 100644 --- a/src/Views/Repository.axaml +++ b/src/Views/Repository.axaml @@ -131,14 +131,19 @@ - + + + - + + + - - - - diff --git a/src/Views/RepositoryConfigure.axaml b/src/Views/RepositoryConfigure.axaml index 91af9c447..b56925617 100644 --- a/src/Views/RepositoryConfigure.axaml +++ b/src/Views/RepositoryConfigure.axaml @@ -242,13 +242,9 @@ - - - - @@ -347,13 +343,10 @@ - - - From a14ef05641f57c853cb0088e44bf75f8f2c46f58 Mon Sep 17 00:00:00 2001 From: leo Date: Wed, 22 Oct 2025 20:15:34 +0800 Subject: [PATCH 0072/1230] enhance: supports to find `zed` or `zeditor` from `PATH` and `~/.local/bin/zed` (#1869) Signed-off-by: leo --- src/Native/Linux.cs | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/src/Native/Linux.cs b/src/Native/Linux.cs index f6eb4ebf3..38d3da8b2 100644 --- a/src/Native/Linux.cs +++ b/src/Native/Linux.cs @@ -57,7 +57,18 @@ public string FindTerminal(Models.ShellOrTerminal shell) finder.Fleet(() => FindJetBrainsFleet(localAppDataDir)); finder.FindJetBrainsFromToolbox(() => Path.Combine(localAppDataDir, "JetBrains/Toolbox")); finder.SublimeText(() => FindExecutable("subl")); - finder.Zed(() => FindExecutable("zeditor")); + finder.Zed(() => + { + var exec = FindExecutable("zeditor"); + if (string.IsNullOrEmpty(exec)) + { + exec = FindExecutable("zed"); + if (string.IsNullOrEmpty(exec)) + exec = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.UserProfile), ".local", "bin", "zed"); + } + + return exec; + }); return finder.Tools; } From ba06f83f1dca3a483f50b7adc73d486dcd90b954 Mon Sep 17 00:00:00 2001 From: leo Date: Wed, 22 Oct 2025 20:29:28 +0800 Subject: [PATCH 0073/1230] refactor: `FindExecutable` on Linux will always try to find file under `~/.local/bin` Signed-off-by: leo --- src/Native/Linux.cs | 12 +++--------- 1 file changed, 3 insertions(+), 9 deletions(-) diff --git a/src/Native/Linux.cs b/src/Native/Linux.cs index 38d3da8b2..9cae29f46 100644 --- a/src/Native/Linux.cs +++ b/src/Native/Linux.cs @@ -60,14 +60,7 @@ public string FindTerminal(Models.ShellOrTerminal shell) finder.Zed(() => { var exec = FindExecutable("zeditor"); - if (string.IsNullOrEmpty(exec)) - { - exec = FindExecutable("zed"); - if (string.IsNullOrEmpty(exec)) - exec = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.UserProfile), ".local", "bin", "zed"); - } - - return exec; + return string.IsNullOrEmpty(exec) ? FindExecutable("zed") : exec; }); return finder.Tools; } @@ -141,7 +134,8 @@ private string FindExecutable(string filename) return test; } - return string.Empty; + var local = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.UserProfile), ".local", "bin", filename); + return File.Exists(local) ? local : string.Empty; } private string FindJetBrainsFleet(string localAppDataDir) From adf48be160de8755fdf61a57eeaf1a3316bbbf6c Mon Sep 17 00:00:00 2001 From: AquariusStar <48148723+AquariusStar@users.noreply.github.com> Date: Thu, 23 Oct 2025 05:30:56 +0300 Subject: [PATCH 0074/1230] localization: update Russian translation (#1870) --- src/Resources/Locales/ru_RU.axaml | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/src/Resources/Locales/ru_RU.axaml b/src/Resources/Locales/ru_RU.axaml index 56db54577..3373ec63b 100644 --- a/src/Resources/Locales/ru_RU.axaml +++ b/src/Resources/Locales/ru_RU.axaml @@ -56,6 +56,7 @@ Пропустить Раздвоение. Сделать текущую ревизию хорошей или плохой и переключиться на другой. Расследование + Расследование на предыдущей редакции РАССЛЕДОВАНИЕ В ЭТОМ ФАЙЛЕ НЕ ПОДДЕРЖИВАЕТСЯ!!! Переключиться на ${0}$... Сравнить с ${0}$ @@ -188,19 +189,19 @@ Введите тему ревизии Настройка репозитория ШАБЛОН РЕВИЗИИ - Параметры сборки: + Встроенные параметры: ${branch_name} Имя текущей локальной ветки ${files_num} Количество изменённых файлов ${files} Пути изменённых файлов - ${files:N} Максимальное количество N путей изменённых файлов - ${pure_files} Похожие ${files}, но только чистые имена файлов - ${pure_files:N} Похожие ${files:N}, но без каталогов + ${files:N} Пути изменённых файлов, не более N + ${pure_files} То же, что и ${files}, но только имена файлов + ${pure_files:N} То же, что и ${files:N}, но только имена файлов Cодержание: Название: ПОЛЬЗОВАТЕЛЬСКОЕ ДЕЙСТВИЕ Аргументы: - Параметры сборки: + Встроенные параметры: ${REPO} Путь репозитория ${REMOTE} Выбранная удаённая ветка @@ -225,6 +226,7 @@ GIT Автозагрузка изменений Минут(а/ы) + Общепринятые типы ревизии Внешний репозиторий по умолчанию Предпочтительный режим слияния ОТСЛЕЖИВАНИЕ ПРОБЛЕМ @@ -258,6 +260,7 @@ Метка: Опции: Используйте разделитель «|» для опций + Встроенные переменные ${REPO}, ${REMOTE}, ${BRANCH}, ${BRANCH_FRIENDLY_NAME}, ${SHA}, и ${TAG} останутся здесь доступными Тип: Рабочие пространства Цвет @@ -266,7 +269,7 @@ ПРОДОЛЖИТЬ Обнаружена пустая ревизия! Вы хотите продолжить (--allow-empty)? Сформировать всё и зафиксировать ревизию - Обнаружена пустая ревизия! Вы хотите продолжить (--allow-empty) или отложить всё, затем зафиксировать ревизию? + Обнаружена пустая ревизия! Вы хотите продолжить (--allow-empty) или отложить всё, а затем зафиксировать ревизию? Требуется перезапуск Вы должны перезапустить приложение после применения изменений. Общепринятый помощник по ревизии @@ -767,7 +770,7 @@ Загрузка Пропустить эту версию Обновление ПО - В настоящее время обновления недоступны. + Сейчас нет обновлений. Установить ветку подмодуля Подмодуль: Текущий: From 1a5c63da22e7a4ce485e0b89aadc95e4cd59f63b Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Thu, 23 Oct 2025 02:31:16 +0000 Subject: [PATCH 0075/1230] 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 c02202014..ff6dba740 100644 --- a/TRANSLATION.md +++ b/TRANSLATION.md @@ -814,16 +814,7 @@ This document shows the translation status of each locale file in the repository
-### ![ru__RU](https://img.shields.io/badge/ru__RU-99.67%25-yellow) - -
-Missing keys in ru_RU.axaml - -- Text.Blame.BlameOnPreviousRevision -- Text.Configure.Git.ConventionalTypesOverride -- Text.ConfigureCustomActionControls.StringValue.Tip - -
+### ![ru__RU](https://img.shields.io/badge/ru__RU-%E2%88%9A-brightgreen) ### ![ta__IN](https://img.shields.io/badge/ta__IN-77.26%25-yellow) From 99389dba899656f3635945c64308967c4548500c Mon Sep 17 00:00:00 2001 From: leo Date: Thu, 23 Oct 2025 10:59:23 +0800 Subject: [PATCH 0076/1230] refactor: always load commit message from `.git/MERGE_MSG` or `.git/rebase-merge/message` when there is some operation in progress (#1795) Signed-off-by: leo --- src/ViewModels/WorkingCopy.cs | 65 ++++++++++++++++++++--------------- 1 file changed, 38 insertions(+), 27 deletions(-) diff --git a/src/ViewModels/WorkingCopy.cs b/src/ViewModels/WorkingCopy.cs index 1acda60c1..586a13dd0 100644 --- a/src/ViewModels/WorkingCopy.cs +++ b/src/ViewModels/WorkingCopy.cs @@ -769,43 +769,54 @@ private void UpdateDetail() private void UpdateInProgressState() { - if (string.IsNullOrEmpty(_commitMessage)) - { - var mergeMsgFile = Path.Combine(_repo.GitDir, "MERGE_MSG"); - if (File.Exists(mergeMsgFile)) - CommitMessage = File.ReadAllText(mergeMsgFile); - } + var oldType = _inProgressContext != null ? _inProgressContext.GetType() : null; if (File.Exists(Path.Combine(_repo.GitDir, "CHERRY_PICK_HEAD"))) - { InProgressContext = new CherryPickInProgress(_repo); - } else if (Directory.Exists(Path.Combine(_repo.GitDir, "rebase-merge")) || Directory.Exists(Path.Combine(_repo.GitDir, "rebase-apply"))) - { - var rebasing = new RebaseInProgress(_repo); - InProgressContext = rebasing; - - if (string.IsNullOrEmpty(_commitMessage)) - { - var rebaseMsgFile = Path.Combine(_repo.GitDir, "rebase-merge", "message"); - if (File.Exists(rebaseMsgFile)) - CommitMessage = File.ReadAllText(rebaseMsgFile); - else if (rebasing.StoppedAt != null) - CommitMessage = new Commands.QueryCommitFullMessage(_repo.FullPath, rebasing.StoppedAt.SHA).GetResult(); - } - } + InProgressContext = new RebaseInProgress(_repo); else if (File.Exists(Path.Combine(_repo.GitDir, "REVERT_HEAD"))) - { InProgressContext = new RevertInProgress(_repo); - } else if (File.Exists(Path.Combine(_repo.GitDir, "MERGE_HEAD"))) - { InProgressContext = new MergeInProgress(_repo); - } else - { InProgressContext = null; - } + + if (_inProgressContext == null) + return; + + if (_inProgressContext.GetType() == oldType && !string.IsNullOrEmpty(_commitMessage)) + return; + + do + { + if (LoadCommitMessageFromFile(Path.Combine(_repo.GitDir, "MERGE_MSG"))) + break; + + if (LoadCommitMessageFromFile(Path.Combine(_repo.GitDir, "rebase-merge", "message"))) + break; + + var rebaseProcessingFile = Path.Combine(_repo.GitDir, "rebase-apply", "final-commit"); + if (File.Exists(rebaseProcessingFile)) + { + var sha = File.ReadAllText(rebaseProcessingFile); + if (!string.IsNullOrEmpty(sha)) + CommitMessage = new Commands.QueryCommitFullMessage(_repo.FullPath, sha).GetResult(); + } + } while (false); + } + + private bool LoadCommitMessageFromFile(string file) + { + if (!File.Exists(file)) + return false; + + var msg = File.ReadAllText(file).Trim(); + if (string.IsNullOrEmpty(msg)) + return false; + + CommitMessage = msg; + return true; } private void SetDetail(Models.Change change, bool isUnstaged) From 89bc8a7e06db5f54766a46466151cdee846f78ee Mon Sep 17 00:00:00 2001 From: leo Date: Thu, 23 Oct 2025 14:49:48 +0800 Subject: [PATCH 0077/1230] feature: add original pick-order for commits in `Interactive Rebase` window (#1676) Signed-off-by: leo --- src/ViewModels/InteractiveRebase.cs | 10 ++++++++-- src/Views/InteractiveRebase.axaml | 20 ++++++++++++++------ 2 files changed, 22 insertions(+), 8 deletions(-) diff --git a/src/ViewModels/InteractiveRebase.cs b/src/ViewModels/InteractiveRebase.cs index 719b8354b..5e88ce519 100644 --- a/src/ViewModels/InteractiveRebase.cs +++ b/src/ViewModels/InteractiveRebase.cs @@ -15,6 +15,11 @@ public record InteractiveRebasePrefill(string SHA, Models.InteractiveRebaseActio public class InteractiveRebaseItem : ObservableObject { + public int OriginalOrder + { + get; + } + public Models.Commit Commit { get; @@ -59,8 +64,9 @@ public string FullMessage } } - public InteractiveRebaseItem(Models.Commit c, string message, bool canSquashOrFixup) + public InteractiveRebaseItem(int order, Models.Commit c, string message, bool canSquashOrFixup) { + OriginalOrder = order; Commit = c; FullMessage = message; CanSquashOrFixup = canSquashOrFixup; @@ -142,7 +148,7 @@ public InteractiveRebase(Repository repo, Models.Commit on, InteractiveRebasePre for (var i = 0; i < commits.Count; i++) { var c = commits[i]; - list.Add(new InteractiveRebaseItem(c.Commit, c.Message, i < commits.Count - 1)); + list.Add(new InteractiveRebaseItem(commits.Count - i, c.Commit, c.Message, i < commits.Count - 1)); } var selected = list.Count > 0 ? list[0] : null; diff --git a/src/Views/InteractiveRebase.axaml b/src/Views/InteractiveRebase.axaml index 1c9ed42e5..86561d81c 100644 --- a/src/Views/InteractiveRebase.axaml +++ b/src/Views/InteractiveRebase.axaml @@ -84,6 +84,7 @@ + @@ -111,8 +112,15 @@ VerticalAlignment="Center"/> + + + - - + - +