Patchwork [5,of,7,path-configs] ui.paths: obtain paths from [path x] sections

login
register
mail settings
Submitter Gregory Szorc
Date Feb. 8, 2015, 1:13 a.m.
Message ID <8070ddf4999636a5bb52.1423358021@vm-ubuntu-main.gateway.sonic.net>
Download mbox | patch
Permalink /patch/7766/
State Changes Requested
Headers show

Comments

Gregory Szorc - Feb. 8, 2015, 1:13 a.m.
# HG changeset patch
# User Gregory Szorc <gregory.szorc@gmail.com>
# Date 1423346419 28800
#      Sat Feb 07 14:00:19 2015 -0800
# Node ID 8070ddf4999636a5bb52ffa6bfaca69299d86006
# Parent  e93fb90e9b87879402147a2fdff2a937087639ca
ui.paths: obtain paths from [path x] sections

More advanced Mercurial users have long wanted the ability to change
the behavior of individual paths. One common request is the ability
to change the default behavior for push and pull for different paths.
e.g. you may want to default to only pushing a single head for
your "production" path but you may still want to use the push
everything default for your personal remote. Another requested
feature is the ability to define different URLs for different
operations (e.g. pushes should go through ssh:// but pulls should
utilize https://). And the list goes on.

This patch teaches the paths system how to obtain path configurations
from separate [path x] config file sections. We currently only
support defining a simple URL for each path. This puts us on
feature parity with [paths]. Future patches will teach the paths
system about new configuration options, so more advanced can define
the behavior they want.
Gregory Szorc - Feb. 8, 2015, 1:28 a.m.
This patch is the critical one in the series. I have some work building on
top of this to enable support for different URLs for different operations
(namely a separate "push URL") and support for a default revset to be used
with push. I'd like to see this approach of using separate config sections
for defining separate path configs signed off before I sink more time into
the features building on top of it.

On Sat, Feb 7, 2015 at 5:13 PM, Gregory Szorc <gregory.szorc@gmail.com>
wrote:

> # HG changeset patch
> # User Gregory Szorc <gregory.szorc@gmail.com>
> # Date 1423346419 28800
> #      Sat Feb 07 14:00:19 2015 -0800
> # Node ID 8070ddf4999636a5bb52ffa6bfaca69299d86006
> # Parent  e93fb90e9b87879402147a2fdff2a937087639ca
> ui.paths: obtain paths from [path x] sections
>
> More advanced Mercurial users have long wanted the ability to change
> the behavior of individual paths. One common request is the ability
> to change the default behavior for push and pull for different paths.
> e.g. you may want to default to only pushing a single head for
> your "production" path but you may still want to use the push
> everything default for your personal remote. Another requested
> feature is the ability to define different URLs for different
> operations (e.g. pushes should go through ssh:// but pulls should
> utilize https://). And the list goes on.
>
> This patch teaches the paths system how to obtain path configurations
> from separate [path x] config file sections. We currently only
> support defining a simple URL for each path. This puts us on
> feature parity with [paths]. Future patches will teach the paths
> system about new configuration options, so more advanced can define
> the behavior they want.
>
> diff --git a/mercurial/help/urls.txt b/mercurial/help/urls.txt
> --- a/mercurial/help/urls.txt
> +++ b/mercurial/help/urls.txt
> @@ -40,10 +40,19 @@ Some notes about using SSH with Mercuria
>
>    Alternatively specify "ssh -C" as your ssh command in your
>    configuration file or with the --ssh command line option.
>
> -These URLs can all be stored in your configuration file with path
> -aliases under the [paths] section like so::
> +Defining Paths in Config Files
> +==============================
> +
> +These URLs can all be stored in your configuration file. This is
> +both a simple configuration and an advanced configuration.
> +
> +Simple Configuration
> +--------------------
> +
> +For simple configurations, store the path aliases under the [paths]
> +section like so::
>
>    [paths]
>    alias1 = URL1
>    alias2 = URL2
> @@ -51,8 +60,12 @@ aliases under the [paths] section like s
>
>  You can then use the alias for any command that uses a URL (for
>  example :hg:`pull alias1` will be treated as :hg:`pull URL1`).
>
> +The behavior of paths registered via [paths] will be consistent.
> +For more advanced options or to change the behavior of single paths,
> +see the "Advanced Configuration" section below.
> +
>  Two path aliases are special because they are used as defaults when
>  you do not provide the URL to a command:
>
>  default:
> @@ -63,4 +76,22 @@ default:
>
>  default-push:
>    The push command will look for a path named 'default-push', and
>    prefer it over 'default' if both are defined.
> +
> +Advanced Configuration
> +----------------------
> +
> +It is possible to have different settings for different paths by creating
> +a separate config section for that path. To do this, create sections
> +named ``[path <name>]`` with settings. Like so::
> +
> +  [path alias1]
> +  url = URL1
> +
> +  [path alias2]
> +  url = URL2
> +
> +The following configuration keys are recognized:
> +
> +url
> +  The URL this path should expand to.
> diff --git a/mercurial/ui.py b/mercurial/ui.py
> --- a/mercurial/ui.py
> +++ b/mercurial/ui.py
> @@ -976,13 +976,32 @@ class paths(object):
>          return self._uiref()
>
>      def __iter__(self):
>          """Iterate all available paths, as ``path`` instances."""
> +        r = {}
> +
> +        for name, conf in self.ui.sections(group='path').iteritems():
> +            if not conf:
> +                self.ui.warn(_('empty config section: [path %s]\n') %
> name)
> +                continue
> +
> +            if 'url' not in conf:
> +                self.ui.warn(_('url not defined in path: [path %s]\n') %
> name)
> +                continue
> +
> +            r[name] = path(name, url=conf['url'])
> +
>          for name, loc in self.ui.configitems('paths'):
>              # No URL is the same as not existing.
>              if not loc:
>                  continue
> -            yield path(name, url=loc)
> +
> +            if name in r:
> +                self.ui.warn(_('path defined multiple times: %s\n') %
> name)
> +            r[name] = path(name, url=loc)
> +
> +        for name, p in sorted(r.items()):
> +            yield p
>
>      def getpath(self, name, default=None):
>          """Return a ``path`` for the specified name.
>
> diff --git a/tests/test-paths.t b/tests/test-paths.t
> --- a/tests/test-paths.t
> +++ b/tests/test-paths.t
> @@ -41,8 +41,24 @@
>    not found!
>    [1]
>    $ hg paths -q unknown
>    [1]
> +
> +Paths coming from [path x] sections are found
> +
> +  $ cat > .hg/hgrc << EOF
> +  > [path p1]
> +  > url = http://hg.example.com/p1
> +  > [path "p2"]
> +  > url = http://hg.example.com/p2
> +  > [paths]
> +  > p3 = http://hg.example.com/p3
> +  > EOF
> +  $ hg paths
> +  p1 = http://hg.example.com/p1
> +  p2 = http://hg.example.com/p2
> +  p3 = http://hg.example.com/p3
> +
>    $ cd ..
>
>  'file:' disables [paths] entries for clone destination
>
>
Sean Farley - Feb. 8, 2015, 9:34 p.m.
Gregory Szorc writes:

> # HG changeset patch
> # User Gregory Szorc <gregory.szorc@gmail.com>
> # Date 1423346419 28800
> #      Sat Feb 07 14:00:19 2015 -0800
> # Node ID 8070ddf4999636a5bb52ffa6bfaca69299d86006
> # Parent  e93fb90e9b87879402147a2fdff2a937087639ca
> ui.paths: obtain paths from [path x] sections
>
> More advanced Mercurial users have long wanted the ability to change
> the behavior of individual paths. One common request is the ability
> to change the default behavior for push and pull for different paths.
> e.g. you may want to default to only pushing a single head for
> your "production" path but you may still want to use the push
> everything default for your personal remote. Another requested
> feature is the ability to define different URLs for different
> operations (e.g. pushes should go through ssh:// but pulls should
> utilize https://). And the list goes on.

This is great. I could see this dove-tailing very well with remote
bookmarks.
Augie Fackler - Feb. 9, 2015, 11:07 p.m.
On Sat, Feb 07, 2015 at 05:13:41PM -0800, Gregory Szorc wrote:
> # HG changeset patch
> # User Gregory Szorc <gregory.szorc@gmail.com>
> # Date 1423346419 28800
> #      Sat Feb 07 14:00:19 2015 -0800
> # Node ID 8070ddf4999636a5bb52ffa6bfaca69299d86006
> # Parent  e93fb90e9b87879402147a2fdff2a937087639ca
> ui.paths: obtain paths from [path x] sections

I'm not remembering any existing cases where we have pseudo-dynamic
sections like this. It'd be more like merge-tools and auth to do something like

[paths]
default = foo
default.pushurl = bar
default.ponies = no

(etc)

I'm not sure how you feel about that. Heck, I'm not sure how I feel
about it. One advantage is that it grows somewhat naturally from the
existing paths section.

Overall, I like the idea. I definitely think something like this would
be handy, but we'll want to be careful about the overall UI
complexity.

>
> More advanced Mercurial users have long wanted the ability to change
> the behavior of individual paths. One common request is the ability
> to change the default behavior for push and pull for different paths.
> e.g. you may want to default to only pushing a single head for
> your "production" path but you may still want to use the push
> everything default for your personal remote. Another requested
> feature is the ability to define different URLs for different
> operations (e.g. pushes should go through ssh:// but pulls should
> utilize https://). And the list goes on.
>
> This patch teaches the paths system how to obtain path configurations
> from separate [path x] config file sections. We currently only
> support defining a simple URL for each path. This puts us on
> feature parity with [paths]. Future patches will teach the paths
> system about new configuration options, so more advanced can define
> the behavior they want.
>
> diff --git a/mercurial/help/urls.txt b/mercurial/help/urls.txt
> --- a/mercurial/help/urls.txt
> +++ b/mercurial/help/urls.txt
> @@ -40,10 +40,19 @@ Some notes about using SSH with Mercuria
>
>    Alternatively specify "ssh -C" as your ssh command in your
>    configuration file or with the --ssh command line option.
>
> -These URLs can all be stored in your configuration file with path
> -aliases under the [paths] section like so::
> +Defining Paths in Config Files
> +==============================
> +
> +These URLs can all be stored in your configuration file. This is
> +both a simple configuration and an advanced configuration.
> +
> +Simple Configuration
> +--------------------
> +
> +For simple configurations, store the path aliases under the [paths]
> +section like so::
>
>    [paths]
>    alias1 = URL1
>    alias2 = URL2
> @@ -51,8 +60,12 @@ aliases under the [paths] section like s
>
>  You can then use the alias for any command that uses a URL (for
>  example :hg:`pull alias1` will be treated as :hg:`pull URL1`).
>
> +The behavior of paths registered via [paths] will be consistent.
> +For more advanced options or to change the behavior of single paths,
> +see the "Advanced Configuration" section below.
> +
>  Two path aliases are special because they are used as defaults when
>  you do not provide the URL to a command:
>
>  default:
> @@ -63,4 +76,22 @@ default:
>
>  default-push:
>    The push command will look for a path named 'default-push', and
>    prefer it over 'default' if both are defined.
> +
> +Advanced Configuration
> +----------------------
> +
> +It is possible to have different settings for different paths by creating
> +a separate config section for that path. To do this, create sections
> +named ``[path <name>]`` with settings. Like so::
> +
> +  [path alias1]
> +  url = URL1
> +
> +  [path alias2]
> +  url = URL2
> +
> +The following configuration keys are recognized:
> +
> +url
> +  The URL this path should expand to.
> diff --git a/mercurial/ui.py b/mercurial/ui.py
> --- a/mercurial/ui.py
> +++ b/mercurial/ui.py
> @@ -976,13 +976,32 @@ class paths(object):
>          return self._uiref()
>
>      def __iter__(self):
>          """Iterate all available paths, as ``path`` instances."""
> +        r = {}
> +
> +        for name, conf in self.ui.sections(group='path').iteritems():
> +            if not conf:
> +                self.ui.warn(_('empty config section: [path %s]\n') % name)
> +                continue
> +
> +            if 'url' not in conf:
> +                self.ui.warn(_('url not defined in path: [path %s]\n') % name)
> +                continue
> +
> +            r[name] = path(name, url=conf['url'])
> +
>          for name, loc in self.ui.configitems('paths'):
>              # No URL is the same as not existing.
>              if not loc:
>                  continue
> -            yield path(name, url=loc)
> +
> +            if name in r:
> +                self.ui.warn(_('path defined multiple times: %s\n') % name)
> +            r[name] = path(name, url=loc)
> +
> +        for name, p in sorted(r.items()):
> +            yield p
>
>      def getpath(self, name, default=None):
>          """Return a ``path`` for the specified name.
>
> diff --git a/tests/test-paths.t b/tests/test-paths.t
> --- a/tests/test-paths.t
> +++ b/tests/test-paths.t
> @@ -41,8 +41,24 @@
>    not found!
>    [1]
>    $ hg paths -q unknown
>    [1]
> +
> +Paths coming from [path x] sections are found
> +
> +  $ cat > .hg/hgrc << EOF
> +  > [path p1]
> +  > url = http://hg.example.com/p1
> +  > [path "p2"]
> +  > url = http://hg.example.com/p2
> +  > [paths]
> +  > p3 = http://hg.example.com/p3
> +  > EOF
> +  $ hg paths
> +  p1 = http://hg.example.com/p1
> +  p2 = http://hg.example.com/p2
> +  p3 = http://hg.example.com/p3
> +
>    $ cd ..
>
>  'file:' disables [paths] entries for clone destination
>
> _______________________________________________
> Mercurial-devel mailing list
> Mercurial-devel@selenic.com
> http://selenic.com/mailman/listinfo/mercurial-devel
Gregory Szorc - Feb. 9, 2015, 11:14 p.m.
On Mon, Feb 9, 2015 at 3:07 PM, Augie Fackler <raf@durin42.com> wrote:

> On Sat, Feb 07, 2015 at 05:13:41PM -0800, Gregory Szorc wrote:
> > # HG changeset patch
> > # User Gregory Szorc <gregory.szorc@gmail.com>
> > # Date 1423346419 28800
> > #      Sat Feb 07 14:00:19 2015 -0800
> > # Node ID 8070ddf4999636a5bb52ffa6bfaca69299d86006
> > # Parent  e93fb90e9b87879402147a2fdff2a937087639ca
> > ui.paths: obtain paths from [path x] sections
>
> I'm not remembering any existing cases where we have pseudo-dynamic
> sections like this. It'd be more like merge-tools and auth to do something
> like
>
> [paths]
> default = foo
> default.pushurl = bar
> default.ponies = no
>
> (etc)
>
> I'm not sure how you feel about that. Heck, I'm not sure how I feel
> about it. One advantage is that it grows somewhat naturally from the
> existing paths section.
>
> Overall, I like the idea. I definitely think something like this would
> be handy, but we'll want to be careful about the overall UI
> complexity.
>

I view this particular difference as cosmetic only and thus I have no
strong adversarial opinion. Actually, I think this approach is arguably
better because you can override things via --config (defining spaces inside
--config is a bit awkward). The flip side is that separate sections are
likely easier to read and maintain, since [section] is visually easier for
humans to parse, IMO.

I'll be happy to paint this bikeshed whatever color is needed to see
path-specific configs land in core.

Patch

diff --git a/mercurial/help/urls.txt b/mercurial/help/urls.txt
--- a/mercurial/help/urls.txt
+++ b/mercurial/help/urls.txt
@@ -40,10 +40,19 @@  Some notes about using SSH with Mercuria
 
   Alternatively specify "ssh -C" as your ssh command in your
   configuration file or with the --ssh command line option.
 
-These URLs can all be stored in your configuration file with path
-aliases under the [paths] section like so::
+Defining Paths in Config Files
+==============================
+
+These URLs can all be stored in your configuration file. This is
+both a simple configuration and an advanced configuration.
+
+Simple Configuration
+--------------------
+
+For simple configurations, store the path aliases under the [paths]
+section like so::
 
   [paths]
   alias1 = URL1
   alias2 = URL2
@@ -51,8 +60,12 @@  aliases under the [paths] section like s
 
 You can then use the alias for any command that uses a URL (for
 example :hg:`pull alias1` will be treated as :hg:`pull URL1`).
 
+The behavior of paths registered via [paths] will be consistent.
+For more advanced options or to change the behavior of single paths,
+see the "Advanced Configuration" section below.
+
 Two path aliases are special because they are used as defaults when
 you do not provide the URL to a command:
 
 default:
@@ -63,4 +76,22 @@  default:
 
 default-push:
   The push command will look for a path named 'default-push', and
   prefer it over 'default' if both are defined.
+
+Advanced Configuration
+----------------------
+
+It is possible to have different settings for different paths by creating
+a separate config section for that path. To do this, create sections
+named ``[path <name>]`` with settings. Like so::
+
+  [path alias1]
+  url = URL1
+
+  [path alias2]
+  url = URL2
+
+The following configuration keys are recognized:
+
+url
+  The URL this path should expand to.
diff --git a/mercurial/ui.py b/mercurial/ui.py
--- a/mercurial/ui.py
+++ b/mercurial/ui.py
@@ -976,13 +976,32 @@  class paths(object):
         return self._uiref()
 
     def __iter__(self):
         """Iterate all available paths, as ``path`` instances."""
+        r = {}
+
+        for name, conf in self.ui.sections(group='path').iteritems():
+            if not conf:
+                self.ui.warn(_('empty config section: [path %s]\n') % name)
+                continue
+
+            if 'url' not in conf:
+                self.ui.warn(_('url not defined in path: [path %s]\n') % name)
+                continue
+
+            r[name] = path(name, url=conf['url'])
+
         for name, loc in self.ui.configitems('paths'):
             # No URL is the same as not existing.
             if not loc:
                 continue
-            yield path(name, url=loc)
+
+            if name in r:
+                self.ui.warn(_('path defined multiple times: %s\n') % name)
+            r[name] = path(name, url=loc)
+
+        for name, p in sorted(r.items()):
+            yield p
 
     def getpath(self, name, default=None):
         """Return a ``path`` for the specified name.
 
diff --git a/tests/test-paths.t b/tests/test-paths.t
--- a/tests/test-paths.t
+++ b/tests/test-paths.t
@@ -41,8 +41,24 @@ 
   not found!
   [1]
   $ hg paths -q unknown
   [1]
+
+Paths coming from [path x] sections are found
+
+  $ cat > .hg/hgrc << EOF
+  > [path p1]
+  > url = http://hg.example.com/p1
+  > [path "p2"]
+  > url = http://hg.example.com/p2
+  > [paths]
+  > p3 = http://hg.example.com/p3
+  > EOF
+  $ hg paths
+  p1 = http://hg.example.com/p1
+  p2 = http://hg.example.com/p2
+  p3 = http://hg.example.com/p3
+
   $ cd ..
 
 'file:' disables [paths] entries for clone destination