Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

cli: Add a jj git init command. #2932

Merged
merged 1 commit into from
Feb 5, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,12 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
**Deadline**: `jj checkout` and `jj merge` will be deleted and are expected
become a **hard error later in 2024**.

* `jj init --git` and `jj init --git-repo` are now deprecated and will be removed
in the near future.

Use `jj git init` instead.


### Breaking changes

* (Minor) Diff summaries (e.g. `jj diff -s`) now use `D` for "Deleted" instead
Expand Down Expand Up @@ -75,6 +81,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

* `jj diff` no longer shows the contents of binary files.

* `jj git` now has an `init` command that initializes a git backed repo.


### Fixed bugs

* Fixed snapshots of symlinks in `gitignore`-d directory.
Expand Down
84 changes: 83 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`
#[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` directory
/// in the root of the `jj` repo along with the `.jj` directory.
///
/// 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.
essiene marked this conversation as resolved.
Show resolved Hide resolved
///
/// 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,19 @@ 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 {
let (workspace, repo) = Workspace::init_colocated_git(command.settings(), workspace_root)?;
let workspace_command = command.for_loaded_repo(ui, workspace, repo)?;
maybe_add_gitignore(&workspace_command)?;
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 +401,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 +413,35 @@ 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(),
)?;

let relative_wc_path = file_util::relative_path(&cwd, &wc_path);
writeln!(
ui.stderr(),
r#"Initialized repo in "{}""#,
relative_wc_path.display()
)?;

Ok(())
}

#[tracing::instrument(skip(ui, command))]
fn cmd_git_fetch(
ui: &mut Ui,
Expand Down Expand Up @@ -1106,6 +1187,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
18 changes: 14 additions & 4 deletions cli/src/commands/init.rs
Original file line number Diff line number Diff line change
Expand Up @@ -33,11 +33,13 @@ 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)]
#[arg(long, hide = true)]
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)]
#[arg(long, hide = true, 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 colocated repo.
let colocated = 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, colocated, args.git_repo.as_deref())?;
writeln!(
ui.warning(),
"warning: `--git` and `--git-repo` are deprecated.
Use `jj git init` instead"
)?;
} 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
30 changes: 27 additions & 3 deletions cli/tests/[email protected]
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,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 @@ -746,6 +747,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 @@ -817,6 +819,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 @@ -909,7 +933,7 @@ Create a new repo in the given directory
If the given directory does not exist, it will be created. If no directory is given, the current directory is used.
**Usage:** `jj init [OPTIONS] [DESTINATION]`
**Usage:** `jj init [DESTINATION]`
###### **Arguments:**
Expand All @@ -919,11 +943,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
Loading