mirror of
https://github.com/martinvonz/jj.git
synced 2024-12-28 23:55:54 +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 split;
|
||||||
mod squash;
|
mod squash;
|
||||||
mod status;
|
mod status;
|
||||||
|
mod unsquash;
|
||||||
mod util;
|
mod util;
|
||||||
|
|
||||||
use std::fmt::Debug;
|
use std::fmt::Debug;
|
||||||
|
@ -143,7 +144,7 @@ enum Commands {
|
||||||
Util(util::UtilCommands),
|
Util(util::UtilCommands),
|
||||||
/// Undo an operation (shortcut for `jj op undo`)
|
/// Undo an operation (shortcut for `jj op undo`)
|
||||||
Undo(operation::OperationUndoArgs),
|
Undo(operation::OperationUndoArgs),
|
||||||
Unsquash(UnsquashArgs),
|
Unsquash(unsquash::UnsquashArgs),
|
||||||
Untrack(UntrackArgs),
|
Untrack(UntrackArgs),
|
||||||
Version(VersionArgs),
|
Version(VersionArgs),
|
||||||
#[command(subcommand)]
|
#[command(subcommand)]
|
||||||
|
@ -162,28 +163,6 @@ struct UntrackArgs {
|
||||||
paths: Vec<String>,
|
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
|
/// Commands for working with workspaces
|
||||||
///
|
///
|
||||||
/// Workspaces let you add additional working copies attached to the same repo.
|
/// Workspaces let you add additional working copies attached to the same repo.
|
||||||
|
@ -438,84 +417,6 @@ fn combine_messages(
|
||||||
Ok(description)
|
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(
|
fn description_template_for_commit(
|
||||||
ui: &Ui,
|
ui: &Ui,
|
||||||
settings: &UserSettings,
|
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::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::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::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::Restore(sub_args) => restore::cmd_restore(ui, command_helper, sub_args),
|
||||||
Commands::Run(sub_args) => run::cmd_run(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),
|
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