Skip to main content

git_proc/
reset.rs

1//! `git reset` command builder.
2//!
3//! Uses the same typestate as `checkout` for the optional commit-ish target,
4//! but `Build`/`status` are available in **both** states because `git reset`
5//! without a target is valid (defaults to `HEAD`).
6
7use std::marker::PhantomData;
8use std::path::Path;
9
10use crate::CommandError;
11use crate::commit_ish::{CommitIsh, NoTarget, WithTarget};
12
13/// Create a new `git reset` command builder.
14#[must_use]
15pub fn new() -> Reset<'static, NoTarget> {
16    Reset {
17        repo_path: None,
18        hard: false,
19        state: NoTarget,
20        _phantom: PhantomData,
21    }
22}
23
24/// Builder for `git reset` command.
25///
26/// See `git reset --help` for full documentation.
27#[derive(Debug)]
28pub struct Reset<'a, S> {
29    repo_path: Option<&'a Path>,
30    hard: bool,
31    state: S,
32    _phantom: PhantomData<&'a ()>,
33}
34
35impl<'a, S> Reset<'a, S> {
36    /// Set the repository path (`-C <path>`).
37    #[must_use]
38    pub fn repo_path(mut self, path: &'a Path) -> Self {
39        self.repo_path = Some(path);
40        self
41    }
42
43    crate::flag_methods! {
44        /// Reset the index and working tree.
45        ///
46        /// Corresponds to `--hard`.
47        pub fn hard / hard_if, hard, "Conditionally reset the index and working tree."
48    }
49}
50
51impl<'a, S> crate::RepoPath<'a> for Reset<'a, S> {
52    fn repo_path(self, path: &'a Path) -> Self {
53        self.repo_path(path)
54    }
55}
56
57impl<'a> Reset<'a, NoTarget> {
58    /// Select the commit-ish to reset to.
59    ///
60    /// Calling this once locks the target in — selecting a second is a
61    /// compile error.
62    #[must_use]
63    pub fn commit_ish(self, commit_ish: impl Into<CommitIsh<'a>>) -> Reset<'a, WithTarget<'a>> {
64        Reset {
65            repo_path: self.repo_path,
66            hard: self.hard,
67            state: WithTarget {
68                target: commit_ish.into(),
69            },
70            _phantom: PhantomData,
71        }
72    }
73}
74
75impl<'a, S> Reset<'a, S>
76where
77    Self: crate::Build,
78{
79    /// Execute the command and return the exit status.
80    pub async fn status(self) -> Result<(), CommandError> {
81        crate::Build::build(self).status().await
82    }
83}
84
85impl crate::Build for Reset<'_, NoTarget> {
86    fn build(self) -> cmd_proc::Command {
87        crate::base_command(self.repo_path)
88            .argument("reset")
89            .optional_flag(self.hard, "--hard")
90    }
91}
92
93impl<'a> crate::Build for Reset<'a, WithTarget<'a>> {
94    fn build(self) -> cmd_proc::Command {
95        crate::base_command(self.repo_path)
96            .argument("reset")
97            .optional_flag(self.hard, "--hard")
98            .argument(self.state.target)
99    }
100}
101
102#[cfg(feature = "test-utils")]
103impl<'a> Reset<'a, NoTarget> {
104    /// Compare the built command with another command using debug representation.
105    pub fn test_eq(&self, other: &cmd_proc::Command) {
106        let command = crate::Build::build(Self {
107            repo_path: self.repo_path,
108            hard: self.hard,
109            state: NoTarget,
110            _phantom: PhantomData,
111        });
112        command.test_eq(other);
113    }
114}
115
116#[cfg(feature = "test-utils")]
117impl<'a> Reset<'a, WithTarget<'a>> {
118    /// Compare the built command with another command using debug representation.
119    pub fn test_eq(&self, other: &cmd_proc::Command) {
120        let command = crate::Build::build(Self {
121            repo_path: self.repo_path,
122            hard: self.hard,
123            state: WithTarget {
124                target: self.state.target,
125            },
126            _phantom: PhantomData,
127        });
128        command.test_eq(other);
129    }
130}