mirror of
https://github.com/martinvonz/jj.git
synced 2025-01-18 02:04:19 +00:00
cli: add jj edit
for editing a commit in the wokring copy
This commit is contained in:
parent
6952b4f91e
commit
42b2937d5e
4 changed files with 101 additions and 3 deletions
|
@ -34,7 +34,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|||
* The [`$NO_COLOR` environment variable](https://no-color.org/) no longer
|
||||
overrides the `ui.color` configuration if explicitly set.
|
||||
|
||||
* `jj edit` has been renamed to `jj touchup`.
|
||||
* `jj edit` has been renamed to `jj touchup`, and `jj edit` is now a new command
|
||||
with different behavior. The new `jj edit` lets you edit a commit in the
|
||||
working copy, even if the specified commit is closed.
|
||||
|
||||
* `jj git push` no longer aborts if you attempt to push an open commit (but it
|
||||
now aborts if a commit does not have a description).
|
||||
|
|
|
@ -597,7 +597,6 @@ impl MutableRepo {
|
|||
}
|
||||
|
||||
pub fn edit(&mut self, workspace_id: WorkspaceId, commit: &Commit) {
|
||||
assert!(commit.is_open(), "commit to edit is closed");
|
||||
self.leave_commit(&workspace_id);
|
||||
self.set_checkout(workspace_id, commit.id().clone());
|
||||
}
|
||||
|
@ -606,7 +605,6 @@ impl MutableRepo {
|
|||
let maybe_current_checkout_id = self.view.borrow().get_checkout(workspace_id).cloned();
|
||||
if let Some(current_checkout_id) = maybe_current_checkout_id {
|
||||
let current_checkout = self.store().get_commit(¤t_checkout_id).unwrap();
|
||||
assert!(current_checkout.is_open(), "current checkout is closed");
|
||||
if current_checkout.is_empty()
|
||||
&& current_checkout.description().is_empty()
|
||||
&& self.view().heads().contains(current_checkout.id())
|
||||
|
|
|
@ -1127,6 +1127,7 @@ enum Commands {
|
|||
Open(OpenArgs),
|
||||
Duplicate(DuplicateArgs),
|
||||
Abandon(AbandonArgs),
|
||||
Edit(EditArgs),
|
||||
New(NewArgs),
|
||||
Move(MoveArgs),
|
||||
Squash(SquashArgs),
|
||||
|
@ -1401,6 +1402,16 @@ struct AbandonArgs {
|
|||
revisions: Vec<String>,
|
||||
}
|
||||
|
||||
/// Edit a commit in the working copy
|
||||
///
|
||||
/// Puts the contents of a commit in the working copy for editing. Any changes
|
||||
/// you make in the working copy will update (amend) the commit.
|
||||
#[derive(clap::Args, Clone, Debug)]
|
||||
struct EditArgs {
|
||||
/// The commit to edit
|
||||
revision: String,
|
||||
}
|
||||
|
||||
/// Create a new, empty change and check it out
|
||||
///
|
||||
/// This may be useful if you want to make some changes you're unsure of on top
|
||||
|
@ -3439,6 +3450,22 @@ fn cmd_abandon(
|
|||
Ok(())
|
||||
}
|
||||
|
||||
fn cmd_edit(ui: &mut Ui, command: &CommandHelper, args: &EditArgs) -> Result<(), CommandError> {
|
||||
let mut workspace_command = command.workspace_helper(ui)?;
|
||||
let new_commit = workspace_command.resolve_single_rev(ui, &args.revision)?;
|
||||
let workspace_id = workspace_command.workspace_id();
|
||||
if workspace_command.repo().view().get_checkout(&workspace_id) == Some(new_commit.id()) {
|
||||
ui.write("Already editing that commit\n")?;
|
||||
} else {
|
||||
workspace_command.commit_working_copy(ui)?;
|
||||
let mut tx =
|
||||
workspace_command.start_transaction(&format!("edit commit {}", new_commit.id().hex()));
|
||||
tx.mut_repo().edit(workspace_id, &new_commit);
|
||||
workspace_command.finish_transaction(ui, tx)?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn cmd_new(ui: &mut Ui, command: &CommandHelper, args: &NewArgs) -> Result<(), CommandError> {
|
||||
let mut workspace_command = command.workspace_helper(ui)?;
|
||||
let parent = workspace_command.resolve_single_rev(ui, &args.revision)?;
|
||||
|
@ -5294,6 +5321,7 @@ where
|
|||
Commands::Open(sub_args) => cmd_open(ui, &command_helper, sub_args),
|
||||
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::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),
|
||||
|
|
70
tests/test_edit_command.rs
Normal file
70
tests/test_edit_command.rs
Normal file
|
@ -0,0 +1,70 @@
|
|||
// Copyright 2022 Google LLC
|
||||
//
|
||||
// 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 std::path::Path;
|
||||
|
||||
use crate::common::TestEnvironment;
|
||||
|
||||
pub mod common;
|
||||
|
||||
#[test]
|
||||
fn test_edit() {
|
||||
let test_env = TestEnvironment::default();
|
||||
test_env.jj_cmd_success(test_env.env_root(), &["init", "repo", "--git"]);
|
||||
let repo_path = test_env.env_root().join("repo");
|
||||
std::fs::write(repo_path.join("file1"), "0").unwrap();
|
||||
test_env.jj_cmd_success(&repo_path, &["close", "-m", "first"]);
|
||||
test_env.jj_cmd_success(&repo_path, &["describe", "-m", "second"]);
|
||||
std::fs::write(repo_path.join("file1"), "1").unwrap();
|
||||
|
||||
// Errors out without argument
|
||||
test_env.jj_cmd_cli_error(&repo_path, &["edit"]);
|
||||
|
||||
// Can edit a closed commit
|
||||
let stdout = test_env.jj_cmd_success(&repo_path, &["edit", "@-"]);
|
||||
insta::assert_snapshot!(stdout, @r###"
|
||||
Working copy now at: 5c9d6c787f29 first
|
||||
Added 0 files, modified 1 files, removed 0 files
|
||||
"###);
|
||||
insta::assert_snapshot!(get_log_output(&test_env, &repo_path), @r###"
|
||||
o 37ed5225d0fd open second
|
||||
@ 5c9d6c787f29 closed first
|
||||
o 000000000000 closed (no description set)
|
||||
"###);
|
||||
insta::assert_snapshot!(read_file(&repo_path.join("file1")), @"0");
|
||||
|
||||
// Changes in the working copy are amended into the commit
|
||||
std::fs::write(repo_path.join("file2"), "0").unwrap();
|
||||
insta::assert_snapshot!(get_log_output(&test_env, &repo_path), @r###"
|
||||
Rebased 1 descendant commits onto updated working copy
|
||||
o 57e61f6b2ce1 open second
|
||||
@ f1b9706b17d0 closed first
|
||||
o 000000000000 closed (no description set)
|
||||
"###);
|
||||
}
|
||||
|
||||
fn read_file(path: &Path) -> String {
|
||||
String::from_utf8(std::fs::read(path).unwrap()).unwrap()
|
||||
}
|
||||
|
||||
fn get_log_output(test_env: &TestEnvironment, cwd: &Path) -> String {
|
||||
test_env.jj_cmd_success(
|
||||
cwd,
|
||||
&[
|
||||
"log",
|
||||
"-T",
|
||||
r#"commit_id.short() " " if(open, "open", "closed") " " description"#,
|
||||
],
|
||||
)
|
||||
}
|
Loading…
Reference in a new issue