From 1c1676a5d915833d0454d604118a044c631907f5 Mon Sep 17 00:00:00 2001 From: Stefan Haller Date: Thu, 20 Nov 2025 08:41:44 +0100 Subject: [PATCH] Use git ls-files to list all file suggestions --- pkg/commands/git_commands/working_tree.go | 16 ++++++++++ .../git_commands/working_tree_test.go | 29 +++++++++++++++++ .../controllers/helpers/suggestions_helper.go | 31 +++++++++++-------- 3 files changed, 63 insertions(+), 13 deletions(-) diff --git a/pkg/commands/git_commands/working_tree.go b/pkg/commands/git_commands/working_tree.go index 1cba0d511..e74ccf1e5 100644 --- a/pkg/commands/git_commands/working_tree.go +++ b/pkg/commands/git_commands/working_tree.go @@ -451,3 +451,19 @@ func (self *WorkingTreeCommands) MergeFileForObjectIDs(strategy string, oursID s return self.cmd.New(cmdArgs).RunWithOutput() } + +// Returns all tracked files in the repo (not in the working tree). The returned entries are +// relative paths to the repo root, using '/' as the path separator on all platforms. +// Does not really belong in WorkingTreeCommands, but it's close enough, and we don't seem to have a +// better place for it right now. +func (self *WorkingTreeCommands) AllRepoFiles() ([]string, error) { + cmdArgs := NewGitCmd("ls-files").Arg("-z").ToArgv() + output, err := self.cmd.New(cmdArgs).DontLog().RunWithOutput() + if err != nil { + return nil, err + } + if output == "" { + return []string{}, nil + } + return strings.Split(strings.TrimRight(output, "\x00"), "\x00"), nil +} diff --git a/pkg/commands/git_commands/working_tree_test.go b/pkg/commands/git_commands/working_tree_test.go index 759eb1bbe..712b08ca1 100644 --- a/pkg/commands/git_commands/working_tree_test.go +++ b/pkg/commands/git_commands/working_tree_test.go @@ -563,3 +563,32 @@ func TestWorkingTreeResetHard(t *testing.T) { }) } } + +func TestWorkingTreeCommands_AllRepoFiles(t *testing.T) { + scenarios := []struct { + name string + runner *oscommands.FakeCmdObjRunner + expected []string + }{ + { + name: "no files", + runner: oscommands.NewFakeRunner(t). + ExpectGitArgs([]string{"ls-files", "-z"}, "", nil), + expected: []string{}, + }, + { + name: "two files", + runner: oscommands.NewFakeRunner(t). + ExpectGitArgs([]string{"ls-files", "-z"}, "dir/file1.txt\x00dir2/file2.go\x00", nil), + expected: []string{"dir/file1.txt", "dir2/file2.go"}, + }, + } + for _, s := range scenarios { + t.Run(s.name, func(t *testing.T) { + instance := buildWorkingTreeCommands(commonDeps{runner: s.runner}) + result, err := instance.AllRepoFiles() + assert.NoError(t, err) + assert.Equal(t, s.expected, result) + }) + } +} diff --git a/pkg/gui/controllers/helpers/suggestions_helper.go b/pkg/gui/controllers/helpers/suggestions_helper.go index 38ef45430..de0d04843 100644 --- a/pkg/gui/controllers/helpers/suggestions_helper.go +++ b/pkg/gui/controllers/helpers/suggestions_helper.go @@ -2,15 +2,14 @@ package helpers import ( "fmt" - "os" "strings" + "github.com/jesseduffield/generics/set" "github.com/jesseduffield/gocui" "github.com/jesseduffield/lazygit/pkg/commands/models" "github.com/jesseduffield/lazygit/pkg/gui/presentation" "github.com/jesseduffield/lazygit/pkg/gui/types" "github.com/jesseduffield/lazygit/pkg/utils" - "github.com/jesseduffield/minimal/gitignore" "github.com/samber/lo" "golang.org/x/exp/slices" "gopkg.in/ozeidan/fuzzy-patricia.v3/patricia" @@ -92,22 +91,28 @@ func (self *SuggestionsHelper) GetBranchNameSuggestionsFunc() func(string) []*ty func (self *SuggestionsHelper) GetFilePathSuggestionsFunc() func(string) []*types.Suggestion { _ = self.c.WithWaitingStatus(self.c.Tr.LoadingFileSuggestions, func(gocui.Task) error { trie := patricia.NewTrie() - // load every non-gitignored file in the repo - ignore, err := gitignore.FromGit() + + // load every file in the repo + files, err := self.c.Git().WorkingTree.AllRepoFiles() if err != nil { return err } - err = ignore.Walk(".", - func(path string, info os.FileInfo, err error) error { - if err != nil { - return err + seen := set.New[string]() + for _, file := range files { + // For every file we also want to add its parent directories, but only once. + for i := range len(file) { + if file[i] == '/' { + dir := file[:i] + if !seen.Includes(dir) { + trie.Insert(patricia.Prefix(dir), dir) + seen.Add(dir) + } } - if path != "." { - trie.Insert(patricia.Prefix(path), path) - } - return nil - }) + } + + trie.Insert(patricia.Prefix(file), file) + } // cache the trie for future use self.c.Model().FilesTrie = trie