diff --git a/CHANGELOG.md b/CHANGELOG.md index a93fd4ff0f..2307f6bb91 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -20,8 +20,9 @@ to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). individually instead of being passed a directory by setting `merge-tools.$TOOL.diff-invocation-mode="file-by-file"` in config.toml. -* `jj git clone` adds the default branch of the remote as repository - settings for `revset-aliases."trunk()"`.` +* `jj git clone` and `jj git init` with an existing git repository adds the + default branch of the remote as repository settings for + `revset-aliases."trunk()"`.` ### Fixed bugs diff --git a/cli/src/commands/git/init.rs b/cli/src/commands/git/init.rs index 2d0257e1b3..45c71ea06d 100644 --- a/cli/src/commands/git/init.rs +++ b/cli/src/commands/git/init.rs @@ -16,6 +16,7 @@ use std::io::Write; use std::path::{Path, PathBuf}; use std::sync::Arc; +use jj_lib::git::{parse_git_ref, RefName}; use jj_lib::repo::{ReadonlyRepo, Repo}; use jj_lib::workspace::Workspace; use jj_lib::{file_util, git}; @@ -23,8 +24,9 @@ use jj_lib::{file_util, git}; use crate::cli_util::{print_trackable_remote_branches, start_repo_transaction, CommandHelper}; use crate::command_error::{user_error_with_hint, user_error_with_message, CommandError}; use crate::commands::git::maybe_add_gitignore; +use crate::config::{write_config_value_to_file, ConfigNamePathBuf}; use crate::git_util::{ - is_colocated_git_workspace, print_failed_git_export, print_git_import_stats, + get_git_repo, is_colocated_git_workspace, print_failed_git_export, print_git_import_stats, }; use crate::ui::Ui; @@ -152,6 +154,7 @@ pub fn do_init( let mut workspace_command = command.for_loaded_repo(ui, workspace, repo)?; maybe_add_gitignore(&workspace_command)?; workspace_command.maybe_snapshot(ui)?; + maybe_set_repository_level_trunk_alias(ui, workspace_command.repo())?; if !workspace_command.working_copy_shared_with_git() { let mut tx = workspace_command.start_transaction(); jj_lib::git::import_head(tx.mut_repo())?; @@ -210,3 +213,33 @@ fn init_git_refs( )?; Ok(repo) } + +// Set repository level `trunk()` alias to the default branch for "origin". +pub fn maybe_set_repository_level_trunk_alias( + ui: &Ui, + repo: &Arc, +) -> Result<(), CommandError> { + let git_repo = get_git_repo(repo.store())?; + if let Ok(reference) = git_repo.find_reference("refs/remotes/origin/HEAD") { + if let Some(reference_name) = reference.symbolic_target() { + if let Some(RefName::RemoteBranch { + branch: default_branch, + .. + }) = parse_git_ref(reference_name) + { + let config_path = repo.repo_path().join("config.toml"); + write_config_value_to_file( + &ConfigNamePathBuf::from_iter(["revset-aliases", "trunk()"]), + &format!("{default_branch}@origin"), + &config_path, + )?; + writeln!( + ui.status(), + "Setting the revset alias \"trunk()\" to \"{default_branch}@origin\"", + )?; + } + }; + }; + + Ok(()) +} diff --git a/cli/tests/test_git_init.rs b/cli/tests/test_git_init.rs index 20f2217305..ee84fb55f0 100644 --- a/cli/tests/test_git_init.rs +++ b/cli/tests/test_git_init.rs @@ -148,6 +148,65 @@ fn test_git_init_external(bare: bool) { } } +#[test_case(false; "full")] +#[test_case(true; "bare")] +fn test_git_init_external_import_trunk(bare: bool) { + let test_env = TestEnvironment::default(); + let git_repo_path = test_env.env_root().join("git-repo"); + let git_repo = init_git_repo(&git_repo_path, bare); + + // Add remote branch "trunk" for remote "origin", and set it as "origin/HEAD" + let oid = git_repo + .find_reference("refs/heads/my-branch") + .unwrap() + .target() + .unwrap(); + git_repo + .reference("refs/remotes/origin/trunk", oid, false, "") + .unwrap(); + git_repo + .reference_symbolic( + "refs/remotes/origin/HEAD", + "refs/remotes/origin/trunk", + false, + "", + ) + .unwrap(); + + let (stdout, stderr) = test_env.jj_cmd_ok( + test_env.env_root(), + &[ + "git", + "init", + "repo", + "--git-repo", + git_repo_path.to_str().unwrap(), + ], + ); + insta::allow_duplicates! { + insta::assert_snapshot!(stdout, @""); + insta::assert_snapshot!(stderr, @r###" + Done importing changes from the underlying Git repo. + Setting the revset alias "trunk()" to "trunk@origin" + Working copy now at: sqpuoqvx f6950fc1 (empty) (no description set) + Parent commit : mwrttmos 8d698d4a my-branch trunk@origin | My commit message + Added 1 files, modified 0 files, removed 0 files + Initialized repo in "repo" + "###); + } + + // "trunk()" alias should be set to remote "origin"'s default branch "trunk" + let stdout = test_env.jj_cmd_success( + &test_env.env_root().join("repo"), + &["config", "list", "--repo", "revset-aliases.\"trunk()\""], + ); + insta::allow_duplicates! { + insta::assert_snapshot!(stdout, @r###" + revset-aliases."trunk()" = "trunk@origin" + "###); + } +} + #[test] fn test_git_init_external_non_existent_directory() { let test_env = TestEnvironment::default(); diff --git a/docs/revsets.md b/docs/revsets.md index 7d4f58fbca..e72d0f5818 100644 --- a/docs/revsets.md +++ b/docs/revsets.md @@ -362,9 +362,9 @@ for a comprehensive list. tried. If more than one potential trunk commit exists, the newest one is chosen. If none of the branches exist, the revset evaluates to `root()`. - When working with an existing Git repository (via `jj git clone`), - `trunk()` will be overridden at the repository level to the default branch of - the remote. + When working with an existing Git repository (via `jj git clone` or + `jj git init`), `trunk()` will be overridden at the repository level + to the default branch of the remote `origin`. You can [override](./config.md) this as appropriate. If you do, make sure it always resolves to exactly one commit. For example: diff --git a/lib/src/git.rs b/lib/src/git.rs index 3086d1c34b..666ea23194 100644 --- a/lib/src/git.rs +++ b/lib/src/git.rs @@ -62,7 +62,7 @@ impl fmt::Display for RefName { } } -fn parse_git_ref(ref_name: &str) -> Option { +pub fn parse_git_ref(ref_name: &str) -> Option { if let Some(branch_name) = ref_name.strip_prefix("refs/heads/") { // Git CLI says 'HEAD' is not a valid branch name (branch_name != "HEAD").then(|| RefName::LocalBranch(branch_name.to_string()))