##// END OF EJS Templates
share: drop 'relshared' requirement as well
Yuya Nishihara -
r31212:344121b3 default
parent child Browse files
Show More
@@ -1,220 +1,221 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 119 repo.requirements.discard('shared')
120 repo.requirements.discard('relshared')
120 121 repo._writerequirements()
121 122 finally:
122 123 destlock and destlock.release()
123 124 lock and lock.release()
124 125
125 126 # update store, spath, svfs and sjoin of repo
126 127 repo.unfiltered().__init__(repo.baseui, repo.root)
127 128
128 129 # Wrap clone command to pass auto share options.
129 130 def clone(orig, ui, source, *args, **opts):
130 131 pool = ui.config('share', 'pool', None)
131 132 if pool:
132 133 pool = util.expandpath(pool)
133 134
134 135 opts['shareopts'] = dict(
135 136 pool=pool,
136 137 mode=ui.config('share', 'poolnaming', 'identity'),
137 138 )
138 139
139 140 return orig(ui, source, *args, **opts)
140 141
141 142 def extsetup(ui):
142 143 extensions.wrapfunction(bookmarks, '_getbkfile', getbkfile)
143 144 extensions.wrapfunction(bookmarks.bmstore, 'recordchange', recordchange)
144 145 extensions.wrapfunction(bookmarks.bmstore, '_writerepo', writerepo)
145 146 extensions.wrapcommand(commands.table, 'clone', clone)
146 147
147 148 def _hassharedbookmarks(repo):
148 149 """Returns whether this repo has shared bookmarks"""
149 150 try:
150 151 shared = repo.vfs.read('shared').splitlines()
151 152 except IOError as inst:
152 153 if inst.errno != errno.ENOENT:
153 154 raise
154 155 return False
155 156 return hg.sharedbookmarks in shared
156 157
157 158 def _getsrcrepo(repo):
158 159 """
159 160 Returns the source repository object for a given shared repository.
160 161 If repo is not a shared repository, return None.
161 162 """
162 163 if repo.sharedpath == repo.path:
163 164 return None
164 165
165 166 if util.safehasattr(repo, 'srcrepo') and repo.srcrepo:
166 167 return repo.srcrepo
167 168
168 169 # the sharedpath always ends in the .hg; we want the path to the repo
169 170 source = repo.vfs.split(repo.sharedpath)[0]
170 171 srcurl, branches = parseurl(source)
171 172 srcrepo = repository(repo.ui, srcurl)
172 173 repo.srcrepo = srcrepo
173 174 return srcrepo
174 175
175 176 def getbkfile(orig, repo):
176 177 if _hassharedbookmarks(repo):
177 178 srcrepo = _getsrcrepo(repo)
178 179 if srcrepo is not None:
179 180 # just orig(srcrepo) doesn't work as expected, because
180 181 # HG_PENDING refers repo.root.
181 182 try:
182 183 fp, pending = txnutil.trypending(repo.root, repo.vfs,
183 184 'bookmarks')
184 185 if pending:
185 186 # only in this case, bookmark information in repo
186 187 # is up-to-date.
187 188 return fp
188 189 fp.close()
189 190 except IOError as inst:
190 191 if inst.errno != errno.ENOENT:
191 192 raise
192 193
193 194 # otherwise, we should read bookmarks from srcrepo,
194 195 # because .hg/bookmarks in srcrepo might be already
195 196 # changed via another sharing repo
196 197 repo = srcrepo
197 198
198 199 # TODO: Pending changes in repo are still invisible in
199 200 # srcrepo, because bookmarks.pending is written only into repo.
200 201 # See also https://www.mercurial-scm.org/wiki/SharedRepository
201 202 return orig(repo)
202 203
203 204 def recordchange(orig, self, tr):
204 205 # Continue with write to local bookmarks file as usual
205 206 orig(self, tr)
206 207
207 208 if _hassharedbookmarks(self._repo):
208 209 srcrepo = _getsrcrepo(self._repo)
209 210 if srcrepo is not None:
210 211 category = 'share-bookmarks'
211 212 tr.addpostclose(category, lambda tr: self._writerepo(srcrepo))
212 213
213 214 def writerepo(orig, self, repo):
214 215 # First write local bookmarks file in case we ever unshare
215 216 orig(self, repo)
216 217
217 218 if _hassharedbookmarks(self._repo):
218 219 srcrepo = _getsrcrepo(self._repo)
219 220 if srcrepo is not None:
220 221 orig(self, srcrepo)
@@ -1,401 +1,416 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 117 $ grep shared .hg/requires
118 118 [1]
119 119 $ hg unshare
120 120 abort: this is not a shared repo
121 121 [255]
122 122
123 123 check that a change does not propagate
124 124
125 125 $ echo b >> b
126 126 $ hg commit -m'change in unshared'
127 127 $ cd ../repo1
128 128 $ hg id -r tip
129 129 c2e0ac586386 tip
130 130
131 131 $ cd ..
132 132
133 133
134 134 test sharing bookmarks
135 135
136 136 $ hg share -B repo1 repo3
137 137 updating working directory
138 138 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
139 139 $ cd repo1
140 140 $ hg bookmark bm1
141 141 $ hg bookmarks
142 142 * bm1 2:c2e0ac586386
143 143 $ cd ../repo2
144 144 $ hg book bm2
145 145 $ hg bookmarks
146 146 * bm2 3:0e6e70d1d5f1
147 147 $ cd ../repo3
148 148 $ hg bookmarks
149 149 bm1 2:c2e0ac586386
150 150 $ hg book bm3
151 151 $ hg bookmarks
152 152 bm1 2:c2e0ac586386
153 153 * bm3 2:c2e0ac586386
154 154 $ cd ../repo1
155 155 $ hg bookmarks
156 156 * bm1 2:c2e0ac586386
157 157 bm3 2:c2e0ac586386
158 158
159 159 check whether HG_PENDING makes pending changes only in relatd
160 160 repositories visible to an external hook.
161 161
162 162 In "hg share" case, another transaction can't run in other
163 163 repositories sharing same source repository, because starting
164 164 transaction requires locking store of source repository.
165 165
166 166 Therefore, this test scenario ignores checking visibility of
167 167 .hg/bookmakrs.pending in repo2, which shares repo1 without bookmarks.
168 168
169 169 $ cat > $TESTTMP/checkbookmarks.sh <<EOF
170 170 > echo "@repo1"
171 171 > hg -R $TESTTMP/repo1 bookmarks
172 172 > echo "@repo2"
173 173 > hg -R $TESTTMP/repo2 bookmarks
174 174 > echo "@repo3"
175 175 > hg -R $TESTTMP/repo3 bookmarks
176 176 > exit 1 # to avoid adding new bookmark for subsequent tests
177 177 > EOF
178 178
179 179 $ cd ../repo1
180 180 $ hg --config hooks.pretxnclose="sh $TESTTMP/checkbookmarks.sh" -q book bmX
181 181 @repo1
182 182 bm1 2:c2e0ac586386
183 183 bm3 2:c2e0ac586386
184 184 * bmX 2:c2e0ac586386
185 185 @repo2
186 186 * bm2 3:0e6e70d1d5f1
187 187 @repo3
188 188 bm1 2:c2e0ac586386
189 189 * bm3 2:c2e0ac586386
190 190 bmX 2:c2e0ac586386
191 191 transaction abort!
192 192 rollback completed
193 193 abort: pretxnclose hook exited with status 1
194 194 [255]
195 195 $ hg book bm1
196 196
197 197 FYI, in contrast to above test, bmX is invisible in repo1 (= shared
198 198 src), because (1) HG_PENDING refers only repo3 and (2)
199 199 "bookmarks.pending" is written only into repo3.
200 200
201 201 $ cd ../repo3
202 202 $ hg --config hooks.pretxnclose="sh $TESTTMP/checkbookmarks.sh" -q book bmX
203 203 @repo1
204 204 * bm1 2:c2e0ac586386
205 205 bm3 2:c2e0ac586386
206 206 @repo2
207 207 * bm2 3:0e6e70d1d5f1
208 208 @repo3
209 209 bm1 2:c2e0ac586386
210 210 bm3 2:c2e0ac586386
211 211 * bmX 2:c2e0ac586386
212 212 transaction abort!
213 213 rollback completed
214 214 abort: pretxnclose hook exited with status 1
215 215 [255]
216 216 $ hg book bm3
217 217
218 218 $ cd ../repo1
219 219
220 220 test that commits work
221 221
222 222 $ echo 'shared bookmarks' > a
223 223 $ hg commit -m 'testing shared bookmarks'
224 224 $ hg bookmarks
225 225 * bm1 3:b87954705719
226 226 bm3 2:c2e0ac586386
227 227 $ cd ../repo3
228 228 $ hg bookmarks
229 229 bm1 3:b87954705719
230 230 * bm3 2:c2e0ac586386
231 231 $ echo 'more shared bookmarks' > a
232 232 $ hg commit -m 'testing shared bookmarks'
233 233 created new head
234 234 $ hg bookmarks
235 235 bm1 3:b87954705719
236 236 * bm3 4:62f4ded848e4
237 237 $ cd ../repo1
238 238 $ hg bookmarks
239 239 * bm1 3:b87954705719
240 240 bm3 4:62f4ded848e4
241 241 $ cd ..
242 242
243 243 test pushing bookmarks works
244 244
245 245 $ hg clone repo3 repo4
246 246 updating to branch default
247 247 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
248 248 $ cd repo4
249 249 $ hg boo bm4
250 250 $ echo foo > b
251 251 $ hg commit -m 'foo in b'
252 252 $ hg boo
253 253 bm1 3:b87954705719
254 254 bm3 4:62f4ded848e4
255 255 * bm4 5:92793bfc8cad
256 256 $ hg push -B bm4
257 257 pushing to $TESTTMP/repo3 (glob)
258 258 searching for changes
259 259 adding changesets
260 260 adding manifests
261 261 adding file changes
262 262 added 1 changesets with 1 changes to 1 files
263 263 exporting bookmark bm4
264 264 $ cd ../repo1
265 265 $ hg bookmarks
266 266 * bm1 3:b87954705719
267 267 bm3 4:62f4ded848e4
268 268 bm4 5:92793bfc8cad
269 269 $ cd ../repo3
270 270 $ hg bookmarks
271 271 bm1 3:b87954705719
272 272 * bm3 4:62f4ded848e4
273 273 bm4 5:92793bfc8cad
274 274 $ cd ..
275 275
276 276 test behavior when sharing a shared repo
277 277
278 278 $ hg share -B repo3 repo5
279 279 updating working directory
280 280 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
281 281 $ cd repo5
282 282 $ hg book
283 283 bm1 3:b87954705719
284 284 bm3 4:62f4ded848e4
285 285 bm4 5:92793bfc8cad
286 286 $ cd ..
287 287
288 288 test what happens when an active bookmark is deleted
289 289
290 290 $ cd repo1
291 291 $ hg boo -d bm3
292 292 $ hg boo
293 293 * bm1 3:b87954705719
294 294 bm4 5:92793bfc8cad
295 295 $ cd ../repo3
296 296 $ hg boo
297 297 bm1 3:b87954705719
298 298 bm4 5:92793bfc8cad
299 299 $ cd ..
300 300
301 301 verify that bookmarks are not written on failed transaction
302 302
303 303 $ cat > failpullbookmarks.py << EOF
304 304 > """A small extension that makes bookmark pulls fail, for testing"""
305 305 > from mercurial import extensions, exchange, error
306 306 > def _pullbookmarks(orig, pullop):
307 307 > orig(pullop)
308 308 > raise error.HookAbort('forced failure by extension')
309 309 > def extsetup(ui):
310 310 > extensions.wrapfunction(exchange, '_pullbookmarks', _pullbookmarks)
311 311 > EOF
312 312 $ cd repo4
313 313 $ hg boo
314 314 bm1 3:b87954705719
315 315 bm3 4:62f4ded848e4
316 316 * bm4 5:92793bfc8cad
317 317 $ cd ../repo3
318 318 $ hg boo
319 319 bm1 3:b87954705719
320 320 bm4 5:92793bfc8cad
321 321 $ hg --config "extensions.failpullbookmarks=$TESTTMP/failpullbookmarks.py" pull $TESTTMP/repo4
322 322 pulling from $TESTTMP/repo4 (glob)
323 323 searching for changes
324 324 no changes found
325 325 adding remote bookmark bm3
326 326 abort: forced failure by extension
327 327 [255]
328 328 $ hg boo
329 329 bm1 3:b87954705719
330 330 bm4 5:92793bfc8cad
331 331 $ hg pull $TESTTMP/repo4
332 332 pulling from $TESTTMP/repo4 (glob)
333 333 searching for changes
334 334 no changes found
335 335 adding remote bookmark bm3
336 336 $ hg boo
337 337 bm1 3:b87954705719
338 338 * bm3 4:62f4ded848e4
339 339 bm4 5:92793bfc8cad
340 340 $ cd ..
341 341
342 342 verify bookmark behavior after unshare
343 343
344 344 $ cd repo3
345 345 $ hg unshare
346 346 $ hg boo
347 347 bm1 3:b87954705719
348 348 * bm3 4:62f4ded848e4
349 349 bm4 5:92793bfc8cad
350 350 $ hg boo -d bm4
351 351 $ hg boo bm5
352 352 $ hg boo
353 353 bm1 3:b87954705719
354 354 bm3 4:62f4ded848e4
355 355 * bm5 4:62f4ded848e4
356 356 $ cd ../repo1
357 357 $ hg boo
358 358 * bm1 3:b87954705719
359 359 bm3 4:62f4ded848e4
360 360 bm4 5:92793bfc8cad
361 361 $ cd ..
362 362
363 363 test shared clones using relative paths work
364 364
365 365 $ mkdir thisdir
366 366 $ hg init thisdir/orig
367 367 $ hg share -U thisdir/orig thisdir/abs
368 368 $ hg share -U --relative thisdir/abs thisdir/rel
369 369 $ cat thisdir/rel/.hg/sharedpath
370 370 ../../orig/.hg (no-eol)
371 371 $ grep shared thisdir/*/.hg/requires
372 372 thisdir/abs/.hg/requires:shared
373 373 thisdir/rel/.hg/requires:shared
374 374 thisdir/rel/.hg/requires:relshared
375 375
376 376 test that relative shared paths aren't relative to $PWD
377 377
378 378 $ cd thisdir
379 379 $ hg -R rel root
380 380 $TESTTMP/thisdir/rel
381 381 $ cd ..
382 382
383 383 now test that relative paths really are relative, survive across
384 384 renames and changes of PWD
385 385
386 386 $ hg -R thisdir/abs root
387 387 $TESTTMP/thisdir/abs
388 388 $ hg -R thisdir/rel root
389 389 $TESTTMP/thisdir/rel
390 390 $ mv thisdir thatdir
391 391 $ hg -R thatdir/abs root
392 392 abort: .hg/sharedpath points to nonexistent directory $TESTTMP/thisdir/orig/.hg!
393 393 [255]
394 394 $ hg -R thatdir/rel root
395 395 $TESTTMP/thatdir/rel
396
397 test unshare relshared repo
398
399 $ cd thatdir/rel
400 $ hg unshare
401 $ test -d .hg/store
402 $ test -f .hg/sharedpath
403 [1]
404 $ grep shared .hg/requires
405 [1]
406 $ hg unshare
407 abort: this is not a shared repo
408 [255]
409 $ cd ../..
410
396 411 $ rm -r thatdir
397 412
398 413 Explicitly kill daemons to let the test exit on Windows
399 414
400 415 $ killdaemons.py
401 416
General Comments 0
You need to be logged in to leave comments. Login now