Skip to content

Commit b57be9e

Browse files
authored
Fix and speed up the file list for the "Enter path to filter by" feature (#5056)
The file suggestions list that appears when you choose "Enter path to filter by" from the Filtering menu was previously implemented by walking the file system to collect all files, and then filter out the ones that are git-ignored. This approach had several problems: - for certain .gitignore configurations there was a bug in the code that filters out ignored files, causing the list to be empty. See #2642 (comment) for details. - the list included files from submodules. While at first glance this might sound useful, it doesn't work: choosing a file from the list shows an empty log. Lazygit would have to switch to the submodule first to show the log, which would be unexpected in the context of the Filtering dialog, so it's better to not show these files, and require the user to enter a submodule explicitly before they can filter on a file in it. - it was rather slow. This PR improves all these by using a call to `git ls-files` to list the repositories files. Here are some rough hand-timed measurements (from opening the Filtering dialog until the file list shows) for various repos: | | before | after | |:--------------------------- | ------:| -------------- | | lazygit (3'200 files) | ~1s | not measurable | | my work repo (90'000 files) | 7s | <1s | | llvm (150'000 files) | 5s | ~1s | Fixes #2642
2 parents 9d24963 + b06c2bb commit b57be9e

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

44 files changed

+63
-3318
lines changed

go.mod

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,6 @@ require (
2020
github.com/jesseduffield/go-git/v5 v5.14.1-0.20250407170251-e1a013310ccd
2121
github.com/jesseduffield/gocui v0.3.1-0.20251002151855-67e0e55ff42a
2222
github.com/jesseduffield/lazycore v0.0.0-20221012050358-03d2e40243c5
23-
github.com/jesseduffield/minimal/gitignore v0.3.3-0.20211018110810-9cde264e6b1e
2423
github.com/kardianos/osext v0.0.0-20190222173326-2bc1f35cddc0
2524
github.com/karimkhaleel/jsonschema v0.0.0-20231001195015-d933f0d94ea3
2625
github.com/kyokomi/emoji/v2 v2.2.8
@@ -60,7 +59,6 @@ require (
6059
github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376 // indirect
6160
github.com/go-git/go-billy/v5 v5.6.2 // indirect
6261
github.com/go-logfmt/logfmt v0.5.0 // indirect
63-
github.com/gobwas/glob v0.2.3 // indirect
6462
github.com/golang/groupcache v0.0.0-20241129210726-2c02b8208cf8 // indirect
6563
github.com/hpcloud/tail v1.0.0 // indirect
6664
github.com/invopop/jsonschema v0.10.0 // indirect

go.sum

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -117,8 +117,6 @@ github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2
117117
github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk=
118118
github.com/go-logfmt/logfmt v0.5.0 h1:TrB8swr/68K7m9CcGut2g3UOihhbcbiMAYiuTXdEih4=
119119
github.com/go-logfmt/logfmt v0.5.0/go.mod h1:wCYkCAKZfumFQihp8CzCvQ3paCTfi41vtzG1KdI/P7A=
120-
github.com/gobwas/glob v0.2.3 h1:A4xDbljILXROh+kObIiy5kIaPYD8e96x1tgBhUI5J+Y=
121-
github.com/gobwas/glob v0.2.3/go.mod h1:d3Ez4x06l9bZtSvzIay5+Yzi0fmZzPgnTbPcKjJAkT8=
122120
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
123121
github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
124122
github.com/golang/groupcache v0.0.0-20191227052852-215e87163ea7/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
@@ -200,8 +198,6 @@ github.com/jesseduffield/gocui v0.3.1-0.20251002151855-67e0e55ff42a h1:z3NQvFXAW
200198
github.com/jesseduffield/gocui v0.3.1-0.20251002151855-67e0e55ff42a/go.mod h1:sLIyZ2J42R6idGdtemZzsiR3xY5EF0KsvYEGh3dQv3s=
201199
github.com/jesseduffield/lazycore v0.0.0-20221012050358-03d2e40243c5 h1:CDuQmfOjAtb1Gms6a1p5L2P8RhbLUq5t8aL7PiQd2uY=
202200
github.com/jesseduffield/lazycore v0.0.0-20221012050358-03d2e40243c5/go.mod h1:qxN4mHOAyeIDLP7IK7defgPClM/z1Kze8VVQiaEjzsQ=
203-
github.com/jesseduffield/minimal/gitignore v0.3.3-0.20211018110810-9cde264e6b1e h1:uw/oo+kg7t/oeMs6sqlAwr85ND/9cpO3up3VxphxY0U=
204-
github.com/jesseduffield/minimal/gitignore v0.3.3-0.20211018110810-9cde264e6b1e/go.mod h1:u60qdFGXRd36jyEXxetz0vQceQIxzI13lIo3EFUDf4I=
205201
github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y=
206202
github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU=
207203
github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk=

pkg/commands/git_commands/working_tree.go

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -451,3 +451,19 @@ func (self *WorkingTreeCommands) MergeFileForObjectIDs(strategy string, oursID s
451451

452452
return self.cmd.New(cmdArgs).RunWithOutput()
453453
}
454+
455+
// Returns all tracked files in the repo (not in the working tree). The returned entries are
456+
// relative paths to the repo root, using '/' as the path separator on all platforms.
457+
// Does not really belong in WorkingTreeCommands, but it's close enough, and we don't seem to have a
458+
// better place for it right now.
459+
func (self *WorkingTreeCommands) AllRepoFiles() ([]string, error) {
460+
cmdArgs := NewGitCmd("ls-files").Arg("-z").ToArgv()
461+
output, err := self.cmd.New(cmdArgs).DontLog().RunWithOutput()
462+
if err != nil {
463+
return nil, err
464+
}
465+
if output == "" {
466+
return []string{}, nil
467+
}
468+
return strings.Split(strings.TrimRight(output, "\x00"), "\x00"), nil
469+
}

pkg/commands/git_commands/working_tree_test.go

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -563,3 +563,32 @@ func TestWorkingTreeResetHard(t *testing.T) {
563563
})
564564
}
565565
}
566+
567+
func TestWorkingTreeCommands_AllRepoFiles(t *testing.T) {
568+
scenarios := []struct {
569+
name string
570+
runner *oscommands.FakeCmdObjRunner
571+
expected []string
572+
}{
573+
{
574+
name: "no files",
575+
runner: oscommands.NewFakeRunner(t).
576+
ExpectGitArgs([]string{"ls-files", "-z"}, "", nil),
577+
expected: []string{},
578+
},
579+
{
580+
name: "two files",
581+
runner: oscommands.NewFakeRunner(t).
582+
ExpectGitArgs([]string{"ls-files", "-z"}, "dir/file1.txt\x00dir2/file2.go\x00", nil),
583+
expected: []string{"dir/file1.txt", "dir2/file2.go"},
584+
},
585+
}
586+
for _, s := range scenarios {
587+
t.Run(s.name, func(t *testing.T) {
588+
instance := buildWorkingTreeCommands(commonDeps{runner: s.runner})
589+
result, err := instance.AllRepoFiles()
590+
assert.NoError(t, err)
591+
assert.Equal(t, s.expected, result)
592+
})
593+
}
594+
}

