mirror of
https://github.com/martinvonz/jj.git
synced 2025-05-22 15:45:28 +00:00
view: move merging of views up to repo level (#111)
I want to make it so when we apply a repo-level change that removes a head, we rebase descendants of that head onto the closes visible ancestor, or onto its successor if the head has been rewritten (see #111 for details). The view itself doesn't have enough information for that; we need repo-level information (to figure out relationships between commits). The view doesn't have enough information to do the.
This commit is contained in:
parent
a663a5d89c
commit
39838a76bf
107
lib/src/repo.rs
107
lib/src/repo.rs
@ -693,12 +693,113 @@ impl MutableRepo {
|
|||||||
self.index.merge_in(other_repo.index());
|
self.index.merge_in(other_repo.index());
|
||||||
|
|
||||||
self.enforce_view_invariants();
|
self.enforce_view_invariants();
|
||||||
self.view
|
self.merge_view(&base_repo.view, &other_repo.view);
|
||||||
.get_mut()
|
|
||||||
.merge(self.index.as_index_ref(), &base_repo.view, &other_repo.view);
|
|
||||||
self.view_dirty = true;
|
self.view_dirty = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn merge_view(&mut self, base: &View, other: &View) {
|
||||||
|
// Merge checkouts. If there's a conflict, we keep the self side.
|
||||||
|
for (workspace_id, base_checkout) in base.checkouts() {
|
||||||
|
let self_checkout = self.view().get_checkout(workspace_id);
|
||||||
|
let other_checkout = other.get_checkout(workspace_id);
|
||||||
|
if other_checkout == Some(base_checkout) || other_checkout == self_checkout {
|
||||||
|
// The other side didn't change or both sides changed in the
|
||||||
|
// same way.
|
||||||
|
} else if let Some(other_checkout) = other_checkout {
|
||||||
|
if self_checkout == Some(base_checkout) {
|
||||||
|
self.view_mut()
|
||||||
|
.set_checkout(workspace_id.clone(), other_checkout.clone());
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// The other side removed the workspace. We want to remove it even if the self
|
||||||
|
// side changed the checkout.
|
||||||
|
self.view_mut().remove_checkout(workspace_id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for (workspace_id, other_checkout) in other.checkouts() {
|
||||||
|
if self.view().get_checkout(workspace_id).is_none()
|
||||||
|
&& base.get_checkout(workspace_id).is_none()
|
||||||
|
{
|
||||||
|
// The other side added the workspace.
|
||||||
|
self.view_mut()
|
||||||
|
.set_checkout(workspace_id.clone(), other_checkout.clone());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for removed_head in base.public_heads().difference(other.public_heads()) {
|
||||||
|
self.view_mut().remove_public_head(removed_head);
|
||||||
|
}
|
||||||
|
for added_head in other.public_heads().difference(base.public_heads()) {
|
||||||
|
self.view_mut().add_public_head(added_head);
|
||||||
|
}
|
||||||
|
|
||||||
|
for removed_head in base.heads().difference(other.heads()) {
|
||||||
|
self.view_mut().remove_head(removed_head);
|
||||||
|
}
|
||||||
|
for added_head in other.heads().difference(base.heads()) {
|
||||||
|
self.view_mut().add_head(added_head);
|
||||||
|
}
|
||||||
|
// TODO: Should it be considered a conflict if a commit-head is removed on one
|
||||||
|
// side while a child or successor is created on another side? Maybe a
|
||||||
|
// warning?
|
||||||
|
|
||||||
|
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.branches().get(branch_name);
|
||||||
|
let other_branch = other.branches().get(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(),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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);
|
||||||
|
self.view.get_mut().merge_single_ref(
|
||||||
|
self.index.as_index_ref(),
|
||||||
|
&ref_name,
|
||||||
|
base_target.as_ref(),
|
||||||
|
other_target.as_ref(),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub fn merge_single_ref(
|
pub fn merge_single_ref(
|
||||||
&mut self,
|
&mut self,
|
||||||
ref_name: &RefName,
|
ref_name: &RefName,
|
||||||
|
101
lib/src/view.rs
101
lib/src/view.rs
@ -236,107 +236,6 @@ impl View {
|
|||||||
&mut self.data
|
&mut self.data
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn merge(&mut self, index: IndexRef, base: &View, other: &View) {
|
|
||||||
// Merge checkouts. If there's a conflict, we keep the self side.
|
|
||||||
for (workspace_id, base_checkout) in base.checkouts() {
|
|
||||||
let self_checkout = self.get_checkout(workspace_id);
|
|
||||||
let other_checkout = other.get_checkout(workspace_id);
|
|
||||||
if other_checkout == Some(base_checkout) || other_checkout == self_checkout {
|
|
||||||
// The other side didn't change or both sides changed in the
|
|
||||||
// same way.
|
|
||||||
} else if let Some(other_checkout) = other_checkout {
|
|
||||||
if self_checkout == Some(base_checkout) {
|
|
||||||
self.set_checkout(workspace_id.clone(), other_checkout.clone());
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// The other side removed the workspace. We want to remove it even if the self
|
|
||||||
// side changed the checkout.
|
|
||||||
self.remove_checkout(workspace_id);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
for (workspace_id, other_checkout) in other.checkouts() {
|
|
||||||
if self.get_checkout(workspace_id).is_none()
|
|
||||||
&& base.get_checkout(workspace_id).is_none()
|
|
||||||
{
|
|
||||||
// The other side added the workspace.
|
|
||||||
self.set_checkout(workspace_id.clone(), other_checkout.clone());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
for removed_head in base.public_heads().difference(other.public_heads()) {
|
|
||||||
self.remove_public_head(removed_head);
|
|
||||||
}
|
|
||||||
for added_head in other.public_heads().difference(base.public_heads()) {
|
|
||||||
self.add_public_head(added_head);
|
|
||||||
}
|
|
||||||
|
|
||||||
for removed_head in base.heads().difference(other.heads()) {
|
|
||||||
self.remove_head(removed_head);
|
|
||||||
}
|
|
||||||
for added_head in other.heads().difference(base.heads()) {
|
|
||||||
self.add_head(added_head);
|
|
||||||
}
|
|
||||||
// TODO: Should it be considered a conflict if a commit-head is removed on one
|
|
||||||
// side while a child or successor is created on another side? Maybe a
|
|
||||||
// warning?
|
|
||||||
|
|
||||||
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.branches().get(branch_name);
|
|
||||||
let other_branch = other.branches().get(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(),
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
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);
|
|
||||||
self.merge_single_ref(
|
|
||||||
index,
|
|
||||||
&ref_name,
|
|
||||||
base_target.as_ref(),
|
|
||||||
other_target.as_ref(),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn merge_single_ref(
|
pub fn merge_single_ref(
|
||||||
&mut self,
|
&mut self,
|
||||||
index: IndexRef,
|
index: IndexRef,
|
||||||
|
Loading…
x
Reference in New Issue
Block a user