From 6f09824552a5589bf03208c0e5baab2e1ad9a953 Mon Sep 17 00:00:00 2001 From: Maria Solano Date: Sat, 20 Jun 2026 16:26:58 -0700 Subject: [PATCH] Refresh main branch cache after branch deletion --- pkg/commands/git_commands/main_branches.go | 13 +++-- pkg/gui/controllers/helpers/refresh_helper.go | 13 ++++- .../tests/branch/delete_after_main_branch.go | 49 +++++++++++++++++++ pkg/integration/tests/test_list.go | 1 + 4 files changed, 72 insertions(+), 4 deletions(-) create mode 100644 pkg/integration/tests/branch/delete_after_main_branch.go diff --git a/pkg/commands/git_commands/main_branches.go b/pkg/commands/git_commands/main_branches.go index 109f9cfd847..ed96ed75dee 100644 --- a/pkg/commands/git_commands/main_branches.go +++ b/pkg/commands/git_commands/main_branches.go @@ -46,12 +46,19 @@ func (self *MainBranches) Get() []string { if self.existingMainBranches == nil || !slices.Equal(self.previousMainBranches, configuredMainBranches) { self.existingMainBranches = self.determineMainBranches(configuredMainBranches) - self.previousMainBranches = configuredMainBranches + self.previousMainBranches = slices.Clone(configuredMainBranches) } return self.existingMainBranches } +func (self *MainBranches) Invalidate() { + self.mutex.Lock() + defer self.mutex.Unlock() + + self.existingMainBranches = nil +} + // Return the merge base of the given refName with the closest main branch. func (self *MainBranches) GetMergeBase(refName string) string { mainBranches := self.Get() @@ -66,8 +73,8 @@ func (self *MainBranches) GetMergeBase(refName string) string { // error is because one of the main branches has been deleted since the last // call to determineMainBranches, or because the refName has no common // history with any of the main branches. Since the former should happen - // very rarely, users must quit and restart lazygit to fix it; the latter is - // also not very common, but can totally happen and is not an error. + // very rarely and will be fixed on the next branch/commit refresh, we treat + // both cases as no merge base for now. output, _, _ := self.cmd.New( NewGitCmd("merge-base").Arg(refName).Arg(mainBranches...). diff --git a/pkg/gui/controllers/helpers/refresh_helper.go b/pkg/gui/controllers/helpers/refresh_helper.go index 31035d1040d..27c6f2500f4 100644 --- a/pkg/gui/controllers/helpers/refresh_helper.go +++ b/pkg/gui/controllers/helpers/refresh_helper.go @@ -136,6 +136,7 @@ func (self *RefreshHelper) Refresh(options types.RefreshOptions) { // refreshing; the risk is one potential extra refresh, but capturing the // snapshot at the end would risk missing one, which is worse. self.updateRefsSnapshotIfRelevant(scopeSet) + self.invalidateMainBranchesIfRelevant(scopeSet) wg := sync.WaitGroup{} refresh := func(name string, f func()) { @@ -306,7 +307,7 @@ func (self *RefreshHelper) RefsSnapshotChangedSince(snapshot string) bool { // top of Refresh has already added these whenever REFLOG or BISECT_INFO are // in scope, and whenever a nil scope was passed. func (self *RefreshHelper) updateRefsSnapshotIfRelevant(scopeSet *set.Set[types.RefreshableView]) { - if !scopeSet.Includes(types.COMMITS) && !scopeSet.Includes(types.BRANCHES) { + if !refreshesRefs(scopeSet) { return } @@ -318,6 +319,16 @@ func (self *RefreshHelper) updateRefsSnapshotIfRelevant(scopeSet *set.Set[types. self.SetRefsSnapshot(snapshot) } +func (self *RefreshHelper) invalidateMainBranchesIfRelevant(scopeSet *set.Set[types.RefreshableView]) { + if refreshesRefs(scopeSet) { + self.c.Model().MainBranches.Invalidate() + } +} + +func refreshesRefs(scopeSet *set.Set[types.RefreshableView]) bool { + return scopeSet.Includes(types.COMMITS) || scopeSet.Includes(types.BRANCHES) +} + func getScopeNames(scopes []types.RefreshableView) []string { scopeNameMap := map[types.RefreshableView]string{ types.COMMITS: "commits", diff --git a/pkg/integration/tests/branch/delete_after_main_branch.go b/pkg/integration/tests/branch/delete_after_main_branch.go new file mode 100644 index 00000000000..06527732337 --- /dev/null +++ b/pkg/integration/tests/branch/delete_after_main_branch.go @@ -0,0 +1,49 @@ +package branch + +import ( + "github.com/jesseduffield/lazygit/pkg/config" + . "github.com/jesseduffield/lazygit/pkg/integration/components" +) + +var DeleteAfterMainBranch = NewIntegrationTest(NewIntegrationTestArgs{ + Description: "Delete a local branch after deleting one of the configured main branches", + ExtraCmdArgs: []string{}, + Skip: false, + SetupConfig: func(config *config.AppConfig) {}, + SetupRepo: func(shell *Shell) { + shell. + EmptyCommit("first commit"). + NewBranch("main"). + Checkout("master"). + NewBranch("dev"). + Checkout("master") + }, + Run: func(t *TestDriver, keys config.KeybindingConfig) { + t.Views().Branches(). + Focus(). + NavigateToLine(Contains("main")). + Press(keys.Universal.Remove). + Tap(func() { + t.ExpectPopup(). + Menu(). + Title(Equals("Delete branch 'main'?")). + Select(Contains("Delete local branch")). + Confirm() + }). + Lines( + Contains("master"), + Contains("dev").IsSelected(), + ). + Press(keys.Universal.Remove). + Tap(func() { + t.ExpectPopup(). + Menu(). + Title(Equals("Delete branch 'dev'?")). + Select(Contains("Delete local branch")). + Confirm() + }). + Lines( + Contains("master").IsSelected(), + ) + }, +}) diff --git a/pkg/integration/tests/test_list.go b/pkg/integration/tests/test_list.go index 1b264e50dc9..bea94af9972 100644 --- a/pkg/integration/tests/test_list.go +++ b/pkg/integration/tests/test_list.go @@ -44,6 +44,7 @@ var tests = []*components.IntegrationTest{ branch.CheckoutPreviousBranch, branch.CreateTag, branch.Delete, + branch.DeleteAfterMainBranch, branch.DeleteMultiple, branch.DeleteRemoteBranchWhenTagWithSameNameExists, branch.DeleteRemoteBranchWithCredentialPrompt,