Skip to content

Commit

Permalink
cli: ensure default remote branch is imported by "git clone"
Browse files Browse the repository at this point in the history
I originally thought this would be unavoidable, but I was wrong. "jj git clone"
doesn't implicitly create any local branch if git.auto-local-branch is off, and
that's fine because the detached HEAD state is normal in jj.

That being said, Git users would expect that the main/master branch exists.
Since importing the default branch is harmless, let's create and track it no
matter if git.auto-local-branch is off.
  • Loading branch information
yuja committed Dec 16, 2023
1 parent d4d3ea9 commit 3e5b108
Show file tree
Hide file tree
Showing 3 changed files with 64 additions and 8 deletions.
4 changes: 4 additions & 0 deletions cli/src/commands/git.rs
Original file line number Diff line number Diff line change
Expand Up @@ -507,6 +507,10 @@ fn cmd_git_clone(
.get_remote_branch(default_branch, remote_name);
if let Some(commit_id) = default_branch_remote_ref.target.as_normal().cloned() {
let mut checkout_tx = workspace_command.start_transaction();
// For convenience, create local branch as Git would do.
checkout_tx
.mut_repo()
.track_remote_branch(default_branch, remote_name);
if let Ok(commit) = checkout_tx.repo().store().get_commit(&commit_id) {
checkout_tx.check_out(&commit)?;
}
Expand Down
65 changes: 59 additions & 6 deletions cli/tests/test_git_clone.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
// See the License for the specific language governing permissions and
// limitations under the License.

use std::path::{self, PathBuf};
use std::path::{self, Path, PathBuf};

use crate::common::TestEnvironment;

Expand Down Expand Up @@ -213,11 +213,8 @@ fn test_git_clone_colocate() {
"###);

// The old default branch "master" shouldn't exist.
let stdout = test_env.jj_cmd_success(
&test_env.env_root().join("clone"),
&["branch", "list", "--all"],
);
insta::assert_snapshot!(stdout, @r###"
insta::assert_snapshot!(
get_branch_output(&test_env, &test_env.env_root().join("clone")), @r###"
main: mzyxwzks 9f01a0e0 message
@git: mzyxwzks 9f01a0e0 message
@origin: mzyxwzks 9f01a0e0 message
Expand Down Expand Up @@ -309,3 +306,59 @@ fn test_git_clone_colocate() {
Error: Destination path exists and is not an empty directory
"###);
}

#[test]
fn test_git_clone_remote_default_branch() {
let test_env = TestEnvironment::default();
let git_repo_path = test_env.env_root().join("source");
let git_repo = git2::Repository::init(git_repo_path).unwrap();
set_up_non_empty_git_repo(&git_repo);
// Create non-default branch in remote
let oid = git_repo
.find_reference("refs/heads/main")
.unwrap()
.target()
.unwrap();
git_repo
.reference("refs/heads/feature1", oid, false, "")
.unwrap();

// All fetched branches will be imported if auto-local-branch is on
test_env.add_config("git.auto-local-branch = true");
let (_stdout, stderr) =
test_env.jj_cmd_ok(test_env.env_root(), &["git", "clone", "source", "clone1"]);
insta::assert_snapshot!(stderr, @r###"
Fetching into new repo in "$TEST_ENV/clone1"
Working copy now at: sqpuoqvx cad212e1 (empty) (no description set)
Parent commit : mzyxwzks 9f01a0e0 feature1 main | message
Added 1 files, modified 0 files, removed 0 files
"###);
insta::assert_snapshot!(
get_branch_output(&test_env, &test_env.env_root().join("clone1")), @r###"
feature1: mzyxwzks 9f01a0e0 message
@origin: mzyxwzks 9f01a0e0 message
main: mzyxwzks 9f01a0e0 message
@origin: mzyxwzks 9f01a0e0 message
"###);

// Only the default branch will be imported if auto-local-branch is off
test_env.add_config("git.auto-local-branch = false");
let (_stdout, stderr) =
test_env.jj_cmd_ok(test_env.env_root(), &["git", "clone", "source", "clone2"]);
insta::assert_snapshot!(stderr, @r###"
Fetching into new repo in "$TEST_ENV/clone2"
Working copy now at: pmmvwywv fa729b1e (empty) (no description set)
Parent commit : mzyxwzks 9f01a0e0 feature1@origin main | message
Added 1 files, modified 0 files, removed 0 files
"###);
insta::assert_snapshot!(
get_branch_output(&test_env, &test_env.env_root().join("clone2")), @r###"
feature1@origin: mzyxwzks 9f01a0e0 message
main: mzyxwzks 9f01a0e0 message
@origin: mzyxwzks 9f01a0e0 message
"###);
}

fn get_branch_output(test_env: &TestEnvironment, repo_path: &Path) -> String {
test_env.jj_cmd_success(repo_path, &["branch", "list", "--all"])
}
3 changes: 1 addition & 2 deletions docs/design/tracking-branches.md
Original file line number Diff line number Diff line change
Expand Up @@ -222,8 +222,7 @@ In particular, a merge of local and remote targets is
* `jj git clone`
* Import, track, and merge per `git.auto_local_branch` config.
* The default branch will be tracked regardless of `git.auto_local_branch`
config. (Because local branch is created for the default remote branch,
it makes sense to track.)
config. This isn't technically needed, but will help users coming from Git.

### branch

Expand Down

0 comments on commit 3e5b108

Please sign in to comment.