git_spawn/command/
worktree.rs1use crate::command::{CommandExecutor, CommandOutput, GitCommand};
4use crate::error::Result;
5use async_trait::async_trait;
6use std::path::PathBuf;
7
8#[derive(Debug, Clone)]
10pub enum WorktreeAction {
11 Add {
13 path: PathBuf,
15 commit_ish: Option<String>,
17 new_branch: Option<String>,
19 detach: bool,
21 force: bool,
23 track: bool,
25 },
26 List {
28 porcelain: bool,
30 },
31 Remove {
33 path: PathBuf,
35 force: bool,
37 },
38 Prune {
40 verbose: bool,
42 dry_run: bool,
44 },
45 Move {
47 source: PathBuf,
49 destination: PathBuf,
51 },
52 Lock {
54 path: PathBuf,
56 reason: Option<String>,
58 },
59 Unlock {
61 path: PathBuf,
63 },
64}
65
66#[derive(Debug, Clone)]
68pub struct WorktreeCommand {
69 pub executor: CommandExecutor,
71 pub action: WorktreeAction,
73}
74
75impl WorktreeCommand {
76 pub fn add(path: impl Into<PathBuf>) -> Self {
78 Self {
79 executor: CommandExecutor::default(),
80 action: WorktreeAction::Add {
81 path: path.into(),
82 commit_ish: None,
83 new_branch: None,
84 detach: false,
85 force: false,
86 track: false,
87 },
88 }
89 }
90
91 #[must_use]
93 pub fn commit_ish(mut self, c: impl Into<String>) -> Self {
94 if let WorktreeAction::Add { commit_ish, .. } = &mut self.action {
95 *commit_ish = Some(c.into());
96 }
97 self
98 }
99
100 #[must_use]
102 pub fn new_branch(mut self, b: impl Into<String>) -> Self {
103 if let WorktreeAction::Add { new_branch, .. } = &mut self.action {
104 *new_branch = Some(b.into());
105 }
106 self
107 }
108
109 #[must_use]
111 pub fn detach(mut self) -> Self {
112 if let WorktreeAction::Add { detach, .. } = &mut self.action {
113 *detach = true;
114 }
115 self
116 }
117
118 #[must_use]
120 pub fn force(mut self) -> Self {
121 match &mut self.action {
122 WorktreeAction::Add { force, .. } | WorktreeAction::Remove { force, .. } => {
123 *force = true;
124 }
125 _ => {}
126 }
127 self
128 }
129
130 #[must_use]
132 pub fn list() -> Self {
133 Self {
134 executor: CommandExecutor::default(),
135 action: WorktreeAction::List { porcelain: false },
136 }
137 }
138
139 #[must_use]
141 pub fn list_porcelain() -> Self {
142 Self {
143 executor: CommandExecutor::default(),
144 action: WorktreeAction::List { porcelain: true },
145 }
146 }
147
148 pub fn remove(path: impl Into<PathBuf>) -> Self {
150 Self {
151 executor: CommandExecutor::default(),
152 action: WorktreeAction::Remove {
153 path: path.into(),
154 force: false,
155 },
156 }
157 }
158
159 #[must_use]
161 pub fn prune() -> Self {
162 Self {
163 executor: CommandExecutor::default(),
164 action: WorktreeAction::Prune {
165 verbose: false,
166 dry_run: false,
167 },
168 }
169 }
170
171 pub fn move_tree(source: impl Into<PathBuf>, destination: impl Into<PathBuf>) -> Self {
173 Self {
174 executor: CommandExecutor::default(),
175 action: WorktreeAction::Move {
176 source: source.into(),
177 destination: destination.into(),
178 },
179 }
180 }
181
182 pub fn lock(path: impl Into<PathBuf>) -> Self {
184 Self {
185 executor: CommandExecutor::default(),
186 action: WorktreeAction::Lock {
187 path: path.into(),
188 reason: None,
189 },
190 }
191 }
192
193 #[must_use]
195 pub fn reason(mut self, r: impl Into<String>) -> Self {
196 if let WorktreeAction::Lock { reason, .. } = &mut self.action {
197 *reason = Some(r.into());
198 }
199 self
200 }
201
202 pub fn unlock(path: impl Into<PathBuf>) -> Self {
204 Self {
205 executor: CommandExecutor::default(),
206 action: WorktreeAction::Unlock { path: path.into() },
207 }
208 }
209}
210
211#[async_trait]
212impl GitCommand for WorktreeCommand {
213 type Output = CommandOutput;
214 fn get_executor(&self) -> &CommandExecutor {
215 &self.executor
216 }
217 fn get_executor_mut(&mut self) -> &mut CommandExecutor {
218 &mut self.executor
219 }
220 fn build_command_args(&self) -> Vec<String> {
221 let mut args = vec!["worktree".to_string()];
222 match &self.action {
223 WorktreeAction::Add {
224 path,
225 commit_ish,
226 new_branch,
227 detach,
228 force,
229 track,
230 } => {
231 args.push("add".into());
232 if *force {
233 args.push("--force".into());
234 }
235 if *detach {
236 args.push("--detach".into());
237 }
238 if *track {
239 args.push("--track".into());
240 }
241 if let Some(b) = new_branch {
242 args.push("-b".into());
243 args.push(b.clone());
244 }
245 args.push(path.display().to_string());
246 if let Some(c) = commit_ish {
247 args.push(c.clone());
248 }
249 }
250 WorktreeAction::List { porcelain } => {
251 args.push("list".into());
252 if *porcelain {
253 args.push("--porcelain".into());
254 }
255 }
256 WorktreeAction::Remove { path, force } => {
257 args.push("remove".into());
258 if *force {
259 args.push("--force".into());
260 }
261 args.push(path.display().to_string());
262 }
263 WorktreeAction::Prune { verbose, dry_run } => {
264 args.push("prune".into());
265 if *verbose {
266 args.push("-v".into());
267 }
268 if *dry_run {
269 args.push("--dry-run".into());
270 }
271 }
272 WorktreeAction::Move {
273 source,
274 destination,
275 } => {
276 args.push("move".into());
277 args.push(source.display().to_string());
278 args.push(destination.display().to_string());
279 }
280 WorktreeAction::Lock { path, reason } => {
281 args.push("lock".into());
282 if let Some(r) = reason {
283 args.push("--reason".into());
284 args.push(r.clone());
285 }
286 args.push(path.display().to_string());
287 }
288 WorktreeAction::Unlock { path } => {
289 args.push("unlock".into());
290 args.push(path.display().to_string());
291 }
292 }
293 args
294 }
295 async fn execute(&self) -> Result<CommandOutput> {
296 self.execute_raw().await
297 }
298}