Skip to content

Commit

Permalink
commands: Implement next and prev
Browse files Browse the repository at this point in the history
This is a naive implementation, which cannot deal with multiple children
or parents stemming from merges.

Note: I currently gave each command separate a separate argument struct
for extensibility. 

Fixes martinvonz#878
  • Loading branch information
PhilipMetzger committed Feb 12, 2023
1 parent aacdcf6 commit 0c832e7
Show file tree
Hide file tree
Showing 2 changed files with 136 additions and 0 deletions.
135 changes: 135 additions & 0 deletions src/commands/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,8 @@ enum Commands {
Diffedit(DiffeditArgs),
Resolve(ResolveArgs),
Split(SplitArgs),
Next(NextArgs),
Prev(PrevArgs),
/// Merge work from multiple branches
///
/// Unlike most other VCSs, `jj merge` does not implicitly include the
Expand Down Expand Up @@ -468,6 +470,68 @@ struct NewArgs {
insert_before: bool,
}

/// Move the current working copy commit to the next child revision in the
/// repository. The command moves you to the next child in a linear fashion.
///
/// F F @
/// | | /
/// C @ => C
/// | / |
/// B B
///
/// If `edit` is passed as an argument, it will move you directly to the child
/// revision.
///
/// F F
/// | |
/// C C
/// | |
/// B @ => @
/// | / |
/// A A
// TODO(#NNN): Handle multiple child revisions properly.
#[derive(clap::Args, Clone, Debug)]
struct NextArgs {
/// How many revisions to move forward. By default advances to the next
/// child.
#[default = "1"]
amount: usize,
/// Instead of moving the empty commit from `jj new`, edit the child
/// revision directly. This mirrors the behavior of Mercurial and
/// Sapling.
edit: bool,
}

/// Move the working copy commit to the parent of the current revision.
/// The command moves you to the parent in a linear fashion.
///
/// F @ F
/// |/ |
/// A => A @
/// | | /
/// B B
///
/// If `edit` is passed as an argument, it will move the working copy commit
/// directly to the parent.
///
/// F @ F
/// |/ |
/// C => C
/// | |
/// B @
/// | |
/// A A
// TODO(#NNN): Handle multiple parents, e.g merges.
#[derive(clap::Args, Clone, Debug)]
struct PrevArgs {
/// How many revisions to move backward. By default moves to the parent.
#[default = "1"]
amount: usize,
/// Edit the parent directly, instead of moving the empty revision.
/// This mirrors the behavior of Mercurial and Sapling.
edit: bool,
}

/// Move changes from one revision into another
///
/// Use `--interactive` to move only part of the source revision into the
Expand Down Expand Up @@ -2176,6 +2240,75 @@ fn combine_messages(
Ok(description)
}

fn cmd_next(ui: &mut Ui, command: &CommandHelper, args: &NextArgs) -> Result<(), CommandError> {
let mut workspace_command = command.workspace_helper(ui)?;
let child = resolve_destination_revs(
&workspace_command,
&["@-"],
/* allow_large_revsets= */ false,
);

let edit = args.edit;
assert!(args.amount == 1 || args.amount > 1);
// Handle the simple `jj next` call.
if args.amount == 1 {}
assert!(args.amount > 1, "Expected to descend to further children");

if edit {}
// TODO(#NNN) We currently cannot deal with multiple children, which resulted
// from branches.
Ok(())
}

fn cmd_prev(ui: &mut Ui, command: &CommandHelper, args: &NextArgs) -> Result<(), CommandError> {
let mut workspace_command = command.workspace_helper(ui)?;
let parent = workspace_command.resolve_single_rev("@-")?;
let edit = args.edit;
assert!(args.amount == 1 || args.amount > 1);
// Handle the simple case of a basic `prev` call.
if args.amount == 1 {
// TODO(#NNN): Handle multiple parents.
if parent.parents().len() > 1 {
return Err(user_error(
"Revision has multiple parents,see issue #NNN for more info",
));
}
workspace_command.check_rewritable(&parent)?;
let mut tx = workspace_command.start_transaction(format!("prev: {current} -> {parent}"))?;
if edit {
tx.edit(&parent).unwrap();
tx.finish(ui)?;
Ok(())
}
// Move the workspace commit after the parent.
let new_ws_revision = tx
.mut_repo()
.new_commit(
command.settings(),
target_id.clone(),
merged_tree.id().clone(),
)
.write()?;
tx.edit(&new_ws_revision).unwrap();
tx.finish(ui)?;
Ok(())
}
assert!(args.amount > 1, "Expected more parents to traverse");
let amount = args.amount;
// Collect all parents and their parents.
let all_parents = cur_rev.parents().map(|parent| parent.id()).collect_vec();
let mut tx = workspace_helper.start_transaction("prev: moved {amount} {current} -> {parent}");

// TODO(#NNN): We currently cannot deal with multiple parents.
if parent.parents().len() > 1 {
return Err(user_error(
"Revision has multiple parents,see issue #NNN for more info",
));
}
if edit {}
Ok(())
}

fn cmd_move(ui: &mut Ui, command: &CommandHelper, args: &MoveArgs) -> Result<(), CommandError> {
let mut workspace_command = command.workspace_helper(ui)?;
let source = workspace_command.resolve_single_rev(args.from.as_deref().unwrap_or("@"))?;
Expand Down Expand Up @@ -3403,6 +3536,8 @@ pub fn run_command(
Commands::Duplicate(sub_args) => cmd_duplicate(ui, command_helper, sub_args),
Commands::Abandon(sub_args) => cmd_abandon(ui, command_helper, sub_args),
Commands::Edit(sub_args) => cmd_edit(ui, command_helper, sub_args),
Commands::Next(sub_args) => cmd_next(ui, command_helper, sub_args),
Commands::Prev(sub_args) => cmd_prev(ui, command_helper, sub_args),
Commands::New(sub_args) => cmd_new(ui, command_helper, sub_args),
Commands::Move(sub_args) => cmd_move(ui, command_helper, sub_args),
Commands::Squash(sub_args) => cmd_squash(ui, command_helper, sub_args),
Expand Down
1 change: 1 addition & 0 deletions tests/test_next_prev_commands.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
// TODO: Write insta tests for `jj next` and `jj prev`

0 comments on commit 0c832e7

Please sign in to comment.