File size: 4,685 Bytes
d90101d | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 | use std::path::PathBuf;
use std::process::Command;
use anyhow::{Context, Result, bail};
use bstr::ByteSlice;
use forge_domain::TitleFormat;
use crate::title_display::TitleDisplayExt;
pub struct Sandbox<'a> {
dir: &'a str,
}
impl<'a> Sandbox<'a> {
pub fn new(dir: &'a str) -> Self {
Self { dir }
}
/// Handles worktree creation and returns the path to the worktree directory
pub fn create(&self) -> Result<PathBuf> {
let worktree_name = self.dir;
// First check if we're in a git repository
let git_check = Command::new("git")
.args(["rev-parse", "--is-inside-work-tree"])
.output()
.context("Failed to check if current directory is a git repository")?;
if !git_check.status.success() {
bail!(
"Current directory is not inside a git repository. Worktree creation requires a git repository."
);
}
// Get the git root directory
let git_root_output = Command::new("git")
.args(["rev-parse", "--show-toplevel"])
.output()
.context("Failed to get git root directory")?;
if !git_root_output.status.success() {
bail!("Failed to determine git repository root");
}
let git_root = String::from_utf8(git_root_output.stdout)
.context("Git root path contains invalid UTF-8")?
.trim()
.to_string();
let git_root_path = PathBuf::from(&git_root);
// Get the parent directory of the git root
let parent_dir = git_root_path.parent().context(
"Git repository is at filesystem root - cannot create worktree in parent directory",
)?;
// Create the worktree path in the parent directory
let worktree_path = parent_dir.join(worktree_name);
// Check if worktree already exists
if worktree_path.exists() {
// Check if it's already a git worktree by checking if it has a .git file
// (worktree marker)
let git_file = worktree_path.join(".git");
if git_file.exists() {
let worktree_check = Command::new("git")
.args(["rev-parse", "--is-inside-work-tree"])
.current_dir(&worktree_path)
.output()
.context("Failed to check if target directory is a git worktree")?;
if worktree_check.status.success() {
println!(
"{}",
TitleFormat::info("Worktree [Reused]")
.sub_title(worktree_path.display().to_string())
.display()
);
return worktree_path
.canonicalize()
.context("Failed to canonicalize worktree path");
}
}
bail!(
"Directory '{}' already exists but is not a git worktree. Please remove it or choose a different name.",
worktree_path.display()
);
}
// Check if branch already exists
let branch_check = Command::new("git")
.args([
"rev-parse",
"--verify",
&format!("refs/heads/{worktree_name}"),
])
.current_dir(&git_root_path)
.output()
.context("Failed to check if branch exists")?;
let branch_exists = branch_check.status.success();
// Create the worktree
let mut worktree_cmd = Command::new("git");
worktree_cmd.args(["worktree", "add"]);
if !branch_exists {
// Create new branch from current HEAD
worktree_cmd.args(["-b", worktree_name]);
}
worktree_cmd.args([worktree_path.to_str().unwrap()]);
if branch_exists {
worktree_cmd.arg(worktree_name);
}
let worktree_output = worktree_cmd
.current_dir(&git_root_path)
.output()
.context("Failed to create git worktree")?;
if !worktree_output.status.success() {
let stderr = worktree_output.stderr.to_str_lossy();
bail!("Failed to create git worktree: {stderr}");
}
println!(
"{}",
TitleFormat::info("Worktree [Created]")
.sub_title(worktree_path.display().to_string())
.display()
);
// Return the canonicalized path
worktree_path
.canonicalize()
.context("Failed to canonicalize worktree path")
}
}
|