#!/usr/bin/env perl # Some aspects of this approach are due to: # http://blog.bluefeet.net/2011/08/chained-git-hooks/ # see: http://web.archive.org/web/20111231092040/http://blog.bluefeet.net/2011/08/chained-git-hooks/ # # This implementation using IO::Pipe is # Copyright (C) 2020, 2022 Sean Whitton # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or (at # your option) any later version. # # This program is distributed in the hope that it will be useful, but # WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program. If not, see . # Use by symlinking this hook to .git/hooks/pre-push (or whichever # hook you want to chain) and then add your hooks as # .git/hooks/pre-push_01foo, .git/hooks/pre-push_02bar etc. use strict; use warnings; use IO::Pipe; use File::Basename qw(basename dirname); use File::Spec::Functions qw(abs2rel rel2abs); (my $hook_type = $0) =~ s{^.+/}{}; my @hooks; chomp(my $git_dir = `git rev-parse --absolute-git-dir`); my $rel = abs2rel dirname($git_dir), $ENV{HOME}; # Partially tidy up previous git hooks setup. -l and readlink =~ m#/chained_hook$# and unlink for "$git_dir/hooks/$hook_type"; for my $dir (grep -d, "$git_dir/hooks", "$ENV{HOME}/src/dotfiles/hooks/git/$rel") { opendir(my $dirh, $dir); push @hooks, grep -x, map rel2abs($_, $dir), sort grep /\A($hook_type\z|${hook_type}_)/, readdir $dirh; } my @stdin = ; foreach my $hook (@hooks) { my $pipe = IO::Pipe->new; my $pid = fork; die "fork() failed: $!" unless defined $pid; if ($pid) { $pipe->writer; print $pipe $_ for @stdin; $pipe->close; wait; # give up as soon as one hook fails; since we sort the hooks, # we can put more expensive hooks later in the sequence die "hook $hook exited non-zero" if $?; } else { $pipe->reader; open STDIN, "<&=" . $pipe->fileno or die "couldn't open child's STDIN"; exec $hook, @ARGV; } }