diff --git a/pkg/commands/loading_commit_files.go b/pkg/commands/loading_commit_files.go index 1b5dc02d4..d0f8406b8 100644 --- a/pkg/commands/loading_commit_files.go +++ b/pkg/commands/loading_commit_files.go @@ -4,11 +4,10 @@ import ( "strings" "github.com/jesseduffield/lazygit/pkg/commands/models" - "github.com/jesseduffield/lazygit/pkg/commands/patch" ) // GetFilesInDiff get the specified commit files -func (c *GitCommand) GetFilesInDiff(from string, to string, reverse bool, patchManager *patch.PatchManager) ([]*models.CommitFile, error) { +func (c *GitCommand) GetFilesInDiff(from string, to string, reverse bool) ([]*models.CommitFile, error) { reverseFlag := "" if reverse { reverseFlag = " -R " @@ -19,11 +18,11 @@ func (c *GitCommand) GetFilesInDiff(from string, to string, reverse bool, patchM return nil, err } - return c.getCommitFilesFromFilenames(filenames, to, patchManager), nil + return c.getCommitFilesFromFilenames(filenames), nil } // filenames string is something like "file1\nfile2\nfile3" -func (c *GitCommand) getCommitFilesFromFilenames(filenames string, parent string, patchManager *patch.PatchManager) []*models.CommitFile { +func (c *GitCommand) getCommitFilesFromFilenames(filenames string) []*models.CommitFile { commitFiles := make([]*models.CommitFile, 0) lines := strings.Split(strings.TrimRight(filenames, "\x00"), "\x00") @@ -32,15 +31,10 @@ func (c *GitCommand) getCommitFilesFromFilenames(filenames string, parent string // typical result looks like 'A my_file' meaning my_file was added changeStatus := lines[i] name := lines[i+1] - status := patch.UNSELECTED - if patchManager != nil && patchManager.To == parent { - status = patchManager.GetFileStatus(name) - } commitFiles = append(commitFiles, &models.CommitFile{ Name: name, ChangeStatus: changeStatus, - PatchStatus: status, }) } diff --git a/pkg/commands/models/commit_file.go b/pkg/commands/models/commit_file.go index 57a766a46..b6028e221 100644 --- a/pkg/commands/models/commit_file.go +++ b/pkg/commands/models/commit_file.go @@ -4,9 +4,6 @@ package models type CommitFile struct { Name string - // PatchStatus tells us whether the file has been wholly or partially added to a patch. We might want to pull this logic up into the gui package and make it a map like we do with cherry picked commits - PatchStatus int // one of 'WHOLE' 'PART' 'NONE' - ChangeStatus string // e.g. 'A' for added or 'M' for modified. This is based on the result from git diff --name-status } diff --git a/pkg/commands/patch/patch_manager.go b/pkg/commands/patch/patch_manager.go index 2c08da67a..ccb2a1081 100644 --- a/pkg/commands/patch/patch_manager.go +++ b/pkg/commands/patch/patch_manager.go @@ -1,7 +1,6 @@ package patch import ( - "errors" "sort" "strings" @@ -9,9 +8,11 @@ import ( "github.com/sirupsen/logrus" ) +type PatchStatus int + const ( // UNSELECTED is for when the commit file has not been added to the patch in any way - UNSELECTED = iota + UNSELECTED PatchStatus = iota // WHOLE is for when you want to add the whole diff of a file to the patch, // including e.g. if it was deleted WHOLE @@ -20,7 +21,7 @@ const ( ) type fileInfo struct { - mode int // one of WHOLE/PART + mode PatchStatus includedLineIndices []int diff string } @@ -81,20 +82,25 @@ func (p *PatchManager) removeFile(info *fileInfo) { info.includedLineIndices = nil } -func (p *PatchManager) ToggleFileWhole(filename string) error { +func (p *PatchManager) AddFileWhole(filename string) error { info, err := p.getFileInfo(filename) if err != nil { return err } - switch info.mode { - case UNSELECTED, PART: - p.addFileWhole(info) - case WHOLE: - p.removeFile(info) - default: - return errors.New("unknown file mode") + + p.addFileWhole(info) + + return nil +} + +func (p *PatchManager) RemoveFile(filename string) error { + info, err := p.getFileInfo(filename) + if err != nil { + return err } + p.removeFile(info) + return nil } @@ -216,7 +222,7 @@ func (p *PatchManager) RenderAggregatedPatchColored(plain bool) string { return result } -func (p *PatchManager) GetFileStatus(filename string) int { +func (p *PatchManager) GetFileStatus(filename string) PatchStatus { info, ok := p.fileInfoMap[filename] if !ok { return UNSELECTED diff --git a/pkg/gui/commit_files_panel.go b/pkg/gui/commit_files_panel.go index cd4019dd5..6a7baad2f 100644 --- a/pkg/gui/commit_files_panel.go +++ b/pkg/gui/commit_files_panel.go @@ -3,6 +3,7 @@ package gui import ( "github.com/jesseduffield/gocui" "github.com/jesseduffield/lazygit/pkg/commands/models" + "github.com/jesseduffield/lazygit/pkg/commands/patch" "github.com/jesseduffield/lazygit/pkg/gui/filetree" ) @@ -104,7 +105,7 @@ func (gui *Gui) refreshCommitFilesView() error { to := gui.State.Panels.CommitFiles.refName from, reverse := gui.getFromAndReverseArgsForDiff(to) - files, err := gui.GitCommand.GetFilesInDiff(from, to, reverse, gui.GitCommand.PatchManager) + files, err := gui.GitCommand.GetFilesInDiff(from, to, reverse) if err != nil { return gui.surfaceError(err) } @@ -137,8 +138,6 @@ func (gui *Gui) handleToggleFileForPatch(g *gocui.Gui, v *gocui.View) error { return nil } - // TODO: if file is nil, toggle all leaves underneath on/off - toggleTheFile := func() error { if !gui.GitCommand.PatchManager.Active() { if err := gui.startPatchManager(); err != nil { @@ -146,15 +145,29 @@ func (gui *Gui) handleToggleFileForPatch(g *gocui.Gui, v *gocui.View) error { } } - if err := gui.GitCommand.PatchManager.ToggleFileWhole(node.GetPath()); err != nil { - return err + // if there is any file that hasn't been fully added we'll fully add everything, + // otherwise we'll remove everything + adding := node.AnyFile(func(file *models.CommitFile) bool { + return gui.GitCommand.PatchManager.GetFileStatus(file.Name) != patch.WHOLE + }) + + err := node.ForEachFile(func(file *models.CommitFile) error { + if adding { + return gui.GitCommand.PatchManager.AddFileWhole(file.Name) + } else { + return gui.GitCommand.PatchManager.RemoveFile(file.Name) + } + }) + + if err != nil { + return gui.surfaceError(err) } if gui.GitCommand.PatchManager.IsEmpty() { gui.GitCommand.PatchManager.Reset() } - return gui.refreshCommitFilesView() + return gui.postRefreshUpdate(gui.Contexts.CommitFiles.Context) } if gui.GitCommand.PatchManager.Active() && gui.GitCommand.PatchManager.To != gui.State.CommitFileChangeManager.GetParent() { diff --git a/pkg/gui/filetree/commit_file_change_manager.go b/pkg/gui/filetree/commit_file_change_manager.go index d6b70a2bd..669235ef6 100644 --- a/pkg/gui/filetree/commit_file_change_manager.go +++ b/pkg/gui/filetree/commit_file_change_manager.go @@ -2,6 +2,7 @@ package filetree import ( "github.com/jesseduffield/lazygit/pkg/commands/models" + "github.com/jesseduffield/lazygit/pkg/commands/patch" "github.com/jesseduffield/lazygit/pkg/gui/presentation" "github.com/sirupsen/logrus" ) @@ -87,9 +88,9 @@ func (m *CommitFileChangeManager) ToggleCollapsed(path string) { m.collapsedPaths.ToggleCollapsed(path) } -func (m *CommitFileChangeManager) Render(diffName string) []string { +func (m *CommitFileChangeManager) Render(diffName string, patchManager *patch.PatchManager) []string { return renderAux(m.tree, m.collapsedPaths, "", -1, func(n INode, depth int) string { castN := n.(*CommitFileChangeNode) - return presentation.GetCommitFileLine(castN.NameAtDepth(depth), diffName, castN.File) + return presentation.GetCommitFileLine(castN.NameAtDepth(depth), diffName, castN.File, patchManager) }) } diff --git a/pkg/gui/filetree/commit_file_change_node.go b/pkg/gui/filetree/commit_file_change_node.go index c1f99d937..011c1d954 100644 --- a/pkg/gui/filetree/commit_file_change_node.go +++ b/pkg/gui/filetree/commit_file_change_node.go @@ -81,6 +81,21 @@ func (s *CommitFileChangeNode) Any(test func(node *CommitFileChangeNode) bool) b }) } +func (s *CommitFileChangeNode) Every(test func(node *CommitFileChangeNode) bool) bool { + return every(s, func(n INode) bool { + castNode := n.(*CommitFileChangeNode) + return test(castNode) + }) +} + +func (s *CommitFileChangeNode) EveryFile(test func(file *models.CommitFile) bool) bool { + return every(s, func(n INode) bool { + castNode := n.(*CommitFileChangeNode) + + return castNode.File == nil || test(castNode.File) + }) +} + func (n *CommitFileChangeNode) Flatten(collapsedPaths map[string]bool) []*CommitFileChangeNode { results := flatten(n, collapsedPaths) nodes := make([]*CommitFileChangeNode, len(results)) diff --git a/pkg/gui/filetree/inode.go b/pkg/gui/filetree/inode.go index bbd1c3fb6..4357f3a8a 100644 --- a/pkg/gui/filetree/inode.go +++ b/pkg/gui/filetree/inode.go @@ -77,6 +77,20 @@ func any(node INode, test func(INode) bool) bool { return false } +func every(node INode, test func(INode) bool) bool { + if !test(node) { + return false + } + + for _, child := range node.GetChildren() { + if !every(child, test) { + return false + } + } + + return true +} + func flatten(node INode, collapsedPaths map[string]bool) []INode { result := []INode{} result = append(result, node) diff --git a/pkg/gui/list_context.go b/pkg/gui/list_context.go index 441d79ade..205d39306 100644 --- a/pkg/gui/list_context.go +++ b/pkg/gui/list_context.go @@ -465,7 +465,7 @@ func (gui *Gui) commitFilesListContext() *ListContext { return [][]string{{utils.ColoredString("(none)", color.FgRed)}} } - lines := gui.State.CommitFileChangeManager.Render(gui.State.Modes.Diffing.Ref) + lines := gui.State.CommitFileChangeManager.Render(gui.State.Modes.Diffing.Ref, gui.GitCommand.PatchManager) mappedLines := make([][]string, len(lines)) for i, line := range lines { mappedLines[i] = []string{line} diff --git a/pkg/gui/presentation/commit_files.go b/pkg/gui/presentation/commit_files.go index 2e87e61c2..bf8b6243b 100644 --- a/pkg/gui/presentation/commit_files.go +++ b/pkg/gui/presentation/commit_files.go @@ -8,7 +8,7 @@ import ( "github.com/jesseduffield/lazygit/pkg/utils" ) -func GetCommitFileLine(name string, diffName string, commitFile *models.CommitFile) string { +func GetCommitFileLine(name string, diffName string, commitFile *models.CommitFile, patchManager *patch.PatchManager) string { yellow := color.New(color.FgYellow) green := color.New(color.FgGreen) defaultColor := color.New(theme.DefaultTextColor) @@ -22,7 +22,8 @@ func GetCommitFileLine(name string, diffName string, commitFile *models.CommitFi if diffName == name { colour = diffTerminalColor } else if commitFile != nil { - switch commitFile.PatchStatus { + status := patchManager.GetFileStatus(commitFile.Name) + switch status { case patch.UNSELECTED: colour = defaultColor case patch.WHOLE: