Skip to content

Commit 3ab5f1d

Browse files
committed
cli split: Refactor and move tree selection to a function
This change moves the code for prompting the user to select changes for the first commit to a helper function. There are no functional changes. Resulting from the discussion in #5828 we are planning to add several new flags (-B, -A, and -d) to jj split. Since this may change how the user selects changes (i.e. they may select changes to remove from the target commit, rather than what stays in the target commit), I want to move the selection logic to a function to make the main split function more readable.
1 parent 707680b commit 3ab5f1d

File tree

2 files changed

+66
-45
lines changed

2 files changed

+66
-45
lines changed

cli/src/commands/split.rs

+64-43
Original file line numberDiff line numberDiff line change
@@ -19,12 +19,14 @@ use jj_lib::commit::Commit;
1919
use jj_lib::matchers::Matcher;
2020
use jj_lib::object_id::ObjectId;
2121
use jj_lib::repo::Repo;
22+
use jj_lib::rewrite::CommitWithSelection;
2223
use tracing::instrument;
2324

2425
use crate::cli_util::CommandHelper;
2526
use crate::cli_util::DiffSelector;
2627
use crate::cli_util::RevisionArg;
2728
use crate::cli_util::WorkspaceCommandHelper;
29+
use crate::cli_util::WorkspaceCommandTransaction;
2830
use crate::command_error::user_error_with_hint;
2931
use crate::command_error::CommandError;
3032
use crate::complete;
@@ -138,44 +140,14 @@ pub(crate) fn cmd_split(
138140
} = args.resolve(ui, &workspace_command)?;
139141
let text_editor = workspace_command.text_editor()?;
140142
let mut tx = workspace_command.start_transaction();
141-
let end_tree = target_commit.tree()?;
142-
let base_tree = target_commit.parent_tree(tx.repo())?;
143-
let format_instructions = || {
144-
format!(
145-
"\
146-
You are splitting a commit into two: {}
147-
148-
The diff initially shows the changes in the commit you're splitting.
149-
150-
Adjust the right side until it shows the contents you want for the first commit.
151-
The remainder will be in the second commit.
152-
",
153-
tx.format_commit_summary(&target_commit)
154-
)
155-
};
156143

157144
// Prompt the user to select the changes they want for the first commit.
158-
let selected_tree_id =
159-
diff_selector.select(&base_tree, &end_tree, matcher.as_ref(), format_instructions)?;
160-
if &selected_tree_id == target_commit.tree_id() {
161-
// The user selected everything from the original commit.
162-
writeln!(
163-
ui.warning_default(),
164-
"All changes have been selected, so the second commit will be empty"
165-
)?;
166-
} else if selected_tree_id == base_tree.id() {
167-
// The user selected nothing, so the first commit will be empty.
168-
writeln!(
169-
ui.warning_default(),
170-
"No changes have been selected, so the first commit will be empty"
171-
)?;
172-
}
145+
let target = select_diff(ui, &tx, &target_commit, &matcher, &diff_selector)?;
173146

