use std::io::{self, BufRead, Write};
use std::path::Path;
use console::style;
use crate::config::{GrovConfig, WorktreeConfig, write_config};
use crate::git::executor::run_git_ok;
use crate::git::repo::default_branch;
use crate::git::worktree::add_worktree;
use crate::paths::{repo_name_from_url, worktree_dir};
fn prompt(label: &str, default: Option<&str>, reader: &mut impl BufRead) -> io::Result<String> {
let prompt_marker = style("?").cyan().bold();
let label_styled = style(label).bold();
match default {
Some(d) => eprint!(
"{prompt_marker} {label_styled} {}: ",
style(format!("[{d}]")).dim()
),
None => eprint!("{prompt_marker} {label_styled}: "),
}
io::stderr().flush()?;
let mut line = String::new();
reader.read_line(&mut line)?;
Ok(line.trim().to_string())
}
pub fn execute(
url: Option<&str>,
name: Option<&str>,
prefix: Option<&str>,
path: Option<&Path>,
) -> anyhow::Result<()> {
let stdin = io::stdin();
let mut reader = stdin.lock();
let url = match url {
Some(u) => u.to_string(),
None => {
let line = prompt("Repository URL", None, &mut reader)?;
if line.is_empty() {
anyhow::bail!("URL is required");
}
line
}
};
let derived_name = repo_name_from_url(&url);
let project_name = match name {
Some(n) => n.to_string(),
None => {
let line = prompt("Project name", Some(&derived_name), &mut reader)?;
if line.is_empty() { derived_name } else { line }
}
};
let prefix = match prefix {
Some(p) => p.to_string(),
None => prompt(
"Worktree prefix (e.g. short alias, blank for none)",
Some(""),
&mut reader,
)?,
};
let parent = match path {
Some(p) => p.to_path_buf(),
None => std::env::current_dir()?,
};
let project_dir = parent.join(&project_name);
if project_dir.exists() {
anyhow::bail!("directory already exists: {}", project_dir.display());
}
std::fs::create_dir_all(&project_dir)?;
let bare_path = project_dir.join("repo.git");
let bare_str = bare_path.to_string_lossy().to_string();
run_git_ok(None, &["clone", "--bare", &url, &bare_str])?;
let config = GrovConfig {
worktree: WorktreeConfig {
prefix: prefix.clone(),
},
};
write_config(&bare_path, &config)?;
run_git_ok(
Some(&bare_path),
&[
"config",
"remote.origin.fetch",
"+refs/heads/*:refs/remotes/origin/*",
],
)?;
run_git_ok(Some(&bare_path), &["fetch", "origin"])?;
let branch = default_branch(&bare_path)?;
let wt_path = worktree_dir(&bare_path, &branch, &prefix);
add_worktree(&bare_path, &wt_path, Some(&branch), &[])?;
let wt_dir_name = wt_path
.file_name()
.map(|n| n.to_string_lossy().to_string())
.unwrap_or_default();
println!(
"\n{} Initialized {}/ with worktree {}",
style("✓").green().bold(),
style(&project_name).bold(),
style(&wt_dir_name).cyan().bold(),
);
Ok(())
}