1
0
mirror of https://github.com/jesseduffield/lazygit.git synced 2025-06-02 23:27:32 +02:00

avoid deadlock in merge panel

This commit is contained in:
Jesse Duffield 2022-01-15 13:29:28 +11:00
parent cdcfeb396f
commit 8d8bdb948b
9 changed files with 79 additions and 59 deletions

View File

@ -4,10 +4,25 @@ import (
"errors" "errors"
) )
var CONTEXT_KEYS_SHOWING_DIFFS = []ContextKey{
FILES_CONTEXT_KEY,
COMMIT_FILES_CONTEXT_KEY,
STASH_CONTEXT_KEY,
BRANCH_COMMITS_CONTEXT_KEY,
SUB_COMMITS_CONTEXT_KEY,
MAIN_STAGING_CONTEXT_KEY,
MAIN_PATCH_BUILDING_CONTEXT_KEY,
}
func isShowingDiff(gui *Gui) bool { func isShowingDiff(gui *Gui) bool {
key := gui.currentStaticContext().GetKey() key := gui.currentStaticContext().GetKey()
return key == FILES_CONTEXT_KEY || key == COMMIT_FILES_CONTEXT_KEY || key == STASH_CONTEXT_KEY || key == BRANCH_COMMITS_CONTEXT_KEY || key == SUB_COMMITS_CONTEXT_KEY || key == MAIN_STAGING_CONTEXT_KEY || key == MAIN_PATCH_BUILDING_CONTEXT_KEY for _, contextKey := range CONTEXT_KEYS_SHOWING_DIFFS {
if key == contextKey {
return true
}
}
return false
} }
func (gui *Gui) IncreaseContextInDiffView() error { func (gui *Gui) IncreaseContextInDiffView() error {

View File

@ -65,7 +65,9 @@ func TestDoesntIncreaseContextInDiffViewInContextWithoutDiff(t *testing.T) {
func(gui *Gui) Context { return gui.State.Contexts.ReflogCommits }, func(gui *Gui) Context { return gui.State.Contexts.ReflogCommits },
func(gui *Gui) Context { return gui.State.Contexts.RemoteBranches }, func(gui *Gui) Context { return gui.State.Contexts.RemoteBranches },
func(gui *Gui) Context { return gui.State.Contexts.Tags }, func(gui *Gui) Context { return gui.State.Contexts.Tags },
func(gui *Gui) Context { return gui.State.Contexts.Merging }, // not testing this because it will kick straight back to the files context
// upon pushing the context
// func(gui *Gui) Context { return gui.State.Contexts.Merging },
func(gui *Gui) Context { return gui.State.Contexts.CommandLog }, func(gui *Gui) Context { return gui.State.Contexts.CommandLog },
} }
@ -115,7 +117,9 @@ func TestDoesntDecreaseContextInDiffViewInContextWithoutDiff(t *testing.T) {
func(gui *Gui) Context { return gui.State.Contexts.ReflogCommits }, func(gui *Gui) Context { return gui.State.Contexts.ReflogCommits },
func(gui *Gui) Context { return gui.State.Contexts.RemoteBranches }, func(gui *Gui) Context { return gui.State.Contexts.RemoteBranches },
func(gui *Gui) Context { return gui.State.Contexts.Tags }, func(gui *Gui) Context { return gui.State.Contexts.Tags },
func(gui *Gui) Context { return gui.State.Contexts.Merging }, // not testing this because it will kick straight back to the files context
// upon pushing the context
// func(gui *Gui) Context { return gui.State.Contexts.Merging },
func(gui *Gui) Context { return gui.State.Contexts.CommandLog }, func(gui *Gui) Context { return gui.State.Contexts.CommandLog },
} }

View File

