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 pub fn commit_ish(&mut self, c: impl Into<String>) -> &mut Self {
93 if let WorktreeAction::Add { commit_ish, .. } = &mut self.action {
94 *commit_ish = Some(c.into());
95 }
96 self
97 }
98
99 pub fn new_branch(&mut self, b: impl Into<String>) -> &mut Self {
101 if let WorktreeAction::Add { new_branch, .. } = &mut self.action {
102 *new_branch = Some(b.into());
103 }
104 self
105 }
106
107 pub fn detach(&mut self) -> &mut Self {
109 if let WorktreeAction::Add { detach, .. } = &mut self.action {
110 *detach = true;
111 }
112 self
113 }
114
115 pub fn force(&mut self) -> &mut Self {
117 match &mut self.action {
118 WorktreeAction::Add { force, .. } | WorktreeAction::Remove { force, .. } => {
119 *force = true;
120 }
121 _ => {}
122 }
123 self
124 }
125
126 #[must_use]
128 pub fn list() -> Self {
129 Self {
130 executor: CommandExecutor::default(),
131 action: WorktreeAction::List { porcelain: false },
132 }
133 }
134
135 #[must_use]
137 pub fn list_porcelain() -> Self {
138 Self {
139 executor: CommandExecutor::default(),
140 action: WorktreeAction::List { porcelain: true },
141 }
142 }
143
144 pub fn remove(path: impl Into<PathBuf>) -> Self {
146 Self {
147 executor: CommandExecutor::default(),
148 action: WorktreeAction::Remove {
149 path: path.into(),
150 force: false,
151 },
152 }
153 }
154
155 #[must_use]
157 pub fn prune() -> Self {
158 Self {
159 executor: CommandExecutor::default(),
160 action: WorktreeAction::Prune {
161 verbose: false,
162 dry_run: false,
163 },
164 }
165 }
166
167 pub fn move_tree(source: impl Into<PathBuf>, destination: impl Into<PathBuf>) -> Self {
169 Self {
170 executor: CommandExecutor::default(),
171 action: WorktreeAction::Move {
172 source: source.into(),
173 destination: destination.into(),
174 },
175 }
176 }
177
178 pub fn lock(path: impl Into<PathBuf>) -> Self {
180 Self {
181 executor: CommandExecutor::default(),
182 action: WorktreeAction::Lock {
183 path: path.into(),
184 reason: None,
185 },
186 }
187 }
188
189 pub fn reason(&mut self, r: impl Into<String>) -> &mut Self {
191 if let WorktreeAction::Lock { reason, .. } = &mut self.action {
192 *reason = Some(r.into());
193 }
194 self
195 }
196
197 pub fn unlock(path: impl Into<PathBuf>) -> Self {
199 Self {
200 executor: CommandExecutor::default(),
201 action: WorktreeAction::Unlock { path: path.into() },
202 }
203 }
204}
205
206#[async_trait]
207impl GitCommand for WorktreeCommand {
208 type Output = CommandOutput;
209 fn get_executor(&self) -> &CommandExecutor {
210 &self.executor
211 }
212 fn get_executor_mut(&mut self) -> &mut CommandExecutor {
213 &mut self.executor
214 }
215 fn build_command_args(&self) -> Vec<String> {
216 let mut args = vec!["worktree".to_string()];
217 match &self.action {
218 WorktreeAction::Add {
219 path,
220 commit_ish,
221 new_branch,
222 detach,
223 force,
224 track,
225 } => {
226 args.push("add".into());
227 if *force {
228 args.push("--force".into());
229 }
230 if *detach {
231 args.push("--detach".into());
232 }
233 if *track {
234 args.push("--track".into());
235 }
236 if let Some(b) = new_branch {
237 args.push("-b".into());
238 args.push(b.clone());
239 }
240 args.push(path.display().to_string());
241 if let Some(c) = commit_ish {
242 args.push(c.clone());
243 }
244 }
245 WorktreeAction::List { porcelain } => {
246 args.push("list".into());
247 if *porcelain {
248 args.push("--porcelain".into());
249 }
250 }
251 WorktreeAction::Remove { path, force } => {
252 args.push("remove".into());
253 if *force {
254 args.push("--force".into());
255 }
256 args.push(path.display().to_string());
257 }
258 WorktreeAction::Prune { verbose, dry_run } => {
259 args.push("prune".into());
260 if *verbose {
261 args.push("-v".into());
262 }
263 if *dry_run {
264 args.push("--dry-run".into());
265 }
266 }
267 WorktreeAction::Move {
268 source,
269 destination,
270 } => {
271 args.push("move".into());
272 args.push(source.display().to_string());
273 args.push(destination.display().to_string());
274 }
275 WorktreeAction::Lock { path, reason } => {
276 args.push("lock".into());
277 if let Some(r) = reason {
278 args.push("--reason".into());
279 args.push(r.clone());
280 }
281 args.push(path.display().to_string());
282 }
283 WorktreeAction::Unlock { path } => {
284 args.push("unlock".into());
285 args.push(path.display().to_string());
286 }
287 }
288 args
289 }
290 async fn execute(&self) -> Result<CommandOutput> {
291 self.execute_raw().await
292 }
293}