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