Repository: jesseduffield/lazygit (language: go)

Issue:
Unsetting upstream when upstream is gone
**Describe the bug**
When the upstream for a branch is gone it says `(upstream gone)` and the option `Unset upstream of selected branch` is crossed, which is expected, _but_ if I rename the branch, the popup says `"This branch is tracking a remote. This action will only rename the local branch name etc."`, and I have no way of changing which upstream it will push to since it still tracks the old one which I cannot discard.

**To Reproduce**
Steps to reproduce the behavior:

1. create a new branch `test-branch`
2. push it to the upstream
3. delete the upstream
4. rename the branch to `test-branch-new-name`
5. push it
6. the upstream is `test-branch` instead of `test-branch-new-name`

**Expected behavior**
The branch pushes to the upstream with the branch names matching.

**Screenshots**
If applicable, add screenshots to help explain your problem.

**Version info:**
Latest `master` as of yesterday.

**Additional context**
I think we should let the users _unset_ the upstream of branches whose upstream is gone, I seem to recall that that was working at one point.


Discussion / hints:
Yes, I recently ran into this myself and had to fix it on the command line.

I'm not sure I agree with your "Expected behavior" though: I don't think it should silently push to a branch with a matching name without asking. Not if `git config push.default` is set to upstream, at least.

But yes, unsetting the upstream should be allowed in this case, and it shouldn't be too hard to fix. I think this broke with #2992.
> I don't think it should silently push to a branch with a matching name without asking. Not if git config push.default is set to upstream, at least.

Of course, I may have skipped a few steps, but my point was I don't want it being pushed onto the _old_ upstream name (which is also _gone_).
> my point was I don't want it being pushed onto the _old_ upstream name (which is also _gone_).

But how is lazygit (or git for that matter) supposed to know that this is not what you want? Maybe you deleted the upstream by accident, and want to recreate it by pushing to it again.

After all, that's also what a plain `git push` on the command line would do.

I think it's up to the user to fix the situation by unsetting the upstream so that a new one can be set when pushing next. So the only thing for us to fix is to make unsetting the upstream possible in this case.
> But how is lazygit (or git for that matter) supposed to know that this is not what you want?

I completely agree, it can't read minds, all I'm saying is _what I don't want to happen_, but _can't avoid_ due to the disabled option to unset the upstream. 

I'll take a stab at it soon-ish, I presume.

Fix patch:
diff --git a/pkg/gui/controllers/branches_controller.go b/pkg/gui/controllers/branches_controller.go
index 403c51b4f91..3de005a2b97 100644
--- a/pkg/gui/controllers/branches_controller.go
+++ b/pkg/gui/controllers/branches_controller.go
@@ -257,9 +257,12 @@ func (self *BranchesController) viewUpstreamOptions(selectedBranch *models.Branc
 		Key:     'r',
 	}
 
+	if !selectedBranch.IsTrackingRemote() {
+		unsetUpstreamItem.DisabledReason = self.c.Tr.UpstreamNotSetError
+	}
+
 	if !selectedBranch.RemoteBranchStoredLocally() {
 		viewDivergenceItem.DisabledReason = self.c.Tr.UpstreamNotSetError
-		unsetUpstreamItem.DisabledReason = self.c.Tr.UpstreamNotSetError
 		upstreamResetItem.DisabledReason = self.c.Tr.UpstreamNotSetError
 		upstreamRebaseItem.DisabledReason = self.c.Tr.UpstreamNotSetError
 	}


