mirror of
https://github.com/jesseduffield/lazygit.git
synced 2026-08-24 10:13:41 -05:00
Only apply directory-level actions to visible files
This commit is contained in:
@@ -253,11 +253,14 @@ func (self *WorkingTreeCommands) Exclude(filename string) error {
|
|||||||
// WorktreeFileDiff returns the diff of a file
|
// WorktreeFileDiff returns the diff of a file
|
||||||
func (self *WorkingTreeCommands) WorktreeFileDiff(file *models.File, plain bool, cached bool) string {
|
func (self *WorkingTreeCommands) WorktreeFileDiff(file *models.File, plain bool, cached bool) string {
|
||||||
// for now we assume an error means the file was deleted
|
// 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
|
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()
|
colorArg := self.pagerConfig.GetColorArg()
|
||||||
if plain {
|
if plain {
|
||||||
colorArg = "never"
|
colorArg = "never"
|
||||||
@@ -270,6 +273,11 @@ func (self *WorkingTreeCommands) WorktreeFileDiffCmdObj(node models.IFile, plain
|
|||||||
useExtDiff := extDiffCmd != "" && !plain
|
useExtDiff := extDiffCmd != "" && !plain
|
||||||
useExtDiffGitConfig := self.pagerConfig.GetUseExternalDiffGitConfig() && !plain
|
useExtDiffGitConfig := self.pagerConfig.GetUseExternalDiffGitConfig() && !plain
|
||||||
|
|
||||||
|
paths := pathOverrides
|
||||||
|
if len(paths) == 0 {
|
||||||
|
paths = []string{node.GetPath()}
|
||||||
|
}
|
||||||
|
|
||||||
cmdArgs := NewGitCmd("diff").
|
cmdArgs := NewGitCmd("diff").
|
||||||
ConfigIf(useExtDiff, "diff.external="+extDiffCmd).
|
ConfigIf(useExtDiff, "diff.external="+extDiffCmd).
|
||||||
ArgIfElse(useExtDiff || useExtDiffGitConfig, "--ext-diff", "--no-ext-diff").
|
ArgIfElse(useExtDiff || useExtDiffGitConfig, "--ext-diff", "--no-ext-diff").
|
||||||
@@ -282,7 +290,7 @@ func (self *WorkingTreeCommands) WorktreeFileDiffCmdObj(node models.IFile, plain
|
|||||||
ArgIf(noIndex, "--no-index").
|
ArgIf(noIndex, "--no-index").
|
||||||
Arg("--").
|
Arg("--").
|
||||||
ArgIf(noIndex, "/dev/null").
|
ArgIf(noIndex, "/dev/null").
|
||||||
Arg(node.GetPath()).
|
Arg(paths...).
|
||||||
ArgIf(prevPath != "", prevPath).
|
ArgIf(prevPath != "", prevPath).
|
||||||
Dir(self.repoPaths.worktreePath).
|
Dir(self.repoPaths.worktreePath).
|
||||||
ToArgv()
|
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
|
// 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.
|
// 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) {
|
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
|
contextSize := self.UserConfig().Git.DiffContextSize
|
||||||
|
|
||||||
colorArg := self.pagerConfig.GetColorArg()
|
colorArg := self.pagerConfig.GetColorArg()
|
||||||
@@ -321,7 +329,7 @@ func (self *WorkingTreeCommands) ShowFileDiffCmdObj(from string, to string, reve
|
|||||||
ArgIf(reverse, "-R").
|
ArgIf(reverse, "-R").
|
||||||
ArgIf(!plain && self.UserConfig().Git.IgnoreWhitespaceInDiffView, "--ignore-all-space").
|
ArgIf(!plain && self.UserConfig().Git.IgnoreWhitespaceInDiffView, "--ignore-all-space").
|
||||||
Arg("--").
|
Arg("--").
|
||||||
Arg(fileName).
|
Arg(fileNames...).
|
||||||
Dir(self.repoPaths.worktreePath).
|
Dir(self.repoPaths.worktreePath).
|
||||||
ToArgv()
|
ToArgv()
|
||||||
|
|
||||||
|
|||||||
@@ -152,7 +152,8 @@ func (self *CommitFilesController) GetOnRenderToMain() func() {
|
|||||||
from, to := self.context().GetFromAndToForDiff()
|
from, to := self.context().GetFromAndToForDiff()
|
||||||
from, reverse := self.c.Modes().Diffing.GetFromAndReverseArgsForDiff(from)
|
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())
|
task := types.NewRunPtyTask(cmdObj.GetCmd())
|
||||||
|
|
||||||
self.c.RenderToMainViews(types.RefreshMainOpts{
|
self.c.RenderToMainViews(types.RefreshMainOpts{
|
||||||
@@ -171,7 +172,7 @@ func (self *CommitFilesController) copyDiffToClipboard(path string, toastMessage
|
|||||||
from, to := self.context().GetFromAndToForDiff()
|
from, to := self.context().GetFromAndToForDiff()
|
||||||
from, reverse := self.c.Modes().Diffing.GetFromAndReverseArgsForDiff(from)
|
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()
|
diff, err := cmdObj.RunWithOutput()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
@@ -400,15 +401,12 @@ func (self *CommitFilesController) toggleForPatch(selectedNodes []*filetree.Comm
|
|||||||
|
|
||||||
selectedNodes = normalisedSelectedCommitFileNodes(selectedNodes)
|
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
|
// Find if any file in the selection is unselected or partially added
|
||||||
adding := lo.SomeBy(filesToProcess, func(file *models.CommitFile) bool {
|
adding := lo.SomeBy(selectedNodes, func(node *filetree.CommitFileNode) bool {
|
||||||
fileStatus := self.c.Git().Patch.PatchBuilder.GetFileStatus(file.Path, self.context().GetRef().RefName())
|
return node.SomeFile(func(file *models.CommitFile) bool {
|
||||||
return fileStatus == patch.PART || fileStatus == patch.UNSELECTED
|
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
|
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
|
patchOperationFunction = self.c.Git().Patch.PatchBuilder.AddFileWhole
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, file := range filesToProcess {
|
for _, node := range selectedNodes {
|
||||||
if err := patchOperationFunction(file.Path); err != nil {
|
err := node.ForEachFile(func(file *models.CommitFile) error {
|
||||||
|
return patchOperationFunction(file.Path)
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
return err
|
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
|
// NOTE: these functions are identical to those in files_controller.go (except for types) and
|
||||||
// could also be cleaned up with some generics
|
// could also be cleaned up with some generics
|
||||||
func normalisedSelectedCommitFileNodes(selectedNodes []*filetree.CommitFileNode) []*filetree.CommitFileNode {
|
func normalisedSelectedCommitFileNodes(selectedNodes []*filetree.CommitFileNode) []*filetree.CommitFileNode {
|
||||||
@@ -570,30 +586,6 @@ func isDescendentOfSelectedCommitFileNodes(node *filetree.CommitFileNode, select
|
|||||||
return false
|
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 {
|
func (self *CommitFilesController) isInTreeMode() *types.DisabledReason {
|
||||||
if !self.context().CommitFileTreeViewModel.InTreeMode() {
|
if !self.context().CommitFileTreeViewModel.InTreeMode() {
|
||||||
return &types.DisabledReason{Text: self.c.Tr.DisabledInFlatView}
|
return &types.DisabledReason{Text: self.c.Tr.DisabledInFlatView}
|
||||||
|
|||||||
@@ -294,7 +294,8 @@ func (self *FilesController) GetOnRenderToMain() func() {
|
|||||||
split := self.c.UserConfig().Gui.SplitDiff == "always" || (node.GetHasUnstagedChanges() && node.GetHasStagedChanges())
|
split := self.c.UserConfig().Gui.SplitDiff == "always" || (node.GetHasUnstagedChanges() && node.GetHasStagedChanges())
|
||||||
mainShowsStaged := !split && 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
|
title := self.c.Tr.UnstagedChanges
|
||||||
if mainShowsStaged {
|
if mainShowsStaged {
|
||||||
title = self.c.Tr.StagedChanges
|
title = self.c.Tr.StagedChanges
|
||||||
@@ -309,7 +310,7 @@ func (self *FilesController) GetOnRenderToMain() func() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if split {
|
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
|
title := self.c.Tr.StagedChanges
|
||||||
if mainShowsStaged {
|
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 {
|
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 lo.Map(nodes, func(node *filetree.FileNode, _ int) string {
|
||||||
return node.GetPath()
|
return node.GetPath()
|
||||||
})
|
})
|
||||||
@@ -469,22 +482,29 @@ func (self *FilesController) pressWithLock(selectedNodes []*filetree.FileNode) e
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
// need to partition the paths into tracked and untracked (where we assume directories are tracked). Then we'll run the commands separately.
|
if self.context().IsFiltering() {
|
||||||
trackedNodes, untrackedNodes := utils.Partition(selectedNodes, func(node *filetree.FileNode) bool {
|
// When filtering, only unstage visible files
|
||||||
// We treat all directories as tracked. I'm not actually sure why we do this but
|
if err := self.unstageFilteredFiles(selectedNodes); err != nil {
|
||||||
// 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 {
|
|
||||||
return err
|
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 len(untrackedNodes) > 0 {
|
||||||
if err := self.c.Git().WorkingTree.UnstageTrackedFiles(toPaths(trackedNodes)); err != nil {
|
if err := self.c.Git().WorkingTree.UnstageUntrackedFiles(toPaths(untrackedNodes)); err != nil {
|
||||||
return err
|
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
|
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 {
|
func (self *FilesController) Context() types.Context {
|
||||||
return self.context()
|
return self.context()
|
||||||
}
|
}
|
||||||
@@ -648,9 +710,21 @@ func (self *FilesController) toggleStagedAllWithLock() error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
onlyTrackedFiles := self.context().GetStatusFilter() == filetree.DisplayTracked
|
if self.context().IsFiltering() {
|
||||||
if err := self.c.Git().WorkingTree.StageAll(onlyTrackedFiles); err != nil {
|
// When filtering, only stage visible files
|
||||||
return err
|
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 {
|
} else {
|
||||||
self.c.LogAction(self.c.Tr.Actions.UnstageAllFiles)
|
self.c.LogAction(self.c.Tr.Actions.UnstageAllFiles)
|
||||||
@@ -659,8 +733,15 @@ func (self *FilesController) toggleStagedAllWithLock() error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := self.c.Git().WorkingTree.UnstageAll(); err != nil {
|
if self.context().IsFiltering() {
|
||||||
return err
|
// 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
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -125,7 +125,7 @@ func (self *SubmodulesController) GetOnRenderToMain() func() {
|
|||||||
if file == nil {
|
if file == nil {
|
||||||
task = types.NewRenderStringTask(prefix)
|
task = types.NewRenderStringTask(prefix)
|
||||||
} else {
|
} 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)
|
task = types.NewRunCommandTaskWithPrefix(cmdObj.GetCmd(), prefix)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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"),
|
||||||
|
)
|
||||||
|
},
|
||||||
|
})
|
||||||
@@ -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"),
|
||||||
|
)
|
||||||
|
},
|
||||||
|
})
|
||||||
@@ -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"),
|
||||||
|
)
|
||||||
|
},
|
||||||
|
})
|
||||||
@@ -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"),
|
||||||
|
)
|
||||||
|
},
|
||||||
|
})
|
||||||
@@ -232,7 +232,10 @@ var tests = []*components.IntegrationTest{
|
|||||||
file.StageRangeSelect,
|
file.StageRangeSelect,
|
||||||
filter_and_search.FilterByFileStatus,
|
filter_and_search.FilterByFileStatus,
|
||||||
filter_and_search.FilterCommitFiles,
|
filter_and_search.FilterCommitFiles,
|
||||||
|
filter_and_search.FilterCommitFilesToggleDirectory,
|
||||||
filter_and_search.FilterFiles,
|
filter_and_search.FilterFiles,
|
||||||
|
filter_and_search.FilterFilesStageAll,
|
||||||
|
filter_and_search.FilterFilesStageDirectory,
|
||||||
filter_and_search.FilterFuzzy,
|
filter_and_search.FilterFuzzy,
|
||||||
filter_and_search.FilterMenu,
|
filter_and_search.FilterMenu,
|
||||||
filter_and_search.FilterMenuByKeybinding,
|
filter_and_search.FilterMenuByKeybinding,
|
||||||
@@ -354,6 +357,7 @@ var tests = []*components.IntegrationTest{
|
|||||||
patch_building.SelectAllFiles,
|
patch_building.SelectAllFiles,
|
||||||
patch_building.SpecificSelection,
|
patch_building.SpecificSelection,
|
||||||
patch_building.StartNewPatch,
|
patch_building.StartNewPatch,
|
||||||
|
patch_building.ToggleDirectory,
|
||||||
patch_building.ToggleRange,
|
patch_building.ToggleRange,
|
||||||
reflog.Checkout,
|
reflog.Checkout,
|
||||||
reflog.CherryPick,
|
reflog.CherryPick,
|
||||||
|
|||||||
Reference in New Issue
Block a user