]> mj.ucw.cz Git - git-tools.git/blob - update2
Added a new update hook
[git-tools.git] / update2
1 #!/usr/bin/perl
2 # This is a generic update hook script for GIT repositories.
3 # Written by Martin Mares <mj@ucw.cz> and placed into public domain.
4
5 use strict;
6 use warnings;
7
8 use Getopt::Long;
9 use IO::File;
10 use File::Temp;
11
12 my $mail_to;
13 my $subject_prefix = "GIT";
14 my $max_diff_size;
15
16 GetOptions(
17         'mail-to=s' => \$mail_to,
18         'subject-prefix=s' => \$subject_prefix,
19         'max-diff-size=s' => \$max_diff_size,
20 ) and @ARGV == 3 or die <<AMEN ;
21 Usage: $0 [<options>] <refname> <sha1-old> <sha1-new>
22
23 Options:
24 --mail-to=<address>     Send mail to the given address
25 --max-diff-size=<bytes> If the diff is too long, send just a summary
26 --subject-prefix=<px>   Prefix subjects with [<px>] (default: GIT)
27 AMEN
28
29 my ($ref, $old, $new) = @ARGV;
30 $ref =~ s{^refs/heads/}{} or exit 0;
31 $old ne $new or exit 0;
32
33 my $subj = '[' . $subject_prefix . ($ref eq 'master' ? '' : "/$ref") . ']';
34 my $tmpdir = File::Temp->newdir() or die;
35 my $outname = "$tmpdir/0";
36 my $out = IO::File->new($outname, '>') or die;
37 $out->autoflush(1);
38 close STDOUT;
39 open STDOUT, '>&', $out or die;
40
41 my @rev_list_options = ('--pretty', '--no-abbrev', '--date=iso');
42 my @diff_options = ('-C');
43
44 sub scan_branches() {
45         # Is there any branch pointing to $new ?
46         for (`git branch -v --no-abbrev`) {
47                 chomp;
48                 my ($name, $sha) = /^..(\S+)\s+(\S+)/ or die;
49                 if ($name ne $ref && $sha eq $new) {
50                         return $name;
51                 }
52         }
53         return;
54 }
55
56 sub most_recent() {
57         print $out "Most recent commits:\n\n";
58         system 'git', 'rev-list', @rev_list_options, '--max-count=20', $new;
59 }
60
61 sub output_size() {
62         $out->seek(0, 2);
63         return $out->tell;
64 }
65
66 if ($old =~ /^0+$/) {
67         # Creation of a branch
68         $subj .= ' Created branch';
69         my $copy_of = scan_branches();
70         if (defined $copy_of) {
71                 $subj .= " as a copy of $copy_of";
72                 print $out "Created branch $ref as a copy of $copy_of.\n";
73         } else {
74                 print $out "Created branch $ref.\n\n";
75                 most_recent();
76         }
77 } elsif ($new =~ /^0+$/) {
78         # Deletion of a branch
79         $subj .= ' Branch deleted';
80         print $out "Deleted branch $ref.\n\nPrevious tip was $old.\n";
81 } else {
82         my $lca = `git merge-base $old $new`; die if $?;
83         chomp $lca;
84         if ($lca eq $old) {
85                 # Fast forward
86                 # Scan all commits first and construct subject
87                 my @commits = `git rev-list $old..$new --pretty=oneline --no-abbrev --no-merges`; $? and die;
88                 @commits or exit;
89                 my $c = $commits[0];
90                 chomp $c;
91                 $c =~ s{^\S+\s+}{};
92                 $subj .= " $c";
93
94                 # If there are multiple commits, print an overall diffstat first
95                 if (@commits > 1) {
96                         $subj .= ' [...]';
97                         print $out "Overall diffstat:\n\n";
98                         system 'git', 'diff', '--stat', $old, $new;
99                         print $out "\nCommits:\n\n";
100                 }
101                 my $pos_after_header = output_size();
102
103                 # Show individual commits with diffs and stats
104                 system 'git', 'log', @rev_list_options, '--reverse', @diff_options, '--patch', '--stat', "$old..$new";
105
106                 # If the file is too long, truncate it and print just a summary
107                 if (defined($max_diff_size) && output_size() > $max_diff_size) {
108                         $out->truncate($pos_after_header);
109                         output_size();
110                         print $out "Diff was too long, printing just a summary.\n\n";
111                         system 'git', 'log', @rev_list_options, '--reverse', "$old..$new";
112                 }
113         } elsif ($lca eq $new) {
114                 # Rewind
115                 $subj .= ' Branch rewound';
116                 print $out "Rewound branch $ref to commit $new.\n\n";
117                 most_recent();
118         } else {
119                 # Otherwise it is a rebase
120                 $subj .= ' Branch rebased';
121                 print $out "Rebased branch $ref to commit $new.\n\n";
122                 print $out "Commits from common ancestor:\n\n";
123                 system 'git', 'rev-list', @rev_list_options, $new, "^$old";
124         }
125 }
126
127 $out->close();
128 if (defined $mail_to) {
129         close STDIN;
130         open STDIN, '<', $outname;
131         system 'mutt',
132                 '-F/dev/null',
133                 '-x',
134                 '-e',
135                 'set charset="utf-8"; set send_charset="us-ascii:iso-8859-2:utf-8"',
136                 '-s', $subj,
137                 $mail_to;
138 } else {
139         print STDERR "Subject: $subj\n\n";
140         `cat >&2 $outname`;
141 }