Skip to content

Commit

Permalink
repo: rewrite diffing of named refs to compare each targets pair
Browse files Browse the repository at this point in the history
As I'm going to split branches into per-remote map, .get_branch(name) will need
to gather remote branches by name to construct remote_targets map. Let's instead
iterate local/remote branches separately. I also migrated diffing of the other
kinds of refs to filter out unchanged entries upfront.
  • Loading branch information
yuja committed Oct 11, 2023
1 parent e6f4955 commit 679a591
Show file tree
Hide file tree
Showing 2 changed files with 46 additions and 48 deletions.
28 changes: 28 additions & 0 deletions lib/src/refs.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,11 +14,39 @@

#![allow(missing_docs)]

use itertools::EitherOrBoth;

use crate::backend::CommitId;
use crate::index::Index;
use crate::merge::{trivial_merge, Merge};
use crate::op_store::{BranchTarget, RefTarget, RefTargetOptionExt};

/// Compares `refs1` and `refs2` targets, yields entry if they differ.
///
/// `refs1` and `refs2` must be sorted by `K`.
pub fn diff_named_refs<'a, 'b, K: Ord>(
refs1: impl IntoIterator<Item = (K, &'a RefTarget)>,
refs2: impl IntoIterator<Item = (K, &'b RefTarget)>,
) -> impl Iterator<Item = (K, (&'a RefTarget, &'b RefTarget))> {
iter_named_ref_pairs(refs1, refs2).filter(|(_, (target1, target2))| target1 != target2)
}

/// Iterates `refs1` and `refs2` target pairs by name.
///
/// `refs1` and `refs2` must be sorted by `K`.
fn iter_named_ref_pairs<'a, 'b, K: Ord>(
refs1: impl IntoIterator<Item = (K, &'a RefTarget)>,
refs2: impl IntoIterator<Item = (K, &'b RefTarget)>,
) -> impl Iterator<Item = (K, (&'a RefTarget, &'b RefTarget))> {
itertools::merge_join_by(refs1, refs2, |(name1, _), (name2, _)| name1.cmp(name2)).map(|entry| {
match entry {
EitherOrBoth::Both((name, target1), (_, target2)) => (name, (target1, target2)),
EitherOrBoth::Left((name, target1)) => (name, (target1, RefTarget::absent_ref())),
EitherOrBoth::Right((name, target2)) => (name, (RefTarget::absent_ref(), target2)),
}
})
}

pub fn merge_ref_targets(
index: &dyn Index,
left: &RefTarget,
Expand Down
66 changes: 18 additions & 48 deletions lib/src/repo.rs
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ use crate::local_backend::LocalBackend;
use crate::op_heads_store::{self, OpHeadResolutionError, OpHeadsStore};
use crate::op_store::{BranchTarget, OpStore, OpStoreError, OperationId, RefTarget, WorkspaceId};
use crate::operation::Operation;
use crate::refs::merge_ref_targets;
use crate::refs::{diff_named_refs, merge_ref_targets};
use crate::revset::{self, ChangeIdIndex, Revset, RevsetExpression};
use crate::rewrite::DescendantRebaser;
use crate::settings::{RepoSettings, UserSettings};
Expand Down Expand Up @@ -1102,54 +1102,24 @@ impl MutableRepo {
self.view_mut().add_head(added_head);
}

let mut maybe_changed_ref_names = HashSet::new();

let base_branches: HashSet<_> = base.branches().keys().cloned().collect();
let other_branches: HashSet<_> = other.branches().keys().cloned().collect();
for branch_name in base_branches.union(&other_branches) {
let base_branch = base.get_branch(branch_name);
let other_branch = other.get_branch(branch_name);
if other_branch == base_branch {
// Unchanged on other side
continue;
}

maybe_changed_ref_names.insert(RefName::LocalBranch(branch_name.clone()));
if let Some(branch) = base_branch {
for remote in branch.remote_targets.keys() {
maybe_changed_ref_names.insert(RefName::RemoteBranch {
branch: branch_name.clone(),
remote: remote.clone(),
});
}
}
if let Some(branch) = other_branch {
for remote in branch.remote_targets.keys() {
maybe_changed_ref_names.insert(RefName::RemoteBranch {
branch: branch_name.clone(),
remote: remote.clone(),
});
let changed_refs = itertools::chain!(
diff_named_refs(base.local_branches(), other.local_branches())
.map(|(name, diff)| (RefName::LocalBranch(name.to_owned()), diff)),
diff_named_refs(base.remote_branches(), other.remote_branches()).map(
|((branch, remote), diff)| {
let ref_name = RefName::RemoteBranch {
branch: branch.to_owned(),
remote: remote.to_owned(),
};
(ref_name, diff)
}
}
}

for tag_name in base.tags().keys() {
maybe_changed_ref_names.insert(RefName::Tag(tag_name.clone()));
}
for tag_name in other.tags().keys() {
maybe_changed_ref_names.insert(RefName::Tag(tag_name.clone()));
}

for git_ref_name in base.git_refs().keys() {
maybe_changed_ref_names.insert(RefName::GitRef(git_ref_name.clone()));
}
for git_ref_name in other.git_refs().keys() {
maybe_changed_ref_names.insert(RefName::GitRef(git_ref_name.clone()));
}

for ref_name in maybe_changed_ref_names {
let base_target = base.get_ref(&ref_name);
let other_target = other.get_ref(&ref_name);
),
diff_named_refs(base.tags(), other.tags())
.map(|(name, diff)| (RefName::Tag(name.to_owned()), diff)),
diff_named_refs(base.git_refs(), other.git_refs())
.map(|(name, diff)| (RefName::GitRef(name.to_owned()), diff)),
);
for (ref_name, (base_target, other_target)) in changed_refs {
self.view.get_mut().merge_single_ref(
self.index.as_index(),
&ref_name,
Expand Down

0 comments on commit 679a591

Please sign in to comment.