diff options
author | Matthias Beyer <mail@beyermatthias.de> | 2016-09-18 09:49:47 +0200 |
---|---|---|
committer | GitHub <noreply@github.com> | 2016-09-18 09:49:46 +0200 |
commit | 9b332815b9b69dd3f4bb69b5247ad19966920689 (patch) | |
tree | 3819f386f36a0eb04eca2c539b8741c10a4a6d80 /libimagstorestdhook/src | |
parent | ee60f6057cd7887682fbde0a22d22b012475598c (diff) | |
parent | f9ea5c50137c1cffabb4ace99c3a74f7860d8b4a (diff) |
Merge pull request #739 from matthiasbeyer/libimagstorestdhook/git-delete-hook
libimagstorestdhook/git delete hook
Diffstat (limited to 'libimagstorestdhook/src')
-rw-r--r-- | libimagstorestdhook/src/vcs/git/delete.rs | 133 |
1 files changed, 128 insertions, 5 deletions
diff --git a/libimagstorestdhook/src/vcs/git/delete.rs b/libimagstorestdhook/src/vcs/git/delete.rs index 902e5096..63e9f65c 100644 --- a/libimagstorestdhook/src/vcs/git/delete.rs +++ b/libimagstorestdhook/src/vcs/git/delete.rs @@ -1,42 +1,70 @@ use std::path::PathBuf; +use std::path::Path; +use std::fmt::{Debug, Formatter, Error as FmtError}; +use std::result::Result as RResult; use toml::Value; +use libimagerror::trace::trace_error; use libimagstore::storeid::StoreId; 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 libimagutil::debug_result::*; + +use vcs::git::error::GitHookErrorKind as GHEK; +use vcs::git::error::MapErrInto; +use vcs::git::runtime::Runtime as GRuntime; -#[derive(Debug)] pub struct DeleteHook { storepath: PathBuf, + runtime: GRuntime, + position: HookPosition, - config: Option<Value>, } impl DeleteHook { pub fn new(storepath: PathBuf, p: HookPosition) -> DeleteHook { DeleteHook { + runtime: GRuntime::new(&storepath), storepath: storepath, position: p, - config: None, } } } +impl Debug for DeleteHook { + fn fmt(&self, fmt: &mut Formatter) -> RResult<(), FmtError> { + write!(fmt, "DeleteHook(storepath={:?}, repository={}, pos={:?}, cfg={:?})", + self.storepath, + (if self.runtime.has_repository() { "Some(_)" } else { "None" }), + self.position, + self.runtime.has_config()) + } +} + + impl Hook for DeleteHook { fn name(&self) -> &'static str { "stdhook_git_delete" } + /// Set the configuration of the hook. See + /// `libimagstorestdhook::vcs::git::runtime::Runtime::set_config()`. + /// + /// This function traces the error (using `trace_error()`) that + /// `libimagstorestdhook::vcs::git::runtime::Runtime::set_config()` + /// returns, if any. fn set_config(&mut self, config: &Value) { - self.config = Some(config.clone()); + if let Err(e) = self.runtime.set_config(config) { + trace_error(&e); + } } } @@ -51,8 +79,103 @@ impl HookDataAccessorProvider for DeleteHook { impl StoreIdAccessor for DeleteHook { 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; + use git2::{ADD_DEFAULT, STATUS_WT_DELETED, IndexMatchedPath}; + debug!("[GIT DELETE HOOK]: {:?}", id); - Ok(()) + + let action = StoreAction::Delete; + 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 signature = try!( + repo.signature() + .map_err_into(GHEK::MkSignature) + .map_dbg_err_str("Failed to fetch signature") + .map_into_hook_error() + ); + + let head = try!( + repo.head() + .map_err_into(GHEK::HeadFetchError) + .map_dbg_err_str("Failed to fetch HEAD") + .map_into_hook_error() + ); + + let file_status = try!( + repo + .status_file(id.local()) + .map_dbg_err_str("Failed to fetch file status") + .map_dbg_err(|e| format!("\t-> {:?}", e)) + .map_err_into(GHEK::RepositoryFileStatusError) + .map_into_hook_error() + ); + + let cb = &mut |path: &Path, _matched_spec: &[u8]| -> i32 { + debug!("[GIT DELETE HOOK]: Checking file status for: {}", path.display()); + if file_status.contains(STATUS_WT_DELETED) { + debug!("[GIT DELETE HOOK]: File is deleted: {}", path.display()); + 0 + } else { + debug!("[GIT DELETE HOOK]: Ignoring file: {}", path.display()); + 1 + } + }; + + try!( + index.add_all(&[id.local()], ADD_DEFAULT, Some(cb as &mut IndexMatchedPath)) + .map_err_into(GHEK::RepositoryPathAddingError) + .map_dbg_err_str("Failed to add to index") + .map_into_hook_error() + ); + + let tree_id = try!( + index.write_tree() + .map_err_into(GHEK::RepositoryIndexWritingError) + .map_dbg_err_str("Failed to write tree") + .map_into_hook_error() + ); + + let mut parents = Vec::new(); + { + let commit = try!( + repo.find_commit(head.target().unwrap()) + .map_err_into(GHEK::RepositoryParentFetchingError) + .map_dbg_err_str("Failed to find commit HEAD") + .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_dbg_err_str("Failed to find tree") + .map_into_hook_error() + ); + + let message = try!(commit_message(&repo, cfg, action) + .map_dbg_err_str("Failed to get commit message")); + + try!(repo.commit(Some("HEAD"), &signature, &signature, &message, &tree, &parents) + .map_dbg_str("Committed") + .map_dbg_err_str("Failed to commit") + .map_err_into(GHEK::RepositoryCommittingError) + .map_into_hook_error() + ); + + index.write() + .map_err_into(GHEK::RepositoryIndexWritingError) + .map_dbg_err_str("Failed to write tree") + .map_into_hook_error() + .map(|_| ()) } } |