Merge branch 'libimagstorestdhook/git-update' into libimagstorestdhook/git

This commit is contained in:
Matthias Beyer 2016-09-07 22:09:31 +02:00
commit 566c463383
7 changed files with 148 additions and 60 deletions

View file

@ -8,6 +8,18 @@ pub enum StoreAction {
Delete, Delete,
} }
impl StoreAction {
pub fn uppercase(&self) -> &str {
match *self {
StoreAction::Create => "CREATE",
StoreAction::Retrieve => "RETRIEVE",
StoreAction::Update => "UPDATE",
StoreAction::Delete => "DELETE",
}
}
}
impl Display for StoreAction { impl Display for StoreAction {
fn fmt(&self, fmt: &mut Formatter) -> Result<(), Error> { fn fmt(&self, fmt: &mut Formatter) -> Result<(), Error> {

View file

@ -13,11 +13,11 @@ pub fn commit_interactive(config: &Value) -> bool {
false false
} }
pub fn commit_message(config: &Value, action: StoreAction) -> String { pub fn commit_message(config: &Value, action: StoreAction) -> Result<String> {
if commit_interactive(config) { if commit_interactive(config) {
unimplemented!() unimplemented!()
} else { } else {
String::from("Dummy commit") Ok(String::from("Dummy commit"))
} }
} }

View file

@ -92,6 +92,7 @@ impl StoreIdAccessor for CreateHook {
use vcs::git::action::StoreAction; use vcs::git::action::StoreAction;
use vcs::git::config::commit_message; use vcs::git::config::commit_message;
use vcs::git::error::MapIntoHookError; use vcs::git::error::MapIntoHookError;
use vcs::git::util::fetch_index;
debug!("[GIT CREATE HOOK]: {:?}", id); debug!("[GIT CREATE HOOK]: {:?}", id);
@ -102,32 +103,12 @@ impl StoreIdAccessor for CreateHook {
.map_into_hook_error() .map_into_hook_error()
); );
let cfg = try!( let action = StoreAction::Create;
self.runtime try!(self.runtime.ensure_cfg_branch_is_checked_out(&action));
.config_value_or_err()
.map_dbg_err_str("[GIT CREATE HOOK]: Couldn't get Value object from config")
);
debug!("[GIT CREATE HOOK]: Ensuring branch checkout"); let cfg = try!(self.runtime.config_value_or_err(&action));
try!(self.runtime.ensure_cfg_branch_is_checked_out()); let repo = try!(self.runtime.repository(&action));
debug!("[GIT CREATE HOOK]: Branch checked out"); let mut index = try!(fetch_index(repo, &action));
debug!("[GIT CREATE HOOK]: Getting repository");
let repo = try!(
self.runtime
.repository()
.map_dbg_err_str("[GIT CREATE HOOK]: Couldn't fetch Repository")
.map_err_into(GHEK::RepositoryError)
.map_into_hook_error()
);
debug!("[GIT CREATE HOOK]: Repository object fetched");
let mut index = try!(
repo
.index()
.map_err_into(GHEK::RepositoryIndexFetchingError)
.map_into_hook_error()
);
let file_status = try!( let file_status = try!(
repo repo

View file

@ -24,6 +24,7 @@ generate_error_module!(
RepositoryCommittingError => "Error while committing", RepositoryCommittingError => "Error while committing",
RepositoryHeadFetchingError => "Error while fetching HEAD", RepositoryHeadFetchingError => "Error while fetching HEAD",
RepositoryHeadTargetFetchingError => "Error while fetching target of HEAD", RepositoryHeadTargetFetchingError => "Error while fetching target of HEAD",
RepositoryParentFetchingError => "Error while fetching parent of commit",
HeadFetchError => "Error while getting HEAD", HeadFetchError => "Error while getting HEAD",
NotOnBranch => "No Branch is checked out", NotOnBranch => "No Branch is checked out",
MkRepo => "Repository creation error", MkRepo => "Repository creation error",

View file

@ -10,6 +10,7 @@ use libimagstore::hook::error::HookErrorKind as HEK;
use libimagstore::hook::result::HookResult; use libimagstore::hook::result::HookResult;
use libimagutil::debug_result::*; use libimagutil::debug_result::*;
use vcs::git::action::StoreAction;
use vcs::git::result::Result; use vcs::git::result::Result;
use vcs::git::error::{MapErrInto, GitHookErrorKind as GHEK}; use vcs::git::error::{MapErrInto, GitHookErrorKind as GHEK};
@ -47,7 +48,7 @@ impl Runtime {
self.config.is_some() self.config.is_some()
} }
pub fn config_value_or_err(&self) -> HookResult<&Value> { pub fn config_value_or_err(&self, action: &StoreAction) -> HookResult<&Value> {
self.config self.config
.as_ref() .as_ref()
.ok_or(GHEK::NoConfigError.into_error()) .ok_or(GHEK::NoConfigError.into_error())
@ -55,21 +56,30 @@ impl Runtime {
.map_err(Box::new) .map_err(Box::new)
.map_err(|e| HEK::HookExecutionError.into_error_with_cause(e)) .map_err(|e| HEK::HookExecutionError.into_error_with_cause(e))
.map_err(|mut e| e.with_custom_data(CustomData::default().aborting(false))) .map_err(|mut e| e.with_custom_data(CustomData::default().aborting(false)))
.map_dbg_err(|_| {
format!("[GIT {} HOOK]: Couldn't get Value object from config", action.uppercase())
})
} }
pub fn repository(&self) -> HookResult<&Repository> { pub fn repository(&self, action: &StoreAction) -> HookResult<&Repository> {
use vcs::git::error::MapIntoHookError;
debug!("[GIT {} HOOK]: Getting repository", action.uppercase());
self.repository self.repository
.as_ref() .as_ref()
.ok_or(GHEK::MkRepo.into_error()) .ok_or(GHEK::MkRepo.into_error())
.map_err(Box::new) .map_err_into(GHEK::RepositoryError)
.map_err(|e| HEK::HookExecutionError.into_error_with_cause(e)) .map_into_hook_error()
.map_dbg_err(|_| format!("[GIT {} HOOK]: Couldn't fetch Repository", action.uppercase()))
.map_dbg(|_| format!("[GIT {} HOOK]: Repository object fetched", action.uppercase()))
} }
pub fn ensure_cfg_branch_is_checked_out(&self) -> HookResult<()> { pub fn ensure_cfg_branch_is_checked_out(&self, action: &StoreAction) -> HookResult<()> {
use vcs::git::config::ensure_branch; use vcs::git::config::ensure_branch;
debug!("[GIT CREATE HOOK]: Ensuring branch checkout");
let head = try!(self let head = try!(self
.repository() .repository(action)
.and_then(|r| { .and_then(|r| {
debug!("Repository fetched, getting head"); debug!("Repository fetched, getting head");
r.head() r.head()
@ -119,6 +129,7 @@ impl Runtime {
} }
.map_err(Box::new) .map_err(Box::new)
.map_err(|e| HEK::HookExecutionError.into_error_with_cause(e)) .map_err(|e| HEK::HookExecutionError.into_error_with_cause(e))
.map_dbg_str("[GIT CREATE HOOK]: Branch checked out")
} }
} }

View file

@ -1,34 +1,58 @@
use std::path::PathBuf; use std::path::PathBuf;
use std::fmt::{Debug, Formatter, Error as FmtError};
use std::result::Result as RResult;
use toml::Value; use toml::Value;
use libimagstore::storeid::StoreId; use libimagerror::into::IntoError;
use libimagerror::trace::trace_error;
use libimagstore::hook::Hook; use libimagstore::hook::Hook;
use libimagstore::hook::result::HookResult;
use libimagstore::hook::position::HookPosition;
use libimagstore::hook::accessor::{HookDataAccessor, HookDataAccessorProvider};
use libimagstore::hook::accessor::StoreIdAccessor; use libimagstore::hook::accessor::StoreIdAccessor;
use libimagstore::hook::accessor::{HookDataAccessor, HookDataAccessorProvider};
use libimagstore::hook::error::HookError as HE;
use libimagstore::hook::error::HookErrorKind as HEK;
use libimagstore::hook::position::HookPosition;
use libimagstore::hook::result::HookResult;
use libimagstore::storeid::StoreId;
use libimagutil::debug_result::*;
use vcs::git::error::GitHookError as GHE;
use vcs::git::error::GitHookErrorKind as GHEK;
use vcs::git::error::MapErrInto;
use vcs::git::error::MapIntoHookError;
use vcs::git::result::Result;
use vcs::git::runtime::Runtime as GRuntime;
#[derive(Debug)]
pub struct UpdateHook { pub struct UpdateHook {
storepath: PathBuf, storepath: PathBuf,
runtime: GRuntime,
position: HookPosition, position: HookPosition,
config: Option<Value>,
} }
impl UpdateHook { impl UpdateHook {
pub fn new(storepath: PathBuf, p: HookPosition) -> UpdateHook { pub fn new(storepath: PathBuf, p: HookPosition) -> UpdateHook {
UpdateHook { UpdateHook {
runtime: GRuntime::new(&storepath),
storepath: storepath, storepath: storepath,
position: p, position: p,
config: None,
} }
} }
} }
impl Debug for UpdateHook {
fn fmt(&self, fmt: &mut Formatter) -> RResult<(), FmtError> {
write!(fmt, "UpdateHook(storepath={:?}, repository={}, pos={:?}, cfg={:?}",
self.storepath,
(if self.runtime.has_repository() { "Some(_)" } else { "None" }),
self.position,
self.runtime.has_config())
}
}
impl Hook for UpdateHook { impl Hook for UpdateHook {
fn name(&self) -> &'static str { fn name(&self) -> &'static str {
@ -36,7 +60,9 @@ impl Hook for UpdateHook {
} }
fn set_config(&mut self, config: &Value) { fn set_config(&mut self, config: &Value) {
self.config = Some(config.clone()); if let Err(e) = self.runtime.set_config(config) {
trace_error(&e);
}
} }
} }
@ -50,9 +76,70 @@ impl HookDataAccessorProvider for UpdateHook {
impl StoreIdAccessor for UpdateHook { impl StoreIdAccessor for UpdateHook {
/// The implementation of the UpdateHook
///
/// # Scope
///
/// This hook takes the git index and commits it either interactively or with a default message,
/// if there is no configuration for an interactive commit.
///
fn access(&self, id: &StoreId) -> HookResult<()> { fn access(&self, id: &StoreId) -> HookResult<()> {
use vcs::git::action::StoreAction;
use vcs::git::config::commit_message;
use vcs::git::error::MapIntoHookError;
use vcs::git::util::fetch_index;
debug!("[GIT UPDATE HOOK]: {:?}", id); debug!("[GIT UPDATE HOOK]: {:?}", id);
Ok(())
let action = StoreAction::Update;
let cfg = try!(self.runtime.config_value_or_err(&action));
let repo = try!(self.runtime.repository(&action));
let mut index = try!(fetch_index(repo, &action));
let tree_id = try!(
index.write_tree()
.map_err_into(GHEK::RepositoryIndexWritingError)
.map_into_hook_error()
);
let signature = try!(
repo.signature()
.map_err_into(GHEK::MkSignature)
.map_into_hook_error()
);
let head = try!(
repo.head()
.map_err_into(GHEK::HeadFetchError)
.map_into_hook_error()
);
let mut parents = Vec::new();
{
let commit = try!(
repo.find_commit(head.target().unwrap())
.map_err_into(GHEK::RepositoryParentFetchingError)
.map_into_hook_error()
);
parents.push(commit);
}
// for converting from Vec<Commit> to Vec<&Commit>
let parents = parents.iter().collect::<Vec<_>>();
let tree = try!(
repo.find_tree(tree_id)
.map_err_into(GHEK::RepositoryParentFetchingError)
.map_into_hook_error()
);
let message = try!(commit_message(cfg, StoreAction::Update));
repo.commit(Some("HEAD"), &signature, &signature, &message, &tree, &parents)
.map_err_into(GHEK::RepositoryCommittingError)
.map_into_hook_error()
.map(|_| ())
} }
} }

View file

@ -2,27 +2,23 @@
//! //!
//! Contains primitives to create a repository within the store path //! Contains primitives to create a repository within the store path
use git2::Repository; use git2::{Repository, Index};
use git2::RepositoryInitOptions;
use libimagstore::store::Store;
use vcs::git::error::GitHookErrorKind as GHEK; use vcs::git::error::GitHookErrorKind as GHEK;
use vcs::git::error::MapErrInto; use vcs::git::error::MapErrInto;
use vcs::git::result::Result; use vcs::git::runtime::Runtime as GRuntime;
use vcs::git::action::StoreAction;
use vcs::git::error::MapIntoHookError;
pub fn mkrepo(store: &Store) -> Result<()> { use libimagutil::debug_result::*;
let mut opts = RepositoryInitOptions::new(); use libimagstore::hook::error::HookError;
opts.bare(false);
opts.no_reinit(true); pub fn fetch_index(repo: &Repository, action: &StoreAction) -> Result<Index, HookError> {
opts.mkdir(false); debug!("[GIT {} HOOK]: Getting Index", action.uppercase());
opts.external_template(false); repo.index()
Repository::init_opts(store.path(), &opts) .map_dbg_err(|_| format!("[GIT {} HOOK]: Couldn't fetch Index", action.uppercase()))
.map(|_| ()) .map_dbg(|_| format!("[GIT {} HOOK]: Index object fetched", action.uppercase()))
.map_err_into(GHEK::MkRepo) .map_err_into(GHEK::RepositoryIndexFetchingError)
} .map_into_hook_error()
pub fn hasrepo(store: &Store) -> bool {
Repository::open(store.path()).is_ok()
} }