git init: add revset alias for trunk() when intializing with existing git repository

This commit is contained in:
Benjamin Tan 2024-05-31 18:56:05 +08:00
parent d8899e1ae7
commit 0c0e001262
5 changed files with 100 additions and 7 deletions

View file

@ -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

View file

@ -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<ReadonlyRepo>,
) -> 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(())
}

View file

@ -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();

View file

@ -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:

View file

@ -62,7 +62,7 @@ impl fmt::Display for RefName {
}
}
fn parse_git_ref(ref_name: &str) -> Option<RefName> {
pub fn parse_git_ref(ref_name: &str) -> Option<RefName> {
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()))