Skip to main content

git_spawn/command/
add.rs

1//! `git add` — add file contents to the index.
2
3use crate::command::{CommandExecutor, CommandOutput, GitCommand};
4use crate::error::Result;
5use async_trait::async_trait;
6
7/// Builder for `git add`.
8#[derive(Debug, Clone, Default)]
9pub struct AddCommand {
10    /// Shared executor.
11    pub executor: CommandExecutor,
12    /// Pathspecs to add. `.` or `-A` is common.
13    pub paths: Vec<String>,
14    /// `--all` / `-A`.
15    pub all: bool,
16    /// `--update` / `-u`.
17    pub update: bool,
18    /// `--force` / `-f`.
19    pub force: bool,
20    /// `--dry-run` / `-n`.
21    pub dry_run: bool,
22    /// `--verbose` / `-v`.
23    pub verbose: bool,
24    /// `--intent-to-add` / `-N`.
25    pub intent_to_add: bool,
26    /// `--patch` / `-p`.
27    pub patch: bool,
28    /// `--ignore-errors`.
29    pub ignore_errors: bool,
30}
31
32impl AddCommand {
33    /// New empty `add` command.
34    #[must_use]
35    pub fn new() -> Self {
36        Self::default()
37    }
38
39    /// Add a pathspec.
40    pub fn path(&mut self, p: impl Into<String>) -> &mut Self {
41        self.paths.push(p.into());
42        self
43    }
44
45    /// Add many pathspecs.
46    pub fn paths<I, S>(&mut self, ps: I) -> &mut Self
47    where
48        I: IntoIterator<Item = S>,
49        S: Into<String>,
50    {
51        self.paths.extend(ps.into_iter().map(Into::into));
52        self
53    }
54
55    /// Stage every change, including deletions and new files (`-A`).
56    pub fn all(&mut self) -> &mut Self {
57        self.all = true;
58        self
59    }
60
61    /// Stage only modifications and deletions (`-u`).
62    pub fn update(&mut self) -> &mut Self {
63        self.update = true;
64        self
65    }
66
67    /// Override ignore rules.
68    pub fn force(&mut self) -> &mut Self {
69        self.force = true;
70        self
71    }
72
73    /// Show what would happen without changing anything.
74    pub fn dry_run(&mut self) -> &mut Self {
75        self.dry_run = true;
76        self
77    }
78
79    /// Be verbose.
80    pub fn verbose(&mut self) -> &mut Self {
81        self.verbose = true;
82        self
83    }
84
85    /// Record only that a path will be added later.
86    pub fn intent_to_add(&mut self) -> &mut Self {
87        self.intent_to_add = true;
88        self
89    }
90
91    /// Interactively stage hunks.
92    pub fn patch(&mut self) -> &mut Self {
93        self.patch = true;
94        self
95    }
96
97    /// Continue past files that cannot be added.
98    pub fn ignore_errors(&mut self) -> &mut Self {
99        self.ignore_errors = true;
100        self
101    }
102}
103
104#[async_trait]
105impl GitCommand for AddCommand {
106    type Output = CommandOutput;
107
108    fn get_executor(&self) -> &CommandExecutor {
109        &self.executor
110    }
111
112    fn get_executor_mut(&mut self) -> &mut CommandExecutor {
113        &mut self.executor
114    }
115
116    fn build_command_args(&self) -> Vec<String> {
117        let mut args = vec!["add".to_string()];
118        if self.all {
119            args.push("--all".into());
120        }
121        if self.update {
122            args.push("--update".into());
123        }
124        if self.force {
125            args.push("--force".into());
126        }
127        if self.dry_run {
128            args.push("--dry-run".into());
129        }
130        if self.verbose {
131            args.push("--verbose".into());
132        }
133        if self.intent_to_add {
134            args.push("--intent-to-add".into());
135        }
136        if self.patch {
137            args.push("--patch".into());
138        }
139        if self.ignore_errors {
140            args.push("--ignore-errors".into());
141        }
142        if !self.paths.is_empty() {
143            args.push("--".into());
144            args.extend(self.paths.iter().cloned());
145        }
146        args
147    }
148
149    async fn execute(&self) -> Result<CommandOutput> {
150        self.execute_raw().await
151    }
152}