Patchwork [2,of,4,RFC,path,options] ui.paths: add a "pushurl" path option

login
register
mail settings
Submitter Gregory Szorc
Date March 1, 2015, 11:23 p.m.
Message ID <d5f40bb2b253311c24a4.1425252226@vm-ubuntu-main.gateway.sonic.net>
Download mbox | patch
Permalink /patch/7872/
State Deferred
Headers show

Comments

Gregory Szorc - March 1, 2015, 11:23 p.m.
# HG changeset patch
# User Gregory Szorc <gregory.szorc@gmail.com>
# Date 1423519354 28800
#      Mon Feb 09 14:02:34 2015 -0800
# Node ID d5f40bb2b253311c24a4a1c9dd26b4893fb8a74c
# Parent  7fc8f6c277f60658bfbceb0bb8c24aad07f0845f
ui.paths: add a "pushurl" path option

Individual paths can now define a "pushurl" option. This option,
if present, will be used by "hg push" instead of the regular URL.

If this option is not defined, the normal URL is utilized by pushing.
This patch is therefore fully backwards compatible.

This feature is useful for deployments that have separate read-write
URLs from read-only URLs. For example, Mozilla utilizes
unauthenticated, read-only https:// URLs and authenticated, read-write
ssh:// URLs. Without this feature or the employ of extensions, a
separate named path must be defined to distinguish between these
endpoints. This works. However, it confuses people and other
extensions (such as remotenames) that map state back to names. The
separate URLs are logically identical, so being able to map them to
the same named entity makes sense.

Patch

diff --git a/mercurial/commands.py b/mercurial/commands.py
--- a/mercurial/commands.py
+++ b/mercurial/commands.py
@@ -5084,9 +5084,9 @@  def push(ui, repo, dest=None, **opts):
                 # this lets simultaneous -r, -b options continue working
                 opts.setdefault('rev', []).append("null")
 
     path = ui.paths.getpath(dest, default='push', require=True)
-    url = path.url
+    url = path.pushurl
     branches = [path.rev, opts.get('branch', [])]
     ui.status(_('pushing to %s\n') % util.hidepassword(url))
     revs, checkout = hg.addbranchrevs(repo, repo, branches, opts.get('rev'))
     other = hg.peer(repo, opts, url)
diff --git a/mercurial/help/config.txt b/mercurial/help/config.txt
--- a/mercurial/help/config.txt
+++ b/mercurial/help/config.txt
@@ -1091,8 +1091,18 @@  used from the command line. Example::
 To push to the path defined in ``my_path`` run the command::
 
     hg push my_path
 
+It is possible to define per-path options. Per-path options have the form
+``path.option = value``. e.g. ``default.pushurl = ssh://example.com/repo``.
+
+The following per-path options are recognized:
+
+``pushurl``
+    The URL to be used with :hg:`push` operations. If not defined, the
+    location assigned to the main path option will be used.
+
+    Note: ``#revision`` fragments are not allowed in ``purlurl``.
 
 ``phases``
 ----------
 
diff --git a/mercurial/ui.py b/mercurial/ui.py
--- a/mercurial/ui.py
+++ b/mercurial/ui.py
@@ -946,9 +946,11 @@  class paths(object):
         return self._uiref()
 
     def __iter__(self):
         """Iterate all available paths, as ``path`` instances."""
-        for name, loc in self.ui.configitems('paths'):
+        ui = self.ui
+
+        for name, loc in ui.configitems('paths'):
             # No URL is the same as not existing.
             if not loc:
                 continue
 
@@ -956,9 +958,10 @@  class paths(object):
             # per-path attributes.
             if '.' in name:
                 continue
 
-            yield path(name, url=loc)
+            yield path(name, url=loc,
+                       pushurl=ui.config('paths', '%s.pushurl' % name))
 
     def __getitem__(self, key):
         for path in self:
             if path.name == key:
@@ -1019,9 +1022,9 @@  class paths(object):
 
 class path(object):
     """Represents an individual path and its configuration."""
 
-    def __init__(self, name, url=None, local=None):
+    def __init__(self, name, url=None, local=None, pushurl=None):
         """Construct a path from its config options.
 
         The primary URL for the path is defined as either a URL via ``url``
         (preferred) or from a local, relative filesystem path (``local``).
@@ -1037,4 +1040,10 @@  class path(object):
         url, revs = hg.parseurl(url)
 
         self.url = url
         self.rev = revs[0]
+
+        if pushurl and '#' in pushurl:
+            raise util.Abort(_('pushurl may not have #revision fragment: %s') %
+                               pushurl)
+
+        self.pushurl = pushurl or url
diff --git a/tests/test-push-path-config.t b/tests/test-push-path-config.t
new file mode 100644
--- /dev/null
+++ b/tests/test-push-path-config.t
@@ -0,0 +1,50 @@ 
+  $ cat >> $HGRCPATH << EOF
+  > [ui]
+  > ssh = python "$TESTDIR/dummyssh"
+  > EOF
+
+  $ hg init server
+  $ hg -q clone server client
+  $ cd client
+
+"pushurl" with "#revision" fragment is rejected
+
+  $ cat > .hg/hgrc << EOF
+  > [paths]
+  > bad = $TESTTMP/server
+  > bad.pushurl = ssh://user@dummy/$TESTTMP/server#branch
+  > EOF
+
+  $ hg push bad
+  abort: pushurl may not have #revision fragment: ssh://user@dummy/$TESTTMP/server#branch
+  [255]
+
+  $ cat > .hg/hgrc << EOF
+  > [paths]
+  > srv = $TESTTMP/server
+  > srv.pushurl = ssh://user@dummy/$TESTTMP/server
+  > EOF
+
+Outgoing should use "url"
+
+  $ touch foo
+  $ hg -q commit -A -m initial
+  $ hg outgoing srv
+  comparing with $TESTTMP/server
+  searching for changes
+  changeset:   0:96ee1d7354c4
+  tag:         tip
+  user:        test
+  date:        Thu Jan 01 00:00:00 1970 +0000
+  summary:     initial
+  
+
+Push should use "pushurl"
+
+  $ hg push srv
+  pushing to ssh://user@dummy/$TESTTMP/server
+  searching for changes
+  remote: adding changesets
+  remote: adding manifests
+  remote: adding file changes
+  remote: added 1 changesets with 1 changes to 1 files