174147
// Create the first commit, which includes the changes selected by the user.
175-
let selected_tree = tx.repo().store().get_root_tree(&selected_tree_id)?;
176148
let first_commit = {
177-
let mut commit_builder = tx.repo_mut().rewrite_commit(&target_commit).detach();
178-
commit_builder.set_tree_id(selected_tree_id);
149+
let mut commit_builder = tx.repo_mut().rewrite_commit(&target.commit).detach();
150+
commit_builder.set_tree_id(target.selected_tree.id());
179151
if commit_builder.description().is_empty() {
180152
commit_builder.set_description(tx.settings().get_string("ui.default-description")?);
181153
}
@@ -194,27 +166,28 @@ The remainder will be in the second commit.
194166
// Create the second commit, which includes everything the user didn't
195167
// select.
196168
let second_commit = {
197-
let new_tree = if parallel {
169+
let target_tree = target.commit.tree()?;
170+
let new_tree = if args.parallel {
198171
// Merge the original commit tree with its parent using the tree
199172
// containing the user selected changes as the base for the merge.
200173
// This results in a tree with the changes the user didn't select.
201-
end_tree.merge(&selected_tree, &base_tree)?
174+
target_tree.merge(&target.selected_tree, &target.parent_tree)?
202175
} else {
203-
end_tree
176+
target_tree
204177
};
205178
let parents = if parallel {
206-
target_commit.parent_ids().to_vec()
179+
target.commit.parent_ids().to_vec()
207180
} else {
208181
vec![first_commit.id().clone()]
209182
};
210-
let mut commit_builder = tx.repo_mut().rewrite_commit(&target_commit).detach();
183+
let mut commit_builder = tx.repo_mut().rewrite_commit(&target.commit).detach();
211184
commit_builder
212185
.set_parents(parents)
213186
.set_tree_id(new_tree.id())
214187
// Generate a new change id so that the commit being split doesn't
215188
// become divergent.
216189
.generate_new_change_id();
217-
let description = if target_commit.description().is_empty() {
190+
let description = if target.commit.description().is_empty() {
218191
// If there was no description before, don't ask for one for the
219192
// second commit.
220193
"".to_string()
@@ -238,11 +211,11 @@ The remainder will be in the second commit.
238211
// moves any bookmarks pointing to the target commit to the second
239212
// commit.
240213
tx.repo_mut()
241-
.set_rewritten_commit(target_commit.id().clone(), second_commit.id().clone());
214+
.set_rewritten_commit(target.commit.id().clone(), second_commit.id().clone());
242215
}
243216
let mut num_rebased = 0;
244217
tx.repo_mut()
245-
.transform_descendants(vec![target_commit.id().clone()], |mut rewriter| {
218+
.transform_descendants(vec![target.commit.id().clone()], |mut rewriter| {
246219
num_rebased += 1;
247220
if parallel && legacy_bookmark_behavior {
248221
// The old_parent is the second commit due to the rewrite above.
@@ -259,7 +232,7 @@ The remainder will be in the second commit.
259232
// Move the working copy commit (@) to the second commit for any workspaces
260233
// where the target commit is the working copy commit.
261234
for (workspace_id, working_copy_commit) in tx.base_repo().clone().view().wc_commit_ids() {
262-
if working_copy_commit == target_commit.id() {
235+
if working_copy_commit == target.commit.id() {
263236
tx.repo_mut().edit(workspace_id.clone(), &second_commit)?;
264237
}
265238
}
@@ -274,7 +247,55 @@ The remainder will be in the second commit.
274247
tx.write_commit_summary(formatter.as_mut(), &second_commit)?;
275248
writeln!(formatter)?;
276249
}
277-
tx.finish(ui, format!("split commit {}", target_commit.id().hex()))?;
250+
tx.finish(ui, format!("split commit {}", target.commit.id().hex()))?;
278251
Ok(())
279252
}
280253

254+
/// Prompts the user to select the content they want in the first commit and
255+
/// returns the target commit and the tree corresponding to the selection.
256+
fn select_diff(
257+
ui: &Ui,
258+
tx: &WorkspaceCommandTransaction,
259+
target_commit: &Commit,
260+
matcher: &dyn Matcher,
261+
diff_selector: &DiffSelector,
262+
) -> Result<CommitWithSelection, CommandError> {
263+
let format_instructions = || {
264+
format!(
265+
"\
266+
You are splitting a commit into two: {}
267+
268+
The diff initially shows the changes in the commit you're splitting.
269+
270+
Adjust the right side until it shows the contents you want for the first commit.
271+
The remainder will be in the second commit.
272+
",
273+
tx.format_commit_summary(target_commit)
274+
)
275+
};
276+
let parent_tree = target_commit.parent_tree(tx.repo())?;
277+
let selected_tree_id = diff_selector.select(
278+
&parent_tree,
279+
&target_commit.tree()?,
280+
matcher,
281+
format_instructions,
282+
)?;
283+
let selection = CommitWithSelection {
284+
commit: target_commit.clone(),
285+
selected_tree: tx.repo().store().get_root_tree(&selected_tree_id)?,
286+
parent_tree,
287+
};
288+
if selection.is_full_selection() {
289+
writeln!(
290+
ui.warning_default(),
291+
"All changes have been selected, so the second commit will be empty"
292+
)?;
293+
} else if selection.is_empty_selection() {
294+
writeln!(
295+
ui.warning_default(),
296+
"No changes have been selected, so the first commit will be empty"
297+
)?;
298+
}
299+
300+
Ok(selection)
301+
}

lib/src/rewrite.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -1037,7 +1037,7 @@ pub struct CommitWithSelection {
10371037

10381038
impl CommitWithSelection {
10391039
/// Returns true if the selection contains all changes in the commit.
1040-
fn is_full_selection(&self) -> bool {
1040+
pub fn is_full_selection(&self) -> bool {
10411041
&self.selected_tree.id() == self.commit.tree_id()
10421042
}
10431043

@@ -1046,7 +1046,7 @@ impl CommitWithSelection {
10461046
///
10471047
/// Both `is_full_selection()` and `is_empty_selection()`
10481048
/// can be true if the commit is itself empty.
1049-
fn is_empty_selection(&self) -> bool {
1049+
pub fn is_empty_selection(&self) -> bool {
10501050
self.selected_tree.id() == self.parent_tree.id()
10511051
}
10521052
}

0 commit comments

Comments
 (0)