From ccbea9efe2f72500d4de7b9558b9c2a1ef5acc75 Mon Sep 17 00:00:00 2001 From: Valentin Gatien-Baron Date: Wed, 10 Jan 2024 07:54:09 -0500 Subject: [PATCH] cli: implement jj root This is a convenient command when scripting around, for things like: $ cd $(jj root) && do something Currently this fails if the path to the repo contains non-utf8 bytes, similarly to `jj status` failing on non-utf8 paths inside the repo. --- CHANGELOG.md | 2 ++ cli/src/commands/mod.rs | 3 +++ cli/src/commands/root.rs | 41 ++++++++++++++++++++++++++++++++++++++++ cli/tests/test_root.rs | 36 +++++++++++++++++++++++++++++++++++ 4 files changed, 82 insertions(+) create mode 100644 cli/src/commands/root.rs create mode 100644 cli/tests/test_root.rs diff --git a/CHANGELOG.md b/CHANGELOG.md index 37dea3f08d..ce268822c9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -19,6 +19,8 @@ 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 root` will print the path to the working copy root. + ### 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..2378395fe4 --- /dev/null +++ b/cli/src/commands/root.rs @@ -0,0 +1,41 @@ +// 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; + +#[derive(clap::Args, Clone, Debug)] +pub(crate) struct RootArgs {} + +#[instrument(skip_all)] +pub(crate) fn cmd_root( + ui: &mut Ui, + command: &CommandHelper, + RootArgs {}: &RootArgs, +) -> Result<(), CommandError> { + let workspace_root = command.workspace_loader()?.workspace_root(); + if let Some(p) = workspace_root.to_str() { + writeln!(ui.stdout(), "{}", p)?; + Ok(()) + } else { + Err(user_error(format!( + "root path {:?} is not valid UTF-8", + workspace_root + ))) + } +} diff --git a/cli/tests/test_root.rs b/cli/tests/test_root.rs new file mode 100644 index 0000000000..66e3678e4b --- /dev/null +++ b/cli/tests/test_root.rs @@ -0,0 +1,36 @@ +// 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 crate::common::TestEnvironment; + +pub mod common; + +#[test] +fn test_root() { + let test_env = TestEnvironment::default(); + test_env.jj_cmd_ok(test_env.env_root(), &["init", ".", "--git"]); + let subdir = test_env.env_root().join("subdir"); + std::fs::create_dir(&subdir).unwrap(); + let stdout = test_env.jj_cmd_success(&subdir, &["root"]); + assert_eq!( + &stdout, + &[&*test_env.env_root().to_string_lossy(), "\n"].concat() + ); + let stdout = test_env.jj_cmd_failure(Path::new("/"), &["root"]); + insta::assert_snapshot!(stdout, @r###" + Error: There is no jj repo in "." + "###); +}