Skip to content

Commit

Permalink
add commit subcommand and make message optonial in push. auth in push…
Browse files Browse the repository at this point in the history
… now also works
  • Loading branch information
Lunarequest committed Jan 27, 2024
1 parent 25a8a05 commit c358013
Show file tree
Hide file tree
Showing 7 changed files with 166 additions and 27 deletions.
16 changes: 14 additions & 2 deletions src/cli.rs
Original file line number Diff line number Diff line change
Expand Up @@ -24,15 +24,27 @@ pub enum Commands {
#[clap(help = "path to repo, defaults to repositry name")]
path: Option<PathBuf>,
},
#[clap(about = "commit without pushing")]
Commit {
#[clap(short = 'm', long = "message", help = "message for commit")]
message: String,
#[clap(help = "path to repo, optional defaults to current dir")]
path: Option<PathBuf>,
},
#[clap(about = "sync repo to home directory")]
Sync {
#[clap(help = "path to repo, optional defaults to current dir")]
path: Option<PathBuf>,
},
#[clap(about = "commit and push changes")]
Push {
#[clap(short = 'm', long = "message", help = "message for commit")]
message: String,
#[clap(
short = 'm',
long = "message",
help = "message for commit",
required = false
)]
message: Option<String>,
#[clap(help = "path to repo, optional defaults to current dir")]
path: Option<PathBuf>,
},
Expand Down
37 changes: 32 additions & 5 deletions src/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,10 +14,17 @@ pub struct Config {
#[derive(Debug, Deserialize)]
pub struct Programs {
os: Option<String>,
hostname: Option<String>,
hostname: Option<Hostname>,
folder: PathBuf,
}

#[derive(Debug, Deserialize)]
#[serde(untagged)]
enum Hostname {
Single(String),
Multiple(Vec<String>),
}

impl Config {
pub fn folders(self) -> Result<Vec<PathBuf>> {
let sys = match PlatformInfo::new() {
Expand All @@ -34,8 +41,17 @@ impl Config {
} else if program.os.is_none() {
let targethost = program.hostname;
if let Some(target) = targethost {
if current_hostname == target {
folders.append(&mut vec![program.folder]);
match target {
Hostname::Single(host) => {
if host == current_hostname {
folders.append(&mut vec![program.folder]);
}
}
Hostname::Multiple(hosts) => {
if hosts.contains(&current_hostname.to_string()) {
folders.append(&mut vec![program.folder]);
}
}
}
}
} else if program.hostname.is_none() {
Expand All @@ -50,8 +66,19 @@ impl Config {
let targethost = program.hostname;
if let Some(targetos) = targetos {
if let Some(target) = targethost {
if current_hostname == target && current_os == targetos {
folders.append(&mut vec![program.folder]);
if current_os == targetos {
match target {
Hostname::Single(host) => {
if host == current_hostname {
folders.append(&mut vec![program.folder]);
}
}
Hostname::Multiple(hosts) => {
if hosts.contains(&current_hostname.to_string()) {
folders.append(&mut vec![program.folder]);
}
}
}
}
}
}
Expand Down
50 changes: 49 additions & 1 deletion src/git/commit.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
use anyhow::{anyhow, Context as anyhowContext, Result};
use git2::{Commit, Config, ObjectType, Repository};
use git2::{Commit, Config, FetchOptions, ObjectType, RemoteCallbacks, Repository};
use git2_credentials::CredentialHandler;
use gpgme::Context;

fn find_last_commit(repo: &Repository) -> Result<Commit, git2::Error> {
Expand Down Expand Up @@ -64,3 +65,50 @@ pub fn sign_commit_or_regular(repo: &Repository, message: &str) -> Result<()> {
};
Ok(())
}

pub fn unsynced_commits(repo: &Repository) -> bool {
let local_head = match repo.head() {
Ok(head) => match head.peel_to_commit() {
Ok(commit) => commit,
Err(e) => {
eprintln!("{e}");
return false;
}
},
Err(e) => {
eprintln!("{e}");
return false;
}
};
let config = match git2::Config::open_default() {
Ok(config) => config,
Err(_) => {
eprintln!("Failed to open gitconfig");
return false;
}
};

let mut fetch_opts = FetchOptions::new();
let mut callbacks = RemoteCallbacks::new();
let mut cred_handler = CredentialHandler::new(config);
callbacks.credentials(move |url, username, allowed_types| {
cred_handler.try_next_credential(url, username, allowed_types)
});
fetch_opts.remote_callbacks(callbacks);

let mut remote = repo.find_remote("origin").unwrap();
remote
.fetch(
&["refs/heads/*:refs/remotes/origin/*"],
Some(&mut fetch_opts),
None,
)
.unwrap();

let remote_head = repo
.find_reference("refs/remotes/origin/HEAD")
.unwrap()
.peel_to_commit()
.unwrap();
local_head.id() != remote_head.id()
}
24 changes: 9 additions & 15 deletions src/git/pull.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
use crate::utils::{print_error, print_info};
use anyhow::{anyhow, Result};
use git2::{build::CheckoutBuilder, AnnotatedCommit, Config, Cred, Reference, Repository};
use gpgme::Context;
use anyhow::{anyhow, Context, Result};
use git2::{build::CheckoutBuilder, AnnotatedCommit, Config, Reference, Repository};
use git2_credentials::CredentialHandler;

fn fast_forward(repo: &Repository, lb: &mut Reference, rc: AnnotatedCommit) -> Result<()> {
let name = match lb.name() {
Expand All @@ -23,18 +23,12 @@ pub fn do_fetch<'a>(
remote: &'a mut git2::Remote,
) -> Result<git2::AnnotatedCommit<'a>> {
let mut cb = git2::RemoteCallbacks::new();
let config = Config::open_default().context("failed to open gitconfig")?;
let mut ch = CredentialHandler::new(config);

let url = remote
.url()
.ok_or(git2::Error::from_str("Unable to get remote url"))?;
if url.starts_with("git@") {
cb.credentials(|_, _, _| {
let creds =
Cred::ssh_key_from_agent("git").expect("Could not create credentials object");
Ok(creds)
});
}

cb.credentials(move |url, username, allowed_types| {
ch.try_next_credential(url, username, allowed_types)
});
// Print out our transfer progress.
cb.transfer_progress(|stats| {
if stats.received_objects() == stats.total_objects() {
Expand Down Expand Up @@ -126,7 +120,7 @@ fn normal_merge(
)?)
.to_string();

let mut ctx = Context::from_protocol(gpgme::Protocol::OpenPgp)?;
let mut ctx = gpgme::Context::from_protocol(gpgme::Protocol::OpenPgp)?;
ctx.set_armor(true);
let gpg_key = ctx.get_secret_key(&key)?;

Expand Down
22 changes: 22 additions & 0 deletions src/git/push.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
use crate::utils::print_info;
use anyhow::{Context, Result};
use git2::{Config, PushOptions, RemoteCallbacks, Repository};
use git2_credentials::CredentialHandler;
Expand All @@ -11,14 +12,35 @@ pub fn git_push(repo: &Repository) -> Result<()> {
callbacks.credentials(move |url, username, allowed_types| {
cred_handler.try_next_credential(url, username, allowed_types)
});

callbacks.transfer_progress(|stats| {
if stats.received_objects() == stats.total_objects() {
print!(
"Resolving deltas {}/{}\r",
stats.indexed_deltas(),
stats.total_deltas()
);
} else if stats.total_objects() > 0 {
print!(
"Received {}/{} objects ({}) in {} bytes\r",
stats.received_objects(),
stats.total_objects(),
stats.indexed_objects(),
stats.received_bytes()
);
}
true
});
let mut remote = repo
.find_remote("origin")
.map_err(|_| git2::Error::from_str("failed to resolve remote origin"))?;

let mut push_options = PushOptions::new();
push_options.remote_callbacks(callbacks);
remote.push(
&[&format!("refs/heads/{}", head.shorthand().unwrap())],
Some(&mut push_options),
)?;
print_info("uploaded all commits".to_string());
Ok(())
}
8 changes: 7 additions & 1 deletion src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ use clap::Parser;
use cli::Commands;
use git2::Repository;
use std::{fs::create_dir_all, path::PathBuf, process::exit};
use utils::{clone, print_error, print_info, pull, push, sync};
use utils::{clone, commit, print_error, print_info, pull, push, sync};
mod cli;
mod config;
mod git;
Expand Down Expand Up @@ -76,6 +76,12 @@ fn main() -> Result<()> {
Ok(())
}

Commands::Commit { message, path } => {
let path = resolve_dir(path)?;
commit(&path, message)?;
Ok(())
}

Commands::Push { message, path } => {
let path = resolve_dir(path)?;
push(&path, message)?;
Expand Down
36 changes: 33 additions & 3 deletions src/utils.rs
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ pub fn print_info(msg: String) {
);
}

pub fn push(path: &Path, message: String) -> Result<()> {
pub fn commit(path: &Path, message: String) -> Result<()> {
let repo = Repository::open(path).context(format!(
"unable to open repo {} is it really a git repo?",
path.display()
Expand All @@ -53,12 +53,42 @@ pub fn push(path: &Path, message: String) -> Result<()> {
let statuses = repo.statuses(Some(&mut status_opts))?;

if statuses.is_empty() {
print_error("No files to commit".to_string());
print_error("No files to commit ".to_string());
exit(1);
}

commit::sign_commit_or_regular(&repo, &message)?;
println!("commit made time to push");
Ok(())
}

pub fn push(path: &Path, message: Option<String>) -> Result<()> {
let repo = Repository::open(path).context(format!(
"unable to open repo {} is it really a git repo?",
path.display()
))?;

set_current_dir(path)?;
add::git_add(&repo)?;

let mut status_opts = StatusOptions::default();

let statuses = repo.statuses(Some(&mut status_opts))?;
let out_of_sync = commit::unsynced_commits(&repo);

if statuses.is_empty() && !out_of_sync {
print_error("No files to commit or out of sync commits".to_string());
exit(1);
}

if !statuses.is_empty() && message.is_some() {
commit::sign_commit_or_regular(&repo, &message.unwrap())?;
} else if !out_of_sync {
eprintln!(
"commit message should have been passed as there are no commits that are out of sync"
);
exit(1);
}

push::git_push(&repo)?;
Ok(())
}
Expand Down

0 comments on commit c358013

Please sign in to comment.