code_review: PR #1153

- use a single filter for both unstage and staged files
- show confirm dialog if staged files are displayed partially

Signed-off-by: leo <longshuang@msn.cn>
This commit is contained in:
leo 2025-04-07 11:45:14 +08:00
parent a37c6b29ec
commit fa02c65da5
No known key found for this signature in database
9 changed files with 103 additions and 143 deletions

View file

@ -719,6 +719,7 @@
<x:String x:Key="Text.WorkingCopy.CommitTip" xml:space="preserve">Trigger click event</x:String>
<x:String x:Key="Text.WorkingCopy.CommitToEdit" xml:space="preserve">Commit (Edit)</x:String>
<x:String x:Key="Text.WorkingCopy.CommitWithAutoStage" xml:space="preserve">Stage all changes and commit</x:String>
<x:String x:Key="Text.WorkingCopy.ConfirmCommitWithFilter">You have staged {0} file(s) but only {1} file(s) displayed ({2} files are filtered out). Do you want to continue?</x:String>
<x:String x:Key="Text.WorkingCopy.ConfirmCommitWithoutFiles" xml:space="preserve">Empty commit detected! Do you want to continue (--allow-empty)?</x:String>
<x:String x:Key="Text.WorkingCopy.Conflicts" xml:space="preserve">CONFLICTS DETECTED</x:String>
<x:String x:Key="Text.WorkingCopy.Conflicts.Resolved" xml:space="preserve">FILE CONFLICTS ARE RESOLVED</x:String>

View file

@ -723,6 +723,7 @@
<x:String x:Key="Text.WorkingCopy.CommitTip" xml:space="preserve">触发点击事件</x:String>
<x:String x:Key="Text.WorkingCopy.CommitToEdit" xml:space="preserve">提交(修改原始提交)</x:String>
<x:String x:Key="Text.WorkingCopy.CommitWithAutoStage" xml:space="preserve">自动暂存所有变更并提交</x:String>
<x:String x:Key="Text.WorkingCopy.ConfirmCommitWithFilter">当前有 {0} 个文件在暂存区中,但仅显示了 {1} 个文件({2} 个文件被过滤掉了),是否继续提交?</x:String>
<x:String x:Key="Text.WorkingCopy.ConfirmCommitWithoutFiles" xml:space="preserve">提交未包含变更文件!是否继续(--allow-empty)</x:String>
<x:String x:Key="Text.WorkingCopy.Conflicts" xml:space="preserve">检测到冲突</x:String>
<x:String x:Key="Text.WorkingCopy.Conflicts.Resolved" xml:space="preserve">文件冲突已解决</x:String>

View file

@ -722,6 +722,7 @@
<x:String x:Key="Text.WorkingCopy.CommitTip" xml:space="preserve">觸發點擊事件</x:String>
<x:String x:Key="Text.WorkingCopy.CommitToEdit" xml:space="preserve">提交 (修改原始提交)</x:String>
<x:String x:Key="Text.WorkingCopy.CommitWithAutoStage" xml:space="preserve">自動暫存全部變更並提交</x:String>
<x:String x:Key="Text.WorkingCopy.ConfirmCommitWithFilter">您已暫存 {0} 檔案,但只顯示 {1} 檔案 ({2} 檔案被篩選器隱藏)。您要繼續嗎?</x:String>
<x:String x:Key="Text.WorkingCopy.ConfirmCommitWithoutFiles" xml:space="preserve">未包含任何檔案變更! 您是否仍要提交 (--allow-empty)?</x:String>
<x:String x:Key="Text.WorkingCopy.Conflicts" xml:space="preserve">檢測到衝突</x:String>
<x:String x:Key="Text.WorkingCopy.Conflicts.Resolved" xml:space="preserve">檔案衝突已解決</x:String>

View file

@ -0,0 +1,26 @@
using System;
namespace SourceGit.ViewModels
{
public class ConfirmCommit
{
public string Message
{
get;
private set;
}
public ConfirmCommit(string message, Action onSure)
{
Message = message;
_onSure = onSure;
}
public void Continue()
{
_onSure?.Invoke();
}
private Action _onSure;
}
}

View file

@ -1,19 +0,0 @@
namespace SourceGit.ViewModels
{
public class ConfirmCommitWithoutFiles
{
public ConfirmCommitWithoutFiles(WorkingCopy wc, bool autoPush)
{
_wc = wc;
_autoPush = autoPush;
}
public void Continue()
{
_wc.CommitWithoutFiles(_autoPush);
}
private readonly WorkingCopy _wc;
private bool _autoPush;
}
}

