2 # This is a generic update hook script for GIT repositories.
3 # Written by Martin Mares <mj@ucw.cz> and placed into public domain.
14 my $subject_prefix = "GIT";
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>
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)
30 my ($ref, $old, $new) = @ARGV;
31 $ref =~ s{^refs/heads/}{} or exit 0;
32 $old ne $new or exit 0;
34 my $repo = POSIX::getcwd();
37 my $subj = '[' . $subject_prefix . ($ref eq 'master' ? '' : "/$ref") . ']';
38 my $out = File::Temp->new() or die;
39 my $outname = $out->filename;
41 open ORIG_STDOUT, '>&', \*STDOUT;
43 open STDOUT, '>&', $out or die;
45 my @rev_list_options = ('--pretty', '--no-abbrev', '--date=iso');
46 my @diff_options = ('-C');
49 # Is there any branch pointing to $new ?
50 for (`git branch -v --no-abbrev`) {
52 my ($name, $sha) = /^..(\S+)\s+(\S+)/ or die;
53 if ($name ne $ref && $sha eq $new) {
62 for (`git rev-list $old..$new --pretty=format:"# %H (%P) %s"`) {
65 my ($hash, $parents, $subject) = m{^# (\S+) \(([^)]*)\) (.*)} or die;
68 parents => [ split /\s+/, $parents ],
76 print $out "Most recent commits:\n\n";
77 system 'git', 'rev-list', @rev_list_options, '--max-count=20', $new;
86 # Creation of a branch
87 $subj .= ' Created branch';
88 my $copy_of = scan_branches();
89 if (defined $copy_of) {
90 $subj .= " as a copy of $copy_of";
91 print $out "Created branch $ref as a copy of $copy_of ($new).\n";
93 print $out "Created branch $ref ($new).\n\n";
96 } elsif ($new =~ /^0+$/) {
97 # Deletion of a branch
98 $subj .= ' Branch deleted';
99 print $out "Deleted branch $ref ($old).\n";
101 my $lca = `git merge-base $old $new`; die if $?;
104 # Fast forward ... scan all objects
105 my @commits = scan_commits();
106 my @nonmerges = grep { @{$_->{parents}} == 1 } @commits;
110 # Try to recognize simple merges and display them as such
111 my $c0 = $commits[0];
112 my $n0 = $nonmerges[0];
113 my $c0p = $c0->{parents};
115 ($c0p->[0] eq $old || $c0p->[1] eq $old) &&
116 $c0->{subject} =~ m{^\s*Merge branch '([^']*)' into (\S+)} &&
117 (($1 eq $ref) != ($2 eq $ref))) {
118 # Pushed a merge of the current branch with another local branch
119 $subj .= ' ' . $c0->{subject};
121 # Otherwise take the subject of the first non-merge commit
122 $subj .= ' ' . $n0->{subject};
124 # If there is none, take the first merge
125 $subj .= ' ' . $c0->{subject};
128 print $out "Push to branch $ref ($old -> $new)\n\n";
130 # If there are multiple commits, mention that
131 if (@nonmerges > 1) {
132 $subj .= ' [' . (scalar @commits) . ' commits]';
133 print $out 'Pushed ', (scalar @commits), " commits. Overall diffstat:\n\n";
136 # Print an overall diffstat
137 system 'git', 'diff', '--stat', $old, $new;
139 my $pos_after_header = output_size();
141 # Show individual commits with diffs
142 system 'git', 'log', @rev_list_options, @diff_options, '-p', "$old..$new";
144 # If the file is too long, truncate it and print just a summary
145 if (defined($max_diff_size) && output_size() > $max_diff_size) {
146 $out->truncate($pos_after_header);
148 print $out "Diff was too long, printing just a summary.\n\n";
149 system 'git', 'log', @rev_list_options, "$old..$new";
151 } elsif ($lca eq $new) {
153 $subj .= ' Branch rewound';
154 print $out "Rewound branch $ref ($old -> $new).\n\n";
157 # Otherwise it is a rebase
158 $subj .= ' Branch rebased';
159 print $out "Rebased branch $ref ($old -> $new).\n\n";
160 print $out "Commits from common ancestor:\n\n";
161 system 'git', 'rev-list', @rev_list_options, $new, "^$old";
166 if (defined $mail_to) {
168 open STDIN, '<', $outname;
172 '-e', 'set charset="utf-8"',
173 '-e', 'set send_charset="us-ascii:iso-8859-2:utf-8"',
174 '-e', "my_hdr X-Git-Repo: $repo",
175 '-e', "my_hdr X-Git-Branch: $ref",
176 '-e', "my_hdr X-Git-Old-SHA: $old",
177 '-e', "my_hdr X-Git-New-SHA: $new",
181 open STDOUT, '>&', \*ORIG_STDOUT;
182 print "Subject: $subj\n\n";
183 system 'cat', $outname;