@@ -99,12 +99,34 @@ public bool UseAmend
99
99
}
100
100
}
101
101
102
+ public string UnstagedFilter
103
+ {
104
+ get => _unstagedFilter ;
105
+ set
106
+ {
107
+ if ( SetProperty ( ref _unstagedFilter , value ) )
108
+ {
109
+ if ( _isLoadingData )
110
+ return ;
111
+
112
+ VisibleUnstaged = GetVisibleUnstagedChanges ( ) ;
113
+ SelectedUnstaged = [ ] ;
114
+ }
115
+ }
116
+ }
117
+
102
118
public List < Models . Change > Unstaged
103
119
{
104
120
get => _unstaged ;
105
121
private set => SetProperty ( ref _unstaged , value ) ;
106
122
}
107
123
124
+ public List < Models . Change > VisibleUnstaged
125
+ {
126
+ get => _visibleUnstaged ;
127
+ private set => SetProperty ( ref _visibleUnstaged , value ) ;
128
+ }
129
+
108
130
public List < Models . Change > Staged
109
131
{
110
132
get => _staged ;
@@ -191,8 +213,9 @@ public void Cleanup()
191
213
_selectedStaged . Clear ( ) ;
192
214
OnPropertyChanged ( nameof ( SelectedStaged ) ) ;
193
215
216
+ _visibleUnstaged . Clear ( ) ;
194
217
_unstaged . Clear ( ) ;
195
- OnPropertyChanged ( nameof ( Unstaged ) ) ;
218
+ OnPropertyChanged ( nameof ( VisibleUnstaged ) ) ;
196
219
197
220
_staged . Clear ( ) ;
198
221
OnPropertyChanged ( nameof ( Staged ) ) ;
@@ -249,20 +272,26 @@ public void SetData(List<Models.Change> changes)
249
272
}
250
273
251
274
var unstaged = new List < Models . Change > ( ) ;
252
- var selectedUnstaged = new List < Models . Change > ( ) ;
253
275
var hasConflict = false ;
254
276
foreach ( var c in changes )
255
277
{
256
278
if ( c . WorkTree != Models . ChangeState . None )
257
279
{
258
280
unstaged . Add ( c ) ;
259
281
hasConflict |= c . IsConflit ;
260
-
261
- if ( lastSelectedUnstaged . Contains ( c . Path ) )
262
- selectedUnstaged . Add ( c ) ;
263
282
}
264
283
}
265
284
285
+ _unstaged = unstaged ;
286
+
287
+ var visibleUnstaged = GetVisibleUnstagedChanges ( ) ;
288
+ var selectedUnstaged = new List < Models . Change > ( ) ;
289
+ foreach ( var c in visibleUnstaged )
290
+ {
291
+ if ( lastSelectedUnstaged . Contains ( c . Path ) )
292
+ selectedUnstaged . Add ( c ) ;
293
+ }
294
+
266
295
var staged = GetStagedChanges ( ) ;
267
296
var selectedStaged = new List < Models . Change > ( ) ;
268
297
foreach ( var c in staged )
@@ -275,7 +304,7 @@ public void SetData(List<Models.Change> changes)
275
304
{
276
305
_isLoadingData = true ;
277
306
HasUnsolvedConflicts = hasConflict ;
278
- Unstaged = unstaged ;
307
+ VisibleUnstaged = visibleUnstaged ;
279
308
Staged = staged ;
280
309
SelectedUnstaged = selectedUnstaged ;
281
310
SelectedStaged = selectedStaged ;
@@ -336,46 +365,7 @@ public void StageSelected(Models.Change next)
336
365
337
366
public void StageAll ( )
338
367
{
339
- StageChanges ( _unstaged , null ) ;
340
- }
341
-
342
- public async void StageChanges ( List < Models . Change > changes , Models . Change next )
343
- {
344
- if ( _unstaged . Count == 0 || changes . Count == 0 )
345
- return ;
346
-
347
- // Use `_selectedUnstaged` instead of `SelectedUnstaged` to avoid UI refresh.
348
- _selectedUnstaged = next != null ? [ next ] : [ ] ;
349
-
350
- IsStaging = true ;
351
- _repo . SetWatcherEnabled ( false ) ;
352
- if ( changes . Count == _unstaged . Count )
353
- {
354
- await Task . Run ( ( ) => new Commands . Add ( _repo . FullPath , _repo . IncludeUntracked ) . Exec ( ) ) ;
355
- }
356
- else if ( Native . OS . GitVersion >= Models . GitVersions . ADD_WITH_PATHSPECFILE )
357
- {
358
- var paths = new List < string > ( ) ;
359
- foreach ( var c in changes )
360
- paths . Add ( c . Path ) ;
361
-
362
- var tmpFile = Path . GetTempFileName ( ) ;
363
- File . WriteAllLines ( tmpFile , paths ) ;
364
- await Task . Run ( ( ) => new Commands . Add ( _repo . FullPath , tmpFile ) . Exec ( ) ) ;
365
- File . Delete ( tmpFile ) ;
366
- }
367
- else
368
- {
369
- for ( int i = 0 ; i < changes . Count ; i += 10 )
370
- {
371
- var count = Math . Min ( 10 , changes . Count - i ) ;
372
- var step = changes . GetRange ( i , count ) ;
373
- await Task . Run ( ( ) => new Commands . Add ( _repo . FullPath , step ) . Exec ( ) ) ;
374
- }
375
- }
376
- _repo . MarkWorkingCopyDirtyManually ( ) ;
377
- _repo . SetWatcherEnabled ( true ) ;
378
- IsStaging = false ;
368
+ StageChanges ( _visibleUnstaged , null ) ;
379
369
}
380
370
381
371
public void UnstageSelected ( Models . Change next )
@@ -388,44 +378,17 @@ public void UnstageAll()
388
378
UnstageChanges ( _staged , null ) ;
389
379
}
390
380
391
- public async void UnstageChanges ( List < Models . Change > changes , Models . Change next )
392
- {
393
- if ( _staged . Count == 0 || changes . Count == 0 )
394
- return ;
395
-
396
- // Use `_selectedStaged` instead of `SelectedStaged` to avoid UI refresh.
397
- _selectedStaged = next != null ? [ next ] : [ ] ;
398
-
399
- IsUnstaging = true ;
400
- _repo . SetWatcherEnabled ( false ) ;
401
- if ( _useAmend )
402
- {
403
- await Task . Run ( ( ) => new Commands . UnstageChangesForAmend ( _repo . FullPath , changes ) . Exec ( ) ) ;
404
- }
405
- else if ( changes . Count == _staged . Count )
406
- {
407
- await Task . Run ( ( ) => new Commands . Reset ( _repo . FullPath ) . Exec ( ) ) ;
408
- }
409
- else
410
- {
411
- for ( int i = 0 ; i < changes . Count ; i += 10 )
412
- {
413
- var count = Math . Min ( 10 , changes . Count - i ) ;
414
- var step = changes . GetRange ( i , count ) ;
415
- await Task . Run ( ( ) => new Commands . Reset ( _repo . FullPath , step ) . Exec ( ) ) ;
416
- }
417
- }
418
- _repo . MarkWorkingCopyDirtyManually ( ) ;
419
- _repo . SetWatcherEnabled ( true ) ;
420
- IsUnstaging = false ;
421
- }
422
-
423
381
public void Discard ( List < Models . Change > changes )
424
382
{
425
383
if ( _repo . CanCreatePopup ( ) )
426
384
_repo . ShowPopup ( new Discard ( _repo , changes ) ) ;
427
385
}
428
386
387
+ public void ClearUnstagedFilter ( )
388
+ {
389
+ UnstagedFilter = string . Empty ;
390
+ }
391
+
429
392
public async void UseTheirs ( List < Models . Change > changes )
430
393
{
431
394
var files = new List < string > ( ) ;
@@ -1496,6 +1459,22 @@ public ContextMenu CreateContextForOpenAI()
1496
1459
}
1497
1460
}
1498
1461
1462
+ private List < Models . Change > GetVisibleUnstagedChanges ( )
1463
+ {
1464
+ if ( string . IsNullOrEmpty ( _unstagedFilter ) )
1465
+ return _unstaged ;
1466
+
1467
+ var visible = new List < Models . Change > ( ) ;
1468
+
1469
+ foreach ( var c in _unstaged )
1470
+ {
1471
+ if ( c . Path . Contains ( _unstagedFilter , StringComparison . OrdinalIgnoreCase ) )
1472
+ visible . Add ( c ) ;
1473
+ }
1474
+
1475
+ return visible ;
1476
+ }
1477
+
1499
1478
private List < Models . Change > GetStagedChanges ( )
1500
1479
{
1501
1480
if ( _useAmend )
@@ -1511,6 +1490,77 @@ public ContextMenu CreateContextForOpenAI()
1511
1490
return rs ;
1512
1491
}
1513
1492
1493
+ private async void StageChanges ( List < Models . Change > changes , Models . Change next )
1494
+ {
1495
+ if ( changes . Count == 0 )
1496
+ return ;
1497
+
1498
+ // Use `_selectedUnstaged` instead of `SelectedUnstaged` to avoid UI refresh.
1499
+ _selectedUnstaged = next != null ? [ next ] : [ ] ;
1500
+
1501
+ IsStaging = true ;
1502
+ _repo . SetWatcherEnabled ( false ) ;
1503
+ if ( changes . Count == _unstaged . Count )
1504
+ {
1505
+ await Task . Run ( ( ) => new Commands . Add ( _repo . FullPath , _repo . IncludeUntracked ) . Exec ( ) ) ;
1506
+ }
1507
+ else if ( Native . OS . GitVersion >= Models . GitVersions . ADD_WITH_PATHSPECFILE )
1508
+ {
1509
+ var paths = new List < string > ( ) ;
1510
+ foreach ( var c in changes )
1511
+ paths . Add ( c . Path ) ;
1512
+
1513
+ var tmpFile = Path . GetTempFileName ( ) ;
1514
+ File . WriteAllLines ( tmpFile , paths ) ;
1515
+ await Task . Run ( ( ) => new Commands . Add ( _repo . FullPath , tmpFile ) . Exec ( ) ) ;
1516
+ File . Delete ( tmpFile ) ;
1517
+ }
1518
+ else
1519
+ {
1520
+ for ( int i = 0 ; i < changes . Count ; i += 10 )
1521
+ {
1522
+ var count = Math . Min ( 10 , changes . Count - i ) ;
1523
+ var step = changes . GetRange ( i , count ) ;
1524
+ await Task . Run ( ( ) => new Commands . Add ( _repo . FullPath , step ) . Exec ( ) ) ;
1525
+ }
1526
+ }
1527
+ _repo . MarkWorkingCopyDirtyManually ( ) ;
1528
+ _repo . SetWatcherEnabled ( true ) ;
1529
+ IsStaging = false ;
1530
+ }
1531
+
1532
+ private async void UnstageChanges ( List < Models . Change > changes , Models . Change next )
1533
+ {
1534
+ if ( changes . Count == 0 )
1535
+ return ;
1536
+
1537
+ // Use `_selectedStaged` instead of `SelectedStaged` to avoid UI refresh.
1538
+ _selectedStaged = next != null ? [ next ] : [ ] ;
1539
+
1540
+ IsUnstaging = true ;
1541
+ _repo . SetWatcherEnabled ( false ) ;
1542
+ if ( _useAmend )
1543
+ {
1544
+ await Task . Run ( ( ) => new Commands . UnstageChangesForAmend ( _repo . FullPath , changes ) . Exec ( ) ) ;
1545
+ }
1546
+ else if ( changes . Count == _staged . Count )
1547
+ {
1548
+ await Task . Run ( ( ) => new Commands . Reset ( _repo . FullPath ) . Exec ( ) ) ;
1549
+ }
1550
+ else
1551
+ {
1552
+ for ( int i = 0 ; i < changes . Count ; i += 10 )
1553
+ {
1554
+ var count = Math . Min ( 10 , changes . Count - i ) ;
1555
+ var step = changes . GetRange ( i , count ) ;
1556
+ await Task . Run ( ( ) => new Commands . Reset ( _repo . FullPath , step ) . Exec ( ) ) ;
1557
+ }
1558
+ }
1559
+ _repo . MarkWorkingCopyDirtyManually ( ) ;
1560
+ _repo . SetWatcherEnabled ( true ) ;
1561
+ IsUnstaging = false ;
1562
+ }
1563
+
1514
1564
private void SetDetail ( Models . Change change , bool isUnstaged )
1515
1565
{
1516
1566
if ( _isLoadingData )
@@ -1609,11 +1659,13 @@ private bool IsChanged(List<Models.Change> old, List<Models.Change> cur)
1609
1659
private bool _hasRemotes = false ;
1610
1660
private List < Models . Change > _cached = [ ] ;
1611
1661
private List < Models . Change > _unstaged = [ ] ;
1662
+ private List < Models . Change > _visibleUnstaged = [ ] ;
1612
1663
private List < Models . Change > _staged = [ ] ;
1613
1664
private List < Models . Change > _selectedUnstaged = [ ] ;
1614
1665
private List < Models . Change > _selectedStaged = [ ] ;
1615
1666
private int _count = 0 ;
1616
1667
private object _detailContext = null ;
1668
+ private string _unstagedFilter = string . Empty ;
1617
1669
private string _commitMessage = string . Empty ;
1618
1670
1619
1671
private bool _hasUnsolvedConflicts = false ;
0 commit comments