Patchwork share: add --relative flag to store a relative path to the source

login
register
mail settings
Submitter Dan Villiom Podlaski Christiansen
Date Feb. 14, 2017, 3:53 p.m.
Message ID <e6d4c5b2de24b6151d70.1487087627@magenta-158.intern.it-huset.dk>
Download mbox | patch
Permalink /patch/18477/
State Accepted
Headers show

Comments

Dan Villiom Podlaski Christiansen - Feb. 14, 2017, 3:53 p.m.
# HG changeset patch
# User Dan Villiom Podlaski Christiansen <danchr@gmail.com>
# Date 1486991124 -3600
#      Mon Feb 13 14:05:24 2017 +0100
# Node ID e6d4c5b2de24b6151d709696891eed665000d363
# Parent  a0e3d808690d57d1c9dff840e0b8ee099526397b
share: add --relative flag to store a relative path to the source

Storing a relative path the source repository is useful when exporting
repositories over the network or when they're located on external
drives where the mountpoint isn't always fixed.

Currently, Mercurial interprets paths in `.hg/shared` relative to
$PWD. I suspect this is very much unintentional, and you have to
manually edit `.hg/shared` in order to trigger this behaviour.

However, on the off chance that someone might rely on it, I added a
new capability called 'relshared'. In addition, this makes earlier
versions of Mercurial fail with a graceful error.

I should note that I haven't tested this patch on Windows.
Yuya Nishihara - March 2, 2017, 2:39 p.m.
On Tue, 14 Feb 2017 16:53:47 +0100, Dan Villiom Podlaski Christiansen wrote:
> # HG changeset patch
> # User Dan Villiom Podlaski Christiansen <danchr@gmail.com>
> # Date 1486991124 -3600
> #      Mon Feb 13 14:05:24 2017 +0100
> # Node ID e6d4c5b2de24b6151d709696891eed665000d363
> # Parent  a0e3d808690d57d1c9dff840e0b8ee099526397b
> share: add --relative flag to store a relative path to the source

Sorry for really late review. This looks good and I think is useful. Also,
there were no negative comments for weeks, so queued, thanks.

> Currently, Mercurial interprets paths in `.hg/shared` relative to
> $PWD. I suspect this is very much unintentional, and you have to
> manually edit `.hg/shared` in order to trigger this behaviour.
> 
> However, on the off chance that someone might rely on it, I added a
> new capability called 'relshared'. In addition, this makes earlier
> versions of Mercurial fail with a graceful error.

Yep, new capability would be needed to kick out old Mercurial versions.
Yuya Nishihara - March 2, 2017, 2:53 p.m.
On Tue, 14 Feb 2017 16:53:47 +0100, Dan Villiom Podlaski Christiansen wrote:
> # HG changeset patch
> # User Dan Villiom Podlaski Christiansen <danchr@gmail.com>
> # Date 1486991124 -3600
> #      Mon Feb 13 14:05:24 2017 +0100
> # Node ID e6d4c5b2de24b6151d709696891eed665000d363
> # Parent  a0e3d808690d57d1c9dff840e0b8ee099526397b
> share: add --relative flag to store a relative path to the source

> --- a/mercurial/hg.py
> +++ b/mercurial/hg.py
> @@ -195,7 +195,8 @@ def defaultdest(source):
>          return ''
>      return os.path.basename(os.path.normpath(path))
>  
> -def share(ui, source, dest=None, update=True, bookmarks=True, defaultpath=None):
> +def share(ui, source, dest=None, update=True, bookmarks=True, defaultpath=None,
> +          relative=False):
>      '''create a shared repository'''
>  
>      if not islocal(source):
> @@ -235,7 +236,16 @@ def share(ui, source, dest=None, update=
>          if inst.errno != errno.ENOENT:
>              raise
>  
> -    requirements += 'shared\n'
> +    if relative:
> +        try:
> +            sharedpath = os.path.relpath(sharedpath, destvfs.base)
> +            requirements += 'relshared\n'
> +        except IOError as e:
> +            raise error.Abort(_('cannot calculate relative path'),
> +                              hint=e.message)

BaseException.message is deprecated. Replaced with str(e) in flight.

Do you know when IOError would be raised? I suspect it might be OSError.

Patch

diff --git a/hgext/share.py b/hgext/share.py
--- a/hgext/share.py
+++ b/hgext/share.py
@@ -64,10 +64,14 @@  testedwith = 'ships-with-hg-core'
 
 @command('share',
     [('U', 'noupdate', None, _('do not create a working directory')),
-     ('B', 'bookmarks', None, _('also share bookmarks'))],
+     ('B', 'bookmarks', None, _('also share bookmarks')),
+     ('', 'relative', None, _('point to source using a relative path '
+                              '(EXPERIMENTAL)')),
+    ],
     _('[-U] [-B] SOURCE [DEST]'),
     norepo=True)
