cli: don't keep Repo references from before transaction start

We had a recent bug where we used a repo reference from before we
started a transaction and modified the repo. While it's often safe and
correct to use such references, it isn't always. This patch removes
all such cases. I think it generally makes the code clearer, and
better prepared for #50, if we ever get around to that. I found these
by temporarily making `WorkspaceCommandHelper::start_transaction()`
take a mutable reference.
This commit is contained in:
Martin von Zweigbergk 2022-07-05 23:01:21 -07:00 committed by Martin von Zweigbergk
parent 1b5cd140d5
commit 3a351bee7d
2 changed files with 23 additions and 23 deletions

View file

@ -52,6 +52,10 @@ impl Transaction {
self.tags.insert(key, value); self.tags.insert(key, value);
} }
pub fn repo(&self) -> &MutableRepo {
self.repo.as_ref().unwrap()
}
pub fn mut_repo(&mut self) -> &mut MutableRepo { pub fn mut_repo(&mut self) -> &mut MutableRepo {
self.repo.as_mut().unwrap() self.repo.as_mut().unwrap()
} }

View file

@ -3587,7 +3587,6 @@ fn cmd_squash(ui: &mut Ui, command: &CommandHelper, args: &SquashArgs) -> Result
let mut workspace_command = command.workspace_helper(ui)?; let mut workspace_command = command.workspace_helper(ui)?;
let commit = workspace_command.resolve_single_rev(ui, &args.revision)?; let commit = workspace_command.resolve_single_rev(ui, &args.revision)?;
workspace_command.check_rewriteable(&commit)?; workspace_command.check_rewriteable(&commit)?;
let repo = workspace_command.repo();
let parents = commit.parents(); let parents = commit.parents();
if parents.len() != 1 { if parents.len() != 1 {
return Err(CommandError::UserError(String::from( return Err(CommandError::UserError(String::from(
@ -3598,7 +3597,6 @@ fn cmd_squash(ui: &mut Ui, command: &CommandHelper, args: &SquashArgs) -> Result
workspace_command.check_rewriteable(parent)?; workspace_command.check_rewriteable(parent)?;
let mut tx = let mut tx =
workspace_command.start_transaction(&format!("squash commit {}", commit.id().hex())); workspace_command.start_transaction(&format!("squash commit {}", commit.id().hex()));
let mut_repo = tx.mut_repo();
let instructions = format!( let instructions = format!(
"\ "\
You are moving changes from: {} You are moving changes from: {}
@ -3630,7 +3628,8 @@ from the source will be moved into the parent.
// Abandon the child if the parent now has all the content from the child // Abandon the child if the parent now has all the content from the child
// (always the case in the non-interactive case). // (always the case in the non-interactive case).
let abandon_child = let abandon_child =
&new_parent_tree_id == commit.tree_id() && !repo.view().is_checkout(commit.id()); &new_parent_tree_id == commit.tree_id() && !tx.base_repo().view().is_checkout(commit.id());
let mut_repo = tx.mut_repo();
let new_parent = CommitBuilder::for_rewrite_from(ui.settings(), parent) let new_parent = CommitBuilder::for_rewrite_from(ui.settings(), parent)
.set_tree(new_parent_tree_id) .set_tree(new_parent_tree_id)
.set_predecessors(vec![parent.id().clone(), commit.id().clone()]) .set_predecessors(vec![parent.id().clone(), commit.id().clone()])
@ -3655,7 +3654,6 @@ fn cmd_unsquash(
let mut workspace_command = command.workspace_helper(ui)?; let mut workspace_command = command.workspace_helper(ui)?;
let commit = workspace_command.resolve_single_rev(ui, &args.revision)?; let commit = workspace_command.resolve_single_rev(ui, &args.revision)?;
workspace_command.check_rewriteable(&commit)?; workspace_command.check_rewriteable(&commit)?;
let repo = workspace_command.repo();
let parents = commit.parents(); let parents = commit.parents();
if parents.len() != 1 { if parents.len() != 1 {
return Err(CommandError::UserError(String::from( return Err(CommandError::UserError(String::from(
@ -3666,7 +3664,6 @@ fn cmd_unsquash(
workspace_command.check_rewriteable(parent)?; workspace_command.check_rewriteable(parent)?;
let mut tx = let mut tx =
workspace_command.start_transaction(&format!("unsquash commit {}", commit.id().hex())); workspace_command.start_transaction(&format!("unsquash commit {}", commit.id().hex()));
let mut_repo = tx.mut_repo();
let parent_base_tree = let parent_base_tree =
merge_commit_trees(workspace_command.repo().as_repo_ref(), &parent.parents()); merge_commit_trees(workspace_command.repo().as_repo_ref(), &parent.parents());
let new_parent_tree_id; let new_parent_tree_id;
@ -3696,21 +3693,23 @@ aborted.
} }
// Abandon the parent if it is now empty (always the case in the non-interactive // Abandon the parent if it is now empty (always the case in the non-interactive
// case). // case).
if &new_parent_tree_id == parent_base_tree.id() && !repo.view().is_checkout(parent.id()) { if &new_parent_tree_id == parent_base_tree.id()
mut_repo.record_abandoned_commit(parent.id().clone()); && !tx.base_repo().view().is_checkout(parent.id())
{
tx.mut_repo().record_abandoned_commit(parent.id().clone());
// Commit the new child on top of the parent's parents. // Commit the new child on top of the parent's parents.
CommitBuilder::for_rewrite_from(ui.settings(), &commit) CommitBuilder::for_rewrite_from(ui.settings(), &commit)
.set_parents(parent.parent_ids()) .set_parents(parent.parent_ids())
.write_to_repo(mut_repo); .write_to_repo(tx.mut_repo());
} else { } else {
let new_parent = CommitBuilder::for_rewrite_from(ui.settings(), parent) let new_parent = CommitBuilder::for_rewrite_from(ui.settings(), parent)
.set_tree(new_parent_tree_id) .set_tree(new_parent_tree_id)
.set_predecessors(vec![parent.id().clone(), commit.id().clone()]) .set_predecessors(vec![parent.id().clone(), commit.id().clone()])
.write_to_repo(mut_repo); .write_to_repo(tx.mut_repo());
// Commit the new child on top of the new parent. // Commit the new child on top of the new parent.
CommitBuilder::for_rewrite_from(ui.settings(), &commit) CommitBuilder::for_rewrite_from(ui.settings(), &commit)
.set_parents(vec![new_parent.id().clone()]) .set_parents(vec![new_parent.id().clone()])
.write_to_repo(mut_repo); .write_to_repo(tx.mut_repo());
} }
workspace_command.finish_transaction(ui, tx)?; workspace_command.finish_transaction(ui, tx)?;
Ok(()) Ok(())
@ -3840,8 +3839,7 @@ fn cmd_split(ui: &mut Ui, command: &CommandHelper, args: &SplitArgs) -> Result<(
let mut workspace_command = command.workspace_helper(ui)?; let mut workspace_command = command.workspace_helper(ui)?;
let commit = workspace_command.resolve_single_rev(ui, &args.revision)?; let commit = workspace_command.resolve_single_rev(ui, &args.revision)?;
workspace_command.check_rewriteable(&commit)?; workspace_command.check_rewriteable(&commit)?;
let repo = workspace_command.repo(); let base_tree = merge_commit_trees(workspace_command.repo().as_repo_ref(), &commit.parents());
let base_tree = merge_commit_trees(repo.as_repo_ref(), &commit.parents());
let instructions = format!( let instructions = format!(
"\ "\
You are splitting a commit in two: {} You are splitting a commit in two: {}
@ -3868,20 +3866,19 @@ any changes, then the operation will be aborted.
} else { } else {
let mut tx = let mut tx =
workspace_command.start_transaction(&format!("split commit {}", commit.id().hex())); workspace_command.start_transaction(&format!("split commit {}", commit.id().hex()));
let mut_repo = tx.mut_repo();
let first_description = edit_description( let first_description = edit_description(
ui, ui,
repo, tx.base_repo(),
&("JJ: Enter commit description for the first part.\n".to_string() &("JJ: Enter commit description for the first part.\n".to_string()
+ commit.description()), + commit.description()),
)?; )?;
let first_commit = CommitBuilder::for_rewrite_from(ui.settings(), &commit) let first_commit = CommitBuilder::for_rewrite_from(ui.settings(), &commit)
.set_tree(tree_id) .set_tree(tree_id)
.set_description(first_description) .set_description(first_description)
.write_to_repo(mut_repo); .write_to_repo(tx.mut_repo());
let second_description = edit_description( let second_description = edit_description(
ui, ui,
repo, tx.base_repo(),
&("JJ: Enter commit description for the second part.\n".to_string() &("JJ: Enter commit description for the second part.\n".to_string()
+ commit.description()), + commit.description()),
)?; )?;
@ -3890,10 +3887,10 @@ any changes, then the operation will be aborted.
.set_tree(commit.tree_id().clone()) .set_tree(commit.tree_id().clone())
.generate_new_change_id() .generate_new_change_id()
.set_description(second_description) .set_description(second_description)
.write_to_repo(mut_repo); .write_to_repo(tx.mut_repo());
let mut rebaser = DescendantRebaser::new( let mut rebaser = DescendantRebaser::new(
ui.settings(), ui.settings(),
mut_repo, tx.mut_repo(),
hashmap! { commit.id().clone() => hashset!{second_commit.id().clone()} }, hashmap! { commit.id().clone() => hashset!{second_commit.id().clone()} },
hashset! {}, hashset! {},
); );
@ -3904,13 +3901,13 @@ any changes, then the operation will be aborted.
} }
ui.write("First part: ")?; ui.write("First part: ")?;
ui.write_commit_summary( ui.write_commit_summary(
mut_repo.as_repo_ref(), tx.repo().as_repo_ref(),
&workspace_command.workspace_id(), &workspace_command.workspace_id(),
&first_commit, &first_commit,
)?; )?;
ui.write("\nSecond part: ")?; ui.write("\nSecond part: ")?;
ui.write_commit_summary( ui.write_commit_summary(
mut_repo.as_repo_ref(), tx.repo().as_repo_ref(),
&workspace_command.workspace_id(), &workspace_command.workspace_id(),
&second_commit, &second_commit,
)?; )?;
@ -3938,17 +3935,16 @@ fn cmd_merge(ui: &mut Ui, command: &CommandHelper, args: &MergeArgs) -> Result<(
parent_ids.push(commit.id().clone()); parent_ids.push(commit.id().clone());
commits.push(commit); commits.push(commit);
} }
let repo = workspace_command.repo();
let description = if let Some(message) = &args.message { let description = if let Some(message) = &args.message {
message.to_string() message.to_string()
} else { } else {
edit_description( edit_description(
ui, ui,
repo, workspace_command.repo(),
"\n\nJJ: Enter commit description for the merge commit.\n", "\n\nJJ: Enter commit description for the merge commit.\n",
)? )?
}; };
let merged_tree = merge_commit_trees(repo.as_repo_ref(), &commits); let merged_tree = merge_commit_trees(workspace_command.repo().as_repo_ref(), &commits);
let mut tx = workspace_command.start_transaction("merge commits"); let mut tx = workspace_command.start_transaction("merge commits");
CommitBuilder::for_new_commit(ui.settings(), merged_tree.id().clone()) CommitBuilder::for_new_commit(ui.settings(), merged_tree.id().clone())
.set_parents(parent_ids) .set_parents(parent_ids)