From 2848341632055f1c585bd2b30c8fa26828cb076f Mon Sep 17 00:00:00 2001 From: Essien Ita Essien Date: Mon, 8 Jan 2024 10:41:07 +0000 Subject: [PATCH] no-op: Refactor init command in preparation to move git related portions. * Not intended to add/remove or modify existing functionality. Only moving code around. * Add Workspace::create_workspace_root() which is needed by all init functionality regardless of the backend. * Move canonicalization of the external git repo path into the Workspace::init_git_external(). This keeps necessary code together. * Create a git_init() function in cli/src/commands/init.rs where all git related work is done. This function will be moved to cli/src/commands/git.rs in a subsequent PR. * Add a new variant of WorkspaceInitError for reporting path not found errors. The user error string is written to pass existing tests. --- cli/src/cli_util.rs | 3 + cli/src/commands/init.rs | 123 ++++++++++++++++++++------------------- lib/src/workspace.rs | 26 ++++++++- 3 files changed, 92 insertions(+), 60 deletions(-) diff --git a/cli/src/cli_util.rs b/cli/src/cli_util.rs index da2ee3fd74..33784402c9 100644 --- a/cli/src/cli_util.rs +++ b/cli/src/cli_util.rs @@ -191,6 +191,9 @@ impl From for CommandError { WorkspaceInitError::Path(err) => { CommandError::InternalError(format!("Failed to access the repository: {err}")) } + WorkspaceInitError::PathNotFound(path) => { + user_error(format!("{} doesn't exist", path.display())) + } WorkspaceInitError::Backend(err) => { user_error(format!("Failed to access the repository: {err}")) } diff --git a/cli/src/commands/init.rs b/cli/src/commands/init.rs index b261903c24..1f317e8e30 100644 --- a/cli/src/commands/init.rs +++ b/cli/src/commands/init.rs @@ -12,8 +12,9 @@ // See the License for the specific language governing permissions and // limitations under the License. +use std::io; use std::io::Write; -use std::{fs, io}; +use std::path::Path; use clap::ArgGroup; use itertools::Itertools as _; @@ -53,64 +54,11 @@ pub(crate) fn cmd_init( command: &CommandHelper, args: &InitArgs, ) -> Result<(), CommandError> { - let wc_path = command.cwd().join(&args.destination); - match fs::create_dir(&wc_path) { - Ok(()) => {} - Err(_) if wc_path.is_dir() => {} - Err(e) => return Err(user_error(format!("Failed to create workspace: {e}"))), - } - let wc_path = wc_path - .canonicalize() - .map_err(|e| user_error(format!("Failed to create workspace: {e}")))?; // raced? - let cwd = command.cwd().canonicalize().unwrap(); - let relative_wc_path = file_util::relative_path(&cwd, &wc_path); - - if let Some(git_store_str) = &args.git_repo { - let mut git_store_path = command.cwd().join(git_store_str); - git_store_path = git_store_path - .canonicalize() - .map_err(|_| user_error(format!("{} doesn't exist", git_store_path.display())))?; - if !git_store_path.ends_with(".git") { - git_store_path.push(".git"); - // Undo if .git doesn't exist - likely a bare repo. - if !git_store_path.exists() { - git_store_path.pop(); - } - } - let (workspace, repo) = - Workspace::init_external_git(command.settings(), &wc_path, &git_store_path)?; - let mut workspace_command = command.for_loaded_repo(ui, workspace, repo)?; - git::maybe_add_gitignore(&workspace_command)?; - workspace_command.snapshot(ui)?; - if !workspace_command.working_copy_shared_with_git() { - let mut tx = workspace_command.start_transaction(); - let stats = jj_lib::git::import_some_refs( - tx.mut_repo(), - &command.settings().git_settings(), - |ref_name| !jj_lib::git::is_reserved_git_remote_ref(ref_name), - )?; - print_git_import_stats(ui, &stats)?; - 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 refs")?; - } - } - print_trackable_remote_branches(ui, workspace_command.repo().view())?; - } else if args.git { - if wc_path.join(".git").exists() { - 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."#, - relative_wc_path.display() - ), - )); - } + let wc_path = Workspace::create_workspace_root(&command.cwd().join(&args.destination)) + .map_err(|e| user_error(format!("Failed to create workspace: {e}")))?; - Workspace::init_internal_git(command.settings(), &wc_path)?; + if args.git || args.git_repo.is_some() { + git_init(ui, command, &wc_path, args.git, &args.git_repo)?; } else { if !command.settings().allow_native_backend() { return Err(user_error_with_hint( @@ -120,8 +68,10 @@ Set `ui.allow-init-native` to allow initializing a repo with the native backend. )); } Workspace::init_local(command.settings(), &wc_path)?; - }; + } + let cwd = command.cwd().canonicalize().unwrap(); + let relative_wc_path = file_util::relative_path(&cwd, &wc_path); writeln!( ui.stderr(), "Initialized repo in \"{}\"", @@ -163,3 +113,58 @@ fn print_trackable_remote_branches(ui: &Ui, view: &View) -> io::Result<()> { )?; Ok(()) } + +// TODO(essiene): Move to cli/src/commands/git.rs for `jj git init` +fn git_init( + ui: &mut Ui, + command: &CommandHelper, + workspace_root: &Path, + use_git_backend: bool, + git_repo: &Option, +) -> Result<(), CommandError> { + let cwd = command.cwd().canonicalize().unwrap(); + let relative_wc_path = file_util::relative_path(&cwd, workspace_root); + + if git_repo.is_none() { + if use_git_backend { + if workspace_root.join(".git").exists() { + 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."#, + relative_wc_path.display() + ), + )); + } + + Workspace::init_internal_git(command.settings(), workspace_root)?; + } + } else if let Some(git_store_str) = &git_repo { + let git_store_path = command.cwd().join(git_store_str); + let (workspace, repo) = + Workspace::init_external_git(command.settings(), workspace_root, &git_store_path)?; + let mut workspace_command = command.for_loaded_repo(ui, workspace, repo)?; + git::maybe_add_gitignore(&workspace_command)?; + workspace_command.snapshot(ui)?; + if !workspace_command.working_copy_shared_with_git() { + let mut tx = workspace_command.start_transaction(); + let stats = jj_lib::git::import_some_refs( + tx.mut_repo(), + &command.settings().git_settings(), + |ref_name| !jj_lib::git::is_reserved_git_remote_ref(ref_name), + )?; + print_git_import_stats(ui, &stats)?; + 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 refs")?; + } + } + print_trackable_remote_branches(ui, workspace_command.repo().view())?; + } + + Ok(()) +} diff --git a/lib/src/workspace.rs b/lib/src/workspace.rs index 4bef895cce..d8e8c41074 100644 --- a/lib/src/workspace.rs +++ b/lib/src/workspace.rs @@ -48,6 +48,8 @@ pub enum WorkspaceInitError { DestinationExists(PathBuf), #[error("Repo path could not be interpreted as Unicode text")] NonUnicodePath, + #[error("Path {0} does not exist")] + PathNotFound(PathBuf), #[error(transparent)] CheckOutCommit(#[from] CheckOutCommitError), #[error(transparent)] @@ -140,6 +142,17 @@ impl Workspace { }) } + pub fn create_workspace_root(workspace_root: &Path) -> std::io::Result { + match fs::create_dir(workspace_root) { + Ok(()) => {} + Err(_) if workspace_root.is_dir() => {} + Err(e) => return Err(e), + } + + // if it fails, race condition? + workspace_root.canonicalize() + } + pub fn init_local( user_settings: &UserSettings, workspace_root: &Path, @@ -194,6 +207,17 @@ impl Workspace { workspace_root: &Path, git_repo_path: &Path, ) -> Result<(Self, Arc), WorkspaceInitError> { + let mut git_repo_path = git_repo_path + .canonicalize() + .map_err(|_| WorkspaceInitError::PathNotFound(git_repo_path.to_path_buf()))?; + if !git_repo_path.ends_with(".git") { + git_repo_path.push(".git"); + + if !git_repo_path.exists() { + git_repo_path.pop(); + } + } + let backend_initializer = |settings: &UserSettings, store_path: &Path| -> Result, _> { // If the git repo is inside the workspace, use a relative path to it so the @@ -203,7 +227,7 @@ impl Workspace { // Workspace::new(), but it's not yet here. let store_relative_git_repo_path = match ( workspace_root.canonicalize(), - canonicalize_git_repo_path(git_repo_path), + canonicalize_git_repo_path(&git_repo_path), ) { (Ok(workspace_root), Ok(git_repo_path)) if git_repo_path.starts_with(&workspace_root) =>