Skip to content

Commit 9946bd5

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

File tree

1 file changed

+179
-49
lines changed

1 file changed

+179
-49
lines changed

src/graph.rs

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

Comments
 (0)