pkg/gui/controllers/helpers/suggestions_helper.go

Lines changed: 18 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -2,15 +2,14 @@ package helpers
22

33
import (
44
"fmt"
5-
"os"
65
"strings"
76

7+
"github.com/jesseduffield/generics/set"
88
"github.com/jesseduffield/gocui"
99
"github.com/jesseduffield/lazygit/pkg/commands/models"
1010
"github.com/jesseduffield/lazygit/pkg/gui/presentation"
1111
"github.com/jesseduffield/lazygit/pkg/gui/types"
1212
"github.com/jesseduffield/lazygit/pkg/utils"
13-
"github.com/jesseduffield/minimal/gitignore"
1413
"github.com/samber/lo"
1514
"golang.org/x/exp/slices"
1615
"gopkg.in/ozeidan/fuzzy-patricia.v3/patricia"
@@ -92,22 +91,28 @@ func (self *SuggestionsHelper) GetBranchNameSuggestionsFunc() func(string) []*ty
9291
func (self *SuggestionsHelper) GetFilePathSuggestionsFunc() func(string) []*types.Suggestion {
9392
_ = self.c.WithWaitingStatus(self.c.Tr.LoadingFileSuggestions, func(gocui.Task) error {
9493
trie := patricia.NewTrie()
95-
// load every non-gitignored file in the repo
96-
ignore, err := gitignore.FromGit()
94+
95+
// load every file in the repo
96+
files, err := self.c.Git().WorkingTree.AllRepoFiles()
9797
if err != nil {
9898
return err
9999
}
100100

101-
err = ignore.Walk(".",
102-
func(path string, info os.FileInfo, err error) error {
103-
if err != nil {
104-
return err
105-
}
106-
if path != "." {
107-
trie.Insert(patricia.Prefix(path), path)
101+
seen := set.New[string]()
102+
for _, file := range files {
103+
// For every file we also want to add its parent directories, but only once.
104+
for i := range len(file) {
105+
if file[i] == '/' {
106+
dir := file[:i]
107+
if !seen.Includes(dir) {
108+
trie.Insert(patricia.Prefix(dir), dir)
109+
seen.Add(dir)
110+
}
108111
}
109-
return nil
110-
})
112+
}
113+
114+
trie.Insert(patricia.Prefix(file), file)
115+
}
111116

112117
// cache the trie for future use
113118
self.c.Model().FilesTrie = trie

vendor/github.com/gobwas/glob/.gitignore

Lines changed: 0 additions & 8 deletions
This file was deleted.

vendor/github.com/gobwas/glob/.travis.yml

Lines changed: 0 additions & 9 deletions
This file was deleted.

vendor/github.com/gobwas/glob/LICENSE

Lines changed: 0 additions & 21 deletions
This file was deleted.

vendor/github.com/gobwas/glob/bench.sh

Lines changed: 0 additions & 26 deletions
This file was deleted.

0 commit comments

Comments
 (0)