Test patch:
diff --git a/pkg/integration/tests/branch/reset_upstream.go b/pkg/integration/tests/branch/reset_upstream.go
deleted file mode 100644
index 70300c31201..00000000000
--- a/pkg/integration/tests/branch/reset_upstream.go
+++ /dev/null
@@ -1,36 +0,0 @@
-package branch
-
-import (
-	"github.com/jesseduffield/lazygit/pkg/config"
-	. "github.com/jesseduffield/lazygit/pkg/integration/components"
-)
-
-var ResetUpstream = NewIntegrationTest(NewIntegrationTestArgs{
-	Description:  "Reset the upstream of a branch",
-	ExtraCmdArgs: []string{},
-	Skip:         false,
-	SetupConfig:  func(config *config.AppConfig) {},
-	SetupRepo: func(shell *Shell) {
-		shell.EmptyCommit("one")
-		shell.CloneIntoRemote("origin")
-		shell.SetBranchUpstream("master", "origin/master")
-	},
-	Run: func(t *TestDriver, keys config.KeybindingConfig) {
-		t.Views().Branches().
-			Focus().
-			Press(keys.Universal.NextScreenMode). // we need to enlargen the window to see the upstream
-			Lines(
-				Contains("master").Contains("origin master").IsSelected(),
-			).
-			Press(keys.Branches.SetUpstream).
-			Tap(func() {
-				t.ExpectPopup().Menu().
-					Title(Equals("Upstream options")).
-					Select(Contains("Unset upstream of selected branch")).
-					Confirm()
-			}).
-			Lines(
-				Contains("master").DoesNotContain("origin master").IsSelected(),
-			)
-	},
-})
diff --git a/pkg/integration/tests/branch/unset_upstream.go b/pkg/integration/tests/branch/unset_upstream.go
new file mode 100644
index 00000000000..39454d2acc9
--- /dev/null
+++ b/pkg/integration/tests/branch/unset_upstream.go
@@ -0,0 +1,59 @@
+package branch
+
+import (
+	"github.com/jesseduffield/lazygit/pkg/config"
+	. "github.com/jesseduffield/lazygit/pkg/integration/components"
+)
+
+var UnsetUpstream = NewIntegrationTest(NewIntegrationTestArgs{
+	Description:  "Unset upstream of selected branch, both when it exists and when it doesn't",
+	ExtraCmdArgs: []string{},
+	Skip:         false,
+	SetupConfig:  func(config *config.AppConfig) {},
+	SetupRepo: func(shell *Shell) {
+		shell.
+			EmptyCommit("one").
+			NewBranch("branch_to_remove").
+			Checkout("master").
+			CloneIntoRemote("origin").
+			SetBranchUpstream("master", "origin/master").
+			SetBranchUpstream("branch_to_remove", "origin/branch_to_remove").
+			// to get the "(upstream gone)" branch status
+			RunCommand([]string{"git", "push", "origin", "--delete", "branch_to_remove"})
+	},
+	Run: func(t *TestDriver, keys config.KeybindingConfig) {
+		t.Views().Branches().
+			Focus().
+			Press(keys.Universal.NextScreenMode). // we need to enlargen the window to see the upstream
+			SelectedLines(
+				Contains("master").Contains("origin master"),
+			).
+			Press(keys.Branches.SetUpstream).
+			Tap(func() {
+				t.ExpectPopup().Menu().
+					Title(Equals("Upstream options")).
+					Select(Contains("Unset upstream of selected branch")).
+					Confirm()
+			}).
+			SelectedLines(
+				Contains("master").DoesNotContain("origin master"),
+			)
+
+		t.Views().Branches().
+			Focus().
+			SelectNextItem().
+			SelectedLines(
+				Contains("branch_to_remove").Contains("origin branch_to_remove").Contains("upstream gone"),
+			).
+			Press(keys.Branches.SetUpstream).
+			Tap(func() {
+				t.ExpectPopup().Menu().
+					Title(Equals("Upstream options")).
+					Select(Contains("Unset upstream of selected branch")).
+					Confirm()
+			}).
+			SelectedLines(
+				Contains("branch_to_remove").DoesNotContain("origin branch_to_remove").DoesNotContain("upstream gone"),
+			)
+	},
+})
diff --git a/pkg/integration/tests/test_list.go b/pkg/integration/tests/test_list.go
index cea838380f0..5716507f846 100644
--- a/pkg/integration/tests/test_list.go
+++ b/pkg/integration/tests/test_list.go
@@ -52,10 +52,10 @@ var tests = []*components.IntegrationTest{
 	branch.Rename,
 	branch.Reset,
 	branch.ResetToUpstream,
-	branch.ResetUpstream,
 	branch.SetUpstream,
 	branch.ShowDivergenceFromUpstream,
 	branch.Suggestions,
+	branch.UnsetUpstream,
 	cherry_pick.CherryPick,
 	cherry_pick.CherryPickConflicts,
 	cherry_pick.CherryPickDuringRebase,