yarli_cli/yarli-git/src/
error.rs1use std::path::PathBuf;
7
8use thiserror::Error;
9
10use crate::yarli_core::fsm::merge::MergeState;
11use crate::yarli_core::fsm::worktree::WorktreeState;
12
13#[derive(Debug, Error)]
15pub enum GitError {
16 #[error("worktree creation failed: {reason}")]
19 WorktreeCreation { reason: String },
20
21 #[error("worktree path already exists: {path}")]
23 WorktreePathExists { path: PathBuf },
24
25 #[error("worktree not found: {path}")]
27 WorktreeNotFound { path: PathBuf },
28
29 #[error("mutations denied in worktree state {state:?}")]
32 MutationDenied { state: WorktreeState },
33
34 #[error("path escapes worktree root: {path} is outside {root}")]
36 PathConfinementViolation { path: PathBuf, root: PathBuf },
37
38 #[error("worktree is dirty: {path}")]
40 DirtyWorktree { path: PathBuf },
41
42 #[error("detached HEAD in worktree: {path}")]
44 DetachedHead { path: PathBuf },
45
46 #[error("invalid .git indirection in worktree: {path}")]
48 InvalidGitIndirection { path: PathBuf },
49
50 #[error("worktree cleanup blocked: {reason}")]
52 CleanupBlocked { reason: String },
53
54 #[error("merge precheck failed: {reason}")]
57 MergePrecheckFailed { reason: String },
58
59 #[error("target ref stale: expected {expected}, found {actual}")]
61 TargetRefStale { expected: String, actual: String },
62
63 #[error("merge conflicts detected: {count} file(s)")]
65 MergeConflict { count: usize },
66
67 #[error("git hook rejected merge: {hook} — {stderr}")]
71 HookRejected { hook: String, stderr: String },
72
73 #[error("merge lock unavailable for branch {branch}")]
75 MergeLockUnavailable { branch: String },
76
77 #[error("merge verification failed: {reason}")]
79 MergeVerifyFailed { reason: String },
80
81 #[error("invalid merge state for operation: {state:?}")]
83 InvalidMergeState { state: MergeState },
84
85 #[error("uninitialized submodule: {path}")]
88 UninitializedSubmodule { path: String },
89
90 #[error("dirty submodule: {path}")]
92 DirtySubmodule { path: String },
93
94 #[error("submodule policy violation at {path}: {reason}")]
96 SubmodulePolicyViolation { path: String, reason: String },
97
98 #[error("forbidden git operation: {operation}")]
101 ForbiddenOperation { operation: ForbiddenOp },
102
103 #[error("interrupted {operation} detected in {path}")]
106 InterruptedOperation {
107 operation: InterruptedOp,
108 path: PathBuf,
109 },
110
111 #[error("recovery failed: {reason}")]
113 RecoveryFailed { reason: String },
114
115 #[error("ref not found: {refspec}")]
118 RefNotFound { refspec: String },
119
120 #[error("branch already exists: {branch}")]
122 BranchAlreadyExists { branch: String },
123
124 #[error("git command failed (exit {exit_code}): {stderr}")]
127 CommandFailed { exit_code: i32, stderr: String },
128
129 #[error("io error: {0}")]
131 Io(#[from] std::io::Error),
132
133 #[error("transition error: {0}")]
135 Transition(#[from] crate::yarli_core::error::TransitionError),
136
137 #[error("exec error: {0}")]
139 Exec(#[from] crate::yarli_exec::error::ExecError),
140}
141
142#[derive(Debug, Clone, PartialEq, Eq)]
144pub enum ForbiddenOp {
145 Push,
147 ForcePush,
149 Tag,
151 BranchDelete { branch: String },
153 StashClear,
155 DestructiveCleanup { command: String },
157}
158
159impl std::fmt::Display for ForbiddenOp {
160 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
161 match self {
162 ForbiddenOp::Push => write!(f, "git push"),
163 ForbiddenOp::ForcePush => write!(f, "git push --force"),
164 ForbiddenOp::Tag => write!(f, "git tag"),
165 ForbiddenOp::BranchDelete { branch } => {
166 write!(f, "git branch -D {branch}")
167 }
168 ForbiddenOp::StashClear => write!(f, "git stash clear"),
169 ForbiddenOp::DestructiveCleanup { command } => write!(f, "{command}"),
170 }
171 }
172}
173
174#[derive(Debug, Clone, Copy, PartialEq, Eq)]
176pub enum InterruptedOp {
177 Merge,
179 Rebase,
181 CherryPick,
183 Revert,
185}
186
187impl std::fmt::Display for InterruptedOp {
188 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
189 match self {
190 InterruptedOp::Merge => write!(f, "merge"),
191 InterruptedOp::Rebase => write!(f, "rebase"),
192 InterruptedOp::CherryPick => write!(f, "cherry-pick"),
193 InterruptedOp::Revert => write!(f, "revert"),
194 }
195 }
196}
197
198#[derive(Debug, Clone, Copy, PartialEq, Eq)]
200pub enum RecoveryAction {
201 Abort,
203 Resume,
205 ManualBlock,
207}