[PATCH] notify: support revset selection for subscriptions

michalsznajder at gmail.com michalsznajder at gmail.com
Mon Aug 13 16:12:09 CDT 2012


# HG changeset patch
# User Michal Sznajder <michalsznajder at gmail.com>
# Date 1344890530 -7200
# Node ID 8c8aa7d83db0b1fd0d1062b79fe3768c032d29fb
# Parent  c6f88e7f95b764e23b7e0b4353c5a6458bbc3cc4
notify: support revset selection for subscriptions

A repo pattern for any notify configuration contains a glob matching the
path to the repo. Additionally, it may now contain a revset spec, separated
from the glob by '#'.

Example:
  [reposubs]
  */widgets#branch(release) = qa-team at example.com

This sends to ``qa-team at example.com`` whenever a changeset on the ``release``
branch triggers a notification in any repository ending in ``widgets``.

This patch was completely done by David Champion <dgc at uchicago.edu> with me
making tiny changes to his tests.

diff --git a/hgext/notify.py b/hgext/notify.py
--- a/hgext/notify.py
+++ b/hgext/notify.py
@@ -28,21 +28,26 @@
 be assigned to repositories. The ``[usersubs]`` section maps multiple
 repositories to a given recipient. The ``[reposubs]`` section maps
 multiple recipients to a single repository::
 
   [usersubs]
-  # key is subscriber email, value is a comma-separated list of repo glob
-  # patterns
+  # key is subscriber email, value is a comma-separated list of repo patterns
   user at host = pattern
 
   [reposubs]
-  # key is glob pattern, value is a comma-separated list of subscriber
-  # emails
+  # key is repo pattern, value is a comma-separated list of subscriber emails
   pattern = user at host
 
-Glob patterns are matched against absolute path to repository
-root.
+A ``pattern`` is a ``glob`` matching the absolute path to a repository,
+optionally combined with a revset expression. A revset expression, if
+present, is separated from the glob by a hash. Example::
+
+  [reposubs]
+  */widgets#branch(release) = qa-team at example.com
+
+This sends to ``qa-team at example.com`` whenever a changeset on the ``release``
+branch triggers a notification in any repository ending in ``widgets``.
 
 In order to place them under direct user management, ``[usersubs]`` and
 ``[reposubs]`` sections may be placed in a separate ``hgrc`` file and
 incorporated by reference::
 
@@ -215,18 +220,26 @@
     def subscribers(self):
         '''return list of email addresses of subscribers to this repo.'''
         subs = set()
         for user, pats in self.ui.configitems('usersubs'):
             for pat in pats.split(','):
+                if '#' in pat:
+                    pat, revs = pat.split('#', 1)
+                else:
+                    revs = None
                 if fnmatch.fnmatch(self.repo.root, pat.strip()):
-                    subs.add(self.fixmail(user))
+                    subs.add((self.fixmail(user), revs))
         for pat, users in self.ui.configitems('reposubs'):
+            if '#' in pat:
+                pat, revs = pat.split('#', 1)
+            else:
+                revs = None
             if fnmatch.fnmatch(self.repo.root, pat):
                 for user in users.split(','):
-                    subs.add(self.fixmail(user))
-        return [mail.addressencode(self.ui, s, self.charsets, self.test)
-                for s in sorted(subs)]
+                    subs.add((self.fixmail(user), revs))
+        return [(mail.addressencode(self.ui, s, self.charsets, self.test), r)
+                for s, r in sorted(subs)]
 
     def node(self, ctx, **props):
         '''format one changeset, unless it is a suppressed merge.'''
         if not self.merge and len(ctx.parents()) > 1:
             return False
@@ -241,10 +254,25 @@
         return source not in ok_sources
 
     def send(self, ctx, count, data):
         '''send message.'''
 
+        # Select subscribers by revset
+        subs = set()
+        for sub, spec in self.subs:
+            if spec is None:
+                subs.add(sub)
+                continue
+            revs = self.repo.revs('%r and %d:', spec, ctx.rev())
+            if len(revs):
+                subs.add(sub)
+                continue
+        if len(subs) == 0:
+            self.ui.debug('notify: no subscribers to selected repo '
+                          'and revset\n')
+            return
+
         p = email.Parser.Parser()
         try:
             msg = p.parsestr(data)
         except email.Errors.MessageParseError, inst:
             raise util.Abort(inst)
@@ -290,22 +318,22 @@
         msg['X-Hg-Notification'] = 'changeset %s' % ctx
         if not msg['Message-Id']:
             msg['Message-Id'] = ('<hg.%s.%s.%s@%s>' %
                                  (ctx, int(time.time()),
                                   hash(self.repo.root), socket.getfqdn()))
