worktree_io/
multi_workspace.rs1use anyhow::{Context, Result};
3use std::{
4 fs,
5 path::{Path, PathBuf},
6};
7
8use crate::{
9 git::{
10 bare_clone, branch_exists_remote, create_worktree, detect_default_branch, git_fetch,
11 git_worktree_prune,
12 },
13 issue::IssueRef,
14 name_gen,
15 ttl::WorkspaceRegistry,
16};
17
18pub enum MultiSpec {
20 WithIssue(IssueRef),
22 BareRepo {
24 owner: String,
26 repo: String,
28 },
29}
30
31pub fn create_multi_workspace(specs: &[MultiSpec], workspaces_root: &Path) -> Result<PathBuf> {
37 let root = workspaces_root.join(name_gen::generate_name());
38 fs::create_dir_all(&root)
39 .with_context(|| format!("failed to create workspace root {}", root.display()))?;
40 for spec in specs {
41 open_one(spec, &root)?;
42 }
43 if let Ok(mut registry) = WorkspaceRegistry::load() {
44 registry.register(root.clone());
45 let _ = registry.save();
46 }
47 Ok(root)
48}
49
50fn github_bare_path(owner: &str, repo: &str) -> PathBuf {
51 dirs::home_dir()
52 .expect("could not determine home directory")
53 .join("worktrees")
54 .join("github")
55 .join(owner)
56 .join(repo)
57}
58
59fn open_one(spec: &MultiSpec, root: &Path) -> Result<()> {
60 match spec {
61 MultiSpec::WithIssue(issue) => open_one_issue(issue, root),
62 MultiSpec::BareRepo { owner, repo } => open_one_bare(owner, repo, root),
63 }
64}
65
66fn open_one_issue(issue: &IssueRef, root: &Path) -> Result<()> {
67 let bare_path = issue.bare_clone_path();
68 if bare_path.exists() {
69 eprintln!("Fetching origin for {}…", bare_path.display());
70 git_fetch(&bare_path)?;
71 } else {
72 eprintln!("Cloning {} (bare)…", issue.clone_url());
73 bare_clone(&issue.clone_url(), &bare_path)?;
74 }
75 let base_branch = detect_default_branch(&bare_path)?;
76 let branch = issue.branch_name();
77 let branch_exists = branch_exists_remote(&bare_path, &branch);
78 let _ = git_worktree_prune(&bare_path);
79 let dest = root.join(issue.multi_dir_name());
80 create_worktree(&bare_path, &dest, &branch, &base_branch, branch_exists)
81 .with_context(|| format!("failed to create worktree at {}", dest.display()))
82}
83
84fn open_one_bare(owner: &str, repo: &str, root: &Path) -> Result<()> {
85 let bare_path = github_bare_path(owner, repo);
86 let url = format!("https://github.com/{owner}/{repo}.git");
87 if bare_path.exists() {
88 eprintln!("Fetching origin for {}…", bare_path.display());
89 git_fetch(&bare_path)?;
90 } else {
91 eprintln!("Cloning {url} (bare)…");
92 bare_clone(&url, &bare_path)?;
93 }
94 let branch = detect_default_branch(&bare_path)?;
95 let _ = git_worktree_prune(&bare_path);
96 let dest = root.join(repo);
97 create_worktree(&bare_path, &dest, &branch, &branch, true)
98 .with_context(|| format!("failed to create worktree at {}", dest.display()))
99}
100