Skip to content

Commit

Permalink
Allow user to filter the files view to only show untracked files
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
jesseduffield committed Feb 6, 2025
1 parent 31e54ea commit 7e85cdd
Show file tree
Hide file tree
Showing 7 changed files with 100 additions and 4 deletions.
6 changes: 5 additions & 1 deletion pkg/commands/git_commands/file_loader.go
Original file line number Diff line number Diff line change
Expand Up @@ -32,13 +32,17 @@ func NewFileLoader(gitCommon *GitCommon, cmd oscommands.ICmdObjBuilder, config F

type GetStatusFileOptions struct {
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 {
// check if config wants us ignoring untracked files
untrackedFilesSetting := self.config.GetShowUntrackedFiles()

if untrackedFilesSetting == "" {
if opts.ForceShowUntracked || untrackedFilesSetting == "" {
untrackedFilesSetting = "all"
}
untrackedFilesArg := fmt.Sprintf("--untracked-files=%s", untrackedFilesSetting)
Expand Down
21 changes: 19 additions & 2 deletions pkg/gui/controllers/files_controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -777,6 +777,13 @@ func (self *FilesController) handleStatusFilterPressed() error {
},
Key: 't',
},
{
Label: self.c.Tr.FilterUntrackedFiles,
OnPress: func() error {
return self.setStatusFiltering(filetree.DisplayUntracked)
},
Key: 'T',
},
{
Label: self.c.Tr.ResetFilter,
OnPress: func() error {
Expand All @@ -789,9 +796,19 @@ func (self *FilesController) handleStatusFilterPressed() error {
}

func (self *FilesController) setStatusFiltering(filter filetree.FileTreeDisplayFilter) error {
previousFilter := self.context().GetFilter()

self.context().FileTreeViewModel.SetStatusFilter(filter)
self.c.PostRefreshUpdate(self.context())
return nil

// 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())

return nil
}
}

func (self *FilesController) edit(nodes []*filetree.FileNode) error {
Expand Down
4 changes: 3 additions & 1 deletion pkg/gui/controllers/helpers/refresh_helper.go
Original file line number Diff line number Diff line change
Expand Up @@ -570,7 +570,9 @@ func (self *RefreshHelper) refreshStateFiles() error {
}

files := self.c.Git().Loaders.FileLoader.
GetStatusFiles(git_commands.GetStatusFileOptions{})
GetStatusFiles(git_commands.GetStatusFileOptions{
ForceShowUntracked: self.c.Contexts().Files.ForceShowUntracked(),
})

conflictFileCount := 0
for _, file := range files {
Expand Down
8 changes: 8 additions & 0 deletions pkg/gui/filetree/file_tree.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ const (
DisplayStaged
DisplayUnstaged
DisplayTracked
DisplayUntracked
// this shows files with merge conflicts
DisplayConflicted
)
Expand All @@ -40,6 +41,7 @@ type IFileTree interface {

FilterFiles(test func(*models.File) bool) []*models.File
SetStatusFilter(filter FileTreeDisplayFilter)
ForceShowUntracked() bool
Get(index int) *FileNode
GetFile(path string) *models.File
GetAllItems() []*FileNode
Expand Down Expand Up @@ -87,13 +89,19 @@ func (self *FileTree) getFilesForDisplay() []*models.File {
return self.FilterFiles(func(file *models.File) bool { return file.HasUnstagedChanges })
case DisplayTracked:
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:
return self.FilterFiles(func(file *models.File) bool { return file.HasMergeConflicts })
default:
panic(fmt.Sprintf("Unexpected files display filter: %d", self.filter))
}
}

func (self *FileTree) ForceShowUntracked() bool {
return self.filter == DisplayUntracked
}

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) })
}
Expand Down
2 changes: 2 additions & 0 deletions pkg/i18n/english.go
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,7 @@ type TranslationSet struct {
FilterStagedFiles string
FilterUnstagedFiles string
FilterTrackedFiles string
FilterUntrackedFiles string
ResetFilter string
MergeConflictsTitle string
Checkout string
Expand Down Expand Up @@ -1113,6 +1114,7 @@ func EnglishTranslationSet() *TranslationSet {
FilterStagedFiles: "Show only staged files",
FilterUnstagedFiles: "Show only unstaged files",
FilterTrackedFiles: "Show only tracked files",
FilterUntrackedFiles: "Show only untracked files",
ResetFilter: "Reset filter",
NoChangedFiles: "No changed files",
SoftReset: "Soft reset",
Expand Down
62 changes: 62 additions & 0 deletions pkg/integration/tests/filter_and_search/filter_by_file_status.go
Original file line number Diff line number Diff line change
@@ -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(),
)
},
})
1 change: 1 addition & 0 deletions pkg/integration/tests/test_list.go
Original file line number Diff line number Diff line change
Expand Up @@ -186,6 +186,7 @@ var tests = []*components.IntegrationTest{
file.StageChildrenRangeSelect,
file.StageDeletedRangeSelect,
file.StageRangeSelect,
filter_and_search.FilterByFileStatus,
filter_and_search.FilterCommitFiles,
filter_and_search.FilterFiles,
filter_and_search.FilterFuzzy,
Expand Down

0 comments on commit 7e85cdd

Please sign in to comment.