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:
@ -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)
|
||||||
|
@ -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)
|
||||||
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 {
|
func (self *FilesController) edit(nodes []*filetree.FileNode) error {
|
||||||
|
@ -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 {
|
||||||
|
@ -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) })
|
||||||
}
|
}
|
||||||
|
@ -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",
|
||||||
|
@ -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(),
|
||||||
|
)
|
||||||
|
},
|
||||||
|
})
|
@ -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,
|
||||||
|
Reference in New Issue
Block a user