From 53a0e23759f9a3d8690b779187db191776ad1a39 Mon Sep 17 00:00:00 2001 From: Martin von Zweigbergk Date: Thu, 11 Apr 2024 06:46:11 -0700 Subject: [PATCH] rewrite: move functions for updating refs to `MutableRepo` The functions now depend only on `MutableRepo`, so I think they belong on that type. This gets us closer to being able to make `parent_mapping` private again. --- lib/src/repo.rs | 126 +++++++++++++++++++++++++++++++++++++++++++ lib/src/rewrite.rs | 130 +-------------------------------------------- 2 files changed, 128 insertions(+), 128 deletions(-) diff --git a/lib/src/repo.rs b/lib/src/repo.rs index e0137cf72..0df769515 100644 --- a/lib/src/repo.rs +++ b/lib/src/repo.rs @@ -50,6 +50,7 @@ use crate::operation::Operation; use crate::refs::{ diff_named_ref_targets, diff_named_remote_refs, merge_ref_targets, merge_remote_refs, }; +use crate::revset::RevsetExpression; use crate::rewrite::{DescendantRebaser, RebaseOptions}; use crate::settings::{RepoSettings, UserSettings}; use crate::signing::{SignInitError, Signer}; @@ -981,6 +982,131 @@ impl MutableRepo { } } + /// Updates branches, working copies, and anonymous heads after rewriting + /// and/or abandoning commits. + pub fn update_rewritten_references(&mut self, settings: &UserSettings) -> BackendResult<()> { + self.update_all_references(settings)?; + self.update_heads(); + Ok(()) + } + + fn update_all_references(&mut self, settings: &UserSettings) -> Result<(), BackendError> { + for (old_parent_id, (_, new_parent_ids)) in self.parent_mapping.clone() { + // Call `new_parents()` here since `parent_mapping` only contains direct + // mappings, not transitive ones. + // TODO: keep parent_mapping updated with transitive mappings so we don't need + // to call `new_parents()` here. + let new_parent_ids = self.new_parents(&new_parent_ids); + self.update_references(settings, old_parent_id, new_parent_ids)?; + } + Ok(()) + } + + fn update_references( + &mut self, + settings: &UserSettings, + old_commit_id: CommitId, + new_commit_ids: Vec, + ) -> Result<(), BackendError> { + // We arbitrarily pick a new working-copy commit among the candidates. + let abandoned_old_commit = matches!( + self.parent_mapping.get(&old_commit_id), + Some((RewriteType::Abandoned, _)) + ); + self.update_wc_commits( + settings, + &old_commit_id, + &new_commit_ids[0], + abandoned_old_commit, + )?; + + // Build a map from commit to branches pointing to it, so we don't need to scan + // all branches each time we rebase a commit. + // TODO: We no longer need to do this now that we update branches for all + // commits at once. + let mut branches: HashMap<_, HashSet<_>> = HashMap::new(); + for (branch_name, target) in self.view().local_branches() { + for commit in target.added_ids() { + branches + .entry(commit.clone()) + .or_default() + .insert(branch_name.to_owned()); + } + } + + if let Some(branch_names) = branches.get(&old_commit_id).cloned() { + let mut branch_updates = vec![]; + for branch_name in &branch_names { + let local_target = self.get_local_branch(branch_name); + for old_add in local_target.added_ids() { + if *old_add == old_commit_id { + branch_updates.push(branch_name.clone()); + } + } + } + + let old_target = RefTarget::normal(old_commit_id.clone()); + assert!(!new_commit_ids.is_empty()); + let new_target = RefTarget::from_legacy_form( + std::iter::repeat(old_commit_id).take(new_commit_ids.len() - 1), + new_commit_ids, + ); + for branch_name in &branch_updates { + self.merge_local_branch(branch_name, &old_target, &new_target); + } + } + + Ok(()) + } + + fn update_wc_commits( + &mut self, + settings: &UserSettings, + old_commit_id: &CommitId, + new_commit_id: &CommitId, + abandoned_old_commit: bool, + ) -> Result<(), BackendError> { + let workspaces_to_update = self.view().workspaces_for_wc_commit_id(old_commit_id); + if workspaces_to_update.is_empty() { + return Ok(()); + } + + let new_commit = self.store().get_commit(new_commit_id)?; + let new_wc_commit = if !abandoned_old_commit { + new_commit + } else { + self.new_commit( + settings, + vec![new_commit.id().clone()], + new_commit.tree_id().clone(), + ) + .write()? + }; + for workspace_id in workspaces_to_update.into_iter() { + self.edit(workspace_id, &new_wc_commit).unwrap(); + } + Ok(()) + } + + fn update_heads(&mut self) { + let old_commits_expression = + RevsetExpression::commits(self.parent_mapping.keys().cloned().collect()); + let heads_to_add_expression = old_commits_expression + .parents() + .minus(&old_commits_expression); + let heads_to_add = heads_to_add_expression + .evaluate_programmatic(self) + .unwrap() + .iter(); + + let mut view = self.view().store_view().clone(); + for commit_id in self.parent_mapping.keys() { + view.head_ids.remove(commit_id); + } + view.head_ids.extend(heads_to_add); + self.set_view(view); + } + /// After the rebaser returned by this function is dropped, /// self.parent_mapping needs to be cleared. fn rebase_descendants_return_rebaser<'settings, 'repo>( diff --git a/lib/src/rewrite.rs b/lib/src/rewrite.rs index ac919031a..8c8b43f8e 100644 --- a/lib/src/rewrite.rs +++ b/lib/src/rewrite.rs @@ -29,8 +29,7 @@ use crate::index::Index; use crate::matchers::{Matcher, Visit}; use crate::merged_tree::{MergedTree, MergedTreeBuilder}; use crate::object_id::ObjectId; -use crate::op_store::RefTarget; -use crate::repo::{MutableRepo, Repo, RewriteType}; +use crate::repo::{MutableRepo, Repo}; use crate::repo_path::RepoPath; use crate::revset::{RevsetExpression, RevsetIteratorExt}; use crate::settings::UserSettings; @@ -349,97 +348,6 @@ impl<'settings, 'repo> DescendantRebaser<'settings, 'repo> { self.rebased } - fn update_references( - &mut self, - settings: &UserSettings, - old_commit_id: CommitId, - new_commit_ids: Vec, - ) -> Result<(), BackendError> { - // We arbitrarily pick a new working-copy commit among the candidates. - let abandoned_old_commit = matches!( - self.mut_repo.parent_mapping.get(&old_commit_id), - Some((RewriteType::Abandoned, _)) - ); - self.update_wc_commits( - settings, - &old_commit_id, - &new_commit_ids[0], - abandoned_old_commit, - )?; - - // Build a map from commit to branches pointing to it, so we don't need to scan - // all branches each time we rebase a commit. - // TODO: We no longer need to do this now that we update branches for all - // commits at once. - let mut branches: HashMap<_, HashSet<_>> = HashMap::new(); - for (branch_name, target) in self.mut_repo.view().local_branches() { - for commit in target.added_ids() { - branches - .entry(commit.clone()) - .or_default() - .insert(branch_name.to_owned()); - } - } - - if let Some(branch_names) = branches.get(&old_commit_id).cloned() { - let mut branch_updates = vec![]; - for branch_name in &branch_names { - let local_target = self.mut_repo.get_local_branch(branch_name); - for old_add in local_target.added_ids() { - if *old_add == old_commit_id { - branch_updates.push(branch_name.clone()); - } - } - } - - let old_target = RefTarget::normal(old_commit_id.clone()); - assert!(!new_commit_ids.is_empty()); - let new_target = RefTarget::from_legacy_form( - std::iter::repeat(old_commit_id).take(new_commit_ids.len() - 1), - new_commit_ids, - ); - for branch_name in &branch_updates { - self.mut_repo - .merge_local_branch(branch_name, &old_target, &new_target); - } - } - - Ok(()) - } - - fn update_wc_commits( - &mut self, - settings: &UserSettings, - old_commit_id: &CommitId, - new_commit_id: &CommitId, - abandoned_old_commit: bool, - ) -> Result<(), BackendError> { - let workspaces_to_update = self - .mut_repo - .view() - .workspaces_for_wc_commit_id(old_commit_id); - if workspaces_to_update.is_empty() { - return Ok(()); - } - - let new_commit = self.mut_repo.store().get_commit(new_commit_id)?; - let new_wc_commit = if !abandoned_old_commit { - new_commit - } else { - self.mut_repo - .new_commit( - settings, - vec![new_commit.id().clone()], - new_commit.tree_id().clone(), - ) - .write()? - }; - for workspace_id in workspaces_to_update.into_iter() { - self.mut_repo.edit(workspace_id, &new_wc_commit).unwrap(); - } - Ok(()) - } - fn rebase_one(&mut self, old_commit: Commit) -> BackendResult<()> { let old_commit_id = old_commit.id().clone(); let old_parent_ids = old_commit.parent_ids(); @@ -469,44 +377,10 @@ impl<'settings, 'repo> DescendantRebaser<'settings, 'repo> { Ok(()) } - fn update_all_references(&mut self, settings: &UserSettings) -> Result<(), BackendError> { - for (old_parent_id, (_, new_parent_ids)) in self.mut_repo.parent_mapping.clone() { - // Call `new_parents()` here since `parent_mapping` only contains direct - // mappings, not transitive ones. - // TODO: keep parent_mapping updated with transitive mappings so we don't need - // to call `new_parents()` here. - let new_parent_ids = self.mut_repo.new_parents(&new_parent_ids); - self.update_references(settings, old_parent_id, new_parent_ids)?; - } - Ok(()) - } - - fn update_heads(&mut self) { - let old_commits_expression = - RevsetExpression::commits(self.mut_repo.parent_mapping.keys().cloned().collect()); - let heads_to_add_expression = old_commits_expression - .parents() - .minus(&old_commits_expression); - let heads_to_add = heads_to_add_expression - .evaluate_programmatic(self.mut_repo) - .unwrap() - .iter(); - - let mut view = self.mut_repo.view().store_view().clone(); - for commit_id in self.mut_repo.parent_mapping.keys() { - view.head_ids.remove(commit_id); - } - view.head_ids.extend(heads_to_add); - self.mut_repo.set_view(view); - } - pub fn rebase_all(&mut self) -> BackendResult<()> { while let Some(old_commit) = self.to_visit.pop() { self.rebase_one(old_commit)?; } - self.update_all_references(self.settings)?; - self.update_heads(); - - Ok(()) + self.mut_repo.update_rewritten_references(self.settings) } }