[Differential] [Request, 149 lines] D98: revset: support reading aliases from a .hgrevsets file

indygreg (Gregory Szorc) phabricator at mercurial-scm.org
Sat Jul 15 23:41:58 UTC 2017


indygreg created this revision.
Herald added a subscriber: mercurial-devel.
Herald added a reviewer: hg-reviewers.

REVISION SUMMARY
  It is common for projects or companies to define/recommend
  common customizations for Mercurial. However, distributing these
  customizations can be difficult because there's no distribution
  channel built into Mercurial itself. The "configexpress"
  extension is making some inroads here. But it is geared towards
  hgrc files. I think there's room to make certain customizations
  outside of the config system via specially named files checked
  into the repository.
  
  In this commit, we introduce a new experimental feature for
  reading revset aliases from a .hgrevsets file in the working
  directory. A repository can then add this file to define
  custom revset aliases which will automagically be made available
  to anyone who clones the repo and has a modern enough Mercurial
  client [with the feature enabled].
  
  Essentially, the .hgrevsets file is parsed as a [revsetalias]
  config section but with lower priority than all config files.
  This is because a user may want to overwrite an alias defined in
  the repo and since not all users can commit to the repo, config
  files or --config must take precedence.
  
  One can imagine applying this pattern of "define customizations in
  .hg<thing> files" to other features. For example, repositories could
  also define templates - either keywords or full template maps.
  Another potential area is filesets. You could create named
  collections of files and do things like `hg files mycollection`
  or even reference those named collections as part of building a
  narrow clone or sparse checkout profile.
  
  While I'm reasonably confident in the implementation, the feature
  is marked as experimental because it is close to freeze and this
  feature feels a bit dangerous to enable by default with minimal
  time to bake.

REPOSITORY
  rHG Mercurial

REVISION DETAIL
  https://phab.mercurial-scm.org/D98

AFFECTED FILES
  mercurial/configitems.py
  mercurial/revset.py
  tests/test-revset.t

CHANGE DETAILS

Index: tests/test-revset.t
===================================================================
--- tests/test-revset.t
+++ tests/test-revset.t
@@ -4114,6 +4114,102 @@
   5:904fa392b941
   6:e0cc66ef77e8
 
+.hgrevsets file from working directory can define aliases
+
+  $ cat > .hgrevsets << EOF
+  > filealiasall = all()
+  > filealiasthroughfour = 0:4
+  > EOF
+
+  $ hg log -q -r 'filealiasall'
+  abort: unknown revision 'filealiasall'!
+  [255]
+
+  $ hg log -q -r 'filealiasall' --config experimental.hgrevsetsfile=true
+  0:2785f51eece5
+  1:d75937da8da0
+  2:5ed5505e9f1c
+  3:8528aa5637f2
+  4:2326846efdab
+  5:904fa392b941
+  6:e0cc66ef77e8
+  7:013af1973af4
+  8:d5d0dcbdc4d9
+  9:24286f4ae135
+
+.hgrevsets is combined with revsetalias
+
+  $ hg log -q -r 'filealiasall & m' --config experimental.hgrevsetsfile=true
+  6:e0cc66ef77e8
+
+Value from config overwrites .hgrevsets
+
+  $ cat > .hgrevsets << EOF
+  > m = all()
+  > EOF
+
+  $ hg log -q -r m --config experimental.hgrevsetsfile=true
+  6:e0cc66ef77e8
+
+Minimum version filtering in .hgrevset entries works
+
+  $ cat > fakeversion.py << EOF
+  > from mercurial import util
+  > util.version = lambda: '4.1'
+  > EOF
+
+  $ cat > .hgrevsets << EOF
+  > requiresnew = 0:1
+  > requiresnew:minversion = 4.2
+  > requiresold = 2:3
+  > requiresold:minversion = 4.1
+  > EOF
+
+  $ hg log -q -r requiresnew --config experimental.hgrevsetsfile=true --config extensions.fakeversion=`pwd`/fakeversion.py
+  abort: unknown revision 'requiresnew'!
+  [255]
+
+  $ hg log -q -r requiresold --config experimental.hgrevsetsfile=true --config extensions.fakeversion=`pwd`/fakeversion.py
+  2:5ed5505e9f1c
+  3:8528aa5637f2
+
+Missing .hgrevsetsfile is OK
+
+  $ rm .hgrevsets
+  $ hg log -q -r 1 --config experimental.hgrevsetsfile=true
+  1:d75937da8da0
+
+Invalid syntax in .hgrevsets file
+
+  $ cat > .hgrevsets << EOF
+  > fileall = all()
+  > bar
+  > EOF
+
+  $ hg log -q -r fileall --config experimental.hgrevsetsfile=true
+  invalid syntax in .hgrevsets: bar
+  0:2785f51eece5
+  1:d75937da8da0
+  2:5ed5505e9f1c
+  3:8528aa5637f2
+  4:2326846efdab
+  5:904fa392b941
+  6:e0cc66ef77e8
+  7:013af1973af4
+  8:d5d0dcbdc4d9
+  9:24286f4ae135
+
+Unable to parse :minversion in .hgrevsets file
+
+  $ cat > .hgrevsets << EOF
+  > fileall = all()
+  > fileall:minversion = invalid
+  > EOF
+
+  $ hg log -q -r foo --config experimental.hgrevsetsfile=true
+  abort: unknown revision 'foo'!
+  [255]
+
 issue2549 - correct optimizations
 
   $ try 'limit(1 or 2 or 3, 2) and not 2'
