Skip to content

Commit d79c742

Browse files
committed
better resolution of remote branch lkg_parents
1 parent 3923e0b commit d79c742

File tree

3 files changed

+77
-24
lines changed

3 files changed

+77
-24
lines changed

src/git2_ops.rs

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,26 @@ impl GitRepo {
7373
.unwrap_or(false))
7474
}
7575

76+
/// Find the merge-base (common ancestor) of two refs.
77+
/// Equivalent to `git merge-base <ref1> <ref2>`
78+
pub fn merge_base(&self, ref1: &str, ref2: &str) -> Result<String> {
79+
let _bench = GitBenchmark::start("git2:merge-base");
80+
let obj1 = self
81+
.repo
82+
.revparse_single(ref1)
83+
.with_context(|| format!("Failed to resolve ref: {}", ref1))?;
84+
let obj2 = self
85+
.repo
86+
.revparse_single(ref2)
87+
.with_context(|| format!("Failed to resolve ref: {}", ref2))?;
88+
89+
let oid = self
90+
.repo
91+
.merge_base(obj1.id(), obj2.id())
92+
.with_context(|| format!("Failed to find merge-base between {} and {}", ref1, ref2))?;
93+
Ok(oid.to_string())
94+
}
95+
7696
/// Check if a local branch exists.
7797
/// Only checks for local branches, not remote refs.
7898
pub fn branch_exists(&self, branch: &str) -> bool {
@@ -87,6 +107,22 @@ impl GitRepo {
87107
self.repo.revparse_single(ref_name).is_ok()
88108
}
89109

110+
/// Resolve a branch name to a ref that exists.
111+
/// Tries the branch name first, then falls back to origin/{branch}.
112+
/// Returns None if neither exists.
113+
pub fn resolve_branch_ref(&self, branch: &str) -> Option<String> {
114+
if self.branch_exists(branch) {
115+
Some(branch.to_string())
116+
} else {
117+
let remote_ref = format!("origin/{}", branch);
118+
if self.ref_exists(&remote_ref) {
119+
Some(remote_ref)
120+
} else {
121+
None
122+
}
123+
}
124+
}
125+
90126
pub fn branch_status(
91127
&self,
92128
parent_branch: Option<&str>,

src/main.rs

Lines changed: 28 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -627,25 +627,35 @@ fn recur_tree(
627627
.truecolor(branch_color.0, branch_color.1, branch_color.2)
628628
};
629629

630-
// Get diff stats from LKG ancestor (or parent branch) to current branch
630+
// Get diff stats from LKG ancestor (or merge-base) to current branch
631631
let diff_stats = {
632-
// Try lkg_parent first, fall back to resolved parent branch
633-
let base_ref = branch
634-
.lkg_parent
635-
.as_deref()
636-
.unwrap_or(&branch_status.parent_branch);
637-
638-
match git_repo.diff_stats(base_ref, &branch_status.sha) {
639-
Ok((adds, dels)) => {
640-
let green = dim.apply(colors::GREEN);
641-
let red = dim.apply(colors::RED);
642-
format!(
643-
" [{} {}]",
644-
format!("+{}", adds).truecolor(green.0, green.1, green.2),
645-
format!("-{}", dels).truecolor(red.0, red.1, red.2)
646-
)
647-
}
648-
Err(_) => String::new(),
632+
// Determine base ref and whether it's reliable
633+
let (base_ref, is_reliable) = if let Some(lkg) = branch.lkg_parent.as_deref() {
634+
(Some(lkg.to_string()), true)
635+
} else if let Ok(merge_base) =
636+
git_repo.merge_base(&branch_status.parent_branch, &branch_status.sha)
637+
{
638+
(Some(merge_base), false) // merge-base is less reliable
639+
} else {
640+
(None, false)
641+
};
642+
643+
match base_ref {
644+
Some(base) => match git_repo.diff_stats(&base, &branch_status.sha) {
645+
Ok((adds, dels)) => {
646+
let green = dim.apply(colors::GREEN);
647+
let red = dim.apply(colors::RED);
648+
let prefix = if is_reliable { "" } else { "~ " };
649+
format!(
650+
" [{}{}{}]",
651+
prefix,
652+
format!("+{}", adds).truecolor(green.0, green.1, green.2),
653+
format!(" -{}", dels).truecolor(red.0, red.1, red.2)
654+
)
655+
}
656+
Err(_) => String::new(),
657+
},
658+
None => String::new(),
649659
}
650660
};
651661

src/state.rs

Lines changed: 13 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -663,23 +663,30 @@ impl State {
663663
let mut queue: VecDeque<(Option<String>, String)> = VecDeque::new();
664664
queue.push_back((None, trunk.main_branch.clone()));
665665
while let Some((parent, branch)) = queue.pop_front() {
666-
// Skip branches that don't exist locally
667-
if !git_branch_exists(git_repo, &branch) {
666+
// Resolve branch to local or remote ref
667+
let Some(branch_ref) = git_repo.resolve_branch_ref(&branch) else {
668668
tracing::debug!("Skipping non-existent branch {} in refresh_lkgs", branch);
669669
continue;
670-
}
670+
};
671671

672672
if let Some(parent) = parent {
673+
// Resolve parent to local or remote ref
674+
let parent_ref = git_repo
675+
.resolve_branch_ref(&parent)
676+
.unwrap_or_else(|| parent.clone());
677+
673678
let tree_branch = self.get_tree_branch(repo, &branch).unwrap();
674679
if let Some(lkg_parent) = tree_branch.lkg_parent.as_deref() {
675-
if git_repo.is_ancestor(lkg_parent, &branch)? {
680+
if git_repo.is_ancestor(lkg_parent, &branch_ref)? {
676681
parent_lkgs.insert(tree_branch.name.clone(), Some(lkg_parent.to_string()));
677682
} else {
678683
parent_lkgs.insert(tree_branch.name.clone(), None);
679684
}
680685
}
681-
if git_repo.is_ancestor(&parent, &branch).unwrap_or(false)
682-
&& let Ok(new_lkg_parent) = git_repo.sha(&parent)
686+
if git_repo
687+
.is_ancestor(&parent_ref, &branch_ref)
688+
.unwrap_or(false)
689+
&& let Ok(new_lkg_parent) = git_repo.sha(&parent_ref)
683690
{
684691
tracing::debug!(
685692
lkg_parent = ?new_lkg_parent,

0 commit comments

Comments
 (0)