mirror of
https://github.com/jesseduffield/lazygit.git
synced 2024-12-04 10:34:55 +02:00
add optimistic rendering for staging and unstaging files
This commit is contained in:
parent
c26650258d
commit
7077ea428f
@ -7,7 +7,6 @@ import (
|
|||||||
"github.com/jesseduffield/lazygit/pkg/commands/models"
|
"github.com/jesseduffield/lazygit/pkg/commands/models"
|
||||||
"github.com/jesseduffield/lazygit/pkg/commands/oscommands"
|
"github.com/jesseduffield/lazygit/pkg/commands/oscommands"
|
||||||
"github.com/jesseduffield/lazygit/pkg/common"
|
"github.com/jesseduffield/lazygit/pkg/common"
|
||||||
"github.com/samber/lo"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
type FileLoaderConfig interface {
|
type FileLoaderConfig interface {
|
||||||
@ -54,28 +53,15 @@ func (self *FileLoader) GetStatusFiles(opts GetStatusFileOptions) []*models.File
|
|||||||
self.Log.Warningf("warning when calling git status: %s", status.StatusString)
|
self.Log.Warningf("warning when calling git status: %s", status.StatusString)
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
change := status.Change
|
|
||||||
stagedChange := change[0:1]
|
|
||||||
unstagedChange := change[1:2]
|
|
||||||
untracked := lo.Contains([]string{"??", "A ", "AM"}, change)
|
|
||||||
hasNoStagedChanges := lo.Contains([]string{" ", "U", "?"}, stagedChange)
|
|
||||||
hasInlineMergeConflicts := lo.Contains([]string{"UU", "AA"}, change)
|
|
||||||
hasMergeConflicts := hasInlineMergeConflicts || lo.Contains([]string{"DD", "AU", "UA", "UD", "DU"}, change)
|
|
||||||
|
|
||||||
file := &models.File{
|
file := &models.File{
|
||||||
Name: status.Name,
|
Name: status.Name,
|
||||||
PreviousName: status.PreviousName,
|
PreviousName: status.PreviousName,
|
||||||
DisplayString: status.StatusString,
|
DisplayString: status.StatusString,
|
||||||
HasStagedChanges: !hasNoStagedChanges,
|
Type: self.getFileType(status.Name),
|
||||||
HasUnstagedChanges: unstagedChange != " ",
|
|
||||||
Tracked: !untracked,
|
|
||||||
Deleted: unstagedChange == "D" || stagedChange == "D",
|
|
||||||
Added: unstagedChange == "A" || untracked,
|
|
||||||
HasMergeConflicts: hasMergeConflicts,
|
|
||||||
HasInlineMergeConflicts: hasInlineMergeConflicts,
|
|
||||||
Type: self.getFileType(status.Name),
|
|
||||||
ShortStatus: change,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
models.SetStatusFields(file, status.Change)
|
||||||
files = append(files, file)
|
files = append(files, file)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2,6 +2,7 @@ package models
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/jesseduffield/lazygit/pkg/utils"
|
"github.com/jesseduffield/lazygit/pkg/utils"
|
||||||
|
"github.com/samber/lo"
|
||||||
)
|
)
|
||||||
|
|
||||||
// File : A file from git status
|
// File : A file from git status
|
||||||
@ -90,3 +91,48 @@ func (f *File) GetPath() string {
|
|||||||
func (f *File) GetPreviousPath() string {
|
func (f *File) GetPreviousPath() string {
|
||||||
return f.PreviousName
|
return f.PreviousName
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type StatusFields struct {
|
||||||
|
HasStagedChanges bool
|
||||||
|
HasUnstagedChanges bool
|
||||||
|
Tracked bool
|
||||||
|
Deleted bool
|
||||||
|
Added bool
|
||||||
|
HasMergeConflicts bool
|
||||||
|
HasInlineMergeConflicts bool
|
||||||
|
ShortStatus string
|
||||||
|
}
|
||||||
|
|
||||||
|
func SetStatusFields(file *File, shortStatus string) {
|
||||||
|
derived := deriveStatusFields(shortStatus)
|
||||||
|
|
||||||
|
file.HasStagedChanges = derived.HasStagedChanges
|
||||||
|
file.HasUnstagedChanges = derived.HasUnstagedChanges
|
||||||
|
file.Tracked = derived.Tracked
|
||||||
|
file.Deleted = derived.Deleted
|
||||||
|
file.Added = derived.Added
|
||||||
|
file.HasMergeConflicts = derived.HasMergeConflicts
|
||||||
|
file.HasInlineMergeConflicts = derived.HasInlineMergeConflicts
|
||||||
|
file.ShortStatus = derived.ShortStatus
|
||||||
|
}
|
||||||
|
|
||||||
|
// shortStatus is something like '??' or 'A '
|
||||||
|
func deriveStatusFields(shortStatus string) StatusFields {
|
||||||
|
stagedChange := shortStatus[0:1]
|
||||||
|
unstagedChange := shortStatus[1:2]
|
||||||
|
untracked := lo.Contains([]string{"??", "A ", "AM"}, shortStatus)
|
||||||
|
hasNoStagedChanges := lo.Contains([]string{" ", "U", "?"}, stagedChange)
|
||||||
|
hasInlineMergeConflicts := lo.Contains([]string{"UU", "AA"}, shortStatus)
|
||||||
|
hasMergeConflicts := hasInlineMergeConflicts || lo.Contains([]string{"DD", "AU", "UA", "UD", "DU"}, shortStatus)
|
||||||
|
|
||||||
|
return StatusFields{
|
||||||
|
HasStagedChanges: !hasNoStagedChanges,
|
||||||
|
HasUnstagedChanges: unstagedChange != " ",
|
||||||
|
Tracked: !untracked,
|
||||||
|
Deleted: unstagedChange == "D" || stagedChange == "D",
|
||||||
|
Added: unstagedChange == "A" || untracked,
|
||||||
|
HasMergeConflicts: hasMergeConflicts,
|
||||||
|
HasInlineMergeConflicts: hasInlineMergeConflicts,
|
||||||
|
ShortStatus: shortStatus,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -62,6 +62,7 @@ func (gui *Gui) resetControllers() {
|
|||||||
model,
|
model,
|
||||||
gui.State.Contexts,
|
gui.State.Contexts,
|
||||||
gui.State.Modes,
|
gui.State.Modes,
|
||||||
|
&gui.Mutexes,
|
||||||
)
|
)
|
||||||
|
|
||||||
syncController := controllers.NewSyncController(
|
syncController := controllers.NewSyncController(
|
||||||
|
@ -16,6 +16,7 @@ type controllerCommon struct {
|
|||||||
model *types.Model
|
model *types.Model
|
||||||
contexts *context.ContextTree
|
contexts *context.ContextTree
|
||||||
modes *types.Modes
|
modes *types.Modes
|
||||||
|
mutexes *types.Mutexes
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewControllerCommon(
|
func NewControllerCommon(
|
||||||
@ -26,6 +27,7 @@ func NewControllerCommon(
|
|||||||
model *types.Model,
|
model *types.Model,
|
||||||
contexts *context.ContextTree,
|
contexts *context.ContextTree,
|
||||||
modes *types.Modes,
|
modes *types.Modes,
|
||||||
|
mutexes *types.Mutexes,
|
||||||
) *controllerCommon {
|
) *controllerCommon {
|
||||||
return &controllerCommon{
|
return &controllerCommon{
|
||||||
c: c,
|
c: c,
|
||||||
@ -35,5 +37,6 @@ func NewControllerCommon(
|
|||||||
model: model,
|
model: model,
|
||||||
contexts: contexts,
|
contexts: contexts,
|
||||||
modes: modes,
|
modes: modes,
|
||||||
|
mutexes: mutexes,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -108,7 +108,7 @@ func (self *FilesController) GetKeybindings(opts types.KeybindingsOpts) []*types
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
Key: opts.GetKey(opts.Config.Files.ToggleStagedAll),
|
Key: opts.GetKey(opts.Config.Files.ToggleStagedAll),
|
||||||
Handler: self.stageAll,
|
Handler: self.toggleStagedAll,
|
||||||
Description: self.c.Tr.LcToggleStagedAll,
|
Description: self.c.Tr.LcToggleStagedAll,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@ -167,7 +167,86 @@ func (self *FilesController) GetOnClick() func() error {
|
|||||||
return self.checkSelectedFileNode(self.press)
|
return self.checkSelectedFileNode(self.press)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (self *FilesController) press(node *filetree.FileNode) error {
|
// if we are dealing with a status for which there is no key in this map,
|
||||||
|
// then we won't optimistically render: we'll just let `git status` tell
|
||||||
|
// us what the new status is.
|
||||||
|
// There are no doubt more entries that could be added to these two maps.
|
||||||
|
var stageStatusMap = map[string]string{
|
||||||
|
"??": "A ",
|
||||||
|
" M": "M ",
|
||||||
|
"MM": "M ",
|
||||||
|
" D": "D ",
|
||||||
|
" A": "A ",
|
||||||
|
"AM": "A ",
|
||||||
|
"MD": "D ",
|
||||||
|
}
|
||||||
|
|
||||||
|
var unstageStatusMap = map[string]string{
|
||||||
|
"A ": "??",
|
||||||
|
"M ": " M",
|
||||||
|
"D ": " D",
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *FilesController) optimisticStage(file *models.File) bool {
|
||||||
|
newShortStatus, ok := stageStatusMap[file.ShortStatus]
|
||||||
|
if !ok {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
models.SetStatusFields(file, newShortStatus)
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *FilesController) optimisticUnstage(file *models.File) bool {
|
||||||
|
newShortStatus, ok := unstageStatusMap[file.ShortStatus]
|
||||||
|
if !ok {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
models.SetStatusFields(file, newShortStatus)
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
// Running a git add command followed by a git status command can take some time (e.g. 200ms).
|
||||||
|
// Given how often users stage/unstage files in Lazygit, we're adding some
|
||||||
|
// optimistic rendering to make things feel faster. When we go to stage
|
||||||
|
// a file, we'll first update that file's status in-memory, then re-render
|
||||||
|
// the files panel. Then we'll immediately do a proper git status call
|
||||||
|
// so that if the optimistic rendering got something wrong, it's quickly
|
||||||
|
// corrected.
|
||||||
|
func (self *FilesController) optimisticChange(node *filetree.FileNode, optimisticChangeFn func(*models.File) bool) error {
|
||||||
|
rerender := false
|
||||||
|
err := node.ForEachFile(func(f *models.File) error {
|
||||||
|
// can't act on the file itself: we need to update the original model file
|
||||||
|
for _, modelFile := range self.model.Files {
|
||||||
|
if modelFile.Name == f.Name {
|
||||||
|
if optimisticChangeFn(modelFile) {
|
||||||
|
rerender = true
|
||||||
|
}
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if rerender {
|
||||||
|
if err := self.c.PostRefreshUpdate(self.contexts.Files); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *FilesController) pressWithLock(node *filetree.FileNode) error {
|
||||||
|
// Obtaining this lock because optimistic rendering requires us to mutate
|
||||||
|
// the files in our model.
|
||||||
|
self.mutexes.RefreshingFilesMutex.Lock()
|
||||||
|
defer self.mutexes.RefreshingFilesMutex.Unlock()
|
||||||
|
|
||||||
if node.IsLeaf() {
|
if node.IsLeaf() {
|
||||||
file := node.File
|
file := node.File
|
||||||
|
|
||||||
@ -177,11 +256,21 @@ func (self *FilesController) press(node *filetree.FileNode) error {
|
|||||||
|
|
||||||
if file.HasUnstagedChanges {
|
if file.HasUnstagedChanges {
|
||||||
self.c.LogAction(self.c.Tr.Actions.StageFile)
|
self.c.LogAction(self.c.Tr.Actions.StageFile)
|
||||||
|
|
||||||
|
if err := self.optimisticChange(node, self.optimisticStage); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
if err := self.git.WorkingTree.StageFile(file.Name); err != nil {
|
if err := self.git.WorkingTree.StageFile(file.Name); err != nil {
|
||||||
return self.c.Error(err)
|
return self.c.Error(err)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
self.c.LogAction(self.c.Tr.Actions.UnstageFile)
|
self.c.LogAction(self.c.Tr.Actions.UnstageFile)
|
||||||
|
|
||||||
|
if err := self.optimisticChange(node, self.optimisticUnstage); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
if err := self.git.WorkingTree.UnStageFile(file.Names(), file.Tracked); err != nil {
|
if err := self.git.WorkingTree.UnStageFile(file.Names(), file.Tracked); err != nil {
|
||||||
return self.c.Error(err)
|
return self.c.Error(err)
|
||||||
}
|
}
|
||||||
@ -195,19 +284,37 @@ func (self *FilesController) press(node *filetree.FileNode) error {
|
|||||||
|
|
||||||
if node.GetHasUnstagedChanges() {
|
if node.GetHasUnstagedChanges() {
|
||||||
self.c.LogAction(self.c.Tr.Actions.StageFile)
|
self.c.LogAction(self.c.Tr.Actions.StageFile)
|
||||||
|
|
||||||
|
if err := self.optimisticChange(node, self.optimisticStage); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
if err := self.git.WorkingTree.StageFile(node.Path); err != nil {
|
if err := self.git.WorkingTree.StageFile(node.Path); err != nil {
|
||||||
return self.c.Error(err)
|
return self.c.Error(err)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// pretty sure it doesn't matter that we're always passing true here
|
|
||||||
self.c.LogAction(self.c.Tr.Actions.UnstageFile)
|
self.c.LogAction(self.c.Tr.Actions.UnstageFile)
|
||||||
|
|
||||||
|
if err := self.optimisticChange(node, self.optimisticUnstage); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// pretty sure it doesn't matter that we're always passing true here
|
||||||
if err := self.git.WorkingTree.UnStageFile([]string{node.Path}, true); err != nil {
|
if err := self.git.WorkingTree.UnStageFile([]string{node.Path}, true); err != nil {
|
||||||
return self.c.Error(err)
|
return self.c.Error(err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := self.c.Refresh(types.RefreshOptions{Scope: []types.RefreshableView{types.FILES}}); err != nil {
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *FilesController) press(node *filetree.FileNode) error {
|
||||||
|
if err := self.pressWithLock(node); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := self.c.Refresh(types.RefreshOptions{Scope: []types.RefreshableView{types.FILES}, Mode: types.ASYNC}); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -273,33 +380,53 @@ func (self *FilesController) EnterFile(opts types.OnFocusOpts) error {
|
|||||||
return self.c.PushContext(self.contexts.Staging, opts)
|
return self.c.PushContext(self.contexts.Staging, opts)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (self *FilesController) allFilesStaged() bool {
|
func (self *FilesController) toggleStagedAll() error {
|
||||||
for _, file := range self.model.Files {
|
if err := self.toggleStagedAllWithLock(); err != nil {
|
||||||
if file.HasUnstagedChanges {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
func (self *FilesController) stageAll() error {
|
|
||||||
var err error
|
|
||||||
if self.allFilesStaged() {
|
|
||||||
self.c.LogAction(self.c.Tr.Actions.UnstageAllFiles)
|
|
||||||
err = self.git.WorkingTree.UnstageAll()
|
|
||||||
} else {
|
|
||||||
self.c.LogAction(self.c.Tr.Actions.StageAllFiles)
|
|
||||||
err = self.git.WorkingTree.StageAll()
|
|
||||||
}
|
|
||||||
if err != nil {
|
|
||||||
_ = self.c.Error(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := self.c.Refresh(types.RefreshOptions{Scope: []types.RefreshableView{types.FILES}}); err != nil {
|
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
return self.contexts.Files.HandleFocus()
|
if err := self.c.Refresh(types.RefreshOptions{Scope: []types.RefreshableView{types.FILES}, Mode: types.ASYNC}); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return self.context().HandleFocus()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *FilesController) toggleStagedAllWithLock() error {
|
||||||
|
self.mutexes.RefreshingFilesMutex.Lock()
|
||||||
|
defer self.mutexes.RefreshingFilesMutex.Unlock()
|
||||||
|
|
||||||
|
root := self.context().FileTreeViewModel.GetRoot()
|
||||||
|
|
||||||
|
// if any files within have inline merge conflicts we can't stage or unstage,
|
||||||
|
// or it'll end up with those >>>>>> lines actually staged
|
||||||
|
if root.GetHasInlineMergeConflicts() {
|
||||||
|
return self.c.ErrorMsg(self.c.Tr.ErrStageDirWithInlineMergeConflicts)
|
||||||
|
}
|
||||||
|
|
||||||
|
if root.GetHasUnstagedChanges() {
|
||||||
|
self.c.LogAction(self.c.Tr.Actions.StageAllFiles)
|
||||||
|
|
||||||
|
if err := self.optimisticChange(root, self.optimisticStage); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := self.git.WorkingTree.StageAll(); err != nil {
|
||||||
|
return self.c.Error(err)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
self.c.LogAction(self.c.Tr.Actions.UnstageAllFiles)
|
||||||
|
|
||||||
|
if err := self.optimisticChange(root, self.optimisticUnstage); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := self.git.WorkingTree.UnstageAll(); err != nil {
|
||||||
|
return self.c.Error(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (self *FilesController) unstageFiles(node *filetree.FileNode) error {
|
func (self *FilesController) unstageFiles(node *filetree.FileNode) error {
|
||||||
|
@ -41,6 +41,7 @@ type IFileTree interface {
|
|||||||
GetAllItems() []*FileNode
|
GetAllItems() []*FileNode
|
||||||
GetAllFiles() []*models.File
|
GetAllFiles() []*models.File
|
||||||
GetFilter() FileTreeDisplayFilter
|
GetFilter() FileTreeDisplayFilter
|
||||||
|
GetRoot() *FileNode
|
||||||
}
|
}
|
||||||
|
|
||||||
type FileTree struct {
|
type FileTree struct {
|
||||||
@ -159,6 +160,10 @@ func (self *FileTree) Tree() INode {
|
|||||||
return self.tree
|
return self.tree
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (self *FileTree) GetRoot() *FileNode {
|
||||||
|
return self.tree
|
||||||
|
}
|
||||||
|
|
||||||
func (self *FileTree) CollapsedPaths() *CollapsedPaths {
|
func (self *FileTree) CollapsedPaths() *CollapsedPaths {
|
||||||
return self.collapsedPaths
|
return self.collapsedPaths
|
||||||
}
|
}
|
||||||
|
@ -97,7 +97,7 @@ type Gui struct {
|
|||||||
// recent repo with the recent repos popup showing
|
// recent repo with the recent repos popup showing
|
||||||
showRecentRepos bool
|
showRecentRepos bool
|
||||||
|
|
||||||
Mutexes guiMutexes
|
Mutexes types.Mutexes
|
||||||
|
|
||||||
// findSuggestions will take a string that the user has typed into a prompt
|
// findSuggestions will take a string that the user has typed into a prompt
|
||||||
// and return a slice of suggestions which match that string.
|
// and return a slice of suggestions which match that string.
|
||||||
@ -237,18 +237,6 @@ const (
|
|||||||
COMPLETE
|
COMPLETE
|
||||||
)
|
)
|
||||||
|
|
||||||
// if you add a new mutex here be sure to instantiate it. We're using pointers to
|
|
||||||
// mutexes so that we can pass the mutexes to controllers.
|
|
||||||
type guiMutexes struct {
|
|
||||||
RefreshingFilesMutex *sync.Mutex
|
|
||||||
RefreshingStatusMutex *sync.Mutex
|
|
||||||
SyncMutex *sync.Mutex
|
|
||||||
LocalCommitsMutex *sync.Mutex
|
|
||||||
LineByLinePanelMutex *sync.Mutex
|
|
||||||
SubprocessMutex *sync.Mutex
|
|
||||||
PopupMutex *sync.Mutex
|
|
||||||
}
|
|
||||||
|
|
||||||
func (gui *Gui) onNewRepo(startArgs types.StartArgs, reuseState bool) error {
|
func (gui *Gui) onNewRepo(startArgs types.StartArgs, reuseState bool) error {
|
||||||
var err error
|
var err error
|
||||||
gui.git, err = commands.NewGitCommand(
|
gui.git, err = commands.NewGitCommand(
|
||||||
@ -418,7 +406,7 @@ func NewGui(
|
|||||||
// but now we do it via state. So we need to still support the config for the
|
// but now we do it via state. So we need to still support the config for the
|
||||||
// sake of backwards compatibility. We're making use of short circuiting here
|
// sake of backwards compatibility. We're making use of short circuiting here
|
||||||
ShowExtrasWindow: cmn.UserConfig.Gui.ShowCommandLog && !config.GetAppState().HideCommandLog,
|
ShowExtrasWindow: cmn.UserConfig.Gui.ShowCommandLog && !config.GetAppState().HideCommandLog,
|
||||||
Mutexes: guiMutexes{
|
Mutexes: types.Mutexes{
|
||||||
RefreshingFilesMutex: &sync.Mutex{},
|
RefreshingFilesMutex: &sync.Mutex{},
|
||||||
RefreshingStatusMutex: &sync.Mutex{},
|
RefreshingStatusMutex: &sync.Mutex{},
|
||||||
SyncMutex: &sync.Mutex{},
|
SyncMutex: &sync.Mutex{},
|
||||||
|
@ -1,6 +1,8 @@
|
|||||||
package types
|
package types
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"sync"
|
||||||
|
|
||||||
"github.com/jesseduffield/lazygit/pkg/commands/git_commands"
|
"github.com/jesseduffield/lazygit/pkg/commands/git_commands"
|
||||||
"github.com/jesseduffield/lazygit/pkg/commands/models"
|
"github.com/jesseduffield/lazygit/pkg/commands/models"
|
||||||
"github.com/jesseduffield/lazygit/pkg/commands/oscommands"
|
"github.com/jesseduffield/lazygit/pkg/commands/oscommands"
|
||||||
@ -156,3 +158,15 @@ type Model struct {
|
|||||||
// for displaying suggestions while typing in a file name
|
// for displaying suggestions while typing in a file name
|
||||||
FilesTrie *patricia.Trie
|
FilesTrie *patricia.Trie
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// if you add a new mutex here be sure to instantiate it. We're using pointers to
|
||||||
|
// mutexes so that we can pass the mutexes to controllers.
|
||||||
|
type Mutexes struct {
|
||||||
|
RefreshingFilesMutex *sync.Mutex
|
||||||
|
RefreshingStatusMutex *sync.Mutex
|
||||||
|
SyncMutex *sync.Mutex
|
||||||
|
LocalCommitsMutex *sync.Mutex
|
||||||
|
LineByLinePanelMutex *sync.Mutex
|
||||||
|
SubprocessMutex *sync.Mutex
|
||||||
|
PopupMutex *sync.Mutex
|
||||||
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user