##// END OF EJS Templates
share: make option docs more check-config friendly
Matt Mackall -
r25851:bf3d10f0 default
parent child Browse files
Show More
@@ -1,175 +1,175 b''
1 # Copyright 2006, 2007 Matt Mackall <mpm@selenic.com>
1 # Copyright 2006, 2007 Matt Mackall <mpm@selenic.com>
2 #
2 #
3 # This software may be used and distributed according to the terms of the
3 # This software may be used and distributed according to the terms of the
4 # GNU General Public License version 2 or any later version.
4 # GNU General Public License version 2 or any later version.
5
5
6 '''share a common history between several working directories
6 '''share a common history between several working directories
7
7
8 Automatic Pooled Storage for Clones
8 Automatic Pooled Storage for Clones
9 -----------------------------------
9 -----------------------------------
10
10
11 When this extension is active, :hg:`clone` can be configured to
11 When this extension is active, :hg:`clone` can be configured to
12 automatically share/pool storage across multiple clones. This
12 automatically share/pool storage across multiple clones. This
13 mode effectively converts :hg:`clone` to :hg:`clone` + :hg:`share`.
13 mode effectively converts :hg:`clone` to :hg:`clone` + :hg:`share`.
14 The benefit of using this mode is the automatic management of
14 The benefit of using this mode is the automatic management of
15 store paths and intelligent pooling of related repositories.
15 store paths and intelligent pooling of related repositories.
16
16
17 The following ``share.`` config options influence this feature:
17 The following ``share.`` config options influence this feature:
18
18
19 ``pool``
19 ``share.pool``
20 Filesystem path where shared repository data will be stored. When
20 Filesystem path where shared repository data will be stored. When
21 defined, :hg:`clone` will automatically use shared repository
21 defined, :hg:`clone` will automatically use shared repository
22 storage instead of creating a store inside each clone.
22 storage instead of creating a store inside each clone.
23
23
24 ``poolnaming``
24 ``share.poolnaming``
25 How directory names in ``share.pool`` are constructed.
25 How directory names in ``share.pool`` are constructed.
26
26
27 "identity" means the name is derived from the first changeset in the
27 "identity" means the name is derived from the first changeset in the
28 repository. In this mode, different remotes share storage if their
28 repository. In this mode, different remotes share storage if their
29 root/initial changeset is identical. In this mode, the local shared
29 root/initial changeset is identical. In this mode, the local shared
30 repository is an aggregate of all encountered remote repositories.
30 repository is an aggregate of all encountered remote repositories.
31
31
32 "remote" means the name is derived from the source repository's
32 "remote" means the name is derived from the source repository's
33 path or URL. In this mode, storage is only shared if the path or URL
33 path or URL. In this mode, storage is only shared if the path or URL
34 requested in the :hg:`clone` command matches exactly to a repository
34 requested in the :hg:`clone` command matches exactly to a repository
35 that was cloned before.
35 that was cloned before.
36
36
37 The default naming mode is "identity."
37 The default naming mode is "identity."
38 '''
38 '''
39
39
40 from mercurial.i18n import _
40 from mercurial.i18n import _
41 from mercurial import cmdutil, commands, hg, util, extensions, bookmarks
41 from mercurial import cmdutil, commands, hg, util, extensions, bookmarks
42 from mercurial.hg import repository, parseurl
42 from mercurial.hg import repository, parseurl
43 import errno
43 import errno
44
44
45 cmdtable = {}
45 cmdtable = {}
46 command = cmdutil.command(cmdtable)
46 command = cmdutil.command(cmdtable)
47 # Note for extension authors: ONLY specify testedwith = 'internal' for
47 # Note for extension authors: ONLY specify testedwith = 'internal' for
48 # extensions which SHIP WITH MERCURIAL. Non-mainline extensions should
48 # extensions which SHIP WITH MERCURIAL. Non-mainline extensions should
49 # be specifying the version(s) of Mercurial they are tested with, or
49 # be specifying the version(s) of Mercurial they are tested with, or
50 # leave the attribute unspecified.
50 # leave the attribute unspecified.
51 testedwith = 'internal'
51 testedwith = 'internal'
52
52
53 @command('share',
53 @command('share',
54 [('U', 'noupdate', None, _('do not create a working directory')),
54 [('U', 'noupdate', None, _('do not create a working directory')),
55 ('B', 'bookmarks', None, _('also share bookmarks'))],
55 ('B', 'bookmarks', None, _('also share bookmarks'))],
56 _('[-U] [-B] SOURCE [DEST]'),
56 _('[-U] [-B] SOURCE [DEST]'),
57 norepo=True)
57 norepo=True)
58 def share(ui, source, dest=None, noupdate=False, bookmarks=False):
58 def share(ui, source, dest=None, noupdate=False, bookmarks=False):
59 """create a new shared repository
59 """create a new shared repository
60
60
61 Initialize a new repository and working directory that shares its
61 Initialize a new repository and working directory that shares its
62 history (and optionally bookmarks) with another repository.
62 history (and optionally bookmarks) with another repository.
63
63
64 .. note::
64 .. note::
65
65
66 using rollback or extensions that destroy/modify history (mq,
66 using rollback or extensions that destroy/modify history (mq,
67 rebase, etc.) can cause considerable confusion with shared
67 rebase, etc.) can cause considerable confusion with shared
68 clones. In particular, if two shared clones are both updated to
68 clones. In particular, if two shared clones are both updated to
69 the same changeset, and one of them destroys that changeset
69 the same changeset, and one of them destroys that changeset
70 with rollback, the other clone will suddenly stop working: all
70 with rollback, the other clone will suddenly stop working: all
71 operations will fail with "abort: working directory has unknown
71 operations will fail with "abort: working directory has unknown
72 parent". The only known workaround is to use debugsetparents on
72 parent". The only known workaround is to use debugsetparents on
73 the broken clone to reset it to a changeset that still exists.
73 the broken clone to reset it to a changeset that still exists.
74 """
74 """
75
75
76 return hg.share(ui, source, dest, not noupdate, bookmarks)
76 return hg.share(ui, source, dest, not noupdate, bookmarks)
77
77
78 @command('unshare', [], '')
78 @command('unshare', [], '')
79 def unshare(ui, repo):
79 def unshare(ui, repo):
80 """convert a shared repository to a normal one
80 """convert a shared repository to a normal one
81
81
82 Copy the store data to the repo and remove the sharedpath data.
82 Copy the store data to the repo and remove the sharedpath data.
83 """
83 """
84
84
85 if not repo.shared():
85 if not repo.shared():
86 raise util.Abort(_("this is not a shared repo"))
86 raise util.Abort(_("this is not a shared repo"))
87
87
88 destlock = lock = None
88 destlock = lock = None
89 lock = repo.lock()
89 lock = repo.lock()
90 try:
90 try:
91 # we use locks here because if we race with commit, we
91 # we use locks here because if we race with commit, we
92 # can end up with extra data in the cloned revlogs that's
92 # can end up with extra data in the cloned revlogs that's
93 # not pointed to by changesets, thus causing verify to
93 # not pointed to by changesets, thus causing verify to
94 # fail
94 # fail
95
95
96 destlock = hg.copystore(ui, repo, repo.path)
96 destlock = hg.copystore(ui, repo, repo.path)
97
97
98 sharefile = repo.join('sharedpath')
98 sharefile = repo.join('sharedpath')
99 util.rename(sharefile, sharefile + '.old')
99 util.rename(sharefile, sharefile + '.old')
100
100
101 repo.requirements.discard('sharedpath')
101 repo.requirements.discard('sharedpath')
102 repo._writerequirements()
102 repo._writerequirements()
103 finally:
103 finally:
104 destlock and destlock.release()
104 destlock and destlock.release()
105 lock and lock.release()
105 lock and lock.release()
106
106
107 # update store, spath, svfs and sjoin of repo
107 # update store, spath, svfs and sjoin of repo
108 repo.unfiltered().__init__(repo.baseui, repo.root)
108 repo.unfiltered().__init__(repo.baseui, repo.root)
109
109
110 # Wrap clone command to pass auto share options.
110 # Wrap clone command to pass auto share options.
111 def clone(orig, ui, source, *args, **opts):
111 def clone(orig, ui, source, *args, **opts):
112 pool = ui.config('share', 'pool', None)
112 pool = ui.config('share', 'pool', None)
113 if pool:
113 if pool:
114 pool = util.expandpath(pool)
114 pool = util.expandpath(pool)
115
115
116 opts['shareopts'] = dict(
116 opts['shareopts'] = dict(
117 pool=pool,
117 pool=pool,
118 mode=ui.config('share', 'poolnaming', 'identity'),
118 mode=ui.config('share', 'poolnaming', 'identity'),
119 )
119 )
120
120
121 return orig(ui, source, *args, **opts)
121 return orig(ui, source, *args, **opts)
122
122
123 def extsetup(ui):
123 def extsetup(ui):
124 extensions.wrapfunction(bookmarks.bmstore, 'getbkfile', getbkfile)
124 extensions.wrapfunction(bookmarks.bmstore, 'getbkfile', getbkfile)
125 extensions.wrapfunction(bookmarks.bmstore, 'recordchange', recordchange)
125 extensions.wrapfunction(bookmarks.bmstore, 'recordchange', recordchange)
126 extensions.wrapfunction(bookmarks.bmstore, 'write', write)
126 extensions.wrapfunction(bookmarks.bmstore, 'write', write)
127 extensions.wrapcommand(commands.table, 'clone', clone)
127 extensions.wrapcommand(commands.table, 'clone', clone)
128
128
129 def _hassharedbookmarks(repo):
129 def _hassharedbookmarks(repo):
130 """Returns whether this repo has shared bookmarks"""
130 """Returns whether this repo has shared bookmarks"""
131 try:
131 try:
132 shared = repo.vfs.read('shared').splitlines()
132 shared = repo.vfs.read('shared').splitlines()
133 except IOError as inst:
133 except IOError as inst:
134 if inst.errno != errno.ENOENT:
134 if inst.errno != errno.ENOENT:
135 raise
135 raise
136 return False
136 return False
137 return 'bookmarks' in shared
137 return 'bookmarks' in shared
138
138
139 def _getsrcrepo(repo):
139 def _getsrcrepo(repo):
140 """
140 """
141 Returns the source repository object for a given shared repository.
141 Returns the source repository object for a given shared repository.
142 If repo is not a shared repository, return None.
142 If repo is not a shared repository, return None.
143 """
143 """
144 if repo.sharedpath == repo.path:
144 if repo.sharedpath == repo.path:
145 return None
145 return None
146
146
147 # the sharedpath always ends in the .hg; we want the path to the repo
147 # the sharedpath always ends in the .hg; we want the path to the repo
148 source = repo.vfs.split(repo.sharedpath)[0]
148 source = repo.vfs.split(repo.sharedpath)[0]
149 srcurl, branches = parseurl(source)
149 srcurl, branches = parseurl(source)
150 return repository(repo.ui, srcurl)
150 return repository(repo.ui, srcurl)
151
151
152 def getbkfile(orig, self, repo):
152 def getbkfile(orig, self, repo):
153 if _hassharedbookmarks(repo):
153 if _hassharedbookmarks(repo):
154 srcrepo = _getsrcrepo(repo)
154 srcrepo = _getsrcrepo(repo)
155 if srcrepo is not None:
155 if srcrepo is not None:
156 repo = srcrepo
156 repo = srcrepo
157 return orig(self, repo)
157 return orig(self, repo)
158
158
159 def recordchange(orig, self, tr):
159 def recordchange(orig, self, tr):
160 # Continue with write to local bookmarks file as usual
160 # Continue with write to local bookmarks file as usual
161 orig(self, tr)
161 orig(self, tr)
162
162
163 if _hassharedbookmarks(self._repo):
163 if _hassharedbookmarks(self._repo):
164 srcrepo = _getsrcrepo(self._repo)
164 srcrepo = _getsrcrepo(self._repo)
165 if srcrepo is not None:
165 if srcrepo is not None:
166 category = 'share-bookmarks'
166 category = 'share-bookmarks'
167 tr.addpostclose(category, lambda tr: self._writerepo(srcrepo))
167 tr.addpostclose(category, lambda tr: self._writerepo(srcrepo))
168
168
169 def write(orig, self):
169 def write(orig, self):
170 # First write local bookmarks file in case we ever unshare
170 # First write local bookmarks file in case we ever unshare
171 orig(self)
171 orig(self)
172 if _hassharedbookmarks(self._repo):
172 if _hassharedbookmarks(self._repo):
173 srcrepo = _getsrcrepo(self._repo)
173 srcrepo = _getsrcrepo(self._repo)
174 if srcrepo is not None:
174 if srcrepo is not None:
175 self._writerepo(srcrepo)
175 self._writerepo(srcrepo)
General Comments 0
You need to be logged in to leave comments. Login now