Skip to main content

worktree_io/
workspace.rs

1use anyhow::Result;
2use std::path::PathBuf;
3
4use crate::git::{
5    bare_clone, branch_exists_local, branch_exists_remote, create_local_worktree, create_worktree,
6    detect_default_branch, git_fetch,
7};
8use crate::issue::IssueRef;
9
10/// An open (or newly created) git worktree for a given issue.
11pub struct Workspace {
12    /// Absolute path to the worktree directory.
13    pub path: PathBuf,
14    /// The issue this workspace was opened for.
15    pub issue: IssueRef,
16    /// `true` if this call created the worktree; `false` if it already existed.
17    pub created: bool,
18}
19
20impl Workspace {
21    /// Open an existing worktree or create a fresh one.
22    ///
23    /// # Errors
24    ///
25    /// Returns an error if the repository cannot be cloned/fetched, the branch
26    /// cannot be detected, or the worktree cannot be created.
27    pub fn open_or_create(issue: IssueRef) -> Result<Self> {
28        let worktree_path = issue.temp_path();
29        let bare_path = issue.bare_clone_path();
30
31        // Fast path: worktree already exists
32        if worktree_path.exists() {
33            return Ok(Self {
34                path: worktree_path,
35                issue,
36                created: false,
37            });
38        }
39
40        // LLVM_COV_EXCL_START
41        if let IssueRef::Local { project_path, .. } = &issue {
42            // No bare clone — use the local repo directly.
43            eprintln!("Creating local worktree at {}…", worktree_path.display());
44            let branch = issue.branch_name();
45            let branch_exists = branch_exists_local(project_path, &branch);
46            std::fs::create_dir_all(worktree_path.parent().unwrap_or(&worktree_path))?;
47            create_local_worktree(project_path, &worktree_path, &branch, branch_exists)?;
48        } else {
49            if bare_path.exists() {
50                eprintln!("Fetching origin…");
51                git_fetch(&bare_path)?;
52            } else {
53                eprintln!(
54                    "Cloning {} (bare) into {}…",
55                    issue.clone_url(),
56                    bare_path.display()
57                );
58                bare_clone(&issue.clone_url(), &bare_path)?;
59            }
60
61            let base_branch = detect_default_branch(&bare_path)?;
62            eprintln!("Default branch: {base_branch}");
63
64            let branch = issue.branch_name();
65            let branch_exists = branch_exists_remote(&bare_path, &branch);
66
67            eprintln!(
68                "Creating worktree {} at {}…",
69                branch,
70                worktree_path.display()
71            );
72            create_worktree(
73                &bare_path,
74                &worktree_path,
75                &branch,
76                &base_branch,
77                branch_exists,
78            )?;
79        }
80
81        Ok(Self {
82            path: worktree_path,
83            issue,
84            created: true,
85        })
86        // LLVM_COV_EXCL_STOP
87    }
88}
89
90#[cfg(test)]
91#[path = "workspace_tests.rs"]
92mod workspace_tests;