##// END OF EJS Templates
share: fix typo to drop 'shared' requirement on unshare...
Yuya Nishihara -
r31211:ecbd378d default
parent child Browse files
Show More
@@ -1,220 +1,220 b''
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 cmdutil,
47 47 commands,
48 48 error,
49 49 extensions,
50 50 hg,
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 = cmdutil.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 @command('share',
67 67 [('U', 'noupdate', None, _('do not create a working directory')),
68 68 ('B', 'bookmarks', None, _('also share bookmarks')),
69 69 ('', 'relative', None, _('point to source using a relative path '
70 70 '(EXPERIMENTAL)')),
71 71 ],
72 72 _('[-U] [-B] SOURCE [DEST]'),
73 73 norepo=True)
74 74 def share(ui, source, dest=None, noupdate=False, bookmarks=False,
75 75 relative=False):
76 76 """create a new shared repository
77 77
78 78 Initialize a new repository and working directory that shares its
79 79 history (and optionally bookmarks) with another repository.
80 80
81 81 .. note::
82 82
83 83 using rollback or extensions that destroy/modify history (mq,
84 84 rebase, etc.) can cause considerable confusion with shared
85 85 clones. In particular, if two shared clones are both updated to
86 86 the same changeset, and one of them destroys that changeset
87 87 with rollback, the other clone will suddenly stop working: all
88 88 operations will fail with "abort: working directory has unknown
89 89 parent". The only known workaround is to use debugsetparents on
90 90 the broken clone to reset it to a changeset that still exists.
91 91 """
92 92
93 93 return hg.share(ui, source, dest=dest, update=not noupdate,
94 94 bookmarks=bookmarks, relative=relative)
95 95
96 96 @command('unshare', [], '')
97 97 def unshare(ui, repo):
98 98 """convert a shared repository to a normal one
99 99
100 100 Copy the store data to the repo and remove the sharedpath data.
101 101 """
102 102
103 103 if not repo.shared():
104 104 raise error.Abort(_("this is not a shared repo"))
105 105
106 106 destlock = lock = None
107 107 lock = repo.lock()
108 108 try:
109 109 # we use locks here because if we race with commit, we
110 110 # can end up with extra data in the cloned revlogs that's
111 111 # not pointed to by changesets, thus causing verify to
112 112 # fail
113 113
114 114 destlock = hg.copystore(ui, repo, repo.path)
115 115
116 116 sharefile = repo.join('sharedpath')
117 117 util.rename(sharefile, sharefile + '.old')
118 118
119 repo.requirements.discard('sharedpath')
119 repo.requirements.discard('shared')
120 120 repo._writerequirements()
121 121 finally:
122 122 destlock and destlock.release()
123 123 lock and lock.release()
124 124
125 125 # update store, spath, svfs and sjoin of repo
126 126 repo.unfiltered().__init__(repo.baseui, repo.root)
127 127
128 128 # Wrap clone command to pass auto share options.
129 129 def clone(orig, ui, source, *args, **opts):
130 130 pool = ui.config('share', 'pool', None)
131 131 if pool:
132 132 pool = util.expandpath(pool)
133 133
134 134 opts['shareopts'] = dict(
135 135 pool=pool,
136 136 mode=ui.config('share', 'poolnaming', 'identity'),
137 137 )
138 138
139 139 return orig(ui, source, *args, **opts)
140 140
141 141 def extsetup(ui):
142 142 extensions.wrapfunction(bookmarks, '_getbkfile', getbkfile)
143 143 extensions.wrapfunction(bookmarks.bmstore, 'recordchange', recordchange)
144 144 extensions.wrapfunction(bookmarks.bmstore, '_writerepo', writerepo)
145 145 extensions.wrapcommand(commands.table, 'clone', clone)
146 146
147 147 def _hassharedbookmarks(repo):
148 148 """Returns whether this repo has shared bookmarks"""
149 149 try:
150 150 shared = repo.vfs.read('shared').splitlines()
151 151 except IOError as inst:
152 152 if inst.errno != errno.ENOENT:
153 153 raise
154 154 return False
155 155 return hg.sharedbookmarks in shared
156 156
157 157 def _getsrcrepo(repo):
158 158 """
159 159 Returns the source repository object for a given shared repository.
160 160 If repo is not a shared repository, return None.
161 161 """
162 162 if repo.sharedpath == repo.path:
163 163 return None
164 164
165 165 if util.safehasattr(repo, 'srcrepo') and repo.srcrepo:
166 166 return repo.srcrepo
167 167
168 168 # the sharedpath always ends in the .hg; we want the path to the repo
169 169 source = repo.vfs.split(repo.sharedpath)[0]
170 170 srcurl, branches = parseurl(source)
171 171 srcrepo = repository(repo.ui, srcurl)
172 172 repo.srcrepo = srcrepo
173 173 return srcrepo
174 174
175 175 def getbkfile(orig, repo):
176 176 if _hassharedbookmarks(repo):
177 177 srcrepo = _getsrcrepo(repo)
178 178 if srcrepo is not None:
179 179 # just orig(srcrepo) doesn't work as expected, because
180 180 # HG_PENDING refers repo.root.
181 181 try:
182 182 fp, pending = txnutil.trypending(repo.root, repo.vfs,
183 183 'bookmarks')
184 184 if pending:
185 185 # only in this case, bookmark information in repo
186 186 # is up-to-date.
187 187 return fp
188 188 fp.close()
189 189 except IOError as inst:
190 190 if inst.errno != errno.ENOENT:
191 191 raise
192 192
193 193 # otherwise, we should read bookmarks from srcrepo,
194 194 # because .hg/bookmarks in srcrepo might be already
195 195 # changed via another sharing repo
196 196 repo = srcrepo
197 197
198 198 # TODO: Pending changes in repo are still invisible in
199 199 # srcrepo, because bookmarks.pending is written only into repo.
200 200 # See also https://www.mercurial-scm.org/wiki/SharedRepository
201 201 return orig(repo)
202 202
203 203 def recordchange(orig, self, tr):
204 204 # Continue with write to local bookmarks file as usual
205 205 orig(self, tr)
206 206
207 207 if _hassharedbookmarks(self._repo):
208 208 srcrepo = _getsrcrepo(self._repo)
209 209 if srcrepo is not None:
210 210 category = 'share-bookmarks'
211 211 tr.addpostclose(category, lambda tr: self._writerepo(srcrepo))
212 212
213 213 def writerepo(orig, self, repo):
214 214 # First write local bookmarks file in case we ever unshare
215 215 orig(self, repo)
216 216
217 217 if _hassharedbookmarks(self._repo):
218 218 srcrepo = _getsrcrepo(self._repo)
219 219 if srcrepo is not None:
220 220 orig(self, srcrepo)
@@ -1,399 +1,401 b''
1 1 #require killdaemons
2 2
3 3 $ echo "[extensions]" >> $HGRCPATH
4 4 $ echo "share = " >> $HGRCPATH
5 5
6 6 prepare repo1
7 7
8 8 $ hg init repo1
9 9 $ cd repo1
10 10 $ echo a > a
11 11 $ hg commit -A -m'init'
12 12 adding a
13 13
14 14 share it
15 15
16 16 $ cd ..
17 17 $ hg share repo1 repo2
18 18 updating working directory
19 19 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
20 20
21 21 share shouldn't have a store dir
22 22
23 23 $ cd repo2
24 24 $ test -d .hg/store
25 25 [1]
26 26
27 27 Some sed versions appends newline, some don't, and some just fails
28 28
29 29 $ cat .hg/sharedpath; echo
30 30 $TESTTMP/repo1/.hg (glob)
31 31
32 32 trailing newline on .hg/sharedpath is ok
33 33 $ hg tip -q
34 34 0:d3873e73d99e
35 35 $ echo '' >> .hg/sharedpath
36 36 $ cat .hg/sharedpath
37 37 $TESTTMP/repo1/.hg (glob)
38 38 $ hg tip -q
39 39 0:d3873e73d99e
40 40
41 41 commit in shared clone
42 42
43 43 $ echo a >> a
44 44 $ hg commit -m'change in shared clone'
45 45
46 46 check original
47 47
48 48 $ cd ../repo1
49 49 $ hg log
50 50 changeset: 1:8af4dc49db9e
51 51 tag: tip
52 52 user: test
53 53 date: Thu Jan 01 00:00:00 1970 +0000
54 54 summary: change in shared clone
55 55
56 56 changeset: 0:d3873e73d99e
57 57 user: test
58 58 date: Thu Jan 01 00:00:00 1970 +0000
59 59 summary: init
60 60
61 61 $ hg update
62 62 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
63 63 $ cat a # should be two lines of "a"
64 64 a
65 65 a
66 66
67 67 commit in original
68 68
69 69 $ echo b > b
70 70 $ hg commit -A -m'another file'
71 71 adding b
72 72
73 73 check in shared clone
74 74
75 75 $ cd ../repo2
76 76 $ hg log
77 77 changeset: 2:c2e0ac586386
78 78 tag: tip
79 79 user: test
80 80 date: Thu Jan 01 00:00:00 1970 +0000
81 81 summary: another file
82 82
83 83 changeset: 1:8af4dc49db9e
84 84 user: test
85 85 date: Thu Jan 01 00:00:00 1970 +0000
86 86 summary: change in shared clone
87 87
88 88 changeset: 0:d3873e73d99e
89 89 user: test
90 90 date: Thu Jan 01 00:00:00 1970 +0000
91 91 summary: init
92 92
93 93 $ hg update
94 94 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
95 95 $ cat b # should exist with one "b"
96 96 b
97 97
98 98 hg serve shared clone
99 99
100 100 $ hg serve -n test -p $HGPORT -d --pid-file=hg.pid
101 101 $ cat hg.pid >> $DAEMON_PIDS
102 102 $ get-with-headers.py localhost:$HGPORT 'raw-file/'
103 103 200 Script output follows
104 104
105 105
106 106 -rw-r--r-- 4 a
107 107 -rw-r--r-- 2 b
108 108
109 109
110 110
111 111 test unshare command
112 112
113 113 $ hg unshare
114 114 $ test -d .hg/store
115 115 $ test -f .hg/sharedpath
116 116 [1]
117 $ grep shared .hg/requires
118 [1]
117 119 $ hg unshare
118 120 abort: this is not a shared repo
119 121 [255]
120 122
121 123 check that a change does not propagate
122 124
123 125 $ echo b >> b
124 126 $ hg commit -m'change in unshared'
125 127 $ cd ../repo1
126 128 $ hg id -r tip
127 129 c2e0ac586386 tip
128 130
129 131 $ cd ..
130 132
131 133
132 134 test sharing bookmarks
133 135
134 136 $ hg share -B repo1 repo3
135 137 updating working directory
136 138 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
137 139 $ cd repo1
138 140 $ hg bookmark bm1
139 141 $ hg bookmarks
140 142 * bm1 2:c2e0ac586386
141 143 $ cd ../repo2
142 144 $ hg book bm2
143 145 $ hg bookmarks
144 146 * bm2 3:0e6e70d1d5f1
145 147 $ cd ../repo3
146 148 $ hg bookmarks
147 149 bm1 2:c2e0ac586386
148 150 $ hg book bm3
149 151 $ hg bookmarks
150 152 bm1 2:c2e0ac586386
151 153 * bm3 2:c2e0ac586386
152 154 $ cd ../repo1
153 155 $ hg bookmarks
154 156 * bm1 2:c2e0ac586386
155 157 bm3 2:c2e0ac586386
156 158
157 159 check whether HG_PENDING makes pending changes only in relatd
158 160 repositories visible to an external hook.
159 161
160 162 In "hg share" case, another transaction can't run in other
161 163 repositories sharing same source repository, because starting
162 164 transaction requires locking store of source repository.
163 165
164 166 Therefore, this test scenario ignores checking visibility of
165 167 .hg/bookmakrs.pending in repo2, which shares repo1 without bookmarks.
166 168
167 169 $ cat > $TESTTMP/checkbookmarks.sh <<EOF
168 170 > echo "@repo1"
169 171 > hg -R $TESTTMP/repo1 bookmarks
170 172 > echo "@repo2"
171 173 > hg -R $TESTTMP/repo2 bookmarks
172 174 > echo "@repo3"
173 175 > hg -R $TESTTMP/repo3 bookmarks
174 176 > exit 1 # to avoid adding new bookmark for subsequent tests
175 177 > EOF
176 178
177 179 $ cd ../repo1
178 180 $ hg --config hooks.pretxnclose="sh $TESTTMP/checkbookmarks.sh" -q book bmX
179 181 @repo1
180 182 bm1 2:c2e0ac586386
181 183 bm3 2:c2e0ac586386
182 184 * bmX 2:c2e0ac586386
183 185 @repo2
184 186 * bm2 3:0e6e70d1d5f1
185 187 @repo3
186 188 bm1 2:c2e0ac586386
187 189 * bm3 2:c2e0ac586386
188 190 bmX 2:c2e0ac586386
189 191 transaction abort!
190 192 rollback completed
191 193 abort: pretxnclose hook exited with status 1
192 194 [255]
193 195 $ hg book bm1
194 196
195 197 FYI, in contrast to above test, bmX is invisible in repo1 (= shared
196 198 src), because (1) HG_PENDING refers only repo3 and (2)
197 199 "bookmarks.pending" is written only into repo3.
198 200
199 201 $ cd ../repo3
200 202 $ hg --config hooks.pretxnclose="sh $TESTTMP/checkbookmarks.sh" -q book bmX
201 203 @repo1
202 204 * bm1 2:c2e0ac586386
203 205 bm3 2:c2e0ac586386
204 206 @repo2
205 207 * bm2 3:0e6e70d1d5f1
206 208 @repo3
207 209 bm1 2:c2e0ac586386
208 210 bm3 2:c2e0ac586386
209 211 * bmX 2:c2e0ac586386
210 212 transaction abort!
211 213 rollback completed
212 214 abort: pretxnclose hook exited with status 1
213 215 [255]
214 216 $ hg book bm3
215 217
216 218 $ cd ../repo1
217 219
218 220 test that commits work
219 221
220 222 $ echo 'shared bookmarks' > a
221 223 $ hg commit -m 'testing shared bookmarks'
222 224 $ hg bookmarks
223 225 * bm1 3:b87954705719
224 226 bm3 2:c2e0ac586386
225 227 $ cd ../repo3
226 228 $ hg bookmarks
227 229 bm1 3:b87954705719
228 230 * bm3 2:c2e0ac586386
229 231 $ echo 'more shared bookmarks' > a
230 232 $ hg commit -m 'testing shared bookmarks'
231 233 created new head
232 234 $ hg bookmarks
233 235 bm1 3:b87954705719
234 236 * bm3 4:62f4ded848e4
235 237 $ cd ../repo1
236 238 $ hg bookmarks
237 239 * bm1 3:b87954705719
238 240 bm3 4:62f4ded848e4
239 241 $ cd ..
240 242
241 243 test pushing bookmarks works
242 244
243 245 $ hg clone repo3 repo4
244 246 updating to branch default
245 247 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
246 248 $ cd repo4
247 249 $ hg boo bm4
248 250 $ echo foo > b
249 251 $ hg commit -m 'foo in b'
250 252 $ hg boo
251 253 bm1 3:b87954705719
252 254 bm3 4:62f4ded848e4
253 255 * bm4 5:92793bfc8cad
254 256 $ hg push -B bm4
255 257 pushing to $TESTTMP/repo3 (glob)
256 258 searching for changes
257 259 adding changesets
258 260 adding manifests
259 261 adding file changes
260 262 added 1 changesets with 1 changes to 1 files
261 263 exporting bookmark bm4
262 264 $ cd ../repo1
263 265 $ hg bookmarks
264 266 * bm1 3:b87954705719
265 267 bm3 4:62f4ded848e4
266 268 bm4 5:92793bfc8cad
267 269 $ cd ../repo3
268 270 $ hg bookmarks
269 271 bm1 3:b87954705719
270 272 * bm3 4:62f4ded848e4
271 273 bm4 5:92793bfc8cad
272 274 $ cd ..
273 275
274 276 test behavior when sharing a shared repo
275 277
276 278 $ hg share -B repo3 repo5
277 279 updating working directory
278 280 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
279 281 $ cd repo5
280 282 $ hg book
281 283 bm1 3:b87954705719
282 284 bm3 4:62f4ded848e4
283 285 bm4 5:92793bfc8cad
284 286 $ cd ..
285 287
286 288 test what happens when an active bookmark is deleted
287 289
288 290 $ cd repo1
289 291 $ hg boo -d bm3
290 292 $ hg boo
291 293 * bm1 3:b87954705719
292 294 bm4 5:92793bfc8cad
293 295 $ cd ../repo3
294 296 $ hg boo
295 297 bm1 3:b87954705719
296 298 bm4 5:92793bfc8cad
297 299 $ cd ..
298 300
299 301 verify that bookmarks are not written on failed transaction
300 302
301 303 $ cat > failpullbookmarks.py << EOF
302 304 > """A small extension that makes bookmark pulls fail, for testing"""
303 305 > from mercurial import extensions, exchange, error
304 306 > def _pullbookmarks(orig, pullop):
305 307 > orig(pullop)
306 308 > raise error.HookAbort('forced failure by extension')
307 309 > def extsetup(ui):
308 310 > extensions.wrapfunction(exchange, '_pullbookmarks', _pullbookmarks)
309 311 > EOF
310 312 $ cd repo4
311 313 $ hg boo
312 314 bm1 3:b87954705719
313 315 bm3 4:62f4ded848e4
314 316 * bm4 5:92793bfc8cad
315 317 $ cd ../repo3
316 318 $ hg boo
317 319 bm1 3:b87954705719
318 320 bm4 5:92793bfc8cad
319 321 $ hg --config "extensions.failpullbookmarks=$TESTTMP/failpullbookmarks.py" pull $TESTTMP/repo4
320 322 pulling from $TESTTMP/repo4 (glob)
321 323 searching for changes
322 324 no changes found
323 325 adding remote bookmark bm3
324 326 abort: forced failure by extension
325 327 [255]
326 328 $ hg boo
327 329 bm1 3:b87954705719
328 330 bm4 5:92793bfc8cad
329 331 $ hg pull $TESTTMP/repo4
330 332 pulling from $TESTTMP/repo4 (glob)
331 333 searching for changes
332 334 no changes found
333 335 adding remote bookmark bm3
334 336 $ hg boo
335 337 bm1 3:b87954705719
336 338 * bm3 4:62f4ded848e4
337 339 bm4 5:92793bfc8cad
338 340 $ cd ..
339 341
340 342 verify bookmark behavior after unshare
341 343
342 344 $ cd repo3
343 345 $ hg unshare
344 346 $ hg boo
345 347 bm1 3:b87954705719
346 348 * bm3 4:62f4ded848e4
347 349 bm4 5:92793bfc8cad
348 350 $ hg boo -d bm4
349 351 $ hg boo bm5
350 352 $ hg boo
351 353 bm1 3:b87954705719
352 354 bm3 4:62f4ded848e4
353 355 * bm5 4:62f4ded848e4
354 356 $ cd ../repo1
355 357 $ hg boo
356 358 * bm1 3:b87954705719
357 359 bm3 4:62f4ded848e4
358 360 bm4 5:92793bfc8cad
359 361 $ cd ..
360 362
361 363 test shared clones using relative paths work
362 364
363 365 $ mkdir thisdir
364 366 $ hg init thisdir/orig
365 367 $ hg share -U thisdir/orig thisdir/abs
366 368 $ hg share -U --relative thisdir/abs thisdir/rel
367 369 $ cat thisdir/rel/.hg/sharedpath
368 370 ../../orig/.hg (no-eol)
369 371 $ grep shared thisdir/*/.hg/requires
370 372 thisdir/abs/.hg/requires:shared
371 373 thisdir/rel/.hg/requires:shared
372 374 thisdir/rel/.hg/requires:relshared
373 375
374 376 test that relative shared paths aren't relative to $PWD
375 377
376 378 $ cd thisdir
377 379 $ hg -R rel root
378 380 $TESTTMP/thisdir/rel
379 381 $ cd ..
380 382
381 383 now test that relative paths really are relative, survive across
382 384 renames and changes of PWD
383 385
384 386 $ hg -R thisdir/abs root
385 387 $TESTTMP/thisdir/abs
386 388 $ hg -R thisdir/rel root
387 389 $TESTTMP/thisdir/rel
388 390 $ mv thisdir thatdir
389 391 $ hg -R thatdir/abs root
390 392 abort: .hg/sharedpath points to nonexistent directory $TESTTMP/thisdir/orig/.hg!
391 393 [255]
392 394 $ hg -R thatdir/rel root
393 395 $TESTTMP/thatdir/rel
394 396 $ rm -r thatdir
395 397
396 398 Explicitly kill daemons to let the test exit on Windows
397 399
398 400 $ killdaemons.py
399 401
General Comments 0
You need to be logged in to leave comments. Login now