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