Skip to content

Instantly share code, notes, and snippets.

@dmfilipenko
Created May 30, 2016 10:40
Show Gist options
  • Save dmfilipenko/56ae080c1b546b521ca548c6ce0865db to your computer and use it in GitHub Desktop.
Save dmfilipenko/56ae080c1b546b521ca548c6ce0865db to your computer and use it in GitHub Desktop.
#!/usr/bin/env bash
#
# Copyright (c) 2010-2014, Benjamin C. Meyer <ben@meyerhome.net>
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions are met:
# 1. Redistributions of source code must retain the above copyright
# notice, this list of conditions and the following disclaimer.
# 2. Redistributions in binary form must reproduce the above copyright
# notice, this list of conditions and the following disclaimer in the
# documentation and/or other materials provided with the distribution.
# 3. Neither the name of the project nor the
# names of its contributors may be used to endorse or promote products
# derived from this software without specific prior written permission.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER ''AS IS'' AND ANY
# EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE FOR ANY
# DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#
function hook_dirs
{
if [ ! -z "${1}" ] ; then
hook="/${1}"
else
hook=""
fi
echo "${HOME}/.git_hooks${hook}"
git rev-parse --git-dir &> /dev/null
if [ $? -eq 0 ]; then
if [ $(git rev-parse --is-bare-repository) = 'false' ]; then
cd $(git rev-parse --show-toplevel)
echo "${PWD}/git_hooks${hook}"
echo "${PWD}/.githooks${hook}"
fi
fi
eval echo "`git config hooks.global`"${hook}
}
function list_hooks_in_dir
{
path="${1}"
level="${2}"
find --help 2>&1 | grep -- '-L' 2>/dev/null >/dev/null
if [ $? -eq 1 ] ; then
find "${path}/" -mindepth ${level} -maxdepth ${level} -perm +111 -type f 2>/dev/null | grep -v "^.$" | sort
else
find -L "${path}/" -mindepth ${level} -maxdepth ${level} -perm +111 -type f 2>/dev/null | grep -v "^.$" | sort
fi
}
function list_hooks
{
GITDIR=`git rev-parse --git-dir`
cat "${GITDIR}/hooks/pre-commit" 2> /dev/null | grep 'git-hooks' > /dev/null 2> /dev/null
if [ $? = 0 ] ; then
echo "Git hooks ARE installed in this repository."
echo ""
else
echo "Git hooks are NOT installed in this repository. (Run 'git hooks --install' to install it)"
echo ""
fi
echo 'Listing User, Project, and Global hooks:'
echo '---'
for dir in `hook_dirs`; do
echo "${dir}:"
for hook in `list_hooks_in_dir "${dir}" 2` ; do
echo -n `basename \`dirname "${hook}"\``
echo -e "/`basename "${hook}"` \t- `${hook} --about`"
done
echo ""
done
}
function run_hooks
{
dir="${1}"
if [[ -z ${dir} || ! -d "${dir}" ]] ; then
echo "run_hooks requires a directory name as an argument."
return 1
fi
shift 1
for hook in `list_hooks_in_dir "${dir}" 1`
do
export last_run_hook="${hook} $@"
if [ ! -z ${GIT_HOOKS_VERBOSE} ] ; then
echo -n "@@ Running hook: "
echo -n `basename \`dirname "${hook}"\``
echo "/`basename "${hook}"`"
fi
${hook} "$@"
done
}
function run_hook
{
set -e
hook=`basename "${1}"`
if [ -z ${hook} ] ; then
echo "run requires a hook argument"
return 1
fi
shift 1
for dir in `hook_dirs "${hook}"`; do
if [ ! -d "${dir}" ] ; then
continue
fi
run_hooks "${dir}" "$@"
done
set +e
}
function install_hooks_into
{
DIR=$1
cd "${DIR}"
set -e
mv hooks hooks.old
set +e
mkdir hooks
cd hooks
for file in applypatch-msg commit-msg post-applypatch post-checkout post-commit post-merge post-receive pre-applypatch pre-auto-gc pre-commit prepare-commit-msg pre-rebase pre-receive update pre-push
do
echo "${2}" > "${file}"
chmod +x "${file}"
done
}
function install_hooks
{
GITDIR=`git rev-parse --git-dir`
if [ ! $? -eq 0 ] ; then
echo "$1 must be run inside a git repository"
return 1
fi
cd "${GITDIR}"
if [ "${1}" = "--install" ] ; then
if [ -d hooks.old ] ; then
echo "hooks.old already exists, perhaps you already installed?"
return 1
fi
cmd='#!/usr/bin/env bash
git-hooks run "$0" "$@"';
install_hooks_into "${PWD}" "${cmd}"
else
if [ ! -d hooks.old ] ; then
echo "Error, hooks.old doesn't exists, aborting uninstall to not destroy something"
return 1
fi
rm -rf hooks
mv hooks.old hooks
fi
}
function install_global
{
TEMPLATE="$HOME/.git-template-with-git-hooks"
if [ ! -d "${TEMPLATE}" ] ; then
DEFAULT=/usr/share/git-core/templates
if [ -d ${DEFAULT} ] ; then
cp -rf /usr/share/git-core/templates "${TEMPLATE}"
else
mkdir -p "${TEMPLATE}/hooks"
fi
cmd="#!/usr/bin/env bash
echo \"git hooks not installed in this repository. Run 'git hooks --install' to install it or 'git hooks -h' for more information.\"";
install_hooks_into "${TEMPLATE}" "${cmd}"
mv "${TEMPLATE}/hooks.old" "${TEMPLATE}/hooks.original"
fi
git config --global init.templatedir "${TEMPLATE}"
echo "Git global config init.templatedir is now set to ${TEMPLATE}"
}
function uninstall_global
{
git config --global --unset init.templatedir
}
function report_error
{
echo "Hook failed: $last_run_hook"
exit 1
}
case $1 in
run )
if [ ! -z "${GIT_DIR}" ] ; then
unset GIT_DIR
fi
shift
trap report_error ERR
run_hook "$@"
;;
--install|--uninstall )
install_hooks "$1"
;;
--install-global|--installglobal )
install_global
;;
--uninstall-global|--uninstallglobal )
uninstall_global
;;
-h|--help|-? )
echo 'Git Hooks'
echo ' A tool to manage project, user, and global Git hooks for multiple git repositories.'
echo ' https://github.com/icefox/git-hooks'
echo ''
echo 'Options:'
echo ' --install Replace existing hooks in this repository with a call to'
echo ' git hooks run [hook]. Move old hooks directory to hooks.old'
echo ' --uninstall Remove existing hooks in this repository and rename hooks.old'
echo ' back to hooks'
echo ' --install-global'
echo ' Create a template .git directory that that will be used whenever'
echo ' a git repository is created or cloned that will remind the user'
echo ' to install git-hooks.'
echo ' --uninstall-global'
echo ' Turn off the global .git directory template that has the reminder.'
echo " run <cmd> Run the hooks for <cmd> (such as pre-commit)"
echo " (no arguments) Show currently installed hooks"
;;
* )
list_hooks
;;
esac
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment