From 75c667f7b2bf24188f1b57abb0adbdc1ad06bdfd Mon Sep 17 00:00:00 2001 From: leo Date: Wed, 5 Nov 2025 11:38:03 +0800 Subject: [PATCH] refactor: move searching commit code from `Repository` to `SearchCommitContext` and keep inputs remained while repo is opened (#1884) Signed-off-by: leo --- src/ViewModels/Histories.cs | 12 +- src/ViewModels/Repository.cs | 229 +----------------------- src/ViewModels/SearchCommitContext.cs | 239 ++++++++++++++++++++++++++ src/Views/Repository.axaml | 28 +-- src/Views/Repository.axaml.cs | 27 +-- 5 files changed, 283 insertions(+), 252 deletions(-) create mode 100644 src/ViewModels/SearchCommitContext.cs diff --git a/src/ViewModels/Histories.cs b/src/ViewModels/Histories.cs index 0554c449..b264cd2f 100644 --- a/src/ViewModels/Histories.cs +++ b/src/ViewModels/Histories.cs @@ -157,14 +157,14 @@ namespace SourceGit.ViewModels { if (commits.Count == 0) { - _repo.SelectedSearchedCommit = null; + _repo.SearchCommitContext.Selected = null; DetailContext = null; } else if (commits.Count == 1) { var commit = (commits[0] as Models.Commit)!; - if (_repo.SelectedSearchedCommit == null || _repo.SelectedSearchedCommit.SHA != commit.SHA) - _repo.SelectedSearchedCommit = _repo.SearchedCommits.Find(x => x.SHA == commit.SHA); + if (_repo.SearchCommitContext.Selected == null || _repo.SearchCommitContext.Selected.SHA != commit.SHA) + _repo.SearchCommitContext.Selected = _repo.SearchCommitContext.Results?.Find(x => x.SHA == commit.SHA); AutoSelectedCommit = commit; NavigationId = _navigationId + 1; @@ -182,7 +182,7 @@ namespace SourceGit.ViewModels } else if (commits.Count == 2) { - _repo.SelectedSearchedCommit = null; + _repo.SearchCommitContext.Selected = null; var end = commits[0] as Models.Commit; var start = commits[1] as Models.Commit; @@ -190,7 +190,7 @@ namespace SourceGit.ViewModels } else { - _repo.SelectedSearchedCommit = null; + _repo.SearchCommitContext.Selected = null; DetailContext = new Models.Count(commits.Count); } } @@ -379,7 +379,7 @@ namespace SourceGit.ViewModels var head = _commits.Find(x => x.IsCurrentHead); if (head == null) { - _repo.SelectedSearchedCommit = null; + _repo.SearchCommitContext.Selected = null; head = await new Commands.QuerySingleCommit(_repo.FullPath, "HEAD").GetResultAsync(); if (head != null) DetailContext = new RevisionCompare(_repo.FullPath, commit, head); diff --git a/src/ViewModels/Repository.cs b/src/ViewModels/Repository.cs index 920f725d..7b400d8c 100644 --- a/src/ViewModels/Repository.cs +++ b/src/ViewModels/Repository.cs @@ -271,83 +271,16 @@ namespace SourceGit.ViewModels if (SetProperty(ref _isSearching, value)) { if (value) - { SelectedViewIndex = 0; - CalcWorktreeFilesForSearching(); - } else - { - SearchedCommits = new List(); - SelectedSearchedCommit = null; - SearchCommitFilter = string.Empty; - MatchedFilesForSearching = null; - _requestingWorktreeFiles = false; - _worktreeFiles = null; - } + _searchCommitContext.EndSearch(); } } } - public bool IsSearchLoadingVisible + public SearchCommitContext SearchCommitContext { - get => _isSearchLoadingVisible; - private set => SetProperty(ref _isSearchLoadingVisible, value); - } - - public bool OnlySearchCommitsInCurrentBranch - { - get => _onlySearchCommitsInCurrentBranch; - set - { - if (SetProperty(ref _onlySearchCommitsInCurrentBranch, value) && !string.IsNullOrEmpty(_searchCommitFilter)) - StartSearchCommits(); - } - } - - public int SearchCommitFilterType - { - get => _searchCommitFilterType; - set - { - if (SetProperty(ref _searchCommitFilterType, value)) - { - CalcWorktreeFilesForSearching(); - if (!string.IsNullOrEmpty(_searchCommitFilter)) - StartSearchCommits(); - } - } - } - - public string SearchCommitFilter - { - get => _searchCommitFilter; - set - { - if (SetProperty(ref _searchCommitFilter, value) && IsSearchingCommitsByFilePath()) - CalcMatchedFilesForSearching(); - } - } - - public List MatchedFilesForSearching - { - get => _matchedFilesForSearching; - private set => SetProperty(ref _matchedFilesForSearching, value); - } - - public List SearchedCommits - { - get => _searchedCommits; - set => SetProperty(ref _searchedCommits, value); - } - - public Models.Commit SelectedSearchedCommit - { - get => _selectedSearchedCommit; - set - { - if (SetProperty(ref _selectedSearchedCommit, value) && value != null) - NavigateToCommit(value.SHA); - } + get => _searchCommitContext; } public bool IsLocalBranchGroupExpanded @@ -562,6 +495,7 @@ namespace SourceGit.ViewModels _histories = new Histories(this); _workingCopy = new WorkingCopy(this) { CommitMessage = _settings.LastCommitMessage }; _stashesPage = new StashesPage(this); + _searchCommitContext = new SearchCommitContext(this); if (Preferences.Instance.ShowLocalChangesByDefault) { @@ -633,6 +567,7 @@ namespace SourceGit.ViewModels _histories.Dispose(); _workingCopy.Dispose(); _stashesPage.Dispose(); + _searchCommitContext.Dispose(); _watcher = null; _histories = null; @@ -650,12 +585,6 @@ namespace SourceGit.ViewModels _visibleTags = null; _submodules.Clear(); _visibleSubmodules = null; - _searchedCommits.Clear(); - _selectedSearchedCommit = null; - - _requestingWorktreeFiles = false; - _worktreeFiles = null; - _matchedFilesForSearching = null; } public bool CanCreatePopup() @@ -904,83 +833,6 @@ namespace SourceGit.ViewModels Filter = string.Empty; } - public void ClearSearchCommitFilter() - { - SearchCommitFilter = string.Empty; - } - - public void ClearMatchedFilesForSearching() - { - MatchedFilesForSearching = null; - } - - public void StartSearchCommits() - { - if (_histories == null) - return; - - IsSearchLoadingVisible = true; - SelectedSearchedCommit = null; - MatchedFilesForSearching = null; - - Task.Run(async () => - { - var visible = new List(); - var method = (Models.CommitSearchMethod)_searchCommitFilterType; - - if (method == Models.CommitSearchMethod.BySHA) - { - var isCommitSHA = await new Commands.IsCommitSHA(FullPath, _searchCommitFilter) - .GetResultAsync() - .ConfigureAwait(false); - - if (isCommitSHA) - { - var commit = await new Commands.QuerySingleCommit(FullPath, _searchCommitFilter) - .GetResultAsync() - .ConfigureAwait(false); - - commit.IsMerged = await new Commands.IsAncestor(FullPath, commit.SHA, "HEAD") - .GetResultAsync() - .ConfigureAwait(false); - - visible.Add(commit); - } - } - else if (_onlySearchCommitsInCurrentBranch) - { - visible = await new Commands.QueryCommits(FullPath, _searchCommitFilter, method, true) - .GetResultAsync() - .ConfigureAwait(false); - - foreach (var c in visible) - c.IsMerged = true; - } - else - { - visible = await new Commands.QueryCommits(FullPath, _searchCommitFilter, method, false) - .GetResultAsync() - .ConfigureAwait(false); - - if (visible.Count > 0) - { - var set = await new Commands.QueryCurrentBranchCommitHashes(FullPath, visible[^1].CommitterTime) - .GetResultAsync() - .ConfigureAwait(false); - - foreach (var c in visible) - c.IsMerged = set.Contains(c.SHA); - } - } - - Dispatcher.UIThread.Post(() => - { - SearchedCommits = visible; - IsSearchLoadingVisible = false; - }); - }); - } - public IDisposable LockWatcher() { return _watcher?.Lock(); @@ -1559,7 +1411,7 @@ namespace SourceGit.ViewModels { if (_histories != null) { - SelectedSearchedCommit = null; + _searchCommitContext.Selected = null; var target = await new Commands.QuerySingleCommit(FullPath, branch.Head).GetResultAsync(); _histories.AutoSelectedCommit = null; @@ -1956,65 +1808,6 @@ namespace SourceGit.ViewModels return null; } - private bool IsSearchingCommitsByFilePath() - { - return _isSearching && _searchCommitFilterType == (int)Models.CommitSearchMethod.ByPath; - } - - private void CalcWorktreeFilesForSearching() - { - if (!IsSearchingCommitsByFilePath()) - { - _requestingWorktreeFiles = false; - _worktreeFiles = null; - MatchedFilesForSearching = null; - GC.Collect(); - return; - } - - if (_requestingWorktreeFiles) - return; - - _requestingWorktreeFiles = true; - - Task.Run(async () => - { - _worktreeFiles = await new Commands.QueryRevisionFileNames(FullPath, "HEAD") - .GetResultAsync() - .ConfigureAwait(false); - - Dispatcher.UIThread.Post(() => - { - if (IsSearchingCommitsByFilePath() && _requestingWorktreeFiles) - CalcMatchedFilesForSearching(); - - _requestingWorktreeFiles = false; - }); - }); - } - - private void CalcMatchedFilesForSearching() - { - if (_worktreeFiles == null || _worktreeFiles.Count == 0 || _searchCommitFilter.Length < 3) - { - MatchedFilesForSearching = null; - return; - } - - var matched = new List(); - foreach (var file in _worktreeFiles) - { - if (file.Contains(_searchCommitFilter, StringComparison.OrdinalIgnoreCase) && file.Length != _searchCommitFilter.Length) - { - matched.Add(file); - if (matched.Count > 100) - break; - } - } - - MatchedFilesForSearching = matched; - } - private void FetchInBackground(object sender) { Dispatcher.UIThread.Invoke(async Task () => @@ -2081,15 +1874,7 @@ namespace SourceGit.ViewModels private int _stashesCount = 0; private bool _isSearching = false; - private bool _isSearchLoadingVisible = false; - private int _searchCommitFilterType = (int)Models.CommitSearchMethod.ByMessage; - private bool _onlySearchCommitsInCurrentBranch = false; - private string _searchCommitFilter = string.Empty; - private List _searchedCommits = []; - private Models.Commit _selectedSearchedCommit = null; - private bool _requestingWorktreeFiles = false; - private List _worktreeFiles = null; - private List _matchedFilesForSearching = null; + private SearchCommitContext _searchCommitContext = null; private string _filter = string.Empty; private List _remotes = []; diff --git a/src/ViewModels/SearchCommitContext.cs b/src/ViewModels/SearchCommitContext.cs new file mode 100644 index 00000000..5177a1b4 --- /dev/null +++ b/src/ViewModels/SearchCommitContext.cs @@ -0,0 +1,239 @@ +using System; +using System.Collections.Generic; +using System.Threading.Tasks; +using Avalonia.Threading; +using CommunityToolkit.Mvvm.ComponentModel; + +namespace SourceGit.ViewModels +{ + public class SearchCommitContext : ObservableObject, IDisposable + { + public int Method + { + get => _method; + set + { + if (SetProperty(ref _method, value)) + { + UpdateSuggestions(); + StartSearch(); + } + } + } + + public string Filter + { + get => _filter; + set + { + if (SetProperty(ref _filter, value)) + UpdateSuggestions(); + } + } + + public bool OnlySearchCurrentBranch + { + get => _onlySearchCurrentBranch; + set + { + if (SetProperty(ref _onlySearchCurrentBranch, value)) + StartSearch(); + } + } + + public List Suggestions + { + get => _suggestions; + private set => SetProperty(ref _suggestions, value); + } + + public bool IsQuerying + { + get => _isQuerying; + private set => SetProperty(ref _isQuerying, value); + } + + public List Results + { + get => _results; + private set => SetProperty(ref _results, value); + } + + public Models.Commit Selected + { + get => _selected; + set + { + if (SetProperty(ref _selected, value) && value != null) + _repo.NavigateToCommit(value.SHA); + } + } + + public SearchCommitContext(Repository repo) + { + _repo = repo; + } + + public void Dispose() + { + _repo = null; + _suggestions?.Clear(); + _results?.Clear(); + _worktreeFiles?.Clear(); + } + + public void ClearFilter() + { + Filter = string.Empty; + } + + public void ClearSuggestions() + { + Suggestions = null; + } + + public void StartSearch() + { + Results = null; + Selected = null; + Suggestions = null; + + if (string.IsNullOrEmpty(_filter)) + return; + + IsQuerying = true; + + Task.Run(async () => + { + var result = new List(); + var method = (Models.CommitSearchMethod)_method; + var repoPath = _repo.FullPath; + + if (method == Models.CommitSearchMethod.BySHA) + { + var isCommitSHA = await new Commands.IsCommitSHA(repoPath, _filter) + .GetResultAsync() + .ConfigureAwait(false); + + if (isCommitSHA) + { + var commit = await new Commands.QuerySingleCommit(repoPath, _filter) + .GetResultAsync() + .ConfigureAwait(false); + + commit.IsMerged = await new Commands.IsAncestor(repoPath, commit.SHA, "HEAD") + .GetResultAsync() + .ConfigureAwait(false); + + result.Add(commit); + } + } + else if (_onlySearchCurrentBranch) + { + result = await new Commands.QueryCommits(repoPath, _filter, method, true) + .GetResultAsync() + .ConfigureAwait(false); + + foreach (var c in result) + c.IsMerged = true; + } + else + { + result = await new Commands.QueryCommits(repoPath, _filter, method, false) + .GetResultAsync() + .ConfigureAwait(false); + + if (result.Count > 0) + { + var set = await new Commands.QueryCurrentBranchCommitHashes(repoPath, result[^1].CommitterTime) + .GetResultAsync() + .ConfigureAwait(false); + + foreach (var c in result) + c.IsMerged = set.Contains(c.SHA); + } + } + + Dispatcher.UIThread.Post(() => + { + IsQuerying = false; + + if (_repo.IsSearching) + Results = result; + }); + }); + } + + public void EndSearch() + { + _worktreeFiles = null; + Suggestions = null; + Results = null; + GC.Collect(); + } + + private void UpdateSuggestions() + { + if (_method != (int)Models.CommitSearchMethod.ByPath || _requestingWorktreeFiles) + { + Suggestions = null; + return; + } + + if (_worktreeFiles == null) + { + _requestingWorktreeFiles = true; + + Task.Run(async () => + { + var files = await new Commands.QueryRevisionFileNames(_repo.FullPath, "HEAD") + .GetResultAsync() + .ConfigureAwait(false); + + Dispatcher.UIThread.Post(() => + { + _requestingWorktreeFiles = false; + + if (_repo.IsSearching) + { + _worktreeFiles = files; + UpdateSuggestions(); + } + }); + }); + + return; + } + + if (_worktreeFiles.Count == 0 || _filter.Length < 3) + { + Suggestions = null; + return; + } + + var matched = new List(); + foreach (var file in _worktreeFiles) + { + if (file.Contains(_filter, StringComparison.OrdinalIgnoreCase) && file.Length != _filter.Length) + { + matched.Add(file); + if (matched.Count > 100) + break; + } + } + + Suggestions = matched; + } + + private Repository _repo = null; + private int _method = (int)Models.CommitSearchMethod.ByMessage; + private string _filter = string.Empty; + private bool _onlySearchCurrentBranch = false; + private List _suggestions = null; + private bool _isQuerying = false; + private List _results = null; + private Models.Commit _selected = null; + private bool _requestingWorktreeFiles = false; + private List _worktreeFiles = null; + } +} diff --git a/src/Views/Repository.axaml b/src/Views/Repository.axaml index 259109d5..e26a41a7 100644 --- a/src/Views/Repository.axaml +++ b/src/Views/Repository.axaml @@ -447,7 +447,7 @@ Background="{DynamicResource Brush.Contents}" CornerRadius="12" Watermark="{DynamicResource Text.Repository.Search}" - Text="{Binding SearchCommitFilter, Mode=TwoWay}" + Text="{Binding SearchCommitContext.Filter, Mode=TwoWay}" VerticalContentAlignment="Center" KeyDown="OnSearchKeyDown"> @@ -461,8 +461,8 @@