@@ -509,180 +509,308 @@ fn assign_sources_targets(
509509 }
510510}
511511
512- /// Extracts (real or derived from merge summary) and assigns basic properties.
513- fn extract_branches (
512+ /// Extracts and processes actual Git branches (local and remote) from the repository.
513+ ///
514+ /// This function iterates through the branches found in the Git repository,
515+ /// filters them based on the `include_remote` setting, and constructs `BranchInfo`
516+ /// objects for each valid branch. It assigns properties like name, type (local/remote),
517+ /// visual order, and colors based on the provided settings.
518+ ///
519+ /// Arguments:
520+ /// - `repository`: A reference to the Git `Repository` object.
521+ /// - `indices`: A HashMap mapping commit OIDs to their corresponding indices in the `commits` list.
522+ /// - `settings`: A reference to the application `Settings` containing branch configuration.
523+ /// - `counter`: A mutable reference to a counter, incremented for each processed branch to aid in color assignment.
524+ ///
525+ /// Returns:
526+ /// A `Result` containing a `Vec<BranchInfo>` on success, or a `String` error message on failure.
527+ fn extract_actual_branches (
514528 repository : & Repository ,
515- commits : & [ CommitInfo ] ,
516529 indices : & HashMap < Oid , usize > ,
517530 settings : & Settings ,
531+ counter : & mut usize ,
518532) -> Result < Vec < BranchInfo > , String > {
533+ // Determine if remote branches should be included based on settings.
519534 let filter = if settings. include_remote {
520535 None
521536 } else {
522537 Some ( BranchType :: Local )
523538 } ;
539+
540+ // Retrieve branches from the repository, handling potential errors.
524541 let actual_branches = repository
525542 . branches ( filter)
526543 . map_err ( |err| err. message ( ) . to_string ( ) ) ?
527544 . collect :: < Result < Vec < _ > , Error > > ( )
528545 . map_err ( |err| err. message ( ) . to_string ( ) ) ?;
529546
530- let mut counter = 0 ;
531-
532- let mut valid_branches = actual_branches
547+ // Process each actual branch to create `BranchInfo` objects.
548+ let valid_branches = actual_branches
533549 . iter ( )
534550 . filter_map ( |( br, tp) | {
535551 br. get ( ) . name ( ) . and_then ( |n| {
536552 br. get ( ) . target ( ) . map ( |t| {
537- counter += 1 ;
553+ * counter += 1 ; // Increment counter for unique branch identification/coloring.
554+
555+ // Determine the starting index for slicing the branch name string.
538556 let start_index = match tp {
539- BranchType :: Local => 11 ,
540- BranchType :: Remote => 13 ,
557+ BranchType :: Local => 11 , // "refs/heads/"
558+ BranchType :: Remote => 13 , // "refs/remotes/"
541559 } ;
542560 let name = & n[ start_index..] ;
543561 let end_index = indices. get ( & t) . cloned ( ) ;
544562
563+ // Convert branch color to a terminal-compatible format.
545564 let term_color = match to_terminal_color (
546565 & branch_color (
547566 name,
548567 & settings. branches . terminal_colors [ ..] ,
549568 & settings. branches . terminal_colors_unknown ,
550- counter,
569+ * counter,
551570 ) [ ..] ,
552571 ) {
553572 Ok ( col) => col,
554- Err ( err) => return Err ( err) ,
573+ Err ( err) => return Err ( err) , // Propagate color conversion errors.
555574 } ;
556575
576+ // Create and return the BranchInfo object.
557577 Ok ( BranchInfo :: new (
558578 t,
559- None ,
579+ None , // No merge OID for actual branches.
560580 name. to_string ( ) ,
561581 branch_order ( name, & settings. branches . persistence ) as u8 ,
562- & BranchType :: Remote == tp,
563- false ,
564- false ,
582+ & BranchType :: Remote == tp, // Check if it's a remote branch.
583+ false , // Not a derived merge branch.
584+ false , // Not a tag.
565585 BranchVis :: new (
566586 branch_order ( name, & settings. branches . order ) ,
567587 term_color,
568588 branch_color (
569589 name,
570590 & settings. branches . svg_colors ,
571591 & settings. branches . svg_colors_unknown ,
572- counter,
592+ * counter,
573593 ) ,
574594 ) ,
575595 end_index,
576596 ) )
577597 } )
578598 } )
579599 } )
580- . collect :: < Result < Vec < _ > , String > > ( ) ?;
600+ . collect :: < Result < Vec < _ > , String > > ( ) ?; // Collect results, propagating any errors.
601+
602+ Ok ( valid_branches)
603+ }
604+
605+ /// Iterates through commits, identifies merge commits, and derives branch information
606+ /// from their summaries.
607+ ///
608+ /// This function processes each commit in the provided list. If a commit is identified
609+ /// as a merge commit and has a summary, it attempts to parse a branch name from the summary.
610+ /// A `BranchInfo` object is then created for this derived branch, representing the merge
611+ /// point and its properties.
612+ ///
613+ /// Arguments:
614+ /// - `repository`: A reference to the Git `Repository` object.
615+ /// - `commits`: A slice of `CommitInfo` objects, representing the commits to process.
616+ /// - `settings`: A reference to the application `Settings` containing branch and merge pattern configuration.
617+ /// - `counter`: A mutable reference to a counter, incremented for each processed merge branch.
618+ ///
619+ /// Returns:
620+ /// A `Result` containing a `Vec<BranchInfo>` on success, or a `String` error message on failure.
621+ fn extract_merge_branches (
622+ repository : & Repository ,
623+ commits : & [ CommitInfo ] ,
624+ settings : & Settings ,
625+ counter : & mut usize ,
626+ ) -> Result < Vec < BranchInfo > , String > {
627+ let mut merge_branches = Vec :: new ( ) ;
581628
582629 for ( idx, info) in commits. iter ( ) . enumerate ( ) {
583- let commit = repository
584- . find_commit ( info. oid )
585- . map_err ( |err| err. message ( ) . to_string ( ) ) ?;
630+ // Only process if the commit is a merge.
586631 if info. is_merge {
632+ let commit = repository
633+ . find_commit ( info. oid )
634+ . map_err ( |err| err. message ( ) . to_string ( ) ) ?;
635+
636+ // Attempt to get the commit summary.
587637 if let Some ( summary) = commit. summary ( ) {
588- counter += 1 ;
638+ * counter += 1 ; // Increment counter for unique branch identification/coloring.
589639
590640 let parent_oid = commit
591641 . parent_id ( 1 )
592642 . map_err ( |err| err. message ( ) . to_string ( ) ) ?;
593643
644+ // Parse the branch name from the merge summary using configured patterns.
594645 let branch_name = parse_merge_summary ( summary, & settings. merge_patterns )
595646 . unwrap_or_else ( || "unknown" . to_string ( ) ) ;
596647
648+ // Determine persistence and order for the derived branch.
597649 let persistence = branch_order ( & branch_name, & settings. branches . persistence ) as u8 ;
598-
599650 let pos = branch_order ( & branch_name, & settings. branches . order ) ;
600651
652+ // Get terminal and SVG colors for the branch.
601653 let term_col = to_terminal_color (
602654 & branch_color (
603655 & branch_name,
604656 & settings. branches . terminal_colors [ ..] ,
605657 & settings. branches . terminal_colors_unknown ,
606- counter,
658+ * counter,
607659 ) [ ..] ,
608660 ) ?;
609661 let svg_col = branch_color (
610662 & branch_name,
611663 & settings. branches . svg_colors ,
612664 & settings. branches . svg_colors_unknown ,
613- counter,
665+ * counter,
614666 ) ;
615667
668+ // Create and add the BranchInfo for the derived merge branch.
616669 let branch_info = BranchInfo :: new (
617- parent_oid,
618- Some ( info. oid ) ,
670+ parent_oid, // Target is the parent of the merge.
671+ Some ( info. oid ) , // The merge commit itself.
619672 branch_name,
620673 persistence,
621- false ,
622- true ,
623- false ,
674+ false , // Not a remote branch.
675+ true , // This is a derived merge branch.
676+ false , // Not a tag.
624677 BranchVis :: new ( pos, term_col, svg_col) ,
625- Some ( idx + 1 ) ,
678+ Some ( idx + 1 ) , // End index typically points to the commit after the merge.
626679 ) ;
627- valid_branches . push ( branch_info) ;
680+ merge_branches . push ( branch_info) ;
628681 }
629682 }
630683 }
684+ Ok ( merge_branches)
685+ }
631686
632- valid_branches. sort_by_cached_key ( |branch| ( branch. persistence , !branch. is_merged ) ) ;
633-
634- let mut tags = Vec :: new ( ) ;
687+ /// Extracts Git tags and treats them as branches, assigning appropriate properties.
688+ ///
689+ /// This function iterates through all tags in the repository, resolves their target
690+ /// commit OID, and if the target commit is found within the `commits` list,
691+ /// a `BranchInfo` object is created for the tag. Tags are assigned a higher
692+ /// persistence value to ensure they are displayed prominently.
693+ ///
694+ /// Arguments:
695+ /// - `repository`: A reference to the Git `Repository` object.
696+ /// - `indices`: A HashMap mapping commit OIDs to their corresponding indices in the `commits` list.
697+ /// - `settings`: A reference to the application `Settings` containing branch configuration.
698+ /// - `counter`: A mutable reference to a counter, incremented for each processed tag.
699+ ///
700+ /// Returns:
701+ /// A `Result` containing a `Vec<BranchInfo>` on success, or a `String` error message on failure.
702+ fn extract_tags_as_branches (
703+ repository : & Repository ,
704+ indices : & HashMap < Oid , usize > ,
705+ settings : & Settings ,
706+ counter : & mut usize ,
707+ ) -> Result < Vec < BranchInfo > , String > {
708+ let mut tags_info = Vec :: new ( ) ;
709+ let mut tags_raw = Vec :: new ( ) ;
635710
711+ // Iterate over all tags in the repository.
636712 repository
637713 . tag_foreach ( |oid, name| {
638- tags . push ( ( oid, name. to_vec ( ) ) ) ;
639- true
714+ tags_raw . push ( ( oid, name. to_vec ( ) ) ) ;
715+ true // Continue iteration.
640716 } )
641717 . map_err ( |err| err. message ( ) . to_string ( ) ) ?;
642718
643- for ( oid, name) in tags {
644- let name = std:: str:: from_utf8 ( & name[ 5 ..] ) . map_err ( |err| err. to_string ( ) ) ?;
719+ for ( oid, name_bytes) in tags_raw {
720+ // Convert tag name bytes to a UTF-8 string. Tags typically start with "refs/tags/".
721+ let name = std:: str:: from_utf8 ( & name_bytes[ 5 ..] ) . map_err ( |err| err. to_string ( ) ) ?;
645722
723+ // Resolve the target OID of the tag. It could be a tag object or directly a commit.
646724 let target = repository
647725 . find_tag ( oid)
648726 . map ( |tag| tag. target_id ( ) )
649- . or_else ( |_| repository. find_commit ( oid) . map ( |_| oid) ) ;
727+ . or_else ( |_| repository. find_commit ( oid) . map ( |_| oid) ) ; // If not a tag object, try as a direct commit.
650728
651729 if let Ok ( target_oid) = target {
730+ // If the target commit is within our processed commits, create a BranchInfo.
652731 if let Some ( target_index) = indices. get ( & target_oid) {
653- counter += 1 ;
732+ * counter += 1 ; // Increment counter for unique tag identification/coloring.
733+
734+ // Get terminal and SVG colors for the tag.
654735 let term_col = to_terminal_color (
655736 & branch_color (
656737 name,
657738 & settings. branches . terminal_colors [ ..] ,
658739 & settings. branches . terminal_colors_unknown ,
659- counter,
740+ * counter,
660741 ) [ ..] ,
661742 ) ?;
662743 let pos = branch_order ( name, & settings. branches . order ) ;
663744 let svg_col = branch_color (
664745 name,
665746 & settings. branches . svg_colors ,
666747 & settings. branches . svg_colors_unknown ,
667- counter,
748+ * counter,
668749 ) ;
750+
751+ // Create the BranchInfo object for the tag.
669752 let tag_info = BranchInfo :: new (
670753 target_oid,
671- None ,
754+ None , // No merge OID for tags.
672755 name. to_string ( ) ,
673- settings. branches . persistence . len ( ) as u8 + 1 ,
674- false ,
675- false ,
676- true ,
756+ settings. branches . persistence . len ( ) as u8 + 1 , // Tags usually have highest persistence.
757+ false , // Not a remote branch.
758+ false , // Not a derived merge branch.
759+ true , // This is a tag.
677760 BranchVis :: new ( pos, term_col, svg_col) ,
678761 Some ( * target_index) ,
679762 ) ;
680- valid_branches . push ( tag_info) ;
763+ tags_info . push ( tag_info) ;
681764 }
682765 }
683766 }
767+ Ok ( tags_info)
768+ }
684769
685- Ok ( valid_branches)
770+ /// Extracts (real or derived from merge summary) and assigns basic properties to branches and tags.
771+ ///
772+ /// This function orchestrates the extraction of branch information from various sources:
773+ /// 1. Actual Git branches (local and remote).
774+ /// 2. Branches derived from merge commit summaries.
775+ /// 3. Git tags, treated as branches for visualization purposes.
776+ ///
777+ /// It combines the results from these extraction steps, sorts them based on
778+ /// persistence and merge status, and returns a comprehensive list of `BranchInfo` objects.
779+ ///
780+ /// Arguments:
781+ /// - `repository`: A reference to the Git `Repository` object.
782+ /// - `commits`: A slice of `CommitInfo` objects, representing all relevant commits.
783+ /// - `indices`: A HashMap mapping commit OIDs to their corresponding indices in the `commits` list.
784+ /// - `settings`: A reference to the application `Settings` containing all necessary configuration.
785+ ///
786+ /// Returns:
787+ /// A `Result` containing a `Vec<BranchInfo>` on success, or a `String` error message on failure.
788+ fn extract_branches (
789+ repository : & Repository ,
790+ commits : & [ CommitInfo ] ,
791+ indices : & HashMap < Oid , usize > ,
792+ settings : & Settings ,
793+ ) -> Result < Vec < BranchInfo > , String > {
794+ let mut counter = 0 ; // Counter for unique branch/tag identification, especially for coloring.
795+ let mut all_branches: Vec < BranchInfo > = Vec :: new ( ) ;
796+
797+ // 1. Extract actual local and remote branches.
798+ let actual_branches = extract_actual_branches ( repository, indices, settings, & mut counter) ?;
799+ all_branches. extend ( actual_branches) ;
800+
801+ // 2. Extract branches derived from merge commit summaries.
802+ let merge_branches = extract_merge_branches ( repository, commits, settings, & mut counter) ?;
803+ all_branches. extend ( merge_branches) ;
804+
805+ // 3. Extract tags and treat them as branches for visualization.
806+ let tags_as_branches = extract_tags_as_branches ( repository, indices, settings, & mut counter) ?;
807+ all_branches. extend ( tags_as_branches) ;
808+
809+ // Sort all collected branches and tags.
810+ // Sorting criteria: first by persistence, then by whether they are merged (unmerged first).
811+ all_branches. sort_by_cached_key ( |branch| ( branch. persistence , !branch. is_merged ) ) ;
812+
813+ Ok ( all_branches)
686814}
687815
688816/// Traces back branches by following 1st commit parent,
0 commit comments