@ -54,7 +54,7 @@ func (gui *Gui) filesRenderToMain() error {
} }
if node.File != nil && node.File.HasInlineMergeConflicts { if node.File != nil && node.File.HasInlineMergeConflicts {
return gui.refreshMergePanelWithLock() return gui.renderConflictsFromFilesPanel()
} }
cmdObj := gui.Git.WorkingTree.WorktreeFileDiffCmdObj(node, false, !node.GetHasUnstagedChanges() && node.GetHasStagedChanges(), gui.State.IgnoreWhitespaceInDiffView) cmdObj := gui.Git.WorkingTree.WorktreeFileDiffCmdObj(node, false, !node.GetHasUnstagedChanges() && node.GetHasStagedChanges(), gui.State.IgnoreWhitespaceInDiffView)
@ -182,7 +182,7 @@ func (gui *Gui) enterFile(opts OnFocusOpts) error {
} }
if file.HasInlineMergeConflicts { if file.HasInlineMergeConflicts {
return gui.handleSwitchToMerge() return gui.switchToMerge()
} }
if file.HasMergeConflicts { if file.HasMergeConflicts {
return gui.createErrorPanel(gui.Tr.FileStagingRequirements) return gui.createErrorPanel(gui.Tr.FileStagingRequirements)
@ -201,7 +201,7 @@ func (gui *Gui) handleFilePress() error {
file := node.File file := node.File
if file.HasInlineMergeConflicts { if file.HasInlineMergeConflicts {
return gui.handleSwitchToMerge() return gui.switchToMerge()
} }
if file.HasUnstagedChanges { if file.HasUnstagedChanges {
@ -880,16 +880,12 @@ func (gui *Gui) upstreamForBranchInConfig(branchName string) (string, string, er
return "", "", nil return "", "", nil
} }
func (gui *Gui) handleSwitchToMerge() error { func (gui *Gui) switchToMerge() error {
file := gui.getSelectedFile() file := gui.getSelectedFile()
if file == nil { if file == nil {
return nil return nil
} }
if !file.HasInlineMergeConflicts {
return gui.createErrorPanel(gui.Tr.FileNoMergeCons)
}
return gui.pushContext(gui.State.Contexts.Merging) return gui.pushContext(gui.State.Contexts.Merging)
} }

View File

@ -39,7 +39,7 @@ func (gui *Gui) getMenuOptions() map[string]string {
} }
func (gui *Gui) handleMenuClose() error { func (gui *Gui) handleMenuClose() error {
return gui.returnFromContextSync() return gui.returnFromContext()
} }
type createMenuOptions struct { type createMenuOptions struct {

View File

@ -90,6 +90,7 @@ func (gui *Gui) handlePickHunk() error {
if err := gui.handleCompleteMerge(); err != nil { if err := gui.handleCompleteMerge(); err != nil {
return err return err
} }
return nil
} }
return gui.refreshMergePanel() return gui.refreshMergePanel()
}) })
@ -151,11 +152,19 @@ func (gui *Gui) refreshMergePanelWithLock() error {
return gui.withMergeConflictLock(gui.refreshMergePanel) return gui.withMergeConflictLock(gui.refreshMergePanel)
} }
func (gui *Gui) refreshMergePanel() error { // not re-using state here because we can run into issues with mutexes when
panelState := gui.State.Panels.Merging // doing that.
func (gui *Gui) renderConflictsFromFilesPanel() error {
state := mergeconflicts.NewState()
_, err := gui.renderConflicts(state, false)
return err
}
func (gui *Gui) renderConflicts(state *mergeconflicts.State, hasFocus bool) (bool, error) {
cat, err := gui.catSelectedFile() cat, err := gui.catSelectedFile()
if err != nil { if err != nil {
return gui.refreshMainViews(refreshMainOpts{ return false, gui.refreshMainViews(refreshMainOpts{
main: &viewUpdateOpts{ main: &viewUpdateOpts{
title: "", title: "",
task: NewRenderStringTask(err.Error()), task: NewRenderStringTask(err.Error()),
@ -163,20 +172,20 @@ func (gui *Gui) refreshMergePanel() error {
}) })
} }
panelState.SetConflictsFromCat(cat) state.SetConflictsFromCat(cat)
if panelState.NoConflicts() { if state.NoConflicts() {
return gui.handleCompleteMerge() // we shouldn't end up here
return false, nil
} }
hasFocus := gui.currentViewName() == "main" content := mergeconflicts.ColoredConflictFile(cat, state, hasFocus)
content := mergeconflicts.ColoredConflictFile(cat, panelState.State, hasFocus)
if err := gui.scrollToConflict(); err != nil { if !gui.State.Panels.Merging.UserVerticalScrolling {
return err gui.centerYPos(gui.Views.Main, state.GetConflictMiddle())
} }
return gui.refreshMainViews(refreshMainOpts{ return true, gui.refreshMainViews(refreshMainOpts{
main: &viewUpdateOpts{ main: &viewUpdateOpts{
title: gui.Tr.MergeConflictsTitle, title: gui.Tr.MergeConflictsTitle,
task: NewRenderStringWithoutScrollTask(content), task: NewRenderStringWithoutScrollTask(content),
@ -185,6 +194,19 @@ func (gui *Gui) refreshMergePanel() error {
}) })
} }
func (gui *Gui) refreshMergePanel() error {
conflictsFound, err := gui.renderConflicts(gui.State.Panels.Merging.State, true)
if err != nil {
return err
}
if !conflictsFound {
return gui.handleCompleteMerge()
}
return nil
}
func (gui *Gui) catSelectedFile() (string, error) { func (gui *Gui) catSelectedFile() (string, error) {
item := gui.getSelectedFile() item := gui.getSelectedFile()
if item == nil { if item == nil {
@ -203,21 +225,6 @@ func (gui *Gui) catSelectedFile() (string, error) {
return cat, nil return cat, nil
} }
func (gui *Gui) scrollToConflict() error {
if gui.State.Panels.Merging.UserVerticalScrolling {
return nil
}
panelState := gui.State.Panels.Merging
if panelState.NoConflicts() {
return nil
}
gui.centerYPos(gui.Views.Main, panelState.GetConflictMiddle())
return nil
}
func (gui *Gui) centerYPos(view *gocui.View, y int) { func (gui *Gui) centerYPos(view *gocui.View, y int) {
ox, _ := view.Origin() ox, _ := view.Origin()
_, height := view.Size() _, height := view.Size()
@ -238,18 +245,11 @@ func (gui *Gui) getMergingOptions() map[string]string {
} }
func (gui *Gui) handleEscapeMerge() error { func (gui *Gui) handleEscapeMerge() error {
gui.takeOverMergeConflictScrolling()
gui.State.Panels.Merging.Reset()
if err := gui.refreshSidePanels(refreshOptions{scope: []RefreshableView{FILES}}); err != nil { if err := gui.refreshSidePanels(refreshOptions{scope: []RefreshableView{FILES}}); err != nil {
return err return err
} }
// it's possible this method won't be called from the merging view so we need to
// ensure we only 'return' focus if we already have it return gui.escapeMerge()
if gui.g.CurrentView() == gui.Views.Main {
return gui.pushContext(gui.State.Contexts.Files)
}
return nil
} }
func (gui *Gui) handleCompleteMerge() error { func (gui *Gui) handleCompleteMerge() error {
@ -259,16 +259,26 @@ func (gui *Gui) handleCompleteMerge() error {
if err := gui.refreshSidePanels(refreshOptions{scope: []RefreshableView{FILES}}); err != nil { if err := gui.refreshSidePanels(refreshOptions{scope: []RefreshableView{FILES}}); err != nil {
return err return err
} }
// if we got conflicts after unstashing, we don't want to call any git
// commands to continue rebasing/merging here
if gui.Git.Status.WorkingTreeState() == enums.REBASE_MODE_NONE {
return gui.handleEscapeMerge()
}
// if there are no more files with merge conflicts, we should ask whether the user wants to continue // if there are no more files with merge conflicts, we should ask whether the user wants to continue
if !gui.anyFilesWithMergeConflicts() { if gui.Git.Status.WorkingTreeState() != enums.REBASE_MODE_NONE && !gui.anyFilesWithMergeConflicts() {
return gui.promptToContinueRebase() return gui.promptToContinueRebase()
} }
return gui.handleEscapeMerge()
return gui.escapeMerge()
}
func (gui *Gui) escapeMerge() error {
gui.takeOverMergeConflictScrolling()
gui.State.Panels.Merging.Reset()
// it's possible this method won't be called from the merging view so we need to
// ensure we only 'return' focus if we already have it
if gui.g.CurrentView() == gui.Views.Main {
return gui.pushContext(gui.State.Contexts.Files)
}
return nil
} }
// promptToContinueRebase asks the user if they want to continue the rebase/merge that's in progress // promptToContinueRebase asks the user if they want to continue the rebase/merge that's in progress

View File

@ -73,7 +73,6 @@ func chineseTranslationSet() TranslationSet {
PullWait: "拉取中……", PullWait: "拉取中……",
PushWait: "推送中……", PushWait: "推送中……",
FetchWait: "正在抓取……", FetchWait: "正在抓取……",
FileNoMergeCons: "该文件没有合并冲突",
LcSoftReset: "软重置", LcSoftReset: "软重置",
AlreadyCheckedOutBranch: "您已经检出了这个分支", AlreadyCheckedOutBranch: "您已经检出了这个分支",
SureForceCheckout: "您确定要强制检出吗?您将丢失所有本地更改", SureForceCheckout: "您确定要强制检出吗?您将丢失所有本地更改",

View File

@ -44,7 +44,6 @@ func dutchTranslationSet() TranslationSet {
PullWait: "Pullen...", PullWait: "Pullen...",
PushWait: "Pushen...", PushWait: "Pushen...",
FetchWait: "Fetchen...", FetchWait: "Fetchen...",
FileNoMergeCons: "Dit bestand heeft geen merge conflicten",
LcSoftReset: "zacht reset", LcSoftReset: "zacht reset",
AlreadyCheckedOutBranch: "Je hebt deze branch al uitgecheckt", AlreadyCheckedOutBranch: "Je hebt deze branch al uitgecheckt",
SureForceCheckout: "Weet je zeker dat je het uitchecken wil forceren? Al je lokale verandering zullen worden verwijdert", SureForceCheckout: "Weet je zeker dat je het uitchecken wil forceren? Al je lokale verandering zullen worden verwijdert",

View File

@ -58,7 +58,6 @@ type TranslationSet struct {
PullWait string PullWait string
PushWait string PushWait string
FetchWait string FetchWait string
FileNoMergeCons string
LcSoftReset string LcSoftReset string
AlreadyCheckedOutBranch string AlreadyCheckedOutBranch string
SureForceCheckout string SureForceCheckout string
@ -608,7 +607,6 @@ func EnglishTranslationSet() TranslationSet {
PullWait: "Pulling...", PullWait: "Pulling...",
PushWait: "Pushing...", PushWait: "Pushing...",
FetchWait: "Fetching...", FetchWait: "Fetching...",
FileNoMergeCons: "This file has no inline merge conflicts",
LcSoftReset: "soft reset", LcSoftReset: "soft reset",
AlreadyCheckedOutBranch: "You have already checked out this branch", AlreadyCheckedOutBranch: "You have already checked out this branch",
SureForceCheckout: "Are you sure you want force checkout? You will lose all local changes", SureForceCheckout: "Are you sure you want force checkout? You will lose all local changes",

View File

@ -39,7 +39,6 @@ func polishTranslationSet() TranslationSet {
PullWait: "Pobieranie zmian...", PullWait: "Pobieranie zmian...",
PushWait: "Wysyłanie zmian...", PushWait: "Wysyłanie zmian...",
FetchWait: "Pobieram...", FetchWait: "Pobieram...",
FileNoMergeCons: "Brak konfliktów scalania w pliku",
AlreadyCheckedOutBranch: "Już przęłączono na tą gałąź", AlreadyCheckedOutBranch: "Już przęłączono na tą gałąź",
SureForceCheckout: "Jesteś pewny, że chcesz wymusić przełączenie? Stracisz wszystkie lokalne zmiany", SureForceCheckout: "Jesteś pewny, że chcesz wymusić przełączenie? Stracisz wszystkie lokalne zmiany",
ForceCheckoutBranch: "Wymuś przełączenie gałęzi", ForceCheckoutBranch: "Wymuś przełączenie gałęzi",