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;
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            create_local_worktree(project_path, &worktree_path, &branch, branch_exists)?;
49        } else {
50            if bare_path.exists() {
51                eprintln!("Fetching origin…");
52                git_fetch(&bare_path)?;
53            } else {
54                eprintln!(
55                    "Cloning {} (bare) into {}…",
56                    issue.clone_url(),
57                    bare_path.display()
58                );
59                bare_clone(&issue.clone_url(), &bare_path)?;
60            }
61
62            let base_branch = detect_default_branch(&bare_path)?;
63            eprintln!("Default branch: {base_branch}");
64
65            let branch = issue.branch_name();
66            let branch_exists = branch_exists_remote(&bare_path, &branch);
67
68            eprintln!(
69                "Creating worktree {} at {}…",
70                branch,
71                worktree_path.display()
72            );
73            create_worktree(
74                &bare_path,
75                &worktree_path,
76                &branch,
77                &base_branch,
78                branch_exists,
79            )?;
80        }
81
82        if let Ok(mut registry) = WorkspaceRegistry::load() {
83            registry.register(worktree_path.clone());
84            let _ = registry.save();
85        }
86
87        Ok(Self {
88            path: worktree_path,
89            issue,
90            created: true,
91        })
92        // LLVM_COV_EXCL_STOP
93    }
94}
95
96#[cfg(test)]
97#[path = "workspace_tests.rs"]
98mod workspace_tests;