Skip to content

Commit

Permalink
cli: add a jj git init command.
Browse files Browse the repository at this point in the history
This initializes a git backed repo.

* It does the same thing as `jj init --git` except that it
  has a --colocated flag to explicitly specify that we want
  the .git repo to be side-by-side the .jj repo in the working
  directory.
* `jj init --git` will keep the current behaviour and will not
  be able to create colocated git backed repos.
* Update test snapshots.
  • Loading branch information
essiene committed Feb 3, 2024
1 parent bcdb9be commit c86b522
Show file tree
Hide file tree
Showing 5 changed files with 137 additions and 7 deletions.
5 changes: 5 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,11 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

* `jj workspace root` was aliased to `jj root`, for ease of discoverability

* `jj git` now has an `init` command that initializes a git backed repo.
`jj init --git` and `jj init --git-repo` are now deprecated and
will be removed in the near future.


### Fixed bugs

* Fixed snapshots of symlinks in `gitignore`-d directory.
Expand Down
73 changes: 72 additions & 1 deletion cli/src/commands/git.rs
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,7 @@ use crate::ui::Ui;
pub enum GitCommand {
#[command(subcommand)]
Remote(GitRemoteCommand),
Init(GitInitArgs),
Fetch(GitFetchArgs),
Clone(GitCloneArgs),
Push(GitPushArgs),
Expand Down Expand Up @@ -107,6 +108,49 @@ pub struct GitRemoteRenameArgs {
#[derive(clap::Args, Clone, Debug)]
pub struct GitRemoteListArgs {}

/// Create a new Git backed repo.
#[derive(clap::Args, Clone, Debug)]
pub struct GitInitArgs {
/// The destination directory where the `jj` repo will be created.
/// If the directory does not exist, it will be created.
/// If no directory is diven, the current directory is used.
///
/// By default the `git` repo is under `$destination/.jj/repo/store/git`
#[arg(default_value = ".", value_hint = clap::ValueHint::DirPath)]
destination: String,

/// Specifies that the `jj` repo should also be a valid
/// `git` repo, allowing the use of both `jj` and `git` commands
/// in the same directory.
///
/// This is done by placing the backing git repo into a `.git` folder
/// in the root of the Jujutsu repo along with the `.jj` folder.
///
/// This option is only valid when creating new repos. To
/// reuse an existing `.git` directory in an existing git
/// repo, see the `--git-repo` param below.
///
/// This option is mutually exclusive with `--git-repo`.
#[arg(long, conflicts_with = "git_repo")]
colocated: bool,

/// Specifies a path to an **existing** git repository to be
/// used as the backing git repo for the newly created `jj` repo.
///
/// If the specified `--git-repo` path happens to be the same as
/// the `jj` repo path (both .jj and .git directories are in the
/// same working directory), then both `jj` and `git` commands
/// will work on the repo, with the exception that changes from `jj`
/// will not be auto-exported to the git repo.
///
/// Auto-exporting from `jj` to `git` is only enabled for new repos.
/// See `--colocated` above.
///
/// This option is mutually exclusive with `--colocated`.
#[arg(long, conflicts_with = "colocated", value_hint = clap::ValueHint::DirPath)]
git_repo: Option<String>,
}

/// Fetch from a Git remote
#[derive(clap::Args, Clone, Debug)]
pub struct GitFetchArgs {
Expand Down Expand Up @@ -317,11 +361,17 @@ pub fn git_init(
ui: &mut Ui,
command: &CommandHelper,
workspace_root: &Path,
colocated: bool,
git_repo: Option<&str>,
) -> Result<(), CommandError> {
let cwd = command.cwd().canonicalize().unwrap();
let relative_wc_path = file_util::relative_path(&cwd, workspace_root);

if colocated {
Workspace::init_colocated_git(command.settings(), workspace_root)?;
return Ok(());
}

if let Some(git_store_str) = git_repo {
let git_store_path = cwd.join(git_store_str);
let (workspace, repo) =
Expand Down Expand Up @@ -349,7 +399,7 @@ pub fn git_init(
return Err(user_error_with_hint(
"Did not create a jj repo because there is an existing Git repo in this directory.",
format!(
r#"To create a repo backed by the existing Git repo, run `jj init --git-repo={}` instead."#,
r#"To create a repo backed by the existing Git repo, run `jj git init --git-repo={}` instead."#,
relative_wc_path.display()
),
));
Expand All @@ -361,6 +411,26 @@ pub fn git_init(
Ok(())
}

fn cmd_git_init(
ui: &mut Ui,
command: &CommandHelper,
args: &GitInitArgs,
) -> Result<(), CommandError> {
let cwd = command.cwd().canonicalize().unwrap();
let wc_path = cwd.join(&args.destination);
let wc_path = file_util::create_or_reuse_dir(&wc_path)
.and_then(|_| wc_path.canonicalize())
.map_err(|e| user_error_with_message("Failed to create workspace", e))?;

git_init(
ui,
command,
&wc_path,
args.colocated,
args.git_repo.as_deref(),
)
}

#[tracing::instrument(skip(ui, command))]
fn cmd_git_fetch(
ui: &mut Ui,
Expand Down Expand Up @@ -1097,6 +1167,7 @@ pub fn cmd_git(
subcommand: &GitCommand,
) -> Result<(), CommandError> {
match subcommand {
GitCommand::Init(args) => cmd_git_init(ui, command, args),
GitCommand::Fetch(args) => cmd_git_fetch(ui, command, args),
GitCommand::Clone(args) => cmd_git_clone(ui, command, args),
GitCommand::Remote(GitRemoteCommand::Add(args)) => cmd_git_remote_add(ui, command, args),
Expand Down
14 changes: 12 additions & 2 deletions cli/src/commands/init.rs
Original file line number Diff line number Diff line change
Expand Up @@ -33,9 +33,11 @@ pub(crate) struct InitArgs {
/// The destination directory
#[arg(default_value = ".", value_hint = clap::ValueHint::DirPath)]
destination: String,
/// DEPRECATED: Use `jj git init`
/// Use the Git backend, creating a jj repo backed by a Git repo
#[arg(long)]
git: bool,
/// DEPRECATED: Use `jj git init`
/// Path to a git repo the jj repo will be backed by
#[arg(long, value_hint = clap::ValueHint::DirPath)]
git_repo: Option<String>,
Expand All @@ -53,13 +55,21 @@ pub(crate) fn cmd_init(
.and_then(|_| wc_path.canonicalize())
.map_err(|e| user_error_with_message("Failed to create workspace", e))?;

// Preserve existing behaviour where `jj init` is not able to create
// a collocated repo.
let collocated = false;
if args.git || args.git_repo.is_some() {
git::git_init(ui, command, &wc_path, args.git_repo.as_deref())?;
git::git_init(ui, command, &wc_path, collocated, args.git_repo.as_deref())?;
writeln!(
ui.warning(),
"warning: `--git` and `--git-repo` are deprecated.
Use `jj git init` to create git repos"
)?;
} else {
if !command.settings().allow_native_backend() {
return Err(user_error_with_hint(
"The native backend is disallowed by default.",
"Did you mean to pass `--git`?
"Did you mean to call `jj git init`?
Set `ui.allow-init-native` to allow initializing a repo with the native backend.",
));
}
Expand Down
28 changes: 26 additions & 2 deletions cli/tests/[email protected]
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ This document contains the help content for the `jj` command-line program.
* [`jj git remote remove`↴](#jj-git-remote-remove)
* [`jj git remote rename`↴](#jj-git-remote-rename)
* [`jj git remote list`↴](#jj-git-remote-list)
* [`jj git init`↴](#jj-git-init)
* [`jj git fetch`↴](#jj-git-fetch)
* [`jj git clone`↴](#jj-git-clone)
* [`jj git push`↴](#jj-git-push)
Expand Down Expand Up @@ -764,6 +765,7 @@ For a comparison with Git, including a table of commands, see https://github.com
###### **Subcommands:**
* `remote` — Manage Git remotes
* `init` — Create a new Git backed repo
* `fetch` — Fetch from a Git remote
* `clone` — Create a new repo backed by a clone of a Git repo
* `push` — Push to a Git remote
Expand Down Expand Up @@ -835,6 +837,28 @@ List Git remotes
## `jj git init`
Create a new Git backed repo
**Usage:** `jj git init [OPTIONS] [DESTINATION]`
###### **Arguments:**
* `<DESTINATION>` — The destination directory where the `jj` repo will be created. If the directory does not exist, it will be created. If no directory is diven, the current directory is used
Default value: `.`
###### **Options:**
* `--colocated` — Specifies that the `jj` repo should also be a valid `git` repo, allowing the use of both `jj` and `git` commands in the same directory
Possible values: `true`, `false`
* `--git-repo <GIT_REPO>` — Specifies a path to an **existing** git repository to be used as the backing git repo for the newly created `jj` repo
## `jj git fetch`
Fetch from a Git remote
Expand Down Expand Up @@ -937,11 +961,11 @@ If the given directory does not exist, it will be created. If no directory is gi
###### **Options:**
* `--git` — Use the Git backend, creating a jj repo backed by a Git repo
* `--git` — DEPRECATED: Use `jj git init` Use the Git backend, creating a jj repo backed by a Git repo
Possible values: `true`, `false`
* `--git-repo <GIT_REPO>` — Path to a git repo the jj repo will be backed by
* `--git-repo <GIT_REPO>` — DEPRECATED: Use `jj git init` Path to a git repo the jj repo will be backed by
Expand Down
24 changes: 22 additions & 2 deletions cli/tests/test_init_command.rs
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,8 @@ fn test_init_git_internal() {
let (stdout, stderr) = test_env.jj_cmd_ok(test_env.env_root(), &["init", "repo", "--git"]);
insta::assert_snapshot!(stdout, @"");
insta::assert_snapshot!(stderr, @r###"
warning: `--git` and `--git-repo` are deprecated.
Use `jj git init` to create git repos
Initialized repo in "repo"
"###);

Expand Down Expand Up @@ -113,6 +115,8 @@ fn test_init_git_external(bare: bool) {
Working copy now at: sqpuoqvx f6950fc1 (empty) (no description set)
Parent commit : mwrttmos 8d698d4a my-branch | My commit message
Added 1 files, modified 0 files, removed 0 files
warning: `--git` and `--git-repo` are deprecated.
Use `jj git init` to create git repos
Initialized repo in "repo"
"###);
}
Expand Down Expand Up @@ -183,6 +187,8 @@ fn test_init_git_colocated() {
insta::assert_snapshot!(stdout, @"");
insta::assert_snapshot!(stderr, @r###"
Done importing changes from the underlying Git repo.
warning: `--git` and `--git-repo` are deprecated.
Use `jj git init` to create git repos
Initialized repo in "."
"###);

Expand Down Expand Up @@ -231,6 +237,8 @@ fn test_init_git_colocated_gitlink() {
insta::assert_snapshot!(stdout, @"");
insta::assert_snapshot!(stderr, @r###"
Done importing changes from the underlying Git repo.
warning: `--git` and `--git-repo` are deprecated.
Use `jj git init` to create git repos
Initialized repo in "."
"###);
insta::assert_snapshot!(read_git_target(&workspace_root), @"../../../.git");
Expand Down Expand Up @@ -267,6 +275,8 @@ fn test_init_git_colocated_symlink_directory() {
insta::assert_snapshot!(stdout, @"");
insta::assert_snapshot!(stderr, @r###"
Done importing changes from the underlying Git repo.
warning: `--git` and `--git-repo` are deprecated.
Use `jj git init` to create git repos
Initialized repo in "."
"###);
insta::assert_snapshot!(read_git_target(&workspace_root), @"../../../.git");
Expand Down Expand Up @@ -306,6 +316,8 @@ fn test_init_git_colocated_symlink_directory_without_bare_config() {
insta::assert_snapshot!(stdout, @"");
insta::assert_snapshot!(stderr, @r###"
Done importing changes from the underlying Git repo.
warning: `--git` and `--git-repo` are deprecated.
Use `jj git init` to create git repos
Initialized repo in "."
"###);
insta::assert_snapshot!(read_git_target(&workspace_root), @"../../../.git");
Expand Down Expand Up @@ -347,6 +359,8 @@ fn test_init_git_colocated_symlink_gitlink() {
insta::assert_snapshot!(stdout, @"");
insta::assert_snapshot!(stderr, @r###"
Done importing changes from the underlying Git repo.
warning: `--git` and `--git-repo` are deprecated.
Use `jj git init` to create git repos
Initialized repo in "."
"###);
insta::assert_snapshot!(read_git_target(&workspace_root), @"../../../.git");
Expand Down Expand Up @@ -407,6 +421,8 @@ fn test_init_git_colocated_imported_refs() {
let (_stdout, stderr) = test_env.jj_cmd_ok(&local_path, &["init", "--git-repo=."]);
insta::assert_snapshot!(stderr, @r###"
Done importing changes from the underlying Git repo.
warning: `--git` and `--git-repo` are deprecated.
Use `jj git init` to create git repos
Initialized repo in "."
"###);
insta::assert_snapshot!(get_branch_output(&test_env, &local_path), @r###"
Expand All @@ -428,6 +444,8 @@ fn test_init_git_colocated_imported_refs() {
The following remote branches aren't associated with the existing local branches:
local-remote@origin
Hint: Run `jj branch track local-remote@origin` to keep local branches updated on future pulls.
warning: `--git` and `--git-repo` are deprecated.
Use `jj git init` to create git repos
Initialized repo in "."
"###);
insta::assert_snapshot!(get_branch_output(&test_env, &local_path), @r###"
Expand All @@ -451,6 +469,8 @@ fn test_init_git_external_but_git_dir_exists() {
);
insta::assert_snapshot!(stdout, @"");
insta::assert_snapshot!(stderr, @r###"
warning: `--git` and `--git-repo` are deprecated.
Use `jj git init` to create git repos
Initialized repo in "."
"###);

Expand Down Expand Up @@ -479,7 +499,7 @@ fn test_init_git_internal_must_be_colocated() {
let stderr = test_env.jj_cmd_failure(&workspace_root, &["init", "--git"]);
insta::assert_snapshot!(stderr, @r###"
Error: Did not create a jj repo because there is an existing Git repo in this directory.
Hint: To create a repo backed by the existing Git repo, run `jj init --git-repo=.` instead.
Hint: To create a repo backed by the existing Git repo, run `jj git init --git-repo=.` instead.
"###);
}

Expand All @@ -497,7 +517,7 @@ fn test_init_local_disallowed() {
let stdout = test_env.jj_cmd_failure(test_env.env_root(), &["init", "repo"]);
insta::assert_snapshot!(stdout, @r###"
Error: The native backend is disallowed by default.
Hint: Did you mean to pass `--git`?
Hint: Did you mean to call `jj git init`?
Set `ui.allow-init-native` to allow initializing a repo with the native backend.
"###);
}
Expand Down

0 comments on commit c86b522

Please sign in to comment.