1
0
mirror of https://github.com/jesseduffield/lazygit.git synced 2025-07-28 16:02:01 +03:00

faster patch manager

This commit is contained in:
Jesse Duffield
2020-08-22 16:46:19 +10:00
parent c837c54c39
commit 12bf851c7d
5 changed files with 113 additions and 54 deletions

View File

@ -144,7 +144,7 @@ func NewGitCommand(log *logrus.Entry, osCommand *OSCommand, tr *i18n.Localizer,
PushToCurrent: pushToCurrent, PushToCurrent: pushToCurrent,
} }
gitCommand.PatchManager = patch.NewPatchManager(log, gitCommand.ApplyPatch) gitCommand.PatchManager = patch.NewPatchManager(log, gitCommand.ApplyPatch, gitCommand.ShowCommitFile)
return gitCommand, nil return gitCommand, nil
} }
@ -1077,7 +1077,12 @@ func (c *GitCommand) GetCommitFilesFromFilenames(filenames string, parent string
for _, file := range strings.Split(strings.TrimRight(filenames, "\n"), "\n") { for _, file := range strings.Split(strings.TrimRight(filenames, "\n"), "\n") {
status := patch.UNSELECTED status := patch.UNSELECTED
if patchManager != nil && patchManager.Parent == parent { if patchManager != nil && patchManager.Parent == parent {
status = patchManager.GetFileStatus(file) var err error
status, err = patchManager.GetFileStatus(file)
if err != nil {
c.Log.Error(err)
continue
}
} }
commitFiles = append(commitFiles, &CommitFile{ commitFiles = append(commitFiles, &CommitFile{

View File

@ -1,7 +1,9 @@
package patch package patch
import ( import (
"errors"
"sort" "sort"
"strings"
"github.com/jesseduffield/lazygit/pkg/utils" "github.com/jesseduffield/lazygit/pkg/utils"
"github.com/sirupsen/logrus" "github.com/sirupsen/logrus"
@ -24,6 +26,7 @@ type fileInfo struct {
} }
type applyPatchFunc func(patch string, flags ...string) error type applyPatchFunc func(patch string, flags ...string) error
type loadFileDiffFunc func(parent string, filename string, plain bool) (string, error)
// PatchManager manages the building of a patch for a commit to be applied to another commit (or the working tree, or removed from the current commit). We also support building patches from things like stashes, for which there is less flexibility // PatchManager manages the building of a patch for a commit to be applied to another commit (or the working tree, or removed from the current commit). We also support building patches from things like stashes, for which there is less flexibility
type PatchManager struct { type PatchManager struct {
@ -31,53 +34,63 @@ type PatchManager struct {
Parent string Parent string
// CanRebase tells us whether we're allowed to modify our commits. CanRebase should be true for commits of the currently checked out branch and false for everything else // CanRebase tells us whether we're allowed to modify our commits. CanRebase should be true for commits of the currently checked out branch and false for everything else
CanRebase bool CanRebase bool
// fileInfoMap starts empty but you add files to it as you go along
fileInfoMap map[string]*fileInfo fileInfoMap map[string]*fileInfo
Log *logrus.Entry Log *logrus.Entry
ApplyPatch applyPatchFunc ApplyPatch applyPatchFunc
// LoadFileDiff loads the diff of a file, for a given parent (typically a commit SHA)
LoadFileDiff loadFileDiffFunc
} }
// NewPatchManager returns a new PatchManager // NewPatchManager returns a new PatchManager
func NewPatchManager(log *logrus.Entry, applyPatch applyPatchFunc) *PatchManager { func NewPatchManager(log *logrus.Entry, applyPatch applyPatchFunc, loadFileDiff loadFileDiffFunc) *PatchManager {
return &PatchManager{ return &PatchManager{
Log: log, Log: log,
ApplyPatch: applyPatch, ApplyPatch: applyPatch,
LoadFileDiff: loadFileDiff,
} }
} }
// NewPatchManager returns a new PatchManager // NewPatchManager returns a new PatchManager
func (p *PatchManager) Start(parent string, diffMap map[string]string, canRebase bool) { func (p *PatchManager) Start(parent string, canRebase bool) {
p.Parent = parent p.Parent = parent
p.CanRebase = canRebase p.CanRebase = canRebase
p.fileInfoMap = map[string]*fileInfo{} p.fileInfoMap = map[string]*fileInfo{}
for filename, diff := range diffMap { }
p.fileInfoMap[filename] = &fileInfo{
mode: UNSELECTED, func (p *PatchManager) addFileWhole(info *fileInfo) {
diff: diff, info.mode = WHOLE
} lineCount := len(strings.Split(info.diff, "\n"))
info.includedLineIndices = make([]int, lineCount)
// add every line index
for i := 0; i < lineCount; i++ {
info.includedLineIndices[i] = i
} }
} }
func (p *PatchManager) AddFile(filename string) { func (p *PatchManager) removeFile(info *fileInfo) {
p.fileInfoMap[filename].mode = WHOLE info.mode = UNSELECTED
p.fileInfoMap[filename].includedLineIndices = nil info.includedLineIndices = nil
} }
func (p *PatchManager) RemoveFile(filename string) { func (p *PatchManager) ToggleFileWhole(filename string) error {
p.fileInfoMap[filename].mode = UNSELECTED info, err := p.getFileInfo(filename)
p.fileInfoMap[filename].includedLineIndices = nil if err != nil {
} return err
}
func (p *PatchManager) ToggleFileWhole(filename string) {
info := p.fileInfoMap[filename]
switch info.mode { switch info.mode {
case UNSELECTED: case UNSELECTED, PART:
p.AddFile(filename) p.addFileWhole(info)
case WHOLE: case WHOLE:
p.RemoveFile(filename) p.removeFile(info)
case PART: default:
p.AddFile(filename) return errors.New("unknown file mode")
} }
return nil
} }
func getIndicesForRange(first, last int) []int { func getIndicesForRange(first, last int) []int {
@ -88,24 +101,55 @@ func getIndicesForRange(first, last int) []int {
return indices return indices
} }
func (p *PatchManager) AddFileLineRange(filename string, firstLineIdx, lastLineIdx int) { func (p *PatchManager) getFileInfo(filename string) (*fileInfo, error) {
info := p.fileInfoMap[filename] info, ok := p.fileInfoMap[filename]
info.mode = PART if ok {
info.includedLineIndices = utils.UnionInt(info.includedLineIndices, getIndicesForRange(firstLineIdx, lastLineIdx)) return info, nil
}
diff, err := p.LoadFileDiff(p.Parent, filename, true)
if err != nil {
return nil, err
}
info = &fileInfo{
mode: UNSELECTED,
diff: diff,
}
p.fileInfoMap[filename] = info
return info, nil
} }
func (p *PatchManager) RemoveFileLineRange(filename string, firstLineIdx, lastLineIdx int) { func (p *PatchManager) AddFileLineRange(filename string, firstLineIdx, lastLineIdx int) error {
info := p.fileInfoMap[filename] info, err := p.getFileInfo(filename)
if err != nil {
return err
}
info.mode = PART
info.includedLineIndices = utils.UnionInt(info.includedLineIndices, getIndicesForRange(firstLineIdx, lastLineIdx))
return nil
}
func (p *PatchManager) RemoveFileLineRange(filename string, firstLineIdx, lastLineIdx int) error {
info, err := p.getFileInfo(filename)
if err != nil {
return err
}
info.mode = PART info.mode = PART
info.includedLineIndices = utils.DifferenceInt(info.includedLineIndices, getIndicesForRange(firstLineIdx, lastLineIdx)) info.includedLineIndices = utils.DifferenceInt(info.includedLineIndices, getIndicesForRange(firstLineIdx, lastLineIdx))
if len(info.includedLineIndices) == 0 { if len(info.includedLineIndices) == 0 {
p.RemoveFile(filename) p.removeFile(info)
} }
return nil
} }
func (p *PatchManager) RenderPlainPatchForFile(filename string, reverse bool, keepOriginalHeader bool) string { func (p *PatchManager) renderPlainPatchForFile(filename string, reverse bool, keepOriginalHeader bool) string {
info := p.fileInfoMap[filename] info, err := p.getFileInfo(filename)
if info == nil { if err != nil {
p.Log.Error(err)
return "" return ""
} }
@ -123,7 +167,7 @@ func (p *PatchManager) RenderPlainPatchForFile(filename string, reverse bool, ke
} }
func (p *PatchManager) RenderPatchForFile(filename string, plain bool, reverse bool, keepOriginalHeader bool) string { func (p *PatchManager) RenderPatchForFile(filename string, plain bool, reverse bool, keepOriginalHeader bool) string {
patch := p.RenderPlainPatchForFile(filename, reverse, keepOriginalHeader) patch := p.renderPlainPatchForFile(filename, reverse, keepOriginalHeader)
if plain { if plain {
return patch return patch
} }
@ -136,7 +180,7 @@ func (p *PatchManager) RenderPatchForFile(filename string, plain bool, reverse b
return parser.Render(-1, -1, nil) return parser.Render(-1, -1, nil)
} }
func (p *PatchManager) RenderEachFilePatch(plain bool) []string { func (p *PatchManager) renderEachFilePatch(plain bool) []string {
// sort files by name then iterate through and render each patch // sort files by name then iterate through and render each patch
filenames := make([]string, len(p.fileInfoMap)) filenames := make([]string, len(p.fileInfoMap))
index := 0 index := 0
@ -159,7 +203,7 @@ func (p *PatchManager) RenderEachFilePatch(plain bool) []string {
func (p *PatchManager) RenderAggregatedPatchColored(plain bool) string { func (p *PatchManager) RenderAggregatedPatchColored(plain bool) string {
result := "" result := ""
for _, patch := range p.RenderEachFilePatch(plain) { for _, patch := range p.renderEachFilePatch(plain) {
if patch != "" { if patch != "" {
result += patch + "\n" result += patch + "\n"
} }
@ -167,20 +211,21 @@ func (p *PatchManager) RenderAggregatedPatchColored(plain bool) string {
return result return result
} }
func (p *PatchManager) GetFileStatus(filename string) int { func (p *PatchManager) GetFileStatus(filename string) (int, error) {
info := p.fileInfoMap[filename] info, err := p.getFileInfo(filename)
if info == nil { if err != nil {
return UNSELECTED return 0, err
} }
return info.mode
return info.mode, nil
} }
func (p *PatchManager) GetFileIncLineIndices(filename string) []int { func (p *PatchManager) GetFileIncLineIndices(filename string) ([]int, error) {
info := p.fileInfoMap[filename] info, err := p.getFileInfo(filename)
if info == nil { if err != nil {
return []int{} return nil, err
} }
return info.includedLineIndices return info.includedLineIndices, nil
} }
func (p *PatchManager) ApplyPatches(reverse bool) error { func (p *PatchManager) ApplyPatches(reverse bool) error {

View File

@ -156,7 +156,9 @@ func (gui *Gui) handleToggleFileForPatch(g *gocui.Gui, v *gocui.View) error {
} }
} }
gui.GitCommand.PatchManager.ToggleFileWhole(commitFile.Name) if err := gui.GitCommand.PatchManager.ToggleFileWhole(commitFile.Name); err != nil {
return err
}
if gui.GitCommand.PatchManager.IsEmpty() { if gui.GitCommand.PatchManager.IsEmpty() {
gui.GitCommand.PatchManager.Reset() gui.GitCommand.PatchManager.Reset()
@ -193,7 +195,7 @@ func (gui *Gui) startPatchManager() error {
} }
canRebase := gui.State.Panels.CommitFiles.refType == REF_TYPE_LOCAL_COMMIT canRebase := gui.State.Panels.CommitFiles.refType == REF_TYPE_LOCAL_COMMIT
gui.GitCommand.PatchManager.Start(gui.State.Panels.CommitFiles.refName, diffMap, canRebase) gui.GitCommand.PatchManager.Start(gui.State.Panels.CommitFiles.refName, canRebase)
return nil return nil
} }

View File

@ -236,7 +236,11 @@ func (gui *Gui) refreshMainView() error {
// how to get around this // how to get around this
if gui.currentContextKey() == gui.Contexts.PatchBuilding.Context.GetKey() { if gui.currentContextKey() == gui.Contexts.PatchBuilding.Context.GetKey() {
filename := gui.getSelectedCommitFileName() filename := gui.getSelectedCommitFileName()
includedLineIndices = gui.GitCommand.PatchManager.GetFileIncLineIndices(filename) var err error
includedLineIndices, err = gui.GitCommand.PatchManager.GetFileIncLineIndices(filename)
if err != nil {
return err
}
} }
colorDiff := state.PatchParser.Render(state.FirstLineIdx, state.LastLineIdx, includedLineIndices) colorDiff := state.PatchParser.Render(state.FirstLineIdx, state.LastLineIdx, includedLineIndices)

View File

@ -49,7 +49,10 @@ func (gui *Gui) handleToggleSelectionForPatch(g *gocui.Gui, v *gocui.View) error
toggleFunc := gui.GitCommand.PatchManager.AddFileLineRange toggleFunc := gui.GitCommand.PatchManager.AddFileLineRange
filename := gui.getSelectedCommitFileName() filename := gui.getSelectedCommitFileName()
includedLineIndices := gui.GitCommand.PatchManager.GetFileIncLineIndices(filename) includedLineIndices, err := gui.GitCommand.PatchManager.GetFileIncLineIndices(filename)
if err != nil {
return err
}
currentLineIsStaged := utils.IncludesInt(includedLineIndices, state.SelectedLineIdx) currentLineIsStaged := utils.IncludesInt(includedLineIndices, state.SelectedLineIdx)
if currentLineIsStaged { if currentLineIsStaged {
toggleFunc = gui.GitCommand.PatchManager.RemoveFileLineRange toggleFunc = gui.GitCommand.PatchManager.RemoveFileLineRange