diff --git a/libimagstorestdhook/src/vcs/git/action.rs b/libimagstorestdhook/src/vcs/git/action.rs index 26caf5a0..277260f0 100644 --- a/libimagstorestdhook/src/vcs/git/action.rs +++ b/libimagstorestdhook/src/vcs/git/action.rs @@ -8,6 +8,18 @@ pub enum StoreAction { 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 { fn fmt(&self, fmt: &mut Formatter) -> Result<(), Error> { diff --git a/libimagstorestdhook/src/vcs/git/config.rs b/libimagstorestdhook/src/vcs/git/config.rs index 0a9d5907..a7e262d6 100644 --- a/libimagstorestdhook/src/vcs/git/config.rs +++ b/libimagstorestdhook/src/vcs/git/config.rs @@ -13,11 +13,11 @@ pub fn commit_interactive(config: &Value) -> bool { false } -pub fn commit_message(config: &Value, action: StoreAction) -> String { +pub fn commit_message(config: &Value, action: StoreAction) -> Result { if commit_interactive(config) { unimplemented!() } else { - String::from("Dummy commit") + Ok(String::from("Dummy commit")) } } diff --git a/libimagstorestdhook/src/vcs/git/create.rs b/libimagstorestdhook/src/vcs/git/create.rs index 54845cdd..fcba4535 100644 --- a/libimagstorestdhook/src/vcs/git/create.rs +++ b/libimagstorestdhook/src/vcs/git/create.rs @@ -92,6 +92,7 @@ impl StoreIdAccessor for CreateHook { use vcs::git::action::StoreAction; use vcs::git::config::commit_message; use vcs::git::error::MapIntoHookError; + use vcs::git::util::fetch_index; debug!("[GIT CREATE HOOK]: {:?}", id); @@ -102,32 +103,12 @@ impl StoreIdAccessor for CreateHook { .map_into_hook_error() ); - let cfg = try!( - self.runtime - .config_value_or_err() - .map_dbg_err_str("[GIT CREATE HOOK]: Couldn't get Value object from config") - ); + let action = StoreAction::Create; + try!(self.runtime.ensure_cfg_branch_is_checked_out(&action)); - debug!("[GIT CREATE HOOK]: Ensuring branch checkout"); - try!(self.runtime.ensure_cfg_branch_is_checked_out()); - debug!("[GIT CREATE HOOK]: Branch checked out"); - - 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 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 file_status = try!( repo diff --git a/libimagstorestdhook/src/vcs/git/error.rs b/libimagstorestdhook/src/vcs/git/error.rs index 1ad15a3d..a8a00e14 100644 --- a/libimagstorestdhook/src/vcs/git/error.rs +++ b/libimagstorestdhook/src/vcs/git/error.rs @@ -24,6 +24,7 @@ generate_error_module!( RepositoryCommittingError => "Error while committing", RepositoryHeadFetchingError => "Error while fetching HEAD", RepositoryHeadTargetFetchingError => "Error while fetching target of HEAD", + RepositoryParentFetchingError => "Error while fetching parent of commit", HeadFetchError => "Error while getting HEAD", NotOnBranch => "No Branch is checked out", MkRepo => "Repository creation error", diff --git a/libimagstorestdhook/src/vcs/git/runtime.rs b/libimagstorestdhook/src/vcs/git/runtime.rs index 54334f44..13902b7d 100644 --- a/libimagstorestdhook/src/vcs/git/runtime.rs +++ b/libimagstorestdhook/src/vcs/git/runtime.rs @@ -10,6 +10,7 @@ use libimagstore::hook::error::HookErrorKind as HEK; use libimagstore::hook::result::HookResult; use libimagutil::debug_result::*; +use vcs::git::action::StoreAction; use vcs::git::result::Result; use vcs::git::error::{MapErrInto, GitHookErrorKind as GHEK}; @@ -47,7 +48,7 @@ impl Runtime { 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 .as_ref() .ok_or(GHEK::NoConfigError.into_error()) @@ -55,21 +56,30 @@ impl Runtime { .map_err(Box::new) .map_err(|e| HEK::HookExecutionError.into_error_with_cause(e)) .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 .as_ref() .ok_or(GHEK::MkRepo.into_error()) - .map_err(Box::new) - .map_err(|e| HEK::HookExecutionError.into_error_with_cause(e)) + .map_err_into(GHEK::RepositoryError) + .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; + debug!("[GIT CREATE HOOK]: Ensuring branch checkout"); let head = try!(self - .repository() + .repository(action) .and_then(|r| { debug!("Repository fetched, getting head"); r.head() @@ -119,6 +129,7 @@ impl Runtime { } .map_err(Box::new) .map_err(|e| HEK::HookExecutionError.into_error_with_cause(e)) + .map_dbg_str("[GIT CREATE HOOK]: Branch checked out") } } diff --git a/libimagstorestdhook/src/vcs/git/update.rs b/libimagstorestdhook/src/vcs/git/update.rs index 4a5026d0..fa4af2c9 100644 --- a/libimagstorestdhook/src/vcs/git/update.rs +++ b/libimagstorestdhook/src/vcs/git/update.rs @@ -1,34 +1,58 @@ use std::path::PathBuf; +use std::fmt::{Debug, Formatter, Error as FmtError}; +use std::result::Result as RResult; use toml::Value; -use libimagstore::storeid::StoreId; +use libimagerror::into::IntoError; +use libimagerror::trace::trace_error; 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::{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 { storepath: PathBuf, + runtime: GRuntime, + position: HookPosition, - config: Option, } impl UpdateHook { pub fn new(storepath: PathBuf, p: HookPosition) -> UpdateHook { UpdateHook { + runtime: GRuntime::new(&storepath), storepath: storepath, 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 { fn name(&self) -> &'static str { @@ -36,7 +60,9 @@ impl Hook for UpdateHook { } 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 { + /// 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<()> { + 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); - 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 to Vec<&Commit> + let parents = parents.iter().collect::>(); + + 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(|_| ()) + } } diff --git a/libimagstorestdhook/src/vcs/git/util.rs b/libimagstorestdhook/src/vcs/git/util.rs index e57e6acc..56b66323 100644 --- a/libimagstorestdhook/src/vcs/git/util.rs +++ b/libimagstorestdhook/src/vcs/git/util.rs @@ -2,27 +2,23 @@ //! //! Contains primitives to create a repository within the store path -use git2::Repository; -use git2::RepositoryInitOptions; - -use libimagstore::store::Store; +use git2::{Repository, Index}; use vcs::git::error::GitHookErrorKind as GHEK; 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<()> { - let mut opts = RepositoryInitOptions::new(); - opts.bare(false); - opts.no_reinit(true); - opts.mkdir(false); - opts.external_template(false); - Repository::init_opts(store.path(), &opts) - .map(|_| ()) - .map_err_into(GHEK::MkRepo) -} - -pub fn hasrepo(store: &Store) -> bool { - Repository::open(store.path()).is_ok() +use libimagutil::debug_result::*; +use libimagstore::hook::error::HookError; + +pub fn fetch_index(repo: &Repository, action: &StoreAction) -> Result { + debug!("[GIT {} HOOK]: Getting Index", action.uppercase()); + repo.index() + .map_dbg_err(|_| format!("[GIT {} HOOK]: Couldn't fetch Index", action.uppercase())) + .map_dbg(|_| format!("[GIT {} HOOK]: Index object fetched", action.uppercase())) + .map_err_into(GHEK::RepositoryIndexFetchingError) + .map_into_hook_error() }