Index: mercurial/revset.py
===================================================================
--- mercurial/revset.py
+++ mercurial/revset.py
@@ -2061,6 +2061,9 @@
 
     If localalias is not None, it is a dict {name: definitionstring}. It takes
     precedence over [revsetalias] config section.
+
+    Aliases from the config file and a ``.hgrevsets`` file in the working
+    directory are only expanded if ``ui`` is defined.
     """
     if not specs:
         def mfunc(repo, subset=None):
@@ -2079,6 +2082,12 @@
 
     aliases = []
     warn = None
+
+    # Aliases from the .hgrevsets file in the repo have the lowest priority
+    # because the user is in the least control of them. An hgrc or config
+    # option can be set easily. A file in the repo cannot be changed as easily.
+    if ui and repo and repo.ui.configbool('experimental', 'hgrevsetsfile'):
+        aliases.extend(_parsehgrevsetsfile(repo))
     if ui:
         aliases.extend(ui.configitems('revsetalias'))
         warn = ui.warn
@@ -2108,6 +2117,47 @@
         if func._safe:
             safesymbols.add(name)
 
+def _parsehgrevsetsfile(repo):
+    options = {}
+
+    data = repo.wvfs.tryread('.hgrevsets')
+    for line in data.splitlines():
+        line = line.strip()
+        if not line or line.startswith('#'):
+            continue
+
+        if '=' not in line:
+            repo.ui.warn(_('invalid syntax in .hgrevsets: %s\n') % line)
+            continue
+
+        name, value = line.split('=', 1)
+        options[name.strip()] = value.strip()
+
+    aliases = []
+    ourversion = util.versiontuple(n=2)
+
+    for key, value in options.iteritems():
+        if ':' in key:
+            continue
+
+        # Revsets in repo may require features not available to old clients.
+        # Allow revsets to self-advertise minimum version requirements.
+        minversion = options.get('%s:minversion' % key)
+        if minversion:
+            try:
+                wantversion = util.versiontuple(minversion, n=2)
+            except IndexError:
+                repo.ui.warn(_('illegal value for %s in .hgrevsetsfile: %s\n') %
+                             ('%s:minversion' % key, minversion))
+                continue
+
+            if wantversion > ourversion:
+                continue
+
+        aliases.append((key, value))
+
+    return aliases
+
 # load built-in predicates explicitly to setup safesymbols
 loadpredicate(None, None, predicate)
 
Index: mercurial/configitems.py
===================================================================
--- mercurial/configitems.py
+++ mercurial/configitems.py
@@ -199,6 +199,9 @@
 coreconfigitem('experimental', 'graphshorten',
     default=False,
 )
+coreconfigitem('experimental', 'hgrevsetsfile',
+    default=False,
+)
 coreconfigitem('experimental', 'hook-track-tags',
     default=False,
 )


EMAIL PREFERENCES
  https://phab.mercurial-scm.org/settings/panel/emailpreferences/

To: indygreg, #hg-reviewers
Cc: mercurial-devel


More information about the Mercurial-devel mailing list