@@ -9,11 +9,12 @@ use super::{utils::bytes2string, RepoPath};
99use crate :: {
1010 error:: { Error , Result } ,
1111 sync:: {
12- remotes:: get_default_remote_for_push_in_repo,
12+ gix_repo , remotes:: get_default_remote_for_push_in_repo,
1313 repository:: repo, utils:: get_head_repo, CommitId ,
1414 } ,
1515} ;
1616use git2:: { Branch , BranchType , Repository } ;
17+ use gix:: remote:: Direction ;
1718use scopetime:: scope_time;
1819use std:: collections:: HashSet ;
1920
@@ -128,76 +129,121 @@ pub fn get_branches_info(
128129) -> Result < Vec < BranchInfo > > {
129130 scope_time ! ( "get_branches_info" ) ;
130131
131- let repo = repo ( repo_path) ?;
132+ let gix_repo = gix_repo ( repo_path) ?;
133+ let platform = gix_repo. references ( ) ?;
134+ let head_name = gix_repo. head_name ( ) . ok ( ) . flatten ( ) ;
135+
136+ let mut branches_for_display: Vec < _ > = if local {
137+ platform
138+ . local_branches ( ) ?
139+ . flatten ( )
140+ . filter_map ( |mut branch| {
141+ let branch_name = branch. name ( ) ;
142+ let name = branch_name. shorten ( ) . to_string ( ) ;
143+ let reference = branch_name. to_owned ( ) . to_string ( ) ;
144+
145+ // There is no `gitoxide` equivalent of [`Branch::is_head`][branch-is-head]. This will
146+ // also not change in the future as it would require reading and resolving the `HEAD`
147+ // reference each time it is called. It is more efficient for us to read the `HEAD`
148+ // reference once and then do the individual comparisons ourselves.
149+ //
150+ // This implementation is based on [`git_branch_is_head`][libgit2-git-branch-is-head].
151+ //
152+ // [branch-is-head]: https://docs.rs/git2/latest/git2/struct.Branch.html#method.is_head
153+ // [libgit2-git-branch-is-head]: https://github.com/libgit2/libgit2/blob/58d9363f02f1fa39e46d49b604f27008e75b72f2/src/libgit2/branch.c#L773-L800
154+ let is_head =
155+ head_name. as_ref ( ) . is_some_and ( |head_name| {
156+ head_name. as_ref ( ) == branch_name
157+ } ) ;
158+
159+ let top_commit = branch. peel_to_commit ( ) . ok ( ) ?;
160+ let upstream = branch. remote_tracking_ref_name (
161+ // Using `Fetch` replicates the behaviour of `Branch::upstream` as much as possible.
162+ //
163+ // See https://docs.rs/git2/latest/git2/struct.Branch.html#method.upstream
164+ Direction :: Fetch ,
165+ ) ;
166+
167+ let upstream_branch = match upstream {
168+ Some ( Ok ( reference) ) => Some ( UpstreamBranch {
169+ reference : reference. into_owned ( ) . to_string ( ) ,
170+ } ) ,
171+ _ => None ,
172+ } ;
173+
174+ let remote = branch
175+ . remote_name (
176+ // Using `Fetch` replicates the behaviour of `Repository::branch_upstream_remote` as much as possible.
177+ //
178+ // See https://docs.rs/git2/latest/git2/struct.Repository.html#method.branch_upstream_remote
179+ Direction :: Fetch ,
180+ )
181+ . map ( |name| name. as_bstr ( ) . to_string ( ) ) ;
182+
183+ let details = BranchDetails :: Local ( LocalBranch {
184+ is_head,
185+ has_upstream : upstream_branch. is_some ( ) ,
186+ upstream : upstream_branch,
187+ remote,
188+ } ) ;
132189
133- let ( filter, remotes_with_tracking) = if local {
134- ( BranchType :: Local , HashSet :: default ( ) )
190+ Some ( BranchInfo {
191+ name,
192+ reference,
193+ top_commit_message : top_commit
194+ . message ( )
195+ . ok ( ) ?
196+ . title
197+ . to_string ( ) ,
198+ top_commit : top_commit. into ( ) ,
199+ details,
200+ } )
201+ } )
202+ . collect ( )
135203 } else {
136- let remotes: HashSet < _ > = repo
137- . branches ( Some ( BranchType :: Local ) ) ?
138- . filter_map ( |b| {
139- let branch = b. ok ( ) ?. 0 ;
140- let upstream = branch. upstream ( ) ;
141- upstream
142- . ok ( ) ?
143- . name_bytes ( )
144- . ok ( )
145- . map ( ToOwned :: to_owned)
204+ let remotes_with_tracking: HashSet < _ > = platform
205+ . local_branches ( ) ?
206+ . flatten ( )
207+ . filter_map ( |branch| {
208+ let upstream = branch. remote_tracking_ref_name (
209+ // Using `Fetch` replicates the behaviour of `Branch::upstream` as much as possible.
210+ //
211+ // See https://docs.rs/git2/latest/git2/struct.Branch.html#method.upstream
212+ Direction :: Fetch ,
213+ ) ?;
214+ Some ( upstream. ok ( ) ?. into_owned ( ) )
146215 } )
147216 . collect ( ) ;
148- ( BranchType :: Remote , remotes)
149- } ;
150-
151- let mut branches_for_display: Vec < BranchInfo > = repo
152- . branches ( Some ( filter) ) ?
153- . map ( |b| {
154- let branch = b?. 0 ;
155- let top_commit = branch. get ( ) . peel_to_commit ( ) ?;
156- let reference = bytes2string ( branch. get ( ) . name_bytes ( ) ) ?;
157- let upstream = branch. upstream ( ) ;
158-
159- let remote = repo
160- . branch_upstream_remote ( & reference)
161- . ok ( )
162- . as_ref ( )
163- . and_then ( git2:: Buf :: as_str)
164- . map ( String :: from) ;
165217
166- let name_bytes = branch. name_bytes ( ) ?;
218+ platform
219+ . remote_branches ( ) ?
220+ . flatten ( )
221+ . filter_map ( |mut branch| {
222+ let branch_name = branch. name ( ) ;
223+ let name = branch_name. shorten ( ) . to_string ( ) ;
224+ let reference = branch_name. to_owned ( ) . to_string ( ) ;
167225
168- let upstream_branch =
169- upstream. ok ( ) . and_then ( |upstream| {
170- bytes2string ( upstream. get ( ) . name_bytes ( ) )
171- . ok ( )
172- . map ( |reference| UpstreamBranch { reference } )
226+ let details = BranchDetails :: Remote ( RemoteBranch {
227+ has_tracking : remotes_with_tracking
228+ . contains ( branch_name) ,
173229 } ) ;
174230
175- let details = if local {
176- BranchDetails :: Local ( LocalBranch {
177- is_head : branch. is_head ( ) ,
178- has_upstream : upstream_branch. is_some ( ) ,
179- upstream : upstream_branch,
180- remote,
231+ let top_commit = branch. peel_to_commit ( ) . ok ( ) ?;
232+
233+ Some ( BranchInfo {
234+ name,
235+ reference,
236+ top_commit_message : top_commit
237+ . message ( )
238+ . ok ( ) ?
239+ . title
240+ . to_string ( ) ,
241+ top_commit : top_commit. into ( ) ,
242+ details,
181243 } )
182- } else {
183- BranchDetails :: Remote ( RemoteBranch {
184- has_tracking : remotes_with_tracking
185- . contains ( name_bytes) ,
186- } )
187- } ;
188-
189- Ok ( BranchInfo {
190- name : bytes2string ( name_bytes) ?,
191- reference,
192- top_commit_message : bytes2string (
193- top_commit. summary_bytes ( ) . unwrap_or_default ( ) ,
194- ) ?,
195- top_commit : top_commit. id ( ) . into ( ) ,
196- details,
197244 } )
198- } )
199- . filter_map ( Result :: ok)
200- . collect ( ) ;
245+ . collect ( )
246+ } ;
201247
202248 branches_for_display. sort_by ( |a, b| a. name . cmp ( & b. name ) ) ;
203249
0 commit comments