Skip to main content

git_workflow/state/
sync.rs

1//! Sync state with remote
2
3use crate::error::Result;
4use crate::git;
5
6/// Sync state with upstream
7#[derive(Debug, Clone, PartialEq, Eq)]
8pub enum SyncState {
9    /// No upstream tracking branch
10    NoUpstream,
11    /// Synced with upstream
12    Synced,
13    /// Has commits not pushed to upstream
14    HasUnpushedCommits { count: usize },
15    /// Behind upstream
16    Behind { count: usize },
17    /// Diverged (both ahead and behind)
18    Diverged { ahead: usize, behind: usize },
19}
20
21impl SyncState {
22    /// Detect the sync state for a branch
23    pub fn detect(branch: &str) -> Result<Self> {
24        if !git::has_remote_tracking(branch) {
25            return Ok(SyncState::NoUpstream);
26        }
27
28        let ahead = git::unpushed_commit_count(branch).unwrap_or(0);
29        let behind = git::behind_upstream_count(branch).unwrap_or(0);
30
31        Ok(match (ahead, behind) {
32            (0, 0) => SyncState::Synced,
33            (a, 0) if a > 0 => SyncState::HasUnpushedCommits { count: a },
34            (0, b) if b > 0 => SyncState::Behind { count: b },
35            (a, b) => SyncState::Diverged {
36                ahead: a,
37                behind: b,
38            },
39        })
40    }
41
42    /// Check if there are unpushed commits
43    pub fn has_unpushed(&self) -> bool {
44        matches!(
45            self,
46            SyncState::HasUnpushedCommits { .. } | SyncState::Diverged { .. }
47        )
48    }
49
50    /// Get the number of unpushed commits, if any
51    pub fn unpushed_count(&self) -> usize {
52        match self {
53            SyncState::HasUnpushedCommits { count } => *count,
54            SyncState::Diverged { ahead, .. } => *ahead,
55            _ => 0,
56        }
57    }
58
59    /// Get a human-readable description
60    pub fn description(&self) -> String {
61        match self {
62            SyncState::NoUpstream => "no upstream".to_string(),
63            SyncState::Synced => "synced".to_string(),
64            SyncState::HasUnpushedCommits { count } => format!("{count} unpushed commit(s)"),
65            SyncState::Behind { count } => format!("{count} commit(s) behind"),
66            SyncState::Diverged { ahead, behind } => {
67                format!("{ahead} ahead, {behind} behind")
68            }
69        }
70    }
71}