Skip to content

Commit 4ba9c8e

Browse files
committed
refactor: Split fn extract_branches into 3 smaller.
1 parent 1909852 commit 4ba9c8e

1 file changed

Lines changed: 177 additions & 49 deletions

File tree

src/graph.rs

Lines changed: 177 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -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

Comments
 (0)