Merge branch 'js/diff-color-check'
[git] / templates / hooks--update
1 #!/bin/sh
2 #
3 # An example hook script to mail out commit update information.
4 # It can also blocks tags that aren't annotated.
5 # Called by git-receive-pack with arguments: refname sha1-old sha1-new
6 #
7 # To enable this hook, make this file executable by "chmod +x update".
8 #
9 # Config
10 # ------
11 # hooks.mailinglist
12 #   This is the list that all pushes will go to; leave it blank to not send
13 #   emails frequently.  The log email will list every log entry in full between
14 #   the old ref value and the new ref value.
15 # hooks.announcelist
16 #   This is the list that all pushes of annotated tags will go to.  Leave it
17 #   blank to just use the mailinglist field.  The announce emails list the
18 #   short log summary of the changes since the last annotated tag
19 # hooks.allowunannotated
20 #   This boolean sets whether unannotated tags will be allowed into the
21 #   repository.  By default they won't be.
22 #
23 # Notes
24 # -----
25 # All emails have their subjects prefixed with "[SCM]" to aid filtering.
26 # All emails include the headers "X-Git-Refname", "X-Git-Oldrev",
27 # "X-Git-Newrev", and "X-Git-Reftype" to enable fine tuned filtering and info.
28
29 # --- Constants
30 EMAILPREFIX="[SCM] "
31 LOGBEGIN="- Log -----------------------------------------------------------------"
32 LOGEND="-----------------------------------------------------------------------"
33 DATEFORMAT="%F %R %z"
34
35 # --- Command line
36 refname="$1"
37 oldrev="$2"
38 newrev="$3"
39
40 # --- Safety check
41 if [ -z "$GIT_DIR" ]; then
42         echo "Don't run this script from the command line." >&2
43         echo " (if you want, you could supply GIT_DIR then run" >&2
44         echo "  $0 <ref> <oldrev> <newrev>)" >&2
45         exit 1
46 fi
47
48 if [ -z "$refname" -o -z "$oldrev" -o -z "$newrev" ]; then
49         echo "Usage: $0 <ref> <oldrev> <newrev>" >&2
50         exit 1
51 fi
52
53 # --- Config
54 projectdesc=$(cat $GIT_DIR/description)
55 recipients=$(git-repo-config hooks.mailinglist)
56 announcerecipients=$(git-repo-config hooks.announcelist)
57 allowunannotated=$(git-repo-config --bool hooks.allowunannotated)
58
59 # --- Check types
60 newrev_type=$(git-cat-file -t $newrev)
61
62 case "$refname","$newrev_type" in
63         refs/tags/*,commit)
64                 # un-annotated tag
65                 refname_type="tag"
66                 short_refname=${refname##refs/tags/}
67                 if [ $allowunannotated != "true" ]; then
68                         echo "*** The un-annotated tag, $short_refname is not allowed in this repository" >&2
69                         echo "*** Use 'git tag [ -a | -s ]' for tags you want to propagate." >&2
70                         exit 1
71                 fi
72                 ;;
73         refs/tags/*,tag)
74                 # annotated tag
75                 refname_type="annotated tag"
76                 short_refname=${refname##refs/tags/}
77                 # change recipients
78                 if [ -n "$announcerecipients" ]; then
79                         recipients="$announcerecipients"
80                 fi
81                 ;;
82         refs/heads/*,commit)
83                 # branch
84                 refname_type="branch"
85                 short_refname=${refname##refs/heads/}
86                 ;;
87         refs/remotes/*,commit)
88                 # tracking branch
89                 refname_type="tracking branch"
90                 short_refname=${refname##refs/remotes/}
91                 # Should this even be allowed?
92                 echo "*** Push-update of tracking branch, $refname.  No email generated." >&2
93                 exit 0
94                 ;;
95         *)
96                 # Anything else (is there anything else?)
97                 echo "*** Update hook: unknown type of update, \"$newrev_type\", to ref $refname" >&2
98                 exit 1
99                 ;;
100 esac
101
102 # Check if we've got anyone to send to
103 if [ -z "$recipients" ]; then
104         # If the email isn't sent, then at least give the user some idea of what command
105         # would generate the email at a later date
106         echo "*** No recipients found - no email will be sent, but the push will continue" >&2
107         echo "*** for $0 $1 $2 $3" >&2
108         exit 0
109 fi
110
111 # --- Email parameters
112 committer=$(git show --pretty=full -s $newrev | grep "^Commit: " | sed -e "s/^Commit: //")
113 describe=$(git describe $newrev 2>/dev/null)
114 if [ -z "$describe" ]; then
115         describe=$newrev
116 fi
117
118 # --- Email (all stdout will be the email)
119 (
120 # Generate header
121 cat <<-EOF
122 From: $committer
123 To: $recipients
124 Subject: ${EMAILPREFIX}$projectdesc $refname_type, $short_refname now at $describe
125 X-Git-Refname: $refname
126 X-Git-Reftype: $refname_type
127 X-Git-Oldrev: $oldrev
128 X-Git-Newrev: $newrev
129
130 Hello,
131
132 This is an automated email from the git hooks/update script, it was
133 generated because a ref change was pushed to the repository.
134
135 Updating $refname_type, $short_refname,
136 EOF
137
138 case "$refname_type" in
139         "tracking branch"|branch)
140                 if expr "$oldrev" : '0*$' >/dev/null
141                 then
142                         # If the old reference is "0000..0000" then this is a new branch
143                         # and so oldrev is not valid
144                         echo "  as a new  $refname_type"
145                     echo "        to  $newrev ($newrev_type)"
146                         echo ""
147                         echo $LOGBEGIN
148                         # This shows all log entries that are not already covered by
149                         # another ref - i.e. commits that are now accessible from this
150                         # ref that were previously not accessible
151                         git-rev-parse --not --all | git-rev-list --stdin --pretty $newref
152                         echo $LOGEND
153                 else
154                         # oldrev is valid
155                         oldrev_type=$(git-cat-file -t "$oldrev")
156
157                         # Now the problem is for cases like this:
158                         #   * --- * --- * --- * (oldrev)
159                         #          \
160                         #           * --- * --- * (newrev)
161                         # i.e. there is no guarantee that newrev is a strict subset
162                         # of oldrev - (would have required a force, but that's allowed).
163                         # So, we can't simply say rev-list $oldrev..$newrev.  Instead
164                         # we find the common base of the two revs and list from there
165                         baserev=$(git-merge-base $oldrev $newrev)
166
167                         # Commit with a parent
168                         for rev in $(git-rev-parse --not --all | git-rev-list --stdin $newrev ^$baserev)
169                         do
170                                 revtype=$(git-cat-file -t "$rev")
171                                 echo "       via  $rev ($revtype)"
172                         done
173                         if [ "$baserev" = "$oldrev" ]; then
174                                 echo "      from  $oldrev ($oldrev_type)"
175                         else
176                                 echo "  based on  $baserev"
177                                 echo "      from  $oldrev ($oldrev_type)"
178                                 echo ""
179                                 echo "This ref update crossed a branch point; i.e. the old rev is not a strict subset"
180                                 echo "of the new rev.  This occurs, when you --force push a change in a situation"
181                                 echo "like this:"
182                                 echo ""
183                                 echo " * -- * -- B -- O -- O -- O ($oldrev)"
184                                 echo "            \\"
185                                 echo "             N -- N -- N ($newrev)"
186                                 echo ""
187                                 echo "Therefore, we assume that you've already had alert emails for all of the O"
188                                 echo "revisions, and now give you all the revisions in the N branch from the common"
189                                 echo "base, B ($baserev), up to the new revision."
190                         fi
191                         echo ""
192                         echo $LOGBEGIN
193                         git-rev-parse --not --all |
194                         git-rev-list --stdin --pretty $newrev ^$baserev
195                         echo $LOGEND
196                         echo ""
197                         echo "Diffstat:"
198                         git-diff-tree --no-color --stat -M -C --find-copies-harder $newrev ^$baserev
199                 fi
200                 ;;
201         "annotated tag")
202                 # Should we allow changes to annotated tags?
203                 if expr "$oldrev" : '0*$' >/dev/null
204                 then
205                         # If the old reference is "0000..0000" then this is a new atag
206                         # and so oldrev is not valid
207                         echo "        to  $newrev ($newrev_type)"
208                 else
209                         echo "        to  $newrev ($newrev_type)"
210                         echo "      from  $oldrev"
211                 fi
212
213                 # If this tag succeeds another, then show which tag it replaces
214                 prevtag=$(git describe $newrev^ 2>/dev/null | sed 's/-g.*//')
215                 if [ -n "$prevtag" ]; then
216                         echo "  replaces  $prevtag"
217                 fi
218
219                 # Read the tag details
220                 eval $(git cat-file tag $newrev | \
221                         sed -n '4s/tagger \([^>]*>\)[^0-9]*\([0-9]*\).*/tagger="\1" ts="\2"/p')
222                 tagged=$(date --date="1970-01-01 00:00:00 +0000 $ts seconds" +"$DATEFORMAT")
223
224                 echo " tagged by  $tagger"
225                 echo "        on  $tagged"
226
227                 echo ""
228                 echo $LOGBEGIN
229                 echo ""
230
231                 if [ -n "$prevtag" ]; then
232                         git rev-list --pretty=short "$prevtag..$newrev" | git shortlog
233                 else
234                         git rev-list --pretty=short $newrev | git shortlog
235                 fi
236
237                 echo $LOGEND
238                 echo ""
239                 ;;
240         *)
241                 # By default, unannotated tags aren't allowed in; if
242                 # they are though, it's debatable whether we would even want an
243                 # email to be generated; however, I don't want to add another config
244                 # option just for that.
245                 #
246                 # Unannotated tags are more about marking a point than releasing
247                 # a version; therefore we don't do the shortlog summary that we
248                 # do for annotated tags above - we simply show that the point has
249                 # been marked, and print the log message for the marked point for
250                 # reference purposes
251                 #
252                 # Note this section also catches any other reference type (although
253                 # there aren't any) and deals with them in the same way.
254                 if expr "$oldrev" : '0*$' >/dev/null
255                 then
256                         # If the old reference is "0000..0000" then this is a new tag
257                         # and so oldrev is not valid
258                         echo "  as a new  $refname_type"
259                         echo "        to  $newrev ($newrev_type)"
260                 else
261                         echo "        to  $newrev ($newrev_type)"
262                         echo "      from  $oldrev"
263                 fi
264                 echo ""
265                 echo $LOGBEGIN
266                 git-show --no-color --root -s $newrev
267                 echo $LOGEND
268                 echo ""
269                 ;;
270 esac
271
272 # Footer
273 cat <<-EOF
274
275 hooks/update
276 ---
277 Git Source Code Management System
278 $0 $1 \\
279   $2 \\
280   $3
281 EOF
282 #) | cat >&2
283 ) | /usr/sbin/sendmail -t
284
285 # --- Finished
286 exit 0