forked from mirrors/jj
cli: unblock "jj git init --colocate" in existing Git repo directory
I'm not sure what's the conclusion in #2747, but I don't think there is a disagreement on allowing --colocate to import existing Git repo.
This commit is contained in:
parent
356037379a
commit
8d0414549b
9 changed files with 90 additions and 60 deletions
|
@ -66,6 +66,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
||||||
* When creating a new workspace, the sparse patterns are now copied over from
|
* When creating a new workspace, the sparse patterns are now copied over from
|
||||||
the current workspace.
|
the current workspace.
|
||||||
|
|
||||||
|
* `jj git init --colocate` can now import an existing Git repository. This is
|
||||||
|
equivalent to `jj git init --git-repo=.`.
|
||||||
|
|
||||||
* `jj git fetch` now automatically prints new remote branches and tags by default.
|
* `jj git fetch` now automatically prints new remote branches and tags by default.
|
||||||
|
|
||||||
* `--verbose/-v` is now `--debug` (no short option since it's not intended to be used often)
|
* `--verbose/-v` is now `--debug` (no short option since it's not intended to be used often)
|
||||||
|
|
|
@ -1829,7 +1829,7 @@ fn map_workspace_load_error(err: WorkspaceLoadError, workspace_path: Option<&str
|
||||||
message,
|
message,
|
||||||
"It looks like this is a git repo. You can create a jj repo backed by it by \
|
"It looks like this is a git repo. You can create a jj repo backed by it by \
|
||||||
running this:
|
running this:
|
||||||
jj git init --git-repo=.",
|
jj git init --colocate",
|
||||||
)
|
)
|
||||||
} else {
|
} else {
|
||||||
user_error(message)
|
user_error(message)
|
||||||
|
|
|
@ -125,12 +125,9 @@ pub struct GitInitArgs {
|
||||||
/// `git` repo, allowing the use of both `jj` and `git` commands
|
/// `git` repo, allowing the use of both `jj` and `git` commands
|
||||||
/// in the same directory.
|
/// in the same directory.
|
||||||
///
|
///
|
||||||
/// This is done by placing the backing git repo into a `.git` directory
|
/// This is done by placing the backing git repo into a `.git` directory in
|
||||||
/// in the root of the `jj` repo along with the `.jj` directory.
|
/// the root of the `jj` repo along with the `.jj` directory. If the `.git`
|
||||||
///
|
/// directory already exists, all the existing commits will be imported.
|
||||||
/// 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`.
|
/// This option is mutually exclusive with `--git-repo`.
|
||||||
#[arg(long, conflicts_with = "git_repo")]
|
#[arg(long, conflicts_with = "git_repo")]
|
||||||
|
@ -379,52 +376,67 @@ pub fn git_init(
|
||||||
colocate: bool,
|
colocate: bool,
|
||||||
git_repo: Option<&str>,
|
git_repo: Option<&str>,
|
||||||
) -> Result<(), CommandError> {
|
) -> Result<(), CommandError> {
|
||||||
if colocate {
|
#[derive(Clone, Debug)]
|
||||||
let (workspace, repo) = Workspace::init_colocated_git(command.settings(), workspace_root)?;
|
enum GitInitMode {
|
||||||
let workspace_command = command.for_loaded_repo(ui, workspace, repo)?;
|
Colocate,
|
||||||
maybe_add_gitignore(&workspace_command)?;
|
External(PathBuf),
|
||||||
return Ok(());
|
Internal,
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Some(git_store_str) = git_repo {
|
let colocated_git_repo_path = workspace_root.join(".git");
|
||||||
let git_store_path = command.cwd().join(git_store_str);
|
let init_mode = if colocate {
|
||||||
let (workspace, repo) =
|
if colocated_git_repo_path.exists() {
|
||||||
Workspace::init_external_git(command.settings(), workspace_root, &git_store_path)?;
|
GitInitMode::External(colocated_git_repo_path)
|
||||||
// Import refs first so all the reachable commits are indexed in
|
} else {
|
||||||
// chronological order.
|
GitInitMode::Colocate
|
||||||
let colocated = is_colocated_git_workspace(&workspace, &repo);
|
|
||||||
let repo = init_git_refs(ui, command, repo, colocated)?;
|
|
||||||
let mut workspace_command = command.for_loaded_repo(ui, workspace, repo)?;
|
|
||||||
maybe_add_gitignore(&workspace_command)?;
|
|
||||||
workspace_command.maybe_snapshot(ui)?;
|
|
||||||
if !workspace_command.working_copy_shared_with_git() {
|
|
||||||
let mut tx = workspace_command.start_transaction();
|
|
||||||
jj_lib::git::import_head(tx.mut_repo())?;
|
|
||||||
if let Some(git_head_id) = tx.mut_repo().view().git_head().as_normal().cloned() {
|
|
||||||
let git_head_commit = tx.mut_repo().store().get_commit(&git_head_id)?;
|
|
||||||
tx.check_out(&git_head_commit)?;
|
|
||||||
}
|
|
||||||
if tx.mut_repo().has_changes() {
|
|
||||||
tx.finish(ui, "import git head")?;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
print_trackable_remote_branches(ui, workspace_command.repo().view())?;
|
} else if let Some(path_str) = git_repo {
|
||||||
|
GitInitMode::External(command.cwd().join(path_str))
|
||||||
} else {
|
} else {
|
||||||
if workspace_root.join(".git").exists() {
|
if colocated_git_repo_path.exists() {
|
||||||
let cwd = command.cwd().canonicalize().unwrap();
|
|
||||||
let relative_wc_path = file_util::relative_path(&cwd, workspace_root);
|
|
||||||
return Err(user_error_with_hint(
|
return Err(user_error_with_hint(
|
||||||
"Did not create a jj repo because there is an existing Git repo in this directory.",
|
"Did not create a jj repo because there is an existing Git repo in this directory.",
|
||||||
format!(
|
"To create a repo backed by the existing Git repo, run `jj git init --colocate` \
|
||||||
r#"To create a repo backed by the existing Git repo, run `jj git init --git-repo={}` instead."#,
|
instead.",
|
||||||
relative_wc_path.display()
|
|
||||||
),
|
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
GitInitMode::Internal
|
||||||
|
};
|
||||||
|
|
||||||
Workspace::init_internal_git(command.settings(), workspace_root)?;
|
match &init_mode {
|
||||||
|
GitInitMode::Colocate => {
|
||||||
|
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)?;
|
||||||
|
}
|
||||||
|
GitInitMode::External(git_repo_path) => {
|
||||||
|
let (workspace, repo) =
|
||||||
|
Workspace::init_external_git(command.settings(), workspace_root, git_repo_path)?;
|
||||||
|
// Import refs first so all the reachable commits are indexed in
|
||||||
|
// chronological order.
|
||||||
|
let colocated = is_colocated_git_workspace(&workspace, &repo);
|
||||||
|
let repo = init_git_refs(ui, command, repo, colocated)?;
|
||||||
|
let mut workspace_command = command.for_loaded_repo(ui, workspace, repo)?;
|
||||||
|
maybe_add_gitignore(&workspace_command)?;
|
||||||
|
workspace_command.maybe_snapshot(ui)?;
|
||||||
|
if !workspace_command.working_copy_shared_with_git() {
|
||||||
|
let mut tx = workspace_command.start_transaction();
|
||||||
|
jj_lib::git::import_head(tx.mut_repo())?;
|
||||||
|
if let Some(git_head_id) = tx.mut_repo().view().git_head().as_normal().cloned() {
|
||||||
|
let git_head_commit = tx.mut_repo().store().get_commit(&git_head_id)?;
|
||||||
|
tx.check_out(&git_head_commit)?;
|
||||||
|
}
|
||||||
|
if tx.mut_repo().has_changes() {
|
||||||
|
tx.finish(ui, "import git head")?;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
print_trackable_remote_branches(ui, workspace_command.repo().view())?;
|
||||||
|
}
|
||||||
|
GitInitMode::Internal => {
|
||||||
|
Workspace::init_internal_git(command.settings(), workspace_root)?;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -542,12 +542,29 @@ fn test_git_init_colocated_via_flag_git_dir_exists() {
|
||||||
let workspace_root = test_env.env_root().join("repo");
|
let workspace_root = test_env.env_root().join("repo");
|
||||||
init_git_repo(&workspace_root, false);
|
init_git_repo(&workspace_root, false);
|
||||||
|
|
||||||
let stderr = test_env.jj_cmd_failure(&workspace_root, &["git", "init", "--colocate"]);
|
let (stdout, stderr) =
|
||||||
|
test_env.jj_cmd_ok(test_env.env_root(), &["git", "init", "--colocate", "repo"]);
|
||||||
|
insta::assert_snapshot!(stdout, @"");
|
||||||
insta::assert_snapshot!(stderr, @r###"
|
insta::assert_snapshot!(stderr, @r###"
|
||||||
Error: Failed to access the repository
|
Done importing changes from the underlying Git repo.
|
||||||
Caused by:
|
Initialized repo in "repo"
|
||||||
1: Failed to initialize git repository
|
"###);
|
||||||
2: Refusing to initialize the existing '$TEST_ENV/repo/.git' directory
|
|
||||||
|
// Check that the Git repo's HEAD got checked out
|
||||||
|
let stdout = test_env.jj_cmd_success(&workspace_root, &["log", "-r", "@-"]);
|
||||||
|
insta::assert_snapshot!(stdout, @r###"
|
||||||
|
◉ mwrttmos git.user@example.com 1970-01-01 01:02:03.000 +01:00 my-branch HEAD@git 8d698d4a
|
||||||
|
│ My commit message
|
||||||
|
~
|
||||||
|
"###);
|
||||||
|
|
||||||
|
// Check that the Git repo's HEAD moves
|
||||||
|
test_env.jj_cmd_ok(&workspace_root, &["new"]);
|
||||||
|
let stdout = test_env.jj_cmd_success(&workspace_root, &["log", "-r", "@-"]);
|
||||||
|
insta::assert_snapshot!(stdout, @r###"
|
||||||
|
◉ sqpuoqvx test.user@example.com 2001-02-03 04:05:07.000 +07:00 HEAD@git f61b77cd
|
||||||
|
│ (no description set)
|
||||||
|
~
|
||||||
"###);
|
"###);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -210,7 +210,7 @@ fn test_no_workspace_directory() {
|
||||||
insta::assert_snapshot!(stderr, @r###"
|
insta::assert_snapshot!(stderr, @r###"
|
||||||
Error: There is no jj repo in "."
|
Error: There is no jj repo in "."
|
||||||
Hint: It looks like this is a git repo. You can create a jj repo backed by it by running this:
|
Hint: It looks like this is a git repo. You can create a jj repo backed by it by running this:
|
||||||
jj git init --git-repo=.
|
jj git init --colocate
|
||||||
"###);
|
"###);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -497,7 +497,7 @@ fn test_init_git_internal_must_be_colocated() {
|
||||||
let stderr = test_env.jj_cmd_failure(&workspace_root, &["init", "--git"]);
|
let stderr = test_env.jj_cmd_failure(&workspace_root, &["init", "--git"]);
|
||||||
insta::assert_snapshot!(stderr, @r###"
|
insta::assert_snapshot!(stderr, @r###"
|
||||||
Error: Did not create a jj repo because there is an existing Git repo in this directory.
|
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 git init --git-repo=.` instead.
|
Hint: To create a repo backed by the existing Git repo, run `jj git init --colocate` instead.
|
||||||
"###);
|
"###);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -100,8 +100,7 @@ parent.
|
||||||
<tbody>
|
<tbody>
|
||||||
<tr>
|
<tr>
|
||||||
<td>Create a new repo</td>
|
<td>Create a new repo</td>
|
||||||
<td><code>jj init --git</code> (without <code>--git</code>, you get a
|
<td><code>jj git init [--colocate]</code></td>
|
||||||
native Jujutsu repo, which is slow and whose format will change)</td>
|
|
||||||
<td><code>git init</code></td>
|
<td><code>git init</code></td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
|
|
|
@ -96,8 +96,8 @@ into a directory by the same name.
|
||||||
## Co-located Jujutsu/Git repos
|
## Co-located Jujutsu/Git repos
|
||||||
|
|
||||||
A "co-located" Jujutsu repo is a hybrid Jujutsu/Git repo. These can be created
|
A "co-located" Jujutsu repo is a hybrid Jujutsu/Git repo. These can be created
|
||||||
if you initialize the Jujutsu repo in an existing Git repo by running `jj init
|
if you initialize the Jujutsu repo in an existing Git repo by running `jj git
|
||||||
--git-repo=.` or with `jj git clone --colocate`. The Git repo and the Jujutsu
|
init --colocate` or with `jj git clone --colocate`. The Git repo and the Jujutsu
|
||||||
repo then share the same working copy. Jujutsu will import and export from and
|
repo then share the same working copy. Jujutsu will import and export from and
|
||||||
to the Git repo on every `jj` command automatically.
|
to the Git repo on every `jj` command automatically.
|
||||||
|
|
||||||
|
|
|
@ -66,12 +66,11 @@ changes.
|
||||||
|
|
||||||
## Working in a Git co-located repository
|
## Working in a Git co-located repository
|
||||||
|
|
||||||
After doing `jj init --git-repo=.`, Git will be in
|
After doing `jj git init --colocate`, Git will be in a [detached HEAD
|
||||||
a [detached HEAD state][detached], which is unusual, as Git mainly works with
|
state][detached], which is unusual, as Git mainly works with branches. In a
|
||||||
branches. In a co-located repository, every `jj` command will automatically
|
co-located repository, every `jj` command will automatically synchronize
|
||||||
synchronize Jujutsu's view of the repo with Git's view. For example,
|
Jujutsu's view of the repo with Git's view. For example, `jj commit` updates the
|
||||||
`jj commit` updates the HEAD of the Git repository, enabling an incremental
|
HEAD of the Git repository, enabling an incremental migration.
|
||||||
migration.
|
|
||||||
|
|
||||||
```shell
|
```shell
|
||||||
$ nvim docs/tutorial.md
|
$ nvim docs/tutorial.md
|
||||||
|
|
Loading…
Reference in a new issue