mercurial/utils/urlutil.py
changeset 47198 7531cc34713c
parent 47197 26b3953ba1b0
child 47283 a671832a8e41
equal deleted inserted replaced
47197:26b3953ba1b0 47198:7531cc34713c
   445     return bytes(u)
   445     return bytes(u)
   446 
   446 
   447 
   447 
   448 def list_paths(ui, target_path=None):
   448 def list_paths(ui, target_path=None):
   449     """list all the (name, paths) in the passed ui"""
   449     """list all the (name, paths) in the passed ui"""
       
   450     result = []
   450     if target_path is None:
   451     if target_path is None:
   451         return sorted(pycompat.iteritems(ui.paths))
   452         for name, paths in sorted(pycompat.iteritems(ui.paths)):
       
   453             for p in paths:
       
   454                 result.append((name, p))
       
   455 
   452     else:
   456     else:
   453         path = ui.paths.get(target_path)
   457         for path in ui.paths.get(target_path, []):
   454         if path is None:
   458             result.append((target_path, path))
   455             return []
   459     return result
   456         else:
       
   457             return [(target_path, path)]
       
   458 
   460 
   459 
   461 
   460 def try_path(ui, url):
   462 def try_path(ui, url):
   461     """try to build a path from a url
   463     """try to build a path from a url
   462 
   464 
   471 
   473 
   472 def get_push_paths(repo, ui, dests):
   474 def get_push_paths(repo, ui, dests):
   473     """yields all the `path` selected as push destination by `dests`"""
   475     """yields all the `path` selected as push destination by `dests`"""
   474     if not dests:
   476     if not dests:
   475         if b'default-push' in ui.paths:
   477         if b'default-push' in ui.paths:
   476             yield ui.paths[b'default-push']
   478             for p in ui.paths[b'default-push']:
       
   479                 yield p
   477         elif b'default' in ui.paths:
   480         elif b'default' in ui.paths:
   478             yield ui.paths[b'default']
   481             for p in ui.paths[b'default']:
       
   482                 yield p
   479         else:
   483         else:
   480             raise error.ConfigError(
   484             raise error.ConfigError(
   481                 _(b'default repository not configured!'),
   485                 _(b'default repository not configured!'),
   482                 hint=_(b"see 'hg help config.paths'"),
   486                 hint=_(b"see 'hg help config.paths'"),
   483             )
   487             )
   484     else:
   488     else:
   485         for dest in dests:
   489         for dest in dests:
   486             if dest in ui.paths:
   490             if dest in ui.paths:
   487                 yield ui.paths[dest]
   491                 for p in ui.paths[dest]:
       
   492                     yield p
   488             else:
   493             else:
   489                 path = try_path(ui, dest)
   494                 path = try_path(ui, dest)
   490                 if path is None:
   495                 if path is None:
   491                     msg = _(b'repository %s does not exist')
   496                     msg = _(b'repository %s does not exist')
   492                     msg %= dest
   497                     msg %= dest
   498     """yields all the `(path, branch)` selected as pull source by `sources`"""
   503     """yields all the `(path, branch)` selected as pull source by `sources`"""
   499     if not sources:
   504     if not sources:
   500         sources = [b'default']
   505         sources = [b'default']
   501     for source in sources:
   506     for source in sources:
   502         if source in ui.paths:
   507         if source in ui.paths:
   503             url = ui.paths[source].rawloc
   508             for p in ui.paths[source]:
       
   509                 yield parseurl(p.rawloc, default_branches)
   504         else:
   510         else:
   505             # Try to resolve as a local path or URI.
   511             # Try to resolve as a local path or URI.
   506             path = try_path(ui, source)
   512             path = try_path(ui, source)
   507             if path is not None:
   513             if path is not None:
   508                 url = path.rawloc
   514                 url = path.rawloc
   509             else:
   515             else:
   510                 url = source
   516                 url = source
   511         yield parseurl(url, default_branches)
   517             yield parseurl(url, default_branches)
   512 
   518 
   513 
   519 
   514 def get_unique_push_path(action, repo, ui, dest=None):
   520 def get_unique_push_path(action, repo, ui, dest=None):
   515     """return a unique `path` or abort if multiple are found
   521     """return a unique `path` or abort if multiple are found
   516 
   522 
   524     if dest is None:
   530     if dest is None:
   525         dests = []
   531         dests = []
   526     else:
   532     else:
   527         dests = [dest]
   533         dests = [dest]
   528     dests = list(get_push_paths(repo, ui, dests))
   534     dests = list(get_push_paths(repo, ui, dests))
   529     assert len(dests) == 1
   535     if len(dests) != 1:
       
   536         if dest is None:
       
   537             msg = _("default path points to %d urls while %s only supports one")
       
   538             msg %= (len(dests), action)
       
   539         else:
       
   540             msg = _("path points to %d urls while %s only supports one: %s")
       
   541             msg %= (len(dests), action, dest)
       
   542         raise error.Abort(msg)
   530     return dests[0]
   543     return dests[0]
   531 
   544 
   532 
   545 
   533 def get_unique_pull_path(action, repo, ui, source=None, default_branches=()):
   546 def get_unique_pull_path(action, repo, ui, source=None, default_branches=()):
   534     """return a unique `(path, branch)` or abort if multiple are found
   547     """return a unique `(path, branch)` or abort if multiple are found
   538 
   551 
   539     Note that for now, we cannot get multiple destination so this function is "trivial".
   552     Note that for now, we cannot get multiple destination so this function is "trivial".
   540 
   553 
   541     The `action` parameter will be used for the error message.
   554     The `action` parameter will be used for the error message.
   542     """
   555     """
       
   556     urls = []
   543     if source is None:
   557     if source is None:
   544         if b'default' in ui.paths:
   558         if b'default' in ui.paths:
   545             url = ui.paths[b'default'].rawloc
   559             urls.extend(p.rawloc for p in ui.paths[b'default'])
   546         else:
   560         else:
   547             # XXX this is the historical default behavior, but that is not
   561             # XXX this is the historical default behavior, but that is not
   548             # great, consider breaking BC on this.
   562             # great, consider breaking BC on this.
   549             url = b'default'
   563             urls.append(b'default')
   550     else:
   564     else:
   551         if source in ui.paths:
   565         if source in ui.paths:
   552             url = ui.paths[source].rawloc
   566             urls.extend(p.rawloc for p in ui.paths[source])
   553         else:
   567         else:
   554             # Try to resolve as a local path or URI.
   568             # Try to resolve as a local path or URI.
   555             path = try_path(ui, source)
   569             path = try_path(ui, source)
   556             if path is not None:
   570             if path is not None:
   557                 url = path.rawloc
   571                 urls.append(path.rawloc)
   558             else:
   572             else:
   559                 url = source
   573                 urls.append(source)
   560     return parseurl(url, default_branches)
   574     if len(urls) != 1:
       
   575         if source is None:
       
   576             msg = _("default path points to %d urls while %s only supports one")
       
   577             msg %= (len(urls), action)
       
   578         else:
       
   579             msg = _("path points to %d urls while %s only supports one: %s")
       
   580             msg %= (len(urls), action, source)
       
   581         raise error.Abort(msg)
       
   582     return parseurl(urls[0], default_branches)
   561 
   583 
   562 
   584 
   563 def get_clone_path(ui, source, default_branches=()):
   585 def get_clone_path(ui, source, default_branches=()):
   564     """return the `(origsource, path, branch)` selected as clone source"""
   586     """return the `(origsource, path, branch)` selected as clone source"""
       
   587     urls = []
   565     if source is None:
   588     if source is None:
   566         if b'default' in ui.paths:
   589         if b'default' in ui.paths:
   567             url = ui.paths[b'default'].rawloc
   590             urls.extend(p.rawloc for p in ui.paths[b'default'])
   568         else:
   591         else:
   569             # XXX this is the historical default behavior, but that is not
   592             # XXX this is the historical default behavior, but that is not
   570             # great, consider breaking BC on this.
   593             # great, consider breaking BC on this.
   571             url = b'default'
   594             urls.append(b'default')
   572     else:
   595     else:
   573         if source in ui.paths:
   596         if source in ui.paths:
   574             url = ui.paths[source].rawloc
   597             urls.extend(p.rawloc for p in ui.paths[source])
   575         else:
   598         else:
   576             # Try to resolve as a local path or URI.
   599             # Try to resolve as a local path or URI.
   577             path = try_path(ui, source)
   600             path = try_path(ui, source)
   578             if path is not None:
   601             if path is not None:
   579                 url = path.rawloc
   602                 urls.append(path.rawloc)
   580             else:
   603             else:
   581                 url = source
   604                 urls.append(source)
       
   605     if len(urls) != 1:
       
   606         if source is None:
       
   607             msg = _(
       
   608                 "default path points to %d urls while only one is supported"
       
   609             )
       
   610             msg %= len(urls)
       
   611         else:
       
   612             msg = _("path points to %d urls while only one is supported: %s")
       
   613             msg %= (len(urls), source)
       
   614         raise error.Abort(msg)
       
   615     url = urls[0]
   582     clone_path, branch = parseurl(url, default_branches)
   616     clone_path, branch = parseurl(url, default_branches)
   583     return url, clone_path, branch
   617     return url, clone_path, branch
   584 
   618 
   585 
   619 
   586 def parseurl(path, branches=None):
   620 def parseurl(path, branches=None):
   606         for name, loc in ui.configitems(b'paths', ignoresub=True):
   640         for name, loc in ui.configitems(b'paths', ignoresub=True):
   607             # No location is the same as not existing.
   641             # No location is the same as not existing.
   608             if not loc:
   642             if not loc:
   609                 continue
   643                 continue
   610             loc, sub_opts = ui.configsuboptions(b'paths', name)
   644             loc, sub_opts = ui.configsuboptions(b'paths', name)
   611             self[name] = path(ui, name, rawloc=loc, suboptions=sub_opts)
   645             self[name] = [path(ui, name, rawloc=loc, suboptions=sub_opts)]
   612 
   646 
   613         for name, p in sorted(self.items()):
   647         for name, old_paths in sorted(self.items()):
   614             self[name] = _chain_path(p, ui, self)
   648             new_paths = []
       
   649             for p in old_paths:
       
   650                 new_paths.extend(_chain_path(p, ui, self))
       
   651             self[name] = new_paths
   615 
   652 
   616     def getpath(self, ui, name, default=None):
   653     def getpath(self, ui, name, default=None):
   617         """Return a ``path`` from a string, falling back to default.
   654         """Return a ``path`` from a string, falling back to default.
   618 
   655 
   619         ``name`` can be a named path or locations. Locations are filesystem
   656         ``name`` can be a named path or locations. Locations are filesystem
   630                 default = ()
   667                 default = ()
   631             elif not isinstance(default, (tuple, list)):
   668             elif not isinstance(default, (tuple, list)):
   632                 default = (default,)
   669                 default = (default,)
   633             for k in default:
   670             for k in default:
   634                 try:
   671                 try:
   635                     return self[k]
   672                     return self[k][0]
   636                 except KeyError:
   673                 except KeyError:
   637                     continue
   674                     continue
   638             return None
   675             return None
   639 
   676 
   640         # Most likely empty string.
   677         # Most likely empty string.
   641         # This may need to raise in the future.
   678         # This may need to raise in the future.
   642         if not name:
   679         if not name:
   643             return None
   680             return None
   644         if name in self:
   681         if name in self:
   645             return self[name]
   682             return self[name][0]
   646         else:
   683         else:
   647             # Try to resolve as a local path or URI.
   684             # Try to resolve as a local path or URI.
   648             path = try_path(ui, name)
   685             path = try_path(ui, name)
   649             if path is None:
   686             if path is None:
   650                 raise error.RepoError(_(b'repository %s does not exist') % name)
   687                 raise error.RepoError(_(b'repository %s does not exist') % name)
   702 @pathsuboption(b'pushrev', b'pushrev')
   739 @pathsuboption(b'pushrev', b'pushrev')
   703 def pushrevpathoption(ui, path, value):
   740 def pushrevpathoption(ui, path, value):
   704     return value
   741     return value
   705 
   742 
   706 
   743 
   707 def _chain_path(path, ui, paths):
   744 def _chain_path(base_path, ui, paths):
   708     """return the result of "path://" logic applied on a given path"""
   745     """return the result of "path://" logic applied on a given path"""
   709     if path.url.scheme == b'path':
   746     new_paths = []
   710         assert path.url.path is None
   747     if base_path.url.scheme != b'path':
   711         subpath = paths.get(path.url.host)
   748         new_paths.append(base_path)
   712         if subpath is None:
   749     else:
       
   750         assert base_path.url.path is None
       
   751         sub_paths = paths.get(base_path.url.host)
       
   752         if sub_paths is None:
   713             m = _(b'cannot use `%s`, "%s" is not a known path')
   753             m = _(b'cannot use `%s`, "%s" is not a known path')
   714             m %= (path.rawloc, path.url.host)
   754             m %= (base_path.rawloc, base_path.url.host)
   715             raise error.Abort(m)
   755             raise error.Abort(m)
   716         if subpath.raw_url.scheme == b'path':
   756         for subpath in sub_paths:
   717             m = _(b'cannot use `%s`, "%s" is also defined as a `path://`')
   757             path = base_path.copy()
   718             m %= (path.rawloc, path.url.host)
   758             if subpath.raw_url.scheme == b'path':
   719             raise error.Abort(m)
   759                 m = _(b'cannot use `%s`, "%s" is also defined as a `path://`')
   720         path.url = subpath.url
   760                 m %= (path.rawloc, path.url.host)
   721         path.rawloc = subpath.rawloc
   761                 raise error.Abort(m)
   722         path.loc = subpath.loc
   762             path.url = subpath.url
   723         if path.branch is None:
   763             path.rawloc = subpath.rawloc
   724             path.branch = subpath.branch
   764             path.loc = subpath.loc
   725         else:
   765             if path.branch is None:
   726             base = path.rawloc.rsplit(b'#', 1)[0]
   766                 path.branch = subpath.branch
   727             path.rawloc = b'%s#%s' % (base, path.branch)
   767             else:
   728         suboptions = subpath._all_sub_opts.copy()
   768                 base = path.rawloc.rsplit(b'#', 1)[0]
   729         suboptions.update(path._own_sub_opts)
   769                 path.rawloc = b'%s#%s' % (base, path.branch)
   730         path._apply_suboptions(ui, suboptions)
   770             suboptions = subpath._all_sub_opts.copy()
   731     return path
   771             suboptions.update(path._own_sub_opts)
       
   772             path._apply_suboptions(ui, suboptions)
       
   773             new_paths.append(path)
       
   774     return new_paths
   732 
   775 
   733 
   776 
   734 class path(object):
   777 class path(object):
   735     """Represents an individual path and its configuration."""
   778     """Represents an individual path and its configuration."""
   736 
   779