View file

@ -99,38 +99,23 @@ namespace SourceGit.ViewModels
}
}
public string UnstagedFilter
public string Filter
{
get => _unstagedFilter;
get => _filter;
set
{
if (SetProperty(ref _unstagedFilter, value))
if (SetProperty(ref _filter, value))
{
if (_isLoadingData)
return;
VisibleUnstaged = GetVisibleChanges(_unstaged, _unstagedFilter);
VisibleUnstaged = GetVisibleChanges(_unstaged);
VisibleStaged = GetVisibleChanges(_staged);
SelectedUnstaged = [];
}
}
}
public string StagedFilter
{
get => _stagedFilter;
set
{
if (SetProperty(ref _stagedFilter, value))
{
if (_isLoadingData)
return;
VisibleStaged = GetVisibleChanges(_staged, _stagedFilter);
SelectedStaged = [];
}
}
}
public List<Models.Change> Unstaged
{
get => _unstaged;
@ -294,7 +279,7 @@ namespace SourceGit.ViewModels
}
}
var visibleUnstaged = GetVisibleChanges(unstaged, _unstagedFilter);
var visibleUnstaged = GetVisibleChanges(unstaged);
var selectedUnstaged = new List<Models.Change>();
foreach (var c in visibleUnstaged)
{
@ -304,7 +289,7 @@ namespace SourceGit.ViewModels
var staged = GetStagedChanges();
var visibleStaged = GetVisibleChanges(staged, _stagedFilter);
var visibleStaged = GetVisibleChanges(staged);
var selectedStaged = new List<Models.Change>();
foreach (var c in visibleStaged)
{
@ -374,14 +359,9 @@ namespace SourceGit.ViewModels
_repo.ShowPopup(new Discard(_repo, changes));
}
public void ClearUnstagedFilter()
public void ClearFilter()
{
UnstagedFilter = string.Empty;
}
public void ClearStagedFilter()
{
StagedFilter = string.Empty;
Filter = string.Empty;
}
public async void UseTheirs(List<Models.Change> changes)
@ -571,11 +551,6 @@ namespace SourceGit.ViewModels
DoCommit(false, true, false);
}
public void CommitWithoutFiles(bool autoPush)
{
DoCommit(false, autoPush, true);
}
public ContextMenu CreateContextMenuForUnstagedChanges()
{
if (_selectedUnstaged == null || _selectedUnstaged.Count == 0)
@ -1505,16 +1480,16 @@ namespace SourceGit.ViewModels
return menu;
}
private List<Models.Change> GetVisibleChanges(List<Models.Change> changes, string filter)
private List<Models.Change> GetVisibleChanges(List<Models.Change> changes)
{
if (string.IsNullOrEmpty(filter))
if (string.IsNullOrEmpty(_filter))
return changes;
var visible = new List<Models.Change>();
foreach (var c in changes)
{
if (c.Path.Contains(filter, StringComparison.OrdinalIgnoreCase))
if (c.Path.Contains(_filter, StringComparison.OrdinalIgnoreCase))
visible.Add(c);
}
@ -1675,7 +1650,7 @@ namespace SourceGit.ViewModels
DetailContext = new DiffContext(_repo.FullPath, new Models.DiffOption(change, isUnstaged), _detailContext as DiffContext);
}
private void DoCommit(bool autoStage, bool autoPush, bool allowEmpty)
private void DoCommit(bool autoStage, bool autoPush, bool allowEmpty = false, bool confirmWithFilter = false)
{
if (!_repo.CanCreatePopup())
{
@ -1683,10 +1658,17 @@ namespace SourceGit.ViewModels
return;
}
if (!string.IsNullOrEmpty(_stagedFilter))
if (!string.IsNullOrEmpty(_filter) && _staged.Count > _visibleStaged.Count && !confirmWithFilter)
{
// FIXME - make this a proper warning message-box "Staged-area filter will not be applied to commit. Continue?" Yes/No
App.RaiseException(_repo.FullPath, "Committing with staged-area filter applied is NOT allowed!");
var confirmMessage = App.Text("WorkingCopy.ConfirmCommitWithFilter", _staged.Count, _visibleStaged.Count, _staged.Count - _visibleStaged.Count);
App.OpenDialog(new Views.ConfirmCommit()
{
DataContext = new ConfirmCommit(confirmMessage, () =>
{
DoCommit(autoStage, autoPush, allowEmpty, true);
})
});
return;
}
@ -1700,9 +1682,13 @@ namespace SourceGit.ViewModels
{
if ((autoStage && _count == 0) || (!autoStage && _staged.Count == 0))
{
App.OpenDialog(new Views.ConfirmCommitWithoutFiles()
var confirmMessage = App.Text("WorkingCopy.ConfirmCommitWithoutFiles");
App.OpenDialog(new Views.ConfirmCommit()
{
DataContext = new ConfirmCommitWithoutFiles(this, autoPush)
DataContext = new ConfirmCommit(confirmMessage, () =>
{
DoCommit(autoStage, autoPush, true, confirmWithFilter);
})
});
return;
@ -1774,8 +1760,7 @@ namespace SourceGit.ViewModels
private List<Models.Change> _selectedStaged = [];
private int _count = 0;
private object _detailContext = null;
private string _unstagedFilter = string.Empty;
private string _stagedFilter = string.Empty;
private string _filter = string.Empty;
private string _commitMessage = string.Empty;
private bool _hasUnsolvedConflicts = false;

View file

@ -5,8 +5,8 @@
xmlns:v="using:SourceGit.Views"
xmlns:vm="using:SourceGit.ViewModels"
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
x:Class="SourceGit.Views.ConfirmCommitWithoutFiles"
x:DataType="vm:ConfirmCommitWithoutFiles"
x:Class="SourceGit.Views.ConfirmCommit"
x:DataType="vm:ConfirmCommit"
x:Name="ThisControl"
Icon="/App.ico"
Title="{DynamicResource Text.Warn}"
@ -38,7 +38,7 @@
<!-- Body -->
<Border Grid.Row="1" Margin="16">
<TextBlock Text="{DynamicResource Text.WorkingCopy.ConfirmCommitWithoutFiles}"/>
<TextBlock Text="{Binding Message}" MaxWidth="400" TextWrapping="Wrap"/>
</Border>
<!-- Buttons -->

View file

@ -2,20 +2,16 @@ using Avalonia.Interactivity;
namespace SourceGit.Views
{
public partial class ConfirmCommitWithoutFiles : ChromelessWindow
public partial class ConfirmCommit : ChromelessWindow
{
public ConfirmCommitWithoutFiles()
public ConfirmCommit()
{
InitializeComponent();
}
private void Sure(object _1, RoutedEventArgs _2)
{
if (DataContext is ViewModels.ConfirmCommitWithoutFiles vm)
{
vm.Continue();
}
(DataContext as ViewModels.ConfirmCommit)?.Continue();
Close();
}

View file

@ -19,13 +19,46 @@
<!-- Left -->
<Grid Grid.Column="0">
<Grid.RowDefinitions>
<RowDefinition Height="36"/>
<RowDefinition Height="*" MinHeight="100"/>
<RowDefinition Height="1"/>
<RowDefinition Height="*" MinHeight="100"/>
</Grid.RowDefinitions>
<!-- Search Filter -->
<Border Grid.Row="0" BorderThickness="0,0,0,1" BorderBrush="{DynamicResource Brush.Border0}">
<TextBox Height="24"
Margin="4,0"
BorderThickness="1"
CornerRadius="12"
Text="{Binding Filter, Mode=TwoWay}"
BorderBrush="{DynamicResource Brush.Border2}"
VerticalContentAlignment="Center">
<TextBox.InnerLeftContent>
<Path Width="14" Height="14"
Margin="6,0,0,0"
Fill="{DynamicResource Brush.FG2}"
Data="{StaticResource Icons.Search}"/>
</TextBox.InnerLeftContent>
<TextBox.InnerRightContent>
<Button Classes="icon_button"
Width="16"
Margin="0,0,6,0"
Command="{Binding ClearFilter}"
IsVisible="{Binding Filter, Converter={x:Static StringConverters.IsNotNullOrEmpty}}"
HorizontalAlignment="Right">
<Path Width="14" Height="14"
Margin="0,1,0,0"
Fill="{DynamicResource Brush.FG1}"
Data="{StaticResource Icons.Clear}"/>
</Button>
</TextBox.InnerRightContent>
</TextBox>
</Border>
<!-- Unstaged -->
<Grid Grid.Row="0" RowDefinitions="28,36,*">
<Grid Grid.Row="1" RowDefinitions="28,*">
<!-- Unstaged Toolbar -->
<Border Grid.Row="0" BorderThickness="0,0,0,1" BorderBrush="{DynamicResource Brush.Border0}">
<Grid ColumnDefinitions="Auto,Auto,Auto,Auto,*,Auto,Auto,Auto,Auto,Auto">
@ -75,40 +108,8 @@
</Grid>
</Border>
<!-- Unstaged Filter -->
<Border Grid.Row="1" BorderThickness="0,0,0,1" BorderBrush="{DynamicResource Brush.Border0}">
<TextBox Height="24"
Margin="4,0"
BorderThickness="1"
CornerRadius="12"
Text="{Binding UnstagedFilter, Mode=TwoWay}"
BorderBrush="{DynamicResource Brush.Border2}"
VerticalContentAlignment="Center">
<TextBox.InnerLeftContent>
<Path Width="14" Height="14"
Margin="6,0,0,0"
Fill="{DynamicResource Brush.FG2}"
Data="{StaticResource Icons.Search}"/>
</TextBox.InnerLeftContent>
<TextBox.InnerRightContent>
<Button Classes="icon_button"
Width="16"
Margin="0,0,6,0"
Command="{Binding ClearUnstagedFilter}"
IsVisible="{Binding UnstagedFilter, Converter={x:Static StringConverters.IsNotNullOrEmpty}}"
HorizontalAlignment="Right">
<Path Width="14" Height="14"
Margin="0,1,0,0"
Fill="{DynamicResource Brush.FG1}"
Data="{StaticResource Icons.Clear}"/>
</Button>
</TextBox.InnerRightContent>
</TextBox>
</Border>
<!-- Unstaged Changes -->
<v:ChangeCollectionView Grid.Row="2"
<v:ChangeCollectionView Grid.Row="1"
x:Name="UnstagedChangesView"
Focusable="True"
IsUnstagedChange="True"
@ -123,13 +124,13 @@
</Grid>
<!-- Splitter -->
<GridSplitter Grid.Row="1"
<GridSplitter Grid.Row="2"
MinHeight="1"
HorizontalAlignment="Stretch" VerticalAlignment="Stretch"
Background="{DynamicResource Brush.Border0}"/>
<!-- Staged -->
<Grid Grid.Row="2" RowDefinitions="28,36,*">
<Grid Grid.Row="3" RowDefinitions="28,*">
<!-- Staged Toolbar -->
<Border Grid.Row="0" BorderThickness="0,0,0,1" BorderBrush="{DynamicResource Brush.Border0}">
<Grid ColumnDefinitions="Auto,Auto,Auto,Auto,*,Auto,Auto,Auto">
@ -155,41 +156,9 @@
ViewMode="{Binding Source={x:Static vm:Preferences.Instance}, Path=StagedChangeViewMode, Mode=TwoWay}"/>
</Grid>
</Border>
<!-- Staged Filter -->
<Border Grid.Row="1" BorderThickness="0,0,0,1" BorderBrush="{DynamicResource Brush.Border0}">
<TextBox Height="24"
Margin="4,0"
BorderThickness="1"
CornerRadius="12"
Text="{Binding StagedFilter, Mode=TwoWay}"
BorderBrush="{DynamicResource Brush.Border2}"
VerticalContentAlignment="Center">
<TextBox.InnerLeftContent>
<Path Width="14" Height="14"
Margin="6,0,0,0"
Fill="{DynamicResource Brush.FG2}"
Data="{StaticResource Icons.Search}"/>
</TextBox.InnerLeftContent>
<TextBox.InnerRightContent>
<Button Classes="icon_button"
Width="16"
Margin="0,0,6,0"
Command="{Binding ClearStagedFilter}"
IsVisible="{Binding StagedFilter, Converter={x:Static StringConverters.IsNotNullOrEmpty}}"
HorizontalAlignment="Right">
<Path Width="14" Height="14"
Margin="0,1,0,0"
Fill="{DynamicResource Brush.FG1}"
Data="{StaticResource Icons.Clear}"/>
</Button>
</TextBox.InnerRightContent>
</TextBox>
</Border>
<!-- Staged Changes -->
<v:ChangeCollectionView Grid.Row="2"
<v:ChangeCollectionView Grid.Row="1"
x:Name="StagedChangesView"
Focusable="True"
IsUnstagedChange="False"