diff --git a/pkg/commands/git_commands/working_tree.go b/pkg/commands/git_commands/working_tree.go index 55159a7f9..664436066 100644 --- a/pkg/commands/git_commands/working_tree.go +++ b/pkg/commands/git_commands/working_tree.go @@ -253,11 +253,14 @@ func (self *WorkingTreeCommands) Exclude(filename string) error { // WorktreeFileDiff returns the diff of a file func (self *WorkingTreeCommands) WorktreeFileDiff(file *models.File, plain bool, cached bool) string { // for now we assume an error means the file was deleted - s, _ := self.WorktreeFileDiffCmdObj(file, plain, cached).RunWithOutput() + s, _ := self.WorktreeFileDiffCmdObj(file, plain, cached, nil).RunWithOutput() return s } -func (self *WorkingTreeCommands) WorktreeFileDiffCmdObj(node models.IFile, plain bool, cached bool) *oscommands.CmdObj { +// WorktreeFileDiffCmdObj returns a command object for diffing a file or directory +// in the working tree. When pathOverrides is non-empty, those paths are used instead of +// the node's path (used to diff only filtered/visible files within a directory). +func (self *WorkingTreeCommands) WorktreeFileDiffCmdObj(node models.IFile, plain bool, cached bool, pathOverrides []string) *oscommands.CmdObj { colorArg := self.pagerConfig.GetColorArg() if plain { colorArg = "never" @@ -270,6 +273,11 @@ func (self *WorkingTreeCommands) WorktreeFileDiffCmdObj(node models.IFile, plain useExtDiff := extDiffCmd != "" && !plain useExtDiffGitConfig := self.pagerConfig.GetUseExternalDiffGitConfig() && !plain + paths := pathOverrides + if len(paths) == 0 { + paths = []string{node.GetPath()} + } + cmdArgs := NewGitCmd("diff"). ConfigIf(useExtDiff, "diff.external="+extDiffCmd). ArgIfElse(useExtDiff || useExtDiffGitConfig, "--ext-diff", "--no-ext-diff"). @@ -282,7 +290,7 @@ func (self *WorkingTreeCommands) WorktreeFileDiffCmdObj(node models.IFile, plain ArgIf(noIndex, "--no-index"). Arg("--"). ArgIf(noIndex, "/dev/null"). - Arg(node.GetPath()). + Arg(paths...). ArgIf(prevPath != "", prevPath). Dir(self.repoPaths.worktreePath). ToArgv() @@ -293,10 +301,10 @@ func (self *WorkingTreeCommands) WorktreeFileDiffCmdObj(node models.IFile, plain // ShowFileDiff get the diff of specified from and to. Typically this will be used for a single commit so it'll be 123abc^..123abc // but when we're in diff mode it could be any 'from' to any 'to'. The reverse flag is also here thanks to diff mode. func (self *WorkingTreeCommands) ShowFileDiff(from string, to string, reverse bool, fileName string, plain bool) (string, error) { - return self.ShowFileDiffCmdObj(from, to, reverse, fileName, plain).RunWithOutput() + return self.ShowFileDiffCmdObj(from, to, reverse, []string{fileName}, plain).RunWithOutput() } -func (self *WorkingTreeCommands) ShowFileDiffCmdObj(from string, to string, reverse bool, fileName string, plain bool) *oscommands.CmdObj { +func (self *WorkingTreeCommands) ShowFileDiffCmdObj(from string, to string, reverse bool, fileNames []string, plain bool) *oscommands.CmdObj { contextSize := self.UserConfig().Git.DiffContextSize colorArg := self.pagerConfig.GetColorArg() @@ -321,7 +329,7 @@ func (self *WorkingTreeCommands) ShowFileDiffCmdObj(from string, to string, reve ArgIf(reverse, "-R"). ArgIf(!plain && self.UserConfig().Git.IgnoreWhitespaceInDiffView, "--ignore-all-space"). Arg("--"). - Arg(fileName). + Arg(fileNames...). Dir(self.repoPaths.worktreePath). ToArgv() diff --git a/pkg/gui/controllers/commits_files_controller.go b/pkg/gui/controllers/commits_files_controller.go index ab297cede..2938f1899 100644 --- a/pkg/gui/controllers/commits_files_controller.go +++ b/pkg/gui/controllers/commits_files_controller.go @@ -152,7 +152,8 @@ func (self *CommitFilesController) GetOnRenderToMain() func() { from, to := self.context().GetFromAndToForDiff() from, reverse := self.c.Modes().Diffing.GetFromAndReverseArgsForDiff(from) - cmdObj := self.c.Git().WorkingTree.ShowFileDiffCmdObj(from, to, reverse, node.GetPath(), false) + paths := self.pathsForDiff(node) + cmdObj := self.c.Git().WorkingTree.ShowFileDiffCmdObj(from, to, reverse, paths, false) task := types.NewRunPtyTask(cmdObj.GetCmd()) self.c.RenderToMainViews(types.RefreshMainOpts{ @@ -171,7 +172,7 @@ func (self *CommitFilesController) copyDiffToClipboard(path string, toastMessage from, to := self.context().GetFromAndToForDiff() from, reverse := self.c.Modes().Diffing.GetFromAndReverseArgsForDiff(from) - cmdObj := self.c.Git().WorkingTree.ShowFileDiffCmdObj(from, to, reverse, path, true) + cmdObj := self.c.Git().WorkingTree.ShowFileDiffCmdObj(from, to, reverse, []string{path}, true) diff, err := cmdObj.RunWithOutput() if err != nil { return err @@ -400,15 +401,12 @@ func (self *CommitFilesController) toggleForPatch(selectedNodes []*filetree.Comm selectedNodes = normalisedSelectedCommitFileNodes(selectedNodes) - // Collect all files to operate on. For directory nodes, this - // includes all files under the directory, not just the ones - // visible after filtering. - filesToProcess := self.collectFilesForNodes(selectedNodes) - // Find if any file in the selection is unselected or partially added - adding := lo.SomeBy(filesToProcess, func(file *models.CommitFile) bool { - fileStatus := self.c.Git().Patch.PatchBuilder.GetFileStatus(file.Path, self.context().GetRef().RefName()) - return fileStatus == patch.PART || fileStatus == patch.UNSELECTED + adding := lo.SomeBy(selectedNodes, func(node *filetree.CommitFileNode) bool { + return node.SomeFile(func(file *models.CommitFile) bool { + fileStatus := self.c.Git().Patch.PatchBuilder.GetFileStatus(file.Path, self.context().GetRef().RefName()) + return fileStatus == patch.PART || fileStatus == patch.UNSELECTED + }) }) patchOperationFunction := self.c.Git().Patch.PatchBuilder.RemoveFile @@ -417,8 +415,11 @@ func (self *CommitFilesController) toggleForPatch(selectedNodes []*filetree.Comm patchOperationFunction = self.c.Git().Patch.PatchBuilder.AddFileWhole } - for _, file := range filesToProcess { - if err := patchOperationFunction(file.Path); err != nil { + for _, node := range selectedNodes { + err := node.ForEachFile(func(file *models.CommitFile) error { + return patchOperationFunction(file.Path) + }) + if err != nil { return err } } @@ -550,6 +551,21 @@ func (self *CommitFilesController) GetOnClickFocusedMainView() func(mainViewName } } +// pathsForDiff returns the file paths to use for a diff command. When a text +// filter is active and the node is a directory, only the visible (filtered) +// file paths are returned so the diff reflects what the user sees. +func (self *CommitFilesController) pathsForDiff(node *filetree.CommitFileNode) []string { + if !node.IsFile() && self.context().IsFiltering() { + var paths []string + node.ForEachFile(func(file *models.CommitFile) error { + paths = append(paths, file.Path) + return nil + }) + return paths + } + return []string{node.GetPath()} +} + // NOTE: these functions are identical to those in files_controller.go (except for types) and // could also be cleaned up with some generics func normalisedSelectedCommitFileNodes(selectedNodes []*filetree.CommitFileNode) []*filetree.CommitFileNode { @@ -570,30 +586,6 @@ func isDescendentOfSelectedCommitFileNodes(node *filetree.CommitFileNode, select return false } -// collectFilesForNodes returns all commit files that should be operated on -// for the given nodes. For directory nodes, this includes all files under -// the directory from the full unfiltered list, so that toggling a directory -// for a patch always affects all files regardless of any active text filter. -func (self *CommitFilesController) collectFilesForNodes(nodes []*filetree.CommitFileNode) []*models.CommitFile { - allCommitFiles := self.context().GetAllFiles() - result := []*models.CommitFile{} - - for _, node := range nodes { - if node.IsFile() { - result = append(result, node.File) - } else { - dirPath := node.GetPath() - for _, file := range allCommitFiles { - if dirPath == "" || strings.HasPrefix(file.Path, dirPath+"/") { - result = append(result, file) - } - } - } - } - - return result -} - func (self *CommitFilesController) isInTreeMode() *types.DisabledReason { if !self.context().CommitFileTreeViewModel.InTreeMode() { return &types.DisabledReason{Text: self.c.Tr.DisabledInFlatView} diff --git a/pkg/gui/controllers/files_controller.go b/pkg/gui/controllers/files_controller.go index 5b6cbcef2..0ad31b840 100644 --- a/pkg/gui/controllers/files_controller.go +++ b/pkg/gui/controllers/files_controller.go @@ -294,7 +294,8 @@ func (self *FilesController) GetOnRenderToMain() func() { split := self.c.UserConfig().Gui.SplitDiff == "always" || (node.GetHasUnstagedChanges() && node.GetHasStagedChanges()) mainShowsStaged := !split && node.GetHasStagedChanges() - cmdObj := self.c.Git().WorkingTree.WorktreeFileDiffCmdObj(node, false, mainShowsStaged) + pathOverrides := self.pathOverridesForDiff(node) + cmdObj := self.c.Git().WorkingTree.WorktreeFileDiffCmdObj(node, false, mainShowsStaged, pathOverrides) title := self.c.Tr.UnstagedChanges if mainShowsStaged { title = self.c.Tr.StagedChanges @@ -309,7 +310,7 @@ func (self *FilesController) GetOnRenderToMain() func() { } if split { - cmdObj := self.c.Git().WorkingTree.WorktreeFileDiffCmdObj(node, false, true) + cmdObj := self.c.Git().WorkingTree.WorktreeFileDiffCmdObj(node, false, true, pathOverrides) title := self.c.Tr.StagedChanges if mainShowsStaged { @@ -434,7 +435,19 @@ func (self *FilesController) pressWithLock(selectedNodes []*filetree.FileNode) e } } + // When filtering, expand directory nodes to individual visible file paths + // so that only filtered files are staged/unstaged. toPaths := func(nodes []*filetree.FileNode) []string { + if self.context().IsFiltering() { + var paths []string + for _, node := range nodes { + node.ForEachFile(func(file *models.File) error { + paths = append(paths, file.Path) + return nil + }) + } + return paths + } return lo.Map(nodes, func(node *filetree.FileNode, _ int) string { return node.GetPath() }) @@ -469,22 +482,29 @@ func (self *FilesController) pressWithLock(selectedNodes []*filetree.FileNode) e return err } - // need to partition the paths into tracked and untracked (where we assume directories are tracked). Then we'll run the commands separately. - trackedNodes, untrackedNodes := utils.Partition(selectedNodes, func(node *filetree.FileNode) bool { - // We treat all directories as tracked. I'm not actually sure why we do this but - // it's been the existing behaviour for a while and nobody has complained - return !node.IsFile() || node.GetIsTracked() - }) - - if len(untrackedNodes) > 0 { - if err := self.c.Git().WorkingTree.UnstageUntrackedFiles(toPaths(untrackedNodes)); err != nil { + if self.context().IsFiltering() { + // When filtering, only unstage visible files + if err := self.unstageFilteredFiles(selectedNodes); err != nil { return err } - } + } else { + // need to partition the paths into tracked and untracked (where we assume directories are tracked). Then we'll run the commands separately. + trackedNodes, untrackedNodes := utils.Partition(selectedNodes, func(node *filetree.FileNode) bool { + // We treat all directories as tracked. I'm not actually sure why we do this but + // it's been the existing behaviour for a while and nobody has complained + return !node.IsFile() || node.GetIsTracked() + }) - if len(trackedNodes) > 0 { - if err := self.c.Git().WorkingTree.UnstageTrackedFiles(toPaths(trackedNodes)); err != nil { - return err + if len(untrackedNodes) > 0 { + if err := self.c.Git().WorkingTree.UnstageUntrackedFiles(toPaths(untrackedNodes)); err != nil { + return err + } + } + + if len(trackedNodes) > 0 { + if err := self.c.Git().WorkingTree.UnstageTrackedFiles(toPaths(trackedNodes)); err != nil { + return err + } } } } @@ -503,6 +523,48 @@ func (self *FilesController) press(nodes []*filetree.FileNode) error { return nil } +// pathOverridesForDiff returns file paths to override the node's path in diff +// commands when a text filter is active and the node is a directory. This +// ensures the diff only shows filtered/visible files. +func (self *FilesController) pathOverridesForDiff(node *filetree.FileNode) []string { + if !node.IsFile() && self.context().IsFiltering() { + var paths []string + node.ForEachFile(func(file *models.File) error { + paths = append(paths, file.Path) + return nil + }) + return paths + } + return nil +} + +// unstageFilteredFiles unstages only the visible (filtered) files from the +// given nodes, correctly partitioning by tracked/untracked. +func (self *FilesController) unstageFilteredFiles(nodes []*filetree.FileNode) error { + var trackedPaths, untrackedPaths []string + for _, node := range nodes { + node.ForEachFile(func(file *models.File) error { + if file.Tracked || file.HasStagedChanges { + trackedPaths = append(trackedPaths, file.Path) + } else { + untrackedPaths = append(untrackedPaths, file.Path) + } + return nil + }) + } + if len(untrackedPaths) > 0 { + if err := self.c.Git().WorkingTree.UnstageUntrackedFiles(untrackedPaths); err != nil { + return err + } + } + if len(trackedPaths) > 0 { + if err := self.c.Git().WorkingTree.UnstageTrackedFiles(trackedPaths); err != nil { + return err + } + } + return nil +} + func (self *FilesController) Context() types.Context { return self.context() } @@ -648,9 +710,21 @@ func (self *FilesController) toggleStagedAllWithLock() error { return err } - onlyTrackedFiles := self.context().GetStatusFilter() == filetree.DisplayTracked - if err := self.c.Git().WorkingTree.StageAll(onlyTrackedFiles); err != nil { - return err + if self.context().IsFiltering() { + // When filtering, only stage visible files + var paths []string + root.ForEachFile(func(file *models.File) error { + paths = append(paths, file.Path) + return nil + }) + if err := self.c.Git().WorkingTree.StageFiles(paths, nil); err != nil { + return err + } + } else { + onlyTrackedFiles := self.context().GetStatusFilter() == filetree.DisplayTracked + if err := self.c.Git().WorkingTree.StageAll(onlyTrackedFiles); err != nil { + return err + } } } else { self.c.LogAction(self.c.Tr.Actions.UnstageAllFiles) @@ -659,8 +733,15 @@ func (self *FilesController) toggleStagedAllWithLock() error { return err } - if err := self.c.Git().WorkingTree.UnstageAll(); err != nil { - return err + if self.context().IsFiltering() { + // When filtering, only unstage visible files + if err := self.unstageFilteredFiles([]*filetree.FileNode{root}); err != nil { + return err + } + } else { + if err := self.c.Git().WorkingTree.UnstageAll(); err != nil { + return err + } } } diff --git a/pkg/gui/controllers/submodules_controller.go b/pkg/gui/controllers/submodules_controller.go index a4a1cb796..c0f52bed1 100644 --- a/pkg/gui/controllers/submodules_controller.go +++ b/pkg/gui/controllers/submodules_controller.go @@ -125,7 +125,7 @@ func (self *SubmodulesController) GetOnRenderToMain() func() { if file == nil { task = types.NewRenderStringTask(prefix) } else { - cmdObj := self.c.Git().WorkingTree.WorktreeFileDiffCmdObj(file, false, !file.HasUnstagedChanges && file.HasStagedChanges) + cmdObj := self.c.Git().WorkingTree.WorktreeFileDiffCmdObj(file, false, !file.HasUnstagedChanges && file.HasStagedChanges, nil) task = types.NewRunCommandTaskWithPrefix(cmdObj.GetCmd(), prefix) } } diff --git a/pkg/integration/tests/filter_and_search/filter_commit_files_toggle_directory.go b/pkg/integration/tests/filter_and_search/filter_commit_files_toggle_directory.go new file mode 100644 index 000000000..0cabd8257 --- /dev/null +++ b/pkg/integration/tests/filter_and_search/filter_commit_files_toggle_directory.go @@ -0,0 +1,61 @@ +package filter_and_search + +import ( + "github.com/jesseduffield/lazygit/pkg/config" + . "github.com/jesseduffield/lazygit/pkg/integration/components" +) + +var FilterCommitFilesToggleDirectory = NewIntegrationTest(NewIntegrationTestArgs{ + Description: "Toggle a filtered directory for a custom patch only adds visible files", + ExtraCmdArgs: []string{}, + Skip: false, + SetupConfig: func(config *config.AppConfig) {}, + SetupRepo: func(shell *Shell) { + shell.CreateDir("dir1") + shell.CreateFileAndAdd("dir1/apple-grape", "apple-grape content\n") + shell.CreateFileAndAdd("dir1/apple-orange", "apple-orange content\n") + shell.CreateFileAndAdd("dir1/grape-orange", "grape-orange content\n") + shell.Commit("first commit") + }, + Run: func(t *TestDriver, keys config.KeybindingConfig) { + t.Views().Commits(). + Focus(). + Lines( + Contains("first commit").IsSelected(), + ). + PressEnter() + + t.Views().CommitFiles(). + IsFocused(). + Lines( + Contains("dir1").IsSelected(), + Contains("apple-grape"), + Contains("apple-orange"), + Contains("grape-orange"), + ). + // Filter to show only "apple" files (staying in tree view) + FilterOrSearch("apple"). + Lines( + // first item is always selected after filtering + Contains("dir1").IsSelected(), + Contains("apple-grape"), + Contains("apple-orange"), + ). + // dir1 is already selected; toggle for patch + PressPrimaryAction(). + Lines( + Contains("dir1").IsSelected(), + Contains("● apple-grape"), + Contains("● apple-orange"), + ) + + t.Views().Information().Content(Contains("Building patch")) + + // Verify only the filtered files are in the patch (not grape-orange) + t.Views().Secondary().Content( + Contains("apple-grape"). + Contains("apple-orange"). + DoesNotContain("grape-orange"), + ) + }, +}) diff --git a/pkg/integration/tests/filter_and_search/filter_files_stage_all.go b/pkg/integration/tests/filter_and_search/filter_files_stage_all.go new file mode 100644 index 000000000..ccb6248c3 --- /dev/null +++ b/pkg/integration/tests/filter_and_search/filter_files_stage_all.go @@ -0,0 +1,50 @@ +package filter_and_search + +import ( + "github.com/jesseduffield/lazygit/pkg/config" + . "github.com/jesseduffield/lazygit/pkg/integration/components" +) + +var FilterFilesStageAll = NewIntegrationTest(NewIntegrationTestArgs{ + Description: "Toggle all staging with a filter only stages visible files", + ExtraCmdArgs: []string{}, + Skip: false, + SetupConfig: func(config *config.AppConfig) {}, + SetupRepo: func(shell *Shell) { + shell.CreateDir("dir1") + shell.CreateFile("dir1/apple-grape", "apple-grape content\n") + shell.CreateFile("dir1/apple-orange", "apple-orange content\n") + shell.CreateFile("dir1/grape-orange", "grape-orange content\n") + }, + Run: func(t *TestDriver, keys config.KeybindingConfig) { + t.Views().Files(). + Focus(). + Lines( + Contains("dir1").IsSelected(), + Contains("apple-grape"), + Contains("apple-orange"), + Contains("grape-orange"), + ). + // Filter to show only "apple" files + FilterOrSearch("apple"). + Lines( + // first item is always selected after filtering + Contains("dir1").IsSelected(), + Contains("apple-grape"), + Contains("apple-orange"), + ). + // Stage all visible files + Press(keys.Files.ToggleStagedAll). + // Clear the filter and verify only apple files are staged + PressEscape() + + t.Views().Files(). + IsFocused(). + Lines( + Contains("dir1").IsSelected(), + Contains("A apple-grape"), + Contains("A apple-orange"), + Contains("?? grape-orange"), + ) + }, +}) diff --git a/pkg/integration/tests/filter_and_search/filter_files_stage_directory.go b/pkg/integration/tests/filter_and_search/filter_files_stage_directory.go new file mode 100644 index 000000000..46e55b25f --- /dev/null +++ b/pkg/integration/tests/filter_and_search/filter_files_stage_directory.go @@ -0,0 +1,50 @@ +package filter_and_search + +import ( + "github.com/jesseduffield/lazygit/pkg/config" + . "github.com/jesseduffield/lazygit/pkg/integration/components" +) + +var FilterFilesStageDirectory = NewIntegrationTest(NewIntegrationTestArgs{ + Description: "Staging a filtered directory only stages visible files", + ExtraCmdArgs: []string{}, + Skip: false, + SetupConfig: func(config *config.AppConfig) {}, + SetupRepo: func(shell *Shell) { + shell.CreateDir("dir1") + shell.CreateFile("dir1/apple-grape", "apple-grape content\n") + shell.CreateFile("dir1/apple-orange", "apple-orange content\n") + shell.CreateFile("dir1/grape-orange", "grape-orange content\n") + }, + Run: func(t *TestDriver, keys config.KeybindingConfig) { + t.Views().Files(). + Focus(). + Lines( + Contains("dir1").IsSelected(), + Contains("apple-grape"), + Contains("apple-orange"), + Contains("grape-orange"), + ). + // Filter to show only "apple" files + FilterOrSearch("apple"). + Lines( + // first item is always selected after filtering + Contains("dir1").IsSelected(), + Contains("apple-grape"), + Contains("apple-orange"), + ). + // dir1 is already selected; stage it + PressPrimaryAction(). + // Clear the filter to see all files and verify only apple files are staged + PressEscape() + + t.Views().Files(). + IsFocused(). + Lines( + Contains("dir1"), + Contains("A apple-grape"), + Contains("A apple-orange"), + Contains("?? grape-orange"), + ) + }, +}) diff --git a/pkg/integration/tests/patch_building/toggle_directory.go b/pkg/integration/tests/patch_building/toggle_directory.go new file mode 100644 index 000000000..c9161fe69 --- /dev/null +++ b/pkg/integration/tests/patch_building/toggle_directory.go @@ -0,0 +1,64 @@ +package patch_building + +import ( + "github.com/jesseduffield/lazygit/pkg/config" + . "github.com/jesseduffield/lazygit/pkg/integration/components" +) + +var ToggleDirectory = NewIntegrationTest(NewIntegrationTestArgs{ + Description: "Toggle a directory for a custom patch", + ExtraCmdArgs: []string{}, + Skip: false, + SetupConfig: func(config *config.AppConfig) {}, + SetupRepo: func(shell *Shell) { + shell.CreateDir("dir1") + shell.CreateFileAndAdd("dir1/file1", "file1 content\n") + shell.CreateFileAndAdd("dir1/file2", "file2 content\n") + shell.CreateFileAndAdd("dir1/file3", "file3 content\n") + shell.CreateFileAndAdd("other-file", "other content\n") + shell.Commit("first commit") + }, + Run: func(t *TestDriver, keys config.KeybindingConfig) { + t.Views().Commits(). + Focus(). + Lines( + Contains("first commit").IsSelected(), + ). + PressEnter() + + t.Views().CommitFiles(). + IsFocused(). + Lines( + Equals("▼ /").IsSelected(), + Equals(" ▼ dir1"), + Equals(" A file1"), + Equals(" A file2"), + Equals(" A file3"), + Equals(" A other-file"), + ). + NavigateToLine(Contains("dir1")). + PressPrimaryAction(). + Lines( + Equals("▼ /"), + Equals(" ▼ dir1").IsSelected(), + Equals(" ● file1"), + Equals(" ● file2"), + Equals(" ● file3"), + Equals(" A other-file"), + ) + + t.Views().Information().Content(Contains("Building patch")) + + // Toggle the directory again to remove all files from the patch + t.Views().CommitFiles(). + PressPrimaryAction(). + Lines( + Equals("▼ /"), + Equals(" ▼ dir1").IsSelected(), + Equals(" A file1"), + Equals(" A file2"), + Equals(" A file3"), + Equals(" A other-file"), + ) + }, +}) diff --git a/pkg/integration/tests/test_list.go b/pkg/integration/tests/test_list.go index d098d1185..da38b86cd 100644 --- a/pkg/integration/tests/test_list.go +++ b/pkg/integration/tests/test_list.go @@ -232,7 +232,10 @@ var tests = []*components.IntegrationTest{ file.StageRangeSelect, filter_and_search.FilterByFileStatus, filter_and_search.FilterCommitFiles, + filter_and_search.FilterCommitFilesToggleDirectory, filter_and_search.FilterFiles, + filter_and_search.FilterFilesStageAll, + filter_and_search.FilterFilesStageDirectory, filter_and_search.FilterFuzzy, filter_and_search.FilterMenu, filter_and_search.FilterMenuByKeybinding, @@ -354,6 +357,7 @@ var tests = []*components.IntegrationTest{ patch_building.SelectAllFiles, patch_building.SpecificSelection, patch_building.StartNewPatch, + patch_building.ToggleDirectory, patch_building.ToggleRange, reflog.Checkout, reflog.CherryPick,