1
0
mirror of https://github.com/jesseduffield/lazygit.git synced 2025-06-15 00:15:32 +02:00

Allow user to filter the files view to only show untracked files

This handles the situation where the user's own config says to not show
untracked files, as is often the case with bare repos managing a user's
dotfiles.
This commit is contained in:
Jesse Duffield
2025-01-31 09:30:31 +11:00
parent 31e54eadaa
commit 7e85cdd027
7 changed files with 100 additions and 4 deletions

View File

@ -32,13 +32,17 @@ func NewFileLoader(gitCommon *GitCommon, cmd oscommands.ICmdObjBuilder, config F
type GetStatusFileOptions struct { type GetStatusFileOptions struct {
NoRenames bool NoRenames bool
// If true, we'll show untracked files even if the user has set the config to hide them.
// This is useful for users with bare repos for dotfiles who default to hiding untracked files,
// but want to occasionally see them to `git add` a new file.
ForceShowUntracked bool
} }
func (self *FileLoader) GetStatusFiles(opts GetStatusFileOptions) []*models.File { func (self *FileLoader) GetStatusFiles(opts GetStatusFileOptions) []*models.File {
// check if config wants us ignoring untracked files // check if config wants us ignoring untracked files
untrackedFilesSetting := self.config.GetShowUntrackedFiles() untrackedFilesSetting := self.config.GetShowUntrackedFiles()
if untrackedFilesSetting == "" { if opts.ForceShowUntracked || untrackedFilesSetting == "" {
untrackedFilesSetting = "all" untrackedFilesSetting = "all"
} }
untrackedFilesArg := fmt.Sprintf("--untracked-files=%s", untrackedFilesSetting) untrackedFilesArg := fmt.Sprintf("--untracked-files=%s", untrackedFilesSetting)

View File

@ -777,6 +777,13 @@ func (self *FilesController) handleStatusFilterPressed() error {
}, },
Key: 't', Key: 't',
}, },
{
Label: self.c.Tr.FilterUntrackedFiles,
OnPress: func() error {
return self.setStatusFiltering(filetree.DisplayUntracked)
},
Key: 'T',
},
{ {
Label: self.c.Tr.ResetFilter, Label: self.c.Tr.ResetFilter,
OnPress: func() error { OnPress: func() error {
@ -789,9 +796,19 @@ func (self *FilesController) handleStatusFilterPressed() error {
} }
func (self *FilesController) setStatusFiltering(filter filetree.FileTreeDisplayFilter) error { func (self *FilesController) setStatusFiltering(filter filetree.FileTreeDisplayFilter) error {
previousFilter := self.context().GetFilter()
self.context().FileTreeViewModel.SetStatusFilter(filter) self.context().FileTreeViewModel.SetStatusFilter(filter)
// Whenever we switch between untracked and other filters, we need to refresh the files view
// because the untracked files filter applies when running `git status`.
if previousFilter != filter && (previousFilter == filetree.DisplayUntracked || filter == filetree.DisplayUntracked) {
return self.c.Refresh(types.RefreshOptions{Scope: []types.RefreshableView{types.FILES}, Mode: types.ASYNC})
} else {
self.c.PostRefreshUpdate(self.context()) self.c.PostRefreshUpdate(self.context())
return nil return nil
}
} }
func (self *FilesController) edit(nodes []*filetree.FileNode) error { func (self *FilesController) edit(nodes []*filetree.FileNode) error {

View File

@ -570,7 +570,9 @@ func (self *RefreshHelper) refreshStateFiles() error {
} }
files := self.c.Git().Loaders.FileLoader. files := self.c.Git().Loaders.FileLoader.
GetStatusFiles(git_commands.GetStatusFileOptions{}) GetStatusFiles(git_commands.GetStatusFileOptions{
ForceShowUntracked: self.c.Contexts().Files.ForceShowUntracked(),
})
conflictFileCount := 0 conflictFileCount := 0
for _, file := range files { for _, file := range files {

View File

@ -16,6 +16,7 @@ const (
DisplayStaged DisplayStaged
DisplayUnstaged DisplayUnstaged
DisplayTracked DisplayTracked
DisplayUntracked
// this shows files with merge conflicts // this shows files with merge conflicts
DisplayConflicted DisplayConflicted
) )
@ -40,6 +41,7 @@ type IFileTree interface {
FilterFiles(test func(*models.File) bool) []*models.File FilterFiles(test func(*models.File) bool) []*models.File
SetStatusFilter(filter FileTreeDisplayFilter) SetStatusFilter(filter FileTreeDisplayFilter)
ForceShowUntracked() bool
Get(index int) *FileNode Get(index int) *FileNode
GetFile(path string) *models.File GetFile(path string) *models.File
GetAllItems() []*FileNode GetAllItems() []*FileNode
@ -87,6 +89,8 @@ func (self *FileTree) getFilesForDisplay() []*models.File {
return self.FilterFiles(func(file *models.File) bool { return file.HasUnstagedChanges }) return self.FilterFiles(func(file *models.File) bool { return file.HasUnstagedChanges })
case DisplayTracked: case DisplayTracked:
return self.FilterFiles(func(file *models.File) bool { return file.Tracked }) return self.FilterFiles(func(file *models.File) bool { return file.Tracked })
case DisplayUntracked:
return self.FilterFiles(func(file *models.File) bool { return !file.Tracked })
case DisplayConflicted: case DisplayConflicted:
return self.FilterFiles(func(file *models.File) bool { return file.HasMergeConflicts }) return self.FilterFiles(func(file *models.File) bool { return file.HasMergeConflicts })
default: default:
@ -94,6 +98,10 @@ func (self *FileTree) getFilesForDisplay() []*models.File {
} }
} }
func (self *FileTree) ForceShowUntracked() bool {
return self.filter == DisplayUntracked
}
func (self *FileTree) FilterFiles(test func(*models.File) bool) []*models.File { func (self *FileTree) FilterFiles(test func(*models.File) bool) []*models.File {
return lo.Filter(self.getFiles(), func(file *models.File, _ int) bool { return test(file) }) return lo.Filter(self.getFiles(), func(file *models.File, _ int) bool { return test(file) })
} }

View File

@ -88,6 +88,7 @@ type TranslationSet struct {
FilterStagedFiles string FilterStagedFiles string
FilterUnstagedFiles string FilterUnstagedFiles string
FilterTrackedFiles string FilterTrackedFiles string
FilterUntrackedFiles string
ResetFilter string ResetFilter string
MergeConflictsTitle string MergeConflictsTitle string
Checkout string Checkout string
@ -1113,6 +1114,7 @@ func EnglishTranslationSet() *TranslationSet {
FilterStagedFiles: "Show only staged files", FilterStagedFiles: "Show only staged files",
FilterUnstagedFiles: "Show only unstaged files", FilterUnstagedFiles: "Show only unstaged files",
FilterTrackedFiles: "Show only tracked files", FilterTrackedFiles: "Show only tracked files",
FilterUntrackedFiles: "Show only untracked files",
ResetFilter: "Reset filter", ResetFilter: "Reset filter",
NoChangedFiles: "No changed files", NoChangedFiles: "No changed files",
SoftReset: "Soft reset", SoftReset: "Soft reset",

View File

@ -0,0 +1,62 @@
package filter_and_search
import (
"github.com/jesseduffield/lazygit/pkg/config"
. "github.com/jesseduffield/lazygit/pkg/integration/components"
)
var FilterByFileStatus = NewIntegrationTest(NewIntegrationTestArgs{
Description: "Filtering to show untracked files in repo that hides them by default",
ExtraCmdArgs: []string{},
Skip: false,
SetupConfig: func(config *config.AppConfig) {
},
SetupRepo: func(shell *Shell) {
// need to set untracked files to not be displayed in git config
shell.SetConfig("status.showUntrackedFiles", "no")
shell.CreateFileAndAdd("file-tracked", "foo")
shell.Commit("first commit")
shell.CreateFile("file-untracked", "bar")
shell.UpdateFile("file-tracked", "baz")
},
Run: func(t *TestDriver, keys config.KeybindingConfig) {
t.Views().Files().
Focus().
Lines(
Contains(`file-tracked`).IsSelected(),
).
Press(keys.Files.OpenStatusFilter).
Tap(func() {
t.ExpectPopup().Menu().
Title(Equals("Filtering")).
Select(Contains("Show only untracked files")).
Confirm()
}).
Lines(
Contains(`file-untracked`).IsSelected(),
).
Press(keys.Files.OpenStatusFilter).
Tap(func() {
t.ExpectPopup().Menu().
Title(Equals("Filtering")).
Select(Contains("Show only tracked files")).
Confirm()
}).
Lines(
Contains(`file-tracked`).IsSelected(),
).
Press(keys.Files.OpenStatusFilter).
Tap(func() {
t.ExpectPopup().Menu().
Title(Equals("Filtering")).
Select(Contains("Reset filter")).
Confirm()
}).
Lines(
Contains(`file-tracked`).IsSelected(),
)
},
})

View File

@ -186,6 +186,7 @@ var tests = []*components.IntegrationTest{
file.StageChildrenRangeSelect, file.StageChildrenRangeSelect,
file.StageDeletedRangeSelect, file.StageDeletedRangeSelect,
file.StageRangeSelect, file.StageRangeSelect,
filter_and_search.FilterByFileStatus,
filter_and_search.FilterCommitFiles, filter_and_search.FilterCommitFiles,
filter_and_search.FilterFiles, filter_and_search.FilterFiles,
filter_and_search.FilterFuzzy, filter_and_search.FilterFuzzy,