Skip to content

Commit de22238

Browse files
Merge pull request #2224 from Ryooooooga/ambiguous-branch
2 parents e953659 + 52a2e4c commit de22238

39 files changed

+150
-43
lines changed

pkg/commands/git.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -119,9 +119,9 @@ func NewGitCommandAux(
119119
patchCommands := git_commands.NewPatchCommands(gitCommon, rebaseCommands, commitCommands, statusCommands, stashCommands, patchManager)
120120
bisectCommands := git_commands.NewBisectCommands(gitCommon)
121121

122-
branchLoader := git_commands.NewBranchLoader(cmn, branchCommands.GetRawBranches, branchCommands.CurrentBranchName, configCommands)
122+
branchLoader := git_commands.NewBranchLoader(cmn, branchCommands.GetRawBranches, branchCommands.CurrentBranchInfo, configCommands)
123123
commitFileLoader := git_commands.NewCommitFileLoader(cmn, cmd)
124-
commitLoader := git_commands.NewCommitLoader(cmn, cmd, dotGitDir, branchCommands.CurrentBranchName, statusCommands.RebaseMode)
124+
commitLoader := git_commands.NewCommitLoader(cmn, cmd, dotGitDir, branchCommands.CurrentBranchInfo, statusCommands.RebaseMode)
125125
reflogCommitLoader := git_commands.NewReflogCommitLoader(cmn, cmd)
126126
remoteLoader := git_commands.NewRemoteLoader(cmn, cmd, repo.Remotes)
127127
stashLoader := git_commands.NewStashLoader(cmn, cmd)

pkg/commands/git_commands/branch.go

Lines changed: 18 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -30,29 +30,39 @@ func (self *BranchCommands) New(name string, base string) error {
3030
return self.cmd.New(fmt.Sprintf("git checkout -b %s %s", self.cmd.Quote(name), self.cmd.Quote(base))).Run()
3131
}
3232

33-
// CurrentBranchName get the current branch name and displayname.
34-
// the first returned string is the name and the second is the displayname
35-
// e.g. name is 123asdf and displayname is '(HEAD detached at 123asdf)'
36-
func (self *BranchCommands) CurrentBranchName() (string, string, error) {
33+
// CurrentBranchInfo get the current branch information.
34+
func (self *BranchCommands) CurrentBranchInfo() (BranchInfo, error) {
3735
branchName, err := self.cmd.New("git symbolic-ref --short HEAD").DontLog().RunWithOutput()
3836
if err == nil && branchName != "HEAD\n" {
3937
trimmedBranchName := strings.TrimSpace(branchName)
40-
return trimmedBranchName, trimmedBranchName, nil
38+
return BranchInfo{
39+
RefName: trimmedBranchName,
40+
DisplayName: trimmedBranchName,
41+
DetachedHead: false,
42+
}, nil
4143
}
4244
output, err := self.cmd.New("git branch --contains").DontLog().RunWithOutput()
4345
if err != nil {
44-
return "", "", err
46+
return BranchInfo{}, err
4547
}
4648
for _, line := range utils.SplitLines(output) {
4749
re := regexp.MustCompile(CurrentBranchNameRegex)
4850
match := re.FindStringSubmatch(line)
4951
if len(match) > 0 {
5052
branchName = match[1]
5153
displayBranchName := match[0][2:]
52-
return branchName, displayBranchName, nil
54+
return BranchInfo{
55+
RefName: branchName,
56+
DisplayName: displayBranchName,
57+
DetachedHead: true,
58+
}, nil
5359
}
5460
}
55-
return "HEAD", "HEAD", nil
61+
return BranchInfo{
62+
RefName: "HEAD",
63+
DisplayName: "HEAD",
64+
DetachedHead: true,
65+
}, nil
5666
}
5767

5868
// Delete delete branch

pkg/commands/git_commands/branch_loader.go

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -27,24 +27,30 @@ type BranchLoaderConfigCommands interface {
2727
Branches() (map[string]*config.Branch, error)
2828
}
2929

30+
type BranchInfo struct {
31+
RefName string
32+
DisplayName string // e.g. '(HEAD detached at 123asdf)'
33+
DetachedHead bool
34+
}
35+
3036
// BranchLoader returns a list of Branch objects for the current repo
3137
type BranchLoader struct {
3238
*common.Common
3339
getRawBranches func() (string, error)
34-
getCurrentBranchName func() (string, string, error)
40+
getCurrentBranchInfo func() (BranchInfo, error)
3541
config BranchLoaderConfigCommands
3642
}
3743

3844
func NewBranchLoader(
3945
cmn *common.Common,
4046
getRawBranches func() (string, error),
41-
getCurrentBranchName func() (string, string, error),
47+
getCurrentBranchInfo func() (BranchInfo, error),
4248
config BranchLoaderConfigCommands,
4349
) *BranchLoader {
4450
return &BranchLoader{
4551
Common: cmn,
4652
getRawBranches: getRawBranches,
47-
getCurrentBranchName: getCurrentBranchName,
53+
getCurrentBranchInfo: getCurrentBranchInfo,
4854
config: config,
4955
}
5056
}
@@ -84,11 +90,11 @@ outer:
8490
}
8591
}
8692
if !foundHead {
87-
currentBranchName, currentBranchDisplayName, err := self.getCurrentBranchName()
93+
info, err := self.getCurrentBranchInfo()
8894
if err != nil {
8995
return nil, err
9096
}
91-
branches = slices.Prepend(branches, &models.Branch{Name: currentBranchName, DisplayName: currentBranchDisplayName, Head: true, Recency: " *"})
97+
branches = slices.Prepend(branches, &models.Branch{Name: info.RefName, DisplayName: info.DisplayName, Head: true, DetachedHead: info.DetachedHead, Recency: " *"})
9298
}
9399

