[PATCH] share: add --relative flag to store a relative path to the source

Dan Villiom Podlaski Christiansen danchr at gmail.com
Tue Feb 14 15:53:47 UTC 2017


# HG changeset patch
# User Dan Villiom Podlaski Christiansen <danchr at 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.

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


More information about the Mercurial-devel mailing list