aboutsummaryrefslogtreecommitdiff
blob: 2c6091456acd8ce7838b2e59e302d8c50481e0d6 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
package Gitolite::Hooks::Update;

# everything to do with the update hook
# ----------------------------------------------------------------------

@EXPORT = qw(
  update
  update_hook
);

use Exporter 'import';

use lib $ENV{GL_BINDIR};
use Gitolite::Common;
use Gitolite::Conf::Load;

use strict;
use warnings;

# ----------------------------------------------------------------------

sub update {
    trace( 3, @_ );
    # this is the *real* update hook for gitolite

    my ( $ref, $oldsha, $newsha, $oldtree, $newtree, $aa ) = args(@ARGV);

    my $ret = access( $ENV{GL_REPO}, $ENV{GL_USER}, $aa, $ref );
    trace( 1, "access($ENV{GL_REPO}, $ENV{GL_USER}, $aa, $ref) -> $ret" );
    _die $ret if $ret =~ /DENIED/;

    exit 0;
}

{
    my $text = '';

    sub update_hook {
        trace(1);
        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;

    # XXX $aa = 'D' if ( $repos{$ENV{GL_REPO}}{DELETE_IS_D} or $repos{'@all'}{DELETE_IS_D} ) and $newsha eq '0' x 40;
    # XXX $aa = 'C' if ( $repos{$ENV{GL_REPO}}{CREATE_IS_C} or $repos{'@all'}{CREATE_IS_C} ) and $oldsha eq '0' x 40;

    # and now "M" commits.  This presents a bit of a problem.  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").

    # so we have to *append* 'M' to $aa (if the repo has MERGE_CHECK in
    # effect and this push contains a merge inside)

=for XXX
    if ( $repos{ $ENV{GL_REPO} }{MERGE_CHECK} or $repos{'@all'}{MERGE_CHECK} ) {
        if ( $oldsha eq '0' x 40 or $newsha eq '0' x 40 ) {
            warn "ref create/delete ignored for purposes of merge-check\n";
        } else {
            $aa .= 'M' if `git rev-list -n 1 --merges $oldsha..$newsha` =~ /./;
        }
    }
=cut

    return ( $ref, $oldsha, $newsha, $oldtree, $newtree, $aa );
}

1;

__DATA__
#!/usr/bin/perl

use strict;
use warnings;

BEGIN {
    exit 0 if $ENV{GL_BYPASS_UPDATE_HOOK};
    die "GL_BINDIR not set; aborting\n" unless $ENV{GL_BINDIR};
}
use lib $ENV{GL_BINDIR};
use Gitolite::Hooks::Update;

# gitolite update hook
# ----------------------------------------------------------------------

update(@ARGV);          # is not expected to return
exit 1;                 # so if it does, something is wrong