94100
configBranches, err := self.config.Branches()

pkg/commands/git_commands/branch_test.go

Lines changed: 22 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -53,10 +53,10 @@ func TestBranchGetCommitDifferences(t *testing.T) {
5353

5454
func TestBranchNewBranch(t *testing.T) {
5555
runner := oscommands.NewFakeRunner(t).
56-
Expect(`git checkout -b "test" "master"`, "", nil)
56+
Expect(`git checkout -b "test" "refs/heads/master"`, "", nil)
5757
instance := buildBranchCommands(commonDeps{runner: runner})
5858

59-
assert.NoError(t, instance.New("test", "master"))
59+
assert.NoError(t, instance.New("test", "refs/heads/master"))
6060
runner.CheckForMissingCalls()
6161
}
6262

@@ -162,54 +162,58 @@ func TestBranchGetAllBranchGraph(t *testing.T) {
162162
assert.NoError(t, err)
163163
}
164164

165-
func TestBranchCurrentBranchName(t *testing.T) {
165+
func TestBranchCurrentBranchInfo(t *testing.T) {
166166
type scenario struct {
167167
testName string
168168
runner *oscommands.FakeCmdObjRunner
169-
test func(string, string, error)
169+
test func(BranchInfo, error)
170170
}
171171

172172
scenarios := []scenario{
173173
{
174174
"says we are on the master branch if we are",
175175
oscommands.NewFakeRunner(t).Expect(`git symbolic-ref --short HEAD`, "master", nil),
176-
func(name string, displayname string, err error) {
176+
func(info BranchInfo, err error) {
177177
assert.NoError(t, err)
178-
assert.EqualValues(t, "master", name)
179-
assert.EqualValues(t, "master", displayname)
178+
assert.EqualValues(t, "master", info.RefName)
179+
assert.EqualValues(t, "master", info.DisplayName)
180+
assert.False(t, info.DetachedHead)
180181
},
181182
},
182183
{
183184
"falls back to git `git branch --contains` if symbolic-ref fails",
184185
oscommands.NewFakeRunner(t).
185186
Expect(`git symbolic-ref --short HEAD`, "", errors.New("error")).
186-
Expect(`git branch --contains`, "* master", nil),
187-
func(name string, displayname string, err error) {
187+
Expect(`git branch --contains`, "* (HEAD detached at 8982166a)", nil),
188+
func(info BranchInfo, err error) {
188189
assert.NoError(t, err)
189-
assert.EqualValues(t, "master", name)
190-
assert.EqualValues(t, "master", displayname)
190+
assert.EqualValues(t, "8982166a", info.RefName)
191+
assert.EqualValues(t, "(HEAD detached at 8982166a)", info.DisplayName)
192+
assert.True(t, info.DetachedHead)
191193
},
192194
},
193195
{
194196
"handles a detached head",
195197
oscommands.NewFakeRunner(t).
196198
Expect(`git symbolic-ref --short HEAD`, "", errors.New("error")).
197199
Expect(`git branch --contains`, "* (HEAD detached at 123abcd)", nil),
198-
func(name string, displayname string, err error) {
200+
func(info BranchInfo, err error) {
199201
assert.NoError(t, err)
200-
assert.EqualValues(t, "123abcd", name)
201-
assert.EqualValues(t, "(HEAD detached at 123abcd)", displayname)
202+
assert.EqualValues(t, "123abcd", info.RefName)
203+
assert.EqualValues(t, "(HEAD detached at 123abcd)", info.DisplayName)
204+
assert.True(t, info.DetachedHead)
202205
},
203206
},
204207
{
205208
"bubbles up error if there is one",
206209
oscommands.NewFakeRunner(t).
207210
Expect(`git symbolic-ref --short HEAD`, "", errors.New("error")).
208211
Expect(`git branch --contains`, "", errors.New("error")),
209-
func(name string, displayname string, err error) {
212+
func(info BranchInfo, err error) {
210213
assert.Error(t, err)
211-
assert.EqualValues(t, "", name)
212-
assert.EqualValues(t, "", displayname)
214+
assert.EqualValues(t, "", info.RefName)
215+
assert.EqualValues(t, "", info.DisplayName)
216+
assert.False(t, info.DetachedHead)
213217
},
214218
},
215219
}
@@ -218,7 +222,7 @@ func TestBranchCurrentBranchName(t *testing.T) {
218222
s := s
219223
t.Run(s.testName, func(t *testing.T) {
220224
instance := buildBranchCommands(commonDeps{runner: s.runner})
221-
s.test(instance.CurrentBranchName())
225+
s.test(instance.CurrentBranchInfo())
222226
s.runner.CheckForMissingCalls()
223227
})
224228
}

pkg/commands/git_commands/commit_loader.go

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ type CommitLoader struct {
2929
*common.Common
3030
cmd oscommands.ICmdObjBuilder
3131

32-
getCurrentBranchName func() (string, string, error)
32+
getCurrentBranchInfo func() (BranchInfo, error)
3333
getRebaseMode func() (enums.RebaseMode, error)
3434
readFile func(filename string) ([]byte, error)
3535
walkFiles func(root string, fn filepath.WalkFunc) error
@@ -41,13 +41,13 @@ func NewCommitLoader(
4141
cmn *common.Common,
4242
cmd oscommands.ICmdObjBuilder,
4343
dotGitDir string,
44-
getCurrentBranchName func() (string, string, error),
44+
getCurrentBranchInfo func() (BranchInfo, error),
4545
getRebaseMode func() (enums.RebaseMode, error),
4646
) *CommitLoader {
4747
return &CommitLoader{
4848
Common: cmn,
4949
cmd: cmd,
50-
getCurrentBranchName: getCurrentBranchName,
50+
getCurrentBranchInfo: getCurrentBranchInfo,
5151
getRebaseMode: getRebaseMode,
5252
readFile: os.ReadFile,
5353
walkFiles: filepath.Walk,
@@ -371,13 +371,13 @@ func (self *CommitLoader) setCommitMergedStatuses(refName string, commits []*mod
371371
}
372372

373373
func (self *CommitLoader) getMergeBase(refName string) (string, error) {
374-
currentBranch, _, err := self.getCurrentBranchName()
374+
info, err := self.getCurrentBranchInfo()
375375
if err != nil {
376376
return "", err
377377
}
378378

379379
baseBranch := "master"
380-
if strings.HasPrefix(currentBranch, "feature/") {
380+
if strings.HasPrefix(info.RefName, "feature/") {
381381
baseBranch = "develop"
382382
}
383383

pkg/commands/git_commands/commit_loader_test.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -182,8 +182,8 @@ func TestGetCommits(t *testing.T) {
182182
builder := &CommitLoader{
183183
Common: utils.NewDummyCommon(),
184184
cmd: oscommands.NewDummyCmdObjBuilder(scenario.runner),
185-
getCurrentBranchName: func() (string, string, error) {
186-
return scenario.currentBranchName, scenario.currentBranchName, nil
185+
getCurrentBranchInfo: func() (BranchInfo, error) {
186+
return BranchInfo{RefName: scenario.currentBranchName, DisplayName: scenario.currentBranchName, DetachedHead: false}, nil
187187
},
188188
getRebaseMode: func() (enums.RebaseMode, error) { return scenario.rebaseMode, nil },
189189
dotGitDir: ".git",

pkg/commands/models/branch.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ type Branch struct {
1111
Pullables string
1212
UpstreamGone bool
1313
Head bool
14+
DetachedHead bool
1415
// if we have a named remote locally this will be the name of that remote e.g.
1516
// 'origin' or 'tiwood'. If we don't have the remote locally it'll look like
1617
// 'git@github.com:tiwood/lazygit.git'
@@ -19,6 +20,9 @@ type Branch struct {
1920
}
2021

2122
func (b *Branch) FullRefName() string {
23+
if b.DetachedHead {
24+
return b.Name
25+
}
2226
return "refs/heads/" + b.Name
2327
}
2428

pkg/gui/controllers/branches_controller.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -255,7 +255,7 @@ func (self *BranchesController) createNewBranchWithName(newBranchName string) er
255255
return nil
256256
}
257257

258-
if err := self.git.Branch.New(newBranchName, branch.Name); err != nil {
258+
if err := self.git.Branch.New(newBranchName, branch.FullRefName()); err != nil {
259259
return self.c.Error(err)
260260
}
261261

@@ -411,7 +411,7 @@ func (self *BranchesController) rename(branch *models.Branch) error {
411411
}
412412

413413
func (self *BranchesController) newBranch(selectedBranch *models.Branch) error {
414-
return self.helpers.Refs.NewBranch(selectedBranch.RefName(), selectedBranch.RefName(), "")
414+
return self.helpers.Refs.NewBranch(selectedBranch.FullRefName(), selectedBranch.RefName(), "")
415415
}
416416

417417
func (self *BranchesController) createPullRequestMenu(selectedBranch *models.Branch, checkedOutBranch *models.Branch) error {

pkg/gui/presentation/icons/git_icons.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ var remoteIcons = []remoteIcon{
2929
}
3030

3131
func IconForBranch(branch *models.Branch) string {
32-
if branch.DisplayName != "" {
32+
if branch.DetachedHead {
3333
return DETACHED_HEAD_ICON
3434
}
3535
return BRANCH_ICON
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
package branch
2+
3+
import (
4+
"github.com/jesseduffield/lazygit/pkg/config"
5+
. "github.com/jesseduffield/lazygit/pkg/integration/components"
6+
)
7+
8+
var CheckoutByName = NewIntegrationTest(NewIntegrationTestArgs{
9+
Description: "Try to checkout branch by name. Verify that it also works on the branch with the special name @.",
10+
ExtraCmdArgs: "",
11+
Skip: false,
12+
SetupConfig: func(config *config.AppConfig) {},
13+
SetupRepo: func(shell *Shell) {
14+
shell.
15+
CreateNCommits(3).
16+
NewBranch("@").
17+
Checkout("master").
18+
EmptyCommit("blah")
19+
},
20+
Run: func(shell *Shell, input *Input, assert *Assert, keys config.KeybindingConfig) {
21+
input.SwitchToBranchesWindow()
22+
assert.CurrentViewName("localBranches")
23+
24+
assert.MatchSelectedLine(Contains("master"))
25+
input.NextItem()
26+
assert.MatchSelectedLine(Contains("@"))
27+
input.PressKeys(keys.Branches.CheckoutBranchByName)
28+
assert.InPrompt()
29+
assert.MatchCurrentViewTitle(Equals("Branch name:"))
30+
input.Type("new-branch")
31+
input.Confirm()
32+
assert.InAlert()
33+
assert.MatchCurrentViewContent(Equals("Branch not found. Create a new branch named new-branch?"))
34+
input.Confirm()
35+
36+
assert.CurrentViewName("localBranches")
37+
assert.MatchSelectedLine(Contains("new-branch"))
38+
},
39+
})

0 commit comments

Comments
 (0)