Skip to content

Commit b5e1269

Browse files
committed
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.
1 parent 49f8dc2 commit b5e1269

File tree

7 files changed

+100
-4
lines changed

7 files changed

+100
-4
lines changed

pkg/commands/git_commands/file_loader.go

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,13 +32,17 @@ func NewFileLoader(gitCommon *GitCommon, cmd oscommands.ICmdObjBuilder, config F
3232

3333
type GetStatusFileOptions struct {
3434
NoRenames bool
35+
// If true, we'll show untracked files even if the user has set the config to hide them.
36+
// This is useful for users with bare repos for dotfiles who default to hiding untracked files,
37+
// but want to occasionally see them to `git add` a new file.
38+
ForceShowUntracked bool
3539
}
3640

3741
func (self *FileLoader) GetStatusFiles(opts GetStatusFileOptions) []*models.File {
3842
// check if config wants us ignoring untracked files
3943
untrackedFilesSetting := self.config.GetShowUntrackedFiles()
4044

41-
if untrackedFilesSetting == "" {
45+
if opts.ForceShowUntracked || untrackedFilesSetting == "" {
4246
untrackedFilesSetting = "all"
4347
}
4448
untrackedFilesArg := fmt.Sprintf("--untracked-files=%s", untrackedFilesSetting)

pkg/gui/controllers/files_controller.go

Lines changed: 19 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -777,6 +777,13 @@ func (self *FilesController) handleStatusFilterPressed() error {
777777
},
778778
Key: 't',
779779
},
780+
{
781+
Label: self.c.Tr.FilterUntrackedFiles,
782+
OnPress: func() error {
783+
return self.setStatusFiltering(filetree.DisplayUntracked)
784+
},
785+
Key: 'T',
786+
},
780787
{
781788
Label: self.c.Tr.ResetFilter,
782789
OnPress: func() error {
@@ -789,9 +796,19 @@ func (self *FilesController) handleStatusFilterPressed() error {
789796
}
790797

791798
func (self *FilesController) setStatusFiltering(filter filetree.FileTreeDisplayFilter) error {
799+
previousFilter := self.context().GetFilter()
800+
792801
self.context().FileTreeViewModel.SetStatusFilter(filter)
793-
self.c.PostRefreshUpdate(self.context())
794-
return nil
802+
803+
// Whenever we switch between untracked and other filters, we need to refresh the files view
804+
// because the untracked files filter applies when running `git status`.
805+
if previousFilter != filter && (previousFilter == filetree.DisplayUntracked || filter == filetree.DisplayUntracked) {
806+
return self.c.Refresh(types.RefreshOptions{Scope: []types.RefreshableView{types.FILES}, Mode: types.ASYNC})
807+
} else {
808+
self.c.PostRefreshUpdate(self.context())
809+
810+
return nil
811+
}
795812
}
796813

797814
func (self *FilesController) edit(nodes []*filetree.FileNode) error {

pkg/gui/controllers/helpers/refresh_helper.go

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -570,7 +570,9 @@ func (self *RefreshHelper) refreshStateFiles() error {
570570
}
571571

572572
files := self.c.Git().Loaders.FileLoader.
573-
GetStatusFiles(git_commands.GetStatusFileOptions{})
573+
GetStatusFiles(git_commands.GetStatusFileOptions{
574+
ForceShowUntracked: self.c.Contexts().Files.ForceShowUntracked(),
575+
})
574576

575577
conflictFileCount := 0
576578
for _, file := range files {

pkg/gui/filetree/file_tree.go

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ const (
1616
DisplayStaged
1717
DisplayUnstaged
1818
DisplayTracked
19+
DisplayUntracked
1920
// this shows files with merge conflicts
2021
DisplayConflicted
2122
)
@@ -40,6 +41,7 @@ type IFileTree interface {
4041

4142
FilterFiles(test func(*models.File) bool) []*models.File
4243
SetStatusFilter(filter FileTreeDisplayFilter)
44+
ForceShowUntracked() bool
4345
Get(index int) *FileNode
4446
GetFile(path string) *models.File
4547
GetAllItems() []*FileNode
@@ -87,13 +89,19 @@ func (self *FileTree) getFilesForDisplay() []*models.File {
8789
return self.FilterFiles(func(file *models.File) bool { return file.HasUnstagedChanges })
8890
case DisplayTracked:
8991
return self.FilterFiles(func(file *models.File) bool { return file.Tracked })
92+
case DisplayUntracked:
93+
return self.FilterFiles(func(file *models.File) bool { return !file.Tracked })
9094
case DisplayConflicted:
9195
return self.FilterFiles(func(file *models.File) bool { return file.HasMergeConflicts })
9296
default:
9397
panic(fmt.Sprintf("Unexpected files display filter: %d", self.filter))
9498
}
9599
}
96100

101+
func (self *FileTree) ForceShowUntracked() bool {
102+
return self.filter == DisplayUntracked
103+
}
104+
97105
func (self *FileTree) FilterFiles(test func(*models.File) bool) []*models.File {
98106
return lo.Filter(self.getFiles(), func(file *models.File, _ int) bool { return test(file) })
99107
}

pkg/i18n/english.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -88,6 +88,7 @@ type TranslationSet struct {
8888
FilterStagedFiles string
8989
FilterUnstagedFiles string
9090
FilterTrackedFiles string
91+
FilterUntrackedFiles string
9192
ResetFilter string
9293
MergeConflictsTitle string
9394
Checkout string
@@ -1111,6 +1112,7 @@ func EnglishTranslationSet() *TranslationSet {
11111112
FilterStagedFiles: "Show only staged files",
11121113
FilterUnstagedFiles: "Show only unstaged files",
11131114
FilterTrackedFiles: "Show only tracked files",
1115+
FilterUntrackedFiles: "Show only untracked files",
11141116
ResetFilter: "Reset filter",
11151117
NoChangedFiles: "No changed files",
11161118
SoftReset: "Soft reset",
Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
package filter_and_search
2+
3+
import (
4+
"github.com/jesseduffield/lazygit/pkg/config"
5+
. "github.com/jesseduffield/lazygit/pkg/integration/components"
6+
)
7+
8+
var FilterByFileStatus = NewIntegrationTest(NewIntegrationTestArgs{
9+
Description: "Filtering to show untracked files in repo that hides them by default",
10+
ExtraCmdArgs: []string{},
11+
Skip: false,
12+
SetupConfig: func(config *config.AppConfig) {
13+
},
14+
SetupRepo: func(shell *Shell) {
15+
// need to set untracked files to not be displayed in git config
16+
shell.SetConfig("status.showUntrackedFiles", "no")
17+
18+
shell.CreateFileAndAdd("file-tracked", "foo")
19+
20+
shell.Commit("first commit")
21+
22+
shell.CreateFile("file-untracked", "bar")
23+
shell.UpdateFile("file-tracked", "baz")
24+
},
25+
Run: func(t *TestDriver, keys config.KeybindingConfig) {
26+
t.Views().Files().
27+
Focus().
28+
Lines(
29+
Contains(`file-tracked`).IsSelected(),
30+
).
31+
Press(keys.Files.OpenStatusFilter).
32+
Tap(func() {
33+
t.ExpectPopup().Menu().
34+
Title(Equals("Filtering")).
35+
Select(Contains("Show only untracked files")).
36+
Confirm()
37+
}).
38+
Lines(
39+
Contains(`file-untracked`).IsSelected(),
40+
).
41+
Press(keys.Files.OpenStatusFilter).
42+
Tap(func() {
43+
t.ExpectPopup().Menu().
44+
Title(Equals("Filtering")).
45+
Select(Contains("Show only tracked files")).
46+
Confirm()
47+
}).
48+
Lines(
49+
Contains(`file-tracked`).IsSelected(),
50+
).
51+
Press(keys.Files.OpenStatusFilter).
52+
Tap(func() {
53+
t.ExpectPopup().Menu().
54+
Title(Equals("Filtering")).
55+
Select(Contains("Reset filter")).
56+
Confirm()
57+
}).
58+
Lines(
59+
Contains(`file-tracked`).IsSelected(),
60+
)
61+
},
62+
})

pkg/integration/tests/test_list.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -186,6 +186,7 @@ var tests = []*components.IntegrationTest{
186186
file.StageChildrenRangeSelect,
187187
file.StageDeletedRangeSelect,
188188
file.StageRangeSelect,
189+
filter_and_search.FilterByFileStatus,
189190
filter_and_search.FilterCommitFiles,
190191
filter_and_search.FilterFiles,
191192
filter_and_search.FilterFuzzy,

0 commit comments

Comments
 (0)