diff --git a/CHANGELOG.md b/CHANGELOG.md index 37dea3f08d..92c727aa2a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -19,6 +19,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 * `jj branch rename` will now warn if the renamed branch has a remote branch, since those will have to be manually renamed outside of `jj`. +* `jj workspace root` was renamed to `jj root`, for ease of discoverability (while + keeping the previous name for compatibility) + ### Fixed bugs diff --git a/cli/src/commands/mod.rs b/cli/src/commands/mod.rs index 31a443a68b..7403566594 100644 --- a/cli/src/commands/mod.rs +++ b/cli/src/commands/mod.rs @@ -43,6 +43,7 @@ mod prev; mod rebase; mod resolve; mod restore; +mod root; mod run; mod show; mod sparse; @@ -118,6 +119,7 @@ enum Command { help_template = "Not a real subcommand; consider `jj backout` or `jj restore`" )] Revert(DummyCommandArgs), + Root(root::RootArgs), #[command(hide = true)] // TODO: Flesh out. Run(run::RunArgs), @@ -179,6 +181,7 @@ pub fn run_command(ui: &mut Ui, command_helper: &CommandHelper) -> Result<(), Co Command::Unsquash(sub_args) => unsquash::cmd_unsquash(ui, command_helper, sub_args), Command::Restore(sub_args) => restore::cmd_restore(ui, command_helper, sub_args), Command::Revert(_args) => revert(), + Command::Root(sub_args) => root::cmd_root(ui, command_helper, sub_args), Command::Run(sub_args) => run::cmd_run(ui, command_helper, sub_args), Command::Diffedit(sub_args) => diffedit::cmd_diffedit(ui, command_helper, sub_args), Command::Split(sub_args) => split::cmd_split(ui, command_helper, sub_args), diff --git a/cli/src/commands/root.rs b/cli/src/commands/root.rs new file mode 100644 index 0000000000..99f0ca8e37 --- /dev/null +++ b/cli/src/commands/root.rs @@ -0,0 +1,40 @@ +// Copyright 2024 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 std::io::Write; + +use tracing::instrument; + +use crate::cli_util::{user_error, CommandError, CommandHelper}; +use crate::ui::Ui; + +/// Show the root directory of the current working copy +#[derive(clap::Args, Clone, Debug)] +#[command(visible_aliases = &["qworkspace", "qroot"])] +pub(crate) struct RootArgs {} + +#[instrument(skip_all)] +pub(crate) fn cmd_root( + ui: &mut Ui, + command: &CommandHelper, + RootArgs {}: &RootArgs, +) -> Result<(), CommandError> { + let workspace_command = command.workspace_helper(ui)?; + let root = workspace_command + .workspace_root() + .to_str() + .ok_or_else(|| user_error("The workspace root is not valid UTF-8"))?; + writeln!(ui.stdout(), "{root}")?; + Ok(()) +} diff --git a/cli/src/commands/workspace.rs b/cli/src/commands/workspace.rs index cd413a300c..00762ab782 100644 --- a/cli/src/commands/workspace.rs +++ b/cli/src/commands/workspace.rs @@ -30,6 +30,7 @@ use crate::cli_util::{ self, check_stale_working_copy, print_checkout_stats, user_error, CommandError, CommandHelper, RevisionArg, WorkspaceCommandHelper, }; +use crate::commands::root; use crate::ui::Ui; /// Commands for working with workspaces @@ -46,7 +47,8 @@ pub(crate) enum WorkspaceCommand { Add(WorkspaceAddArgs), Forget(WorkspaceForgetArgs), List(WorkspaceListArgs), - Root(WorkspaceRootArgs), + #[command(hide = true)] // favor jj root + Root(root::RootArgs), UpdateStale(WorkspaceUpdateStaleArgs), } @@ -92,10 +94,6 @@ pub(crate) struct WorkspaceForgetArgs { #[derive(clap::Args, Clone, Debug)] pub(crate) struct WorkspaceListArgs {} -/// Show the current workspace root directory -#[derive(clap::Args, Clone, Debug)] -pub(crate) struct WorkspaceRootArgs {} - /// Update a workspace that has become stale /// /// For information about stale working copies, see @@ -113,7 +111,7 @@ pub(crate) fn cmd_workspace( WorkspaceCommand::Add(args) => cmd_workspace_add(ui, command, args), WorkspaceCommand::Forget(args) => cmd_workspace_forget(ui, command, args), WorkspaceCommand::List(args) => cmd_workspace_list(ui, command, args), - WorkspaceCommand::Root(args) => cmd_workspace_root(ui, command, args), + WorkspaceCommand::Root(args) => root::cmd_root(ui, command, args), WorkspaceCommand::UpdateStale(args) => cmd_workspace_update_stale(ui, command, args), } } @@ -263,21 +261,6 @@ fn cmd_workspace_list( Ok(()) } -#[instrument(skip_all)] -fn cmd_workspace_root( - ui: &mut Ui, - command: &CommandHelper, - _args: &WorkspaceRootArgs, -) -> Result<(), CommandError> { - let workspace_command = command.workspace_helper(ui)?; - let root = workspace_command - .workspace_root() - .to_str() - .ok_or_else(|| user_error("The workspace root is not valid UTF-8"))?; - writeln!(ui.stdout(), "{root}")?; - Ok(()) -} - #[instrument(skip_all)] fn cmd_workspace_update_stale( ui: &mut Ui, diff --git a/cli/tests/test_root.rs b/cli/tests/test_root.rs new file mode 100644 index 0000000000..6870eae240 --- /dev/null +++ b/cli/tests/test_root.rs @@ -0,0 +1,44 @@ +// Copyright 2024 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 std::path::Path; + +use test_case::test_case; +use testutils::{TestRepoBackend, TestWorkspace}; + +use crate::common::TestEnvironment; + +pub mod common; + +#[test_case(TestRepoBackend::Local ; "local backend")] +#[test_case(TestRepoBackend::Git ; "git backend")] +fn test_root(backend: TestRepoBackend) { + let test_env = TestEnvironment::default(); + let settings = testutils::user_settings(); + let test_workspace = TestWorkspace::init_with_backend(&settings, backend); + let root = test_workspace.workspace.workspace_root(); + let subdir = root.join("subdir"); + std::fs::create_dir(&subdir).unwrap(); + let stdout = test_env.jj_cmd_success(&subdir, &["root"]); + assert_eq!(&stdout, &[root.to_str().unwrap(), "\n"].concat()); +} + +#[test] +fn test_root_outside_a_repo() { + let test_env = TestEnvironment::default(); + let stdout = test_env.jj_cmd_failure(Path::new("/"), &["root"]); + insta::assert_snapshot!(stdout, @r###" + Error: There is no jj repo in "." + "###); +} diff --git a/cli/tests/test_workspaces.rs b/cli/tests/test_workspaces.rs index 423f9343ab..67a52ae7d2 100644 --- a/cli/tests/test_workspaces.rs +++ b/cli/tests/test_workspaces.rs @@ -603,13 +603,13 @@ fn test_workspaces_root() { let main_path = test_env.env_root().join("main"); let secondary_path = test_env.env_root().join("secondary"); - let stdout = test_env.jj_cmd_success(&main_path, &["workspace", "root"]); + let stdout = test_env.jj_cmd_success(&main_path, &["root"]); insta::assert_snapshot!(stdout, @r###" $TEST_ENV/main "###); let main_subdir_path = main_path.join("subdir"); std::fs::create_dir(&main_subdir_path).unwrap(); - let stdout = test_env.jj_cmd_success(&main_subdir_path, &["workspace", "root"]); + let stdout = test_env.jj_cmd_success(&main_subdir_path, &["root"]); insta::assert_snapshot!(stdout, @r###" $TEST_ENV/main "###); @@ -618,13 +618,13 @@ fn test_workspaces_root() { &main_path, &["workspace", "add", "--name", "secondary", "../secondary"], ); - let stdout = test_env.jj_cmd_success(&secondary_path, &["workspace", "root"]); + let stdout = test_env.jj_cmd_success(&secondary_path, &["root"]); insta::assert_snapshot!(stdout, @r###" $TEST_ENV/secondary "###); let secondary_subdir_path = secondary_path.join("subdir"); std::fs::create_dir(&secondary_subdir_path).unwrap(); - let stdout = test_env.jj_cmd_success(&secondary_subdir_path, &["workspace", "root"]); + let stdout = test_env.jj_cmd_success(&secondary_subdir_path, &["root"]); insta::assert_snapshot!(stdout, @r###" $TEST_ENV/secondary "###);