From patchwork Fri Dec 26 11:46:53 2014 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit Subject: [5,of,6,V2,RFC] share: add "full share" suport From: Angel Ezquerra X-Patchwork-Id: 7246 Message-Id: To: mercurial-devel@selenic.com Date: Fri, 26 Dec 2014 12:46:53 +0100 # HG changeset patch # User Angel Ezquerra # Date 1419360788 -3600 # Tue Dec 23 19:53:08 2014 +0100 # Node ID bb70464b9121df236db6d264d57b6f56ad78cd3b # Parent dd180345dd166fc51d061a8041e014f320c20d6c share: add "full share" suport Add a new --full flag that makes it possible to create "full shares". The actual full commit message comes below, but first some notes and caveats: # NOTES: 1. This is the first step on the "subrepo store" plan that we discussed during the mercurial 3.2 sprint 2. This is RFC because I am not certain that this is the right way to do this. Maybe there is a better, obvious way to do it. In that case I'd love to hear about it 3. If this approach is not completely crazy, are there any files that I should add to the fullshareexceptions list? (see below for context) 4. hg unshare is currently unsupported 5. This revision does not handle the locking of the non-store parts of the repository properly. That will be handled on a separate patch (which in the final form of this series should be folded into this revision). # Actual commit message: A "fully shared repository" is one that shares _everything_ with its source repository including bookmarks, mq patches and configuration files. The only difference between the source repo and the fully shared copy is that their working directory can be different and point to a different revision, bookmark and branch. In order to do so we replace the regular localrepo vfs object with a unionvfs, object, which is the union of a vfs that points to the share source and another that points to the local repository .hg path. The unionvfs selection function is such that all files are taken from the shared repository vfs except for a few key files (dirstate, branch, bookmarks.current, requires, sharedpath and sharedinfull). The idea is to only keep the files that are strictly necessary to maintain separate working directories and a different current branch and active bookmark in the shared repository, and share everything else with the shared source repository. diff --git a/hgext/share.py b/hgext/share.py --- a/hgext/share.py +++ b/hgext/share.py @@ -16,10 +16,11 @@ @command('share', [('U', 'noupdate', None, _('do not create a working copy')), - ('B', 'bookmarks', None, _('also share bookmarks'))], - _('[-U] [-B] SOURCE [DEST]'), + ('B', 'bookmarks', None, _('also share bookmarks')), + ('', 'full', None, _('share in full'))], + _('[-U] [-B] [--full] SOURCE [DEST]'), norepo=True) -def share(ui, source, dest=None, noupdate=False, bookmarks=False): +def share(ui, source, dest=None, noupdate=False, bookmarks=False, full=False): """create a new shared repository Initialize a new repository and working directory that shares its @@ -37,7 +38,8 @@ the broken clone to reset it to a changeset that still exists. """ - return hg.share(ui, source, dest, not noupdate, bookmarks) + return hg.share(ui, source, dest, not noupdate, bookmarks=bookmarks, + fullshare=full) @command('unshare', [], '') def unshare(ui, repo): @@ -46,6 +48,8 @@ Copy the store data to the repo and remove the sharedpath data. """ + if repo.fullshare: + raise util.Abort(_("unsharing a full repository share is unsupported")) if not repo.shared(): raise util.Abort(_("this is not a shared repo")) diff --git a/mercurial/hg.py b/mercurial/hg.py --- a/mercurial/hg.py +++ b/mercurial/hg.py @@ -158,7 +158,7 @@ return '' return os.path.basename(os.path.normpath(path)) -def share(ui, source, dest=None, update=True, bookmarks=True): +def share(ui, source, dest=None, update=True, bookmarks=True, fullshare=False): '''create a shared repository''' if not islocal(source): @@ -201,6 +201,8 @@ requirements += 'shared\n' destvfs.write('requires', requirements) destvfs.write('sharedpath', sharedpath) + if fullshare: + destvfs.write('sharedinfull', '') r = repository(ui, destwvfs.base) diff --git a/mercurial/localrepo.py b/mercurial/localrepo.py --- a/mercurial/localrepo.py +++ b/mercurial/localrepo.py @@ -189,6 +189,7 @@ return self.requirements[:] def __init__(self, baseui, path=None, create=False): + self.fullshare = False self.wvfs = scmutil.vfs(path, expandpath=True, realpath=True) self.wopener = self.wvfs self.root = self.wvfs.base @@ -256,13 +257,28 @@ self.sharedpath = self.path try: - vfs = scmutil.vfs(self.vfs.read("sharedpath").rstrip('\n'), + fullshareexceptions = ('dirstate', 'branch', 'bookmarks.current', + 'requires', 'sharedpath', 'sharedinfull') + sourcevfs = scmutil.vfs(self.vfs.read("sharedpath").rstrip('\n'), realpath=True) - s = vfs.base - if not vfs.exists(): + s = sourcevfs.base + if not sourcevfs.exists(): raise error.RepoError( _('.hg/sharedpath points to nonexistent directory %s') % s) self.sharedpath = s + self.fullshare = self.vfs.exists('sharedinfull') + if self.fullshare: + # full shares are those in which all files except for the + # requires, sharedpath and sharedinfull files must be read from + # the source repository + self.origpath = self.path + self.path = self.sharedpath + def selectfn(path): + if path in fullshareexceptions: + return 1 + return 0 + self.vfs = scmutil.unionvfs(selectfn, sourcevfs, self.vfs) + self.opener = self.vfs except IOError, inst: if inst.errno != errno.ENOENT: raise @@ -760,7 +776,9 @@ def shared(self): '''the type of shared repository (None if not shared)''' - if self.sharedpath != self.path: + if self.fullshare: + return 'full' + elif self.sharedpath != self.path: return 'store' return None 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,176 @@ bm4 5:92793bfc8cad $ cd .. +create a full share + + $ hg share --full repo1 repo-fullshare + updating working directory + 2 files updated, 0 files merged, 0 files removed, 0 files unresolved + + $ cd repo-fullshare + +full shares share the whole set of revisions + $ hg log -G + @ changeset: 5:92793bfc8cad + | bookmark: bm4 + | tag: tip + | user: test + | date: Thu Jan 01 00:00:00 1970 +0000 + | summary: foo in b + | + o changeset: 4:62f4ded848e4 + | bookmark: bm3 + | parent: 2:c2e0ac586386 + | user: test + | date: Thu Jan 01 00:00:00 1970 +0000 + | summary: testing shared bookmarks + | + | o changeset: 3:b87954705719 + |/ bookmark: bm1 + | user: test + | date: Thu Jan 01 00:00:00 1970 +0000 + | summary: testing shared bookmarks + | + o changeset: 2:c2e0ac586386 + | user: test + | date: Thu Jan 01 00:00:00 1970 +0000 + | summary: another file + | + o changeset: 1:8af4dc49db9e + | user: test + | date: Thu Jan 01 00:00:00 1970 +0000 + | summary: change in shared clone + | + o changeset: 0:d3873e73d99e + user: test + date: Thu Jan 01 00:00:00 1970 +0000 + summary: init + +and bookmarks + $ hg bookmarks + bm1 3:b87954705719 + bm3 4:62f4ded848e4 + bm4 5:92793bfc8cad + $ hg -R ../repo1 bookmarks + * bm1 3:b87954705719 + bm3 4:62f4ded848e4 + bm4 5:92793bfc8cad + +the source and shared repo working directories can point to different revisions +and bookmarks + $ hg update bm3 + 1 files updated, 0 files merged, 0 files removed, 0 files unresolved + (activating bookmark bm3) + $ hg log -r . + changeset: 4:62f4ded848e4 + bookmark: bm3 + parent: 2:c2e0ac586386 + user: test + date: Thu Jan 01 00:00:00 1970 +0000 + summary: testing shared bookmarks + + $ hg -R ../repo1 log -r . + changeset: 3:b87954705719 + bookmark: bm1 + user: test + date: Thu Jan 01 00:00:00 1970 +0000 + summary: testing shared bookmarks + +they can also commit to different branches + $ hg branch createdinshare + marked working directory as branch createdinshare + (branches are permanent and global, did you want a bookmark?) + $ hg -R ../repo1 branch + default + +and commit on top of different revisions + $ cat a + more shared bookmarks + $ echo a > a + $ hg commit -m "created in full share" + $ echo a > ../repo1/a + $ hg commit -R ../repo1 -m "created in share source" + $ hg log -G + o changeset: 7:e0fb5f85a10b + | bookmark: bm1 + | tag: tip + | parent: 3:b87954705719 + | user: test + | date: Thu Jan 01 00:00:00 1970 +0000 + | summary: created in share source + | + | @ changeset: 6:5ea6503932c4 + | | branch: createdinshare + | | bookmark: bm3 + | | parent: 4:62f4ded848e4 + | | user: test + | | date: Thu Jan 01 00:00:00 1970 +0000 + | | summary: created in full share + | | + | | o changeset: 5:92793bfc8cad + | |/ bookmark: bm4 + | | user: test + | | date: Thu Jan 01 00:00:00 1970 +0000 + | | summary: foo in b + | | + | o changeset: 4:62f4ded848e4 + | | parent: 2:c2e0ac586386 + | | user: test + | | date: Thu Jan 01 00:00:00 1970 +0000 + | | summary: testing shared bookmarks + | | + o | changeset: 3:b87954705719 + |/ user: test + | date: Thu Jan 01 00:00:00 1970 +0000 + | summary: testing shared bookmarks + | + o changeset: 2:c2e0ac586386 + | user: test + | date: Thu Jan 01 00:00:00 1970 +0000 + | summary: another file + | + o changeset: 1:8af4dc49db9e + | user: test + | date: Thu Jan 01 00:00:00 1970 +0000 + | summary: change in shared clone + | + o changeset: 0:d3873e73d99e + user: test + date: Thu Jan 01 00:00:00 1970 +0000 + summary: init + + +full shares should have a limited set of files in its .hg folder + + $ ls .hg + bookmarks.current + branch + dirstate + requires + sharedinfull + sharedpath + + $ ls ../repo1/.hg + 00changelog.i + bookmarks + bookmarks.current + branch + cache + dirstate + last-message.txt + requires + store + undo.bookmarks + undo.branch + undo.desc + undo.dirstate + +unsharing a full share is unsupported + + $ hg unshare + abort: unsharing a full repository share is unsupported + [255] + Explicitly kill daemons to let the test exit on Windows $ "$TESTDIR/killdaemons.py" $DAEMON_PIDS