@@ -509,180 +509,310 @@ fn assign_sources_targets(
509509 }
510510}
511511
512- /// Extracts (real or derived from merge summary) and assigns basic properties.
513- fn extract_branches (
512+
513+ /// Extracts and processes actual Git branches (local and remote) from the repository.
514+ ///
515+ /// This function iterates through the branches found in the Git repository,
516+ /// filters them based on the `include_remote` setting, and constructs `BranchInfo`
517+ /// objects for each valid branch. It assigns properties like name, type (local/remote),
518+ /// visual order, and colors based on the provided settings.
519+ ///
520+ /// Arguments:
521+ /// - `repository`: A reference to the Git `Repository` object.
522+ /// - `indices`: A HashMap mapping commit OIDs to their corresponding indices in the `commits` list.
523+ /// - `settings`: A reference to the application `Settings` containing branch configuration.
524+ /// - `counter`: A mutable reference to a counter, incremented for each processed branch to aid in color assignment.
525+ ///
526+ /// Returns:
527+ /// A `Result` containing a `Vec<BranchInfo>` on success, or a `String` error message on failure.
528+ fn extract_actual_branches (
514529 repository : & Repository ,
515- commits : & [ CommitInfo ] ,
516530 indices : & HashMap < Oid , usize > ,
517531 settings : & Settings ,
532+ counter : & mut usize ,
518533) -> Result < Vec < BranchInfo > , String > {
534+ // Determine if remote branches should be included based on settings.
519535 let filter = if settings. include_remote {
520536 None
521537 } else {
522538 Some ( BranchType :: Local )
523539 } ;
540+
541+ // Retrieve branches from the repository, handling potential errors.
524542 let actual_branches = repository
525543 . branches ( filter)
526544 . map_err ( |err| err. message ( ) . to_string ( ) ) ?
527545 . collect :: < Result < Vec < _ > , Error > > ( )
528546 . map_err ( |err| err. message ( ) . to_string ( ) ) ?;
529547
530- let mut counter = 0 ;
531-
532- let mut valid_branches = actual_branches
548+ // Process each actual branch to create `BranchInfo` objects.
549+ let valid_branches = actual_branches
533550 . iter ( )
534551 . filter_map ( |( br, tp) | {
535552 br. get ( ) . name ( ) . and_then ( |n| {
536553 br. get ( ) . target ( ) . map ( |t| {
537- counter += 1 ;
554+ * counter += 1 ; // Increment counter for unique branch identification/coloring.
555+
556+ // Determine the starting index for slicing the branch name string.
538557 let start_index = match tp {
539- BranchType :: Local => 11 ,
540- BranchType :: Remote => 13 ,
558+ BranchType :: Local => 11 , // "refs/heads/"
559+ BranchType :: Remote => 13 , // "refs/remotes/"
541560 } ;
542561 let name = & n[ start_index..] ;
543562 let end_index = indices. get ( & t) . cloned ( ) ;
544563
564+ // Convert branch color to a terminal-compatible format.
545565 let term_color = match to_terminal_color (
546566 & branch_color (
547567 name,
548568 & settings. branches . terminal_colors [ ..] ,
549569 & settings. branches . terminal_colors_unknown ,
550- counter,
570+ * counter,
551571 ) [ ..] ,
552572 ) {
553573 Ok ( col) => col,
554- Err ( err) => return Err ( err) ,
574+ Err ( err) => return Err ( err) , // Propagate color conversion errors.
555575 } ;
556576
577+ // Create and return the BranchInfo object.
557578 Ok ( BranchInfo :: new (
558579 t,
559- None ,
580+ None , // No merge OID for actual branches.
560581 name. to_string ( ) ,
561582 branch_order ( name, & settings. branches . persistence ) as u8 ,
562- & BranchType :: Remote == tp,
563- false ,
564- false ,
583+ & BranchType :: Remote == tp, // Check if it's a remote branch.
584+ false , // Not a derived merge branch.
585+ false , // Not a tag.
565586 BranchVis :: new (
566587 branch_order ( name, & settings. branches . order ) ,
567588 term_color,
568589 branch_color (
569590 name,
570591 & settings. branches . svg_colors ,
571592 & settings. branches . svg_colors_unknown ,
572- counter,
593+ * counter,
573594 ) ,
574595 ) ,
575596 end_index,
576597 ) )
577598 } )
578599 } )
579600 } )
580- . collect :: < Result < Vec < _ > , String > > ( ) ?;
601+ . collect :: < Result < Vec < _ > , String > > ( ) ?; // Collect results, propagating any errors.
602+
603+ Ok ( valid_branches)
604+ }
605+
606+ /// Iterates through commits, identifies merge commits, and derives branch information
607+ /// from their summaries.
608+ ///
609+ /// This function processes each commit in the provided list. If a commit is identified
610+ /// as a merge commit and has a summary, it attempts to parse a branch name from the summary.
611+ /// A `BranchInfo` object is then created for this derived branch, representing the merge
612+ /// point and its properties.
613+ ///
614+ /// Arguments:
615+ /// - `repository`: A reference to the Git `Repository` object.
616+ /// - `commits`: A slice of `CommitInfo` objects, representing the commits to process.
617+ /// - `settings`: A reference to the application `Settings` containing branch and merge pattern configuration.
618+ /// - `counter`: A mutable reference to a counter, incremented for each processed merge branch.
619+ ///
620+ /// Returns:
621+ /// A `Result` containing a `Vec<BranchInfo>` on success, or a `String` error message on failure.
622+ fn extract_merge_branches (
623+ repository : & Repository ,
624+ commits : & [ CommitInfo ] ,
625+ settings : & Settings ,
626+ counter : & mut usize ,
627+ ) -> Result < Vec < BranchInfo > , String > {
628+ let mut merge_branches = Vec :: new ( ) ;
581629
582630 for ( idx, info) in commits. iter ( ) . enumerate ( ) {
583- let commit = repository
584- . find_commit ( info. oid )
585- . map_err ( |err| err. message ( ) . to_string ( ) ) ?;
631+ // Only process if the commit is a merge.
586632 if info. is_merge {
633+ let commit = repository
634+ . find_commit ( info. oid )
635+ . map_err ( |err| err. message ( ) . to_string ( ) ) ?;
636+
637+ // Attempt to get the commit summary.
587638 if let Some ( summary) = commit. summary ( ) {
588- counter += 1 ;
639+ * counter += 1 ; // Increment counter for unique branch identification/coloring.
589640
590641 let parent_oid = commit
591642 . parent_id ( 1 )
592643 . map_err ( |err| err. message ( ) . to_string ( ) ) ?;
593644
645+ // Parse the branch name from the merge summary using configured patterns.
594646 let branch_name = parse_merge_summary ( summary, & settings. merge_patterns )
595647 . unwrap_or_else ( || "unknown" . to_string ( ) ) ;
596648
649+ // Determine persistence and order for the derived branch.
597650 let persistence = branch_order ( & branch_name, & settings. branches . persistence ) as u8 ;
598-
599651 let pos = branch_order ( & branch_name, & settings. branches . order ) ;
600652
653+ // Get terminal and SVG colors for the branch.
601654 let term_col = to_terminal_color (
602655 & branch_color (
603656 & branch_name,
604657 & settings. branches . terminal_colors [ ..] ,
605658 & settings. branches . terminal_colors_unknown ,
606- counter,
659+ * counter,
607660 ) [ ..] ,
608661 ) ?;
609662 let svg_col = branch_color (
610663 & branch_name,
611664 & settings. branches . svg_colors ,
612665 & settings. branches . svg_colors_unknown ,
613- counter,
666+ * counter,
614667 ) ;
615668
669+ // Create and add the BranchInfo for the derived merge branch.
616670 let branch_info = BranchInfo :: new (
617- parent_oid,
618- Some ( info. oid ) ,
671+ parent_oid, // Target is the parent of the merge.
672+ Some ( info. oid ) , // The merge commit itself.
619673 branch_name,
620674 persistence,
621- false ,
622- true ,
623- false ,
675+ false , // Not a remote branch.
676+ true , // This is a derived merge branch.
677+ false , // Not a tag.
624678 BranchVis :: new ( pos, term_col, svg_col) ,
625- Some ( idx + 1 ) ,
679+ Some ( idx + 1 ) , // End index typically points to the commit after the merge.
626680 ) ;
627- valid_branches . push ( branch_info) ;
681+ merge_branches . push ( branch_info) ;
628682 }
629683 }
630684 }
685+ Ok ( merge_branches)
686+ }
631687
632- valid_branches. sort_by_cached_key ( |branch| ( branch. persistence , !branch. is_merged ) ) ;
633-
634- let mut tags = Vec :: new ( ) ;
688+ /// Extracts Git tags and treats them as branches, assigning appropriate properties.
689+ ///
690+ /// This function iterates through all tags in the repository, resolves their target
691+ /// commit OID, and if the target commit is found within the `commits` list,
692+ /// a `BranchInfo` object is created for the tag. Tags are assigned a higher
693+ /// persistence value to ensure they are displayed prominently.
694+ ///
695+ /// Arguments:
696+ /// - `repository`: A reference to the Git `Repository` object.
697+ /// - `indices`: A HashMap mapping commit OIDs to their corresponding indices in the `commits` list.
698+ /// - `settings`: A reference to the application `Settings` containing branch configuration.
699+ /// - `counter`: A mutable reference to a counter, incremented for each processed tag.
700+ ///
701+ /// Returns:
702+ /// A `Result` containing a `Vec<BranchInfo>` on success, or a `String` error message on failure.
703+ fn extract_tags_as_branches (
704+ repository : & Repository ,
705+ indices : & HashMap < Oid , usize > ,
706+ settings : & Settings ,
707+ counter : & mut usize ,
708+ ) -> Result < Vec < BranchInfo > , String > {
709+ let mut tags_info = Vec :: new ( ) ;
710+ let mut tags_raw = Vec :: new ( ) ;
635711
712+ // Iterate over all tags in the repository.
636713 repository
637714 . tag_foreach ( |oid, name| {
638- tags . push ( ( oid, name. to_vec ( ) ) ) ;
639- true
715+ tags_raw . push ( ( oid, name. to_vec ( ) ) ) ;
716+ true // Continue iteration.
640717 } )
641718 . map_err ( |err| err. message ( ) . to_string ( ) ) ?;
642719
643- for ( oid, name) in tags {
644- let name = std:: str:: from_utf8 ( & name[ 5 ..] ) . map_err ( |err| err. to_string ( ) ) ?;
720+ for ( oid, name_bytes) in tags_raw {
721+ // Convert tag name bytes to a UTF-8 string. Tags typically start with "refs/tags/".
722+ let name = std:: str:: from_utf8 ( & name_bytes[ 5 ..] ) . map_err ( |err| err. to_string ( ) ) ?;
645723
724+ // Resolve the target OID of the tag. It could be a tag object or directly a commit.
646725 let target = repository
647726 . find_tag ( oid)
648727 . map ( |tag| tag. target_id ( ) )
649- . or_else ( |_| repository. find_commit ( oid) . map ( |_| oid) ) ;
728+ . or_else ( |_| repository. find_commit ( oid) . map ( |_| oid) ) ; // If not a tag object, try as a direct commit.
650729
651730 if let Ok ( target_oid) = target {
731+ // If the target commit is within our processed commits, create a BranchInfo.
652732 if let Some ( target_index) = indices. get ( & target_oid) {
653- counter += 1 ;
733+ * counter += 1 ; // Increment counter for unique tag identification/coloring.
734+
735+ // Get terminal and SVG colors for the tag.
654736 let term_col = to_terminal_color (
655737 & branch_color (
656738 name,
657739 & settings. branches . terminal_colors [ ..] ,
658740 & settings. branches . terminal_colors_unknown ,
659- counter,
741+ * counter,
660742 ) [ ..] ,
661743 ) ?;
662744 let pos = branch_order ( name, & settings. branches . order ) ;
663745 let svg_col = branch_color (
664746 name,
665747 & settings. branches . svg_colors ,
666748 & settings. branches . svg_colors_unknown ,
667- counter,
749+ * counter,
668750 ) ;
751+
752+ // Create the BranchInfo object for the tag.
669753 let tag_info = BranchInfo :: new (
670754 target_oid,
671- None ,
755+ None , // No merge OID for tags.
672756 name. to_string ( ) ,
673- settings. branches . persistence . len ( ) as u8 + 1 ,
674- false ,
675- false ,
676- true ,
757+ settings. branches . persistence . len ( ) as u8 + 1 , // Tags usually have highest persistence.
758+ false , // Not a remote branch.
759+ false , // Not a derived merge branch.
760+ true , // This is a tag.
677761 BranchVis :: new ( pos, term_col, svg_col) ,
678762 Some ( * target_index) ,
679763 ) ;
680- valid_branches . push ( tag_info) ;
764+ tags_info . push ( tag_info) ;
681765 }
682766 }
683767 }
768+ Ok ( tags_info)
769+ }
684770
685- Ok ( valid_branches)
771+
772+ /// Extracts (real or derived from merge summary) and assigns basic properties to branches and tags.
773+ ///
774+ /// This function orchestrates the extraction of branch information from various sources:
775+ /// 1. Actual Git branches (local and remote).
776+ /// 2. Branches derived from merge commit summaries.
777+ /// 3. Git tags, treated as branches for visualization purposes.
778+ ///
779+ /// It combines the results from these extraction steps, sorts them based on
780+ /// persistence and merge status, and returns a comprehensive list of `BranchInfo` objects.
781+ ///
782+ /// Arguments:
783+ /// - `repository`: A reference to the Git `Repository` object.
784+ /// - `commits`: A slice of `CommitInfo` objects, representing all relevant commits.
785+ /// - `indices`: A HashMap mapping commit OIDs to their corresponding indices in the `commits` list.
786+ /// - `settings`: A reference to the application `Settings` containing all necessary configuration.
787+ ///
788+ /// Returns:
789+ /// A `Result` containing a `Vec<BranchInfo>` on success, or a `String` error message on failure.
790+ fn extract_branches (
791+ repository : & Repository ,
792+ commits : & [ CommitInfo ] ,
793+ indices : & HashMap < Oid , usize > ,
794+ settings : & Settings ,
795+ ) -> Result < Vec < BranchInfo > , String > {
796+ let mut counter = 0 ; // Counter for unique branch/tag identification, especially for coloring.
797+ let mut all_branches: Vec < BranchInfo > = Vec :: new ( ) ;
798+
799+ // 1. Extract actual local and remote branches.
800+ let actual_branches = extract_actual_branches ( repository, indices, settings, & mut counter) ?;
801+ all_branches. extend ( actual_branches) ;
802+
803+ // 2. Extract branches derived from merge commit summaries.
804+ let merge_branches = extract_merge_branches ( repository, commits, settings, & mut counter) ?;
805+ all_branches. extend ( merge_branches) ;
806+
807+ // 3. Extract tags and treat them as branches for visualization.
808+ let tags_as_branches = extract_tags_as_branches ( repository, indices, settings, & mut counter) ?;
809+ all_branches. extend ( tags_as_branches) ;
810+
811+ // Sort all collected branches and tags.
812+ // Sorting criteria: first by persistence, then by whether they are merged (unmerged first).
813+ all_branches. sort_by_cached_key ( |branch| ( branch. persistence , !branch. is_merged ) ) ;
814+
815+ Ok ( all_branches)
686816}
687817
688818/// Traces back branches by following 1st commit parent,
0 commit comments