From c2187edbe94edf6ea9e3d5a5e198ea8039c11fdf Mon Sep 17 00:00:00 2001 From: leo Date: Tue, 3 Jun 2025 23:36:15 +0800 Subject: [PATCH] fix: running git command in `UIThread` via context menu entry blocks whole app (#1384) Signed-off-by: leo --- src/ViewModels/CommitDetail.cs | 12 ++++++------ src/ViewModels/FileHistories.cs | 23 +++++++++-------------- src/ViewModels/StashesPage.cs | 22 ++++++++++++++++++++-- src/Views/FileHistories.axaml.cs | 4 ++-- 4 files changed, 37 insertions(+), 24 deletions(-) diff --git a/src/ViewModels/CommitDetail.cs b/src/ViewModels/CommitDetail.cs index 959cc3fe..55618897 100644 --- a/src/ViewModels/CommitDetail.cs +++ b/src/ViewModels/CommitDetail.cs @@ -56,7 +56,7 @@ namespace SourceGit.ViewModels { get; private set; - } = []; + } public List Children { @@ -426,12 +426,12 @@ namespace SourceGit.ViewModels var openWith = new MenuItem(); openWith.Header = App.Text("OpenWith"); openWith.Icon = App.CreateMenuIcon("Icons.OpenWith"); - openWith.Click += (_, ev) => + openWith.Click += async (_, ev) => { var fileName = Path.GetFileNameWithoutExtension(fullPath) ?? ""; var fileExt = Path.GetExtension(fullPath) ?? ""; var tmpFile = Path.Combine(Path.GetTempPath(), $"{fileName}~{_commit.SHA.Substring(0, 10)}{fileExt}"); - Commands.SaveRevisionFile.Run(_repo.FullPath, _commit.SHA, file.Path, tmpFile); + await Task.Run(() => Commands.SaveRevisionFile.Run(_repo.FullPath, _commit.SHA, file.Path, tmpFile)); Native.OS.OpenWithDefaultEditor(tmpFile); ev.Handled = true; }; @@ -453,9 +453,9 @@ namespace SourceGit.ViewModels if (selected.Count == 1) { var folder = selected[0]; - var folderPath = folder is { Path: { IsAbsoluteUri: true } path } ? path.LocalPath : folder?.Path.ToString(); - var saveTo = Path.Combine(folderPath, Path.GetFileName(file.Path)); - Commands.SaveRevisionFile.Run(_repo.FullPath, _commit.SHA, file.Path, saveTo); + var folderPath = folder is { Path: { IsAbsoluteUri: true } path } ? path.LocalPath : folder.Path.ToString(); + var saveTo = Path.Combine(folderPath, Path.GetFileName(file.Path)!); + await Task.Run(() => Commands.SaveRevisionFile.Run(_repo.FullPath, _commit.SHA, file.Path, saveTo)); } } catch (Exception e) diff --git a/src/ViewModels/FileHistories.cs b/src/ViewModels/FileHistories.cs index 9f91205e..0e474af2 100644 --- a/src/ViewModels/FileHistories.cs +++ b/src/ViewModels/FileHistories.cs @@ -47,9 +47,9 @@ namespace SourceGit.ViewModels RefreshViewContent(); } - public void ResetToSelectedRevision() + public Task ResetToSelectedRevision() { - new Commands.Checkout(_repo.FullPath).FileWithRevision(_file, $"{_revision.SHA}"); + return Task.Run(() => new Commands.Checkout(_repo.FullPath).FileWithRevision(_file, $"{_revision.SHA}")); } private void RefreshViewContent() @@ -84,7 +84,7 @@ namespace SourceGit.ViewModels var stream = Commands.QueryFileContent.Run(_repo.FullPath, _revision.SHA, _file); var fileSize = stream.Length; var bitmap = fileSize > 0 ? new Bitmap(stream) : null; - var imageType = Path.GetExtension(_file).TrimStart('.').ToUpper(CultureInfo.CurrentCulture); + var imageType = Path.GetExtension(_file)!.TrimStart('.').ToUpper(CultureInfo.CurrentCulture); var image = new Models.RevisionImageFile() { Image = bitmap, FileSize = fileSize, ImageType = imageType }; Dispatcher.UIThread.Invoke(() => ViewContent = new FileHistoriesRevisionFile(_file, image)); } @@ -103,7 +103,7 @@ namespace SourceGit.ViewModels var matchLFS = REG_LFS_FORMAT().Match(content); if (matchLFS.Success) { - var lfs = new Models.RevisionLFSObject() { Object = new Models.LFSObject() }; + var lfs = new Models.RevisionLFSObject() { Object = new() }; lfs.Object.Oid = matchLFS.Groups[1].Value; lfs.Object.Size = long.Parse(matchLFS.Groups[2].Value); Dispatcher.UIThread.Invoke(() => ViewContent = new FileHistoriesRevisionFile(_file, lfs)); @@ -156,15 +156,12 @@ namespace SourceGit.ViewModels [GeneratedRegex(@"^version https://git-lfs.github.com/spec/v\d+\r?\noid sha256:([0-9a-f]+)\r?\nsize (\d+)[\r\n]*$")] private static partial Regex REG_LFS_FORMAT(); - private static readonly HashSet IMG_EXTS = new HashSet() - { - ".ico", ".bmp", ".jpg", ".png", ".jpeg", ".webp" - }; + private static readonly HashSet IMG_EXTS = [".ico", ".bmp", ".jpg", ".png", ".jpeg", ".webp"]; private Repository _repo = null; private string _file = null; private Models.Commit _revision = null; - private bool _isDiffMode = true; + private bool _isDiffMode = false; private object _viewContent = null; } @@ -265,7 +262,6 @@ namespace SourceGit.ViewModels public FileHistories(Repository repo, string file, string commit = null) { _repo = repo; - _file = file; Task.Run(() => { @@ -288,10 +284,10 @@ namespace SourceGit.ViewModels switch (SelectedCommits.Count) { case 1: - ViewContent = new FileHistoriesSingleRevision(_repo, _file, SelectedCommits[0], _prevIsDiffMode); + ViewContent = new FileHistoriesSingleRevision(_repo, file, SelectedCommits[0], _prevIsDiffMode); break; case 2: - ViewContent = new FileHistoriesCompareRevisions(_repo, _file, SelectedCommits[0], SelectedCommits[1]); + ViewContent = new FileHistoriesCompareRevisions(_repo, file, SelectedCommits[0], SelectedCommits[1]); break; default: ViewContent = SelectedCommits.Count; @@ -317,11 +313,10 @@ namespace SourceGit.ViewModels } private readonly Repository _repo = null; - private readonly string _file = null; private bool _isLoading = true; private bool _prevIsDiffMode = true; private List _commits = null; - private Dictionary _fullCommitMessages = new Dictionary(); + private Dictionary _fullCommitMessages = new(); private object _viewContent = null; } } diff --git a/src/ViewModels/StashesPage.cs b/src/ViewModels/StashesPage.cs index 5449e4c1..dec8ea6b 100644 --- a/src/ViewModels/StashesPage.cs +++ b/src/ViewModels/StashesPage.cs @@ -234,10 +234,28 @@ namespace SourceGit.ViewModels var resetToThisRevision = new MenuItem(); resetToThisRevision.Header = App.Text("ChangeCM.CheckoutThisRevision"); resetToThisRevision.Icon = App.CreateMenuIcon("Icons.File.Checkout"); - resetToThisRevision.Click += (_, ev) => + resetToThisRevision.Click += async (_, ev) => { var log = _repo.CreateLog($"Reset File to '{_selectedStash.SHA}'"); - new Commands.Checkout(_repo.FullPath).Use(log).FileWithRevision(change.Path, $"{_selectedStash.SHA}"); + + await Task.Run(() => + { + if (_untracked.Contains(change)) + { + Commands.SaveRevisionFile.Run(_repo.FullPath, _selectedStash.Parents[2], change.Path, fullPath); + } + else if (change.Index == Models.ChangeState.Added) + { + Commands.SaveRevisionFile.Run(_repo.FullPath, _selectedStash.SHA, change.Path, fullPath); + } + else + { + new Commands.Checkout(_repo.FullPath) + .Use(log) + .FileWithRevision(change.Path, $"{_selectedStash.SHA}"); + } + }); + log.Complete(); ev.Handled = true; }; diff --git a/src/Views/FileHistories.axaml.cs b/src/Views/FileHistories.axaml.cs index 3631eb71..f0e001c8 100644 --- a/src/Views/FileHistories.axaml.cs +++ b/src/Views/FileHistories.axaml.cs @@ -25,11 +25,11 @@ namespace SourceGit.Views e.Handled = true; } - private void OnResetToSelectedRevision(object sender, RoutedEventArgs e) + private async void OnResetToSelectedRevision(object sender, RoutedEventArgs e) { if (sender is Button { DataContext: ViewModels.FileHistoriesSingleRevision single }) { - single.ResetToSelectedRevision(); + await single.ResetToSelectedRevision(); NotifyDonePanel.IsVisible = true; }