Skip to content

Commit

Permalink
Version 0.3, ✨ now mirrors properly✨
Browse files Browse the repository at this point in the history
  • Loading branch information
AaronFriel committed Aug 29, 2017
1 parent 293a04e commit f13c47f
Show file tree
Hide file tree
Showing 3 changed files with 86 additions and 55 deletions.
14 changes: 7 additions & 7 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "grapple"
version = "0.2.0"
version = "0.3.0"
authors = ["Aaron Friel <mayreply@aaronfriel.com>"]
license = "BSD"

Expand Down
125 changes: 78 additions & 47 deletions src/git_repository.rs
Original file line number Diff line number Diff line change
@@ -1,13 +1,23 @@
use errors::*;

use git2::{Repository, RepositoryInitOptions, Remote, PushOptions, RemoteCallbacks, Cred};
use std::path::{Path};
use std::fs::{self};
use std::path::{Path};

use std::collections::HashMap;

use git2::{Repository, RepositoryInitOptions, Remote, FetchOptions, Direction, FetchPrune, PushOptions, RemoteCallbacks, Cred};

use config::RepositoryMapping;

const ATTEMPTS: u8 = 3;

#[derive(Debug)]
enum RefAction {
Update,
Create,
Delete,
}

pub trait GitRepository {
fn repository_name(&self) -> &str;

Expand Down Expand Up @@ -49,24 +59,12 @@ fn attempt_open<T: GitRepository>(repo: &T, attempts: u8) -> Result<Repository>
.mkdir(true)
.mkpath(true);

// let repo = init_bare(repo.repository_name())

Ok(Repository::init_opts(repo.repository_name(), &repo_opts)?)
}

const ALL_HEADS: &'static str = "+refs/heads/*:refs/heads/*";
const ALL_TAGS: &'static str = "+refs/tags/*:refs/tags/*";

fn disconnect_head(repo: &Repository) -> Result<()> {
let head = repo.head()?;

let oid = head.target().ok_or(Error::from_kind(ErrorKind::RepositoryOpenError))?;

repo.set_head_detached(oid)?;

Ok(())
}

const REMOTE_FETCH: &'static str = "fetch";

fn open_remote_fetch<'a>(repo: &'a Repository, uri: &str) -> Result<Remote<'a>> {
Expand All @@ -82,45 +80,19 @@ fn open_remote_fetch<'a>(repo: &'a Repository, uri: &str) -> Result<Remote<'a>>
}
}

const REMOTE_PUSH: &'static str = "push";

fn open_remote_push<'a>(repo: &'a Repository, uri: &str) -> Result<Remote<'a>> {
if let Ok(remote) = repo.find_remote(REMOTE_PUSH) {
Ok(remote)
} else {
repo.remote(REMOTE_PUSH, uri)?;

// repo.remote_add_push(REMOTE_PUSH, ALL_HEADS)?;
// repo.remote_add_push(REMOTE_PUSH, ALL_TAGS)?;

open_remote_push(repo, uri)
}
}

fn push_glob(repository: &Repository, remote: &mut Remote, glob: &str, push_options: Option<&mut PushOptions>) -> Result<()> {
let mut refs = repository.references_glob(glob)?;

let mut push_refs : Vec<&str> = Vec::new();

for reference in refs.names() {
if let Ok(name) = reference {
push_refs.push(name);
}
}

for name in &push_refs {
println!("{:?}", name);
}

Ok(())
Ok(repo.remote_anonymous(uri)?)
}

pub fn grapple<T: GitRepository>(payload: &T, mapping: &RepositoryMapping) -> Result<()> {
let repo = open(payload)?;

let mut fetch = open_remote_fetch(&repo, payload.clone_uri())?;

fetch.fetch(&[], None, None)?;
let mut fetch_options = FetchOptions::new();
fetch_options.prune(FetchPrune::On);

fetch.fetch(&[], Some(&mut fetch_options), None)?;

let mut push = open_remote_push(&repo, &mapping.push_uri)?;

Expand All @@ -135,8 +107,67 @@ pub fn grapple<T: GitRepository>(payload: &T, mapping: &RepositoryMapping) -> Re
let mut push_options = PushOptions::new();
push_options.remote_callbacks(callbacks);

push_glob(&repo, &mut push, "refs/heads/*", Some(&mut push_options))?;
push_glob(&repo, &mut push, "refs/tags/*", Some(&mut push_options))?;
fetch.connect(Direction::Fetch)?;

let fetch_list = fetch.list()?;

let mut ref_map = HashMap::new();

println!("Fetch refs:");
for reference in fetch_list.iter().filter(|r| r.name().starts_with("refs/")) {
ref_map.insert(reference.name(), reference.oid());
}


let mut callbacks = RemoteCallbacks::new();
callbacks.credentials(
|_, username, _| Cred::ssh_key(
username.unwrap_or("grapple"),
Some(Path::new(&mapping.deploy_public_key)),
Path::new(&mapping.deploy_private_key),
None));

push.connect_auth(Direction::Push, Some(callbacks), None)?;

let push_list = push.list()?;

let mut action_list = Vec::new();

println!("Push refs:");
for reference in push_list.iter().filter(|r| r.name().starts_with("refs/")) {
let ref_name = reference.name();
let push_oid = reference.oid();
match ref_map.remove(ref_name) {
Some(fetch_oid) if fetch_oid != push_oid => action_list.push((ref_name, RefAction::Update)),
Some(_) => (),
None => action_list.push((ref_name, RefAction::Delete)),
}
}

for (ref_name, _) in ref_map.iter() {
action_list.push((ref_name, RefAction::Create));
}


let mut fetch = open_remote_fetch(&repo, payload.clone_uri())?;

let mut push = open_remote_push(&repo, &mapping.push_uri)?;

for (ref_name, action) in action_list {
match action {
RefAction::Create | RefAction::Update => {
// TODO: collect errors
let refspec = format!("+{}:{}", ref_name, ref_name);
fetch.fetch(&[&refspec], Some(&mut fetch_options), None)?;
push.push(&[&refspec], Some(&mut push_options))?;
},
RefAction::Delete => {
let refspec = format!(":{}", ref_name);
println!("Delete: {}", ref_name);
push.push(&[&refspec], Some(&mut push_options))?;
},
}
}

Ok(())
}

0 comments on commit f13c47f

Please sign in to comment.