Skip to content

Commit a1838d3

Browse files
Allow user to filter the files view to only show untracked files (#4226)
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. - **PR Description** - **Please check if the PR fulfills these requirements** * [x] Cheatsheets are up-to-date (run `go generate ./...`) * [x] Code has been formatted (see [here](https://github.com/jesseduffield/lazygit/blob/master/CONTRIBUTING.md#code-formatting)) * [x] Tests have been added/updated (see [here](https://github.com/jesseduffield/lazygit/blob/master/pkg/integration/README.md) for the integration test guide) * [x] Text is internationalised (see [here](https://github.com/jesseduffield/lazygit/blob/master/CONTRIBUTING.md#internationalisation)) * [ ] If a new UserConfig entry was added, make sure it can be hot-reloaded (see [here](https://github.com/jesseduffield/lazygit/blob/master/docs/dev/Codebase_Guide.md#using-userconfig)) * [ ] Docs have been updated if necessary * [x] You've read through your own file changes for silly mistakes etc <!-- Be sure to name your PR with an imperative e.g. 'Add worktrees view' see https://github.com/jesseduffield/lazygit/releases/tag/v0.40.0 for examples -->
2 parents 31e54ea + 7e85cdd commit a1838d3

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
@@ -1113,6 +1114,7 @@ func EnglishTranslationSet() *TranslationSet {
11131114
FilterStagedFiles: "Show only staged files",
11141115
FilterUnstagedFiles: "Show only unstaged files",
11151116
FilterTrackedFiles: "Show only tracked files",
1117+
FilterUntrackedFiles: "Show only untracked files",
11161118
ResetFilter: "Reset filter",
11171119
NoChangedFiles: "No changed files",
11181120
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)