Skip to content

Commit a6e9e4d

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

1 file changed

Lines changed: 181 additions & 49 deletions

File tree

src/graph.rs

Lines changed: 181 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -509,180 +509,312 @@ 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.
581602

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();
629+
630+
log::debug!("iterate commits for merge branches..");
631+
>>>>>>> Conflict 2 of 5 ends
582632
for (idx, info) in commits.iter().enumerate() {
583-
let commit = repository
584-
.find_commit(info.oid)
585-
.map_err(|err| err.message().to_string())?;
633+
// Only process if the commit is a merge.
586634
if info.is_merge {
635+
let commit = repository
636+
.find_commit(info.oid)
637+
.map_err(|err| err.message().to_string())?;
638+
639+
// Attempt to get the commit summary.
587640
if let Some(summary) = commit.summary() {
588-
counter += 1;
641+
*counter += 1; // Increment counter for unique branch identification/coloring.
589642

590643
let parent_oid = commit
591644
.parent_id(1)
592645
.map_err(|err| err.message().to_string())?;
593646

647+
// Parse the branch name from the merge summary using configured patterns.
594648
let branch_name = parse_merge_summary(summary, &settings.merge_patterns)
595649
.unwrap_or_else(|| "unknown".to_string());
596650

651+
// Determine persistence and order for the derived branch.
597652
let persistence = branch_order(&branch_name, &settings.branches.persistence) as u8;
598-
599653
let pos = branch_order(&branch_name, &settings.branches.order);
600654

655+
// Get terminal and SVG colors for the branch.
601656
let term_col = to_terminal_color(
602657
&branch_color(
603658
&branch_name,
604659
&settings.branches.terminal_colors[..],
605660
&settings.branches.terminal_colors_unknown,
606-
counter,
661+
*counter,
607662
)[..],
608663
)?;
609664
let svg_col = branch_color(
610665
&branch_name,
611666
&settings.branches.svg_colors,
612667
&settings.branches.svg_colors_unknown,
613-
counter,
668+
*counter,
614669
);
615670

671+
// Create and add the BranchInfo for the derived merge branch.
616672
let branch_info = BranchInfo::new(
617-
parent_oid,
618-
Some(info.oid),
673+
parent_oid, // Target is the parent of the merge.
674+
Some(info.oid), // The merge commit itself.
619675
branch_name,
620676
persistence,
621-
false,
622-
true,
623-
false,
677+
false, // Not a remote branch.
678+
true, // This is a derived merge branch.
679+
false, // Not a tag.
624680
BranchVis::new(pos, term_col, svg_col),
625-
Some(idx + 1),
681+
Some(idx + 1), // End index typically points to the commit after the merge.
626682
);
627-
valid_branches.push(branch_info);
683+
merge_branches.push(branch_info);
628684
}
629685
}
630686
}
687+
Ok(merge_branches)
688+
}
631689

