--- a/src/workspace.rs +++ b/src/workspace.rs @@ -114,7 +114,7 @@ .get_wc_commit_id(self.workspace.workspace_name()) } - pub async fn resolve_single_rev(&self, rev: &str) -> Result { + async fn resolve_revisions(&self, rev: &str) -> Result, CommandError> { let workspace_context = RevsetWorkspaceContext { path_converter: &self.path_converter, workspace_name: self.workspace.workspace_name(), @@ -140,19 +140,17 @@ .with_id_prefix_context(&id_prefix_context); let resolved = expression.resolve_user_expression(self.repo.as_ref(), &symbol_resolver)?; let revset = resolved.evaluate(self.repo.as_ref())?; - let mut stream = revset.stream(); - let first = stream - .next() - .await - .ok_or_else(|| user_error(format!("Revset `{rev}` didn't resolve to any revisions")))? - .map_err(|e| user_error(format!("{e}")))?; - if stream.next().await.is_some() { - return Err(user_error(format!( - "Revset `{rev}` resolved to more than one revision" - ))); - } - drop(stream); - let commit = self.repo.store().get_commit_async(&first).await?; + revset.stream().collect::>().await.into_iter() + .collect::, _>>() + .map_err(|e| user_error(format!("{e}"))) + } + + pub async fn resolve_single_rev(&self, rev: &str) -> Result { + let ids = self.resolve_revisions(rev).await?; + let [id] = ids.as_slice() else { + return Err(user_error(format!("Revset `{rev}` must resolve to exactly one revision"))); + }; + let commit = self.repo.store().get_commit_async(id).await?; Ok(commit) } @@ -165,6 +163,12 @@ return Err(user_error(format!( "The root commit {:.12} is immutable", id + ))); + } + let immutable = self.resolve_revisions(&format!("{id}:: & immutable()")).await?; + if !immutable.is_empty() { + return Err(user_error(format!( + "Rewriting {id:.12} would affect immutable commits" ))); } } @@ -238,10 +242,12 @@ let null_ui = Ui::null(); let auto_tracking_matcher = self.auto_tracking_matcher(&null_ui)?; let options = self.snapshot_options(&auto_tracking_matcher)?; + let rewrite_check = self.check_rewritable([wc_commit.id()]).await; let mut locked_ws = self.workspace.start_working_copy_mutation().await?; let (new_tree, _stats) = locked_ws.locked_wc().snapshot(&options).await?; if new_tree.tree_ids_and_labels() != wc_commit.tree().tree_ids_and_labels() { + rewrite_check?; let mut tx = self.repo.start_transaction(); tx.set_workspace_name(&workspace_name); tx.set_is_snapshot(true);