@@ -2,9 +2,7 @@ use crate::VirtualBranchesExt;
22use anyhow:: { Context , Result } ;
33use bstr:: { BStr , BString , ByteSlice } ;
44use core:: fmt;
5- use gitbutler_branch:: {
6- Branch as GitButlerBranch , BranchId , BranchIdentity , ReferenceExtGix , Target ,
7- } ;
5+ use gitbutler_branch:: { Branch as GitButlerBranch , BranchId , ReferenceExtGix , Target } ;
86use gitbutler_command_context:: CommandContext ;
97use gitbutler_reference:: normalize_branch_name;
108use gix:: prelude:: ObjectIdExt ;
@@ -35,12 +33,9 @@ pub fn list_branches(
3533 for reference in platform. all ( ) ?. filter_map ( Result :: ok) {
3634 // Loosely match on branch names
3735 if let Some ( branch_names) = & filter_branch_names {
38- let has_matching_name = branch_names. iter ( ) . any ( |branch_name| {
39- reference
40- . name ( )
41- . as_bstr ( )
42- . ends_with_str ( branch_name. as_bstr ( ) )
43- } ) ;
36+ let has_matching_name = branch_names
37+ . iter ( )
38+ . any ( |branch_name| reference. name ( ) . as_bstr ( ) . ends_with_str ( & branch_name. 0 ) ) ;
4439
4540 if !has_matching_name {
4641 continue ;
@@ -194,13 +189,9 @@ fn branch_group_to_branch(
194189 } ;
195190
196191 if virtual_branch. is_none ( )
197- && local_branches. iter ( ) . any ( |b| {
198- b. name ( )
199- . identity ( remotes)
200- . as_deref ( )
201- . ok ( )
202- . map_or ( false , |identity| identity == target. branch . branch ( ) )
203- } )
192+ && local_branches
193+ . iter ( )
194+ . any ( |b| b. name ( ) . given_name ( remotes) . as_deref ( ) . ok ( ) == Some ( target. branch . branch ( ) ) )
204195 {
205196 return Ok ( None ) ;
206197 }
@@ -212,10 +203,11 @@ fn branch_group_to_branch(
212203 in_workspace : branch. in_workspace ,
213204 } ) ;
214205
215- let mut remotes: Vec < gix:: remote:: Name < ' static > > = Vec :: new ( ) ;
206+ // TODO(ST): keep the type alive, don't reduce to BString
207+ let mut remotes: Vec < BString > = Vec :: new ( ) ;
216208 for branch in remote_branches. iter ( ) {
217209 if let Some ( remote_name) = branch. remote_name ( gix:: remote:: Direction :: Fetch ) {
218- remotes. push ( remote_name. to_owned ( ) ) ;
210+ remotes. push ( remote_name. as_bstr ( ) . into ( ) ) ;
219211 }
220212 }
221213
@@ -301,7 +293,7 @@ impl GroupBranch<'_> {
301293 fn identity ( & self , remotes : & BTreeSet < Cow < ' _ , BStr > > ) -> Option < BranchIdentity > {
302294 match self {
303295 GroupBranch :: Local ( branch) | GroupBranch :: Remote ( branch) => {
304- branch. name ( ) . identity ( remotes) . ok ( )
296+ branch. name ( ) . given_name ( remotes) . ok ( )
305297 }
306298 // The identity of a Virtual branch is derived from the source refname, upstream or the branch given name, in that order
307299 GroupBranch :: Virtual ( branch) => {
@@ -310,24 +302,25 @@ impl GroupBranch<'_> {
310302 let rich_name = branch. name . clone ( ) ;
311303 let rich_name = normalize_branch_name ( & rich_name) . ok ( ) ?;
312304 let identity = name_from_source. unwrap_or ( name_from_upstream. unwrap_or ( & rich_name) ) ;
313- Some ( identity. into ( ) )
305+ Some ( identity. to_string ( ) )
314306 }
315307 }
316- . map ( BranchIdentity :: from )
308+ . map ( BranchIdentity )
317309 }
318310}
319311
320312/// Determines if a branch should be listed in the UI.
321313/// This excludes the target branch as well as gitbutler specific branches.
322314fn should_list_git_branch ( identity : & BranchIdentity ) -> bool {
323315 // Exclude gitbutler technical branches (not useful for the user)
324- const TECHNICAL_IDENTITIES : & [ & [ u8 ] ] = & [
325- b"gitbutler/integration" ,
326- b"gitbutler/target" ,
327- b"gitbutler/oplog" ,
328- b"HEAD" ,
329- ] ;
330- !TECHNICAL_IDENTITIES . contains ( & identity. as_bytes ( ) )
316+ let is_technical = [
317+ "gitbutler/integration" ,
318+ "gitbutler/target" ,
319+ "gitbutler/oplog" ,
320+ "HEAD" ,
321+ ]
322+ . contains ( & & * identity. 0 ) ;
323+ !is_technical
331324}
332325
333326/// A filter that can be applied to the branch listing
@@ -354,8 +347,8 @@ pub struct BranchListing {
354347 pub name : BranchIdentity ,
355348 /// This is a list of remotes that this branch can be found on (e.g. `origin`, `upstream` etc.),
356349 /// by collecting remotes from all local branches with the same identity that have a tracking setup.
357- #[ serde( serialize_with = "gitbutler_serde::as_string_lossy_vec_remote_name " ) ]
358- pub remotes : Vec < gix :: remote :: Name < ' static > > ,
350+ #[ serde( serialize_with = "gitbutler_serde::serde::as_string_lossy_vec " ) ]
351+ pub remotes : Vec < BString > ,
359352 /// The branch may or may not have a virtual branch associated with it.
360353 pub virtual_branch : Option < VirtualBranchReference > ,
361354 /// Timestamp in milliseconds since the branch was last updated.
@@ -377,25 +370,52 @@ pub struct BranchListing {
377370/// Represents a "commit author" or "signature", based on the data from the git history
378371#[ derive( Debug , Clone , Serialize , PartialEq , Eq , Hash ) ]
379372pub struct Author {
373+ // TODO(ST): use `BString` here to not degenerate information
380374 /// The name of the author as configured in the git config
381- pub name : Option < BString > ,
375+ pub name : Option < String > ,
382376 /// The email of the author as configured in the git config
383- pub email : Option < BString > ,
377+ pub email : Option < String > ,
378+ }
379+
380+ /// The identity of a branch as to allow to group similar branches together.
381+ ///
382+ /// * For *local* branches, it is what's left without the standard prefix, like `refs/heads`, e.g. `main`
383+ /// for `refs/heads/main` or `feat/one` for `refs/heads/feat/one`.
384+ /// * For *remote* branches, it is what's without the prefix and remote name, like `main` for `refs/remotes/origin/main`.
385+ /// or `feat/one` for `refs/remotes/my/special/remote/feat/one`.
386+ /// * For virtual branches, it's either the above if there is a `source_refname` or an `upstream`, or it's the normalized
387+ /// name of the virtual branch.
388+ #[ derive( Debug , Clone , Serialize , PartialEq , Eq , Hash , Ord , PartialOrd ) ]
389+ pub struct BranchIdentity ( String ) ;
390+
391+ /// Facilitate obtaining this type from the UI - otherwise it would be better not to have it as it should be
392+ /// a particular thing, not any string.
393+ impl From < String > for BranchIdentity {
394+ fn from ( value : String ) -> Self {
395+ BranchIdentity ( value)
396+ }
397+ }
398+
399+ /// Also not for testing.
400+ impl From < & str > for BranchIdentity {
401+ fn from ( value : & str ) -> Self {
402+ BranchIdentity ( value. into ( ) )
403+ }
384404}
385405
386406impl From < git2:: Signature < ' _ > > for Author {
387407 fn from ( value : git2:: Signature ) -> Self {
388- let name = value. name ( ) . map ( str:: to_string) . map ( Into :: into ) ;
389- let email = value. email ( ) . map ( str:: to_string) . map ( Into :: into ) ;
408+ let name = value. name ( ) . map ( str:: to_string) ;
409+ let email = value. email ( ) . map ( str:: to_string) ;
390410 Author { name, email }
391411 }
392412}
393413
394414impl From < gix:: actor:: SignatureRef < ' _ > > for Author {
395415 fn from ( value : gix:: actor:: SignatureRef < ' _ > ) -> Self {
396416 Author {
397- name : Some ( value. name . to_owned ( ) ) ,
398- email : Some ( value. email . to_owned ( ) ) ,
417+ name : Some ( value. name . to_string ( ) ) ,
418+ email : Some ( value. email . to_string ( ) ) ,
399419 }
400420 }
401421}
@@ -416,13 +436,9 @@ pub struct VirtualBranchReference {
416436/// a list of enriched branch data in the form of `BranchData`.
417437pub fn get_branch_listing_details (
418438 ctx : & CommandContext ,
419- branch_names : impl IntoIterator < Item = impl TryInto < BranchIdentity > > ,
439+ branch_names : impl IntoIterator < Item = impl Into < BranchIdentity > > ,
420440) -> Result < Vec < BranchListingDetails > > {
421- let branch_names: Vec < _ > = branch_names
422- . into_iter ( )
423- . map ( TryInto :: try_into)
424- . filter_map ( Result :: ok)
425- . collect ( ) ;
441+ let branch_names: Vec < _ > = branch_names. into_iter ( ) . map ( Into :: into) . collect ( ) ;
426442 let repo = ctx. repository ( ) ;
427443 let branches = list_branches ( ctx, None , Some ( branch_names. clone ( ) ) ) ?;
428444 let default_target = ctx
@@ -520,10 +536,10 @@ pub struct BranchEntry {
520536 /// The name of the branch (e.g. `main`, `feature/branch`)
521537 pub name : String ,
522538 /// The head commit of the branch
523- #[ serde( with = "gitbutler_serde::oid" ) ]
539+ #[ serde( with = "gitbutler_serde::serde:: oid" ) ]
524540 head : git2:: Oid ,
525541 /// The commit base of the branch
526- #[ serde( with = "gitbutler_serde::oid" ) ]
542+ #[ serde( with = "gitbutler_serde::serde:: oid" ) ]
527543 base : git2:: Oid ,
528544 /// The list of commits associated with the branch
529545 pub commits : Vec < CommitEntry > ,
@@ -552,17 +568,18 @@ pub struct RemoteBranchEntry {
552568#[ serde( rename_all = "camelCase" ) ]
553569pub struct CommitEntry {
554570 /// The commit sha that it can be referenced by
555- #[ serde( with = "gitbutler_serde::oid" ) ]
571+ #[ serde( with = "gitbutler_serde::serde:: oid" ) ]
556572 pub id : git2:: Oid ,
557573 /// If the commit is referencing a specific change, this is its change id
558574 pub change_id : Option < String > ,
559575 /// The commit message
576+ #[ serde( serialize_with = "gitbutler_serde::serde::as_string_lossy" ) ]
560577 pub description : BString ,
561578 /// The timestamp of the commit in milliseconds
562579 pub created_at : u128 ,
563580 /// The author of the commit
564581 pub authors : Vec < Author > ,
565582 /// The parent commits of the commit
566- #[ serde( with = "gitbutler_serde::oid_vec" ) ]
583+ #[ serde( with = "gitbutler_serde::serde:: oid_vec" ) ]
567584 pub parent_ids : Vec < git2:: Oid > ,
568585}
0 commit comments