subrepo: implement 'unshare' for Mercurial subrepos
authorMatt Harbison <matt_harbison@yahoo.com>
Tue, 17 Oct 2017 22:55:33 -0400
changeset 34879 7d51a7792f52
parent 34878 9f7ecc5bbc28
child 34880 9e18ab7f7240
subrepo: implement 'unshare' for Mercurial subrepos I think there's a slight hole here in that a subrepo could be shared, removed from .hgsub, and then it's not part of context.substate (so not iterated over). But the push command has the same hole IIRC, and I think removing a subrepo is an edge case. The import hack is a copy/paste of subrepo.subrepo().
mercurial/hg.py
mercurial/subrepo.py
tests/test-archive.t
--- a/mercurial/hg.py	Tue Oct 17 21:48:56 2017 -0400
+++ b/mercurial/hg.py	Tue Oct 17 22:55:33 2017 -0400
@@ -286,6 +286,13 @@
     # update store, spath, svfs and sjoin of repo
     repo.unfiltered().__init__(repo.baseui, repo.root)
 
+    # TODO: figure out how to access subrepos that exist, but were previously
+    #       removed from .hgsub
+    c = repo['.']
+    subs = c.substate
+    for s in sorted(subs):
+        c.sub(s).unshare()
+
 def postshare(sourcerepo, destrepo, bookmarks=True, defaultpath=None):
     """Called after a new shared repo is created.
 
--- a/mercurial/subrepo.py	Tue Oct 17 21:48:56 2017 -0400
+++ b/mercurial/subrepo.py	Tue Oct 17 22:55:33 2017 -0400
@@ -621,6 +621,11 @@
     def shortid(self, revid):
         return revid
 
+    def unshare(self):
+        '''
+        convert this repository from shared to normal storage.
+        '''
+
     def verify(self):
         '''verify the integrity of the repository.  Return 0 on success or
         warning, 1 on any error.
@@ -1083,6 +1088,24 @@
     def shortid(self, revid):
         return revid[:12]
 
+    @annotatesubrepoerror
+    def unshare(self):
+        # subrepo inherently violates our import layering rules
+        # because it wants to make repo objects from deep inside the stack
+        # so we manually delay the circular imports to not break
+        # scripts that don't use our demand-loading
+        global hg
+        from . import hg as h
+        hg = h
+
+        # Nothing prevents a user from sharing in a repo, and then making that a
+        # subrepo.  Alternately, the previous unshare attempt may have failed
+        # part way through.  So recurse whether or not this layer is shared.
+        if self._repo.shared():
+            self.ui.status(_("unsharing subrepo '%s'\n") % self._relpath)
+
+        hg.unshare(self.ui, self._repo)
+
     def verify(self):
         try:
             rev = self._state[1]
--- a/tests/test-archive.t	Tue Oct 17 21:48:56 2017 -0400
+++ b/tests/test-archive.t	Tue Oct 17 22:55:33 2017 -0400
@@ -51,6 +51,24 @@
   $ hg -R clone1 update -C tip
   cloning subrepo subrepo from $TESTTMP/test/subrepo
   3 files updated, 0 files merged, 0 files removed, 0 files unresolved
+  $ find share2 | egrep 'sharedpath|00.+\.i' | sort
+  share2/.hg/sharedpath
+  share2/subrepo/.hg/sharedpath
+  $ hg -R share2 unshare
+  unsharing subrepo 'subrepo'
+  $ find share2 | egrep 'sharedpath|00.+\.i' | sort
+  share2/.hg/00changelog.i
+  share2/.hg/sharedpath.old
+  share2/.hg/store/00changelog.i
+  share2/.hg/store/00manifest.i
+  share2/subrepo/.hg/00changelog.i
+  share2/subrepo/.hg/sharedpath.old
+  share2/subrepo/.hg/store/00changelog.i
+  share2/subrepo/.hg/store/00manifest.i
+  $ hg -R share2/subrepo log -r tip -T compact
+  1[tip]   559dcc9bfa65   1970-01-01 00:00 +0000   test
+    subrepo mod
+  
   $ rm -rf clone1
 
   $ hg clone -qr 1 test clone1