Skip to main content

worktree_io/git/
mod.rs

1mod branch;
2mod clone;
3mod local_branch;
4
5pub use branch::{branch_exists_remote, detect_default_branch};
6pub use clone::{bare_clone, git_fetch};
7pub use local_branch::{branch_exists_local, detect_local_default_branch};
8
9use anyhow::{bail, Context, Result};
10use std::path::Path;
11use std::process::Command;
12
13/// Build a `git` [`Command`] with worktree-related environment variables unset.
14///
15/// When the process runs inside a git worktree hook, `GIT_DIR`, `GIT_WORK_TREE`,
16/// and `GIT_INDEX_FILE` are set by git itself and would override the `-C <dir>`
17/// flag, causing child git commands to operate on the wrong repository.
18/// Clearing them here ensures `-C dir` is always honoured.
19pub(super) fn git_cmd() -> Command {
20    let mut cmd = Command::new("git");
21    cmd.env_remove("GIT_DIR")
22        .env_remove("GIT_WORK_TREE")
23        .env_remove("GIT_INDEX_FILE");
24    cmd
25}
26
27/// Create a worktree from a local (non-bare) repository without referencing a remote.
28///
29/// When `branch_exists` is false a new branch is created from HEAD.
30/// When `branch_exists` is true the existing local branch is checked out.
31///
32/// # Errors
33///
34/// Returns an error if the git command fails to spawn or exits non-zero.
35pub fn create_local_worktree(
36    repo: &Path,
37    dest: &Path,
38    branch: &str,
39    branch_exists: bool,
40) -> Result<()> {
41    let mut cmd = git_cmd();
42    cmd.args(["-C"]).arg(repo).arg("worktree").arg("add");
43
44    if branch_exists {
45        cmd.arg(dest).arg(branch);
46    } else {
47        // Create new branch from HEAD (no origin/ reference needed)
48        cmd.arg(dest).arg("-b").arg(branch);
49    }
50
51    let status = cmd.status().context("Failed to run `git worktree add`")?;
52
53    if !status.success() {
54        bail!("git worktree add failed for branch {branch}"); // LLVM_COV_EXCL_LINE
55    }
56    Ok(())
57}
58
59/// Create a worktree inside a bare clone.
60///
61/// When `branch_exists` is false a new branch is created from `origin/<base_branch>`.
62/// When `branch_exists` is true the existing branch is checked out.
63///
64/// # Errors
65///
66/// Returns an error if the git command fails to spawn or exits non-zero.
67pub fn create_worktree(
68    bare: &Path,
69    dest: &Path,
70    branch: &str,
71    base_branch: &str,
72    branch_exists: bool,
73) -> Result<()> {
74    let mut cmd = git_cmd();
75    cmd.args(["-C"]).arg(bare).arg("worktree").arg("add");
76
77    if branch_exists {
78        cmd.arg(dest).arg(branch);
79    } else {
80        cmd.arg(dest)
81            .arg("-b")
82            .arg(branch)
83            .arg(format!("origin/{base_branch}"));
84    }
85
86    let status = cmd.status().context("Failed to run `git worktree add`")?;
87
88    if !status.success() {
89        bail!("git worktree add failed for branch {branch}"); // LLVM_COV_EXCL_LINE
90    }
91    Ok(())
92}