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:
@ -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{
|
||||||
|
@ -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 {
|
||||||
|
@ -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
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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)
|
||||||
|
|
||||||
|
@ -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
|
||||||
|
Reference in New Issue
Block a user