632-
valid_branches.sort_by_cached_key(|branch| (branch.persistence, !branch.is_merged));
633-
634-
let mut tags = Vec::new();
690+
/// Extracts Git tags and treats them as branches, assigning appropriate properties.
691+
///
692+
/// This function iterates through all tags in the repository, resolves their target
693+
/// commit OID, and if the target commit is found within the `commits` list,
694+
/// a `BranchInfo` object is created for the tag. Tags are assigned a higher
695+
/// persistence value to ensure they are displayed prominently.
696+
///
697+
/// Arguments:
698+
/// - `repository`: A reference to the Git `Repository` object.
699+
/// - `indices`: A HashMap mapping commit OIDs to their corresponding indices in the `commits` list.
700+
/// - `settings`: A reference to the application `Settings` containing branch configuration.
701+
/// - `counter`: A mutable reference to a counter, incremented for each processed tag.
702+
///
703+
/// Returns:
704+
/// A `Result` containing a `Vec<BranchInfo>` on success, or a `String` error message on failure.
705+
fn extract_tags_as_branches(
706+
repository: &Repository,
707+
indices: &HashMap<Oid, usize>,
708+
settings: &Settings,
709+
counter: &mut usize,
710+
) -> Result<Vec<BranchInfo>, String> {
711+
let mut tags_info = Vec::new();
712+
let mut tags_raw = Vec::new();
635713

714+
// Iterate over all tags in the repository.
636715
repository
637716
.tag_foreach(|oid, name| {
638-
tags.push((oid, name.to_vec()));
639-
true
717+
tags_raw.push((oid, name.to_vec()));
718+
true // Continue iteration.
640719
})
641720
.map_err(|err| err.message().to_string())?;
642721

643-
for (oid, name) in tags {
644-
let name = std::str::from_utf8(&name[5..]).map_err(|err| err.to_string())?;
722+
for (oid, name_bytes) in tags_raw {
723+
// Convert tag name bytes to a UTF-8 string. Tags typically start with "refs/tags/".
724+
let name = std::str::from_utf8(&name_bytes[5..]).map_err(|err| err.to_string())?;
645725

726+
// Resolve the target OID of the tag. It could be a tag object or directly a commit.
646727
let target = repository
647728
.find_tag(oid)
648729
.map(|tag| tag.target_id())
649-
.or_else(|_| repository.find_commit(oid).map(|_| oid));
730+
.or_else(|_| repository.find_commit(oid).map(|_| oid)); // If not a tag object, try as a direct commit.
650731

651732
if let Ok(target_oid) = target {
733+
// If the target commit is within our processed commits, create a BranchInfo.
652734
if let Some(target_index) = indices.get(&target_oid) {
653-
counter += 1;
735+
*counter += 1; // Increment counter for unique tag identification/coloring.
736+
737+
// Get terminal and SVG colors for the tag.
654738
let term_col = to_terminal_color(
655739
&branch_color(
656740
name,
657741
&settings.branches.terminal_colors[..],
658742
&settings.branches.terminal_colors_unknown,
659-
counter,
743+
*counter,
660744
)[..],
661745
)?;
662746
let pos = branch_order(name, &settings.branches.order);
663747
let svg_col = branch_color(
664748
name,
665749
&settings.branches.svg_colors,
666750
&settings.branches.svg_colors_unknown,
667-
counter,
751+
*counter,
668752
);
753+
754+
// Create the BranchInfo object for the tag.
669755
let tag_info = BranchInfo::new(
670756
target_oid,
671-
None,
757+
None, // No merge OID for tags.
672758
name.to_string(),
673-
settings.branches.persistence.len() as u8 + 1,
674-
false,
675-
false,
676-
true,
759+
settings.branches.persistence.len() as u8 + 1, // Tags usually have highest persistence.
760+
false, // Not a remote branch.
761+
false, // Not a derived merge branch.
762+
true, // This is a tag.
677763
BranchVis::new(pos, term_col, svg_col),
678764
Some(*target_index),
679765
);
680-
valid_branches.push(tag_info);
766+
tags_info.push(tag_info);
681767
}
682768
}
683769
}
770+
Ok(tags_info)
771+
}
684772

685-
Ok(valid_branches)
773+
774+
/// Extracts (real or derived from merge summary) and assigns basic properties to branches and tags.
775+
///
776+
/// This function orchestrates the extraction of branch information from various sources:
777+
/// 1. Actual Git branches (local and remote).
778+
/// 2. Branches derived from merge commit summaries.
779+
/// 3. Git tags, treated as branches for visualization purposes.
780+
///
781+
/// It combines the results from these extraction steps, sorts them based on
782+
/// persistence and merge status, and returns a comprehensive list of `BranchInfo` objects.
783+
///
784+
/// Arguments:
785+
/// - `repository`: A reference to the Git `Repository` object.
786+
/// - `commits`: A slice of `CommitInfo` objects, representing all relevant commits.
787+
/// - `indices`: A HashMap mapping commit OIDs to their corresponding indices in the `commits` list.
788+
/// - `settings`: A reference to the application `Settings` containing all necessary configuration.
789+
///
790+
/// Returns:
791+
/// A `Result` containing a `Vec<BranchInfo>` on success, or a `String` error message on failure.
792+
fn extract_branches(
793+
repository: &Repository,
794+
commits: &[CommitInfo],
795+
indices: &HashMap<Oid, usize>,
796+
settings: &Settings,
797+
) -> Result<Vec<BranchInfo>, String> {
798+
let mut counter = 0; // Counter for unique branch/tag identification, especially for coloring.
799+
let mut all_branches: Vec<BranchInfo> = Vec::new();
800+
801+
// 1. Extract actual local and remote branches.
802+
let actual_branches = extract_actual_branches(repository, indices, settings, &mut counter)?;
803+
all_branches.extend(actual_branches);
804+
805+
// 2. Extract branches derived from merge commit summaries.
806+
let merge_branches = extract_merge_branches(repository, commits, settings, &mut counter)?;
807+
all_branches.extend(merge_branches);
808+
809+
// 3. Extract tags and treat them as branches for visualization.
810+
let tags_as_branches = extract_tags_as_branches(repository, indices, settings, &mut counter)?;
811+
all_branches.extend(tags_as_branches);
812+
813+
// Sort all collected branches and tags.
814+
// Sorting criteria: first by persistence, then by whether they are merged (unmerged first).
815+
all_branches.sort_by_cached_key(|branch| (branch.persistence, !branch.is_merged));
816+
817+
Ok(all_branches)
686818
}
687819

688820
/// Traces back branches by following 1st commit parent,

0 commit comments

Comments
 (0)