use std::path::Path;
use git2::{Repository, IndexAddOption, Signature, Commit};
use serde::{Serialize, Deserialize};
#[derive(Debug, Serialize, Deserialize)]
pub struct GitLocal {
name: String,
email: String,
}
#[derive(Debug)]
pub enum Branch {
Main,
Development,
Feature(String),
}
impl Branch {
pub fn as_str(&self) -> String {
match self {
Branch::Main => "main".to_string(),
Branch::Development => "development".to_string(),
Branch::Feature(name) => format!("feature/{}", name),
}
}
}
impl GitLocal {
fn get_signature(repo: &Repository) -> anyhow::Result<Signature> {
let config = repo.config()?;
let name = config.get_string("user.name")?;
let email = config.get_string("user.email")?;
Ok(Signature::now(&name, &email)?)
}
pub fn init(path: &str) -> anyhow::Result<Repository> {
let repo = Repository::init(path)?;
{
let sig = Self::get_signature(&repo)?;
let tree_id = {
let mut index = repo.index()?;
index.write_tree()?
};
let tree = repo.find_tree(tree_id)?;
repo.commit(Some("HEAD"), &sig, &sig, "Initial empty commit", &tree, &[])?;
}
if let Ok(mut master_branch) = repo.find_branch("master", git2::BranchType::Local) {
master_branch.rename("main", false)?;
}
Ok(repo)
}
pub fn remote_add_origin(repo_path: &str, url: &str) -> anyhow::Result<()> {
let repo = Repository::open(Path::new(repo_path))?;
repo.remote("origin", url)?;
Ok(())
}
pub fn add_all(path: &str) -> anyhow::Result<()> {
let repo = Repository::open(Path::new(path))?;
let mut index = repo.index()?;
index.add_all(["*"], IndexAddOption::DEFAULT, None)?;
index.write()?;
Ok(())
}
pub fn commit(path: &str, message: &str) -> anyhow::Result<()> {
let repo = Repository::open(Path::new(path))?;
let mut index = repo.index()?;
let oid = index.write_tree()?;
let signature = Self::get_signature(&repo)?;
let tree = repo.find_tree(oid)?;
let parent_commit_result = repo.head().and_then(|head| head.peel_to_commit());
let parents: Vec<Commit> = match parent_commit_result {
Ok(parent_commit) => vec![parent_commit],
Err(_) => vec![], };
let parents_refs: Vec<&_> = parents.iter().collect();
repo.commit(
Some("HEAD"),
&signature,
&signature,
message,
&tree,
&parents_refs,
)?;
Ok(())
}
pub fn push(repo_path: &str, local_branch: Branch, remote_branch: Branch, token: &str) -> anyhow::Result<()> {
let repo = Repository::open(Path::new(repo_path))?;
let mut remote = repo.find_remote("origin")?;
let mut callbacks = git2::RemoteCallbacks::new();
callbacks.credentials(|_url, _username_from_url, _allowed_types| {
git2::Cred::userpass_plaintext(token, "")
});
let mut push_options = git2::PushOptions::new();
push_options.remote_callbacks(callbacks);
let refspec = format!(
"refs/heads/{}:refs/heads/{}",
local_branch.as_str(),
remote_branch.as_str()
);
remote.push(&[&refspec], Some(&mut push_options))?;
Ok(())
}
pub fn submodule_add(repo_path: &str, remote_url: &str, path: &str) -> anyhow::Result<()> {
let repo = Repository::open(Path::new(repo_path))?;
let mut submodule = repo.submodule(remote_url, Path::new(path), true)?;
let mut opts = git2::SubmoduleUpdateOptions::new();
submodule.add_finalize()?;
submodule.update(true, Some(&mut opts))?;
Ok(())
}
pub fn submodule_update(repo_path: &str) -> anyhow::Result<()> {
let repo = Repository::open(Path::new(repo_path))?;
let mut opts = git2::SubmoduleUpdateOptions::new();
for mut sub in repo.submodules()? {
sub.update(true, Some(&mut opts))?;
}
Ok(())
}
}