-def share(ui, source, dest=None, noupdate=False, bookmarks=False):
+def share(ui, source, dest=None, noupdate=False, bookmarks=False,
+          relative=False):
     """create a new shared repository
 
     Initialize a new repository and working directory that shares its
@@ -86,7 +90,7 @@  def share(ui, source, dest=None, noupdat
     """
 
     return hg.share(ui, source, dest=dest, update=not noupdate,
-                    bookmarks=bookmarks)
+                    bookmarks=bookmarks, relative=relative)
 
 @command('unshare', [], '')
 def unshare(ui, repo):
diff --git a/mercurial/help/internals/requirements.txt b/mercurial/help/internals/requirements.txt
--- a/mercurial/help/internals/requirements.txt
+++ b/mercurial/help/internals/requirements.txt
@@ -55,6 +55,17 @@  This requirement is set when a repositor
 
 The requirement was added in Mercurial 1.3 (released July 2009).
 
+relshared
+=========
+
+Derivative of ``shared``; the location of the store is relative to the
+store of this repository.
+
+This requirement is set when a repository is created via :hg:`share`
+using the ``--relative`` option.
+
+The requirement was added in Mercurial 4.2 (released May 2017).
+
 dotencode
 =========
 
diff --git a/mercurial/hg.py b/mercurial/hg.py
--- a/mercurial/hg.py
+++ b/mercurial/hg.py
@@ -195,7 +195,8 @@  def defaultdest(source):
         return ''
     return os.path.basename(os.path.normpath(path))
 
-def share(ui, source, dest=None, update=True, bookmarks=True, defaultpath=None):
+def share(ui, source, dest=None, update=True, bookmarks=True, defaultpath=None,
+          relative=False):
     '''create a shared repository'''
 
     if not islocal(source):
@@ -235,7 +236,16 @@  def share(ui, source, dest=None, update=
         if inst.errno != errno.ENOENT:
             raise
 
-    requirements += 'shared\n'
+    if relative:
+        try:
+            sharedpath = os.path.relpath(sharedpath, destvfs.base)
+            requirements += 'relshared\n'
+        except IOError as e:
+            raise error.Abort(_('cannot calculate relative path'),
+                              hint=e.message)
+    else:
+        requirements += 'shared\n'
+
     destvfs.write('requires', requirements)
     destvfs.write('sharedpath', sharedpath)
 
diff --git a/mercurial/localrepo.py b/mercurial/localrepo.py
--- a/mercurial/localrepo.py
+++ b/mercurial/localrepo.py
@@ -241,7 +241,7 @@  class localrepository(object):
     supportedformats = set(('revlogv1', 'generaldelta', 'treemanifest',
                             'manifestv2'))
     _basesupported = supportedformats | set(('store', 'fncache', 'shared',
-                                             'dotencode'))
+                                             'relshared', 'dotencode'))
     openerreqs = set(('revlogv1', 'generaldelta', 'treemanifest', 'manifestv2'))
     filtername = None
 
@@ -321,8 +321,11 @@  class localrepository(object):
 
         self.sharedpath = self.path
         try:
-            vfs = scmutil.vfs(self.vfs.read("sharedpath").rstrip('\n'),
-                              realpath=True)
+            sharedpath = self.vfs.read("sharedpath").rstrip('\n')
+            if 'relshared' in self.requirements:
+                sharedpath = self.vfs.join(sharedpath)
+            vfs = scmutil.vfs(sharedpath, realpath=True)
+
             s = vfs.base
             if not vfs.exists():
                 raise error.RepoError(
diff --git a/tests/test-share.t b/tests/test-share.t
--- a/tests/test-share.t
+++ b/tests/test-share.t
@@ -297,6 +297,41 @@  verify bookmark behavior after unshare
      bm4                       5:92793bfc8cad
   $ cd ..
 
+test shared clones using relative paths work
+
+  $ mkdir thisdir
+  $ hg init thisdir/orig
+  $ hg share -U thisdir/orig thisdir/abs
+  $ hg share -U --relative thisdir/abs thisdir/rel
+  $ cat thisdir/rel/.hg/sharedpath
+  ../../orig/.hg (no-eol)
+  $ grep shared thisdir/*/.hg/requires
+  thisdir/abs/.hg/requires:shared
+  thisdir/rel/.hg/requires:shared
+  thisdir/rel/.hg/requires:relshared
+
+test that relative shared paths aren't relative to $PWD
+
+  $ cd thisdir
+  $ hg -R rel root
+  $TESTTMP/thisdir/rel
+  $ cd ..
+
+now test that relative paths really are relative, survive across
+renames and changes of PWD
+
+  $ hg -R thisdir/abs root
+  $TESTTMP/thisdir/abs
+  $ hg -R thisdir/rel root
+  $TESTTMP/thisdir/rel
+  $ mv thisdir thatdir
+  $ hg -R thatdir/abs root
+  abort: .hg/sharedpath points to nonexistent directory $TESTTMP/thisdir/orig/.hg!
+  [255]
+  $ hg -R thatdir/rel root
+  $TESTTMP/thatdir/rel
+  $ rm -r thatdir
+
 Explicitly kill daemons to let the test exit on Windows
 
   $ killdaemons.py