From fd1aff907f596aaea7686d05f9f809bd04e764ee Mon Sep 17 00:00:00 2001 From: "Kyle J. McKay" Date: Fri, 22 Dec 2017 17:40:41 -0800 Subject: [PATCH] hooks: use _global/hooks directly or indirectly Starting with Git 2.9.0, if the config item core.hooksPath has been set then the repository's own hooks directory is completely ignored unless that new config item happens to point there. The implementation of this config item is an abomination. There's simply no way to restore the original behavior once it's been set in any of the possible config areas (system, global, XDG, repository, environment or command line). It does support relative paths and one might mistakenly believe that setting "core.hooksPath = hooks" in the repository's config file would restore the default behavior. One would be horribly mistaken in that belief. The location that a relative core.hooksPath path is relative to varies depending on the situation (pushing into a linked working tree is most especially fun with regard to that). What would an empty path do, you say? That's the worst of all. "core.hooksPath=" is the same as "core.hooksPath=/". Bad bad bad! The only way to alleviate the horrible pain of the core.hooksPath implementation abomination is to always set core.hooksPath in the repository's config file to an absolute path. Fortunately we have just recently started installing all of Girocco's hooks to a well-known absolute path that's accessible both in and out of the sshd chroot at the same absolute path. :) Additionally, the exact same hook scripts are used for both the real repository and the personal mob push "pseudo" repository. :) (This matters because the "pseudo" repository's config file is just a symbolic link to the real repository's config file.) One final issue remains and that is compatibility with pre-2.9.0 versions of Git. This requires hooks to remain in the repository's hooks directory in order for them to be run. Solve all these issues by setting core.hooksPath to point to the new "$Girocco::Config::reporoot/_global/hooks" directory and additionally install symbolic links in each repository's hooks subdirectory for each hook that link to the same-named hook in the _global/hooks directory. The core.hooksPath config value is set in the repository's config file thereby overriding any system, global or XDG setting for such. It's set to an absolute path thereby mitigating the core.hooksPath implementation abomination. Furthermore, earlier versions of Git will still run the hooks in the _global/hooks directory even though they do not understand the core.hooksPath config item courtesy of the symbolic links that are installed into each repository's hooks subdirectory. As a result of these changes, the update-all-hooks script becomes mostly superfluous other than to make sure the symlinks are in place for older Git versions to follow. Obviously update-all-projects must be run at least once after this change to actually switch all the projects over to using _global/hooks. The other nice advantage of this approach is that the hook scripts are always immediately up-to-date for all projects after running "make install". Signed-off-by: Kyle J. McKay --- Girocco/Project.pm | 14 ++++++++------ toolbox/update-all-config.pl | 2 ++ toolbox/update-all-hooks.sh | 29 +++++++++++------------------ 3 files changed, 21 insertions(+), 24 deletions(-) diff --git a/Girocco/Project.pm b/Girocco/Project.pm index ef11a45..a7f60b8 100644 --- a/Girocco/Project.pm +++ b/Girocco/Project.pm @@ -545,12 +545,12 @@ sub _hook_install { -d $hooksdir or mkdir $hooksdir or die "hooks directory does not exist and unable to create it for project " . $self->{name} . ": $!"; umask($oldmask); - open my $src, '<', "$Girocco::Config::basedir/hooks/$name" or die "cannot open hook $name: $!"; - open my $dst, '>', $self->_hook_path($name) or die "cannot open hook $name for writing: $!"; - while (<$src>) { print $dst $_; } - close $dst; - close $src; - chmod 0775, $self->_hook_path($name) or die "cannot chmod hook $name: $!"; + my $globalhooks = $Girocco::Config::reporoot . "/_global/hooks"; + -f "$globalhooks/$name" && -r _ or die "cannot find hook $name: $!"; + ! -e $self->_hook_path($name) || unlink $self->_hook_path($name) && ! -e $self->_hook_path($name) or + die "hooks directory contains unremovable pre-existing hook $name: $!"; + symlink("$globalhooks/$name", $self->_hook_path($name)) or + die "cannot create hook $name symlink: $!"; } sub _hooks_install { @@ -900,6 +900,8 @@ sub _setup { or die "disabling core.logAllRefUpdates failed: $?"; system($Girocco::Config::git_bin, '--git-dir='.$self->{path}, 'config', 'core.ignoreCase', 'false') == 0 or die "disabling core.ignoreCase failed: $?"; + system($Girocco::Config::git_bin, '--git-dir='.$self->{path}, 'config', 'core.hooksPath', $Girocco::Config::reporoot . "/_global/hooks") == 0 + or die "setting core.hooksPath failed: $?"; system($Girocco::Config::git_bin, '--git-dir='.$self->{path}, 'config', 'transfer.fsckObjects', 'true') == 0 or die "enabling transfer.fsckObjects failed: $?"; system($Girocco::Config::git_bin, '--git-dir='.$self->{path}, 'config', 'transfer.unpackLimit', '1') == 0 diff --git a/toolbox/update-all-config.pl b/toolbox/update-all-config.pl index eff6280..3cfdf26 100755 --- a/toolbox/update-all-config.pl +++ b/toolbox/update-all-config.pl @@ -287,6 +287,8 @@ sub process_one_project $repovers = ""; } &$do_config('core.repositoryformatversion', 0) if $repovers eq ""; + my $hookspath = $Girocco::Config::reporoot . "/_global/hooks"; + defval($config->{'core.hookspath'},"") eq $hookspath or &$do_config('core.hookspath', $hookspath); my $cmplvl = defval($config->{'core.compression'},""); if ($cmplvl !~ /^-?\d+$/ || $cmplvl < -1 || $cmplvl > 9 || "" . (0 + $cmplvl) ne "" . $cmplvl) { pmsg($proj, "WARNING: replacing invalid core.compression value: \"$cmplvl\"") unless $cmplvl eq "" || $quiet; diff --git a/toolbox/update-all-hooks.sh b/toolbox/update-all-hooks.sh index 1c26d74..942b026 100755 --- a/toolbox/update-all-hooks.sh +++ b/toolbox/update-all-hooks.sh @@ -22,6 +22,7 @@ fi umask 002 base="${cfg_reporoot%/}" +globalhooks="$base/_global/hooks" hookbin="$cfg_basedir/hooks" cmd='cut -d : -f 1 <"$cfg_chroot/etc/group" | grep -v ^_repo' [ $# -eq 0 ] || cmd='printf "%s\n" "$@"' @@ -35,17 +36,23 @@ eval "$cmd" | ! [ -e "$projdir/.nohooks" ] || { echo "$proj: .nohooks found -- skipping"; continue; } updates= for hook in pre-receive post-receive update; do + doln= if [ -f "$projdir/hooks/$hook" ]; then - if ! cmp -s "$hookbin/$hook" "$projdir/hooks/$hook"; then - [ -n "$dryrun" ] || cat "$hookbin/$hook" >"$projdir/hooks/$hook" + if + ! [ -L "$projdir/hooks/$hook" ] || + [ "$(readlink "$projdir/hooks/$hook")" != "$globalhooks/$hook" ] + then + doln=1 updates="$updates $hook" fi elif ! [ -e "$projdir/hooks/$hook" ]; then [ -n "$dryrun" ] || [ -d "$projdir/hooks" ] || mkdir "$projdir/hooks" - [ -n "$dryrun" ] || cat "$hookbin/$hook" >"$projdir/hooks/$hook" - [ -n "$dryrun" ] || chmod 0775 "$projdir/hooks/$hook" + doln=1 updates="$updates +$hook" fi + if [ -z "$dryrun" ] && [ -n "$doln" ]; then + ln -sfn "$globalhooks/$hook" "$projdir/hooks/$hook" + fi done for hook in post-update; do if [ -e "$projdir/hooks/$hook" ]; then @@ -66,20 +73,6 @@ eval "$cmd" | fi updates="$updates mob/hooks@ -> ../hooks" fi - added= - while read -r fixfilex; do - case "$fixfilex" in ""|*.sample) :;; *) - #[ -n "$added" ] || { added=1; updates="$updates a+rx"; } - #[ -n "$dryrun" ] || chmod a+rx "$projdir/$fixfilex" - : - esac - done <<-EOT - $(cd "$projdir" - # find -P is not POSIX (it is the default though) but we absolutely - # do NOT want either -H or -L here because if either the directory or - # the hook itself is a symbolic link we have no business changing it! - ! [ -d hooks ] || find hooks -xdev -type f ! -perm -a+rx -print - EOT [ -z "$updates" ] || echo "$proj:$updates" ${dryrun:+(dryrun)} done ) -- 2.11.4.GIT