...
This commit is contained in:
597
src/git/git.rs
Normal file
597
src/git/git.rs
Normal file
@@ -0,0 +1,597 @@
|
||||
use std::process::Command;
|
||||
use std::path::Path;
|
||||
use std::fs;
|
||||
use std::env;
|
||||
use regex::Regex;
|
||||
use std::fmt;
|
||||
use std::error::Error;
|
||||
|
||||
// Define a custom error type for git operations
|
||||
#[derive(Debug)]
|
||||
pub enum GitError {
|
||||
GitNotInstalled(std::io::Error),
|
||||
InvalidUrl(String),
|
||||
HomeDirectoryNotFound(std::env::VarError),
|
||||
FileSystemError(std::io::Error),
|
||||
GitCommandFailed(String),
|
||||
CommandExecutionError(std::io::Error),
|
||||
NoRepositoriesFound,
|
||||
RepositoryNotFound(String),
|
||||
MultipleRepositoriesFound(String, usize),
|
||||
NotAGitRepository(String),
|
||||
LocalChangesExist(String),
|
||||
}
|
||||
|
||||
// Implement Display for GitError
|
||||
impl fmt::Display for GitError {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
match self {
|
||||
GitError::GitNotInstalled(e) => write!(f, "Git is not installed: {}", e),
|
||||
GitError::InvalidUrl(url) => write!(f, "Could not parse git URL: {}", url),
|
||||
GitError::HomeDirectoryNotFound(e) => write!(f, "Could not determine home directory: {}", e),
|
||||
GitError::FileSystemError(e) => write!(f, "Error creating directory structure: {}", e),
|
||||
GitError::GitCommandFailed(e) => write!(f, "{}", e),
|
||||
GitError::CommandExecutionError(e) => write!(f, "Error executing command: {}", e),
|
||||
GitError::NoRepositoriesFound => write!(f, "No repositories found"),
|
||||
GitError::RepositoryNotFound(pattern) => write!(f, "No repositories found matching '{}'", pattern),
|
||||
GitError::MultipleRepositoriesFound(pattern, count) =>
|
||||
write!(f, "Multiple repositories ({}) found matching '{}'. Use '*' suffix for multiple matches.", count, pattern),
|
||||
GitError::NotAGitRepository(path) => write!(f, "Not a git repository at {}", path),
|
||||
GitError::LocalChangesExist(path) => write!(f, "Repository at {} has local changes", path),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Implement Error trait for GitError
|
||||
impl Error for GitError {
|
||||
fn source(&self) -> Option<&(dyn Error + 'static)> {
|
||||
match self {
|
||||
GitError::GitNotInstalled(e) => Some(e),
|
||||
GitError::HomeDirectoryNotFound(e) => Some(e),
|
||||
GitError::FileSystemError(e) => Some(e),
|
||||
GitError::CommandExecutionError(e) => Some(e),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Git utility functions
|
||||
|
||||
/**
|
||||
* Clones a git repository to a standardized location in the user's home directory.
|
||||
*
|
||||
* # Arguments
|
||||
*
|
||||
* * `url` - The URL of the git repository to clone. Can be in HTTPS format
|
||||
* (https://github.com/username/repo.git) or SSH format (git@github.com:username/repo.git).
|
||||
*
|
||||
* # Returns
|
||||
*
|
||||
* * `Ok(String)` - The path where the repository was cloned, formatted as
|
||||
* ~/code/server/account/repo (e.g., ~/code/github.com/username/repo).
|
||||
* * `Err(GitError)` - An error if the clone operation failed.
|
||||
*
|
||||
* # Examples
|
||||
*
|
||||
* ```
|
||||
* let repo_path = git_clone("https://github.com/username/repo.git")?;
|
||||
* println!("Repository cloned to: {}", repo_path);
|
||||
* ```
|
||||
*/
|
||||
pub fn git_clone(url: &str) -> Result<String, GitError> {
|
||||
// Check if git is installed
|
||||
let git_check = Command::new("git")
|
||||
.arg("--version")
|
||||
.output()
|
||||
.map_err(GitError::GitNotInstalled)?;
|
||||
|
||||
// Parse the URL to determine the clone path
|
||||
let (server, account, repo) = parse_git_url(url);
|
||||
if server.is_empty() || account.is_empty() || repo.is_empty() {
|
||||
return Err(GitError::InvalidUrl(url.to_string()));
|
||||
}
|
||||
|
||||
// Create the target directory
|
||||
let home_dir = env::var("HOME").map_err(GitError::HomeDirectoryNotFound)?;
|
||||
|
||||
let clone_path = format!("{}/code/{}/{}/{}", home_dir, server, account, repo);
|
||||
let clone_dir = Path::new(&clone_path);
|
||||
|
||||
// Check if repo already exists
|
||||
if clone_dir.exists() {
|
||||
return Ok(format!("Repository already exists at {}", clone_path));
|
||||
}
|
||||
|
||||
// Create parent directory
|
||||
if let Some(parent) = clone_dir.parent() {
|
||||
fs::create_dir_all(parent).map_err(GitError::FileSystemError)?;
|
||||
}
|
||||
|
||||
// Clone the repository
|
||||
let output = Command::new("git")
|
||||
.args(&["clone", "--depth", "1", url, &clone_path])
|
||||
.output()
|
||||
.map_err(GitError::CommandExecutionError)?;
|
||||
|
||||
if output.status.success() {
|
||||
Ok(clone_path)
|
||||
} else {
|
||||
let error = String::from_utf8_lossy(&output.stderr);
|
||||
Err(GitError::GitCommandFailed(format!("Git clone error: {}", error)))
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses a git URL to extract the server, account, and repository name.
|
||||
*
|
||||
* # Arguments
|
||||
*
|
||||
* * `url` - The URL of the git repository to parse. Can be in HTTPS format
|
||||
* (https://github.com/username/repo.git) or SSH format (git@github.com:username/repo.git).
|
||||
*
|
||||
* # Returns
|
||||
*
|
||||
* A tuple containing:
|
||||
* * `server` - The server name (e.g., "github.com")
|
||||
* * `account` - The account or organization name (e.g., "username")
|
||||
* * `repo` - The repository name (e.g., "repo")
|
||||
*
|
||||
* If the URL cannot be parsed, all three values will be empty strings.
|
||||
*
|
||||
* # Examples
|
||||
*
|
||||
* ```
|
||||
* let (server, account, repo) = parse_git_url("https://github.com/username/repo.git");
|
||||
* assert_eq!(server, "github.com");
|
||||
* assert_eq!(account, "username");
|
||||
* assert_eq!(repo, "repo");
|
||||
* ```
|
||||
*/
|
||||
pub fn parse_git_url(url: &str) -> (String, String, String) {
|
||||
// HTTP(S) URL format: https://github.com/username/repo.git
|
||||
let https_re = Regex::new(r"https?://([^/]+)/([^/]+)/([^/\.]+)(?:\.git)?").unwrap();
|
||||
|
||||
// SSH URL format: git@github.com:username/repo.git
|
||||
let ssh_re = Regex::new(r"git@([^:]+):([^/]+)/([^/\.]+)(?:\.git)?").unwrap();
|
||||
|
||||
if let Some(caps) = https_re.captures(url) {
|
||||
let server = caps.get(1).map_or("", |m| m.as_str()).to_string();
|
||||
let account = caps.get(2).map_or("", |m| m.as_str()).to_string();
|
||||
let repo = caps.get(3).map_or("", |m| m.as_str()).to_string();
|
||||
|
||||
return (server, account, repo);
|
||||
} else if let Some(caps) = ssh_re.captures(url) {
|
||||
let server = caps.get(1).map_or("", |m| m.as_str()).to_string();
|
||||
let account = caps.get(2).map_or("", |m| m.as_str()).to_string();
|
||||
let repo = caps.get(3).map_or("", |m| m.as_str()).to_string();
|
||||
|
||||
return (server, account, repo);
|
||||
}
|
||||
|
||||
(String::new(), String::new(), String::new())
|
||||
}
|
||||
|
||||
/**
|
||||
* Lists all git repositories found in the user's ~/code directory.
|
||||
*
|
||||
* This function searches for directories containing a .git subdirectory,
|
||||
* which indicates a git repository.
|
||||
*
|
||||
* # Returns
|
||||
*
|
||||
* * `Ok(Vec<String>)` - A vector of paths to git repositories
|
||||
* * `Err(GitError)` - An error if the operation failed
|
||||
*
|
||||
* # Examples
|
||||
*
|
||||
* ```
|
||||
* let repos = git_list()?;
|
||||
* for repo in repos {
|
||||
* println!("Found repository: {}", repo);
|
||||
* }
|
||||
* ```
|
||||
*/
|
||||
pub fn git_list() -> Result<Vec<String>, GitError> {
|
||||
// Get home directory
|
||||
let home_dir = env::var("HOME").map_err(GitError::HomeDirectoryNotFound)?;
|
||||
|
||||
let code_dir = format!("{}/code", home_dir);
|
||||
let code_path = Path::new(&code_dir);
|
||||
|
||||
if !code_path.exists() || !code_path.is_dir() {
|
||||
return Ok(Vec::new());
|
||||
}
|
||||
|
||||
let mut repos = Vec::new();
|
||||
|
||||
// Find all directories with .git subdirectories
|
||||
let output = Command::new("find")
|
||||
.args(&[&code_dir, "-type", "d", "-name", ".git"])
|
||||
.output()
|
||||
.map_err(GitError::CommandExecutionError)?;
|
||||
|
||||
if output.status.success() {
|
||||
let stdout = String::from_utf8_lossy(&output.stdout);
|
||||
for line in stdout.lines() {
|
||||
// Get the parent directory of .git which is the repo root
|
||||
if let Some(parent) = Path::new(line).parent() {
|
||||
if let Some(path_str) = parent.to_str() {
|
||||
repos.push(path_str.to_string());
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
let error = String::from_utf8_lossy(&output.stderr);
|
||||
return Err(GitError::GitCommandFailed(format!("Failed to find git repositories: {}", error)));
|
||||
}
|
||||
|
||||
Ok(repos)
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if a git repository has uncommitted changes.
|
||||
*
|
||||
* # Arguments
|
||||
*
|
||||
* * `repo_path` - The path to the git repository
|
||||
*
|
||||
* # Returns
|
||||
*
|
||||
* * `Ok(bool)` - True if the repository has uncommitted changes, false otherwise
|
||||
* * `Err(GitError)` - An error if the operation failed
|
||||
*
|
||||
* # Examples
|
||||
*
|
||||
* ```
|
||||
* if has_git_changes("/path/to/repo")? {
|
||||
* println!("Repository has uncommitted changes");
|
||||
* } else {
|
||||
* println!("Repository is clean");
|
||||
* }
|
||||
* ```
|
||||
*/
|
||||
pub fn has_git_changes(repo_path: &str) -> Result<bool, GitError> {
|
||||
let output = Command::new("git")
|
||||
.args(&["-C", repo_path, "status", "--porcelain"])
|
||||
.output()
|
||||
.map_err(GitError::CommandExecutionError)?;
|
||||
|
||||
Ok(!output.stdout.is_empty())
|
||||
}
|
||||
|
||||
/**
|
||||
* Finds repositories matching a pattern or partial path.
|
||||
*
|
||||
* # Arguments
|
||||
*
|
||||
* * `pattern` - The pattern to match against repository paths
|
||||
* - If the pattern ends with '*', all matching repositories are returned
|
||||
* - Otherwise, exactly one matching repository must be found
|
||||
*
|
||||
* # Returns
|
||||
*
|
||||
* * `Ok(Vec<String>)` - A vector of paths to matching repositories
|
||||
* * `Err(GitError)` - An error if no matching repositories are found,
|
||||
* or if multiple repositories match a non-wildcard pattern
|
||||
*
|
||||
* # Examples
|
||||
*
|
||||
* ```
|
||||
* // Find all repositories containing "project"
|
||||
* let repos = find_matching_repos("project*")?;
|
||||
*
|
||||
* // Find exactly one repository containing "unique-project"
|
||||
* let repo = find_matching_repos("unique-project")?[0];
|
||||
* ```
|
||||
*/
|
||||
pub fn find_matching_repos(pattern: &str) -> Result<Vec<String>, GitError> {
|
||||
// Get all repos
|
||||
let repos = git_list()?;
|
||||
|
||||
if repos.is_empty() {
|
||||
return Err(GitError::NoRepositoriesFound);
|
||||
}
|
||||
|
||||
// Check if pattern ends with wildcard
|
||||
if pattern.ends_with('*') {
|
||||
let search_pattern = &pattern[0..pattern.len()-1]; // Remove the *
|
||||
let matching: Vec<String> = repos.iter()
|
||||
.filter(|repo| repo.contains(search_pattern))
|
||||
.cloned()
|
||||
.collect();
|
||||
|
||||
if matching.is_empty() {
|
||||
return Err(GitError::RepositoryNotFound(pattern.to_string()));
|
||||
}
|
||||
|
||||
Ok(matching)
|
||||
} else {
|
||||
// No wildcard, need to find exactly one match
|
||||
let matching: Vec<String> = repos.iter()
|
||||
.filter(|repo| repo.contains(pattern))
|
||||
.cloned()
|
||||
.collect();
|
||||
|
||||
match matching.len() {
|
||||
0 => Err(GitError::RepositoryNotFound(pattern.to_string())),
|
||||
1 => Ok(matching),
|
||||
_ => Err(GitError::MultipleRepositoriesFound(pattern.to_string(), matching.len())),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates a git repository by pulling the latest changes.
|
||||
*
|
||||
* This function will fail if there are uncommitted changes in the repository.
|
||||
*
|
||||
* # Arguments
|
||||
*
|
||||
* * `repo_path` - The path to the git repository, or a partial path that uniquely identifies a repository
|
||||
*
|
||||
* # Returns
|
||||
*
|
||||
* * `Ok(String)` - A success message indicating the repository was updated
|
||||
* * `Err(GitError)` - An error if the update failed
|
||||
*
|
||||
* # Examples
|
||||
*
|
||||
* ```
|
||||
* let result = git_update("my-project")?;
|
||||
* println!("{}", result); // "Successfully updated repository at /home/user/code/github.com/user/my-project"
|
||||
* ```
|
||||
*/
|
||||
pub fn git_update(repo_path: &str) -> Result<String, GitError> {
|
||||
// If repo_path may be a partial path, find the matching repository
|
||||
let repos = find_matching_repos(repo_path)?;
|
||||
|
||||
// Should only be one repository at this point
|
||||
let actual_path = &repos[0];
|
||||
|
||||
// Check if repository exists and is a git repository
|
||||
let git_dir = Path::new(actual_path).join(".git");
|
||||
if !git_dir.exists() || !git_dir.is_dir() {
|
||||
return Err(GitError::NotAGitRepository(actual_path.clone()));
|
||||
}
|
||||
|
||||
// Check for local changes
|
||||
if has_git_changes(actual_path)? {
|
||||
return Err(GitError::LocalChangesExist(actual_path.clone()));
|
||||
}
|
||||
|
||||
// Pull the latest changes
|
||||
let output = Command::new("git")
|
||||
.args(&["-C", actual_path, "pull"])
|
||||
.output()
|
||||
.map_err(GitError::CommandExecutionError)?;
|
||||
|
||||
if output.status.success() {
|
||||
let stdout = String::from_utf8_lossy(&output.stdout);
|
||||
if stdout.contains("Already up to date") {
|
||||
Ok(format!("Repository already up to date at {}", actual_path))
|
||||
} else {
|
||||
Ok(format!("Successfully updated repository at {}", actual_path))
|
||||
}
|
||||
} else {
|
||||
let error = String::from_utf8_lossy(&output.stderr);
|
||||
Err(GitError::GitCommandFailed(format!("Git pull error: {}", error)))
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Force updates a git repository by discarding local changes and pulling the latest changes.
|
||||
*
|
||||
* This function will reset any uncommitted changes and clean untracked files before pulling.
|
||||
*
|
||||
* # Arguments
|
||||
*
|
||||
* * `repo_path` - The path to the git repository, or a partial path that uniquely identifies a repository
|
||||
*
|
||||
* # Returns
|
||||
*
|
||||
* * `Ok(String)` - A success message indicating the repository was force-updated
|
||||
* * `Err(GitError)` - An error if the update failed
|
||||
*
|
||||
* # Examples
|
||||
*
|
||||
* ```
|
||||
* let result = git_update_force("my-project")?;
|
||||
* println!("{}", result); // "Successfully force-updated repository at /home/user/code/github.com/user/my-project"
|
||||
* ```
|
||||
*/
|
||||
pub fn git_update_force(repo_path: &str) -> Result<String, GitError> {
|
||||
// If repo_path may be a partial path, find the matching repository
|
||||
let repos = find_matching_repos(repo_path)?;
|
||||
|
||||
// Should only be one repository at this point
|
||||
let actual_path = &repos[0];
|
||||
|
||||
// Check if repository exists and is a git repository
|
||||
let git_dir = Path::new(actual_path).join(".git");
|
||||
if !git_dir.exists() || !git_dir.is_dir() {
|
||||
return Err(GitError::NotAGitRepository(actual_path.clone()));
|
||||
}
|
||||
|
||||
// Reset any local changes
|
||||
let reset_output = Command::new("git")
|
||||
.args(&["-C", actual_path, "reset", "--hard", "HEAD"])
|
||||
.output()
|
||||
.map_err(GitError::CommandExecutionError)?;
|
||||
|
||||
if !reset_output.status.success() {
|
||||
let error = String::from_utf8_lossy(&reset_output.stderr);
|
||||
return Err(GitError::GitCommandFailed(format!("Git reset error: {}", error)));
|
||||
}
|
||||
|
||||
// Clean untracked files
|
||||
let clean_output = Command::new("git")
|
||||
.args(&["-C", actual_path, "clean", "-fd"])
|
||||
.output()
|
||||
.map_err(GitError::CommandExecutionError)?;
|
||||
|
||||
if !clean_output.status.success() {
|
||||
let error = String::from_utf8_lossy(&clean_output.stderr);
|
||||
return Err(GitError::GitCommandFailed(format!("Git clean error: {}", error)));
|
||||
}
|
||||
|
||||
// Pull the latest changes
|
||||
let pull_output = Command::new("git")
|
||||
.args(&["-C", actual_path, "pull"])
|
||||
.output()
|
||||
.map_err(GitError::CommandExecutionError)?;
|
||||
|
||||
if pull_output.status.success() {
|
||||
Ok(format!("Successfully force-updated repository at {}", actual_path))
|
||||
} else {
|
||||
let error = String::from_utf8_lossy(&pull_output.stderr);
|
||||
Err(GitError::GitCommandFailed(format!("Git pull error: {}", error)))
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Commits changes in a git repository and then updates it by pulling the latest changes.
|
||||
*
|
||||
* # Arguments
|
||||
*
|
||||
* * `repo_path` - The path to the git repository, or a partial path that uniquely identifies a repository
|
||||
* * `message` - The commit message
|
||||
*
|
||||
* # Returns
|
||||
*
|
||||
* * `Ok(String)` - A success message indicating the repository was committed and updated
|
||||
* * `Err(GitError)` - An error if the operation failed
|
||||
*
|
||||
* # Examples
|
||||
*
|
||||
* ```
|
||||
* let result = git_update_commit("my-project", "Fix bug in login form")?;
|
||||
* println!("{}", result); // "Successfully committed and updated repository at /home/user/code/github.com/user/my-project"
|
||||
* ```
|
||||
*/
|
||||
pub fn git_update_commit(repo_path: &str, message: &str) -> Result<String, GitError> {
|
||||
// If repo_path may be a partial path, find the matching repository
|
||||
let repos = find_matching_repos(repo_path)?;
|
||||
|
||||
// Should only be one repository at this point
|
||||
let actual_path = &repos[0];
|
||||
|
||||
// Check if repository exists and is a git repository
|
||||
let git_dir = Path::new(actual_path).join(".git");
|
||||
if !git_dir.exists() || !git_dir.is_dir() {
|
||||
return Err(GitError::NotAGitRepository(actual_path.clone()));
|
||||
}
|
||||
|
||||
// Check for local changes
|
||||
if !has_git_changes(actual_path)? {
|
||||
return Ok(format!("No changes to commit in repository at {}", actual_path));
|
||||
}
|
||||
|
||||
// Add all changes
|
||||
let add_output = Command::new("git")
|
||||
.args(&["-C", actual_path, "add", "."])
|
||||
.output()
|
||||
.map_err(GitError::CommandExecutionError)?;
|
||||
|
||||
if !add_output.status.success() {
|
||||
let error = String::from_utf8_lossy(&add_output.stderr);
|
||||
return Err(GitError::GitCommandFailed(format!("Git add error: {}", error)));
|
||||
}
|
||||
|
||||
// Commit the changes
|
||||
let commit_output = Command::new("git")
|
||||
.args(&["-C", actual_path, "commit", "-m", message])
|
||||
.output()
|
||||
.map_err(GitError::CommandExecutionError)?;
|
||||
|
||||
if !commit_output.status.success() {
|
||||
let error = String::from_utf8_lossy(&commit_output.stderr);
|
||||
return Err(GitError::GitCommandFailed(format!("Git commit error: {}", error)));
|
||||
}
|
||||
|
||||
// Pull the latest changes
|
||||
let pull_output = Command::new("git")
|
||||
.args(&["-C", actual_path, "pull"])
|
||||
.output()
|
||||
.map_err(GitError::CommandExecutionError)?;
|
||||
|
||||
if pull_output.status.success() {
|
||||
Ok(format!("Successfully committed and updated repository at {}", actual_path))
|
||||
} else {
|
||||
let error = String::from_utf8_lossy(&pull_output.stderr);
|
||||
Err(GitError::GitCommandFailed(format!("Git pull error: {}", error)))
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Commits changes in a git repository and pushes them to the remote.
|
||||
*
|
||||
* # Arguments
|
||||
*
|
||||
* * `repo_path` - The path to the git repository, or a partial path that uniquely identifies a repository
|
||||
* * `message` - The commit message
|
||||
*
|
||||
* # Returns
|
||||
*
|
||||
* * `Ok(String)` - A success message indicating the repository was committed and pushed
|
||||
* * `Err(GitError)` - An error if the operation failed
|
||||
*
|
||||
* # Examples
|
||||
*
|
||||
* ```
|
||||
* let result = git_update_commit_push("my-project", "Add new feature")?;
|
||||
* println!("{}", result); // "Successfully committed and pushed repository at /home/user/code/github.com/user/my-project"
|
||||
* ```
|
||||
*/
|
||||
pub fn git_update_commit_push(repo_path: &str, message: &str) -> Result<String, GitError> {
|
||||
// If repo_path may be a partial path, find the matching repository
|
||||
let repos = find_matching_repos(repo_path)?;
|
||||
|
||||
// Should only be one repository at this point
|
||||
let actual_path = &repos[0];
|
||||
|
||||
// Check if repository exists and is a git repository
|
||||
let git_dir = Path::new(actual_path).join(".git");
|
||||
if !git_dir.exists() || !git_dir.is_dir() {
|
||||
return Err(GitError::NotAGitRepository(actual_path.clone()));
|
||||
}
|
||||
|
||||
// Check for local changes
|
||||
if !has_git_changes(actual_path)? {
|
||||
return Ok(format!("No changes to commit in repository at {}", actual_path));
|
||||
}
|
||||
|
||||
// Add all changes
|
||||
let add_output = Command::new("git")
|
||||
.args(&["-C", actual_path, "add", "."])
|
||||
.output()
|
||||
.map_err(GitError::CommandExecutionError)?;
|
||||
|
||||
if !add_output.status.success() {
|
||||
let error = String::from_utf8_lossy(&add_output.stderr);
|
||||
return Err(GitError::GitCommandFailed(format!("Git add error: {}", error)));
|
||||
}
|
||||
|
||||
// Commit the changes
|
||||
let commit_output = Command::new("git")
|
||||
.args(&["-C", actual_path, "commit", "-m", message])
|
||||
.output()
|
||||
.map_err(GitError::CommandExecutionError)?;
|
||||
|
||||
if !commit_output.status.success() {
|
||||
let error = String::from_utf8_lossy(&commit_output.stderr);
|
||||
return Err(GitError::GitCommandFailed(format!("Git commit error: {}", error)));
|
||||
}
|
||||
|
||||
// Push the changes
|
||||
let push_output = Command::new("git")
|
||||
.args(&["-C", actual_path, "push"])
|
||||
.output()
|
||||
.map_err(GitError::CommandExecutionError)?;
|
||||
|
||||
if push_output.status.success() {
|
||||
Ok(format!("Successfully committed and pushed repository at {}", actual_path))
|
||||
} else {
|
||||
let error = String::from_utf8_lossy(&push_output.stderr);
|
||||
Err(GitError::GitCommandFailed(format!("Git push error: {}", error)))
|
||||
}
|
||||
}
|
7
src/git/git_executor.rs
Normal file
7
src/git/git_executor.rs
Normal file
@@ -0,0 +1,7 @@
|
||||
use std::process::Command;
|
||||
use std::path::Path;
|
||||
use std::fs;
|
||||
use std::env;
|
||||
use regex::Regex;
|
||||
use std::fmt;
|
||||
use std::error::Error;
|
12
src/git/instructions.md
Normal file
12
src/git/instructions.md
Normal file
@@ -0,0 +1,12 @@
|
||||
in @/sal/git/git_executor.rs
|
||||
create a GitExecutor which is the one executing git commands
|
||||
and also checking if ssh-agent is loaded
|
||||
or if there is an authentication mechanism defined in redis
|
||||
|
||||
check if there is a redis on ~/hero/var/redis.sock
|
||||
|
||||
over unix domani socket
|
||||
|
||||
if yes then check there is an entry on
|
||||
|
||||
sal::git::
|
2
src/git/mod.rs
Normal file
2
src/git/mod.rs
Normal file
@@ -0,0 +1,2 @@
|
||||
use git::*;
|
||||
use git_executor::*;
|
Reference in New Issue
Block a user