2012-03-08 09:00:13 +01:00
|
|
|
package Gitolite::Hooks::Update;
|
|
|
|
|
|
|
|
# everything to do with the update hook
|
|
|
|
# ----------------------------------------------------------------------
|
|
|
|
|
|
|
|
@EXPORT = qw(
|
|
|
|
update
|
|
|
|
update_hook
|
|
|
|
);
|
|
|
|
|
|
|
|
use Exporter 'import';
|
|
|
|
|
2012-03-19 03:01:09 +01:00
|
|
|
use Gitolite::Rc;
|
2012-03-08 09:00:13 +01:00
|
|
|
use Gitolite::Common;
|
|
|
|
use Gitolite::Conf::Load;
|
|
|
|
|
|
|
|
use strict;
|
|
|
|
use warnings;
|
|
|
|
|
|
|
|
# ----------------------------------------------------------------------
|
|
|
|
|
|
|
|
sub update {
|
|
|
|
# this is the *real* update hook for gitolite
|
|
|
|
|
2012-04-03 09:57:19 +02:00
|
|
|
bypass() if $ENV{GL_BYPASS_ACCESS_CHECKS};
|
|
|
|
|
2012-03-08 09:00:13 +01:00
|
|
|
my ( $ref, $oldsha, $newsha, $oldtree, $newtree, $aa ) = args(@ARGV);
|
|
|
|
|
2012-03-30 02:41:06 +02:00
|
|
|
trace( 1, 'update', $ENV{GL_REPO}, $ENV{GL_USER}, $aa, @ARGV );
|
|
|
|
|
2012-03-08 09:00:13 +01:00
|
|
|
my $ret = access( $ENV{GL_REPO}, $ENV{GL_USER}, $aa, $ref );
|
2012-03-27 12:48:25 +02:00
|
|
|
trigger( 'ACCESS_2', $ENV{GL_REPO}, $ENV{GL_USER}, $aa, $ref, $ret );
|
2012-03-08 09:00:13 +01:00
|
|
|
_die $ret if $ret =~ /DENIED/;
|
|
|
|
|
2012-03-12 16:24:30 +01:00
|
|
|
check_vrefs( $ref, $oldsha, $newsha, $oldtree, $newtree, $aa );
|
2012-03-11 03:06:42 +01:00
|
|
|
|
2012-03-30 02:41:06 +02:00
|
|
|
trace( 1, "-> $ret" );
|
|
|
|
gl_log( 'update', $ENV{GL_REPO}, $ENV{GL_USER}, $aa, @ARGV );
|
2012-03-08 09:00:13 +01:00
|
|
|
exit 0;
|
|
|
|
}
|
|
|
|
|
2012-04-03 09:57:19 +02:00
|
|
|
sub bypass {
|
|
|
|
require Cwd;
|
|
|
|
Cwd->import;
|
|
|
|
gl_log( 'update', getcwd(), '(' . ($ENV{USER} || '?') . ')', 'bypass', @ARGV );
|
|
|
|
exit 0;
|
|
|
|
}
|
|
|
|
|
2012-03-11 03:06:42 +01:00
|
|
|
sub check_vrefs {
|
2012-03-12 16:24:30 +01:00
|
|
|
my ( $ref, $oldsha, $newsha, $oldtree, $newtree, $aa ) = @_;
|
2012-03-11 03:06:42 +01:00
|
|
|
my $name_seen = 0;
|
2012-03-30 02:41:06 +02:00
|
|
|
my $n_vrefs = 0;
|
2012-03-12 16:24:30 +01:00
|
|
|
for my $vref ( vrefs( $ENV{GL_REPO}, $ENV{GL_USER} ) ) {
|
2012-03-21 11:53:50 +01:00
|
|
|
$n_vrefs++;
|
2012-03-12 16:24:30 +01:00
|
|
|
if ( $vref =~ m(^VREF/NAME/) ) {
|
2012-03-11 03:06:42 +01:00
|
|
|
# this one is special; we process it right here, and only once
|
|
|
|
next if $name_seen++;
|
|
|
|
|
|
|
|
for my $ref ( map { chomp; s(^)(VREF/NAME/); $_; } `git diff --name-only $oldtree $newtree` ) {
|
2012-03-12 16:24:30 +01:00
|
|
|
check_vref( $aa, $ref );
|
2012-03-11 03:06:42 +01:00
|
|
|
}
|
|
|
|
} else {
|
2012-03-12 16:24:30 +01:00
|
|
|
my ( $dummy, $pgm, @args ) = split '/', $vref;
|
2012-03-11 03:06:42 +01:00
|
|
|
$pgm = "$ENV{GL_BINDIR}/VREF/$pgm";
|
|
|
|
-x $pgm or die "$vref: helper program missing or unexecutable\n";
|
|
|
|
|
|
|
|
open( my $fh, "-|", $pgm, @_, $vref, @args ) or die "$vref: can't spawn helper program: $!\n";
|
|
|
|
while (<$fh>) {
|
|
|
|
my ( $ref, $deny_message ) = split( ' ', $_, 2 );
|
2012-03-12 16:24:30 +01:00
|
|
|
check_vref( $aa, $ref, $deny_message );
|
2012-03-11 03:06:42 +01:00
|
|
|
}
|
|
|
|
close($fh) or die $!
|
|
|
|
? "Error closing sort pipe: $!"
|
|
|
|
: "$vref: helper program exit status $?";
|
|
|
|
}
|
|
|
|
}
|
2012-03-21 11:53:50 +01:00
|
|
|
return $n_vrefs;
|
2012-03-11 03:06:42 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
sub check_vref {
|
2012-03-12 16:24:30 +01:00
|
|
|
my ( $aa, $ref, $deny_message ) = @_;
|
2012-03-11 03:06:42 +01:00
|
|
|
|
|
|
|
my $ret = access( $ENV{GL_REPO}, $ENV{GL_USER}, $aa, $ref );
|
2012-03-30 02:41:06 +02:00
|
|
|
trace( 2, "access($ENV{GL_REPO}, $ENV{GL_USER}, $aa, $ref)", "-> $ret" );
|
2012-03-11 03:06:42 +01:00
|
|
|
_die "$ret" . ( $deny_message ? "\n$deny_message" : '' )
|
2012-03-12 16:24:30 +01:00
|
|
|
if $ret =~ /DENIED/ and $ret !~ /by fallthru/;
|
2012-03-30 02:41:06 +02:00
|
|
|
trace( 2, "remember, fallthru is success here!" ) if $ret =~ /by fallthru/;
|
2012-03-11 03:06:42 +01:00
|
|
|
}
|
|
|
|
|
2012-03-08 09:00:13 +01:00
|
|
|
{
|
|
|
|
my $text = '';
|
|
|
|
|
|
|
|
sub update_hook {
|
|
|
|
if ( not $text ) {
|
|
|
|
local $/ = undef;
|
|
|
|
$text = <DATA>;
|
|
|
|
}
|
|
|
|
return $text;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
# ----------------------------------------------------------------------
|
|
|
|
|
|
|
|
sub args {
|
|
|
|
my ( $ref, $oldsha, $newsha ) = @_;
|
|
|
|
my ( $oldtree, $newtree, $aa );
|
|
|
|
|
|
|
|
# this is special to git -- the hash of an empty tree
|
|
|
|
my $empty = '4b825dc642cb6eb9a060e54bf8d69288fbee4904';
|
|
|
|
$oldtree = $oldsha eq '0' x 40 ? $empty : $oldsha;
|
|
|
|
$newtree = $newsha eq '0' x 40 ? $empty : $newsha;
|
|
|
|
|
|
|
|
my $merge_base = '0' x 40;
|
|
|
|
# for branch create or delete, merge_base stays at '0'x40
|
|
|
|
chomp( $merge_base = `git merge-base $oldsha $newsha` )
|
|
|
|
unless $oldsha eq '0' x 40
|
|
|
|
or $newsha eq '0' x 40;
|
|
|
|
|
|
|
|
$aa = 'W';
|
|
|
|
# tag rewrite
|
|
|
|
$aa = '+' if $ref =~ m(refs/tags/) and $oldsha ne ( '0' x 40 );
|
|
|
|
# non-ff push to ref (including ref delete)
|
|
|
|
$aa = '+' if $oldsha ne $merge_base;
|
|
|
|
|
2012-03-17 18:20:45 +01:00
|
|
|
$aa = 'D' if ( option( $ENV{GL_REPO}, 'DELETE_IS_D' ) ) and $newsha eq '0' x 40;
|
|
|
|
$aa = 'C' if ( option( $ENV{GL_REPO}, 'CREATE_IS_C' ) ) and $oldsha eq '0' x 40;
|
2012-03-08 09:00:13 +01:00
|
|
|
|
2012-03-17 18:20:45 +01:00
|
|
|
# and now "M" commits. All the other accesses (W, +, C, D) were mutually
|
|
|
|
# exclusive in some sense. Sure a W could be a C or a + could be a D but
|
|
|
|
# that's by design. A merge commit, however, could still be any of the
|
|
|
|
# others (except a "D").
|
2012-03-08 09:00:13 +01:00
|
|
|
|
|
|
|
# so we have to *append* 'M' to $aa (if the repo has MERGE_CHECK in
|
|
|
|
# effect and this push contains a merge inside)
|
|
|
|
|
2012-03-17 18:20:45 +01:00
|
|
|
if ( option( $ENV{GL_REPO}, 'MERGE_CHECK' ) ) {
|
2012-03-08 09:00:13 +01:00
|
|
|
if ( $oldsha eq '0' x 40 or $newsha eq '0' x 40 ) {
|
2012-03-17 18:20:45 +01:00
|
|
|
_warn "ref create/delete ignored for purposes of merge-check\n";
|
2012-03-08 09:00:13 +01:00
|
|
|
} else {
|
|
|
|
$aa .= 'M' if `git rev-list -n 1 --merges $oldsha..$newsha` =~ /./;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return ( $ref, $oldsha, $newsha, $oldtree, $newtree, $aa );
|
|
|
|
}
|
|
|
|
|
|
|
|
1;
|
|
|
|
|
|
|
|
__DATA__
|
|
|
|
#!/usr/bin/perl
|
|
|
|
|
|
|
|
use strict;
|
|
|
|
use warnings;
|
|
|
|
|
2012-04-06 16:59:05 +02:00
|
|
|
use lib $ENV{GL_LIBDIR};
|
2012-03-08 09:00:13 +01:00
|
|
|
use Gitolite::Hooks::Update;
|
|
|
|
|
|
|
|
# gitolite update hook
|
|
|
|
# ----------------------------------------------------------------------
|
|
|
|
|
2012-03-11 17:03:15 +01:00
|
|
|
update(); # is not expected to return
|
2012-03-08 09:00:13 +01:00
|
|
|
exit 1; # so if it does, something is wrong
|