Skip to content

Commit f4c1675

Browse files
committed
fix: the branch list now show remote branches correctly
1 parent 3a9928d commit f4c1675

2 files changed

Lines changed: 45 additions & 19 deletions

File tree

internal/git/git.go

Lines changed: 35 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -107,20 +107,31 @@ type Branch struct {
107107
IsRemote bool
108108
Remote string // e.g. "origin" if remote
109109
RemoteRef string // e.g. "origin/feature"
110+
// For local branches, the upstream they're tracking (e.g. "origin/master"). Empty if none.
111+
Upstream string
110112
}
111113

112114
// ListBranchesDetailed returns local and remote branches, with local first.
113115
func ListBranchesDetailed() ([]Branch, error) {
114-
var branches []Branch
115-
// Local branches, sorted by most recent committer date
116-
outLocal, err := runGit("for-each-ref", "--sort=-committerdate", "--format=%(refname:short)", "refs/heads")
116+
// Collect locals and remotes separately
117+
var locals []Branch
118+
var remotes []Branch
119+
120+
// Local branches, sorted by most recent committer date, include upstream tracking info
121+
// Format: "<name>\t<upstream>" where upstream is short (like origin/master) or empty
122+
outLocal, err := runGit("for-each-ref", "--sort=-committerdate", "--format=%(refname:short)\t%(upstream:short)", "refs/heads")
117123
if err == nil {
118124
for _, l := range strings.Split(strings.TrimSpace(outLocal), "\n") {
119-
l = strings.TrimSpace(l)
120-
if l == "" {
125+
if strings.TrimSpace(l) == "" {
121126
continue
122127
}
123-
branches = append(branches, Branch{Name: l})
128+
name := l
129+
upstream := ""
130+
if tab := strings.Index(l, "\t"); tab != -1 {
131+
name = strings.TrimSpace(l[:tab])
132+
upstream = strings.TrimSpace(l[tab+1:])
133+
}
134+
locals = append(locals, Branch{Name: name, Upstream: upstream})
124135
}
125136
}
126137
// Remote branches (skip HEAD pointers like origin/HEAD), sorted by most recent committer date
@@ -141,10 +152,26 @@ func ListBranchesDetailed() ([]Branch, error) {
141152
}
142153
remote := parts[0]
143154
name := parts[1]
144-
branches = append(branches, Branch{Name: name, IsRemote: true, Remote: remote, RemoteRef: l})
155+
remotes = append(remotes, Branch{Name: name, IsRemote: true, Remote: remote, RemoteRef: l})
145156
}
146157
}
147-
return branches, nil
158+
159+
// Deduplicate: prefer locals; include at most one remote per short name when no local exists
160+
final := make([]Branch, 0, len(locals)+len(remotes))
161+
seen := make(map[string]struct{})
162+
for _, b := range locals {
163+
final = append(final, b)
164+
seen[b.Name] = struct{}{}
165+
}
166+
for _, b := range remotes {
167+
if _, ok := seen[b.Name]; ok {
168+
continue // local exists; skip remote duplicate
169+
}
170+
// Ensure only one remote per short name
171+
seen[b.Name] = struct{}{}
172+
final = append(final, b)
173+
}
174+
return final, nil
148175
}
149176

150177
// CreateWorktreeFromRef creates a new branch from a given ref and adds a worktree.

internal/tui/model.go

Lines changed: 10 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -288,23 +288,22 @@ func (m model) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
288288
return m, m.branches.NewStatusMessage(fmt.Sprintf("Error: %v", msg.err))
289289
}
290290
items := make([]list.Item, 0, len(msg.branches)+1)
291-
labelType := func(s string) string { return lipgloss.NewStyle().Foreground(theme.Peach).Render(s) }
292-
labelRemote := func(s string) string { return lipgloss.NewStyle().Foreground(theme.Sky).Render(s) }
291+
labelTrack := func(s string) string { return lipgloss.NewStyle().Foreground(theme.Blue).Render(s) }
292+
labelMuted := func(s string) string { return lipgloss.NewStyle().Foreground(theme.Surface1).Render(s) }
293293
value := func(s string) string { return s }
294294
// Prepend synthetic option to create a new branch
295295
items = append(items, item{title: "[+] Create new branch", desc: "Type a new branch name", isAdd: true})
296296
for _, b := range msg.branches {
297-
// Title: branch name; Desc: labeled status
298-
var segs []string
299-
if b.IsRemote {
300-
segs = append(segs, labelType("Type:")+" "+value("remote"))
301-
if b.Remote != "" {
302-
segs = append(segs, labelRemote("Remote:")+" "+value(b.Remote))
297+
// Title: branch name; Desc: show tracking info for locals; gray 'no remote' if none
298+
desc := ""
299+
if !b.IsRemote {
300+
up := strings.TrimSpace(b.Upstream)
301+
if up == "" {
302+
desc = labelMuted("No remote")
303+
} else {
304+
desc = labelTrack("Tracking:") + " " + value(up)
303305
}
304-
} else {
305-
segs = append(segs, labelType("Type:")+" "+value("local"))
306306
}
307-
desc := strings.Join(segs, " ")
308307
items = append(items, item{title: b.Name, desc: desc, br: b})
309308
}
310309
m.branches.SetItems(items)

0 commit comments

Comments
 (0)