Skip to main content

ag_git/
error.rs

1use std::time::Duration;
2
3use crate::rebase;
4
5/// Typed error returned by git infrastructure operations.
6///
7/// Wraps command execution failures, output parsing issues, and I/O errors so
8/// callers can distinguish error categories without parsing opaque strings.
9#[derive(Debug, thiserror::Error)]
10pub enum GitError {
11    /// A git subprocess exited with a non-zero status.
12    #[error("{command}: {stderr}")]
13    CommandFailed {
14        /// The git command that was executed (e.g. `"git rebase main"`).
15        command: String,
16        /// Human-readable detail extracted from stderr/stdout.
17        stderr: String,
18    },
19
20    /// A git subprocess exceeded its configured runtime bound.
21    #[error("{command} timed out after {timeout:?}")]
22    CommandTimedOut {
23        /// Git invocation that exceeded the timeout.
24        command: String,
25        /// Configured command timeout.
26        timeout: Duration,
27    },
28
29    /// Git command output could not be parsed into the expected structure.
30    #[error("{0}")]
31    OutputParse(String),
32
33    /// The requested repository or worktree is no longer available.
34    #[error("{detail}")]
35    RepositoryUnavailable {
36        /// Original repository-discovery failure detail.
37        detail: String,
38    },
39
40    /// A filesystem or process-spawn operation failed.
41    #[error("{0}")]
42    Io(#[from] std::io::Error),
43
44    /// A repository declares pre-commit validation but its Git hook is
45    /// unavailable.
46    #[error(
47        "pre-commit validation is configured by `{config_file}`, but the Git pre-commit hook is \
48         not installed or executable. Install it with one of these commands:\n\n  prek install\n  \
49         pre-commit install\n\nAgentty will continue for now, but missing configured hooks will \
50         become an error in a future release."
51    )]
52    PreCommitHookMissing {
53        /// Repository-root-relative configuration file that declares
54        /// validation.
55        config_file: String,
56    },
57
58    /// A `tokio::task::spawn_blocking` join failed.
59    #[error("Join error: {0}")]
60    Join(#[from] tokio::task::JoinError),
61}
62
63impl GitError {
64    /// Returns whether a failed command reports Git index-lock contention.
65    ///
66    /// This identifies the lock failure without implying that the lock is
67    /// stale or safe to remove.
68    #[must_use]
69    pub fn is_index_locked(&self) -> bool {
70        matches!(self, Self::CommandFailed { stderr, .. } if rebase::is_git_index_lock_error(stderr))
71    }
72}
73
74#[cfg(test)]
75#[path = "error_test.rs"]
76mod tests;