Mountain/Environment/SourceControlManagementProvider.rs
1//! # SourceControlManagementProvider (Environment)
2//!
3//! Implements the `SourceControlManagementProvider` trait for
4//! `MountainEnvironment`, providing Git and other source control management
5//! (SCM) capabilities to the application.
6//!
7//! ## RESPONSIBILITIES
8//!
9//! ### 1. Repository Detection
10//! - Scan workspace folders for Git repositories
11//! - Detect repository root directories
12//! - Track repository state (clean, dirty, branching)
13//! - Monitor repository changes (commit, checkout, merge)
14//!
15//! ### 2. SCM Providers
16//! - Create and manage `SourceControlManagementProvider` instances
17//! - Support multiple SCM systems (Git, Mercurial, etc.)
18//! - Load extension-provided SCM providers
19//! - Route SCM operations to appropriate provider
20//!
21//! ### 3. Change Management
22//! - Track file changes (modified, added, deleted, renamed)
23//! - Provide diff information for changed files
24//! - Support staging and unstaging changes
25//! - Handle merge conflicts
26//!
27//! ### 4. Authentication
28//! - Manage SCM credentials and authentication
29//! - Support SSH keys and HTTPS tokens
30//! - Store credentials securely via `SecretProvider`
31//! - Handle authentication failures and prompts
32//!
33//! ### 5. Operations
34//! - Commit, push, pull, fetch operations
35//! - Branch management (create, delete, rename, checkout)
36//! - Merge and rebase operations
37//! - Remote management (add, remove, rename)
38//!
39//! ## ARCHITECTURAL ROLE
40//!
41//! SourceControlManagementProvider is the **SCM integration layer**:
42//!
43//! ```text
44//! UI (SCM View) ──► SourceControlManagementProvider ──► Git CLI / Libgit2
45//! │
46//! └─► Extension SCM Providers
47//! ```
48//!
49//! ### Position in Mountain
50//! - `Environment` module: SCM capability provider
51//! - Implements
52//! `CommonLibrary::SourceControlManagement::SourceControlManagementProvider`
53//! trait
54//! - Accessible via `Environment.Require<dyn
55//! SourceControlManagementProvider>()`
56//!
57//! ### SCM Provider Hierarchy
58//! - **Built-in Git Provider**: Native Git implementation (preferred)
59//! - **Extension Providers**: Custom SCM support (Mercurial, SVN, etc.)
60//! - **Fallback Providers**: Basic functionality for unknown SCM types
61//!
62//! ### Dependencies
63//! - `SecretProvider`: For storing SCM credentials
64//! - `FileSystemReader` / `FileSystemWriter`: For .git operations
65//! - `Log`: SCM operation logging
66//! - External Git binary or libgit2 library
67//!
68//! ### Dependents
69//! - SCM UI view: Display repository state and changes
70//! - Source control commands: Commit, push, pull, etc.
71//! - `Binary::Main`::`MountainGetWorkbenchConfiguration`: SCM state
72//! - Extension SCM providers: Custom SCM implementations
73//!
74//! ## DATA MODEL
75//!
76//! Stored in `ApplicationState`:
77//! - `SourceControlManagementProviders`: Registered providers by ID
78//! - `SourceControlManagementGroups`: Repository groups (by workspace)
79//! - `SourceControlManagementResources`: Resource state (changed files)
80//!
81//! Key structures:
82//! - `SourceControlManagementProviderDTO`: Provider metadata
83//! - `SourceControlManagementGroupDTO`: Repository group state
84//! - `SourceControlManagementResourceDTO`: Changed file information
85//!
86//! ## REPOSITORY STATES
87//!
88//! - **Clean**: No uncommitted changes
89//! - **Dirty**: Unstaged changes present
90//! - **Staged**: Changes staged for commit
91//! - **Merging**: Merge in progress
92//! - **Rebasing**: Rebase in progress
93//! - **Cherry-picking**: Cherry-pick in progress
94//!
95//! ## ERROR HANDLING
96//!
97//! - Repository not found: `CommonError::SCMNotFound`
98//! - Authentication failure: `CommonError::SCMAuthenticationFailed`
99//! - Operation failure: `CommonError::SCMOperationFailed`
100//! - Merge conflict: `CommonError::SCMConflict`
101//! - Uncommitted changes: `CommonError::SCMUncommittedChanges`
102//!
103//! ## PERFORMANCE
104//!
105//! - Repository scanning should be async and cached
106//! - Use file system watchers to detect changes
107//! - Batch operations when possible (e.g., status of multiple files)
108//! - Consider background indexing for large repositories
109//!
110//! ## VS CODE REFERENCE
111//!
112//! Patterns from VS Code:
113//! - `vs/workbench/services/scm/common/scmService.ts` - SCM service
114//! - `vs/platform/scm/common/scm.ts` - SCM provider interface
115//! - `vs/sourcecontrol/git/common/git.ts` - Git provider implementation
116//!
117//! ## TODO
118//!
119//! - [ ] Implement built-in Git provider using libgit2 or Git CLI
120//! - [ ] Add repository discovery and change detection
121//! - [ ] Support staging, unstaging, and committing changes
122//! - [ ] Implement branch management UI and operations
123//! - [ ] Add remote operations (push, pull, fetch)
124//! - [ ] Handle merge conflicts with UI resolution
125//! - [ ] Support Git LFS and submodules
126//! - [ ] Add SCM authentication and credential management
127//! - [ ] Implement SCM extensions API for custom providers
128//! - [ ] Add SCM history and blame views
129//! - [ ] Support stash and pop operations
130//! - [ ] Implement tag management
131//! - [ ] Add SCM configuration and settings
132//! - [ ] Support detached HEAD and bisect operations
133//! - [ ] Implement SCM telemetry and diagnostics
134//!
135//! ## MODULE CONTENTS
136//!
137//! - [`SourceControlManagementProvider`]: Main struct implementing the trait
138//! - Repository detection and tracking
139//! - Provider registration and routing
140//! - SCM operation implementations
141//! - Authentication and credential management
142
143// `MountainEnvironment`. Responsibilities:
144// - Manage source control providers (e.g., Git, Mercurial, SVN).
145// - Handle SCM provider registration and disposal.
146// - Manage resource groups (e.g., changes, untracked, merge conflicts).
147// - Handle input boxes for user input (e.g., commit messages).
148// - Emit events to the Sky frontend for UI updates.
149// - Provide Git integration patterns for common operations.
150// - Handle conflict detection and resolution.
151// - Support multiple SCM providers simultaneously.
152//
153// TODOs:
154// - Implement complete Git integration (status, commit, push, pull, branch)
155// - Add Git diff display with visual comparison
156// - Implement merge conflict resolution UI
157// - Support Git staging/unstaging of resources
158// - Add Git stash operations
159// - Implement Git branch management (create, delete, checkout)
160// - Support Git remote operations
161// - Add Git history/log viewing
162// - Implement Git blame annotations
163// - Support Git submodules
164// - Implement Git LFS (Large File Storage) support
165// - Add Git tag management
166// - Custom implementation for Mercurial, SVN, and other VCS
167// - Implement SCM provider command registration
168// - Support SCM provider decoration (badges, colors)
169// - Add input box validation and validation messaging
170// - Implement resource state caching for performance
171// - Support SCM provider quick picks and menus
172// - Add keyboard shortcuts for common SCM operations
173// - Implement SCM provider extension points
174// - Support Git rebase and cherry-pick operations
175// - Add Git bisect support
176// - Implement Git commit graph visualization
177// - Support Git hooks integration
178// - Add Git signature verification
179// - Implement Git ignore management
180//
181// Inspired by VSCode's SCM service which:
182// - Provides a flexible abstraction over multiple source control systems
183// - Manages resource state changes through groups
184// - Supports provider-specific operations through commands
185// - Handles UI updates through event emission
186// - Manages input boxes for user interaction
187// - Git integration is the primary implementation with patterns for others
188//! # SourceControlManagementProvider Implementation
189//!
190//! Implements the `SourceControlManagementProvider` trait for the
191//! `MountainEnvironment`.
192//!
193//! ## SCM Provider Architecture
194//!
195//! Each SCM provider maintains:
196//! - **Handle**: Unique identifier for the provider
197//! - **Label**: User-friendly name (e.g., "Git")
198//! - **Root URI**: URI of the repository root
199//! - **Groups**: Resource groups organizing changed resources
200//! - **Input Box**: User input widget for operations (e.g., commit messages)
201//! - **Count**: Badge count for changed items
202//!
203//! ## Resource Groups
204//!
205//! Groups organize resources by their state:
206//! - **Changes**: Modified files ready to commit
207//! - **Untracked**: New files not yet tracked
208//! - **Staged**: Files staged for commit
209//! - **Merge Changes**: Files with merge conflicts
210//! - **Conflict Unresolved**: Unresolved conflict markers
211//
212//! ## SCM Lifecycle
213//!
214//! 1. **Create Provider**: Register a new SCM provider with handle and metadata
215//! 2. **Update Provider**: Update provider state (badge count, input box)
216//! 3. **Update Group**: Add or remove resources from groups
217//! 4. **Register Input Box**: Create input widget for user interaction
218//! 5. **Dispose Provider**: Remove provider and all associated state
219//
220//! ## Git Integration Patterns
221//!
222//! Typical Git provider workflow:
223//! - Detect Git repository via `.git` directory
224//! - Run `git status` to populate resource groups
225//! - Run `git diff` to provide file diffs
226//! - Use input box for commit messages
227//! - Show badge count for changed files
228//! - Provide commands: Stage, Unstage, Commit, Push, Pull, Discard
229
230use CommonLibrary::{
231 Error::CommonError::CommonError,
232 SourceControlManagement::{
233 DTO::{
234 SourceControlCreateDTO::SourceControlCreateDTO,
235 SourceControlGroupUpdateDTO::SourceControlGroupUpdateDTO,
236 SourceControlInputBoxDTO::SourceControlInputBoxDTO,
237 SourceControlManagementGroupDTO::SourceControlManagementGroupDTO,
238 SourceControlManagementProviderDTO::SourceControlManagementProviderDTO,
239 SourceControlUpdateDTO::SourceControlUpdateDTO,
240 },
241 SourceControlManagementProvider::SourceControlManagementProvider,
242 },
243};
244use async_trait::async_trait;
245use log::{info, warn};
246use serde_json::{Value, json};
247use tauri::Emitter;
248
249use super::{MountainEnvironment::MountainEnvironment, Utility};
250
251#[async_trait]
252impl SourceControlManagementProvider for MountainEnvironment {
253 async fn CreateSourceControl(&self, ProviderDataValue:Value) -> Result<u32, CommonError> {
254 let ProviderData:SourceControlCreateDTO = serde_json::from_value(ProviderDataValue)?;
255
256 let Handle = self.ApplicationState.GetNextSourceControlManagementProviderHandle();
257
258 info!(
259 "[SourceControlManagementProvider] Creating new SCM provider with handle {}",
260 Handle
261 );
262
263 let ProviderState = SourceControlManagementProviderDTO {
264 Handle,
265 Label:ProviderData.Label,
266 RootURI:Some(json!({ "external": ProviderData.RootUri.to_string() })),
267 CommitTemplate:None,
268 Count:None,
269 InputBox:None,
270 };
271
272 self.ApplicationState
273 .Feature
274 .Markers
275 .SourceControlManagementProviders
276 .lock()
277 .map_err(Utility::MapApplicationStateLockErrorToCommonError)?
278 .insert(Handle, ProviderState.clone());
279
280 self.ApplicationState
281 .Feature
282 .Markers
283 .SourceControlManagementGroups
284 .lock()
285 .map_err(Utility::MapApplicationStateLockErrorToCommonError)?
286 .insert(Handle, Default::default());
287
288 self.ApplicationHandle
289 .emit("sky://scm/provider/added", ProviderState)
290 .map_err(|Error| {
291 CommonError::UserInterfaceInteraction { Reason:format!("Failed to emit scm event: {}", Error) }
292 })?;
293
294 Ok(Handle)
295 }
296
297 async fn DisposeSourceControl(&self, ProviderHandle:u32) -> Result<(), CommonError> {
298 info!(
299 "[SourceControlManagementProvider] Disposing SCM provider with handle {}",
300 ProviderHandle
301 );
302
303 self.ApplicationState
304 .Feature
305 .Markers
306 .SourceControlManagementProviders
307 .lock()
308 .map_err(Utility::MapApplicationStateLockErrorToCommonError)?
309 .remove(&ProviderHandle);
310
311 self.ApplicationState
312 .Feature
313 .Markers
314 .SourceControlManagementGroups
315 .lock()
316 .map_err(Utility::MapApplicationStateLockErrorToCommonError)?
317 .remove(&ProviderHandle);
318
319 self.ApplicationHandle
320 .emit("sky://scm/provider/removed", ProviderHandle)
321 .map_err(|Error| CommonError::UserInterfaceInteraction { Reason:Error.to_string() })?;
322
323 Ok(())
324 }
325
326 async fn UpdateSourceControl(&self, ProviderHandle:u32, UpdateDataValue:Value) -> Result<(), CommonError> {
327 let UpdateData:SourceControlUpdateDTO = serde_json::from_value(UpdateDataValue)?;
328
329 info!("[SourceControlManagementProvider] Updating provider {}", ProviderHandle);
330
331 let mut ProvidersGuard = self
332 .ApplicationState
333 .Feature
334 .Markers
335 .SourceControlManagementProviders
336 .lock()
337 .map_err(Utility::MapApplicationStateLockErrorToCommonError)?;
338
339 if let Some(Provider) = ProvidersGuard.get_mut(&ProviderHandle) {
340 if let Some(count) = UpdateData.Count {
341 Provider.Count = Some(count);
342 }
343
344 if let Some(value) = UpdateData.InputBoxValue {
345 if let Some(input_box) = &mut Provider.InputBox {
346 input_box.Value = value;
347 }
348 }
349
350 let ProviderClone = Provider.clone();
351
352 // Release lock before emitting
353 drop(ProvidersGuard);
354
355 self.ApplicationHandle
356 .emit(
357 "sky://scm/provider/changed",
358 json!({ "handle": ProviderHandle, "provider": ProviderClone }),
359 )
360 .map_err(|Error| CommonError::UserInterfaceInteraction { Reason:Error.to_string() })?;
361 }
362
363 Ok(())
364 }
365
366 async fn UpdateSourceControlGroup(&self, ProviderHandle:u32, GroupDataValue:Value) -> Result<(), CommonError> {
367 let GroupData:SourceControlGroupUpdateDTO = serde_json::from_value(GroupDataValue)?;
368
369 info!(
370 "[SourceControlManagementProvider] Updating group '{}' for provider {}",
371 GroupData.GroupID, ProviderHandle
372 );
373
374 let mut GroupsGuard = self
375 .ApplicationState
376 .Feature
377 .Markers
378 .SourceControlManagementGroups
379 .lock()
380 .map_err(Utility::MapApplicationStateLockErrorToCommonError)?;
381
382 if let Some(ProviderGroups) = GroupsGuard.get_mut(&ProviderHandle) {
383 let Group = ProviderGroups.entry(GroupData.GroupID.clone()).or_insert_with(|| {
384 SourceControlManagementGroupDTO {
385 ProviderHandle,
386 Identifier:GroupData.GroupID.clone(),
387 Label:GroupData.Label.clone(),
388 }
389 });
390
391 Group.Label = GroupData.Label;
392
393 let GroupClone = Group.clone();
394
395 // Release lock before emitting
396 drop(GroupsGuard);
397
398 self.ApplicationHandle
399 .emit(
400 "sky://scm/group/changed",
401 json!({ "providerHandle": ProviderHandle, "group": GroupClone }),
402 )
403 .map_err(|Error| CommonError::UserInterfaceInteraction { Reason:Error.to_string() })?;
404 } else {
405 warn!(
406 "[SourceControlManagementProvider] Received group update for unknown provider handle: {}",
407 ProviderHandle
408 );
409 }
410
411 Ok(())
412 }
413
414 async fn RegisterInputBox(&self, ProviderHandle:u32, InputBoxDataValue:Value) -> Result<(), CommonError> {
415 let InputBoxData:SourceControlInputBoxDTO = serde_json::from_value(InputBoxDataValue)?;
416
417 info!(
418 "[SourceControlManagementProvider] Registering input box for provider {}",
419 ProviderHandle
420 );
421
422 let mut ProvidersGuard = self
423 .ApplicationState
424 .Feature
425 .Markers
426 .SourceControlManagementProviders
427 .lock()
428 .map_err(Utility::MapApplicationStateLockErrorToCommonError)?;
429
430 if let Some(Provider) = ProvidersGuard.get_mut(&ProviderHandle) {
431 Provider.InputBox = Some(InputBoxData);
432
433 let ProviderClone = Provider.clone();
434
435 // Release lock before emitting
436 drop(ProvidersGuard);
437
438 self.ApplicationHandle
439 .emit(
440 "sky://scm/provider/changed",
441 json!({ "handle": ProviderHandle, "provider": ProviderClone }),
442 )
443 .map_err(|Error| CommonError::UserInterfaceInteraction { Reason:Error.to_string() })?;
444 }
445
446 Ok(())
447 }
448}