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