{"thread":{"id":"39129","subject":"[PATCH 1/3] git-multimail: Add an option to filter on branches","startedAt":"2015-04-21T23:04:45Z","lastAt":"2015-04-23T01:48:04Z","messageCount":7,"participants":["Dave Boutcher","Michael Haggerty"],"isPatch":true,"patchVersion":1,"patchTotal":3},"messages":[{"id":"259774","messageId":"1429657487-10809-1-git-send-email-daveboutcher@gmail.com","threadId":"39129","inReplyTo":null,"subject":"[PATCH 1/3] git-multimail: Add an option to filter on branches","fromName":"Dave Boutcher","fromEmail":"daveboutcher@gmail.com","sentAt":"2015-04-21T23:04:45Z","receivedAt":"2015-04-21T23:04:45Z","isPatch":true,"sender":{"key":"daveboutcher@gmail.com","avatar":"https://gravatar.com/avatar/927581be3798bb80d5b9aeed527621b796210d6d9507e4f21755904b8402dcce?d=mp&s=160"},"body":"Add a branches option to the config.  Only changes\npushed to specified branches will generate emails. Changes to tags\nwill continue to generate emails.\n\nSigned-off-by: Dave Boutcher <daveboutcher@gmail.com>\n---\n git-multimail/README           |  7 +++++++\n git-multimail/git_multimail.py | 44 ++++++++++++++++++++++++++++++++++--------\n 2 files changed, 43 insertions(+), 8 deletions(-)\n\ndiff --git a/git-multimail/README b/git-multimail/README\nindex 51add52..a40847a 100644\n--- a/git-multimail/README\n+++ b/git-multimail/README\n@@ -170,6 +170,13 @@ multimailhook.repoName\n     for gitolite repositories, or otherwise to derive this value from\n     the repository path name.\n \n+multimailhook.branches\n+\n+    A comma separated list of branches to monitor. If not set,\n+    notifications will be sent for updates to any branch.  Branch\n+    names can contain regular expressions, and this configuration\n+    option can be multivalued.\n+\n multimailhook.mailingList\n \n     The list of email addresses to which notification emails should be\ndiff --git a/git-multimail/git_multimail.py b/git-multimail/git_multimail.py\nindex 4374907..5ed253f 100755\n--- a/git-multimail/git_multimail.py\n+++ b/git-multimail/git_multimail.py\n@@ -1548,6 +1548,12 @@ class Environment(object):\n             get_reply_to_commit() is used for individual commit\n             emails.\n \n+        get_branches()\n+\n+            Return a list of branches to be handled as a list of regex\n+            patterns.  If empty list, all branches are handled.  Branches\n+            can contain regular expressions (such as foo/.*)\n+\n     They should also define the following attributes:\n \n         announce_show_shortlog (bool)\n@@ -1619,6 +1625,9 @@ class Environment(object):\n     def get_pusher_email(self):\n         return None\n \n+    def get_branches(self):\n+        return []\n+\n     def get_administrator(self):\n         return 'the administrator of this repository'\n \n@@ -1809,6 +1818,10 @@ class ConfigOptionsEnvironmentMixin(ConfigEnvironmentMixin):\n             else:\n                 return self.get_sender()\n \n+    def get_branches(self):\n+        branches = self.config.get_all('branches',[])\n+        return [re.compile('refs/heads/'+b.strip()) for bs in branches for b in bs.split(',')]\n+\n     def get_reply_to_refchange(self, refchange):\n         if self.__reply_to_refchange is None:\n             return super(ConfigOptionsEnvironmentMixin, self).get_reply_to_refchange(refchange)\n@@ -2220,8 +2233,9 @@ class Push(object):\n             ])\n         )\n \n-    def __init__(self, changes):\n+    def __init__(self, changes, environment):\n         self.changes = sorted(changes, key=self._sort_key)\n+        self.environment = environment\n \n         # The SHA-1s of commits referred to by references unaffected\n         # by this push:\n@@ -2246,6 +2260,12 @@ class Push(object):\n     def _sort_key(klass, change):\n         return (klass.SORT_ORDER[change.__class__, change.change_type], change.refname,)\n \n+    def branches_match(self, branch):\n+        branches = self.environment.get_branches()\n+        if not branches:\n+            return [True]\n+        return [x for x in [r.match(branch) for r in branches] if x]\n+\n     def _compute_other_ref_sha1s(self):\n         \"\"\"Return the GitObjects referred to by references unaffected by this push.\"\"\"\n \n@@ -2264,6 +2284,10 @@ class Push(object):\n             )\n         for line in read_git_lines(['for-each-ref', '--format=%s' % (fmt,)]):\n             (sha1, type, name) = line.split(' ', 2)\n+            # If we are using a branch filter, skip other branches\n+            if type == 'commit' and not self.branches_match(name):\n+                continue\n+\n             if sha1 and type == 'commit' and name not in updated_refs:\n                 sha1s.add(sha1)\n \n@@ -2334,6 +2358,16 @@ class Push(object):\n         unhandled_sha1s = set(self.get_new_commits())\n         send_date = IncrementalDateTime()\n         for change in self.changes:\n+            sha1s = []\n+            for sha1 in reversed(list(self.get_new_commits(change))):\n+                if sha1 in unhandled_sha1s:\n+                    sha1s.append(sha1)\n+                    unhandled_sha1s.remove(sha1)\n+\n+            # if we are filtering on branches, skip branches we don't care about\n+            if change.refname_type == 'branch' and not self.branches_match(change.refname):\n+                continue\n+\n             # Check if we've got anyone to send to\n             if not change.recipients:\n                 sys.stderr.write(\n@@ -2349,12 +2383,6 @@ class Push(object):\n                     change.recipients,\n                     )\n \n-            sha1s = []\n-            for sha1 in reversed(list(self.get_new_commits(change))):\n-                if sha1 in unhandled_sha1s:\n-                    sha1s.append(sha1)\n-                    unhandled_sha1s.remove(sha1)\n-\n             max_emails = change.environment.maxcommitemails\n             if max_emails and len(sha1s) > max_emails:\n                 sys.stderr.write(\n@@ -2389,7 +2417,7 @@ def run_as_post_receive_hook(environment, mailer):\n         changes.append(\n             ReferenceChange.create(environment, oldrev, newrev, refname)\n             )\n-    push = Push(changes)\n+    push = Push(changes, environment)\n     push.send_emails(mailer, body_filter=environment.filter_body)\n \n \n-- \n2.3.0\n"},{"id":"259775","messageId":"1429657487-10809-2-git-send-email-daveboutcher@gmail.com","threadId":"39129","inReplyTo":"1429657487-10809-1-git-send-email-daveboutcher@gmail.com","subject":"[PATCH 2/3] git-multimail: Add a quiet option to prevent outputting email addresses from hook","fromName":"Dave Boutcher","fromEmail":"daveboutcher@gmail.com","sentAt":"2015-04-21T23:04:46Z","receivedAt":"2015-04-21T23:04:46Z","isPatch":true,"sender":{"key":"daveboutcher@gmail.com","avatar":"https://gravatar.com/avatar/927581be3798bb80d5b9aeed527621b796210d6d9507e4f21755904b8402dcce?d=mp&s=160"},"body":"We have a very log list of recipients...displaying that list\non every push is annoying\n\nAdd an option to prevent printing the list of email addresses from\nthe hook\n\nSigned-off-by: Dave Boutcher <daveboutcher@gmail.com>\n---\n git-multimail/README           |  3 +++\n git-multimail/git_multimail.py | 11 ++++++++++-\n 2 files changed, 13 insertions(+), 1 deletion(-)\n\ndiff --git a/git-multimail/README b/git-multimail/README\nindex a40847a..49e0937 100644\n--- a/git-multimail/README\n+++ b/git-multimail/README\n@@ -396,6 +396,9 @@ multimailhook.replyToRefchange\n     - The value \"none\", in which case the Reply-To: field will be\n       omitted.\n \n+multimailhook.quiet\n+\n+    Do not output the list of email recipients from the hook\n \n Email filtering aids\n --------------------\ndiff --git a/git-multimail/git_multimail.py b/git-multimail/git_multimail.py\nindex 5ed253f..095110a 100755\n--- a/git-multimail/git_multimail.py\n+++ b/git-multimail/git_multimail.py\n@@ -1582,6 +1582,9 @@ class Environment(object):\n             commit mail.  The value should be a list of strings\n             representing words to be passed to the command.\n \n+        quiet (bool)\n+            On success do not write to stderr\n+\n     \"\"\"\n \n     REPO_NAME_RE = re.compile(r'^(?P<name>.+?)(?:\\.git)$')\n@@ -1594,6 +1597,7 @@ class Environment(object):\n         self.logopts = []\n         self.refchange_showlog = False\n         self.commitlogopts = ['-C', '--stat', '-p', '--cc']\n+        self.quiet = False\n \n         self.COMPUTED_KEYS = [\n             'administrator',\n@@ -1745,6 +1749,10 @@ class ConfigOptionsEnvironmentMixin(ConfigEnvironmentMixin):\n             'refchangeshowlog', default=self.refchange_showlog\n             )\n \n+        self.quiet = config.get_bool(\n+            'quiet', default=False\n+            )\n+\n         maxcommitemails = config.get('maxcommitemails')\n         if maxcommitemails is not None:\n             try:\n@@ -2376,7 +2384,8 @@ class Push(object):\n                     % (change.refname, change.old.sha1, change.new.sha1,)\n                     )\n             else:\n-                sys.stderr.write('Sending notification emails to: %s\\n' % (change.recipients,))\n+                if not self.environment.quiet:\n+                    sys.stderr.write('Sending notification emails to: %s\\n' % (change.recipients,))\n                 extra_values = {'send_date': send_date.next()}\n                 mailer.send(\n                     change.generate_email(self, body_filter, extra_values),\n-- \n2.3.0\n"},{"id":"259776","messageId":"1429657487-10809-3-git-send-email-daveboutcher@gmail.com","threadId":"39129","inReplyTo":"1429657487-10809-1-git-send-email-daveboutcher@gmail.com","subject":"[PATCH 3/3] git-multimail: Add stdout option to config","fromName":"Dave Boutcher","fromEmail":"daveboutcher@gmail.com","sentAt":"2015-04-21T23:04:47Z","receivedAt":"2015-04-21T23:04:47Z","isPatch":true,"sender":{"key":"daveboutcher@gmail.com","avatar":"https://gravatar.com/avatar/927581be3798bb80d5b9aeed527621b796210d6d9507e4f21755904b8402dcce?d=mp&s=160"},"body":"Add a stdout option to the config that is equivalent to the --stdout\ncommand line option. This allows for easier debugging for a hook\n\nSigned-off-by: Dave Boutcher <daveboutcher@gmail.com>\n---\n git-multimail/README           |  5 +++++\n git-multimail/git_multimail.py | 10 +++++++++-\n 2 files changed, 14 insertions(+), 1 deletion(-)\n\ndiff --git a/git-multimail/README b/git-multimail/README\nindex 49e0937..0235b83 100644\n--- a/git-multimail/README\n+++ b/git-multimail/README\n@@ -400,6 +400,11 @@ multimailhook.quiet\n \n     Do not output the list of email recipients from the hook\n \n+multimailhook.stdout\n+\n+    For debugging, send emails to stdout rather than to the\n+    mailer.  Equivalent to the --stdout command line option\n+\n Email filtering aids\n --------------------\n \ndiff --git a/git-multimail/git_multimail.py b/git-multimail/git_multimail.py\nindex 095110a..35a1140 100755\n--- a/git-multimail/git_multimail.py\n+++ b/git-multimail/git_multimail.py\n@@ -1585,6 +1585,9 @@ class Environment(object):\n         quiet (bool)\n             On success do not write to stderr\n \n+        stdout (bool)\n+            Write email to stdout rather than emailing. Useful for debugging\n+\n     \"\"\"\n \n     REPO_NAME_RE = re.compile(r'^(?P<name>.+?)(?:\\.git)$')\n@@ -1598,6 +1601,7 @@ class Environment(object):\n         self.refchange_showlog = False\n         self.commitlogopts = ['-C', '--stat', '-p', '--cc']\n         self.quiet = False\n+        self.stdout = False\n \n         self.COMPUTED_KEYS = [\n             'administrator',\n@@ -1753,6 +1757,10 @@ class ConfigOptionsEnvironmentMixin(ConfigEnvironmentMixin):\n             'quiet', default=False\n             )\n \n+        self.stdout = config.get_bool(\n+            'stdout', default=False\n+            )\n+\n         maxcommitemails = config.get('maxcommitemails')\n         if maxcommitemails is not None:\n             try:\n@@ -2563,7 +2571,7 @@ def main(args):\n                 sys.stderr.write('    %s : %r\\n' % (k, v))\n             sys.stderr.write('\\n')\n \n-        if options.stdout:\n+        if options.stdout or environment.stdout:\n             mailer = OutputMailer(sys.stdout)\n         else:\n             mailer = choose_mailer(config, environment)\n-- \n2.3.0\n"},{"id":"259793","messageId":"55377A60.707@alum.mit.edu","threadId":"39129","inReplyTo":"1429657487-10809-1-git-send-email-daveboutcher@gmail.com","subject":"Re: [PATCH 1/3] git-multimail: Add an option to filter on branches","fromName":"Michael Haggerty","fromEmail":"mhagger@alum.mit.edu","sentAt":"2015-04-22T10:39:28Z","receivedAt":"2015-04-22T10:39:28Z","isPatch":true,"sender":{"key":"mhagger@alum.mit.edu","avatar":"https://avatars.githubusercontent.com/u/119718?v=4"},"body":"On 04/22/2015 01:04 AM, Dave Boutcher wrote:\n> Add a branches option to the config.  Only changes\n> pushed to specified branches will generate emails. Changes to tags\n> will continue to generate emails.\n\nThanks for the patches. Patches 2 and 3 seem uncontroversial, so I\nalready merged them. Patch 1 is more interesting, and there have been\nother proposals for similar features, so I created a pull request for it:\n\n    https://github.com/git-multimail/git-multimail/pull/75\n\n(Note the new URL--I just created a GitHub \"organization\" for\ngit-multimail to make it easier for other people to get involved. More\ninfo soon...)\n\nMichael\n\n-- \nMichael Haggerty\nmhagger@alum.mit.edu\n"},{"id":"259792","messageId":"CAEXuECi=UDo7_0O22+Gi1yew1M=LrrKLXNT9gdSvdgQwCxYjow@mail.gmail.com","threadId":"39129","inReplyTo":"55377A60.707@alum.mit.edu","subject":"Re: [PATCH 1/3] git-multimail: Add an option to filter on branches","fromName":"Dave Boutcher","fromEmail":"daveboutcher@gmail.com","sentAt":"2015-04-22T10:46:36Z","receivedAt":"2015-04-22T10:46:36Z","isPatch":true,"sender":{"key":"daveboutcher@gmail.com","avatar":"https://gravatar.com/avatar/927581be3798bb80d5b9aeed527621b796210d6d9507e4f21755904b8402dcce?d=mp&s=160"},"body":"Thanks Michael,\n\nThe only code I'm not fond of is matching on a list of regular\nexpressions.  There must be a more pythonic way to do:\n\n+ return [x for x in [r.match(branch) for r in branches] if x]\n\nwhich basically returns true if \"branch\" matches any regular\nexpression in the list.\n\nI pushed this change out to our production git server (its good to be\nthe king.)  I'll obviously update here if it does anything too\nunfortunate.\n\n\nOn Wed, Apr 22, 2015 at 6:39 PM, Michael Haggerty <mhagger@alum.mit.edu> wrote:\n> On 04/22/2015 01:04 AM, Dave Boutcher wrote:\n>> Add a branches option to the config.  Only changes\n>> pushed to specified branches will generate emails. Changes to tags\n>> will continue to generate emails.\n>\n> Thanks for the patches. Patches 2 and 3 seem uncontroversial, so I\n> already merged them. Patch 1 is more interesting, and there have been\n> other proposals for similar features, so I created a pull request for it:\n>\n>     https://github.com/git-multimail/git-multimail/pull/75\n>\n> (Note the new URL--I just created a GitHub \"organization\" for\n> git-multimail to make it easier for other people to get involved. More\n> info soon...)\n>\n> Michael\n>\n> --\n> Michael Haggerty\n> mhagger@alum.mit.edu\n>\n\n\n\n-- \nDave B\n包小龙\n"},{"id":"259795","messageId":"553789B6.7030501@alum.mit.edu","threadId":"39129","inReplyTo":"CAEXuECi=UDo7_0O22+Gi1yew1M=LrrKLXNT9gdSvdgQwCxYjow@mail.gmail.com","subject":"Re: [PATCH 1/3] git-multimail: Add an option to filter on branches","fromName":"Michael Haggerty","fromEmail":"mhagger@alum.mit.edu","sentAt":"2015-04-22T11:44:54Z","receivedAt":"2015-04-22T11:44:54Z","isPatch":true,"sender":{"key":"mhagger@alum.mit.edu","avatar":"https://avatars.githubusercontent.com/u/119718?v=4"},"body":"On 04/22/2015 12:46 PM, Dave Boutcher wrote:\n> The only code I'm not fond of is matching on a list of regular\n> expressions.  There must be a more pythonic way to do:\n> \n> + return [x for x in [r.match(branch) for r in branches] if x]\n> \n> which basically returns true if \"branch\" matches any regular\n> expression in the list.\n\nI think what you are looking for is\n\n    return any(r.match(branch) for r in branches)\n\nThis also has the advantage of stopping processing as soon as it finds a\nmatch.\n\nI was also wondering why you decided to support comma-separated lists of\nregexps *and* multivalued config settings. It seems that supporting only\nmultivalued settings would suffice.\n\nMaybe you are following the precedent of our email configuration\nsettings, which support comma-separated lists of email addresses? If so,\nI don't think that is necessary. The email support is there for\nbackwards compatibility and because comma-separated email addresses are\na thing in RFC 2822. Neither of these arguments apply to branch regexps.\n\n> I pushed this change out to our production git server (its good to be\n> the king.)  I'll obviously update here if it does anything too\n> unfortunate.\n\nThanks.\n\nMichael\n\n-- \nMichael Haggerty\nmhagger@alum.mit.edu\n"},{"id":"259880","messageId":"CAEXuECj=m-TVfVzQAtw91bfgMEUvPDt4_PtN73SxWDWSZebpow@mail.gmail.com","threadId":"39129","inReplyTo":"553789B6.7030501@alum.mit.edu","subject":"Re: [PATCH 1/3] git-multimail: Add an option to filter on branches","fromName":"Dave Boutcher","fromEmail":"daveboutcher@gmail.com","sentAt":"2015-04-23T01:48:04Z","receivedAt":"2015-04-23T01:48:04Z","isPatch":true,"sender":{"key":"daveboutcher@gmail.com","avatar":"https://gravatar.com/avatar/927581be3798bb80d5b9aeed527621b796210d6d9507e4f21755904b8402dcce?d=mp&s=160"},"body":"On Wed, Apr 22, 2015 at 7:44 PM, Michael Haggerty <mhagger@alum.mit.edu> wrote:\n> I think what you are looking for is\n>\n>     return any(r.match(branch) for r in branches)\n\nYup, thats exactly what I wanted.  I'll submit an updated patch\n\n> I was also wondering why you decided to support comma-separated lists of\n> regexps *and* multivalued config settings. It seems that supporting only\n> multivalued settings would suffice.\n\nIn practice we are using gitolite and git-multimail.  Allowing both\nmeans our gitolite.conf file can look like\n\nrepo foo\n    # always notify on master\n    config multimailhook.branches     = master\n\n   # notify for the current pre-release branches\n   config multimailhook.branches     = v1.7-pre, v1.8-pre\n\n-- \nDave B\n"}]}