-        msg['To'] = ', '.join(self.subs)
+        msg['To'] = ', '.join(sorted(subs))
 
         msgtext = msg.as_string()
         if self.test:
             self.ui.write(msgtext)
             if not msgtext.endswith('\n'):
                 self.ui.write('\n')
         else:
             self.ui.status(_('notify: sending %d subscribers %d changes\n') %
-                           (len(self.subs), count))
+                           (len(subs), count))
             mail.sendmail(self.ui, util.email(msg['From']),
-                          self.subs, msgtext, mbox=self.mbox)
+                          subs, msgtext, mbox=self.mbox)
 
     def diff(self, ctx, ref=None):
 
         maxdiff = int(self.ui.config('notify', 'maxdiff', 300))
         prev = ctx.p1().node()
diff --git a/tests/test-notify.t b/tests/test-notify.t
--- a/tests/test-notify.t
+++ b/tests/test-notify.t
@@ -40,20 +40,26 @@
   to repositories. The "[usersubs]" section maps multiple repositories to a
   given recipient. The "[reposubs]" section maps multiple recipients to a single
   repository:
   
     [usersubs]
-    # key is subscriber email, value is a comma-separated list of repo glob
-    # patterns
+    # key is subscriber email, value is a comma-separated list of repo patterns
     user at host = pattern
   
     [reposubs]
-    # key is glob pattern, value is a comma-separated list of subscriber
-    # emails
+    # key is repo pattern, value is a comma-separated list of subscriber emails
     pattern = user at host
   
-  Glob patterns are matched against absolute path to repository root.
+  A "pattern" is a "glob" matching the absolute path to a repository, optionally
+  combined with a revset expression. A revset expression, if present, is
+  separated from the glob by a hash. Example:
+  
+    [reposubs]
+    */widgets#branch(release) = qa-team at example.com
+  
+  This sends to "qa-team at example.com" whenever a changeset on the "release"
+  branch triggers a notification in any repository ending in "widgets".
   
   In order to place them under direct user management, "[usersubs]" and
   "[reposubs]" sections may be placed in a separate "hgrc" file and incorporated
   by reference:
   
@@ -471,5 +477,79 @@
   ononononononononononononononononononononononononononononononononononononono=
   nononononononononononononononononononononononononononononononononononononon=
   ononononononononononononononononononononononononononononononononononononono=
   nonononononononononononono
   
+ revset selection: send to address that matches branch and repo
+
+  $ cat << EOF >> $HGRCPATH
+  > [hooks]
+  > incoming.notify = python:hgext.notify.hook
+  > 
+  > [notify]
+  > sources = pull
+  > test = True
+  > diffstat = False
+  > maxdiff = 0
+  > 
+  > [reposubs]
+  > */a#branch(test) = will_no_be_send at example.com
+  > */b#branch(test) = notify at example.com
+  > EOF
+  $ hg --cwd a branch test
+  marked working directory as branch test
+  (branches are permanent and global, did you want a bookmark?)
+  $ echo a >> a/a
+  $ hg --cwd a ci -m test -d '1 0'
+  $ hg --traceback --cwd b pull ../a | \
+  >   python -c 'import sys,re; print re.sub("\n\t", " ", sys.stdin.read()),'
+  pulling from ../a
+  searching for changes
+  adding changesets
+  adding manifests
+  adding file changes
+  added 1 changesets with 1 changes to 1 files
+  Content-Type: text/plain; charset="us-ascii"
+  MIME-Version: 1.0
+  Content-Transfer-Encoding: 7bit
+  X-Test: foo
+  Date: * (glob)
+  Subject: test
+  From: test at test.com
+  X-Hg-Notification: changeset fbbcbc516f2f
+  Message-Id: <hg.fbbcbc516f2f.*.*@*> (glob)
+  To: baz at test.com, foo at bar, notify at example.com
+  
+  changeset fbbcbc516f2f in b
+  description: test
+  (run 'hg update' to get a working copy)
+
+revset selection: don't send to address that waits for mails
+from different branch
+
+  $ hg --cwd a update default
+  1 files updated, 0 files merged, 0 files removed, 0 files unresolved
+  $ echo a >> a/a
+  $ hg --cwd a ci -m test -d '1 0'
+  $ hg --traceback --cwd b pull ../a | \
+  >   python -c 'import sys,re; print re.sub("\n\t", " ", sys.stdin.read()),'
+  pulling from ../a
+  searching for changes
+  adding changesets
+  adding manifests
+  adding file changes
+  added 1 changesets with 0 changes to 0 files (+1 heads)
+  Content-Type: text/plain; charset="us-ascii"
+  MIME-Version: 1.0
+  Content-Transfer-Encoding: 7bit
+  X-Test: foo
+  Date: * (glob)
+  Subject: test
+  From: test at test.com
+  X-Hg-Notification: changeset 38b42fa092de
+  Message-Id: <hg.38b42fa092de.*.*@*> (glob)
+  To: baz at test.com, foo at bar
+  
+  changeset 38b42fa092de in b
+  description: test
+  (run 'hg heads' to see heads)
+


More information about the Mercurial-devel mailing list