[PATCH 1 of 2 RFC] subrepo: create subrepo cache and create working dir subrepos as shared repos

Angel Ezquerra angel.ezquerra at gmail.com
Fri Nov 15 14:15:21 CST 2013


# HG changeset patch
# User Angel Ezquerra <angel.ezquerra at gmail.com>
# Date 1383263921 -3600
#      Fri Nov 01 00:58:41 2013 +0100
# Node ID eabbdd067b8e2f4c3dc7ba9678b0acb54e1ed710
# Parent  c38c3fdc8b9317ba09e03ab09364c3800da7c50c
subrepo: create subrepo cache and create working dir subrepos as shared repos

With this change cloned subrepos are no longer directly cloned as regular
repositories on the working directory. Instead, they are are first cloned into a
subrepo "cache" (in the parent repository's ".hg/cache/subs" directory). Once
this is done, the "working directory subrepos" are created as "shared
repositories" from the actual repositories on the subs cache directory.

The main motivation for this patch is three-fold:

- To make it much safer to remove a subrepo from the working directory.
- To avoid the need to reclone a subrepo from the source repository when you
delete it from your working directory (which sometimes you must do before
running hg update if you converted a regular folder into a subrepo or
viceversa).
- To eventually be able to automatically and safely remove subrepos from the
working directory when updating to a revision that has deleted the subrepo.

Longer term it _might_ be even possible to efficiently implement "advanced"
subrepo operations such as moving or copying subrepos.

This change is fully functional but there are a few things left TODO:

- Change hgweb to be "subcache aware" (will be done on a later patch)
- Handle the addition of new local subrepos to the cache (e.g. an existing,
regular repository that is added as a subrepo to a parent repository).
- Handle the deletion of subrepositories (should be easier to handle thanks to
this patch).
- Perhaps we should go beyond what a regular "shared" repository does, and try
to create most files on the cached repository, including mq patches.
Alternatively we could check if any "unknown" file exists on the working
directory copy of the subrepo and ask the user what to do when a subrepo is
removed.

Other notes:

- This works fine even if the share extension is not enabled
- This change is backwards compatible.
- Currently for simplicity reasons the location of the cached subrepos is
.hg/cache/subs/SUB_PATH. It might be safer to use a hashed name to avoid
problems with long paths.
- I have removed a test that no longer made sense from test-subrepo-recursion.t
and slightly changed a test in test-subrepo.t.

diff --git a/mercurial/subrepo.py b/mercurial/subrepo.py
--- a/mercurial/subrepo.py
+++ b/mercurial/subrepo.py
@@ -463,12 +463,19 @@
         self._path = path
         self._state = state
         r = ctx._repo
+        cachedroot = os.path.join(r.join('cache'), 'subs', path)
+        create = False
+        if not os.path.exists(os.path.join(cachedroot, '.hg')):
+            util.makedirs(cachedroot)
+            create = True
+        self._cachedrepo = hg.repository(r.baseui, cachedroot, create=create)
         root = r.wjoin(path)
         create = False
         if not os.path.exists(os.path.join(root, '.hg')):
             create = True
             util.makedirs(root)
-        self._repo = hg.repository(r.baseui, root, create=create)
+            hg.share(r.baseui, cachedroot, root, False)
+        self._repo = hg.repository(r.baseui, root, create=False)
         for s, k in [('ui', 'commitsubrepos')]:
             v = r.ui.config(s, k)
             if v:
@@ -507,12 +514,12 @@
         filelist = ('bookmarks', 'store/phaseroots', 'store/00changelog.i')
         yield '# %s\n' % _expandedabspath(remotepath)
         for relname in filelist:
-            absname = os.path.normpath(self._repo.join(relname))
+            absname = os.path.normpath(self._cachedrepo.join(relname))
             yield '%s = %s\n' % (relname, _calcfilehash(absname))
 
     def _getstorehashcachepath(self, remotepath):
         '''get a unique path for the store hash cache'''
-        return self._repo.join(os.path.join(
+        return self._cachedrepo.join(os.path.join(
             'cache', 'storehash', _getstorehashcachename(remotepath)))
 
     def _readstorehashcache(self, remotepath):
@@ -655,11 +662,12 @@
                 self._repo.ui.status(_('cloning subrepo %s from %s\n')
                                      % (subrelpath(self), srcurl))
                 parentrepo = self._repo._subparent
-                shutil.rmtree(self._repo.path)
+                shutil.rmtree(self._cachedrepo.path)
                 other, cloned = hg.clone(self._repo._subparent.baseui, {},
-                                         other, self._repo.root,
+                                         other, self._cachedrepo.root,
                                          update=False)
-                self._repo = cloned.local()
+                self._repo = hg.repository(self._repo._subparent.baseui,
+                                           self._repo.root, create=False)
                 self._initrepo(parentrepo, source, create=True)
                 self._cachestorehash(srcurl)
             else:
diff --git a/tests/test-subrepo-recursion.t b/tests/test-subrepo-recursion.t
--- a/tests/test-subrepo-recursion.t
+++ b/tests/test-subrepo-recursion.t
@@ -377,18 +377,6 @@
 
   $ mv $HGRCPATH.no-progress $HGRCPATH
 
-Test archiving when there is a directory in the way for a subrepo
-created by archive:
-
-  $ hg clone -U . ../almost-empty
-  $ cd ../almost-empty
-  $ mkdir foo
-  $ echo f > foo/f
-  $ hg archive --subrepos -r tip archive
-  cloning subrepo foo from $TESTTMP/empty/foo
-  abort: destination '$TESTTMP/almost-empty/foo' is not empty (in subrepo foo) (glob)
-  [255]
-
 Clone and test outgoing:
 
   $ cd ..
diff --git a/tests/test-subrepo.t b/tests/test-subrepo.t
--- a/tests/test-subrepo.t
+++ b/tests/test-subrepo.t
@@ -856,7 +856,7 @@
   updating working directory
   cloning subrepo subrepo-2 from $TESTTMP/subrepo-status/subrepo-2
   2 files updated, 0 files merged, 0 files removed, 0 files unresolved
-  $ test -f ../shared/subrepo-1/.hg/sharedpath
+  $ test -f ../shared/.hg/cache/subs/subrepo-1/sharedpath
   [1]
   $ hg -R ../shared in
   abort: repository default not found!
-------------- next part --------------
A non-text attachment was scrubbed...
Name: hg-2.4-1.patch
Type: text/x-patch
Size: 6150 bytes
Desc: not available
URL: <http://selenic.com/pipermail/mercurial-devel/attachments/20131115/3d238eb0/attachment.bin>


More information about the Mercurial-devel mailing list