@@ -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.
113115func 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.
0 commit comments