refactor: use async methods to commit changes

Signed-off-by: leo <longshuang@msn.cn>
This commit is contained in:
leo
2025-08-14 12:53:16 +08:00
parent 3cce6c50a6
commit ce23e8cb20
12 changed files with 138 additions and 146 deletions

View File

@@ -158,13 +158,12 @@ namespace SourceGit
}
}
public static async Task<bool> AskConfirmAsync(string message, Action onSure)
public static async Task<bool> AskConfirmAsync(string message)
{
if (Current?.ApplicationLifetime is IClassicDesktopStyleApplicationLifetime { MainWindow: { } owner })
{
var confirm = new Views.Confirm();
confirm.Message.Text = message;
confirm.OnSure = onSure;
return await confirm.ShowDialog<bool>(owner);
}

View File

@@ -16,6 +16,14 @@ namespace SourceGit.Models
ByContent,
}
public enum CommitCheckPassed
{
None = 0,
DetachedHead,
Filter,
FileCount,
}
public class Commit
{
// As retrieved by: git mktree </dev/null

View File

@@ -47,7 +47,7 @@ namespace SourceGit.ViewModels
if (refs.Count == 0)
{
var msg = App.Text("Checkout.WarnLostCommits");
var shouldContinue = await App.AskConfirmAsync(msg, null);
var shouldContinue = await App.AskConfirmAsync(msg);
if (!shouldContinue)
{
_repo.SetWatcherEnabled(true);

View File

@@ -52,7 +52,7 @@ namespace SourceGit.ViewModels
if (refs.Count == 0)
{
var msg = App.Text("Checkout.WarnLostCommits");
var shouldContinue = await App.AskConfirmAsync(msg, null);
var shouldContinue = await App.AskConfirmAsync(msg);
if (!shouldContinue)
{
_repo.SetWatcherEnabled(true);

View File

@@ -47,7 +47,7 @@ namespace SourceGit.ViewModels
if (refs.Count == 0)
{
var msg = App.Text("Checkout.WarnLostCommits");
var shouldContinue = await App.AskConfirmAsync(msg, null);
var shouldContinue = await App.AskConfirmAsync(msg);
if (!shouldContinue)
{
_repo.SetWatcherEnabled(true);

View File

@@ -1,4 +1,4 @@
using System;
using System.Threading.Tasks;
namespace SourceGit.ViewModels
{
@@ -16,23 +16,25 @@ namespace SourceGit.ViewModels
private set;
}
public ConfirmEmptyCommit(bool hasLocalChanges, Action<bool> onSure)
public ConfirmEmptyCommit(WorkingCopy wc, bool autoPush, int unstagedCount)
{
HasLocalChanges = hasLocalChanges;
Message = App.Text(hasLocalChanges ? "ConfirmEmptyCommit.WithLocalChanges" : "ConfirmEmptyCommit.NoLocalChanges");
_onSure = onSure;
_wc = wc;
_autoPush = autoPush;
HasLocalChanges = unstagedCount > 0;
Message = App.Text(HasLocalChanges ? "ConfirmEmptyCommit.WithLocalChanges" : "ConfirmEmptyCommit.NoLocalChanges");
}
public void StageAllThenCommit()
public async Task StageAllThenCommitAsync()
{
_onSure?.Invoke(true);
await _wc.CommitAsync(true, _autoPush, Models.CommitCheckPassed.FileCount);
}
public void Continue()
public async Task ContinueAsync()
{
_onSure?.Invoke(false);
await _wc.CommitAsync(false, _autoPush, Models.CommitCheckPassed.FileCount);
}
private Action<bool> _onSure;
private readonly WorkingCopy _wc;
private readonly bool _autoPush;
}
}

View File

@@ -131,7 +131,7 @@ namespace SourceGit.ViewModels
if (refs.Count == 0)
{
var msg = App.Text("Checkout.WarnLostCommits");
var shouldContinue = await App.AskConfirmAsync(msg, null);
var shouldContinue = await App.AskConfirmAsync(msg);
if (!shouldContinue)
{
_repo.SetWatcherEnabled(true);

View File

@@ -606,19 +606,86 @@ namespace SourceGit.ViewModels
CommitMessage = tmpl.Apply(_repo.CurrentBranch, _staged);
}
public void Commit()
public async Task CommitAsync(bool autoStage, bool autoPush, Models.CommitCheckPassed checkPassed = Models.CommitCheckPassed.None)
{
DoCommit(false, false);
}
if (string.IsNullOrWhiteSpace(_commitMessage))
return;
public void CommitWithAutoStage()
{
DoCommit(true, false);
}
if (!_repo.CanCreatePopup())
{
App.RaiseException(_repo.FullPath, "Repository has an unfinished job! Please wait!");
return;
}
public void CommitWithPush()
{
DoCommit(false, true);
if (autoStage && HasUnsolvedConflicts)
{
App.RaiseException(_repo.FullPath, "Repository has unsolved conflict(s). Auto-stage and commit is disabled!");
return;
}
if (_repo.CurrentBranch is { IsDetachedHead: true } && checkPassed < Models.CommitCheckPassed.DetachedHead)
{
var msg = App.Text("WorkingCopy.ConfirmCommitWithDetachedHead");
var sure = await App.AskConfirmAsync(msg);
if (sure)
await CommitAsync(autoStage, autoPush, Models.CommitCheckPassed.DetachedHead);
return;
}
if (!string.IsNullOrEmpty(_filter) && _staged.Count > _visibleStaged.Count && checkPassed < Models.CommitCheckPassed.Filter)
{
var msg = App.Text("WorkingCopy.ConfirmCommitWithFilter", _staged.Count, _visibleStaged.Count, _staged.Count - _visibleStaged.Count);
var sure = await App.AskConfirmAsync(msg);
if (sure)
await CommitAsync(autoStage, autoPush, Models.CommitCheckPassed.Filter);
return;
}
if (checkPassed < Models.CommitCheckPassed.FileCount && !_useAmend)
{
if ((!autoStage && _staged.Count == 0) || (autoStage && _cached.Count == 0))
{
await App.ShowDialog(new ConfirmEmptyCommit(this, autoPush, _cached.Count));
return;
}
}
IsCommitting = true;
_repo.Settings.PushCommitMessage(_commitMessage);
_repo.SetWatcherEnabled(false);
var signOff = _repo.Settings.EnableSignOffForCommit;
var log = _repo.CreateLog("Commit");
var succ = true;
if (autoStage && _unstaged.Count > 0)
succ = await new Commands.Add(_repo.FullPath, _repo.IncludeUntracked).Use(log).ExecAsync().ConfigureAwait(false);
if (succ)
succ = await new Commands.Commit(_repo.FullPath, _commitMessage, signOff, _useAmend, _resetAuthor).Use(log).RunAsync().ConfigureAwait(false);
log.Complete();
if (succ)
{
CommitMessage = string.Empty;
UseAmend = false;
if (autoPush && _repo.Remotes.Count > 0)
{
Models.Branch pushBranch = null;
if (_repo.CurrentBranch == null)
{
var currentBranchName = await new Commands.QueryCurrentBranch(_repo.FullPath).GetResultAsync();
pushBranch = new Models.Branch() { Name = currentBranchName };
}
if (_repo.CanCreatePopup())
_repo.ShowAndStartPopup(new Push(_repo, pushBranch));
}
}
_repo.MarkBranchesDirtyManually();
_repo.SetWatcherEnabled(true);
IsCommitting = false;
}
private List<Models.Change> GetVisibleChanges(List<Models.Change> changes)
@@ -745,102 +812,6 @@ namespace SourceGit.ViewModels
DetailContext = new DiffContext(_repo.FullPath, new Models.DiffOption(change, isUnstaged), _detailContext as DiffContext);
}
private void DoCommit(bool autoStage, bool autoPush, CommitCheckPassed checkPassed = CommitCheckPassed.None)
{
if (string.IsNullOrWhiteSpace(_commitMessage))
return;
if (!_repo.CanCreatePopup())
{
App.RaiseException(_repo.FullPath, "Repository has an unfinished job! Please wait!");
return;
}
if (autoStage && HasUnsolvedConflicts)
{
App.RaiseException(_repo.FullPath, "Repository has unsolved conflict(s). Auto-stage and commit is disabled!");
return;
}
if (_repo.CurrentBranch is { IsDetachedHead: true } && checkPassed < CommitCheckPassed.DetachedHead)
{
var msg = App.Text("WorkingCopy.ConfirmCommitWithDetachedHead");
_ = App.AskConfirmAsync(msg, () => DoCommit(autoStage, autoPush, CommitCheckPassed.DetachedHead));
return;
}
if (!string.IsNullOrEmpty(_filter) && _staged.Count > _visibleStaged.Count && checkPassed < CommitCheckPassed.Filter)
{
var msg = App.Text("WorkingCopy.ConfirmCommitWithFilter", _staged.Count, _visibleStaged.Count, _staged.Count - _visibleStaged.Count);
_ = App.AskConfirmAsync(msg, () => DoCommit(autoStage, autoPush, CommitCheckPassed.Filter));
return;
}
if (checkPassed < CommitCheckPassed.FileCount && !_useAmend)
{
if ((!autoStage && _staged.Count == 0) || (autoStage && _cached.Count == 0))
{
_ = App.ShowDialog(new ConfirmEmptyCommit(_cached.Count > 0, stageAll => DoCommit(stageAll, autoPush, CommitCheckPassed.FileCount)));
return;
}
}
IsCommitting = true;
_repo.Settings.PushCommitMessage(_commitMessage);
_repo.SetWatcherEnabled(false);
var signOff = _repo.Settings.EnableSignOffForCommit;
var log = _repo.CreateLog("Commit");
Task.Run(async () =>
{
var succ = true;
if (autoStage && _unstaged.Count > 0)
succ = await new Commands.Add(_repo.FullPath, _repo.IncludeUntracked).Use(log).ExecAsync().ConfigureAwait(false);
if (succ)
succ = await new Commands.Commit(_repo.FullPath, _commitMessage, signOff, _useAmend, _resetAuthor).Use(log).RunAsync().ConfigureAwait(false);
log.Complete();
Dispatcher.UIThread.Post(() =>
{
if (succ)
{
CommitMessage = string.Empty;
UseAmend = false;
if (autoPush && _repo.Remotes.Count > 0)
PushAfterCommit();
}
_repo.MarkBranchesDirtyManually();
_repo.SetWatcherEnabled(true);
IsCommitting = false;
});
});
}
private void PushAfterCommit()
{
if (_repo.CurrentBranch == null)
{
Task.Run(async () =>
{
var currentBranchName = await new Commands.QueryCurrentBranch(_repo.FullPath).GetResultAsync();
var tmp = new Models.Branch() { Name = currentBranchName };
Dispatcher.UIThread.Post(() =>
{
if (_repo.CanCreatePopup())
_repo.ShowAndStartPopup(new Push(_repo, tmp));
});
});
}
else if (_repo.CanCreatePopup())
{
_repo.ShowAndStartPopup(new Push(_repo, null));
}
}
private bool IsChanged(List<Models.Change> old, List<Models.Change> cur)
{
if (old.Count != cur.Count)
@@ -857,14 +828,6 @@ namespace SourceGit.ViewModels
return false;
}
private enum CommitCheckPassed
{
None = 0,
DetachedHead,
Filter,
FileCount,
}
private Repository _repo = null;
private bool _isLoadingData = false;
private bool _isStaging = false;

View File

@@ -1,16 +1,9 @@
using System;
using Avalonia.Interactivity;
namespace SourceGit.Views
{
public partial class Confirm : ChromelessWindow
{
public Action OnSure
{
get;
set;
}
public Confirm()
{
InitializeComponent();
@@ -18,7 +11,6 @@ namespace SourceGit.Views
private void Sure(object _1, RoutedEventArgs _2)
{
OnSure?.Invoke();
Close(true);
}

View File

@@ -9,15 +9,19 @@ namespace SourceGit.Views
InitializeComponent();
}
private void StageAllThenCommit(object _1, RoutedEventArgs _2)
private async void StageAllThenCommit(object _1, RoutedEventArgs _2)
{
(DataContext as ViewModels.ConfirmEmptyCommit)?.StageAllThenCommit();
if (DataContext is ViewModels.ConfirmEmptyCommit vm)
await vm.StageAllThenCommitAsync();
Close();
}
private void Continue(object _1, RoutedEventArgs _2)
private async void Continue(object _1, RoutedEventArgs _2)
{
(DataContext as ViewModels.ConfirmEmptyCommit)?.Continue();
if (DataContext is ViewModels.ConfirmEmptyCommit vm)
await vm.ContinueAsync();
Close();
}

View File

@@ -295,7 +295,7 @@
<SplitButton.Flyout>
<MenuFlyout>
<MenuItem Header="{DynamicResource Text.WorkingCopy.CommitToEdit}"
Command="{Binding Commit}"
Click="OnCommit"
IsEnabled="{Binding CommitMessage, Converter={x:Static c:StringConverters.IsNotNullOrWhitespace}}"/>
</MenuFlyout>
</SplitButton.Flyout>
@@ -307,7 +307,7 @@
Height="28"
Margin="8,0,0,0"
Padding="8,0"
Command="{Binding Commit}"
Click="OnCommit"
HotKey="{OnPlatform Ctrl+Enter, macOS=⌘+Enter}"
IsVisible="{Binding InProgressContext, Converter={x:Static ObjectConverters.IsNull}}"
ToolTip.Placement="Top"
@@ -337,7 +337,7 @@
<Button Grid.Column="5"
Width="0" Height="0"
Background="Transparent"
Command="{Binding CommitWithAutoStage}"
Click="OnCommitWithAutoStage"
HotKey="{OnPlatform Ctrl+Shift+Enter, macOS=⌘+Shift+Enter}">
<Button.IsEnabled>
<MultiBinding Converter="{x:Static BoolConverters.And}">
@@ -353,7 +353,7 @@
Height="28"
Margin="8,0,0,0"
Padding="8,0"
Command="{Binding CommitWithPush}"
Click="OnCommitWithPush"
HotKey="{OnPlatform Ctrl+Alt+Enter, macOS=⌘+Alt+Enter}"
ToolTip.Tip="{OnPlatform Ctrl+Alt+Enter, macOS=⌘+⌥+Enter}"
ToolTip.Placement="Top"

View File

@@ -193,6 +193,30 @@ namespace SourceGit.Views
e.Handled = true;
}
private async void OnCommit(object _, RoutedEventArgs e)
{
if (DataContext is ViewModels.WorkingCopy vm)
await vm.CommitAsync(false, false);
e.Handled = true;
}
private async void OnCommitWithAutoStage(object _, RoutedEventArgs e)
{
if (DataContext is ViewModels.WorkingCopy vm)
await vm.CommitAsync(true, false);
e.Handled = true;
}
private async void OnCommitWithPush(object _, RoutedEventArgs e)
{
if (DataContext is ViewModels.WorkingCopy vm)
await vm.CommitAsync(false, true);
e.Handled = true;
}
private ContextMenu CreateContextMenuForUnstagedChanges(ViewModels.WorkingCopy vm, string selectedSingleFolder)
{
var repo = vm.Repository;