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 5, 2024
1 parent 52f4fb1 commit dc8ac02
Show file tree
Hide file tree
Showing 6 changed files with 663 additions and 10 deletions.
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.
///
/// 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

0 comments on commit dc8ac02

Please sign in to comment.