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

switching repos without restarting the gui

This commit is contained in:
Jesse Duffield 2021-04-03 13:43:43 +11:00
parent bc9a99387f
commit f1d7f59e49
13 changed files with 182 additions and 113 deletions

View File

@ -239,7 +239,7 @@ func (app *App) Run() error {
os.Exit(0) os.Exit(0)
} }
err := app.Gui.RunWithRestarts() err := app.Gui.RunAndHandleError()
return err return err
} }

View File

@ -181,9 +181,12 @@ func (gui *Gui) getWindowDimensions(informationStr string, appStatus string) map
// the default behaviour when accordian mode is NOT in effect. If it is in effect // the default behaviour when accordian mode is NOT in effect. If it is in effect
// then when it's accessed it will have weight 2, not 1. // then when it's accessed it will have weight 2, not 1.
func (gui *Gui) getDefaultStashWindowBox() *boxlayout.Box { func (gui *Gui) getDefaultStashWindowBox() *boxlayout.Box {
gui.State.ContextManager.Lock()
defer gui.State.ContextManager.Unlock()
box := &boxlayout.Box{Window: "stash"} box := &boxlayout.Box{Window: "stash"}
stashWindowAccessed := false stashWindowAccessed := false
for _, context := range gui.State.ContextStack { for _, context := range gui.State.ContextManager.ContextStack {
if context.GetWindowName() == "stash" { if context.GetWindowName() == "stash" {
stashWindowAccessed = true stashWindowAccessed = true
} }
@ -278,9 +281,12 @@ func (gui *Gui) sidePanelChildren(width int, height int) []*boxlayout.Box {
func (gui *Gui) currentSideWindowName() string { func (gui *Gui) currentSideWindowName() string {
// there is always one and only one cyclable context in the context stack. We'll look from top to bottom // there is always one and only one cyclable context in the context stack. We'll look from top to bottom
for idx := range gui.State.ContextStack { gui.State.ContextManager.Lock()
reversedIdx := len(gui.State.ContextStack) - 1 - idx defer gui.State.ContextManager.Unlock()
context := gui.State.ContextStack[reversedIdx]
for idx := range gui.State.ContextManager.ContextStack {
reversedIdx := len(gui.State.ContextManager.ContextStack) - 1 - idx
context := gui.State.ContextManager.ContextStack[reversedIdx]
if context.GetKind() == SIDE_CONTEXT { if context.GetKind() == SIDE_CONTEXT {
return context.GetWindowName() return context.GetWindowName()

View File

@ -296,7 +296,18 @@ func (gui *Gui) initialViewContextMap() map[string]Context {
} }
} }
func (gui *Gui) viewTabContextMap() map[string][]tabContext { func (gui *Gui) popupViewNames() []string {
result := []string{}
for _, context := range gui.allContexts() {
if context.GetKind() == PERSISTENT_POPUP || context.GetKind() == TEMPORARY_POPUP {
result = append(result, context.GetViewName())
}
}
return result
}
func (gui *Gui) initialViewTabContextMap() map[string][]tabContext {
return map[string][]tabContext{ return map[string][]tabContext{
"branches": { "branches": {
{ {
@ -343,7 +354,10 @@ func (gui *Gui) viewTabContextMap() map[string][]tabContext {
} }
func (gui *Gui) currentContextKeyIgnoringPopups() string { func (gui *Gui) currentContextKeyIgnoringPopups() string {
stack := gui.State.ContextStack gui.State.ContextManager.Lock()
defer gui.State.ContextManager.Unlock()
stack := gui.State.ContextManager.ContextStack
for i := range stack { for i := range stack {
reversedIndex := len(stack) - 1 - i reversedIndex := len(stack) - 1 - i
@ -361,11 +375,14 @@ func (gui *Gui) currentContextKeyIgnoringPopups() string {
// hitting escape: you want to go that context's parent instead. // hitting escape: you want to go that context's parent instead.
func (gui *Gui) replaceContext(c Context) error { func (gui *Gui) replaceContext(c Context) error {
gui.g.Update(func(*gocui.Gui) error { gui.g.Update(func(*gocui.Gui) error {
if len(gui.State.ContextStack) == 0 { gui.State.ContextManager.Lock()
gui.State.ContextStack = []Context{c} defer gui.State.ContextManager.Unlock()
if len(gui.State.ContextManager.ContextStack) == 0 {
gui.State.ContextManager.ContextStack = []Context{c}
} else { } else {
// replace the last item with the given item // replace the last item with the given item
gui.State.ContextStack = append(gui.State.ContextStack[0:len(gui.State.ContextStack)-1], c) gui.State.ContextManager.ContextStack = append(gui.State.ContextManager.ContextStack[0:len(gui.State.ContextManager.ContextStack)-1], c)
} }
return gui.activateContext(c) return gui.activateContext(c)
@ -376,28 +393,37 @@ func (gui *Gui) replaceContext(c Context) error {
func (gui *Gui) pushContext(c Context) error { func (gui *Gui) pushContext(c Context) error {
gui.g.Update(func(*gocui.Gui) error { gui.g.Update(func(*gocui.Gui) error {
// push onto stack gui.State.ContextManager.Lock()
// if we are switching to a side context, remove all other contexts in the stack defer gui.State.ContextManager.Unlock()
if c.GetKind() == SIDE_CONTEXT {
for _, stackContext := range gui.State.ContextStack {
if stackContext.GetKey() != c.GetKey() {
if err := gui.deactivateContext(stackContext); err != nil {
return err
}
}
}
gui.State.ContextStack = []Context{c}
} else {
// TODO: think about other exceptional cases
gui.State.ContextStack = append(gui.State.ContextStack, c)
}
return gui.activateContext(c) return gui.pushContextDirect(c)
}) })
return nil return nil
} }
func (gui *Gui) pushContextDirect(c Context) error {
// push onto stack
// if we are switching to a side context, remove all other contexts in the stack
if c.GetKind() == SIDE_CONTEXT {
for _, stackContext := range gui.State.ContextManager.ContextStack {
if stackContext.GetKey() != c.GetKey() {
if err := gui.deactivateContext(stackContext); err != nil {
return err
}
}
}
gui.State.ContextManager.ContextStack = []Context{c}
} else {
// TODO: think about other exceptional cases
gui.State.ContextManager.ContextStack = append(gui.State.ContextManager.ContextStack, c)
}
return gui.activateContext(c)
}
// asynchronous code idea: functions return an error via a channel, when done
// pushContextWithView is to be used when you don't know which context you // pushContextWithView is to be used when you don't know which context you
// want to switch to: you only know the view that you want to switch to. It will // want to switch to: you only know the view that you want to switch to. It will
// look up the context currently active for that view and switch to that context // look up the context currently active for that view and switch to that context
@ -407,19 +433,20 @@ func (gui *Gui) pushContextWithView(viewName string) error {
func (gui *Gui) returnFromContext() error { func (gui *Gui) returnFromContext() error {
gui.g.Update(func(*gocui.Gui) error { gui.g.Update(func(*gocui.Gui) error {
// TODO: add mutexes gui.State.ContextManager.Lock()
defer gui.State.ContextManager.Unlock()
if len(gui.State.ContextStack) == 1 { if len(gui.State.ContextManager.ContextStack) == 1 {
// cannot escape from bottommost context // cannot escape from bottommost context
return nil return nil
} }
n := len(gui.State.ContextStack) - 1 n := len(gui.State.ContextManager.ContextStack) - 1
currentContext := gui.State.ContextStack[n] currentContext := gui.State.ContextManager.ContextStack[n]
newContext := gui.State.ContextStack[n-1] newContext := gui.State.ContextManager.ContextStack[n-1]
gui.State.ContextStack = gui.State.ContextStack[:n] gui.State.ContextManager.ContextStack = gui.State.ContextManager.ContextStack[:n]
if err := gui.deactivateContext(currentContext); err != nil { if err := gui.deactivateContext(currentContext); err != nil {
return err return err
@ -529,24 +556,30 @@ func (gui *Gui) activateContext(c Context) error {
} }
// currently unused // currently unused
// func (gui *Gui) renderContextStack() string { func (gui *Gui) renderContextStack() string {
// result := "" result := ""
// for _, context := range gui.State.ContextStack { for _, context := range gui.State.ContextManager.ContextStack {
// result += context.GetKey() + "\n" result += context.GetKey() + "\n"
// } }
// return result return result
// } }
func (gui *Gui) currentContext() Context { func (gui *Gui) currentContext() Context {
if len(gui.State.ContextStack) == 0 { gui.State.ContextManager.Lock()
defer gui.State.ContextManager.Unlock()
if len(gui.State.ContextManager.ContextStack) == 0 {
return gui.defaultSideContext() return gui.defaultSideContext()
} }
return gui.State.ContextStack[len(gui.State.ContextStack)-1] return gui.State.ContextManager.ContextStack[len(gui.State.ContextManager.ContextStack)-1]
} }
func (gui *Gui) currentSideContext() *ListContext { func (gui *Gui) currentSideContext() *ListContext {
stack := gui.State.ContextStack gui.State.ContextManager.Lock()
defer gui.State.ContextManager.Unlock()
stack := gui.State.ContextManager.ContextStack
// on startup the stack can be empty so we'll return an empty string in that case // on startup the stack can be empty so we'll return an empty string in that case
if len(stack) == 0 { if len(stack) == 0 {

View File

@ -5,9 +5,7 @@ import "github.com/go-errors/errors"
// SentinelErrors are the errors that have special meaning and need to be checked // SentinelErrors are the errors that have special meaning and need to be checked
// by calling functions. The less of these, the better // by calling functions. The less of these, the better
type SentinelErrors struct { type SentinelErrors struct {
ErrNoFiles error ErrNoFiles error
ErrSwitchRepo error
ErrRestart error
} }
const UNKNOWN_VIEW_ERROR_MSG = "unknown view" const UNKNOWN_VIEW_ERROR_MSG = "unknown view"
@ -24,14 +22,12 @@ const UNKNOWN_VIEW_ERROR_MSG = "unknown view"
// localising things in the code. // localising things in the code.
func (gui *Gui) GenerateSentinelErrors() { func (gui *Gui) GenerateSentinelErrors() {
gui.Errors = SentinelErrors{ gui.Errors = SentinelErrors{
ErrNoFiles: errors.New(gui.Tr.NoChangedFiles), ErrNoFiles: errors.New(gui.Tr.NoChangedFiles),
ErrSwitchRepo: errors.New("switching repo"),
} }
} }
func (gui *Gui) sentinelErrorsArr() []error { func (gui *Gui) sentinelErrorsArr() []error {
return []error{ return []error{
gui.Errors.ErrNoFiles, gui.Errors.ErrNoFiles,
gui.Errors.ErrSwitchRepo,
} }
} }

View File

@ -107,6 +107,10 @@ func (n *CommitFileNode) Flatten(collapsedPaths map[string]bool) []*CommitFileNo
} }
func (node *CommitFileNode) GetNodeAtIndex(index int, collapsedPaths map[string]bool) *CommitFileNode { func (node *CommitFileNode) GetNodeAtIndex(index int, collapsedPaths map[string]bool) *CommitFileNode {
if node == nil {
return nil
}
return getNodeAtIndex(node, index, collapsedPaths).(*CommitFileNode) return getNodeAtIndex(node, index, collapsedPaths).(*CommitFileNode)
} }

View File

@ -93,6 +93,10 @@ func (n *FileNode) Flatten(collapsedPaths map[string]bool) []*FileNode {
} }
func (node *FileNode) GetNodeAtIndex(index int, collapsedPaths map[string]bool) *FileNode { func (node *FileNode) GetNodeAtIndex(index int, collapsedPaths map[string]bool) *FileNode {
if node == nil {
return nil
}
return getNodeAtIndex(node, index, collapsedPaths).(*FileNode) return getNodeAtIndex(node, index, collapsedPaths).(*FileNode)
} }

View File

@ -48,6 +48,18 @@ const StartupPopupVersion = 3
// OverlappingEdges determines if panel edges overlap // OverlappingEdges determines if panel edges overlap
var OverlappingEdges = false var OverlappingEdges = false
type ContextManager struct {
ContextStack []Context
sync.Mutex
}
func NewContextManager(contexts ContextTree) ContextManager {
return ContextManager{
ContextStack: []Context{contexts.Files},
Mutex: sync.Mutex{},
}
}
// Gui wraps the gocui Gui object which handles rendering and events // Gui wraps the gocui Gui object which handles rendering and events
type Gui struct { type Gui struct {
g *gocui.Gui g *gocui.Gui
@ -83,6 +95,10 @@ type Gui struct {
// 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.
findSuggestions func(string) []*types.Suggestion findSuggestions func(string) []*types.Suggestion
// when you enter into a submodule we'll append the superproject's path to this array
// so that you can return to the superproject
RepoPathStack []string
} }
type RecordedEvent struct { type RecordedEvent struct {
@ -298,7 +314,7 @@ type guiState struct {
Modes Modes Modes Modes
ContextStack []Context ContextManager ContextManager
ViewContextMap map[string]Context ViewContextMap map[string]Context
// WindowViewNameMap is a mapping of windows to the current view of that window. // WindowViewNameMap is a mapping of windows to the current view of that window.
@ -306,35 +322,18 @@ type guiState struct {
// side windows we need to know which view to give focus to for a given window // side windows we need to know which view to give focus to for a given window
WindowViewNameMap map[string]string WindowViewNameMap map[string]string
// when you enter into a submodule we'll append the superproject's path to this array // tells us whether we've set up our views. We only do this once per repo
// so that you can return to the superproject ViewsSetup bool
RepoPathStack []string
} }
func (gui *Gui) resetState() { func (gui *Gui) resetState(filterPath string) {
// we carry over the filter path and diff state
prevFiltering := filtering.NewFiltering()
prevDiff := Diffing{}
prevCherryPicking := CherryPicking{
CherryPickedCommits: make([]*models.Commit, 0),
ContextKey: "",
}
prevRepoPathStack := []string{}
if gui.State != nil {
prevFiltering = gui.State.Modes.Filtering
prevDiff = gui.State.Modes.Diffing
prevCherryPicking = gui.State.Modes.CherryPicking
prevRepoPathStack = gui.State.RepoPathStack
}
modes := Modes{
Filtering: prevFiltering,
CherryPicking: prevCherryPicking,
Diffing: prevDiff,
}
showTree := gui.Config.GetUserConfig().Gui.ShowFileTree showTree := gui.Config.GetUserConfig().Gui.ShowFileTree
screenMode := SCREEN_NORMAL
if filterPath != "" {
screenMode = SCREEN_HALF
}
gui.State = &guiState{ gui.State = &guiState{
FileManager: filetree.NewFileManager(make([]*models.File, 0), gui.Log, showTree), FileManager: filetree.NewFileManager(make([]*models.File, 0), gui.Log, showTree),
CommitFileManager: filetree.NewCommitFileManager(make([]*models.CommitFile, 0), gui.Log, showTree), CommitFileManager: filetree.NewCommitFileManager(make([]*models.CommitFile, 0), gui.Log, showTree),
@ -365,18 +364,22 @@ func (gui *Gui) resetState() {
ConflictsMutex: sync.Mutex{}, ConflictsMutex: sync.Mutex{},
}, },
}, },
SideView: nil, SideView: nil,
Ptmx: nil, Ptmx: nil,
Modes: modes, Modes: Modes{
Filtering: filtering.NewFiltering(),
CherryPicking: CherryPicking{
CherryPickedCommits: make([]*models.Commit, 0),
ContextKey: "",
},
Diffing: Diffing{},
},
ViewContextMap: gui.initialViewContextMap(), ViewContextMap: gui.initialViewContextMap(),
RepoPathStack: prevRepoPathStack, ScreenMode: screenMode,
ContextManager: NewContextManager(gui.Contexts),
} }
if gui.State.Modes.Filtering.Active() { gui.ViewTabContextMap = gui.initialViewTabContextMap()
gui.State.ScreenMode = SCREEN_HALF
} else {
gui.State.ScreenMode = SCREEN_NORMAL
}
} }
// for now the split view will always be on // for now the split view will always be on
@ -393,12 +396,11 @@ func NewGui(log *logrus.Entry, gitCommand *commands.GitCommand, oSCommand *oscom
viewBufferManagerMap: map[string]*tasks.ViewBufferManager{}, viewBufferManagerMap: map[string]*tasks.ViewBufferManager{},
showRecentRepos: showRecentRepos, showRecentRepos: showRecentRepos,
RecordedEvents: []RecordedEvent{}, RecordedEvents: []RecordedEvent{},
RepoPathStack: []string{},
} }
gui.resetState()
gui.State.Modes.Filtering.SetPath(filterPath)
gui.Contexts = gui.contextTree() gui.Contexts = gui.contextTree()
gui.ViewTabContextMap = gui.viewTabContextMap() gui.resetState(filterPath)
gui.watchFilesForChanges() gui.watchFilesForChanges()
@ -409,8 +411,6 @@ func NewGui(log *logrus.Entry, gitCommand *commands.GitCommand, oSCommand *oscom
// Run setup the gui with keybindings and start the mainloop // Run setup the gui with keybindings and start the mainloop
func (gui *Gui) Run() error { func (gui *Gui) Run() error {
gui.resetState()
recordEvents := recordingEvents() recordEvents := recordingEvents()
g, err := gocui.NewGui(gocui.OutputTrue, OverlappingEdges, recordEvents) g, err := gocui.NewGui(gocui.OutputTrue, OverlappingEdges, recordEvents)
@ -457,6 +457,11 @@ func (gui *Gui) Run() error {
go utils.Safe(gui.startBackgroundFetch) go utils.Safe(gui.startBackgroundFetch)
} }
go func() {
gui.Updater.CheckForNewUpdate(gui.onBackgroundUpdateCheckFinish, false)
gui.waitForIntro.Done()
}()
gui.goEvery(time.Second*time.Duration(userConfig.Refresher.RefreshInterval), gui.stopChan, gui.refreshFilesAndSubmodules) gui.goEvery(time.Second*time.Duration(userConfig.Refresher.RefreshInterval), gui.stopChan, gui.refreshFilesAndSubmodules)
g.SetManager(gocui.ManagerFunc(gui.layout), gocui.ManagerFunc(gui.getFocusLayout())) g.SetManager(gocui.ManagerFunc(gui.layout), gocui.ManagerFunc(gui.getFocusLayout()))
@ -467,19 +472,17 @@ func (gui *Gui) Run() error {
return err return err
} }
// RunWithRestarts loops, instantiating a new gocui.Gui with each iteration // RunAndHandleError
// (i.e. when switching repos or restarting). If it's a random error, we quit func (gui *Gui) RunAndHandleError() error {
func (gui *Gui) RunWithRestarts() error {
gui.StartTime = time.Now() gui.StartTime = time.Now()
go utils.Safe(gui.replayRecordedEvents) go utils.Safe(gui.replayRecordedEvents)
for { gui.stopChan = make(chan struct{})
gui.stopChan = make(chan struct{}) return utils.SafeWithError(func() error {
if err := gui.Run(); err != nil { if err := gui.Run(); err != nil {
for _, manager := range gui.viewBufferManagerMap { for _, manager := range gui.viewBufferManagerMap {
manager.Close() manager.Close()
} }
gui.viewBufferManagerMap = map[string]*tasks.ViewBufferManager{}
if !gui.fileWatcher.Disabled { if !gui.fileWatcher.Disabled {
gui.fileWatcher.Watcher.Close() gui.fileWatcher.Watcher.Close()
@ -500,13 +503,14 @@ func (gui *Gui) RunWithRestarts() error {
} }
return nil return nil
case gui.Errors.ErrSwitchRepo:
continue
default: default:
return err return err
} }
} }
}
return nil
})
} }
func (gui *Gui) runSubprocessWithSuspense(subprocess *exec.Cmd) error { func (gui *Gui) runSubprocessWithSuspense(subprocess *exec.Cmd) error {
@ -551,11 +555,9 @@ func (gui *Gui) runSubprocess(subprocess *exec.Cmd) error {
} }
func (gui *Gui) loadNewRepo() error { func (gui *Gui) loadNewRepo() error {
gui.Updater.CheckForNewUpdate(gui.onBackgroundUpdateCheckFinish, false)
if err := gui.updateRecentRepoList(); err != nil { if err := gui.updateRecentRepoList(); err != nil {
return err return err
} }
gui.waitForIntro.Done()
if err := gui.refreshSidePanels(refreshOptions{mode: ASYNC}); err != nil { if err := gui.refreshSidePanels(refreshOptions{mode: ASYNC}); err != nil {
return err return err

View File

@ -1706,7 +1706,7 @@ func (gui *Gui) GetInitialKeybindings() []*Binding {
bindings = append(bindings, &Binding{ViewName: "", Key: rune(i+1) + '0', Modifier: gocui.ModNone, Handler: gui.goToSideWindow(window)}) bindings = append(bindings, &Binding{ViewName: "", Key: rune(i+1) + '0', Modifier: gocui.ModNone, Handler: gui.goToSideWindow(window)})
} }
for viewName := range gui.viewTabContextMap() { for viewName := range gui.initialViewTabContextMap() {
bindings = append(bindings, []*Binding{ bindings = append(bindings, []*Binding{
{ {
ViewName: viewName, ViewName: viewName,
@ -1741,7 +1741,7 @@ func (gui *Gui) keybindings() error {
} }
} }
for viewName := range gui.viewTabContextMap() { for viewName := range gui.initialViewTabContextMap() {
viewName := viewName viewName := viewName
tabClickCallback := func(tabIndex int) error { return gui.onViewTabClick(viewName, tabIndex) } tabClickCallback := func(tabIndex int) error { return gui.onViewTabClick(viewName, tabIndex) }

View File

@ -216,11 +216,6 @@ func (gui *Gui) layout(g *gocui.Gui) error {
} }
v.Frame = false v.Frame = false
v.FgColor = theme.OptionsColor v.FgColor = theme.OptionsColor
// doing this here because it'll only happen once
if err := gui.onInitialViewsCreation(); err != nil {
return err
}
} }
// this view takes up one character. Its only purpose is to show the slash when searching // this view takes up one character. Its only purpose is to show the slash when searching
@ -271,6 +266,14 @@ func (gui *Gui) layout(g *gocui.Gui) error {
gui.State.OldInformation = informationStr gui.State.OldInformation = informationStr
} }
if !gui.State.ViewsSetup {
if err := gui.onInitialViewsCreation(); err != nil {
return err
}
gui.State.ViewsSetup = true
}
if gui.g.CurrentView() == nil { if gui.g.CurrentView() == nil {
initialContext := gui.Contexts.Files initialContext := gui.Contexts.Files
if gui.State.Modes.Filtering.Active() { if gui.State.Modes.Filtering.Active() {
@ -323,8 +326,13 @@ func (gui *Gui) layout(g *gocui.Gui) error {
func (gui *Gui) onInitialViewsCreation() error { func (gui *Gui) onInitialViewsCreation() error {
gui.setInitialViewContexts() gui.setInitialViewContexts()
// add tabs to views // hide any popup views. This only applies when we've just switched repos
for _, viewName := range gui.popupViewNames() {
_, _ = gui.g.SetViewOnBottom(viewName)
}
gui.g.Mutexes.ViewsMutex.Lock() gui.g.Mutexes.ViewsMutex.Lock()
// add tabs to views
for _, view := range gui.g.Views() { for _, view := range gui.g.Views() {
tabs := gui.viewTabNames(view.Name()) tabs := gui.viewTabNames(view.Name())
if len(tabs) == 0 { if len(tabs) == 0 {

View File

@ -49,13 +49,13 @@ func (gui *Gui) handleTopLevelReturn() error {
} }
} }
repoPathStack := gui.State.RepoPathStack repoPathStack := gui.RepoPathStack
if len(repoPathStack) > 0 { if len(repoPathStack) > 0 {
n := len(repoPathStack) - 1 n := len(repoPathStack) - 1
path := repoPathStack[n] path := repoPathStack[n]
gui.State.RepoPathStack = repoPathStack[:n] gui.RepoPathStack = repoPathStack[:n]
return gui.dispatchSwitchToRepo(path) return gui.dispatchSwitchToRepo(path)
} }

View File

@ -5,6 +5,7 @@ import (
"path/filepath" "path/filepath"
"github.com/fatih/color" "github.com/fatih/color"
"github.com/jesseduffield/gocui"
"github.com/jesseduffield/lazygit/pkg/commands" "github.com/jesseduffield/lazygit/pkg/commands"
"github.com/jesseduffield/lazygit/pkg/env" "github.com/jesseduffield/lazygit/pkg/env"
"github.com/jesseduffield/lazygit/pkg/utils" "github.com/jesseduffield/lazygit/pkg/utils"
@ -24,6 +25,9 @@ func (gui *Gui) handleCreateRecentReposMenu() error {
yellow.Sprint(path), yellow.Sprint(path),
}, },
onPress: func() error { onPress: func() error {
// if we were in a submodule, we want to forget about that stack of repos
// so that hitting escape in the new repo does nothing
gui.RepoPathStack = []string{}
return gui.dispatchSwitchToRepo(path) return gui.dispatchSwitchToRepo(path)
}, },
} }
@ -73,8 +77,14 @@ func (gui *Gui) dispatchSwitchToRepo(path string) error {
return err return err
} }
gui.GitCommand = newGitCommand gui.GitCommand = newGitCommand
gui.State.Modes.Filtering.Reset()
return gui.Errors.ErrSwitchRepo gui.g.Update(func(*gocui.Gui) error {
gui.resetState("")
return nil
})
return nil
} }
// updateRecentRepoList registers the fact that we opened lazygit in this repo, // updateRecentRepoList registers the fact that we opened lazygit in this repo,

View File

@ -71,7 +71,7 @@ func (gui *Gui) enterSubmodule(submodule *models.SubmoduleConfig) error {
if err != nil { if err != nil {
return err return err
} }
gui.State.RepoPathStack = append(gui.State.RepoPathStack, wd) gui.RepoPathStack = append(gui.RepoPathStack, wd)
return gui.dispatchSwitchToRepo(submodule.Path) return gui.dispatchSwitchToRepo(submodule.Path)
} }

View File

@ -365,6 +365,10 @@ func ResolveTemplate(templateStr string, object interface{}) (string, error) {
// Safe will close tcell if a panic occurs so that we don't end up in a malformed // Safe will close tcell if a panic occurs so that we don't end up in a malformed
// terminal state // terminal state
func Safe(f func()) { func Safe(f func()) {
_ = SafeWithError(func() error { f(); return nil })
}
func SafeWithError(f func() error) error {
panicking := true panicking := true
defer func() { defer func() {
if panicking && gocui.Screen != nil { if panicking && gocui.Screen != nil {
@ -372,7 +376,9 @@ func Safe(f func()) {
} }
}() }()
f() err := f()
panicking = false panicking = false
return err
} }