just playing with tangled
0
fork

Configure Feed

Select the types of activity you want to include in your feed.

cli: git: add `git root` command

This was discussed briefly in [1], and makes it easier to execute
scripts which require a reference to the Git directory.

[1]: https://github.com/jj-vcs/jj/discussions/5767#discussioncomment-12480764

+165
+3
CHANGELOG.md
··· 78 78 adds the `--destination`, `--insert-after`, and `--insert-before` options to 79 79 customize the location of reverted commits. 80 80 81 + * A new command `jj git root` is added, which prints the location of the Git 82 + directory of a repository using the Git backend. 83 + 81 84 ### Fixed bugs 82 85 83 86 * `jj log -p --stat` now shows diff stats as well as the default color-words/git
+5
cli/src/commands/git/mod.rs
··· 19 19 mod init; 20 20 mod push; 21 21 mod remote; 22 + mod root; 22 23 23 24 use std::path::Path; 24 25 ··· 44 45 use self::push::GitPushArgs; 45 46 use self::remote::cmd_git_remote; 46 47 use self::remote::RemoteCommand; 48 + use self::root::cmd_git_root; 49 + use self::root::GitRootArgs; 47 50 use crate::cli_util::CommandHelper; 48 51 use crate::cli_util::WorkspaceCommandHelper; 49 52 use crate::command_error::user_error_with_message; ··· 69 72 Push(GitPushArgs), 70 73 #[command(subcommand)] 71 74 Remote(RemoteCommand), 75 + Root(GitRootArgs), 72 76 } 73 77 74 78 pub fn cmd_git( ··· 84 88 GitCommand::Init(args) => cmd_git_init(ui, command, args), 85 89 GitCommand::Push(args) => cmd_git_push(ui, command, args), 86 90 GitCommand::Remote(args) => cmd_git_remote(ui, command, args), 91 + GitCommand::Root(args) => cmd_git_root(ui, command, args), 87 92 } 88 93 } 89 94
+44
cli/src/commands/git/root.rs
··· 1 + // Copyright 2025 The Jujutsu Authors 2 + // 3 + // Licensed under the Apache License, Version 2.0 (the "License"); 4 + // you may not use this file except in compliance with the License. 5 + // You may obtain a copy of the License at 6 + // 7 + // https://www.apache.org/licenses/LICENSE-2.0 8 + // 9 + // Unless required by applicable law or agreed to in writing, software 10 + // distributed under the License is distributed on an "AS IS" BASIS, 11 + // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 + // See the License for the specific language governing permissions and 13 + // limitations under the License. 14 + 15 + use std::io::Write as _; 16 + 17 + use jj_lib::repo::Repo as _; 18 + use tracing::instrument; 19 + 20 + use crate::cli_util::CommandHelper; 21 + use crate::command_error::user_error; 22 + use crate::command_error::CommandError; 23 + use crate::ui::Ui; 24 + 25 + /// Show the underlying Git directory of a repository using the Git backend 26 + #[derive(clap::Args, Clone, Debug)] 27 + pub struct GitRootArgs {} 28 + 29 + #[instrument(skip_all)] 30 + pub fn cmd_git_root( 31 + ui: &mut Ui, 32 + command: &CommandHelper, 33 + _args: &GitRootArgs, 34 + ) -> Result<(), CommandError> { 35 + let workspace_command = command.workspace_helper(ui)?; 36 + let store = workspace_command.repo().store(); 37 + let git_backend = jj_lib::git::get_git_backend(store)?; 38 + let root = git_backend 39 + .git_repo_path() 40 + .to_str() 41 + .ok_or_else(|| user_error("The workspace root is not valid UTF-8"))?; 42 + writeln!(ui.stdout(), "{root}")?; 43 + Ok(()) 44 + }
+10
cli/tests/cli-reference@.md.snap
··· 58 58 * [`jj git remote remove`↴](#jj-git-remote-remove) 59 59 * [`jj git remote rename`↴](#jj-git-remote-rename) 60 60 * [`jj git remote set-url`↴](#jj-git-remote-set-url) 61 + * [`jj git root`↴](#jj-git-root) 61 62 * [`jj help`↴](#jj-help) 62 63 * [`jj interdiff`↴](#jj-interdiff) 63 64 * [`jj log`↴](#jj-log) ··· 1108 1109 * `init` — Create a new Git backed repo 1109 1110 * `push` — Push to a Git remote 1110 1111 * `remote` — Manage Git remotes 1112 + * `root` — Show the underlying Git directory of a repository using the Git backend 1111 1113 1112 1114 1113 1115 ··· 1339 1341 * `<URL>` — The desired URL or path for `remote` 1340 1342 1341 1343 Local path will be resolved to absolute form. 1344 + 1345 + 1346 + 1347 + ## `jj git root` 1348 + 1349 + Show the underlying Git directory of a repository using the Git backend 1350 + 1351 + **Usage:** `jj git root` 1342 1352 1343 1353 1344 1354
+1
cli/tests/runner.rs
··· 45 45 mod test_git_private_commits; 46 46 mod test_git_push; 47 47 mod test_git_remotes; 48 + mod test_git_root; 48 49 mod test_gitignores; 49 50 mod test_global_opts; 50 51 mod test_help_command;
+102
cli/tests/test_git_root.rs
··· 1 + // Copyright 2025 The Jujutsu Authors 2 + // 3 + // Licensed under the Apache License, Version 2.0 (the "License"); 4 + // you may not use this file except in compliance with the License. 5 + // You may obtain a copy of the License at 6 + // 7 + // https://www.apache.org/licenses/LICENSE-2.0 8 + // 9 + // Unless required by applicable law or agreed to in writing, software 10 + // distributed under the License is distributed on an "AS IS" BASIS, 11 + // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 + // See the License for the specific language governing permissions and 13 + // limitations under the License. 14 + 15 + use testutils::git; 16 + 17 + use crate::common::TestEnvironment; 18 + 19 + #[test] 20 + fn test_git_root_git_backend_noncolocated() { 21 + let test_env = TestEnvironment::default(); 22 + test_env.run_jj_in(".", ["git", "init", "repo"]).success(); 23 + let work_dir = test_env.work_dir("repo"); 24 + 25 + let output = work_dir.run_jj(["git", "root"]); 26 + insta::assert_snapshot!(output, @r#" 27 + $TEST_ENV/repo/.jj/repo/store/git 28 + [EOF] 29 + "#); 30 + } 31 + 32 + #[test] 33 + fn test_git_root_git_backend_colocated() { 34 + let test_env = TestEnvironment::default(); 35 + test_env 36 + .run_jj_in(".", ["git", "init", "--colocate", "repo"]) 37 + .success(); 38 + let work_dir = test_env.work_dir("repo"); 39 + 40 + let output = work_dir.run_jj(["git", "root"]); 41 + insta::assert_snapshot!(output, @r#" 42 + $TEST_ENV/repo/.git 43 + [EOF] 44 + "#); 45 + } 46 + 47 + #[test] 48 + fn test_git_root_git_backend_external_git_dir() { 49 + let test_env = TestEnvironment::default(); 50 + let work_dir = test_env.work_dir("").create_dir("repo"); 51 + let git_repo_work_dir = test_env.work_dir("git-repo"); 52 + let git_repo = git::init(git_repo_work_dir.root()); 53 + 54 + // Create an initial commit in Git 55 + let tree_id = git::add_commit( 56 + &git_repo, 57 + "refs/heads/master", 58 + "file", 59 + b"contents", 60 + "initial", 61 + &[], 62 + ) 63 + .tree_id; 64 + git::checkout_tree_index(&git_repo, tree_id); 65 + assert_eq!(git_repo_work_dir.read_file("file"), b"contents"); 66 + insta::assert_snapshot!( 67 + git_repo.head_id().unwrap().to_string(), 68 + @"97358f54806c7cd005ed5ade68a779595efbae7e" 69 + ); 70 + 71 + work_dir 72 + .run_jj([ 73 + "git", 74 + "init", 75 + "--git-repo", 76 + git_repo_work_dir.root().to_str().unwrap(), 77 + ]) 78 + .success(); 79 + 80 + let output = work_dir.run_jj(["git", "root"]); 81 + insta::assert_snapshot!(output, @r#" 82 + $TEST_ENV/git-repo/.git 83 + [EOF] 84 + "#); 85 + } 86 + 87 + #[test] 88 + fn test_git_root_simple_backend() { 89 + let test_env = TestEnvironment::default(); 90 + test_env 91 + .run_jj_in(".", ["debug", "init-simple", "repo"]) 92 + .success(); 93 + let work_dir = test_env.work_dir("repo"); 94 + 95 + let output = work_dir.run_jj(["git", "root"]); 96 + insta::assert_snapshot!(output, @r#" 97 + ------- stderr ------- 98 + Error: The repo is not backed by a Git repo 99 + [EOF] 100 + [exit status: 1] 101 + "#); 102 + }