Andy Parkins | 4557e0d | 2007-03-30 19:16:26 +0000 | [diff] [blame] | 1 | #!/bin/sh |
| 2 | # |
| 3 | # Copyright (c) 2007 Andy Parkins |
| 4 | # |
Gerrit Pape | 15a2f53 | 2007-11-06 13:48:34 +0000 | [diff] [blame] | 5 | # An example hook script to mail out commit update information. This hook |
| 6 | # sends emails listing new revisions to the repository introduced by the |
| 7 | # change being reported. The rule is that (for branch updates) each commit |
| 8 | # will appear on one email and one email only. |
Andy Parkins | 4557e0d | 2007-03-30 19:16:26 +0000 | [diff] [blame] | 9 | # |
Gerrit Pape | 15a2f53 | 2007-11-06 13:48:34 +0000 | [diff] [blame] | 10 | # This hook is stored in the contrib/hooks directory. Your distribution |
| 11 | # will have put this somewhere standard. You should make this script |
| 12 | # executable then link to it in the repository you would like to use it in. |
| 13 | # For example, on debian the hook is stored in |
| 14 | # /usr/share/doc/git-core/contrib/hooks/post-receive-email: |
Andy Parkins | 4557e0d | 2007-03-30 19:16:26 +0000 | [diff] [blame] | 15 | # |
| 16 | # chmod a+x post-receive-email |
| 17 | # cd /path/to/your/repository.git |
| 18 | # ln -sf /usr/share/doc/git-core/contrib/hooks/post-receive-email hooks/post-receive |
| 19 | # |
Gerrit Pape | 15a2f53 | 2007-11-06 13:48:34 +0000 | [diff] [blame] | 20 | # This hook script assumes it is enabled on the central repository of a |
| 21 | # project, with all users pushing only to it and not between each other. It |
| 22 | # will still work if you don't operate in that style, but it would become |
| 23 | # possible for the email to be from someone other than the person doing the |
| 24 | # push. |
Andy Parkins | 4557e0d | 2007-03-30 19:16:26 +0000 | [diff] [blame] | 25 | # |
Jonathan Nieder | cc24a1d | 2010-05-19 14:01:47 -0500 | [diff] [blame] | 26 | # To help with debugging and use on pre-v1.5.1 git servers, this script will |
| 27 | # also obey the interface of hooks/update, taking its arguments on the |
| 28 | # command line. Unfortunately, hooks/update is called once for each ref. |
| 29 | # To avoid firing one email per ref, this script just prints its output to |
| 30 | # the screen when used in this mode. The output can then be redirected if |
| 31 | # wanted. |
| 32 | # |
Andy Parkins | 4557e0d | 2007-03-30 19:16:26 +0000 | [diff] [blame] | 33 | # Config |
| 34 | # ------ |
| 35 | # hooks.mailinglist |
| 36 | # This is the list that all pushes will go to; leave it blank to not send |
| 37 | # emails for every ref update. |
| 38 | # hooks.announcelist |
| 39 | # This is the list that all pushes of annotated tags will go to. Leave it |
Gerrit Pape | 15a2f53 | 2007-11-06 13:48:34 +0000 | [diff] [blame] | 40 | # blank to default to the mailinglist field. The announce emails lists |
| 41 | # the short log summary of the changes since the last annotated tag. |
Gerrit Pape | b5786c8 | 2007-11-06 13:48:07 +0000 | [diff] [blame] | 42 | # hooks.envelopesender |
Gerrit Pape | 15a2f53 | 2007-11-06 13:48:34 +0000 | [diff] [blame] | 43 | # If set then the -f option is passed to sendmail to allow the envelope |
| 44 | # sender address to be set |
Gerrit Pape | e7509ee | 2007-11-06 13:49:30 +0000 | [diff] [blame] | 45 | # hooks.emailprefix |
| 46 | # All emails have their subjects prefixed with this prefix, or "[SCM]" |
| 47 | # if emailprefix is unset, to aid filtering |
Pete Harlan | b0a7d11 | 2008-11-03 23:19:54 -0800 | [diff] [blame] | 48 | # hooks.showrev |
| 49 | # The shell command used to format each revision in the email, with |
| 50 | # "%s" replaced with the commit id. Defaults to "git rev-list -1 |
| 51 | # --pretty %s", displaying the commit id, author, date and log |
| 52 | # message. To list full patches separated by a blank line, you |
| 53 | # could set this to "git show -C %s; echo". |
Jim Meyering | 5ffd311 | 2009-05-23 14:26:44 +0200 | [diff] [blame] | 54 | # To list a gitweb/cgit URL *and* a full patch for each change set, use this: |
| 55 | # "t=%s; printf 'http://.../?id=%%s' \$t; echo;echo; git show -C \$t; echo" |
| 56 | # Be careful if "..." contains things that will be expanded by shell "eval" |
| 57 | # or printf. |
Andy Parkins | 4557e0d | 2007-03-30 19:16:26 +0000 | [diff] [blame] | 58 | # |
| 59 | # Notes |
| 60 | # ----- |
Andy Parkins | 4557e0d | 2007-03-30 19:16:26 +0000 | [diff] [blame] | 61 | # All emails include the headers "X-Git-Refname", "X-Git-Oldrev", |
| 62 | # "X-Git-Newrev", and "X-Git-Reftype" to enable fine tuned filtering and |
| 63 | # give information for debugging. |
| 64 | # |
| 65 | |
| 66 | # ---------------------------- Functions |
| 67 | |
| 68 | # |
| 69 | # Top level email generation function. This decides what type of update |
| 70 | # this is and calls the appropriate body-generation routine after outputting |
| 71 | # the common header |
| 72 | # |
Gerrit Pape | 15a2f53 | 2007-11-06 13:48:34 +0000 | [diff] [blame] | 73 | # Note this function doesn't actually generate any email output, that is |
| 74 | # taken care of by the functions it calls: |
Andy Parkins | 4557e0d | 2007-03-30 19:16:26 +0000 | [diff] [blame] | 75 | # - generate_email_header |
| 76 | # - generate_create_XXXX_email |
| 77 | # - generate_update_XXXX_email |
| 78 | # - generate_delete_XXXX_email |
| 79 | # - generate_email_footer |
| 80 | # |
| 81 | generate_email() |
| 82 | { |
| 83 | # --- Arguments |
| 84 | oldrev=$(git rev-parse $1) |
| 85 | newrev=$(git rev-parse $2) |
| 86 | refname="$3" |
| 87 | |
| 88 | # --- Interpret |
| 89 | # 0000->1234 (create) |
| 90 | # 1234->2345 (update) |
| 91 | # 2345->0000 (delete) |
| 92 | if expr "$oldrev" : '0*$' >/dev/null |
| 93 | then |
| 94 | change_type="create" |
| 95 | else |
| 96 | if expr "$newrev" : '0*$' >/dev/null |
| 97 | then |
| 98 | change_type="delete" |
| 99 | else |
| 100 | change_type="update" |
| 101 | fi |
| 102 | fi |
| 103 | |
| 104 | # --- Get the revision types |
| 105 | newrev_type=$(git cat-file -t $newrev 2> /dev/null) |
| 106 | oldrev_type=$(git cat-file -t "$oldrev" 2> /dev/null) |
| 107 | case "$change_type" in |
| 108 | create|update) |
| 109 | rev="$newrev" |
| 110 | rev_type="$newrev_type" |
| 111 | ;; |
| 112 | delete) |
| 113 | rev="$oldrev" |
| 114 | rev_type="$oldrev_type" |
| 115 | ;; |
| 116 | esac |
| 117 | |
| 118 | # The revision type tells us what type the commit is, combined with |
| 119 | # the location of the ref we can decide between |
| 120 | # - working branch |
| 121 | # - tracking branch |
| 122 | # - unannoted tag |
| 123 | # - annotated tag |
| 124 | case "$refname","$rev_type" in |
| 125 | refs/tags/*,commit) |
| 126 | # un-annotated tag |
| 127 | refname_type="tag" |
| 128 | short_refname=${refname##refs/tags/} |
| 129 | ;; |
| 130 | refs/tags/*,tag) |
| 131 | # annotated tag |
| 132 | refname_type="annotated tag" |
| 133 | short_refname=${refname##refs/tags/} |
| 134 | # change recipients |
| 135 | if [ -n "$announcerecipients" ]; then |
| 136 | recipients="$announcerecipients" |
| 137 | fi |
| 138 | ;; |
| 139 | refs/heads/*,commit) |
| 140 | # branch |
| 141 | refname_type="branch" |
| 142 | short_refname=${refname##refs/heads/} |
| 143 | ;; |
| 144 | refs/remotes/*,commit) |
| 145 | # tracking branch |
| 146 | refname_type="tracking branch" |
| 147 | short_refname=${refname##refs/remotes/} |
| 148 | echo >&2 "*** Push-update of tracking branch, $refname" |
| 149 | echo >&2 "*** - no email generated." |
| 150 | exit 0 |
| 151 | ;; |
| 152 | *) |
| 153 | # Anything else (is there anything else?) |
| 154 | echo >&2 "*** Unknown type of update to $refname ($rev_type)" |
| 155 | echo >&2 "*** - no email generated" |
| 156 | exit 1 |
| 157 | ;; |
| 158 | esac |
| 159 | |
| 160 | # Check if we've got anyone to send to |
| 161 | if [ -z "$recipients" ]; then |
Jeff Muizelaar | fdfeb87 | 2007-10-11 17:49:21 -0400 | [diff] [blame] | 162 | case "$refname_type" in |
| 163 | "annotated tag") |
| 164 | config_name="hooks.announcelist" |
| 165 | ;; |
| 166 | *) |
| 167 | config_name="hooks.mailinglist" |
| 168 | ;; |
| 169 | esac |
| 170 | echo >&2 "*** $config_name is not set so no email will be sent" |
Andy Parkins | 4557e0d | 2007-03-30 19:16:26 +0000 | [diff] [blame] | 171 | echo >&2 "*** for $refname update $oldrev->$newrev" |
| 172 | exit 0 |
| 173 | fi |
| 174 | |
| 175 | # Email parameters |
Andy Parkins | 4557e0d | 2007-03-30 19:16:26 +0000 | [diff] [blame] | 176 | # The email subject will contain the best description of the ref |
| 177 | # that we can build from the parameters |
| 178 | describe=$(git describe $rev 2>/dev/null) |
| 179 | if [ -z "$describe" ]; then |
| 180 | describe=$rev |
| 181 | fi |
| 182 | |
| 183 | generate_email_header |
| 184 | |
| 185 | # Call the correct body generation function |
| 186 | fn_name=general |
| 187 | case "$refname_type" in |
| 188 | "tracking branch"|branch) |
| 189 | fn_name=branch |
| 190 | ;; |
| 191 | "annotated tag") |
| 192 | fn_name=atag |
| 193 | ;; |
| 194 | esac |
| 195 | generate_${change_type}_${fn_name}_email |
| 196 | |
| 197 | generate_email_footer |
| 198 | } |
| 199 | |
| 200 | generate_email_header() |
| 201 | { |
| 202 | # --- Email (all stdout will be the email) |
| 203 | # Generate header |
| 204 | cat <<-EOF |
Andy Parkins | 4557e0d | 2007-03-30 19:16:26 +0000 | [diff] [blame] | 205 | To: $recipients |
Gerrit Pape | e7509ee | 2007-11-06 13:49:30 +0000 | [diff] [blame] | 206 | Subject: ${emailprefix}$projectdesc $refname_type, $short_refname, ${change_type}d. $describe |
Andy Parkins | 4557e0d | 2007-03-30 19:16:26 +0000 | [diff] [blame] | 207 | X-Git-Refname: $refname |
| 208 | X-Git-Reftype: $refname_type |
| 209 | X-Git-Oldrev: $oldrev |
| 210 | X-Git-Newrev: $newrev |
| 211 | |
| 212 | This is an automated email from the git hooks/post-receive script. It was |
| 213 | generated because a ref change was pushed to the repository containing |
| 214 | the project "$projectdesc". |
| 215 | |
| 216 | The $refname_type, $short_refname has been ${change_type}d |
| 217 | EOF |
| 218 | } |
| 219 | |
| 220 | generate_email_footer() |
| 221 | { |
Andy Parkins | 71bd81a | 2008-04-21 14:44:44 +0100 | [diff] [blame] | 222 | SPACE=" " |
Andy Parkins | 4557e0d | 2007-03-30 19:16:26 +0000 | [diff] [blame] | 223 | cat <<-EOF |
| 224 | |
| 225 | |
| 226 | hooks/post-receive |
Andy Parkins | 71bd81a | 2008-04-21 14:44:44 +0100 | [diff] [blame] | 227 | --${SPACE} |
Andy Parkins | 4557e0d | 2007-03-30 19:16:26 +0000 | [diff] [blame] | 228 | $projectdesc |
| 229 | EOF |
| 230 | } |
| 231 | |
| 232 | # --------------- Branches |
| 233 | |
| 234 | # |
| 235 | # Called for the creation of a branch |
| 236 | # |
| 237 | generate_create_branch_email() |
| 238 | { |
| 239 | # This is a new branch and so oldrev is not valid |
| 240 | echo " at $newrev ($newrev_type)" |
| 241 | echo "" |
| 242 | |
| 243 | echo $LOGBEGIN |
Pete Harlan | 4471649 | 2008-11-03 23:19:53 -0800 | [diff] [blame] | 244 | show_new_revisions |
Andy Parkins | 4557e0d | 2007-03-30 19:16:26 +0000 | [diff] [blame] | 245 | echo $LOGEND |
| 246 | } |
| 247 | |
| 248 | # |
| 249 | # Called for the change of a pre-existing branch |
| 250 | # |
| 251 | generate_update_branch_email() |
| 252 | { |
| 253 | # Consider this: |
| 254 | # 1 --- 2 --- O --- X --- 3 --- 4 --- N |
| 255 | # |
| 256 | # O is $oldrev for $refname |
| 257 | # N is $newrev for $refname |
| 258 | # X is a revision pointed to by some other ref, for which we may |
| 259 | # assume that an email has already been generated. |
| 260 | # In this case we want to issue an email containing only revisions |
| 261 | # 3, 4, and N. Given (almost) by |
| 262 | # |
Dan McGee | 22fa97d | 2008-01-13 22:51:01 -0600 | [diff] [blame] | 263 | # git rev-list N ^O --not --all |
Andy Parkins | 4557e0d | 2007-03-30 19:16:26 +0000 | [diff] [blame] | 264 | # |
| 265 | # The reason for the "almost", is that the "--not --all" will take |
| 266 | # precedence over the "N", and effectively will translate to |
| 267 | # |
Dan McGee | 22fa97d | 2008-01-13 22:51:01 -0600 | [diff] [blame] | 268 | # git rev-list N ^O ^X ^N |
Andy Parkins | 4557e0d | 2007-03-30 19:16:26 +0000 | [diff] [blame] | 269 | # |
Dan McGee | 22fa97d | 2008-01-13 22:51:01 -0600 | [diff] [blame] | 270 | # So, we need to build up the list more carefully. git rev-parse |
| 271 | # will generate a list of revs that may be fed into git rev-list. |
Gerrit Pape | 15a2f53 | 2007-11-06 13:48:34 +0000 | [diff] [blame] | 272 | # We can get it to make the "--not --all" part and then filter out |
| 273 | # the "^N" with: |
Andy Parkins | 4557e0d | 2007-03-30 19:16:26 +0000 | [diff] [blame] | 274 | # |
Dan McGee | 22fa97d | 2008-01-13 22:51:01 -0600 | [diff] [blame] | 275 | # git rev-parse --not --all | grep -v N |
Andy Parkins | 4557e0d | 2007-03-30 19:16:26 +0000 | [diff] [blame] | 276 | # |
Dan McGee | 22fa97d | 2008-01-13 22:51:01 -0600 | [diff] [blame] | 277 | # Then, using the --stdin switch to git rev-list we have effectively |
Andy Parkins | 4557e0d | 2007-03-30 19:16:26 +0000 | [diff] [blame] | 278 | # manufactured |
| 279 | # |
Dan McGee | 22fa97d | 2008-01-13 22:51:01 -0600 | [diff] [blame] | 280 | # git rev-list N ^O ^X |
Andy Parkins | 4557e0d | 2007-03-30 19:16:26 +0000 | [diff] [blame] | 281 | # |
| 282 | # This leaves a problem when someone else updates the repository |
Gerrit Pape | 15a2f53 | 2007-11-06 13:48:34 +0000 | [diff] [blame] | 283 | # while this script is running. Their new value of the ref we're |
| 284 | # working on would be included in the "--not --all" output; and as |
| 285 | # our $newrev would be an ancestor of that commit, it would exclude |
| 286 | # all of our commits. What we really want is to exclude the current |
| 287 | # value of $refname from the --not list, rather than N itself. So: |
Andy Parkins | 4557e0d | 2007-03-30 19:16:26 +0000 | [diff] [blame] | 288 | # |
Dan McGee | 22fa97d | 2008-01-13 22:51:01 -0600 | [diff] [blame] | 289 | # git rev-parse --not --all | grep -v $(git rev-parse $refname) |
Andy Parkins | 4557e0d | 2007-03-30 19:16:26 +0000 | [diff] [blame] | 290 | # |
Gerrit Pape | 15a2f53 | 2007-11-06 13:48:34 +0000 | [diff] [blame] | 291 | # Get's us to something pretty safe (apart from the small time |
Dan McGee | 22fa97d | 2008-01-13 22:51:01 -0600 | [diff] [blame] | 292 | # between refname being read, and git rev-parse running - for that, |
Gerrit Pape | 15a2f53 | 2007-11-06 13:48:34 +0000 | [diff] [blame] | 293 | # I give up) |
Andy Parkins | 4557e0d | 2007-03-30 19:16:26 +0000 | [diff] [blame] | 294 | # |
| 295 | # |
| 296 | # Next problem, consider this: |
| 297 | # * --- B --- * --- O ($oldrev) |
| 298 | # \ |
| 299 | # * --- X --- * --- N ($newrev) |
| 300 | # |
Gerrit Pape | 15a2f53 | 2007-11-06 13:48:34 +0000 | [diff] [blame] | 301 | # That is to say, there is no guarantee that oldrev is a strict |
| 302 | # subset of newrev (it would have required a --force, but that's |
| 303 | # allowed). So, we can't simply say rev-list $oldrev..$newrev. |
| 304 | # Instead we find the common base of the two revs and list from |
| 305 | # there. |
Andy Parkins | 4557e0d | 2007-03-30 19:16:26 +0000 | [diff] [blame] | 306 | # |
Gerrit Pape | 15a2f53 | 2007-11-06 13:48:34 +0000 | [diff] [blame] | 307 | # As above, we need to take into account the presence of X; if |
| 308 | # another branch is already in the repository and points at some of |
| 309 | # the revisions that we are about to output - we don't want them. |
Dan McGee | 22fa97d | 2008-01-13 22:51:01 -0600 | [diff] [blame] | 310 | # The solution is as before: git rev-parse output filtered. |
Andy Parkins | 4557e0d | 2007-03-30 19:16:26 +0000 | [diff] [blame] | 311 | # |
Gerrit Pape | 15a2f53 | 2007-11-06 13:48:34 +0000 | [diff] [blame] | 312 | # Finally, tags: 1 --- 2 --- O --- T --- 3 --- 4 --- N |
Andy Parkins | 4557e0d | 2007-03-30 19:16:26 +0000 | [diff] [blame] | 313 | # |
| 314 | # Tags pushed into the repository generate nice shortlog emails that |
| 315 | # summarise the commits between them and the previous tag. However, |
| 316 | # those emails don't include the full commit messages that we output |
| 317 | # for a branch update. Therefore we still want to output revisions |
| 318 | # that have been output on a tag email. |
| 319 | # |
Dan McGee | 22fa97d | 2008-01-13 22:51:01 -0600 | [diff] [blame] | 320 | # Luckily, git rev-parse includes just the tool. Instead of using |
Gerrit Pape | 15a2f53 | 2007-11-06 13:48:34 +0000 | [diff] [blame] | 321 | # "--all" we use "--branches"; this has the added benefit that |
| 322 | # "remotes/" will be ignored as well. |
Andy Parkins | 4557e0d | 2007-03-30 19:16:26 +0000 | [diff] [blame] | 323 | |
Gerrit Pape | 15a2f53 | 2007-11-06 13:48:34 +0000 | [diff] [blame] | 324 | # List all of the revisions that were removed by this update, in a |
Felipe Contreras | a75d7b5 | 2009-10-24 11:31:32 +0300 | [diff] [blame] | 325 | # fast-forward update, this list will be empty, because rev-list O |
| 326 | # ^N is empty. For a non-fast-forward, O ^N is the list of removed |
Gerrit Pape | 15a2f53 | 2007-11-06 13:48:34 +0000 | [diff] [blame] | 327 | # revisions |
Andy Parkins | 8e404f8 | 2007-04-26 22:35:39 +0100 | [diff] [blame] | 328 | fast_forward="" |
Andy Parkins | 4557e0d | 2007-03-30 19:16:26 +0000 | [diff] [blame] | 329 | rev="" |
| 330 | for rev in $(git rev-list $newrev..$oldrev) |
| 331 | do |
| 332 | revtype=$(git cat-file -t "$rev") |
| 333 | echo " discards $rev ($revtype)" |
| 334 | done |
| 335 | if [ -z "$rev" ]; then |
| 336 | fast_forward=1 |
| 337 | fi |
| 338 | |
| 339 | # List all the revisions from baserev to newrev in a kind of |
Gerrit Pape | 15a2f53 | 2007-11-06 13:48:34 +0000 | [diff] [blame] | 340 | # "table-of-contents"; note this list can include revisions that |
| 341 | # have already had notification emails and is present to show the |
| 342 | # full detail of the change from rolling back the old revision to |
| 343 | # the base revision and then forward to the new revision |
Andy Parkins | 4557e0d | 2007-03-30 19:16:26 +0000 | [diff] [blame] | 344 | for rev in $(git rev-list $oldrev..$newrev) |
| 345 | do |
| 346 | revtype=$(git cat-file -t "$rev") |
| 347 | echo " via $rev ($revtype)" |
| 348 | done |
| 349 | |
Robert Schiele | a2d6b87 | 2007-10-18 00:27:51 +0200 | [diff] [blame] | 350 | if [ "$fast_forward" ]; then |
Andy Parkins | 4557e0d | 2007-03-30 19:16:26 +0000 | [diff] [blame] | 351 | echo " from $oldrev ($oldrev_type)" |
| 352 | else |
Gerrit Pape | 15a2f53 | 2007-11-06 13:48:34 +0000 | [diff] [blame] | 353 | # 1. Existing revisions were removed. In this case newrev |
| 354 | # is a subset of oldrev - this is the reverse of a |
| 355 | # fast-forward, a rewind |
| 356 | # 2. New revisions were added on top of an old revision, |
| 357 | # this is a rewind and addition. |
Andy Parkins | 024e5b3 | 2007-04-26 22:36:24 +0100 | [diff] [blame] | 358 | |
Gerrit Pape | 15a2f53 | 2007-11-06 13:48:34 +0000 | [diff] [blame] | 359 | # (1) certainly happened, (2) possibly. When (2) hasn't |
| 360 | # happened, we set a flag to indicate that no log printout |
| 361 | # is required. |
Andy Parkins | 024e5b3 | 2007-04-26 22:36:24 +0100 | [diff] [blame] | 362 | |
Andy Parkins | 4557e0d | 2007-03-30 19:16:26 +0000 | [diff] [blame] | 363 | echo "" |
Andy Parkins | 024e5b3 | 2007-04-26 22:36:24 +0100 | [diff] [blame] | 364 | |
Gerrit Pape | 15a2f53 | 2007-11-06 13:48:34 +0000 | [diff] [blame] | 365 | # Find the common ancestor of the old and new revisions and |
| 366 | # compare it with newrev |
Andy Parkins | 024e5b3 | 2007-04-26 22:36:24 +0100 | [diff] [blame] | 367 | baserev=$(git merge-base $oldrev $newrev) |
| 368 | rewind_only="" |
| 369 | if [ "$baserev" = "$newrev" ]; then |
| 370 | echo "This update discarded existing revisions and left the branch pointing at" |
| 371 | echo "a previous point in the repository history." |
| 372 | echo "" |
| 373 | echo " * -- * -- N ($newrev)" |
| 374 | echo " \\" |
| 375 | echo " O -- O -- O ($oldrev)" |
| 376 | echo "" |
| 377 | echo "The removed revisions are not necessarilly gone - if another reference" |
| 378 | echo "still refers to them they will stay in the repository." |
| 379 | rewind_only=1 |
| 380 | else |
| 381 | echo "This update added new revisions after undoing existing revisions. That is" |
| 382 | echo "to say, the old revision is not a strict subset of the new revision. This" |
| 383 | echo "situation occurs when you --force push a change and generate a repository" |
| 384 | echo "containing something like this:" |
| 385 | echo "" |
| 386 | echo " * -- * -- B -- O -- O -- O ($oldrev)" |
| 387 | echo " \\" |
| 388 | echo " N -- N -- N ($newrev)" |
| 389 | echo "" |
| 390 | echo "When this happens we assume that you've already had alert emails for all" |
| 391 | echo "of the O revisions, and so we here report only the revisions in the N" |
| 392 | echo "branch from the common base, B." |
| 393 | fi |
Andy Parkins | 4557e0d | 2007-03-30 19:16:26 +0000 | [diff] [blame] | 394 | fi |
| 395 | |
| 396 | echo "" |
Andy Parkins | 024e5b3 | 2007-04-26 22:36:24 +0100 | [diff] [blame] | 397 | if [ -z "$rewind_only" ]; then |
| 398 | echo "Those revisions listed above that are new to this repository have" |
| 399 | echo "not appeared on any other notification email; so we list those" |
| 400 | echo "revisions in full, below." |
Andy Parkins | 4557e0d | 2007-03-30 19:16:26 +0000 | [diff] [blame] | 401 | |
Andy Parkins | 024e5b3 | 2007-04-26 22:36:24 +0100 | [diff] [blame] | 402 | echo "" |
| 403 | echo $LOGBEGIN |
Pete Harlan | 4471649 | 2008-11-03 23:19:53 -0800 | [diff] [blame] | 404 | show_new_revisions |
Andy Parkins | 4557e0d | 2007-03-30 19:16:26 +0000 | [diff] [blame] | 405 | |
Gerrit Pape | 15a2f53 | 2007-11-06 13:48:34 +0000 | [diff] [blame] | 406 | # XXX: Need a way of detecting whether git rev-list actually |
| 407 | # outputted anything, so that we can issue a "no new |
| 408 | # revisions added by this update" message |
Andy Parkins | 4557e0d | 2007-03-30 19:16:26 +0000 | [diff] [blame] | 409 | |
Andy Parkins | 024e5b3 | 2007-04-26 22:36:24 +0100 | [diff] [blame] | 410 | echo $LOGEND |
| 411 | else |
| 412 | echo "No new revisions were added by this update." |
| 413 | fi |
Andy Parkins | 4557e0d | 2007-03-30 19:16:26 +0000 | [diff] [blame] | 414 | |
Gerrit Pape | 15a2f53 | 2007-11-06 13:48:34 +0000 | [diff] [blame] | 415 | # The diffstat is shown from the old revision to the new revision. |
| 416 | # This is to show the truth of what happened in this change. |
| 417 | # There's no point showing the stat from the base to the new |
| 418 | # revision because the base is effectively a random revision at this |
| 419 | # point - the user will be interested in what this revision changed |
| 420 | # - including the undoing of previous revisions in the case of |
Felipe Contreras | a75d7b5 | 2009-10-24 11:31:32 +0300 | [diff] [blame] | 421 | # non-fast-forward updates. |
Andy Parkins | 4557e0d | 2007-03-30 19:16:26 +0000 | [diff] [blame] | 422 | echo "" |
| 423 | echo "Summary of changes:" |
| 424 | git diff-tree --stat --summary --find-copies-harder $oldrev..$newrev |
| 425 | } |
| 426 | |
| 427 | # |
| 428 | # Called for the deletion of a branch |
| 429 | # |
| 430 | generate_delete_branch_email() |
| 431 | { |
| 432 | echo " was $oldrev" |
| 433 | echo "" |
| 434 | echo $LOGEND |
| 435 | git show -s --pretty=oneline $oldrev |
| 436 | echo $LOGEND |
| 437 | } |
| 438 | |
| 439 | # --------------- Annotated tags |
| 440 | |
| 441 | # |
| 442 | # Called for the creation of an annotated tag |
| 443 | # |
| 444 | generate_create_atag_email() |
| 445 | { |
| 446 | echo " at $newrev ($newrev_type)" |
| 447 | |
| 448 | generate_atag_email |
| 449 | } |
| 450 | |
| 451 | # |
| 452 | # Called for the update of an annotated tag (this is probably a rare event |
| 453 | # and may not even be allowed) |
| 454 | # |
| 455 | generate_update_atag_email() |
| 456 | { |
| 457 | echo " to $newrev ($newrev_type)" |
| 458 | echo " from $oldrev (which is now obsolete)" |
| 459 | |
| 460 | generate_atag_email |
| 461 | } |
| 462 | |
| 463 | # |
| 464 | # Called when an annotated tag is created or changed |
| 465 | # |
| 466 | generate_atag_email() |
| 467 | { |
Dan McGee | 22fa97d | 2008-01-13 22:51:01 -0600 | [diff] [blame] | 468 | # Use git for-each-ref to pull out the individual fields from the |
Gerrit Pape | 15a2f53 | 2007-11-06 13:48:34 +0000 | [diff] [blame] | 469 | # tag |
Andy Parkins | 4557e0d | 2007-03-30 19:16:26 +0000 | [diff] [blame] | 470 | eval $(git for-each-ref --shell --format=' |
| 471 | tagobject=%(*objectname) |
| 472 | tagtype=%(*objecttype) |
| 473 | tagger=%(taggername) |
| 474 | tagged=%(taggerdate)' $refname |
| 475 | ) |
| 476 | |
| 477 | echo " tagging $tagobject ($tagtype)" |
| 478 | case "$tagtype" in |
| 479 | commit) |
Gerrit Pape | 15a2f53 | 2007-11-06 13:48:34 +0000 | [diff] [blame] | 480 | |
Andy Parkins | 4557e0d | 2007-03-30 19:16:26 +0000 | [diff] [blame] | 481 | # If the tagged object is a commit, then we assume this is a |
Gerrit Pape | 15a2f53 | 2007-11-06 13:48:34 +0000 | [diff] [blame] | 482 | # release, and so we calculate which tag this tag is |
| 483 | # replacing |
Andy Parkins | 4557e0d | 2007-03-30 19:16:26 +0000 | [diff] [blame] | 484 | prevtag=$(git describe --abbrev=0 $newrev^ 2>/dev/null) |
| 485 | |
| 486 | if [ -n "$prevtag" ]; then |
| 487 | echo " replaces $prevtag" |
| 488 | fi |
| 489 | ;; |
| 490 | *) |
| 491 | echo " length $(git cat-file -s $tagobject) bytes" |
| 492 | ;; |
| 493 | esac |
| 494 | echo " tagged by $tagger" |
| 495 | echo " on $tagged" |
| 496 | |
| 497 | echo "" |
| 498 | echo $LOGBEGIN |
| 499 | |
Gerrit Pape | 15a2f53 | 2007-11-06 13:48:34 +0000 | [diff] [blame] | 500 | # Show the content of the tag message; this might contain a change |
| 501 | # log or release notes so is worth displaying. |
Andy Parkins | 4557e0d | 2007-03-30 19:16:26 +0000 | [diff] [blame] | 502 | git cat-file tag $newrev | sed -e '1,/^$/d' |
| 503 | |
| 504 | echo "" |
| 505 | case "$tagtype" in |
| 506 | commit) |
Gerrit Pape | 15a2f53 | 2007-11-06 13:48:34 +0000 | [diff] [blame] | 507 | # Only commit tags make sense to have rev-list operations |
| 508 | # performed on them |
Andy Parkins | 4557e0d | 2007-03-30 19:16:26 +0000 | [diff] [blame] | 509 | if [ -n "$prevtag" ]; then |
| 510 | # Show changes since the previous release |
| 511 | git rev-list --pretty=short "$prevtag..$newrev" | git shortlog |
| 512 | else |
Gerrit Pape | 15a2f53 | 2007-11-06 13:48:34 +0000 | [diff] [blame] | 513 | # No previous tag, show all the changes since time |
| 514 | # began |
Andy Parkins | 4557e0d | 2007-03-30 19:16:26 +0000 | [diff] [blame] | 515 | git rev-list --pretty=short $newrev | git shortlog |
| 516 | fi |
| 517 | ;; |
| 518 | *) |
Gerrit Pape | 15a2f53 | 2007-11-06 13:48:34 +0000 | [diff] [blame] | 519 | # XXX: Is there anything useful we can do for non-commit |
| 520 | # objects? |
Andy Parkins | 4557e0d | 2007-03-30 19:16:26 +0000 | [diff] [blame] | 521 | ;; |
| 522 | esac |
| 523 | |
| 524 | echo $LOGEND |
| 525 | } |
| 526 | |
| 527 | # |
| 528 | # Called for the deletion of an annotated tag |
| 529 | # |
| 530 | generate_delete_atag_email() |
| 531 | { |
| 532 | echo " was $oldrev" |
| 533 | echo "" |
| 534 | echo $LOGEND |
| 535 | git show -s --pretty=oneline $oldrev |
| 536 | echo $LOGEND |
| 537 | } |
| 538 | |
| 539 | # --------------- General references |
| 540 | |
| 541 | # |
| 542 | # Called when any other type of reference is created (most likely a |
| 543 | # non-annotated tag) |
| 544 | # |
| 545 | generate_create_general_email() |
| 546 | { |
| 547 | echo " at $newrev ($newrev_type)" |
| 548 | |
| 549 | generate_general_email |
| 550 | } |
| 551 | |
| 552 | # |
| 553 | # Called when any other type of reference is updated (most likely a |
| 554 | # non-annotated tag) |
| 555 | # |
| 556 | generate_update_general_email() |
| 557 | { |
| 558 | echo " to $newrev ($newrev_type)" |
| 559 | echo " from $oldrev" |
| 560 | |
| 561 | generate_general_email |
| 562 | } |
| 563 | |
| 564 | # |
| 565 | # Called for creation or update of any other type of reference |
| 566 | # |
| 567 | generate_general_email() |
| 568 | { |
Gerrit Pape | 15a2f53 | 2007-11-06 13:48:34 +0000 | [diff] [blame] | 569 | # Unannotated tags are more about marking a point than releasing a |
| 570 | # version; therefore we don't do the shortlog summary that we do for |
| 571 | # annotated tags above - we simply show that the point has been |
| 572 | # marked, and print the log message for the marked point for |
| 573 | # reference purposes |
Andy Parkins | 4557e0d | 2007-03-30 19:16:26 +0000 | [diff] [blame] | 574 | # |
Gerrit Pape | 15a2f53 | 2007-11-06 13:48:34 +0000 | [diff] [blame] | 575 | # Note this section also catches any other reference type (although |
| 576 | # there aren't any) and deals with them in the same way. |
Andy Parkins | 4557e0d | 2007-03-30 19:16:26 +0000 | [diff] [blame] | 577 | |
| 578 | echo "" |
| 579 | if [ "$newrev_type" = "commit" ]; then |
| 580 | echo $LOGBEGIN |
Denis Cheng | 9225d7b | 2008-03-02 17:05:52 +0800 | [diff] [blame] | 581 | git show --no-color --root -s --pretty=medium $newrev |
Andy Parkins | 4557e0d | 2007-03-30 19:16:26 +0000 | [diff] [blame] | 582 | echo $LOGEND |
| 583 | else |
Gerrit Pape | 15a2f53 | 2007-11-06 13:48:34 +0000 | [diff] [blame] | 584 | # What can we do here? The tag marks an object that is not |
| 585 | # a commit, so there is no log for us to display. It's |
Dan McGee | 22fa97d | 2008-01-13 22:51:01 -0600 | [diff] [blame] | 586 | # probably not wise to output git cat-file as it could be a |
Gerrit Pape | 15a2f53 | 2007-11-06 13:48:34 +0000 | [diff] [blame] | 587 | # binary blob. We'll just say how big it is |
Andy Parkins | 4557e0d | 2007-03-30 19:16:26 +0000 | [diff] [blame] | 588 | echo "$newrev is a $newrev_type, and is $(git cat-file -s $newrev) bytes long." |
| 589 | fi |
| 590 | } |
| 591 | |
| 592 | # |
| 593 | # Called for the deletion of any other type of reference |
| 594 | # |
| 595 | generate_delete_general_email() |
| 596 | { |
| 597 | echo " was $oldrev" |
| 598 | echo "" |
| 599 | echo $LOGEND |
| 600 | git show -s --pretty=oneline $oldrev |
| 601 | echo $LOGEND |
| 602 | } |
| 603 | |
Pete Harlan | 4471649 | 2008-11-03 23:19:53 -0800 | [diff] [blame] | 604 | |
| 605 | # --------------- Miscellaneous utilities |
| 606 | |
| 607 | # |
| 608 | # Show new revisions as the user would like to see them in the email. |
| 609 | # |
| 610 | show_new_revisions() |
| 611 | { |
| 612 | # This shows all log entries that are not already covered by |
| 613 | # another ref - i.e. commits that are now accessible from this |
| 614 | # ref that were previously not accessible |
| 615 | # (see generate_update_branch_email for the explanation of this |
| 616 | # command) |
| 617 | |
| 618 | # Revision range passed to rev-list differs for new vs. updated |
| 619 | # branches. |
| 620 | if [ "$change_type" = create ] |
| 621 | then |
| 622 | # Show all revisions exclusive to this (new) branch. |
| 623 | revspec=$newrev |
| 624 | else |
| 625 | # Branch update; show revisions not part of $oldrev. |
| 626 | revspec=$oldrev..$newrev |
| 627 | fi |
| 628 | |
Pat Notz | e5f5050 | 2009-02-10 09:43:30 -0700 | [diff] [blame] | 629 | other_branches=$(git for-each-ref --format='%(refname)' refs/heads/ | |
| 630 | grep -F -v $refname) |
| 631 | git rev-parse --not $other_branches | |
Pete Harlan | b0a7d11 | 2008-11-03 23:19:54 -0800 | [diff] [blame] | 632 | if [ -z "$custom_showrev" ] |
| 633 | then |
| 634 | git rev-list --pretty --stdin $revspec |
| 635 | else |
| 636 | git rev-list --stdin $revspec | |
| 637 | while read onerev |
| 638 | do |
| 639 | eval $(printf "$custom_showrev" $onerev) |
| 640 | done |
| 641 | fi |
Pete Harlan | 4471649 | 2008-11-03 23:19:53 -0800 | [diff] [blame] | 642 | } |
| 643 | |
| 644 | |
Jim Meyering | d1637a0 | 2007-09-25 08:48:59 +0200 | [diff] [blame] | 645 | send_mail() |
| 646 | { |
| 647 | if [ -n "$envelopesender" ]; then |
| 648 | /usr/sbin/sendmail -t -f "$envelopesender" |
| 649 | else |
| 650 | /usr/sbin/sendmail -t |
| 651 | fi |
| 652 | } |
| 653 | |
Andy Parkins | 4557e0d | 2007-03-30 19:16:26 +0000 | [diff] [blame] | 654 | # ---------------------------- main() |
| 655 | |
| 656 | # --- Constants |
Andy Parkins | 4557e0d | 2007-03-30 19:16:26 +0000 | [diff] [blame] | 657 | LOGBEGIN="- Log -----------------------------------------------------------------" |
| 658 | LOGEND="-----------------------------------------------------------------------" |
| 659 | |
| 660 | # --- Config |
| 661 | # Set GIT_DIR either from the working directory, or from the environment |
| 662 | # variable. |
| 663 | GIT_DIR=$(git rev-parse --git-dir 2>/dev/null) |
| 664 | if [ -z "$GIT_DIR" ]; then |
| 665 | echo >&2 "fatal: post-receive: GIT_DIR not set" |
| 666 | exit 1 |
| 667 | fi |
| 668 | |
Andy Parkins | c855195 | 2007-04-26 22:37:16 +0100 | [diff] [blame] | 669 | projectdesc=$(sed -ne '1p' "$GIT_DIR/description") |
Gerrit Pape | 15a2f53 | 2007-11-06 13:48:34 +0000 | [diff] [blame] | 670 | # Check if the description is unchanged from it's default, and shorten it to |
| 671 | # a more manageable length if it is |
Andy Parkins | 4557e0d | 2007-03-30 19:16:26 +0000 | [diff] [blame] | 672 | if expr "$projectdesc" : "Unnamed repository.*$" >/dev/null |
| 673 | then |
| 674 | projectdesc="UNNAMED PROJECT" |
| 675 | fi |
| 676 | |
Dan McGee | 22fa97d | 2008-01-13 22:51:01 -0600 | [diff] [blame] | 677 | recipients=$(git config hooks.mailinglist) |
| 678 | announcerecipients=$(git config hooks.announcelist) |
| 679 | envelopesender=$(git config hooks.envelopesender) |
| 680 | emailprefix=$(git config hooks.emailprefix || echo '[SCM] ') |
Pete Harlan | b0a7d11 | 2008-11-03 23:19:54 -0800 | [diff] [blame] | 681 | custom_showrev=$(git config hooks.showrev) |
Andy Parkins | 4557e0d | 2007-03-30 19:16:26 +0000 | [diff] [blame] | 682 | |
| 683 | # --- Main loop |
Gerrit Pape | 15a2f53 | 2007-11-06 13:48:34 +0000 | [diff] [blame] | 684 | # Allow dual mode: run from the command line just like the update hook, or |
| 685 | # if no arguments are given then run as a hook script |
Andy Parkins | 4557e0d | 2007-03-30 19:16:26 +0000 | [diff] [blame] | 686 | if [ -n "$1" -a -n "$2" -a -n "$3" ]; then |
| 687 | # Output to the terminal in command line mode - if someone wanted to |
Gerrit Pape | 15a2f53 | 2007-11-06 13:48:34 +0000 | [diff] [blame] | 688 | # resend an email; they could redirect the output to sendmail |
| 689 | # themselves |
Andy Parkins | 4557e0d | 2007-03-30 19:16:26 +0000 | [diff] [blame] | 690 | PAGER= generate_email $2 $3 $1 |
| 691 | else |
Andy Parkins | 4557e0d | 2007-03-30 19:16:26 +0000 | [diff] [blame] | 692 | while read oldrev newrev refname |
| 693 | do |
Jim Meyering | d1637a0 | 2007-09-25 08:48:59 +0200 | [diff] [blame] | 694 | generate_email $oldrev $newrev $refname | send_mail |
Andy Parkins | 4557e0d | 2007-03-30 19:16:26 +0000 | [diff] [blame] | 695 | done |
| 696 | fi |