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
|
||||
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.
|
||||
|
||||
* `--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,
|
||||
"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",
|
||||
)
|
||||
} else {
|
||||
user_error(message)
|
||||
|
|
|
@ -125,12 +125,9 @@ pub struct GitInitArgs {
|
|||
/// `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 is done by placing the backing git repo into a `.git` directory in
|
||||
/// 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 mutually exclusive with `--git-repo`.
|
||||
#[arg(long, conflicts_with = "git_repo")]
|
||||
|
@ -379,17 +376,43 @@ pub fn git_init(
|
|||
colocate: bool,
|
||||
git_repo: Option<&str>,
|
||||
) -> Result<(), CommandError> {
|
||||
if 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)?;
|
||||
return Ok(());
|
||||
#[derive(Clone, Debug)]
|
||||
enum GitInitMode {
|
||||
Colocate,
|
||||
External(PathBuf),
|
||||
Internal,
|
||||
}
|
||||
|
||||
if let Some(git_store_str) = git_repo {
|
||||
let git_store_path = command.cwd().join(git_store_str);
|
||||
let colocated_git_repo_path = workspace_root.join(".git");
|
||||
let init_mode = if colocate {
|
||||
if colocated_git_repo_path.exists() {
|
||||
GitInitMode::External(colocated_git_repo_path)
|
||||
} else {
|
||||
GitInitMode::Colocate
|
||||
}
|
||||
} else if let Some(path_str) = git_repo {
|
||||
GitInitMode::External(command.cwd().join(path_str))
|
||||
} else {
|
||||
if colocated_git_repo_path.exists() {
|
||||
return Err(user_error_with_hint(
|
||||
"Did not create a jj repo because there is an existing Git repo in this directory.",
|
||||
"To create a repo backed by the existing Git repo, run `jj git init --colocate` \
|
||||
instead.",
|
||||
));
|
||||
}
|
||||
GitInitMode::Internal
|
||||
};
|
||||
|
||||
match &init_mode {
|
||||
GitInitMode::Colocate => {
|
||||
let (workspace, repo) =
|
||||
Workspace::init_external_git(command.settings(), workspace_root, &git_store_path)?;
|
||||
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);
|
||||
|
@ -409,22 +432,11 @@ pub fn git_init(
|
|||
}
|
||||
}
|
||||
print_trackable_remote_branches(ui, workspace_command.repo().view())?;
|
||||
} else {
|
||||
if workspace_root.join(".git").exists() {
|
||||
let cwd = command.cwd().canonicalize().unwrap();
|
||||
let relative_wc_path = file_util::relative_path(&cwd, workspace_root);
|
||||
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 git init --git-repo={}` instead."#,
|
||||
relative_wc_path.display()
|
||||
),
|
||||
));
|
||||
}
|
||||
|
||||
GitInitMode::Internal => {
|
||||
Workspace::init_internal_git(command.settings(), workspace_root)?;
|
||||
}
|
||||
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
|
|
|
@ -542,12 +542,29 @@ fn test_git_init_colocated_via_flag_git_dir_exists() {
|
|||
let workspace_root = test_env.env_root().join("repo");
|
||||
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###"
|
||||
Error: Failed to access the repository
|
||||
Caused by:
|
||||
1: Failed to initialize git repository
|
||||
2: Refusing to initialize the existing '$TEST_ENV/repo/.git' directory
|
||||
Done importing changes from the underlying Git repo.
|
||||
Initialized repo in "repo"
|
||||
"###);
|
||||
|
||||
// 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###"
|
||||
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:
|
||||
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"]);
|
||||
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 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>
|
||||
<tr>
|
||||
<td>Create a new repo</td>
|
||||
<td><code>jj init --git</code> (without <code>--git</code>, you get a
|
||||
native Jujutsu repo, which is slow and whose format will change)</td>
|
||||
<td><code>jj git init [--colocate]</code></td>
|
||||
<td><code>git init</code></td>
|
||||
</tr>
|
||||
<tr>
|
||||
|
|
|
@ -96,8 +96,8 @@ into a directory by the same name.
|
|||
## Co-located Jujutsu/Git repos
|
||||
|
||||
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
|
||||
--git-repo=.` or with `jj git clone --colocate`. The Git repo and the Jujutsu
|
||||
if you initialize the Jujutsu repo in an existing Git repo by running `jj git
|
||||
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
|
||||
to the Git repo on every `jj` command automatically.
|
||||
|
||||
|
|
|
@ -66,12 +66,11 @@ changes.
|
|||
|
||||
## Working in a Git co-located repository
|
||||
|
||||
After doing `jj init --git-repo=.`, Git will be in
|
||||
a [detached HEAD state][detached], which is unusual, as Git mainly works with
|
||||
branches. In a co-located repository, every `jj` command will automatically
|
||||
synchronize Jujutsu's view of the repo with Git's view. For example,
|
||||
`jj commit` updates the HEAD of the Git repository, enabling an incremental
|
||||
migration.
|
||||
After doing `jj git init --colocate`, Git will be in a [detached HEAD
|
||||
state][detached], which is unusual, as Git mainly works with branches. In a
|
||||
co-located repository, every `jj` command will automatically synchronize
|
||||
Jujutsu's view of the repo with Git's view. For example, `jj commit` updates the
|
||||
HEAD of the Git repository, enabling an incremental migration.
|
||||
|
||||
```shell
|
||||
$ nvim docs/tutorial.md
|
||||
|
|
Loading…
Reference in a new issue