git-multimail: update to release 1.3.0
The changes are described in CHANGES. Contributions-by: Matthieu Moy <Matthieu.Moy@imag.fr> Contributions-by: Stefan Tatschner <rumpelsepp@sevenbyte.org> Contributions-by: Simon P <simon.git@le-huit.fr> Contributions-by: Leander Hasty <leander@1stplayable.com> Signed-off-by: Matthieu Moy <Matthieu.Moy@imag.fr> Signed-off-by: Junio C Hamano <gitster@pobox.com>maint
							parent
							
								
									5b618c1c8d
								
							
						
					
					
						commit
						4453d76c6a
					
				|  | @ -1,3 +1,38 @@ | ||||||
|  | Release 1.3.0 | ||||||
|  | ============= | ||||||
|  |  | ||||||
|  | * New options multimailhook.htmlInIntro and multimailhook.htmlInFooter | ||||||
|  |   now allow using HTML in the introduction and footer of emails (e.g. | ||||||
|  |   for a more pleasant formatting or to insert a link to the commit on | ||||||
|  |   a web interface). | ||||||
|  |  | ||||||
|  | * A new option multimailhook.commitBrowseURL gives a simpler (and less | ||||||
|  |   flexible) way to add a link to a web interface for commit emails | ||||||
|  |   than multimailhook.htmlInIntro and multimailhook.htmlInFooter. | ||||||
|  |  | ||||||
|  | * A new public function config.add_config_parameters was added to | ||||||
|  |   allow custom hooks to set specific Git configuration variables | ||||||
|  |   without modifying the configuration files. See an example in | ||||||
|  |   post-receive.example. | ||||||
|  |  | ||||||
|  | * Error handling for SMTP has been improved (we used to print Python | ||||||
|  |   backtraces for legitimate errors). | ||||||
|  |  | ||||||
|  | * The SMTP mailer can now check TLS certificates when the newly added | ||||||
|  |   configuration variable multimailhook.smtpCACerts. | ||||||
|  |  | ||||||
|  | * Python 3 portability has been improved. | ||||||
|  |  | ||||||
|  | * The documentation's formatting has been improved. | ||||||
|  |  | ||||||
|  | * The testsuite has been improved (we now use pyflakes to check for | ||||||
|  |   errors in the code). | ||||||
|  |  | ||||||
|  | This version has been tested with Python 2.4 and 2.6 to 3.5, and Git | ||||||
|  | v1.7.10-406-gdc801e7, 2.1.4 and 2.8.1.339.g3ad15fd. | ||||||
|  |  | ||||||
|  | No change since 1.3 RC1. | ||||||
|  |  | ||||||
| Release 1.2.0 | Release 1.2.0 | ||||||
| ============= | ============= | ||||||
|  |  | ||||||
|  |  | ||||||
|  | @ -1,3 +1,6 @@ | ||||||
|  | Contributing | ||||||
|  | ============ | ||||||
|  |  | ||||||
| git-multimail is an open-source project, built by volunteers. We would | git-multimail is an open-source project, built by volunteers. We would | ||||||
| welcome your help! | welcome your help! | ||||||
|  |  | ||||||
|  | @ -6,9 +9,7 @@ and Matthieu Moy <matthieu.moy@grenoble-inp.fr>. | ||||||
|  |  | ||||||
| Please note that although a copy of git-multimail is distributed in | Please note that although a copy of git-multimail is distributed in | ||||||
| the "contrib" section of the main Git project, development takes place | the "contrib" section of the main Git project, development takes place | ||||||
| in a separate git-multimail repository on GitHub: | in a separate `git-multimail repository on GitHub`_. | ||||||
|  |  | ||||||
|     https://github.com/git-multimail/git-multimail |  | ||||||
|  |  | ||||||
| Whenever enough changes to git-multimail have accumulated, a new | Whenever enough changes to git-multimail have accumulated, a new | ||||||
| code-drop of git-multimail will be submitted for inclusion in the Git | code-drop of git-multimail will be submitted for inclusion in the Git | ||||||
|  | @ -21,10 +22,12 @@ to the maintainers). Please sign off your patches as per the `Git | ||||||
| project practice | project practice | ||||||
| <https://github.com/git/git/blob/master/Documentation/SubmittingPatches#L234>`__. | <https://github.com/git/git/blob/master/Documentation/SubmittingPatches#L234>`__. | ||||||
|  |  | ||||||
| General discussion of git-multimail can take place on the main Git | General discussion of git-multimail can take place on the main `Git | ||||||
| mailing list, | mailing list`_. | ||||||
|  |  | ||||||
|     git@vger.kernel.org |  | ||||||
|  |  | ||||||
| Please CC emails regarding git-multimail to the maintainers so that we | Please CC emails regarding git-multimail to the maintainers so that we | ||||||
| don't overlook them. | don't overlook them. | ||||||
|  |  | ||||||
|  |  | ||||||
|  | .. _`git-multimail repository on GitHub`: https://github.com/git-multimail/git-multimail | ||||||
|  | .. _`Git mailing list`: git@vger.kernel.org | ||||||
|  |  | ||||||
|  | @ -1,5 +1,5 @@ | ||||||
| git-multimail (version 1.2.0) | git-multimail 1.3.0 | ||||||
| ============================= | =================== | ||||||
|  |  | ||||||
| .. image:: https://travis-ci.org/git-multimail/git-multimail.svg?branch=master | .. image:: https://travis-ci.org/git-multimail/git-multimail.svg?branch=master | ||||||
|     :target: https://travis-ci.org/git-multimail/git-multimail |     :target: https://travis-ci.org/git-multimail/git-multimail | ||||||
|  | @ -127,6 +127,13 @@ changes of this type, please consider sharing them with the | ||||||
| community.) | community.) | ||||||
|  |  | ||||||
|  |  | ||||||
|  | Troubleshooting/FAQ | ||||||
|  | ------------------- | ||||||
|  |  | ||||||
|  | Please read `<doc/troubleshooting.rst>`__ for frequently asked | ||||||
|  | questions and common issues with git-multimail. | ||||||
|  |  | ||||||
|  |  | ||||||
| Configuration | Configuration | ||||||
| ------------- | ------------- | ||||||
|  |  | ||||||
|  | @ -134,19 +141,16 @@ By default, git-multimail mostly takes its configuration from the | ||||||
| following ``git config`` settings: | following ``git config`` settings: | ||||||
|  |  | ||||||
| multimailhook.environment | multimailhook.environment | ||||||
|  |  | ||||||
|     This describes the general environment of the repository. In most |     This describes the general environment of the repository. In most | ||||||
|     cases, you do not need to specify a value for this variable: |     cases, you do not need to specify a value for this variable: | ||||||
|     `git-multimail` will autodetect which environment to use. |     `git-multimail` will autodetect which environment to use. | ||||||
|     Currently supported values: |     Currently supported values: | ||||||
|  |  | ||||||
|     * generic |     generic | ||||||
|  |  | ||||||
|       the username of the pusher is read from $USER or $USERNAME and |       the username of the pusher is read from $USER or $USERNAME and | ||||||
|       the repository name is derived from the repository's path. |       the repository name is derived from the repository's path. | ||||||
|  |  | ||||||
|     * gitolite |     gitolite | ||||||
|  |  | ||||||
|       the username of the pusher is read from $GL_USER, the repository |       the username of the pusher is read from $GL_USER, the repository | ||||||
|       name is read from $GL_REPO, and the From: header value is |       name is read from $GL_REPO, and the From: header value is | ||||||
|       optionally read from gitolite.conf (see multimailhook.from). |       optionally read from gitolite.conf (see multimailhook.from). | ||||||
|  | @ -154,8 +158,7 @@ multimailhook.environment | ||||||
|       For more information about gitolite and git-multimail, read |       For more information about gitolite and git-multimail, read | ||||||
|       `<doc/gitolite.rst>`__ |       `<doc/gitolite.rst>`__ | ||||||
|  |  | ||||||
|     * stash |     stash | ||||||
|  |  | ||||||
|       Environment to use when ``git-multimail`` is ran as an Atlassian |       Environment to use when ``git-multimail`` is ran as an Atlassian | ||||||
|       BitBucket Server (formerly known as Atlassian Stash) hook. |       BitBucket Server (formerly known as Atlassian Stash) hook. | ||||||
|  |  | ||||||
|  | @ -169,8 +172,7 @@ multimailhook.environment | ||||||
|       and repo come from these two command line flags, which must be |       and repo come from these two command line flags, which must be | ||||||
|       specified. |       specified. | ||||||
|  |  | ||||||
|     * gerrit |     gerrit | ||||||
|  |  | ||||||
|       Environment to use when ``git-multimail`` is ran as a |       Environment to use when ``git-multimail`` is ran as a | ||||||
|       ``ref-updated`` Gerrit hook. |       ``ref-updated`` Gerrit hook. | ||||||
|  |  | ||||||
|  | @ -205,14 +207,12 @@ multimailhook.environment | ||||||
|     * If none of the above apply, then ``generic`` is used. |     * If none of the above apply, then ``generic`` is used. | ||||||
|  |  | ||||||
| multimailhook.repoName | multimailhook.repoName | ||||||
|  |  | ||||||
|     A short name of this Git repository, to be used in various places |     A short name of this Git repository, to be used in various places | ||||||
|     in the notification email text.  The default is to use $GL_REPO |     in the notification email text.  The default is to use $GL_REPO | ||||||
|     for gitolite repositories, or otherwise to derive this value from |     for gitolite repositories, or otherwise to derive this value from | ||||||
|     the repository path name. |     the repository path name. | ||||||
|  |  | ||||||
| multimailhook.mailingList | multimailhook.mailingList | ||||||
|  |  | ||||||
|     The list of email addresses to which notification emails should be |     The list of email addresses to which notification emails should be | ||||||
|     sent, as RFC 2822 email addresses separated by commas.  This |     sent, as RFC 2822 email addresses separated by commas.  This | ||||||
|     configuration option can be multivalued.  Leave it unset or set it |     configuration option can be multivalued.  Leave it unset or set it | ||||||
|  | @ -221,7 +221,6 @@ multimailhook.mailingList | ||||||
|     specific types of notification email. |     specific types of notification email. | ||||||
|  |  | ||||||
| multimailhook.refchangeList | multimailhook.refchangeList | ||||||
|  |  | ||||||
|     The list of email addresses to which summary emails about |     The list of email addresses to which summary emails about | ||||||
|     reference changes should be sent, as RFC 2822 email addresses |     reference changes should be sent, as RFC 2822 email addresses | ||||||
|     separated by commas.  This configuration option can be |     separated by commas.  This configuration option can be | ||||||
|  | @ -231,7 +230,6 @@ multimailhook.refchangeList | ||||||
|     multimailhook.mailingList is set. |     multimailhook.mailingList is set. | ||||||
|  |  | ||||||
| multimailhook.announceList | multimailhook.announceList | ||||||
|  |  | ||||||
|     The list of email addresses to which emails about new annotated |     The list of email addresses to which emails about new annotated | ||||||
|     tags should be sent, as RFC 2822 email addresses separated by |     tags should be sent, as RFC 2822 email addresses separated by | ||||||
|     commas.  This configuration option can be multivalued.  The |     commas.  This configuration option can be multivalued.  The | ||||||
|  | @ -241,7 +239,6 @@ multimailhook.announceList | ||||||
|     even if one of the other values is set. |     even if one of the other values is set. | ||||||
|  |  | ||||||
| multimailhook.commitList | multimailhook.commitList | ||||||
|  |  | ||||||
|     The list of email addresses to which emails about individual new |     The list of email addresses to which emails about individual new | ||||||
|     commits should be sent, as RFC 2822 email addresses separated by |     commits should be sent, as RFC 2822 email addresses separated by | ||||||
|     commas.  This configuration option can be multivalued.  The |     commas.  This configuration option can be multivalued.  The | ||||||
|  | @ -251,7 +248,6 @@ multimailhook.commitList | ||||||
|     multimailhook.mailingList is set. |     multimailhook.mailingList is set. | ||||||
|  |  | ||||||
| multimailhook.announceShortlog | multimailhook.announceShortlog | ||||||
|  |  | ||||||
|     If this option is set to true, then emails about changes to |     If this option is set to true, then emails about changes to | ||||||
|     annotated tags include a shortlog of changes since the previous |     annotated tags include a shortlog of changes since the previous | ||||||
|     tag.  This can be useful if the annotated tags represent releases; |     tag.  This can be useful if the annotated tags represent releases; | ||||||
|  | @ -261,7 +257,6 @@ multimailhook.announceShortlog | ||||||
|     rather than useful.  Default is false. |     rather than useful.  Default is false. | ||||||
|  |  | ||||||
| multimailhook.commitEmailFormat | multimailhook.commitEmailFormat | ||||||
|  |  | ||||||
|     The format of email messages for the individual commits, can be "text" or |     The format of email messages for the individual commits, can be "text" or | ||||||
|     "html". In the latter case, the emails will include diffs using colorized |     "html". In the latter case, the emails will include diffs using colorized | ||||||
|     HTML instead of plain text used by default. Note that this  currently the |     HTML instead of plain text used by default. Note that this  currently the | ||||||
|  | @ -274,8 +269,43 @@ multimailhook.commitEmailFormat | ||||||
|     the message starting with ``+++`` or ``---`` colored in red or |     the message starting with ``+++`` or ``---`` colored in red or | ||||||
|     green). |     green). | ||||||
|  |  | ||||||
| multimailhook.refchangeShowGraph |     By default, all the message is HTML-escaped. See | ||||||
|  |     ``multimailhook.htmlInIntro`` to change this behavior. | ||||||
|  |  | ||||||
|  | multimailhook.commitBrowseURL | ||||||
|  |     Used to generate a link to an online repository browser in commit | ||||||
|  |     emails. This variable must be a string. Format directives like | ||||||
|  |     ``%(<variable>)s`` will be expanded the same way as template | ||||||
|  |     strings. In particular, ``%(id)s`` will be replaced by the full | ||||||
|  |     Git commit identifier (40-chars hexadecimal). | ||||||
|  |  | ||||||
|  |     If the string does not contain any format directive, then | ||||||
|  |     ``%(id)s`` will be automatically added to the string. If you don't | ||||||
|  |     want ``%(id)s`` to be automatically added, use the empty format | ||||||
|  |     directive ``%()s`` anywhere in the string. | ||||||
|  |  | ||||||
|  |     For example, a suitable value for the git-multimail project itself | ||||||
|  |     would be | ||||||
|  |     ``https://github.com/git-multimail/git-multimail/commit/%(id)s``. | ||||||
|  |  | ||||||
|  | multimailhook.htmlInIntro, multimailhook.htmlInFooter | ||||||
|  |     When generating an HTML message, git-multimail escapes any HTML | ||||||
|  |     sequence by default. This means that if a template contains HTML | ||||||
|  |     like ``<a href="foo">link</a>``, the reader will see the HTML | ||||||
|  |     source code and not a proper link. | ||||||
|  |  | ||||||
|  |     Set ``multimailhook.htmlInIntro`` to true to allow writting HTML | ||||||
|  |     formatting in introduction templates. Similarly, set | ||||||
|  |     ``multimailhook.htmlInFooter`` for HTML in the footer. | ||||||
|  |  | ||||||
|  |     Variables expanded in the template are still escaped. For example, | ||||||
|  |     if a repository's path contains a ``<``, it will be rendered as | ||||||
|  |     such in the message. | ||||||
|  |  | ||||||
|  |     Read `<doc/customizing-emails.rst>`__ for more details and | ||||||
|  |     examples. | ||||||
|  |  | ||||||
|  | multimailhook.refchangeShowGraph | ||||||
|     If this option is set to true, then summary emails about reference |     If this option is set to true, then summary emails about reference | ||||||
|     changes will additionally include: |     changes will additionally include: | ||||||
|  |  | ||||||
|  | @ -287,7 +317,6 @@ multimailhook.refchangeShowGraph | ||||||
|     specified in graphOpts.  The default is false. |     specified in graphOpts.  The default is false. | ||||||
|  |  | ||||||
| multimailhook.refchangeShowLog | multimailhook.refchangeShowLog | ||||||
|  |  | ||||||
|     If this option is set to true, then summary emails about reference |     If this option is set to true, then summary emails about reference | ||||||
|     changes will include a detailed log of the added commits in |     changes will include a detailed log of the added commits in | ||||||
|     addition to the one line summary.  The log is generated by running |     addition to the one line summary.  The log is generated by running | ||||||
|  | @ -295,15 +324,13 @@ multimailhook.refchangeShowLog | ||||||
|     Default is false. |     Default is false. | ||||||
|  |  | ||||||
| multimailhook.mailer | multimailhook.mailer | ||||||
|  |  | ||||||
|     This option changes the way emails are sent.  Accepted values are: |     This option changes the way emails are sent.  Accepted values are: | ||||||
|  |  | ||||||
|     - sendmail (the default): use the command ``/usr/sbin/sendmail`` or |     * **sendmail (the default)**: use the command ``/usr/sbin/sendmail`` or | ||||||
|       ``/usr/lib/sendmail`` (or sendmailCommand, if configured).  This |       ``/usr/lib/sendmail`` (or sendmailCommand, if configured).  This | ||||||
|       mode can be further customized via the following options: |       mode can be further customized via the following options: | ||||||
|  |  | ||||||
|       * multimailhook.sendmailCommand |       multimailhook.sendmailCommand | ||||||
|  |  | ||||||
|           The command used by mailer ``sendmail`` to send emails.  Shell |           The command used by mailer ``sendmail`` to send emails.  Shell | ||||||
|           quoting is allowed in the value of this setting, but remember that |           quoting is allowed in the value of this setting, but remember that | ||||||
|           Git requires double-quotes to be escaped; e.g.:: |           Git requires double-quotes to be escaped; e.g.:: | ||||||
|  | @ -314,52 +341,63 @@ multimailhook.mailer | ||||||
|           '/usr/lib/sendmail -oi -t' (depending on which file is |           '/usr/lib/sendmail -oi -t' (depending on which file is | ||||||
|           present and executable). |           present and executable). | ||||||
|  |  | ||||||
|       * multimailhook.envelopeSender |       multimailhook.envelopeSender | ||||||
|  |  | ||||||
|           If set then pass this value to sendmail via the -f option to set |           If set then pass this value to sendmail via the -f option to set | ||||||
|           the envelope sender address. |           the envelope sender address. | ||||||
|  |  | ||||||
|     - smtp: use Python's smtplib.  This is useful when the sendmail |     * **smtp**: use Python's smtplib.  This is useful when the sendmail | ||||||
|       command is not available on the system.  This mode can be |       command is not available on the system.  This mode can be | ||||||
|       further customized via the following options: |       further customized via the following options: | ||||||
|  |  | ||||||
|       * multimailhook.smtpServer |       multimailhook.smtpServer | ||||||
|  |  | ||||||
|           The name of the SMTP server to connect to.  The value can |           The name of the SMTP server to connect to.  The value can | ||||||
|           also include a colon and a port number; e.g., |           also include a colon and a port number; e.g., | ||||||
|           ``mail.example.com:25``.  Default is 'localhost' using port 25. |           ``mail.example.com:25``.  Default is 'localhost' using port 25. | ||||||
|  |  | ||||||
|       * multimailhook.smtpUser |       multimailhook.smtpUser, multimailhook.smtpPass | ||||||
|       * multimailhook.smtpPass |  | ||||||
|  |  | ||||||
|           Server username and password. Required if smtpEncryption is 'ssl'. |           Server username and password. Required if smtpEncryption is 'ssl'. | ||||||
|           Note that the username and password currently need to be |           Note that the username and password currently need to be | ||||||
|           set cleartext in the configuration file, which is not |           set cleartext in the configuration file, which is not | ||||||
|           recommended. If you need to use this option, be sure your |           recommended. If you need to use this option, be sure your | ||||||
|           configuration file is read-only. |           configuration file is read-only. | ||||||
|  |  | ||||||
|       * multimailhook.envelopeSender |       multimailhook.envelopeSender | ||||||
|  |  | ||||||
|         The sender address to be passed to the SMTP server.  If |         The sender address to be passed to the SMTP server.  If | ||||||
|         unset, then the value of multimailhook.from is used. |         unset, then the value of multimailhook.from is used. | ||||||
|  |  | ||||||
|       * multimailhook.smtpServerTimeout |       multimailhook.smtpServerTimeout | ||||||
|  |  | ||||||
|         Timeout in seconds. |         Timeout in seconds. | ||||||
|  |  | ||||||
|       * multimailhook.smtpEncryption |       multimailhook.smtpEncryption | ||||||
|  |         Set the security type. Allowed values: ``none``, ``ssl``, ``tls`` (starttls). | ||||||
|  |         Default is ``none``. | ||||||
|  |  | ||||||
|         Set the security type. Allowed values: none, ssl, tls. |       multimailhook.smtpCACerts | ||||||
|         Default=none. |         Set the path to a list of trusted CA certificate to verify the | ||||||
|  |         server certificate, only supported when ``smtpEncryption`` is | ||||||
|  |         ``tls``. If unset or empty, the server certificate is not | ||||||
|  |         verified. If it targets a file containing a list of trusted CA | ||||||
|  |         certificates (PEM format) these CAs will be used to verify the | ||||||
|  |         server certificate. For debian, you can set | ||||||
|  |         ``/etc/ssl/certs/ca-certificates.crt`` for using the system | ||||||
|  |         trusted CAs. For self-signed server, you can add your server | ||||||
|  |         certificate to the system store:: | ||||||
|  |  | ||||||
|       * multimailhook.smtpServerDebugLevel |             cd /usr/local/share/ca-certificates/ | ||||||
|  |             openssl s_client -starttls smtp \ | ||||||
|  |                    -connect mail.example.net:587 -showcerts \ | ||||||
|  |                    </dev/null 2>/dev/null \ | ||||||
|  |                  | openssl x509 -outform PEM >mail.example.net.crt | ||||||
|  |             update-ca-certificates | ||||||
|  |  | ||||||
|  |         and used the updated ``/etc/ssl/certs/ca-certificates.crt``. Or | ||||||
|  |         directly use your ``/path/to/mail.example.net.crt``. Default is | ||||||
|  |         unset. | ||||||
|  |  | ||||||
|  |       multimailhook.smtpServerDebugLevel | ||||||
|         Integer number. Set to greater than 0 to activate debugging. |         Integer number. Set to greater than 0 to activate debugging. | ||||||
|  |  | ||||||
| multimailhook.from | multimailhook.from, multimailhook.fromCommit, multimailhook.fromRefchange | ||||||
| multimailhook.fromCommit |  | ||||||
| multimailhook.fromRefchange |  | ||||||
|  |  | ||||||
|     If set, use this value in the From: field of generated emails. |     If set, use this value in the From: field of generated emails. | ||||||
|     ``fromCommit`` is used for commit emails, ``fromRefchange`` is |     ``fromCommit`` is used for commit emails, ``fromRefchange`` is | ||||||
|     used for refchange emails, and ``from`` is used as fall-back in |     used for refchange emails, and ``from`` is used as fall-back in | ||||||
|  | @ -372,7 +410,7 @@ multimailhook.fromRefchange | ||||||
|     - The value ``pusher``, in which case the pusher's address (if |     - The value ``pusher``, in which case the pusher's address (if | ||||||
|       available) will be used. |       available) will be used. | ||||||
|  |  | ||||||
|     - The value ``author`` (meaningful only for replyToCommit), in which |     - The value ``author`` (meaningful only for ``fromCommit``), in which | ||||||
|       case the commit author's address will be used. |       case the commit author's address will be used. | ||||||
|  |  | ||||||
|     If config values are unset, the value of the From: header is |     If config values are unset, the value of the From: header is | ||||||
|  | @ -396,14 +434,12 @@ multimailhook.fromRefchange | ||||||
|     3. Use the value of multimailhook.envelopeSender. |     3. Use the value of multimailhook.envelopeSender. | ||||||
|  |  | ||||||
| multimailhook.administrator | multimailhook.administrator | ||||||
|  |  | ||||||
|     The name and/or email address of the administrator of the Git |     The name and/or email address of the administrator of the Git | ||||||
|     repository; used in FOOTER_TEMPLATE.  Default is |     repository; used in FOOTER_TEMPLATE.  Default is | ||||||
|     multimailhook.envelopesender if it is set; otherwise a generic |     multimailhook.envelopesender if it is set; otherwise a generic | ||||||
|     string is used. |     string is used. | ||||||
|  |  | ||||||
| multimailhook.emailPrefix | multimailhook.emailPrefix | ||||||
|  |  | ||||||
|     All emails have this string prepended to their subjects, to aid |     All emails have this string prepended to their subjects, to aid | ||||||
|     email filtering (though filtering based on the X-Git-* email |     email filtering (though filtering based on the X-Git-* email | ||||||
|     headers is probably more robust).  Default is the short name of |     headers is probably more robust).  Default is the short name of | ||||||
|  | @ -411,14 +447,12 @@ multimailhook.emailPrefix | ||||||
|     value to the empty string to suppress the email prefix. |     value to the empty string to suppress the email prefix. | ||||||
|  |  | ||||||
| multimailhook.emailMaxLines | multimailhook.emailMaxLines | ||||||
|  |  | ||||||
|     The maximum number of lines that should be included in the body of |     The maximum number of lines that should be included in the body of | ||||||
|     a generated email.  If not specified, there is no limit.  Lines |     a generated email.  If not specified, there is no limit.  Lines | ||||||
|     beyond the limit are suppressed and counted, and a final line is |     beyond the limit are suppressed and counted, and a final line is | ||||||
|     added indicating the number of suppressed lines. |     added indicating the number of suppressed lines. | ||||||
|  |  | ||||||
| multimailhook.emailMaxLineLength | multimailhook.emailMaxLineLength | ||||||
|  |  | ||||||
|     The maximum length of a line in the email body.  Lines longer than |     The maximum length of a line in the email body.  Lines longer than | ||||||
|     this limit are truncated to this length with a trailing ``[...]`` |     this limit are truncated to this length with a trailing ``[...]`` | ||||||
|     added to indicate the missing text.  The default is 500, because |     added to indicate the missing text.  The default is 500, because | ||||||
|  | @ -428,7 +462,6 @@ multimailhook.emailMaxLineLength | ||||||
|     truncation, set this option to 0. |     truncation, set this option to 0. | ||||||
|  |  | ||||||
| multimailhook.maxCommitEmails | multimailhook.maxCommitEmails | ||||||
|  |  | ||||||
|     The maximum number of commit emails to send for a given change. |     The maximum number of commit emails to send for a given change. | ||||||
|     When the number of patches is larger that this value, only the |     When the number of patches is larger that this value, only the | ||||||
|     summary refchange email is sent.  This can avoid accidental |     summary refchange email is sent.  This can avoid accidental | ||||||
|  | @ -436,14 +469,12 @@ multimailhook.maxCommitEmails | ||||||
|     emails limit, set this option to 0.  The default is 500. |     emails limit, set this option to 0.  The default is 500. | ||||||
|  |  | ||||||
| multimailhook.emailStrictUTF8 | multimailhook.emailStrictUTF8 | ||||||
|  |  | ||||||
|     If this boolean option is set to `true`, then the main part of the |     If this boolean option is set to `true`, then the main part of the | ||||||
|     email body is forced to be valid UTF-8.  Any characters that are |     email body is forced to be valid UTF-8.  Any characters that are | ||||||
|     not valid UTF-8 are converted to the Unicode replacement |     not valid UTF-8 are converted to the Unicode replacement | ||||||
|     character, U+FFFD.  The default is `true`. |     character, U+FFFD.  The default is `true`. | ||||||
|  |  | ||||||
| multimailhook.diffOpts | multimailhook.diffOpts | ||||||
|  |  | ||||||
|     Options passed to ``git diff-tree`` when generating the summary |     Options passed to ``git diff-tree`` when generating the summary | ||||||
|     information for ReferenceChange emails.  Default is ``--stat |     information for ReferenceChange emails.  Default is ``--stat | ||||||
|     --summary --find-copies-harder``.  Add -p to those options to |     --summary --find-copies-harder``.  Add -p to those options to | ||||||
|  | @ -452,7 +483,6 @@ multimailhook.diffOpts | ||||||
|     details. |     details. | ||||||
|  |  | ||||||
| multimailhook.graphOpts | multimailhook.graphOpts | ||||||
|  |  | ||||||
|     Options passed to ``git log --graph`` when generating graphs for the |     Options passed to ``git log --graph`` when generating graphs for the | ||||||
|     reference change summary emails (used only if refchangeShowGraph |     reference change summary emails (used only if refchangeShowGraph | ||||||
|     is true).  The default is '--oneline --decorate'. |     is true).  The default is '--oneline --decorate'. | ||||||
|  | @ -460,7 +490,6 @@ multimailhook.graphOpts | ||||||
|     Shell quoting is allowed; see logOpts for details. |     Shell quoting is allowed; see logOpts for details. | ||||||
|  |  | ||||||
| multimailhook.logOpts | multimailhook.logOpts | ||||||
|  |  | ||||||
|     Options passed to ``git log`` to generate additional info for |     Options passed to ``git log`` to generate additional info for | ||||||
|     reference change emails (used only if refchangeShowLog is set). |     reference change emails (used only if refchangeShowLog is set). | ||||||
|     For example, adding -p will show each commit's complete diff.  The |     For example, adding -p will show each commit's complete diff.  The | ||||||
|  | @ -479,7 +508,6 @@ multimailhook.logOpts | ||||||
|               logopts = --pretty=format:\"%h %aN <%aE>%n%s%n%n%b%n\" |               logopts = --pretty=format:\"%h %aN <%aE>%n%s%n%n%b%n\" | ||||||
|  |  | ||||||
| multimailhook.commitLogOpts | multimailhook.commitLogOpts | ||||||
|  |  | ||||||
|     Options passed to ``git log`` to generate additional info for |     Options passed to ``git log`` to generate additional info for | ||||||
|     revision change emails.  For example, adding --ignore-all-spaces |     revision change emails.  For example, adding --ignore-all-spaces | ||||||
|     will suppress whitespace changes.  The default options are ``-C |     will suppress whitespace changes.  The default options are ``-C | ||||||
|  | @ -487,7 +515,6 @@ multimailhook.commitLogOpts | ||||||
|     multimailhook.logOpts for details. |     multimailhook.logOpts for details. | ||||||
|  |  | ||||||
| multimailhook.dateSubstitute | multimailhook.dateSubstitute | ||||||
|  |  | ||||||
|     String to use as a substitute for ``Date:`` in the output of ``git |     String to use as a substitute for ``Date:`` in the output of ``git | ||||||
|     log`` while formatting commit messages. This is usefull to avoid |     log`` while formatting commit messages. This is usefull to avoid | ||||||
|     emitting a line that can be interpreted by mailers as the start of |     emitting a line that can be interpreted by mailers as the start of | ||||||
|  | @ -496,17 +523,13 @@ multimailhook.dateSubstitute | ||||||
|     the behavior. |     the behavior. | ||||||
|  |  | ||||||
| multimailhook.emailDomain | multimailhook.emailDomain | ||||||
|  |  | ||||||
|     Domain name appended to the username of the person doing the push |     Domain name appended to the username of the person doing the push | ||||||
|     to convert it into an email address |     to convert it into an email address | ||||||
|     (via ``"%s@%s" % (username, emaildomain)``). More complicated |     (via ``"%s@%s" % (username, emaildomain)``). More complicated | ||||||
|     schemes can be implemented by overriding Environment and |     schemes can be implemented by overriding Environment and | ||||||
|     overriding its get_pusher_email() method. |     overriding its get_pusher_email() method. | ||||||
|  |  | ||||||
| multimailhook.replyTo | multimailhook.replyTo, multimailhook.replyToCommit, multimailhook.replyToRefchange | ||||||
| multimailhook.replyToCommit |  | ||||||
| multimailhook.replyToRefchange |  | ||||||
|  |  | ||||||
|     Addresses to use in the Reply-To: field for commit emails |     Addresses to use in the Reply-To: field for commit emails | ||||||
|     (replyToCommit) and refchange emails (replyToRefchange). |     (replyToCommit) and refchange emails (replyToRefchange). | ||||||
|     multimailhook.replyTo is used as default when replyToCommit or |     multimailhook.replyTo is used as default when replyToCommit or | ||||||
|  | @ -519,32 +542,24 @@ multimailhook.replyToRefchange | ||||||
|     commit emails. |     commit emails. | ||||||
|  |  | ||||||
| multimailhook.quiet | multimailhook.quiet | ||||||
|  |  | ||||||
|     Do not output the list of email recipients from the hook |     Do not output the list of email recipients from the hook | ||||||
|  |  | ||||||
| multimailhook.stdout | multimailhook.stdout | ||||||
|  |  | ||||||
|     For debugging, send emails to stdout rather than to the |     For debugging, send emails to stdout rather than to the | ||||||
|     mailer.  Equivalent to the --stdout command line option |     mailer.  Equivalent to the --stdout command line option | ||||||
|  |  | ||||||
| multimailhook.scanCommitForCc | multimailhook.scanCommitForCc | ||||||
|  |  | ||||||
|     If this option is set to true, than recipients from lines in commit body |     If this option is set to true, than recipients from lines in commit body | ||||||
|     that starts with ``CC:`` will be added to CC list. |     that starts with ``CC:`` will be added to CC list. | ||||||
|     Default: false |     Default: false | ||||||
|  |  | ||||||
| multimailhook.combineWhenSingleCommit | multimailhook.combineWhenSingleCommit | ||||||
|  |  | ||||||
|     If this option is set to true and a single new commit is pushed to |     If this option is set to true and a single new commit is pushed to | ||||||
|     a branch, combine the summary and commit email messages into a |     a branch, combine the summary and commit email messages into a | ||||||
|     single email. |     single email. | ||||||
|     Default: true |     Default: true | ||||||
|  |  | ||||||
| multimailhook.refFilterInclusionRegex | multimailhook.refFilterInclusionRegex, multimailhook.refFilterExclusionRegex, multimailhook.refFilterDoSendRegex, multimailhook.refFilterDontSendRegex | ||||||
| multimailhook.refFilterExclusionRegex |  | ||||||
| multimailhook.refFilterDoSendRegex |  | ||||||
| multimailhook.refFilterDontSendRegex |  | ||||||
|  |  | ||||||
|     **Warning:** these options are experimental. They should work, but |     **Warning:** these options are experimental. They should work, but | ||||||
|     the user-interface is not stable yet (in particular, the option |     the user-interface is not stable yet (in particular, the option | ||||||
|     names may change). If you want to participate in stabilizing the |     names may change). If you want to participate in stabilizing the | ||||||
|  | @ -626,9 +641,11 @@ git-multimail is mostly customized via an "environment" that describes | ||||||
| the local environment in which Git is running.  Two types of | the local environment in which Git is running.  Two types of | ||||||
| environment are built in: | environment are built in: | ||||||
|  |  | ||||||
| * GenericEnvironment: a stand-alone Git repository. | GenericEnvironment | ||||||
|  |     a stand-alone Git repository. | ||||||
|  |  | ||||||
| * GitoliteEnvironment: a Git repository that is managed by gitolite | GitoliteEnvironment | ||||||
|  |     a Git repository that is managed by gitolite | ||||||
|     [3]_.  For such repositories, the identity of the pusher is read from |     [3]_.  For such repositories, the identity of the pusher is read from | ||||||
|     environment variable $GL_USER, the name of the repository is read |     environment variable $GL_USER, the name of the repository is read | ||||||
|     from $GL_REPO (if it is not overridden by multimailhook.reponame), |     from $GL_REPO (if it is not overridden by multimailhook.reponame), | ||||||
|  |  | ||||||
|  | @ -6,10 +6,10 @@ website: | ||||||
|     https://github.com/git-multimail/git-multimail |     https://github.com/git-multimail/git-multimail | ||||||
|  |  | ||||||
| The version in this directory was obtained from the upstream project | The version in this directory was obtained from the upstream project | ||||||
| on October 11 2015 and consists of the "git-multimail" subdirectory from | on May 03 2016 and consists of the "git-multimail" subdirectory from | ||||||
| revision | revision | ||||||
|  |  | ||||||
|     c0791b9ef5821a746fc3475c25765e640452eaae refs/tags/1.2.0 |     26f3ae9f86aa7f8a054ba89235c4d3879f98b03d refs/tags/1.3.0 | ||||||
|  |  | ||||||
| Please see the README file in this directory for information about how | Please see the README file in this directory for information about how | ||||||
| to report bugs or contribute to git-multimail. | to report bugs or contribute to git-multimail. | ||||||
|  |  | ||||||
|  | @ -0,0 +1,56 @@ | ||||||
|  | Customizing the content and formatting of emails | ||||||
|  | ================================================ | ||||||
|  |  | ||||||
|  | Overloading template strings | ||||||
|  | ---------------------------- | ||||||
|  |  | ||||||
|  | The content of emails is generated based on template strings defined | ||||||
|  | in ``git_multimail.py``. You can customize these template strings | ||||||
|  | without changing the script itself, by defining a Python wrapper | ||||||
|  | around it. The python wrapper should ``import git_multimail`` and then | ||||||
|  | override the ``git_multimail.*`` strings like this:: | ||||||
|  |  | ||||||
|  |   import sys  # needed for sys.argv | ||||||
|  |  | ||||||
|  |   # Import and customize git_multimail: | ||||||
|  |   import git_multimail | ||||||
|  |   git_multimail.REVISION_INTRO_TEMPLATE = """...""" | ||||||
|  |   git_multimail.COMBINED_INTRO_TEMPLATE = git_multimail.REVISION_INTRO_TEMPLATE | ||||||
|  |  | ||||||
|  |   # start git_multimail itself: | ||||||
|  |   git_multimail.main(sys.argv[1:]) | ||||||
|  |  | ||||||
|  | The template strings can use any value already used in the existing | ||||||
|  | templates (read the source code). | ||||||
|  |  | ||||||
|  | Using HTML in template strings | ||||||
|  | ------------------------------ | ||||||
|  |  | ||||||
|  | If ``multimailhook.commitEmailFormat`` is set to HTML, then | ||||||
|  | git-multimail will generate HTML emails for commit notifications. The | ||||||
|  | log and diff will be formatted automatically by git-multimail. By | ||||||
|  | default, any HTML special character in the templates will be escaped. | ||||||
|  |  | ||||||
|  | To use HTML formatting in the introduction of the email, set | ||||||
|  | ``multimailhook.htmlInIntro`` to ``true``. Then, the template can | ||||||
|  | contain any HTML tags, that will be sent as-is in the email. For | ||||||
|  | example, to add some formatting and a link to the online commit, use | ||||||
|  | a format like:: | ||||||
|  |  | ||||||
|  |   git_multimail.REVISION_INTRO_TEMPLATE = """\ | ||||||
|  |   <span style="color:#808080">This is an automated email from the git hooks/post-receive script.</span><br /><br /> | ||||||
|  |  | ||||||
|  |   <strong>%(pusher)s</strong> pushed a commit to %(refname_type)s %(short_refname)s | ||||||
|  |   in repository %(repo_shortname)s.<br /> | ||||||
|  |  | ||||||
|  |   <a href="https://github.com/git-multimail/git-multimail/commit/%(newrev)s">View on GitHub</a>. | ||||||
|  |   """ | ||||||
|  |  | ||||||
|  | Note that the values expanded from ``%(variable)s`` in the format | ||||||
|  | strings will still be escaped. | ||||||
|  |  | ||||||
|  | For a less flexible but easier to set up way to add a link to commit | ||||||
|  | emails, see ``multimailhook.commitBrowseURL``. | ||||||
|  |  | ||||||
|  | Similarly, one can set ``multimailhook.htmlInFooter`` and override any | ||||||
|  | of the ``*_FOOTER*`` template strings. | ||||||
|  | @ -0,0 +1,44 @@ | ||||||
|  | Troubleshooting issues with git-multimail: a FAQ | ||||||
|  | ================================================ | ||||||
|  |  | ||||||
|  | Git is not using the right address in the From/To/Reply-To field | ||||||
|  | ---------------------------------------------------------------- | ||||||
|  |  | ||||||
|  | First, make sure that git-multimail actually uses what you think it is | ||||||
|  | using. A lot happens to your email (especially when posting to a | ||||||
|  | mailing-list) between the time `git_multimail.py` sends it and the | ||||||
|  | time it reaches your inbox. | ||||||
|  |  | ||||||
|  | A simple test (to do on a test repository, do not use in production as | ||||||
|  | it would disable email sending): change your post-receive hook to call | ||||||
|  | `git_multimail.py` with the `--stdout` option, and try to push to the | ||||||
|  | repository. You should see something like:: | ||||||
|  |  | ||||||
|  |   Counting objects: 3, done. | ||||||
|  |   Writing objects: 100% (3/3), 263 bytes | 0 bytes/s, done. | ||||||
|  |   Total 3 (delta 0), reused 0 (delta 0) | ||||||
|  |   remote: Sending notification emails to: foo.bar@example.com | ||||||
|  |   remote: =========================================================================== | ||||||
|  |   remote: Date: Mon, 25 Apr 2016 18:39:59 +0200 | ||||||
|  |   remote: To: foo.bar@example.com | ||||||
|  |   remote: Subject: [git] branch master updated: foo | ||||||
|  |   remote: MIME-Version: 1.0 | ||||||
|  |   remote: Content-Type: text/plain; charset=utf-8 | ||||||
|  |   remote: Content-Transfer-Encoding: 8bit | ||||||
|  |   remote: Message-ID: <20160425163959.2311.20498@anie> | ||||||
|  |   remote: From: Auth Or <Foo.Bar@example.com> | ||||||
|  |   remote: Reply-To: Auth Or <Foo.Bar@example.com> | ||||||
|  |   remote: X-Git-Host: example | ||||||
|  |   ... | ||||||
|  |   remote: -- | ||||||
|  |   remote: To stop receiving notification emails like this one, please contact | ||||||
|  |   remote: the administrator of this repository. | ||||||
|  |   remote: =========================================================================== | ||||||
|  |   To /path/to/repo | ||||||
|  |      6278f04..e173f20  master -> master | ||||||
|  |  | ||||||
|  | Note: this does not include the sender (Return-Path: header), as it is | ||||||
|  | not part of the message content but passed to the mailer. Some mailer | ||||||
|  | show the ``Sender:`` field instead of the ``From:`` field (for | ||||||
|  | example, Zimbra Webmail shows ``From: <sender-field> on behalf of | ||||||
|  | <from-field>``). | ||||||
|  | @ -1,6 +1,6 @@ | ||||||
| #! /usr/bin/env python | #! /usr/bin/env python | ||||||
|  |  | ||||||
| __version__ = '1.2.0' | __version__ = '1.3.0' | ||||||
|  |  | ||||||
| # Copyright (c) 2015 Matthieu Moy and others | # Copyright (c) 2015 Matthieu Moy and others | ||||||
| # Copyright (c) 2012-2014 Michael Haggerty and others | # Copyright (c) 2012-2014 Michael Haggerty and others | ||||||
|  | @ -57,6 +57,11 @@ import subprocess | ||||||
| import shlex | import shlex | ||||||
| import optparse | import optparse | ||||||
| import smtplib | import smtplib | ||||||
|  | try: | ||||||
|  |     import ssl | ||||||
|  | except ImportError: | ||||||
|  |     # Python < 2.6 do not have ssl, but that's OK if we don't use it. | ||||||
|  |     pass | ||||||
| import time | import time | ||||||
| import cgi | import cgi | ||||||
|  |  | ||||||
|  | @ -75,6 +80,9 @@ def is_ascii(s): | ||||||
|  |  | ||||||
|  |  | ||||||
| if PYTHON3: | if PYTHON3: | ||||||
|  |     def is_string(s): | ||||||
|  |         return isinstance(s, str) | ||||||
|  |  | ||||||
|     def str_to_bytes(s): |     def str_to_bytes(s): | ||||||
|         return s.encode(ENCODING) |         return s.encode(ENCODING) | ||||||
|  |  | ||||||
|  | @ -91,6 +99,12 @@ if PYTHON3: | ||||||
|         except UnicodeEncodeError: |         except UnicodeEncodeError: | ||||||
|             f.buffer.write(msg.encode(ENCODING)) |             f.buffer.write(msg.encode(ENCODING)) | ||||||
| else: | else: | ||||||
|  |     def is_string(s): | ||||||
|  |         try: | ||||||
|  |             return isinstance(s, basestring) | ||||||
|  |         except NameError:  # Silence Pyflakes warning | ||||||
|  |             raise | ||||||
|  |  | ||||||
|     def str_to_bytes(s): |     def str_to_bytes(s): | ||||||
|         return s |         return s | ||||||
|  |  | ||||||
|  | @ -313,6 +327,16 @@ in repository %(repo_shortname)s. | ||||||
|  |  | ||||||
| """ | """ | ||||||
|  |  | ||||||
|  | LINK_TEXT_TEMPLATE = """\ | ||||||
|  | View the commit online: | ||||||
|  | %(browse_url)s | ||||||
|  |  | ||||||
|  | """ | ||||||
|  |  | ||||||
|  | LINK_HTML_TEMPLATE = """\ | ||||||
|  | <p><a href="%(browse_url)s">View the commit online</a>.</p> | ||||||
|  | """ | ||||||
|  |  | ||||||
|  |  | ||||||
| REVISION_FOOTER_TEMPLATE = FOOTER_TEMPLATE | REVISION_FOOTER_TEMPLATE = FOOTER_TEMPLATE | ||||||
|  |  | ||||||
|  | @ -532,6 +556,28 @@ class Config(object): | ||||||
|         assert words[-1] == '' |         assert words[-1] == '' | ||||||
|         return words[:-1] |         return words[:-1] | ||||||
|  |  | ||||||
|  |     @staticmethod | ||||||
|  |     def add_config_parameters(c): | ||||||
|  |         """Add configuration parameters to Git. | ||||||
|  |  | ||||||
|  |         c is either an str or a list of str, each element being of the | ||||||
|  |         form 'var=val' or 'var', with the same syntax and meaning as | ||||||
|  |         the argument of 'git -c var=val'. | ||||||
|  |         """ | ||||||
|  |         if isinstance(c, str): | ||||||
|  |             c = (c,) | ||||||
|  |         parameters = os.environ.get('GIT_CONFIG_PARAMETERS', '') | ||||||
|  |         if parameters: | ||||||
|  |             parameters += ' ' | ||||||
|  |         # git expects GIT_CONFIG_PARAMETERS to be of the form | ||||||
|  |         #    "'name1=value1' 'name2=value2' 'name3=value3'" | ||||||
|  |         # including everything inside the double quotes (but not the double | ||||||
|  |         # quotes themselves).  Spacing is critical.  Also, if a value contains | ||||||
|  |         # a literal single quote that quote must be represented using the | ||||||
|  |         # four character sequence: '\'' | ||||||
|  |         parameters += ' '.join("'" + x.replace("'", "'\\''") + "'" for x in c) | ||||||
|  |         os.environ['GIT_CONFIG_PARAMETERS'] = parameters | ||||||
|  |  | ||||||
|     def get(self, name, default=None): |     def get(self, name, default=None): | ||||||
|         try: |         try: | ||||||
|             values = self._split(read_git_output( |             values = self._split(read_git_output( | ||||||
|  | @ -745,6 +791,12 @@ class Change(object): | ||||||
|         values['multimail_version'] = get_version() |         values['multimail_version'] = get_version() | ||||||
|         return values |         return values | ||||||
|  |  | ||||||
|  |     # Aliases usable in template strings. Tuple of pairs (destination, | ||||||
|  |     # source). | ||||||
|  |     VALUES_ALIAS = ( | ||||||
|  |         ("id", "newrev"), | ||||||
|  |         ) | ||||||
|  |  | ||||||
|     def get_values(self, **extra_values): |     def get_values(self, **extra_values): | ||||||
|         """Return a dictionary {keyword: expansion} for this Change. |         """Return a dictionary {keyword: expansion} for this Change. | ||||||
|  |  | ||||||
|  | @ -760,6 +812,9 @@ class Change(object): | ||||||
|         values = self._values.copy() |         values = self._values.copy() | ||||||
|         if extra_values: |         if extra_values: | ||||||
|             values.update(extra_values) |             values.update(extra_values) | ||||||
|  |  | ||||||
|  |         for alias, val in self.VALUES_ALIAS: | ||||||
|  |             values[alias] = values[val] | ||||||
|         return values |         return values | ||||||
|  |  | ||||||
|     def expand(self, template, **extra_values): |     def expand(self, template, **extra_values): | ||||||
|  | @ -772,10 +827,14 @@ class Change(object): | ||||||
|  |  | ||||||
|         return template % self.get_values(**extra_values) |         return template % self.get_values(**extra_values) | ||||||
|  |  | ||||||
|     def expand_lines(self, template, **extra_values): |     def expand_lines(self, template, html_escape_val=False, **extra_values): | ||||||
|         """Break template into lines and expand each line.""" |         """Break template into lines and expand each line.""" | ||||||
|  |  | ||||||
|         values = self.get_values(**extra_values) |         values = self.get_values(**extra_values) | ||||||
|  |         if html_escape_val: | ||||||
|  |             for k in values: | ||||||
|  |                 if is_string(values[k]): | ||||||
|  |                     values[k] = cgi.escape(values[k], True) | ||||||
|         for line in template.splitlines(True): |         for line in template.splitlines(True): | ||||||
|             yield line % values |             yield line % values | ||||||
|  |  | ||||||
|  | @ -787,9 +846,10 @@ class Change(object): | ||||||
|  |  | ||||||
|         values = self.get_values(**extra_values) |         values = self.get_values(**extra_values) | ||||||
|         if self._contains_html_diff: |         if self._contains_html_diff: | ||||||
|             values['contenttype'] = 'html' |             self._content_type = 'html' | ||||||
|         else: |         else: | ||||||
|             values['contenttype'] = 'plain' |             self._content_type = 'plain' | ||||||
|  |         values['contenttype'] = self._content_type | ||||||
|  |  | ||||||
|         for line in template.splitlines(): |         for line in template.splitlines(): | ||||||
|             (name, value) = line.split(': ', 1) |             (name, value) = line.split(': ', 1) | ||||||
|  | @ -819,7 +879,11 @@ class Change(object): | ||||||
|  |  | ||||||
|         raise NotImplementedError() |         raise NotImplementedError() | ||||||
|  |  | ||||||
|     def generate_email_intro(self): |     def generate_browse_link(self, base_url): | ||||||
|  |         """Generate a link to an online repository browser.""" | ||||||
|  |         return iter(()) | ||||||
|  |  | ||||||
|  |     def generate_email_intro(self, html_escape_val=False): | ||||||
|         """Generate the email intro for this Change, a line at a time. |         """Generate the email intro for this Change, a line at a time. | ||||||
|  |  | ||||||
|         The output will be used as the standard boilerplate at the top |         The output will be used as the standard boilerplate at the top | ||||||
|  | @ -835,7 +899,7 @@ class Change(object): | ||||||
|  |  | ||||||
|         raise NotImplementedError() |         raise NotImplementedError() | ||||||
|  |  | ||||||
|     def generate_email_footer(self): |     def generate_email_footer(self, html_escape_val): | ||||||
|         """Generate the footer of the email, a line at a time. |         """Generate the footer of the email, a line at a time. | ||||||
|  |  | ||||||
|         The footer is always included, irrespective of |         The footer is always included, irrespective of | ||||||
|  | @ -876,7 +940,16 @@ class Change(object): | ||||||
|         for line in self.generate_email_header(**extra_header_values): |         for line in self.generate_email_header(**extra_header_values): | ||||||
|             yield line |             yield line | ||||||
|         yield '\n' |         yield '\n' | ||||||
|         for line in self._wrap_for_html(self.generate_email_intro()): |         html_escape_val = (self.environment.html_in_intro and | ||||||
|  |                            self._contains_html_diff) | ||||||
|  |         intro = self.generate_email_intro(html_escape_val) | ||||||
|  |         if not self.environment.html_in_intro: | ||||||
|  |             intro = self._wrap_for_html(intro) | ||||||
|  |         for line in intro: | ||||||
|  |             yield line | ||||||
|  |  | ||||||
|  |         if self.environment.commitBrowseURL: | ||||||
|  |             for line in self.generate_browse_link(self.environment.commitBrowseURL): | ||||||
|                 yield line |                 yield line | ||||||
|  |  | ||||||
|         body = self.generate_email_body(push) |         body = self.generate_email_body(push) | ||||||
|  | @ -939,8 +1012,12 @@ class Change(object): | ||||||
|             yield line |             yield line | ||||||
|         if self._contains_html_diff: |         if self._contains_html_diff: | ||||||
|             yield '</pre>' |             yield '</pre>' | ||||||
|  |         html_escape_val = (self.environment.html_in_footer and | ||||||
|         for line in self._wrap_for_html(self.generate_email_footer()): |                            self._contains_html_diff) | ||||||
|  |         footer = self.generate_email_footer(html_escape_val) | ||||||
|  |         if not self.environment.html_in_footer: | ||||||
|  |             footer = self._wrap_for_html(footer) | ||||||
|  |         for line in footer: | ||||||
|             yield line |             yield line | ||||||
|  |  | ||||||
|     def get_alt_fromaddr(self): |     def get_alt_fromaddr(self): | ||||||
|  | @ -992,6 +1069,7 @@ class Revision(Change): | ||||||
|         values['rev_short'] = self.rev.short |         values['rev_short'] = self.rev.short | ||||||
|         values['change_type'] = self.change_type |         values['change_type'] = self.change_type | ||||||
|         values['refname'] = self.refname |         values['refname'] = self.refname | ||||||
|  |         values['newrev'] = self.rev.sha1 | ||||||
|         values['short_refname'] = self.reference_change.short_refname |         values['short_refname'] = self.reference_change.short_refname | ||||||
|         values['refname_type'] = self.reference_change.refname_type |         values['refname_type'] = self.reference_change.refname_type | ||||||
|         values['reply_to_msgid'] = self.reference_change.msgid |         values['reply_to_msgid'] = self.reference_change.msgid | ||||||
|  | @ -1015,8 +1093,26 @@ class Revision(Change): | ||||||
|                 ): |                 ): | ||||||
|             yield line |             yield line | ||||||
|  |  | ||||||
|     def generate_email_intro(self): |     def generate_browse_link(self, base_url): | ||||||
|         for line in self.expand_lines(REVISION_INTRO_TEMPLATE): |         if '%(' not in base_url: | ||||||
|  |             base_url += '%(id)s' | ||||||
|  |         url = "".join(self.expand_lines(base_url)) | ||||||
|  |         if self._content_type == 'html': | ||||||
|  |             for line in self.expand_lines(LINK_HTML_TEMPLATE, | ||||||
|  |                                           html_escape_val=True, | ||||||
|  |                                           browse_url=url): | ||||||
|  |                 yield line | ||||||
|  |         elif self._content_type == 'plain': | ||||||
|  |             for line in self.expand_lines(LINK_TEXT_TEMPLATE, | ||||||
|  |                                           html_escape_val=False, | ||||||
|  |                                           browse_url=url): | ||||||
|  |                 yield line | ||||||
|  |         else: | ||||||
|  |             raise NotImplementedError("Content-type %s unsupported. Please report it as a bug.") | ||||||
|  |  | ||||||
|  |     def generate_email_intro(self, html_escape_val=False): | ||||||
|  |         for line in self.expand_lines(REVISION_INTRO_TEMPLATE, | ||||||
|  |                                       html_escape_val=html_escape_val): | ||||||
|             yield line |             yield line | ||||||
|  |  | ||||||
|     def generate_email_body(self, push): |     def generate_email_body(self, push): | ||||||
|  | @ -1031,8 +1127,9 @@ class Revision(Change): | ||||||
|             else: |             else: | ||||||
|                 yield line |                 yield line | ||||||
|  |  | ||||||
|     def generate_email_footer(self): |     def generate_email_footer(self, html_escape_val): | ||||||
|         return self.expand_lines(REVISION_FOOTER_TEMPLATE) |         return self.expand_lines(REVISION_FOOTER_TEMPLATE, | ||||||
|  |                                  html_escape_val=html_escape_val) | ||||||
|  |  | ||||||
|     def generate_email(self, push, body_filter=None, extra_header_values={}): |     def generate_email(self, push, body_filter=None, extra_header_values={}): | ||||||
|         self._contains_diff() |         self._contains_diff() | ||||||
|  | @ -1217,8 +1314,9 @@ class ReferenceChange(Change): | ||||||
|                 ): |                 ): | ||||||
|             yield line |             yield line | ||||||
|  |  | ||||||
|     def generate_email_intro(self): |     def generate_email_intro(self, html_escape_val=False): | ||||||
|         for line in self.expand_lines(self.intro_template): |         for line in self.expand_lines(self.intro_template, | ||||||
|  |                                       html_escape_val=html_escape_val): | ||||||
|             yield line |             yield line | ||||||
|  |  | ||||||
|     def generate_email_body(self, push): |     def generate_email_body(self, push): | ||||||
|  | @ -1238,8 +1336,9 @@ class ReferenceChange(Change): | ||||||
|         for line in self.generate_revision_change_summary(push): |         for line in self.generate_revision_change_summary(push): | ||||||
|             yield line |             yield line | ||||||
|  |  | ||||||
|     def generate_email_footer(self): |     def generate_email_footer(self, html_escape_val): | ||||||
|         return self.expand_lines(self.footer_template) |         return self.expand_lines(self.footer_template, | ||||||
|  |                                  html_escape_val=html_escape_val) | ||||||
|  |  | ||||||
|     def generate_revision_change_graph(self, push): |     def generate_revision_change_graph(self, push): | ||||||
|         if self.showgraph: |         if self.showgraph: | ||||||
|  | @ -1896,6 +1995,7 @@ class SMTPMailer(Mailer): | ||||||
|                  smtpservertimeout=10.0, smtpserverdebuglevel=0, |                  smtpservertimeout=10.0, smtpserverdebuglevel=0, | ||||||
|                  smtpencryption='none', |                  smtpencryption='none', | ||||||
|                  smtpuser='', smtppass='', |                  smtpuser='', smtppass='', | ||||||
|  |                  smtpcacerts='' | ||||||
|                  ): |                  ): | ||||||
|         if not envelopesender: |         if not envelopesender: | ||||||
|             sys.stderr.write( |             sys.stderr.write( | ||||||
|  | @ -1915,6 +2015,7 @@ class SMTPMailer(Mailer): | ||||||
|         self.security = smtpencryption |         self.security = smtpencryption | ||||||
|         self.username = smtpuser |         self.username = smtpuser | ||||||
|         self.password = smtppass |         self.password = smtppass | ||||||
|  |         self.smtpcacerts = smtpcacerts | ||||||
|         try: |         try: | ||||||
|             def call(klass, server, timeout): |             def call(klass, server, timeout): | ||||||
|                 try: |                 try: | ||||||
|  | @ -1925,13 +2026,56 @@ class SMTPMailer(Mailer): | ||||||
|             if self.security == 'none': |             if self.security == 'none': | ||||||
|                 self.smtp = call(smtplib.SMTP, self.smtpserver, timeout=self.smtpservertimeout) |                 self.smtp = call(smtplib.SMTP, self.smtpserver, timeout=self.smtpservertimeout) | ||||||
|             elif self.security == 'ssl': |             elif self.security == 'ssl': | ||||||
|  |                 if self.smtpcacerts: | ||||||
|  |                     raise smtplib.SMTPException( | ||||||
|  |                         "Checking certificate is not supported for ssl, prefer starttls" | ||||||
|  |                         ) | ||||||
|                 self.smtp = call(smtplib.SMTP_SSL, self.smtpserver, timeout=self.smtpservertimeout) |                 self.smtp = call(smtplib.SMTP_SSL, self.smtpserver, timeout=self.smtpservertimeout) | ||||||
|             elif self.security == 'tls': |             elif self.security == 'tls': | ||||||
|  |                 if 'ssl' not in sys.modules: | ||||||
|  |                     sys.stderr.write( | ||||||
|  |                         '*** Your Python version does not have the ssl library installed\n' | ||||||
|  |                         '*** smtpEncryption=tls is not available.\n' | ||||||
|  |                         '*** Either upgrade Python to 2.6 or later\n' | ||||||
|  |                         '    or use git_multimail.py version 1.2.\n') | ||||||
|                 if ':' not in self.smtpserver: |                 if ':' not in self.smtpserver: | ||||||
|                     self.smtpserver += ':587'  # default port for TLS |                     self.smtpserver += ':587'  # default port for TLS | ||||||
|                 self.smtp = call(smtplib.SMTP, self.smtpserver, timeout=self.smtpservertimeout) |                 self.smtp = call(smtplib.SMTP, self.smtpserver, timeout=self.smtpservertimeout) | ||||||
|  |                 # start: ehlo + starttls | ||||||
|  |                 # equivalent to | ||||||
|  |                 #     self.smtp.ehlo() | ||||||
|  |                 #     self.smtp.starttls() | ||||||
|  |                 # with acces to the ssl layer | ||||||
|                 self.smtp.ehlo() |                 self.smtp.ehlo() | ||||||
|                 self.smtp.starttls() |                 if not self.smtp.has_extn("starttls"): | ||||||
|  |                     raise smtplib.SMTPException("STARTTLS extension not supported by server") | ||||||
|  |                 resp, reply = self.smtp.docmd("STARTTLS") | ||||||
|  |                 if resp != 220: | ||||||
|  |                     raise smtplib.SMTPException("Wrong answer to the STARTTLS command") | ||||||
|  |                 if self.smtpcacerts: | ||||||
|  |                     self.smtp.sock = ssl.wrap_socket( | ||||||
|  |                         self.smtp.sock, | ||||||
|  |                         ca_certs=self.smtpcacerts, | ||||||
|  |                         cert_reqs=ssl.CERT_REQUIRED | ||||||
|  |                         ) | ||||||
|  |                 else: | ||||||
|  |                     self.smtp.sock = ssl.wrap_socket( | ||||||
|  |                         self.smtp.sock, | ||||||
|  |                         cert_reqs=ssl.CERT_NONE | ||||||
|  |                         ) | ||||||
|  |                     sys.stderr.write( | ||||||
|  |                         '*** Warning, the server certificat is not verified (smtp) ***\n' | ||||||
|  |                         '***          set the option smtpCACerts                   ***\n' | ||||||
|  |                         ) | ||||||
|  |                 if not hasattr(self.smtp.sock, "read"): | ||||||
|  |                     # using httplib.FakeSocket with Python 2.5.x or earlier | ||||||
|  |                     self.smtp.sock.read = self.smtp.sock.recv | ||||||
|  |                 self.smtp.file = smtplib.SSLFakeFile(self.smtp.sock) | ||||||
|  |                 self.smtp.helo_resp = None | ||||||
|  |                 self.smtp.ehlo_resp = None | ||||||
|  |                 self.smtp.esmtp_features = {} | ||||||
|  |                 self.smtp.does_esmtp = 0 | ||||||
|  |                 # end:   ehlo + starttls | ||||||
|                 self.smtp.ehlo() |                 self.smtp.ehlo() | ||||||
|             else: |             else: | ||||||
|                 sys.stdout.write('*** Error: Control reached an invalid option. ***') |                 sys.stdout.write('*** Error: Control reached an invalid option. ***') | ||||||
|  | @ -1951,6 +2095,7 @@ class SMTPMailer(Mailer): | ||||||
|     def __del__(self): |     def __del__(self): | ||||||
|         if hasattr(self, 'smtp'): |         if hasattr(self, 'smtp'): | ||||||
|             self.smtp.quit() |             self.smtp.quit() | ||||||
|  |             del self.smtp | ||||||
|  |  | ||||||
|     def send(self, lines, to_addrs): |     def send(self, lines, to_addrs): | ||||||
|         try: |         try: | ||||||
|  | @ -1958,13 +2103,24 @@ class SMTPMailer(Mailer): | ||||||
|                 self.smtp.login(self.username, self.password) |                 self.smtp.login(self.username, self.password) | ||||||
|             msg = ''.join(lines) |             msg = ''.join(lines) | ||||||
|             # turn comma-separated list into Python list if needed. |             # turn comma-separated list into Python list if needed. | ||||||
|             if isinstance(to_addrs, basestring): |             if is_string(to_addrs): | ||||||
|                 to_addrs = [email for (name, email) in getaddresses([to_addrs])] |                 to_addrs = [email for (name, email) in getaddresses([to_addrs])] | ||||||
|             self.smtp.sendmail(self.envelopesender, to_addrs, msg) |             self.smtp.sendmail(self.envelopesender, to_addrs, msg) | ||||||
|         except Exception: |         except smtplib.SMTPResponseException: | ||||||
|             sys.stderr.write('*** Error sending email ***\n') |             sys.stderr.write('*** Error sending email ***\n') | ||||||
|  |             err = sys.exc_info()[1] | ||||||
|  |             sys.stderr.write('*** Error %d: %s\n' % (err.smtp_code, | ||||||
|  |                                                      bytes_to_str(err.smtp_error))) | ||||||
|  |             try: | ||||||
|  |                 smtp = self.smtp | ||||||
|  |                 # delete the field before quit() so that in case of | ||||||
|  |                 # error, self.smtp is deleted anyway. | ||||||
|  |                 del self.smtp | ||||||
|  |                 smtp.quit() | ||||||
|  |             except: | ||||||
|  |                 sys.stderr.write('*** Error closing the SMTP connection ***\n') | ||||||
|  |                 sys.stderr.write('*** Exiting anyway ... ***\n') | ||||||
|                 sys.stderr.write('*** %s\n' % sys.exc_info()[1]) |                 sys.stderr.write('*** %s\n' % sys.exc_info()[1]) | ||||||
|             self.smtp.quit() |  | ||||||
|             sys.exit(1) |             sys.exit(1) | ||||||
|  |  | ||||||
|  |  | ||||||
|  | @ -2097,6 +2253,14 @@ class Environment(object): | ||||||
|             If "html", generate commit emails in HTML instead of plain text |             If "html", generate commit emails in HTML instead of plain text | ||||||
|             used by default. |             used by default. | ||||||
|  |  | ||||||
|  |         html_in_intro (bool) | ||||||
|  |         html_in_footer (bool) | ||||||
|  |  | ||||||
|  |             When generating HTML emails, the introduction (respectively, | ||||||
|  |             the footer) will be HTML-escaped iff html_in_intro (respectively, | ||||||
|  |             the footer) is true. When false, only the values used to expand | ||||||
|  |             the template are escaped. | ||||||
|  |  | ||||||
|         refchange_showgraph (bool) |         refchange_showgraph (bool) | ||||||
|  |  | ||||||
|             True iff refchanges emails should include a detailed graph. |             True iff refchanges emails should include a detailed graph. | ||||||
|  | @ -2160,6 +2324,9 @@ class Environment(object): | ||||||
|         self.osenv = osenv or os.environ |         self.osenv = osenv or os.environ | ||||||
|         self.announce_show_shortlog = False |         self.announce_show_shortlog = False | ||||||
|         self.commit_email_format = "text" |         self.commit_email_format = "text" | ||||||
|  |         self.html_in_intro = False | ||||||
|  |         self.html_in_footer = False | ||||||
|  |         self.commitBrowseURL = None | ||||||
|         self.maxcommitemails = 500 |         self.maxcommitemails = 500 | ||||||
|         self.diffopts = ['--stat', '--summary', '--find-copies-harder'] |         self.diffopts = ['--stat', '--summary', '--find-copies-harder'] | ||||||
|         self.graphopts = ['--oneline', '--decorate'] |         self.graphopts = ['--oneline', '--decorate'] | ||||||
|  | @ -2236,7 +2403,7 @@ class Environment(object): | ||||||
|         The return value is always a new dictionary.""" |         The return value is always a new dictionary.""" | ||||||
|  |  | ||||||
|         if self._values is None: |         if self._values is None: | ||||||
|             values = {} |             values = {'': ''}  # %()s expands to the empty string. | ||||||
|  |  | ||||||
|             for key in self.COMPUTED_KEYS: |             for key in self.COMPUTED_KEYS: | ||||||
|                 value = getattr(self, 'get_%s' % (key,))() |                 value = getattr(self, 'get_%s' % (key,))() | ||||||
|  | @ -2375,6 +2542,16 @@ class ConfigOptionsEnvironmentMixin(ConfigEnvironmentMixin): | ||||||
|             else: |             else: | ||||||
|                 self.commit_email_format = commit_email_format |                 self.commit_email_format = commit_email_format | ||||||
|  |  | ||||||
|  |         html_in_intro = config.get_bool('htmlInIntro') | ||||||
|  |         if html_in_intro is not None: | ||||||
|  |             self.html_in_intro = html_in_intro | ||||||
|  |  | ||||||
|  |         html_in_footer = config.get_bool('htmlInFooter') | ||||||
|  |         if html_in_footer is not None: | ||||||
|  |             self.html_in_footer = html_in_footer | ||||||
|  |  | ||||||
|  |         self.commitBrowseURL = config.get('commitBrowseURL') | ||||||
|  |  | ||||||
|         maxcommitemails = config.get('maxcommitemails') |         maxcommitemails = config.get('maxcommitemails') | ||||||
|         if maxcommitemails is not None: |         if maxcommitemails is not None: | ||||||
|             try: |             try: | ||||||
|  | @ -2415,7 +2592,6 @@ class ConfigOptionsEnvironmentMixin(ConfigEnvironmentMixin): | ||||||
|                                  ['author']) |                                  ['author']) | ||||||
|         self.__reply_to_commit = config.get('replyToCommit', default=reply_to) |         self.__reply_to_commit = config.get('replyToCommit', default=reply_to) | ||||||
|  |  | ||||||
|         from_addr = self.config.get('from') |  | ||||||
|         self.from_refchange = config.get('fromRefchange') |         self.from_refchange = config.get('fromRefchange') | ||||||
|         self.forbid_field_values('fromRefchange', |         self.forbid_field_values('fromRefchange', | ||||||
|                                  self.from_refchange, |                                  self.from_refchange, | ||||||
|  | @ -3390,6 +3566,8 @@ def run_as_post_receive_hook(environment, mailer): | ||||||
|     if changes: |     if changes: | ||||||
|         push = Push(environment, changes) |         push = Push(environment, changes) | ||||||
|         push.send_emails(mailer, body_filter=environment.filter_body) |         push.send_emails(mailer, body_filter=environment.filter_body) | ||||||
|  |     if hasattr(mailer, '__del__'): | ||||||
|  |         mailer.__del__() | ||||||
|  |  | ||||||
|  |  | ||||||
| def run_as_update_hook(environment, mailer, refname, oldrev, newrev, force_send=False): | def run_as_update_hook(environment, mailer, refname, oldrev, newrev, force_send=False): | ||||||
|  | @ -3406,6 +3584,8 @@ def run_as_update_hook(environment, mailer, refname, oldrev, newrev, force_send= | ||||||
|         ] |         ] | ||||||
|     push = Push(environment, changes, force_send) |     push = Push(environment, changes, force_send) | ||||||
|     push.send_emails(mailer, body_filter=environment.filter_body) |     push.send_emails(mailer, body_filter=environment.filter_body) | ||||||
|  |     if hasattr(mailer, '__del__'): | ||||||
|  |         mailer.__del__() | ||||||
|  |  | ||||||
|  |  | ||||||
| def choose_mailer(config, environment): | def choose_mailer(config, environment): | ||||||
|  | @ -3418,6 +3598,7 @@ def choose_mailer(config, environment): | ||||||
|         smtpencryption = config.get('smtpencryption', default='none') |         smtpencryption = config.get('smtpencryption', default='none') | ||||||
|         smtpuser = config.get('smtpuser', default='') |         smtpuser = config.get('smtpuser', default='') | ||||||
|         smtppass = config.get('smtppass', default='') |         smtppass = config.get('smtppass', default='') | ||||||
|  |         smtpcacerts = config.get('smtpcacerts', default='') | ||||||
|         mailer = SMTPMailer( |         mailer = SMTPMailer( | ||||||
|             envelopesender=(environment.get_sender() or environment.get_fromaddr()), |             envelopesender=(environment.get_sender() or environment.get_fromaddr()), | ||||||
|             smtpserver=smtpserver, smtpservertimeout=smtpservertimeout, |             smtpserver=smtpserver, smtpservertimeout=smtpservertimeout, | ||||||
|  | @ -3425,6 +3606,7 @@ def choose_mailer(config, environment): | ||||||
|             smtpencryption=smtpencryption, |             smtpencryption=smtpencryption, | ||||||
|             smtpuser=smtpuser, |             smtpuser=smtpuser, | ||||||
|             smtppass=smtppass, |             smtppass=smtppass, | ||||||
|  |             smtpcacerts=smtpcacerts | ||||||
|             ) |             ) | ||||||
|     elif mailer == 'sendmail': |     elif mailer == 'sendmail': | ||||||
|         command = config.get('sendmailcommand') |         command = config.get('sendmailcommand') | ||||||
|  | @ -3691,17 +3873,7 @@ def main(args): | ||||||
|         return |         return | ||||||
|  |  | ||||||
|     if options.c: |     if options.c: | ||||||
|         parameters = os.environ.get('GIT_CONFIG_PARAMETERS', '') |         Config.add_config_parameters(options.c) | ||||||
|         if parameters: |  | ||||||
|             parameters += ' ' |  | ||||||
|         # git expects GIT_CONFIG_PARAMETERS to be of the form |  | ||||||
|         #    "'name1=value1' 'name2=value2' 'name3=value3'" |  | ||||||
|         # including everything inside the double quotes (but not the double |  | ||||||
|         # quotes themselves).  Spacing is critical.  Also, if a value contains |  | ||||||
|         # a literal single quote that quote must be represented using the |  | ||||||
|         # four character sequence: '\'' |  | ||||||
|         parameters += ' '.join("'" + x.replace("'", "'\\''") + "'" for x in options.c) |  | ||||||
|         os.environ['GIT_CONFIG_PARAMETERS'] = parameters |  | ||||||
|  |  | ||||||
|     config = Config('multimailhook') |     config = Config('multimailhook') | ||||||
|  |  | ||||||
|  |  | ||||||
|  | @ -55,6 +55,12 @@ import git_multimail | ||||||
| # git-multimail: | # git-multimail: | ||||||
| config = git_multimail.Config('multimailhook') | config = git_multimail.Config('multimailhook') | ||||||
|  |  | ||||||
|  | # Set some Git configuration variables. Equivalent to passing var=val | ||||||
|  | # to "git -c var=val" each time git is called, or to adding the | ||||||
|  | # configuration in .git/config (must come before instanciating the | ||||||
|  | # environment) : | ||||||
|  | #git_multimail.Config.add_config_parameters('multimailhook.commitEmailFormat=html') | ||||||
|  | #git_multimail.Config.add_config_parameters(('user.name=foo', 'user.email=foo@example.com')) | ||||||
|  |  | ||||||
| # Select the type of environment: | # Select the type of environment: | ||||||
| try: | try: | ||||||
|  |  | ||||||
		Loading…
	
		Reference in New Issue
	
	 Matthieu Moy
						Matthieu Moy