Skip to content

Commit 38f35ae

Browse files
committed
Avoid enumerating untracked files when staging hunks of tracked files
We know that staging or unstaging a hunk in a tracked file doesn't have influence on untracked files, so we can take them over from the previous state of the model. This can be a performance improvement in repos with many untracked files.
1 parent cf491d1 commit 38f35ae

File tree

3 files changed

+34
-7
lines changed

3 files changed

+34
-7
lines changed

pkg/gui/controllers/helpers/refresh_helper.go

Lines changed: 17 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -169,7 +169,7 @@ func (self *RefreshHelper) Refresh(options types.RefreshOptions) {
169169
if scopeSet.Includes(types.FILES) || scopeSet.Includes(types.SUBMODULES) {
170170
fileWg.Add(1)
171171
refresh("files", func() {
172-
_ = self.refreshFilesAndSubmodules()
172+
_ = self.refreshFilesAndSubmodules(options.TakeOverUntrackedFilesFromPreviousModel)
173173
fileWg.Done()
174174
})
175175
}
@@ -539,7 +539,7 @@ func (self *RefreshHelper) refreshBranches(refreshWorktrees bool, keepBranchSele
539539
self.refreshStatus()
540540
}
541541

542-
func (self *RefreshHelper) refreshFilesAndSubmodules() error {
542+
func (self *RefreshHelper) refreshFilesAndSubmodules(takeOverUntrackedFilesFromPreviousModel bool) error {
543543
self.c.Mutexes().RefreshingFilesMutex.Lock()
544544
self.c.State().SetIsRefreshingFiles(true)
545545
defer func() {
@@ -551,7 +551,7 @@ func (self *RefreshHelper) refreshFilesAndSubmodules() error {
551551
return err
552552
}
553553

554-
if err := self.refreshStateFiles(); err != nil {
554+
if err := self.refreshStateFiles(takeOverUntrackedFilesFromPreviousModel); err != nil {
555555
return err
556556
}
557557

@@ -564,7 +564,7 @@ func (self *RefreshHelper) refreshFilesAndSubmodules() error {
564564
return nil
565565
}
566566

567-
func (self *RefreshHelper) refreshStateFiles() error {
567+
func (self *RefreshHelper) refreshStateFiles(takeOverUntrackedFilesFromPreviousModel bool) error {
568568
fileTreeViewModel := self.c.Contexts().Files.FileTreeViewModel
569569

570570
prevConflictFileCount := 0
@@ -599,15 +599,27 @@ func (self *RefreshHelper) refreshStateFiles() error {
599599
}
600600
}
601601

602+
var previousUntrackedFiles []*models.File
603+
if takeOverUntrackedFilesFromPreviousModel {
604+
previousUntrackedFiles = lo.Filter(self.c.Model().Files,
605+
func(file *models.File, _ int) bool { return file.ShortStatus == "??" })
606+
}
607+
602608
showUntracked := git_commands.ShowUntrackedModeAuto
603-
if self.c.Contexts().Files.ForceShowUntracked() {
609+
if takeOverUntrackedFilesFromPreviousModel {
610+
showUntracked = git_commands.ShowUntrackedModeOff
611+
} else if self.c.Contexts().Files.ForceShowUntracked() {
604612
showUntracked = git_commands.ShowUntrackedModeOn
605613
}
606614
files := self.c.Git().Loaders.FileLoader.
607615
GetStatusFiles(git_commands.GetStatusFileOptions{
608616
ShowUntracked: showUntracked,
609617
})
610618

619+
if takeOverUntrackedFilesFromPreviousModel {
620+
files = append(files, previousUntrackedFiles...)
621+
}
622+
611623
conflictFileCount := 0
612624
for _, file := range files {
613625
if file.HasMergeConflicts {

pkg/gui/controllers/staging_controller.go

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -226,11 +226,16 @@ func (self *StagingController) DiscardSelection() error {
226226
}
227227

228228
func (self *StagingController) applySelectionAndRefresh(reverse bool) error {
229+
isTracked := self.c.Contexts().Files.GetSelectedFile().Tracked
230+
229231
if err := self.applySelection(reverse); err != nil {
230232
return err
231233
}
232234

233-
self.c.Refresh(types.RefreshOptions{Scope: []types.RefreshableView{types.FILES, types.STAGING}})
235+
self.c.Refresh(types.RefreshOptions{
236+
Scope: []types.RefreshableView{types.FILES, types.STAGING},
237+
TakeOverUntrackedFilesFromPreviousModel: isTracked,
238+
})
234239
return nil
235240
}
236241

@@ -281,11 +286,16 @@ func (self *StagingController) applySelection(reverse bool) error {
281286
}
282287

283288
func (self *StagingController) EditHunkAndRefresh() error {
289+
isTracked := self.c.Contexts().Files.GetSelectedFile().Tracked
290+
284291
if err := self.editHunk(); err != nil {
285292
return err
286293
}
287294

288-
self.c.Refresh(types.RefreshOptions{Scope: []types.RefreshableView{types.FILES, types.STAGING}})
295+
self.c.Refresh(types.RefreshOptions{
296+
Scope: []types.RefreshableView{types.FILES, types.STAGING},
297+
TakeOverUntrackedFilesFromPreviousModel: isTracked,
298+
})
289299
return nil
290300
}
291301

pkg/gui/types/refresh.go

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,4 +44,9 @@ type RefreshOptions struct {
4444
// keeps the selection index the same. Useful after checking out a detached
4545
// head, and selecting index 0.
4646
KeepBranchSelectionIndex bool
47+
48+
// If true, call git status with --untracked-files=no to skip enumerating untracked files, and
49+
// keep the ones we have in the model. Useful as a performance optimization when we know the
50+
// untracked files can't have changed, e.g. after staging/unstaging hunks of tracked files.
51+
TakeOverUntrackedFilesFromPreviousModel bool
4752
}

0 commit comments

Comments
 (0)