mirror of
https://fastgit.cc/github.com/sourcegit-scm/sourcegit
synced 2026-04-21 05:10:25 +08:00
refactor: move WorkingCopy.SetData to UIThread to avoid data-race condition (#2203)
Signed-off-by: leo <longshuang@msn.cn>
This commit is contained in:
@@ -1326,29 +1326,15 @@ namespace SourceGit.ViewModels
|
||||
.GetResultAsync()
|
||||
.ConfigureAwait(false);
|
||||
|
||||
if (_workingCopy == null || token.IsCancellationRequested)
|
||||
return;
|
||||
|
||||
changes.Sort((l, r) => Models.NumericSort.Compare(l.Path, r.Path));
|
||||
_workingCopy.SetData(changes, token);
|
||||
|
||||
var hasModified = false;
|
||||
foreach (var c in changes)
|
||||
{
|
||||
if (c.Index == Models.ChangeState.Added || c.WorkTree == Models.ChangeState.Untracked)
|
||||
continue;
|
||||
|
||||
hasModified = true;
|
||||
break;
|
||||
}
|
||||
|
||||
Dispatcher.UIThread.Invoke(() =>
|
||||
{
|
||||
if (token.IsCancellationRequested)
|
||||
return;
|
||||
|
||||
_workingCopy.SetData(changes);
|
||||
LocalChangesCount = changes.Count;
|
||||
_canCheckoutDirectly = !hasModified;
|
||||
OnPropertyChanged(nameof(InProgressContext));
|
||||
GetOwnerPage()?.ChangeDirtyState(Models.DirtyState.HasLocalChanges, changes.Count == 0);
|
||||
});
|
||||
@@ -1422,7 +1408,7 @@ namespace SourceGit.ViewModels
|
||||
|
||||
if (branch.IsLocal)
|
||||
{
|
||||
if (_canCheckoutDirectly)
|
||||
if (_workingCopy is { CanSwitchBranchDirectly: true })
|
||||
await ShowAndStartPopupAsync(new Checkout(this, branch));
|
||||
else
|
||||
ShowPopup(new Checkout(this, branch));
|
||||
@@ -1979,7 +1965,6 @@ namespace SourceGit.ViewModels
|
||||
private List<Models.Submodule> _submodules = [];
|
||||
private object _visibleSubmodules = null;
|
||||
private string _navigateToCommitDelayed = string.Empty;
|
||||
private bool _canCheckoutDirectly = false;
|
||||
|
||||
private bool _isAutoFetching = false;
|
||||
private Timer _autoFetchTimer = null;
|
||||
|
||||
@@ -1,10 +1,7 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
using Avalonia.Threading;
|
||||
using CommunityToolkit.Mvvm.ComponentModel;
|
||||
|
||||
namespace SourceGit.ViewModels
|
||||
@@ -41,6 +38,12 @@ namespace SourceGit.ViewModels
|
||||
set => SetProperty(ref _hasUnsolvedConflicts, value);
|
||||
}
|
||||
|
||||
public bool CanSwitchBranchDirectly
|
||||
{
|
||||
get;
|
||||
set;
|
||||
} = false;
|
||||
|
||||
public InProgressContext InProgressContext
|
||||
{
|
||||
get => _inProgressContext;
|
||||
@@ -256,64 +259,67 @@ namespace SourceGit.ViewModels
|
||||
_commitMessage = string.Empty;
|
||||
}
|
||||
|
||||
public void SetData(List<Models.Change> changes, CancellationToken cancellationToken)
|
||||
public void SetData(List<Models.Change> changes)
|
||||
{
|
||||
if (!IsChanged(_cached, changes))
|
||||
{
|
||||
// Just force refresh selected changes.
|
||||
Dispatcher.UIThread.Invoke(() =>
|
||||
{
|
||||
if (cancellationToken.IsCancellationRequested)
|
||||
return;
|
||||
|
||||
HasUnsolvedConflicts = _cached.Find(x => x.IsConflicted) != null;
|
||||
UpdateInProgressState();
|
||||
UpdateDetail();
|
||||
});
|
||||
|
||||
HasUnsolvedConflicts = _cached.Find(x => x.IsConflicted) != null;
|
||||
UpdateInProgressState();
|
||||
UpdateDetail();
|
||||
return;
|
||||
}
|
||||
|
||||
var lastSelectedUnstaged = new HashSet<string>();
|
||||
var lastSelectedStaged = new HashSet<string>();
|
||||
if (_selectedUnstaged is { Count: > 0 })
|
||||
{
|
||||
foreach (var c in _selectedUnstaged)
|
||||
lastSelectedUnstaged.Add(c.Path);
|
||||
}
|
||||
else if (_selectedStaged is { Count: > 0 })
|
||||
{
|
||||
foreach (var c in _selectedStaged)
|
||||
lastSelectedStaged.Add(c.Path);
|
||||
}
|
||||
|
||||
var unstaged = new List<Models.Change>();
|
||||
var visibleUnstaged = new List<Models.Change>();
|
||||
var selectedUnstaged = new List<Models.Change>();
|
||||
var noFilter = string.IsNullOrEmpty(_filter);
|
||||
var hasConflict = false;
|
||||
var canSwitchDirectly = true;
|
||||
foreach (var c in changes)
|
||||
{
|
||||
if (c.WorkTree != Models.ChangeState.None)
|
||||
{
|
||||
unstaged.Add(c);
|
||||
hasConflict |= c.IsConflicted;
|
||||
}
|
||||
}
|
||||
|
||||
var visibleUnstaged = GetVisibleChanges(unstaged);
|
||||
var selectedUnstaged = new List<Models.Change>();
|
||||
foreach (var c in visibleUnstaged)
|
||||
{
|
||||
if (lastSelectedUnstaged.Contains(c.Path))
|
||||
selectedUnstaged.Add(c);
|
||||
if (noFilter || c.Path.Contains(_filter, StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
visibleUnstaged.Add(c);
|
||||
if (lastSelectedUnstaged.Contains(c.Path))
|
||||
selectedUnstaged.Add(c);
|
||||
}
|
||||
}
|
||||
|
||||
if (!canSwitchDirectly)
|
||||
continue;
|
||||
|
||||
if (c.WorkTree == Models.ChangeState.Untracked || c.Index == Models.ChangeState.Added)
|
||||
continue;
|
||||
|
||||
canSwitchDirectly = false;
|
||||
}
|
||||
|
||||
var staged = GetStagedChanges(changes);
|
||||
|
||||
var visibleStaged = GetVisibleChanges(staged);
|
||||
var selectedStaged = new List<Models.Change>();
|
||||
foreach (var c in visibleStaged)
|
||||
if (_selectedStaged is { Count: > 0 })
|
||||
{
|
||||
if (lastSelectedStaged.Contains(c.Path))
|
||||
selectedStaged.Add(c);
|
||||
var set = new HashSet<string>();
|
||||
foreach (var c in _selectedStaged)
|
||||
set.Add(c.Path);
|
||||
|
||||
foreach (var c in visibleStaged)
|
||||
{
|
||||
if (set.Contains(c.Path))
|
||||
selectedStaged.Add(c);
|
||||
}
|
||||
}
|
||||
|
||||
if (selectedUnstaged.Count == 0 && selectedStaged.Count == 0 && hasConflict)
|
||||
@@ -322,25 +328,20 @@ namespace SourceGit.ViewModels
|
||||
selectedUnstaged.Add(firstConflict);
|
||||
}
|
||||
|
||||
Dispatcher.UIThread.Invoke(() =>
|
||||
{
|
||||
if (cancellationToken.IsCancellationRequested)
|
||||
return;
|
||||
_isLoadingData = true;
|
||||
_cached = changes;
|
||||
HasUnsolvedConflicts = hasConflict;
|
||||
CanSwitchBranchDirectly = canSwitchDirectly;
|
||||
VisibleUnstaged = visibleUnstaged;
|
||||
VisibleStaged = visibleStaged;
|
||||
Unstaged = unstaged;
|
||||
Staged = staged;
|
||||
SelectedUnstaged = selectedUnstaged;
|
||||
SelectedStaged = selectedStaged;
|
||||
_isLoadingData = false;
|
||||
|
||||
_isLoadingData = true;
|
||||
_cached = changes;
|
||||
HasUnsolvedConflicts = hasConflict;
|
||||
VisibleUnstaged = visibleUnstaged;
|
||||
VisibleStaged = visibleStaged;
|
||||
Unstaged = unstaged;
|
||||
Staged = staged;
|
||||
SelectedUnstaged = selectedUnstaged;
|
||||
SelectedStaged = selectedStaged;
|
||||
_isLoadingData = false;
|
||||
|
||||
UpdateInProgressState();
|
||||
UpdateDetail();
|
||||
});
|
||||
UpdateInProgressState();
|
||||
UpdateDetail();
|
||||
}
|
||||
|
||||
public async Task StageChangesAsync(List<Models.Change> changes, Models.Change next)
|
||||
@@ -671,8 +672,11 @@ namespace SourceGit.ViewModels
|
||||
|
||||
if (succ)
|
||||
{
|
||||
// Do not use property `UseAmend` but manually trigger property changed to avoid refreshing staged changes here.
|
||||
_useAmend = false;
|
||||
OnPropertyChanged(nameof(UseAmend));
|
||||
|
||||
CommitMessage = string.Empty;
|
||||
UseAmend = false;
|
||||
if (autoPush && _repo.Remotes.Count > 0)
|
||||
{
|
||||
Models.Branch pushBranch = null;
|
||||
|
||||
Reference in New Issue
Block a user