@@ -95,34 +95,94 @@ type getAvailableBranches func() (map[string][]string, error)
9595
9696func listBranches (gitCmd git.Git ) getAvailableBranches {
9797 return func () (map [string ][]string , error ) {
98- if err := runner .Run (gitCmd .Fetch (jobsFlag )); err != nil {
98+ remoteList , err := runner .RunForOutput (gitCmd .RemoteList ())
99+ if err != nil {
99100 return nil , err
100101 }
101- out , err := runner .RunForOutput (gitCmd .Branch ("-r" ))
102+
103+ remoteBranches , err := runner .RunForOutput (gitCmd .RemoteBranches ())
102104 if err != nil {
103105 return nil , err
104106 }
105107
106- return parseListBranchesOutput (out ), nil
108+ return parseListBranchesOutput (remoteList , remoteBranches ), nil
107109 }
108110}
109111
110- func parseListBranchesOutput (output string ) map [string ][]string {
111- lines := strings .Split (output , "\n " )
112- branchesByRemote := map [string ][]string {}
113- for _ , line := range lines {
114- line = strings .Trim (line , " " )
115- split := strings .Split (line , "/" )
116-
117- remote := split [0 ]
118- branch := ""
119- if len (split ) > 1 {
120- branch = strings .Join (split [1 :], "/" )
121- branches := branchesByRemote [remote ]
122- branches = append (branches , branch )
123- branchesByRemote [remote ] = branches
112+ func parseListBranchesOutput (remotes , branches string ) map [string ][]string {
113+ parsedRemotes := extractRemotes (remotes )
114+ branchesByRemote := extractBranches (parsedRemotes , branches )
115+
116+ return branchesByRemote
117+ }
118+
119+ // extractRemotes parses the output of `git remote -v` and returns a map of remote URLs to their names
120+ // Example of what such an output looks like:
121+ //
122+ // origin git_url1 (fetch)
123+ // origin git_url1 (push)
124+ // upstream git_url2 (fetch)
125+ // upstream git_url2 (push)
126+ //
127+ // (Name of the remote, \t, URL, space, (fetch|push), \n)
128+ func extractRemotes (remotes string ) map [string ]string {
129+ parsedRemotes := make (map [string ]string )
130+
131+ for _ , line := range strings .Split (remotes , "\n " ) {
132+ line = strings .TrimSpace (line )
133+ if line == "" {
134+ continue
135+ }
136+ split := strings .Split (line , "\t " )
137+ if len (split ) < 2 {
138+ continue
124139 }
140+
141+ name := split [0 ]
142+
143+ split = strings .Split (split [1 ], " " )
144+ url := split [0 ]
145+
146+ parsedRemotes [url ] = name
125147 }
148+
149+ return parsedRemotes
150+ }
151+
152+ // extractBranches parses the output of `git ls-remote -b` and returns a map of remote names to their branches
153+ // Example of what such an output looks like:
154+ //
155+ // From git_url
156+ // a50bdc5182e3e7c292f7c8c881a1a0d9476c8eda refs/heads/A
157+ // 25c9d97e5c9e7f4c9ad25597f8e1265af07869d7 refs/heads/B
158+ // 5b3dfe10c52cf5c762740ea5cfa619d82d70e205 refs/heads/C
159+ // f6cdf8f2132f22516bb4d71e326e66a45850eb04 refs/heads/D
160+ // b30b826ac9594330d77554f30103492409035aee refs/heads/master
161+ //
162+ // (From, space, URL, \n)
163+ // (SHA, \t, refs/heads/, branch name, \n)
164+ func extractBranches (remotes map [string ]string , branches string ) map [string ][]string {
165+ branchesByRemote := make (map [string ][]string )
166+ currentRemote := ""
167+ for _ , line := range strings .Split (branches , "\n " ) {
168+ if strings .HasPrefix (line , "From " ) {
169+ repoURL := strings .TrimPrefix (line , "From " )
170+ currentRemote = remotes [repoURL ]
171+
172+ continue
173+ }
174+
175+ split := strings .Split (line , "\t " )
176+ if len (split ) < 2 || currentRemote == "" {
177+ continue
178+ }
179+
180+ branch := strings .TrimPrefix (split [1 ], refsHeadsPrefix )
181+ branches := branchesByRemote [currentRemote ]
182+ branches = append (branches , branch )
183+ branchesByRemote [currentRemote ] = branches
184+ }
185+
126186 return branchesByRemote
127187}
128188
0 commit comments