Skip to main content

git_workflow/state/
working_dir.rs

1//! Working directory state
2
3use crate::git;
4
5/// State of the working directory
6#[derive(Debug, Clone, PartialEq, Eq)]
7pub enum WorkingDirState {
8    /// No uncommitted changes and no untracked files
9    Clean,
10    /// Has unstaged changes only
11    HasUnstagedChanges,
12    /// Has staged changes only
13    HasStagedChanges,
14    /// Has both staged and unstaged changes
15    HasMixedChanges,
16    /// Has untracked files only (no staged or unstaged tracked changes)
17    HasUntrackedFiles,
18}
19
20impl WorkingDirState {
21    /// Detect the current working directory state
22    ///
23    /// Untracked files count as "not clean": leaving them out caused `gw pause`
24    /// to skip the WIP commit (and switch home) when the only work was new,
25    /// unstaged files — silently leaving them behind. Tracked changes take
26    /// precedence for the description; untracked-only maps to `HasUntrackedFiles`.
27    pub fn detect() -> Self {
28        let has_unstaged = git::has_unstaged_changes();
29        let has_staged = git::has_staged_changes();
30        let has_untracked = git::has_untracked_files();
31
32        match (has_unstaged, has_staged) {
33            (true, true) => WorkingDirState::HasMixedChanges,
34            (true, false) => WorkingDirState::HasUnstagedChanges,
35            (false, true) => WorkingDirState::HasStagedChanges,
36            (false, false) => {
37                if has_untracked {
38                    WorkingDirState::HasUntrackedFiles
39                } else {
40                    WorkingDirState::Clean
41                }
42            }
43        }
44    }
45
46    /// Check if the working directory is clean (no tracked changes, no untracked files)
47    pub fn is_clean(&self) -> bool {
48        matches!(self, WorkingDirState::Clean)
49    }
50
51    /// Whether there are tracked changes (staged and/or unstaged), ignoring
52    /// untracked files.
53    pub fn has_tracked_changes(&self) -> bool {
54        matches!(
55            self,
56            WorkingDirState::HasUnstagedChanges
57                | WorkingDirState::HasStagedChanges
58                | WorkingDirState::HasMixedChanges
59        )
60    }
61
62    /// Get a human-readable description
63    pub fn description(&self) -> &'static str {
64        match self {
65            WorkingDirState::Clean => "clean",
66            WorkingDirState::HasUnstagedChanges => "has unstaged changes",
67            WorkingDirState::HasStagedChanges => "has staged changes",
68            WorkingDirState::HasMixedChanges => "has staged and unstaged changes",
69            WorkingDirState::HasUntrackedFiles => "has untracked files",
70        }
71    }
72}