From 8b5ff208742f8286bfe259dcee0ec589359d3951 Mon Sep 17 00:00:00 2001 From: Yuya Nishihara Date: Sat, 12 Aug 2023 15:51:37 +0900 Subject: [PATCH] cli: discard "unborn" default branch before checking out fetched head AFAIK, we can't make HEAD detached in an empty Git repository, so we need to temporarily switch to the new default branch before checking out. Fixes #2047 --- cli/src/commands/git.rs | 12 +++++++++--- cli/tests/test_git_clone.rs | 6 ++++++ 2 files changed, 15 insertions(+), 3 deletions(-) diff --git a/cli/src/commands/git.rs b/cli/src/commands/git.rs index 0550ec513..fdf4ec382 100644 --- a/cli/src/commands/git.rs +++ b/cli/src/commands/git.rs @@ -441,7 +441,7 @@ fn cmd_git_clone( } } - if let (mut workspace_command, Some(default_branch)) = clone_result? { + if let (mut workspace_command, git_repo, Some(default_branch)) = clone_result? { let default_branch_target = workspace_command .repo() .view() @@ -449,6 +449,12 @@ fn cmd_git_clone( if let Some(commit_id) = default_branch_target.as_normal().cloned() { let mut checkout_tx = workspace_command.start_transaction("check out git remote's default branch"); + if args.colocate { + // HEAD can't be detached without specifying a commit, so we set the new default + // branch instead. Otherwise, the old "unborn" default branch would move + // alongside the new default branch. + git_repo.set_head(&format!("refs/heads/{default_branch}"))?; + } if let Ok(commit) = checkout_tx.repo().store().get_commit(&commit_id) { checkout_tx.check_out(&commit)?; } @@ -464,7 +470,7 @@ fn do_git_clone( colocate: bool, source: &str, wc_path: &Path, -) -> Result<(WorkspaceCommandHelper, Option), CommandError> { +) -> Result<(WorkspaceCommandHelper, git2::Repository, Option), CommandError> { let (workspace, repo) = if colocate { let git_repo = git2::Repository::init(wc_path)?; Workspace::init_external_git(command.settings(), wc_path, git_repo.path())? @@ -499,7 +505,7 @@ fn do_git_clone( } })?; fetch_tx.finish(ui)?; - Ok((workspace_command, maybe_default_branch)) + Ok((workspace_command, git_repo, maybe_default_branch)) } fn with_remote_callbacks(ui: &mut Ui, f: impl FnOnce(git::RemoteCallbacks<'_>) -> T) -> T { diff --git a/cli/tests/test_git_clone.rs b/cli/tests/test_git_clone.rs index 32a0e1c40..b233ee778 100644 --- a/cli/tests/test_git_clone.rs +++ b/cli/tests/test_git_clone.rs @@ -198,6 +198,12 @@ fn test_git_clone_colocate() { .symbolic_target() ); + // The old default branch "master" shouldn't exist. + let stdout = test_env.jj_cmd_success(&test_env.env_root().join("clone"), &["branch", "list"]); + insta::assert_snapshot!(stdout, @r###" + main: mzyxwzks 9f01a0e0 message + "###); + // Subsequent fetch should just work even if the source path was relative let stdout = test_env.jj_cmd_success(&test_env.env_root().join("clone"), &["git", "fetch"]); insta::assert_snapshot!(stdout, @r###"