1- //! Git operations using system git commands for maximum compatibility
1+ //! Git operations for pull request workflows
2+ //!
3+ //! This module contains git operations that are commonly used in pull request
4+ //! workflows, including checking for changes, creating branches, staging and
5+ //! committing changes, and pushing branches to remotes.
6+ //!
7+ //! ## Typical PR Workflow
8+ //!
9+ //! 1. [`has_changes`] - Check if repository has uncommitted changes
10+ //! 2. [`create_and_checkout_branch`] - Create and switch to a new branch
11+ //! 3. [`add_all_changes`] - Stage all changes for commit
12+ //! 4. [`commit_changes`] - Commit the staged changes with a message
13+ //! 5. [`push_branch`] - Push the branch to the remote repository
14+ //!
15+ //! ## Additional Utilities
16+ //!
17+ //! - [`get_default_branch`] - Determine the repository's default branch
218
3- use crate :: config:: Repository ;
419use anyhow:: { Context , Result } ;
5- use colored:: * ;
6- use std:: path:: Path ;
720use std:: process:: Command ;
821
9- #[ derive( Default ) ]
10- pub struct Logger ;
11-
12- impl Logger {
13- pub fn info ( & self , repo : & Repository , msg : & str ) {
14- println ! ( "{} | {}" , repo. name. cyan( ) . bold( ) , msg) ;
15- }
16-
17- pub fn success ( & self , repo : & Repository , msg : & str ) {
18- println ! ( "{} | {}" , repo. name. cyan( ) . bold( ) , msg. green( ) ) ;
19- }
20-
21- pub fn warn ( & self , repo : & Repository , msg : & str ) {
22- println ! ( "{} | {}" , repo. name. cyan( ) . bold( ) , msg. yellow( ) ) ;
23- }
24-
25- #[ allow( dead_code) ]
26- pub fn error ( & self , repo : & Repository , msg : & str ) {
27- eprintln ! ( "{} | {}" , repo. name. cyan( ) . bold( ) , msg. red( ) ) ;
28- }
29- }
30-
31- pub fn clone_repository ( repo : & Repository ) -> Result < ( ) > {
32- let logger = Logger ;
33- let target_dir = repo. get_target_dir ( ) ;
34-
35- // Check if directory already exists
36- if Path :: new ( & target_dir) . exists ( ) {
37- logger. warn ( repo, "Repository directory already exists, skipping" ) ;
38- return Ok ( ( ) ) ;
39- }
40-
41- let mut args = vec ! [ "clone" ] ;
42-
43- // Add branch flag if a branch is specified
44- if let Some ( branch) = & repo. branch {
45- args. extend_from_slice ( & [ "-b" , branch] ) ;
46- logger. info (
47- repo,
48- & format ! ( "Cloning branch '{}' from {}" , branch, repo. url) ,
49- ) ;
50- } else {
51- logger. info ( repo, & format ! ( "Cloning default branch from {}" , repo. url) ) ;
52- }
53-
54- // Add repository URL and target directory
55- args. push ( & repo. url ) ;
56- args. push ( & target_dir) ;
57-
58- let output = Command :: new ( "git" )
59- . args ( & args)
60- . output ( )
61- . context ( "Failed to execute git clone command" ) ?;
62-
63- if !output. status . success ( ) {
64- let stderr = String :: from_utf8_lossy ( & output. stderr ) ;
65- anyhow:: bail!( "Failed to clone repository: {}" , stderr) ;
66- }
67-
68- logger. success ( repo, "Successfully cloned" ) ;
69- Ok ( ( ) )
70- }
71-
72- pub fn remove_repository ( repo : & Repository ) -> Result < ( ) > {
73- let target_dir = repo. get_target_dir ( ) ;
74-
75- if Path :: new ( & target_dir) . exists ( ) {
76- std:: fs:: remove_dir_all ( & target_dir) . context ( "Failed to remove repository directory" ) ?;
77- Ok ( ( ) )
78- } else {
79- anyhow:: bail!( "Repository directory does not exist: {}" , target_dir) ;
80- }
81- }
82-
22+ /// Check if a repository has uncommitted changes
8323pub fn has_changes ( repo_path : & str ) -> Result < bool > {
8424 // Check if there are any uncommitted changes using git status
8525 let output = Command :: new ( "git" )
@@ -100,6 +40,7 @@ pub fn has_changes(repo_path: &str) -> Result<bool> {
10040 Ok ( !output. stdout . is_empty ( ) )
10141}
10242
43+ /// Create and checkout a new branch
10344pub fn create_and_checkout_branch ( repo_path : & str , branch_name : & str ) -> Result < ( ) > {
10445 // Create and checkout a new branch using git checkout -b
10546 let output = Command :: new ( "git" )
@@ -121,6 +62,7 @@ pub fn create_and_checkout_branch(repo_path: &str, branch_name: &str) -> Result<
12162 Ok ( ( ) )
12263}
12364
65+ /// Add all changes to the staging area
12466pub fn add_all_changes ( repo_path : & str ) -> Result < ( ) > {
12567 // Add all changes using git add .
12668 let output = Command :: new ( "git" )
@@ -140,6 +82,7 @@ pub fn add_all_changes(repo_path: &str) -> Result<()> {
14082 Ok ( ( ) )
14183}
14284
85+ /// Commit staged changes with a message
14386pub fn commit_changes ( repo_path : & str , message : & str ) -> Result < ( ) > {
14487 // Commit changes using git commit
14588 let output = Command :: new ( "git" )
@@ -160,6 +103,7 @@ pub fn commit_changes(repo_path: &str, message: &str) -> Result<()> {
160103 Ok ( ( ) )
161104}
162105
106+ /// Push a branch to remote and set upstream
163107pub fn push_branch ( repo_path : & str , branch_name : & str ) -> Result < ( ) > {
164108 // Push branch using git push
165109 let output = Command :: new ( "git" )
@@ -181,6 +125,7 @@ pub fn push_branch(repo_path: &str, branch_name: &str) -> Result<()> {
181125 Ok ( ( ) )
182126}
183127
128+ /// Get the default branch of a repository
184129pub fn get_default_branch ( repo_path : & str ) -> Result < String > {
185130 // Try to get the default branch using git symbolic-ref
186131 let output = Command :: new ( "git" )
0 commit comments