mirror of
https://github.com/martinvonz/jj.git
synced 2024-12-28 07:20:11 +00:00
commands: move unsquash code to unsquash.rs
This commit is contained in:
parent
8bb0383ad5
commit
9d72fa2a51
2 changed files with 125 additions and 102 deletions
|
@ -49,6 +49,7 @@ mod sparse;
|
|||
mod split;
|
||||
mod squash;
|
||||
mod status;
|
||||
mod unsquash;
|
||||
mod util;
|
||||
|
||||
use std::fmt::Debug;
|
||||
|
@ -143,7 +144,7 @@ enum Commands {
|
|||
Util(util::UtilCommands),
|
||||
/// Undo an operation (shortcut for `jj op undo`)
|
||||
Undo(operation::OperationUndoArgs),
|
||||
Unsquash(UnsquashArgs),
|
||||
Unsquash(unsquash::UnsquashArgs),
|
||||
Untrack(UntrackArgs),
|
||||
Version(VersionArgs),
|
||||
#[command(subcommand)]
|
||||
|
@ -162,28 +163,6 @@ struct UntrackArgs {
|
|||
paths: Vec<String>,
|
||||
}
|
||||
|
||||
/// Move changes from a revision's parent into the revision
|
||||
///
|
||||
/// After moving the changes out of the parent, the child revision will have the
|
||||
/// same content state as before. If moving the change out of the parent change
|
||||
/// made it empty compared to its parent, it will be abandoned. Without
|
||||
/// `--interactive`, the parent change will always become empty.
|
||||
///
|
||||
/// If the source became empty and both the source and destination had a
|
||||
/// non-empty description, you will be asked for the combined description. If
|
||||
/// either was empty, then the other one will be used.
|
||||
#[derive(clap::Args, Clone, Debug)]
|
||||
#[command(visible_alias = "unamend")]
|
||||
struct UnsquashArgs {
|
||||
#[arg(long, short, default_value = "@")]
|
||||
revision: RevisionArg,
|
||||
/// Interactively choose which parts to unsquash
|
||||
// TODO: It doesn't make much sense to run this without -i. We should make that
|
||||
// the default.
|
||||
#[arg(long, short)]
|
||||
interactive: bool,
|
||||
}
|
||||
|
||||
/// Commands for working with workspaces
|
||||
///
|
||||
/// Workspaces let you add additional working copies attached to the same repo.
|
||||
|
@ -438,84 +417,6 @@ fn combine_messages(
|
|||
Ok(description)
|
||||
}
|
||||
|
||||
#[instrument(skip_all)]
|
||||
fn cmd_unsquash(
|
||||
ui: &mut Ui,
|
||||
command: &CommandHelper,
|
||||
args: &UnsquashArgs,
|
||||
) -> Result<(), CommandError> {
|
||||
let mut workspace_command = command.workspace_helper(ui)?;
|
||||
let commit = workspace_command.resolve_single_rev(&args.revision, ui)?;
|
||||
workspace_command.check_rewritable([&commit])?;
|
||||
let parents = commit.parents();
|
||||
if parents.len() != 1 {
|
||||
return Err(user_error("Cannot unsquash merge commits"));
|
||||
}
|
||||
let parent = &parents[0];
|
||||
workspace_command.check_rewritable(&parents[..1])?;
|
||||
let mut tx =
|
||||
workspace_command.start_transaction(&format!("unsquash commit {}", commit.id().hex()));
|
||||
let parent_base_tree = merge_commit_trees(tx.repo(), &parent.parents())?;
|
||||
let new_parent_tree_id;
|
||||
if args.interactive {
|
||||
let instructions = format!(
|
||||
"\
|
||||
You are moving changes from: {}
|
||||
into its child: {}
|
||||
|
||||
The diff initially shows the parent commit's changes.
|
||||
|
||||
Adjust the right side until it shows the contents you want to keep in
|
||||
the parent commit. The changes you edited out will be moved into the
|
||||
child commit. If you don't make any changes, then the operation will be
|
||||
aborted.
|
||||
",
|
||||
tx.format_commit_summary(parent),
|
||||
tx.format_commit_summary(&commit)
|
||||
);
|
||||
let parent_tree = parent.tree()?;
|
||||
new_parent_tree_id = tx.edit_diff(
|
||||
ui,
|
||||
&parent_base_tree,
|
||||
&parent_tree,
|
||||
&EverythingMatcher,
|
||||
&instructions,
|
||||
)?;
|
||||
if new_parent_tree_id == parent_base_tree.id() {
|
||||
return Err(user_error("No changes selected"));
|
||||
}
|
||||
} else {
|
||||
new_parent_tree_id = parent_base_tree.id().clone();
|
||||
}
|
||||
// Abandon the parent if it is now empty (always the case in the non-interactive
|
||||
// case).
|
||||
if new_parent_tree_id == parent_base_tree.id() {
|
||||
tx.mut_repo().record_abandoned_commit(parent.id().clone());
|
||||
let description =
|
||||
combine_messages(tx.base_repo(), parent, &commit, command.settings(), true)?;
|
||||
// Commit the new child on top of the parent's parents.
|
||||
tx.mut_repo()
|
||||
.rewrite_commit(command.settings(), &commit)
|
||||
.set_parents(parent.parent_ids().to_vec())
|
||||
.set_description(description)
|
||||
.write()?;
|
||||
} else {
|
||||
let new_parent = tx
|
||||
.mut_repo()
|
||||
.rewrite_commit(command.settings(), parent)
|
||||
.set_tree_id(new_parent_tree_id)
|
||||
.set_predecessors(vec![parent.id().clone(), commit.id().clone()])
|
||||
.write()?;
|
||||
// Commit the new child on top of the new parent.
|
||||
tx.mut_repo()
|
||||
.rewrite_commit(command.settings(), &commit)
|
||||
.set_parents(vec![new_parent.id().clone()])
|
||||
.write()?;
|
||||
}
|
||||
tx.finish(ui)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn description_template_for_commit(
|
||||
ui: &Ui,
|
||||
settings: &UserSettings,
|
||||
|
@ -833,7 +734,7 @@ pub fn run_command(ui: &mut Ui, command_helper: &CommandHelper) -> Result<(), Co
|
|||
Commands::New(sub_args) => new::cmd_new(ui, command_helper, sub_args),
|
||||
Commands::Move(sub_args) => r#move::cmd_move(ui, command_helper, sub_args),
|
||||
Commands::Squash(sub_args) => squash::cmd_squash(ui, command_helper, sub_args),
|
||||
Commands::Unsquash(sub_args) => cmd_unsquash(ui, command_helper, sub_args),
|
||||
Commands::Unsquash(sub_args) => unsquash::cmd_unsquash(ui, command_helper, sub_args),
|
||||
Commands::Restore(sub_args) => restore::cmd_restore(ui, command_helper, sub_args),
|
||||
Commands::Run(sub_args) => run::cmd_run(ui, command_helper, sub_args),
|
||||
Commands::Diffedit(sub_args) => diffedit::cmd_diffedit(ui, command_helper, sub_args),
|
||||
|
|
122
cli/src/commands/unsquash.rs
Normal file
122
cli/src/commands/unsquash.rs
Normal file
|
@ -0,0 +1,122 @@
|
|||
// Copyright 2020 The Jujutsu Authors
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// https://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
use jj_lib::backend::ObjectId;
|
||||
use jj_lib::matchers::EverythingMatcher;
|
||||
use jj_lib::rewrite::merge_commit_trees;
|
||||
use tracing::instrument;
|
||||
|
||||
use super::combine_messages;
|
||||
use crate::cli_util::{user_error, CommandError, CommandHelper, RevisionArg};
|
||||
use crate::ui::Ui;
|
||||
|
||||
/// Move changes from a revision's parent into the revision
|
||||
///
|
||||
/// After moving the changes out of the parent, the child revision will have the
|
||||
/// same content state as before. If moving the change out of the parent change
|
||||
/// made it empty compared to its parent, it will be abandoned. Without
|
||||
/// `--interactive`, the parent change will always become empty.
|
||||
///
|
||||
/// If the source became empty and both the source and destination had a
|
||||
/// non-empty description, you will be asked for the combined description. If
|
||||
/// either was empty, then the other one will be used.
|
||||
#[derive(clap::Args, Clone, Debug)]
|
||||
#[command(visible_alias = "unamend")]
|
||||
pub(crate) struct UnsquashArgs {
|
||||
#[arg(long, short, default_value = "@")]
|
||||
revision: RevisionArg,
|
||||
/// Interactively choose which parts to unsquash
|
||||
// TODO: It doesn't make much sense to run this without -i. We should make that
|
||||
// the default.
|
||||
#[arg(long, short)]
|
||||
interactive: bool,
|
||||
}
|
||||
|
||||
#[instrument(skip_all)]
|
||||
pub(crate) fn cmd_unsquash(
|
||||
ui: &mut Ui,
|
||||
command: &CommandHelper,
|
||||
args: &UnsquashArgs,
|
||||
) -> Result<(), CommandError> {
|
||||
let mut workspace_command = command.workspace_helper(ui)?;
|
||||
let commit = workspace_command.resolve_single_rev(&args.revision, ui)?;
|
||||
workspace_command.check_rewritable([&commit])?;
|
||||
let parents = commit.parents();
|
||||
if parents.len() != 1 {
|
||||
return Err(user_error("Cannot unsquash merge commits"));
|
||||
}
|
||||
let parent = &parents[0];
|
||||
workspace_command.check_rewritable(&parents[..1])?;
|
||||
let mut tx =
|
||||
workspace_command.start_transaction(&format!("unsquash commit {}", commit.id().hex()));
|
||||
let parent_base_tree = merge_commit_trees(tx.repo(), &parent.parents())?;
|
||||
let new_parent_tree_id;
|
||||
if args.interactive {
|
||||
let instructions = format!(
|
||||
"\
|
||||
You are moving changes from: {}
|
||||
into its child: {}
|
||||
|
||||
The diff initially shows the parent commit's changes.
|
||||
|
||||
Adjust the right side until it shows the contents you want to keep in
|
||||
the parent commit. The changes you edited out will be moved into the
|
||||
child commit. If you don't make any changes, then the operation will be
|
||||
aborted.
|
||||
",
|
||||
tx.format_commit_summary(parent),
|
||||
tx.format_commit_summary(&commit)
|
||||
);
|
||||
let parent_tree = parent.tree()?;
|
||||
new_parent_tree_id = tx.edit_diff(
|
||||
ui,
|
||||
&parent_base_tree,
|
||||
&parent_tree,
|
||||
&EverythingMatcher,
|
||||
&instructions,
|
||||
)?;
|
||||
if new_parent_tree_id == parent_base_tree.id() {
|
||||
return Err(user_error("No changes selected"));
|
||||
}
|
||||
} else {
|
||||
new_parent_tree_id = parent_base_tree.id().clone();
|
||||
}
|
||||
// Abandon the parent if it is now empty (always the case in the non-interactive
|
||||
// case).
|
||||
if new_parent_tree_id == parent_base_tree.id() {
|
||||
tx.mut_repo().record_abandoned_commit(parent.id().clone());
|
||||
let description =
|
||||
combine_messages(tx.base_repo(), parent, &commit, command.settings(), true)?;
|
||||
// Commit the new child on top of the parent's parents.
|
||||
tx.mut_repo()
|
||||
.rewrite_commit(command.settings(), &commit)
|
||||
.set_parents(parent.parent_ids().to_vec())
|
||||
.set_description(description)
|
||||
.write()?;
|
||||
} else {
|
||||
let new_parent = tx
|
||||
.mut_repo()
|
||||
.rewrite_commit(command.settings(), parent)
|
||||
.set_tree_id(new_parent_tree_id)
|
||||
.set_predecessors(vec![parent.id().clone(), commit.id().clone()])
|
||||
.write()?;
|
||||
// Commit the new child on top of the new parent.
|
||||
tx.mut_repo()
|
||||
.rewrite_commit(command.settings(), &commit)
|
||||
.set_parents(vec![new_parent.id().clone()])
|
||||
.write()?;
|
||||
}
|
||||
tx.finish(ui)?;
|
||||
Ok(())
|
||||
}
|
Loading…
Reference in a new issue