forked from mirrors/jj
workspace: workspace forget
multiple names at once
Summary: This allows `workspace forget` to forget multiple workspaces in a single action; it now behaves more consistently with other verbs like `abandon` which can take multiple revisions at one time. There's some hoop-jumping involved to ensure the oplog transaction description looks nice, but as they say: small conveniences cost a lot. Signed-off-by: Austin Seipp <aseipp@pobox.com> Change-Id: Id91da269f87b145010c870b7dc043748
This commit is contained in:
parent
ec1015943a
commit
220292ad84
3 changed files with 94 additions and 18 deletions
|
@ -28,6 +28,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
||||||
|
|
||||||
* `jj workspace add` now takes a `--revision` argument.
|
* `jj workspace add` now takes a `--revision` argument.
|
||||||
|
|
||||||
|
* `jj workspace forget` can now forget multiple workspaces at once.
|
||||||
|
|
||||||
### Fixed bugs
|
### Fixed bugs
|
||||||
|
|
||||||
* Updating the working copy to a commit where a file that's currently ignored
|
* Updating the working copy to a commit where a file that's currently ignored
|
||||||
|
|
|
@ -1063,8 +1063,9 @@ struct WorkspaceAddArgs {
|
||||||
/// before or after running this command.
|
/// before or after running this command.
|
||||||
#[derive(clap::Args, Clone, Debug)]
|
#[derive(clap::Args, Clone, Debug)]
|
||||||
struct WorkspaceForgetArgs {
|
struct WorkspaceForgetArgs {
|
||||||
/// Name of the workspace to forget (the current workspace by default)
|
/// Names of the workspaces to forget. By default, forgets only the current
|
||||||
workspace: Option<String>,
|
/// workspace.
|
||||||
|
workspaces: Vec<String>,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// List workspaces
|
/// List workspaces
|
||||||
|
@ -3812,24 +3813,47 @@ fn cmd_workspace_forget(
|
||||||
args: &WorkspaceForgetArgs,
|
args: &WorkspaceForgetArgs,
|
||||||
) -> Result<(), CommandError> {
|
) -> Result<(), CommandError> {
|
||||||
let mut workspace_command = command.workspace_helper(ui)?;
|
let mut workspace_command = command.workspace_helper(ui)?;
|
||||||
|
let len = args.workspaces.len();
|
||||||
|
|
||||||
let workspace_id = if let Some(workspace_str) = &args.workspace {
|
let mut wss = Vec::new();
|
||||||
WorkspaceId::new(workspace_str.to_string())
|
let description = match len {
|
||||||
} else {
|
// NOTE (aseipp): if there's only 1-or-0 arguments, shortcut. this is
|
||||||
workspace_command.workspace_id().to_owned()
|
// mostly so the oplog description can look good: it removes the need,
|
||||||
|
// in the case of more-than-1 argument, to handle pluralization of the
|
||||||
|
// nouns in the description
|
||||||
|
0 | 1 => {
|
||||||
|
let ws = match len == 0 {
|
||||||
|
true => workspace_command.workspace_id().to_owned(),
|
||||||
|
false => WorkspaceId::new(args.workspaces[0].to_string()),
|
||||||
};
|
};
|
||||||
|
wss.push(ws.clone());
|
||||||
|
format!("forget workspace {}", ws.as_str())
|
||||||
|
}
|
||||||
|
_ => {
|
||||||
|
args.workspaces
|
||||||
|
.iter()
|
||||||
|
.map(|ws| WorkspaceId::new(ws.to_string()))
|
||||||
|
.for_each(|ws| wss.push(ws));
|
||||||
|
|
||||||
|
format!("forget workspaces {}", args.workspaces.join(", "))
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
for ws in &wss {
|
||||||
if workspace_command
|
if workspace_command
|
||||||
.repo()
|
.repo()
|
||||||
.view()
|
.view()
|
||||||
.get_wc_commit_id(&workspace_id)
|
.get_wc_commit_id(ws)
|
||||||
.is_none()
|
.is_none()
|
||||||
{
|
{
|
||||||
return Err(user_error("No such workspace"));
|
return Err(user_error(format!("No such workspace: {}", ws.as_str())));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut tx =
|
// bundle every workspace forget into a single transaction, so that e.g.
|
||||||
workspace_command.start_transaction(&format!("forget workspace {}", workspace_id.as_str()));
|
// undo correctly restores all of them at once.
|
||||||
tx.mut_repo().remove_wc_commit(&workspace_id);
|
let mut tx = workspace_command.start_transaction(&description);
|
||||||
|
wss.iter().for_each(|ws| tx.mut_repo().remove_wc_commit(ws));
|
||||||
tx.finish(ui)?;
|
tx.finish(ui)?;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
|
@ -400,8 +400,11 @@ fn test_workspaces_forget() {
|
||||||
Error: Workspace already exists
|
Error: Workspace already exists
|
||||||
"###);
|
"###);
|
||||||
|
|
||||||
// Forget the secondary workspace
|
// Add a third workspace...
|
||||||
let (stdout, stderr) = test_env.jj_cmd_ok(&main_path, &["workspace", "forget", "secondary"]);
|
test_env.jj_cmd_ok(&main_path, &["workspace", "add", "../third"]);
|
||||||
|
// ... and then forget it, and the secondary workspace too
|
||||||
|
let (stdout, stderr) =
|
||||||
|
test_env.jj_cmd_ok(&main_path, &["workspace", "forget", "secondary", "third"]);
|
||||||
insta::assert_snapshot!(stdout, @"");
|
insta::assert_snapshot!(stdout, @"");
|
||||||
insta::assert_snapshot!(stderr, @"");
|
insta::assert_snapshot!(stderr, @"");
|
||||||
// No workspaces left
|
// No workspaces left
|
||||||
|
@ -409,6 +412,53 @@ fn test_workspaces_forget() {
|
||||||
insta::assert_snapshot!(stdout, @"");
|
insta::assert_snapshot!(stdout, @"");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_workspaces_forget_multi_transaction() {
|
||||||
|
let test_env = TestEnvironment::default();
|
||||||
|
test_env.jj_cmd_ok(test_env.env_root(), &["init", "--git", "main"]);
|
||||||
|
let main_path = test_env.env_root().join("main");
|
||||||
|
|
||||||
|
std::fs::write(main_path.join("file"), "contents").unwrap();
|
||||||
|
test_env.jj_cmd_ok(&main_path, &["new"]);
|
||||||
|
|
||||||
|
test_env.jj_cmd_ok(&main_path, &["workspace", "add", "../second"]);
|
||||||
|
test_env.jj_cmd_ok(&main_path, &["workspace", "add", "../third"]);
|
||||||
|
|
||||||
|
// there should be three workspaces
|
||||||
|
let stdout = test_env.jj_cmd_success(&main_path, &["workspace", "list"]);
|
||||||
|
insta::assert_snapshot!(stdout, @r###"
|
||||||
|
default: rlvkpnrz e949be04 (empty) (no description set)
|
||||||
|
second: pmmvwywv feda1c4e (empty) (no description set)
|
||||||
|
third: rzvqmyuk 485853ed (empty) (no description set)
|
||||||
|
"###);
|
||||||
|
|
||||||
|
// delete two at once, in a single tx
|
||||||
|
test_env.jj_cmd_ok(&main_path, &["workspace", "forget", "second", "third"]);
|
||||||
|
let stdout = test_env.jj_cmd_success(&main_path, &["workspace", "list"]);
|
||||||
|
insta::assert_snapshot!(stdout, @r###"
|
||||||
|
default: rlvkpnrz e949be04 (empty) (no description set)
|
||||||
|
"###);
|
||||||
|
|
||||||
|
// the op log should have multiple workspaces forgotten in a single tx
|
||||||
|
let stdout = test_env.jj_cmd_success(&main_path, &["op", "log", "--limit", "1"]);
|
||||||
|
insta::assert_snapshot!(stdout, @r###"
|
||||||
|
@ e18f223ba3d3 test-username@host.example.com 2001-02-03 04:05:12.000 +07:00 - 2001-02-03 04:05:12.000 +07:00
|
||||||
|
│ forget workspaces second, third
|
||||||
|
│ args: jj workspace forget second third
|
||||||
|
"###);
|
||||||
|
|
||||||
|
// now, undo, and that should restore both workspaces
|
||||||
|
test_env.jj_cmd_ok(&main_path, &["op", "undo"]);
|
||||||
|
|
||||||
|
// finally, there should be three workspaces at the end
|
||||||
|
let stdout = test_env.jj_cmd_success(&main_path, &["workspace", "list"]);
|
||||||
|
insta::assert_snapshot!(stdout, @r###"
|
||||||
|
default: rlvkpnrz e949be04 (empty) (no description set)
|
||||||
|
second: pmmvwywv feda1c4e (empty) (no description set)
|
||||||
|
third: rzvqmyuk 485853ed (empty) (no description set)
|
||||||
|
"###);
|
||||||
|
}
|
||||||
|
|
||||||
/// Test context of commit summary template
|
/// Test context of commit summary template
|
||||||
#[test]
|
#[test]
|
||||||
fn test_list_workspaces_template() {
|
fn test_list_workspaces_template() {
|
||||||
|
|
Loading…
Reference in a new issue