multi-urls: add a boolean suboption that unlock path specification as list
authorPierre-Yves David <pierre-yves.david@octobus.net>
Fri, 16 Apr 2021 00:16:47 +0200
changeset 47284 834f4e9d1af2
parent 47283 a671832a8e41
child 47285 0470a44b9e74
multi-urls: add a boolean suboption that unlock path specification as list When this option is set, a list of patch can be specifed as value for `[paths]` entries. For the command who support it, this behave the same as providing multiple destination of the command line. Differential Revision: https://phab.mercurial-scm.org/D10452
mercurial/utils/urlutil.py
tests/test-exchange-multi-source.t
tests/test-paths.t
--- a/mercurial/utils/urlutil.py	Thu Apr 15 20:13:29 2021 +0200
+++ b/mercurial/utils/urlutil.py	Fri Apr 16 00:16:47 2021 +0200
@@ -20,6 +20,10 @@
     urllibcompat,
 )
 
+from . import (
+    stringutil,
+)
+
 
 if pycompat.TYPE_CHECKING:
     from typing import (
@@ -639,19 +643,30 @@
 
         home_path = os.path.expanduser(b'~')
 
-        for name, loc in ui.configitems(b'paths', ignoresub=True):
+        for name, value in ui.configitems(b'paths', ignoresub=True):
             # No location is the same as not existing.
-            if not loc:
+            if not value:
                 continue
             _value, sub_opts = ui.configsuboptions(b'paths', name)
             s = ui.configsource(b'paths', name)
-            root_key = (name, loc, s)
+            root_key = (name, value, s)
             root = ui._path_to_root.get(root_key, home_path)
-            loc = os.path.expandvars(loc)
-            loc = os.path.expanduser(loc)
-            if not hasscheme(loc) and not os.path.isabs(loc):
-                loc = os.path.normpath(os.path.join(root, loc))
-            self[name] = [path(ui, name, rawloc=loc, suboptions=sub_opts)]
+
+            multi_url = sub_opts.get(b'multi-urls')
+            if multi_url is not None and stringutil.parsebool(multi_url):
+                base_locs = stringutil.parselist(value)
+            else:
+                base_locs = [value]
+
+            paths = []
+            for loc in base_locs:
+                loc = os.path.expandvars(loc)
+                loc = os.path.expanduser(loc)
+                if not hasscheme(loc) and not os.path.isabs(loc):
+                    loc = os.path.normpath(os.path.join(root, loc))
+                p = path(ui, name, rawloc=loc, suboptions=sub_opts)
+                paths.append(p)
+            self[name] = paths
 
         for name, old_paths in sorted(self.items()):
             new_paths = []
@@ -750,6 +765,17 @@
     return value
 
 
+@pathsuboption(b'multi-urls', b'multi_urls')
+def multiurls_pathoption(ui, path, value):
+    res = stringutil.parsebool(value)
+    if res is None:
+        ui.warn(
+            _(b'(paths.%s:multi-urls not a boolean; ignoring)\n') % path.name
+        )
+        res = False
+    return res
+
+
 def _chain_path(base_path, ui, paths):
     """return the result of "path://" logic applied on a given path"""
     new_paths = []
--- a/tests/test-exchange-multi-source.t	Thu Apr 15 20:13:29 2021 +0200
+++ b/tests/test-exchange-multi-source.t	Fri Apr 16 00:16:47 2021 +0200
@@ -611,3 +611,177 @@
   |
   %  A 0
   
+
+Testing multi-path definition
+----------------------------
+
+  $ hg clone main-repo repo-paths --rev 0
+  adding changesets
+  adding manifests
+  adding file changes
+  added 1 changesets with 1 changes to 1 files
+  new changesets 4a2df7238c3b
+  updating to branch default
+  1 files updated, 0 files merged, 0 files removed, 0 files unresolved
+  $ cp -R ./branch-E ./branch-E-paths
+  $ cp -R ./branch-G ./branch-G-paths
+  $ cp -R ./branch-H ./branch-H-paths
+  $ cat << EOF >> repo-paths/.hg/hgrc
+  > [paths]
+  > E=../branch-E-paths
+  > G=../branch-G-paths
+  > H=../branch-H-paths
+  > EHG=path://E,path://H,path://G
+  > EHG:multi-urls=yes
+  > GEH=path://G,path://E,path://H
+  > GEH:multi-urls=yes
+  > EOF
+
+Do various operations and verify that order matters
+
+  $ hg -R repo-paths push EHG --force
+  pushing to $TESTTMP/branch-E-paths
+  searching for changes
+  no changes found
+  pushing to $TESTTMP/branch-H-paths
+  searching for changes
+  no changes found
+  pushing to $TESTTMP/branch-G-paths
+  searching for changes
+  no changes found
+  [1]
+  $ hg -R repo-paths push GEH --force
+  pushing to $TESTTMP/branch-G-paths
+  searching for changes
+  no changes found
+  pushing to $TESTTMP/branch-E-paths
+  searching for changes
+  no changes found
+  pushing to $TESTTMP/branch-H-paths
+  searching for changes
+  no changes found
+  [1]
+  $ hg -R repo-paths push EHG GEH --force
+  pushing to $TESTTMP/branch-E-paths
+  searching for changes
+  no changes found
+  pushing to $TESTTMP/branch-H-paths
+  searching for changes
+  no changes found
+  pushing to $TESTTMP/branch-G-paths
+  searching for changes
+  no changes found
+  pushing to $TESTTMP/branch-G-paths
+  searching for changes
+  no changes found
+  pushing to $TESTTMP/branch-E-paths
+  searching for changes
+  no changes found
+  pushing to $TESTTMP/branch-H-paths
+  searching for changes
+  no changes found
+  [1]
+  $ hg -R repo-paths pull EHG
+  pulling from $TESTTMP/branch-E-paths
+  searching for changes
+  adding changesets
+  adding manifests
+  adding file changes
+  added 4 changesets with 4 changes to 4 files
+  new changesets 27547f69f254:a603bfb5a83e
+  (run 'hg update' to get a working copy)
+  pulling from $TESTTMP/branch-H-paths
+  searching for changes
+  adding changesets
+  adding manifests
+  adding file changes
+  added 1 changesets with 1 changes to 1 files (+1 heads)
+  new changesets 40faebb2ec45
+  (run 'hg heads' to see heads, 'hg merge' to merge)
+  pulling from $TESTTMP/branch-G-paths
+  searching for changes
+  adding changesets
+  adding manifests
+  adding file changes
+  added 2 changesets with 2 changes to 2 files (+1 heads)
+  new changesets 2f3a4c5c1417:c521a06b234b
+  (run 'hg heads .' to see heads, 'hg merge' to merge)
+  $ hg -R repo-paths pull GEH
+  pulling from $TESTTMP/branch-G-paths
+  searching for changes
+  no changes found
+  pulling from $TESTTMP/branch-E-paths
+  searching for changes
+  no changes found
+  pulling from $TESTTMP/branch-H-paths
+  searching for changes
+  no changes found
+  $ hg -R repo-paths pull EHG GEH
+  pulling from $TESTTMP/branch-E-paths
+  searching for changes
+  no changes found
+  pulling from $TESTTMP/branch-H-paths
+  searching for changes
+  no changes found
+  pulling from $TESTTMP/branch-G-paths
+  searching for changes
+  no changes found
+  pulling from $TESTTMP/branch-G-paths
+  searching for changes
+  no changes found
+  pulling from $TESTTMP/branch-E-paths
+  searching for changes
+  no changes found
+  pulling from $TESTTMP/branch-H-paths
+  searching for changes
+  no changes found
+  $ hg -R repo-paths push EHG --force
+  pushing to $TESTTMP/branch-E-paths
+  searching for changes
+  adding changesets
+  adding manifests
+  adding file changes
+  added 3 changesets with 3 changes to 3 files (+2 heads)
+  pushing to $TESTTMP/branch-H-paths
+  searching for changes
+  adding changesets
+  adding manifests
+  adding file changes
+  added 4 changesets with 4 changes to 4 files (+2 heads)
+  pushing to $TESTTMP/branch-G-paths
+  searching for changes
+  adding changesets
+  adding manifests
+  adding file changes
+  added 4 changesets with 4 changes to 4 files (+2 heads)
+  $ hg -R repo-paths push GEH --force
+  pushing to $TESTTMP/branch-G-paths
+  searching for changes
+  no changes found
+  pushing to $TESTTMP/branch-E-paths
+  searching for changes
+  no changes found
+  pushing to $TESTTMP/branch-H-paths
+  searching for changes
+  no changes found
+  [1]
+  $ hg -R repo-paths push EHG GEH --force
+  pushing to $TESTTMP/branch-E-paths
+  searching for changes
+  no changes found
+  pushing to $TESTTMP/branch-H-paths
+  searching for changes
+  no changes found
+  pushing to $TESTTMP/branch-G-paths
+  searching for changes
+  no changes found
+  pushing to $TESTTMP/branch-G-paths
+  searching for changes
+  no changes found
+  pushing to $TESTTMP/branch-E-paths
+  searching for changes
+  no changes found
+  pushing to $TESTTMP/branch-H-paths
+  searching for changes
+  no changes found
+  [1]
--- a/tests/test-paths.t	Thu Apr 15 20:13:29 2021 +0200
+++ b/tests/test-paths.t	Fri Apr 16 00:16:47 2021 +0200
@@ -388,3 +388,128 @@
   abort: cannot use `path://unknown`, "unknown" is not a known path
   [255]
 
+Test path pointing to multiple urls
+===================================
+
+Simple cases
+------------
+- one layer
+- one list
+- no special option
+
+  $ cat << EOF > .hg/hgrc
+  > [paths]
+  > one-path=foo
+  > multiple-path=foo,bar,baz,https://example.org/
+  > multiple-path:multi-urls=yes
+  > EOF
+  $ hg path
+  gpath1 = http://hg.example.com/
+  multiple-path = $TESTTMP/chained_path/foo
+  multiple-path:multi-urls = yes
+  multiple-path = $TESTTMP/chained_path/bar
+  multiple-path:multi-urls = yes
+  multiple-path = $TESTTMP/chained_path/baz
+  multiple-path:multi-urls = yes
+  multiple-path = https://example.org/
+  multiple-path:multi-urls = yes
+  one-path = $TESTTMP/chained_path/foo
+
+Reference to a list
+-------------------
+
+  $ cat << EOF >> .hg/hgrc
+  > ref-to-multi=path://multiple-path
+  > EOF
+  $ hg path | grep ref-to-multi
+  ref-to-multi = $TESTTMP/chained_path/foo
+  ref-to-multi:multi-urls = yes
+  ref-to-multi = $TESTTMP/chained_path/bar
+  ref-to-multi:multi-urls = yes
+  ref-to-multi = $TESTTMP/chained_path/baz
+  ref-to-multi:multi-urls = yes
+  ref-to-multi = https://example.org/
+  ref-to-multi:multi-urls = yes
+
+List with a reference
+---------------------
+
+  $ cat << EOF >> .hg/hgrc
+  > multi-with-ref=path://one-path, ssh://babar@savannah/celeste-ville
+  > multi-with-ref:multi-urls=yes
+  > EOF
+  $ hg path | grep multi-with-ref
+  multi-with-ref = $TESTTMP/chained_path/foo
+  multi-with-ref:multi-urls = yes
+  multi-with-ref = ssh://babar@savannah/celeste-ville
+  multi-with-ref:multi-urls = yes
+
+List with a reference to a list
+-------------------------------
+
+  $ cat << EOF >> .hg/hgrc
+  > multi-to-multi-ref = path://multiple-path, ssh://celeste@savannah/celeste-ville
+  > multi-to-multi-ref:multi-urls = yes
+  > EOF
+  $ hg path | grep multi-to-multi-ref
+  multi-to-multi-ref = $TESTTMP/chained_path/foo
+  multi-to-multi-ref:multi-urls = yes
+  multi-to-multi-ref = $TESTTMP/chained_path/bar
+  multi-to-multi-ref:multi-urls = yes
+  multi-to-multi-ref = $TESTTMP/chained_path/baz
+  multi-to-multi-ref:multi-urls = yes
+  multi-to-multi-ref = https://example.org/
+  multi-to-multi-ref:multi-urls = yes
+  multi-to-multi-ref = ssh://celeste@savannah/celeste-ville
+  multi-to-multi-ref:multi-urls = yes
+
+individual suboptions are inherited
+-----------------------------------
+
+  $ cat << EOF >> .hg/hgrc
+  > with-pushurl = foo
+  > with-pushurl:pushurl = http://foo.bar/
+  > with-pushrev = bar
+  > with-pushrev:pushrev = draft()
+  > with-both = toto
+  > with-both:pushurl = http://ta.ta
+  > with-both:pushrev = secret()
+  > ref-all-no-opts = path://with-pushurl, path://with-pushrev, path://with-both
+  > ref-all-no-opts:multi-urls = yes
+  > with-overwrite = path://with-pushurl, path://with-pushrev, path://with-both
+  > with-overwrite:multi-urls = yes
+  > with-overwrite:pushrev = public()
+  > EOF
+  $ hg path | grep with-pushurl
+  with-pushurl = $TESTTMP/chained_path/foo
+  with-pushurl:pushurl = http://foo.bar/
+  $ hg path | grep with-pushrev
+  with-pushrev = $TESTTMP/chained_path/bar
+  with-pushrev:pushrev = draft()
+  $ hg path | grep with-both
+  with-both = $TESTTMP/chained_path/toto
+  with-both:pushrev = secret()
+  with-both:pushurl = http://ta.ta/
+  $ hg path | grep ref-all-no-opts
+  ref-all-no-opts = $TESTTMP/chained_path/foo
+  ref-all-no-opts:multi-urls = yes
+  ref-all-no-opts:pushurl = http://foo.bar/
+  ref-all-no-opts = $TESTTMP/chained_path/bar
+  ref-all-no-opts:multi-urls = yes
+  ref-all-no-opts:pushrev = draft()
+  ref-all-no-opts = $TESTTMP/chained_path/toto
+  ref-all-no-opts:multi-urls = yes
+  ref-all-no-opts:pushrev = secret()
+  ref-all-no-opts:pushurl = http://ta.ta/
+  $ hg path | grep with-overwrite
+  with-overwrite = $TESTTMP/chained_path/foo
+  with-overwrite:multi-urls = yes
+  with-overwrite:pushrev = public()
+  with-overwrite:pushurl = http://foo.bar/
+  with-overwrite = $TESTTMP/chained_path/bar
+  with-overwrite:multi-urls = yes
+  with-overwrite:pushrev = public()
+  with-overwrite = $TESTTMP/chained_path/toto
+  with-overwrite:multi-urls = yes
+  with-overwrite:pushrev = public()
+  with-overwrite:pushurl = http://ta.ta/