Merge pull request #739 from matthiasbeyer/libimagstorestdhook/git-delete-hook

libimagstorestdhook/git delete hook
This commit is contained in:
Matthias Beyer 2016-09-18 09:49:47 +02:00 committed by GitHub
commit 9b332815b9
3 changed files with 157 additions and 20 deletions

View file

@ -66,20 +66,6 @@ ensure_branch = "master"
# Try to checkout the ensure_branch if it isn't checked out # Try to checkout the ensure_branch if it isn't checked out
try_checkout_ensure_branch = true try_checkout_ensure_branch = true
[store.hooks.stdhook_git_delete]
aspect = "vcs"
# Fail if the repository cannot be opened. If this is set to `false`, the error
# will be printed, but will not abort the store operation. `true` will print the
# error and abort the store action.
abort_on_repo_init_failure = true
# Ensure to be on this branche before doing anything.
ensure_branch = "master"
# Try to checkout the ensure_branch if it isn't checked out
try_checkout_ensure_branch = true
[store.hooks.stdhook_git_update] [store.hooks.stdhook_git_update]
aspect = "vcs" aspect = "vcs"
@ -108,3 +94,31 @@ interactive_editor = false
# Commit message if the commit is not interactive # Commit message if the commit is not interactive
message = "Update" message = "Update"
[store.hooks.stdhook_git_delete]
aspect = "vcs"
# Fail if the repository cannot be opened. If this is set to `false`, the error
# will be printed, but will not abort the store operation. `true` will print the
# error and abort the store action.
abort_on_repo_init_failure = true
# Ensure to be on this branche before doing anything.
ensure_branch = "master"
# Try to checkout the ensure_branch if it isn't checked out
try_checkout_ensure_branch = true
# Commit configuration
[store.hooks.stdhook_git_delete.commit]
# Whether to do the commit interactively
interactive = false
# Set to true to use the $EDITOR for the commit, to false to do on commandline
# When committing without editor, only a single line is allowed as commit
# message
interactive_editor = false
# Commit message if the commit is not interactive
message = "Deleted"

View file

@ -139,7 +139,7 @@ impl<'a> Runtime<'a> {
let sp = storepath; let sp = storepath;
let hooks : Vec<(Box<Hook>, &str, HP)> = vec![ let hooks : Vec<(Box<Hook>, &str, HP)> = vec![
(Box::new(GitDeleteHook::new(sp.clone(), HP::PreDelete)) , "vcs", HP::PreDelete), (Box::new(GitDeleteHook::new(sp.clone(), HP::PostDelete)) , "vcs", HP::PostDelete),
(Box::new(GitUpdateHook::new(sp, HP::PostUpdate)) , "vcs", HP::PostUpdate), (Box::new(GitUpdateHook::new(sp, HP::PostUpdate)) , "vcs", HP::PostUpdate),
]; ];

View file

@ -1,42 +1,70 @@
use std::path::PathBuf; 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 toml::Value;
use libimagerror::trace::trace_error;
use libimagstore::storeid::StoreId; use libimagstore::storeid::StoreId;
use libimagstore::hook::Hook; use libimagstore::hook::Hook;
use libimagstore::hook::result::HookResult; use libimagstore::hook::result::HookResult;
use libimagstore::hook::position::HookPosition; use libimagstore::hook::position::HookPosition;
use libimagstore::hook::accessor::{HookDataAccessor, HookDataAccessorProvider}; use libimagstore::hook::accessor::{HookDataAccessor, HookDataAccessorProvider};
use libimagstore::hook::accessor::StoreIdAccessor; 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 { pub struct DeleteHook {
storepath: PathBuf, storepath: PathBuf,
runtime: GRuntime,
position: HookPosition, position: HookPosition,
config: Option<Value>,
} }
impl DeleteHook { impl DeleteHook {
pub fn new(storepath: PathBuf, p: HookPosition) -> DeleteHook { pub fn new(storepath: PathBuf, p: HookPosition) -> DeleteHook {
DeleteHook { DeleteHook {
runtime: GRuntime::new(&storepath),
storepath: storepath, storepath: storepath,
position: p, 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 { impl Hook for DeleteHook {
fn name(&self) -> &'static str { fn name(&self) -> &'static str {
"stdhook_git_delete" "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) { 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 { impl StoreIdAccessor for DeleteHook {
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;
use git2::{ADD_DEFAULT, STATUS_WT_DELETED, IndexMatchedPath};
debug!("[GIT DELETE HOOK]: {:?}", id); 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(|_| ())
} }
} }