##// END OF EJS Templates
streamclone: clear caches after writing changes into files for visibility...
FUJIWARA Katsunori -
r29919:519a0226 default
parent child Browse files
Show More
@@ -1,383 +1,398 b''
1 1 # streamclone.py - producing and consuming streaming repository data
2 2 #
3 3 # Copyright 2015 Gregory Szorc <gregory.szorc@gmail.com>
4 4 #
5 5 # This software may be used and distributed according to the terms of the
6 6 # GNU General Public License version 2 or any later version.
7 7
8 8 from __future__ import absolute_import
9 9
10 10 import struct
11 11 import time
12 12
13 13 from .i18n import _
14 14 from . import (
15 15 branchmap,
16 16 error,
17 17 store,
18 18 util,
19 19 )
20 20
21 21 def canperformstreamclone(pullop, bailifbundle2supported=False):
22 22 """Whether it is possible to perform a streaming clone as part of pull.
23 23
24 24 ``bailifbundle2supported`` will cause the function to return False if
25 25 bundle2 stream clones are supported. It should only be called by the
26 26 legacy stream clone code path.
27 27
28 28 Returns a tuple of (supported, requirements). ``supported`` is True if
29 29 streaming clone is supported and False otherwise. ``requirements`` is
30 30 a set of repo requirements from the remote, or ``None`` if stream clone
31 31 isn't supported.
32 32 """
33 33 repo = pullop.repo
34 34 remote = pullop.remote
35 35
36 36 bundle2supported = False
37 37 if pullop.canusebundle2:
38 38 if 'v1' in pullop.remotebundle2caps.get('stream', []):
39 39 bundle2supported = True
40 40 # else
41 41 # Server doesn't support bundle2 stream clone or doesn't support
42 42 # the versions we support. Fall back and possibly allow legacy.
43 43
44 44 # Ensures legacy code path uses available bundle2.
45 45 if bailifbundle2supported and bundle2supported:
46 46 return False, None
47 47 # Ensures bundle2 doesn't try to do a stream clone if it isn't supported.
48 48 #elif not bailifbundle2supported and not bundle2supported:
49 49 # return False, None
50 50
51 51 # Streaming clone only works on empty repositories.
52 52 if len(repo):
53 53 return False, None
54 54
55 55 # Streaming clone only works if all data is being requested.
56 56 if pullop.heads:
57 57 return False, None
58 58
59 59 streamrequested = pullop.streamclonerequested
60 60
61 61 # If we don't have a preference, let the server decide for us. This
62 62 # likely only comes into play in LANs.
63 63 if streamrequested is None:
64 64 # The server can advertise whether to prefer streaming clone.
65 65 streamrequested = remote.capable('stream-preferred')
66 66
67 67 if not streamrequested:
68 68 return False, None
69 69
70 70 # In order for stream clone to work, the client has to support all the
71 71 # requirements advertised by the server.
72 72 #
73 73 # The server advertises its requirements via the "stream" and "streamreqs"
74 74 # capability. "stream" (a value-less capability) is advertised if and only
75 75 # if the only requirement is "revlogv1." Else, the "streamreqs" capability
76 76 # is advertised and contains a comma-delimited list of requirements.
77 77 requirements = set()
78 78 if remote.capable('stream'):
79 79 requirements.add('revlogv1')
80 80 else:
81 81 streamreqs = remote.capable('streamreqs')
82 82 # This is weird and shouldn't happen with modern servers.
83 83 if not streamreqs:
84 84 return False, None
85 85
86 86 streamreqs = set(streamreqs.split(','))
87 87 # Server requires something we don't support. Bail.
88 88 if streamreqs - repo.supportedformats:
89 89 return False, None
90 90 requirements = streamreqs
91 91
92 92 return True, requirements
93 93
94 94 def maybeperformlegacystreamclone(pullop):
95 95 """Possibly perform a legacy stream clone operation.
96 96
97 97 Legacy stream clones are performed as part of pull but before all other
98 98 operations.
99 99
100 100 A legacy stream clone will not be performed if a bundle2 stream clone is
101 101 supported.
102 102 """
103 103 supported, requirements = canperformstreamclone(pullop)
104 104
105 105 if not supported:
106 106 return
107 107
108 108 repo = pullop.repo
109 109 remote = pullop.remote
110 110
111 111 # Save remote branchmap. We will use it later to speed up branchcache
112 112 # creation.
113 113 rbranchmap = None
114 114 if remote.capable('branchmap'):
115 115 rbranchmap = remote.branchmap()
116 116
117 117 repo.ui.status(_('streaming all changes\n'))
118 118
119 119 fp = remote.stream_out()
120 120 l = fp.readline()
121 121 try:
122 122 resp = int(l)
123 123 except ValueError:
124 124 raise error.ResponseError(
125 125 _('unexpected response from remote server:'), l)
126 126 if resp == 1:
127 127 raise error.Abort(_('operation forbidden by server'))
128 128 elif resp == 2:
129 129 raise error.Abort(_('locking the remote repository failed'))
130 130 elif resp != 0:
131 131 raise error.Abort(_('the server sent an unknown error code'))
132 132
133 133 l = fp.readline()
134 134 try:
135 135 filecount, bytecount = map(int, l.split(' ', 1))
136 136 except (ValueError, TypeError):
137 137 raise error.ResponseError(
138 138 _('unexpected response from remote server:'), l)
139 139
140 140 with repo.lock():
141 141 consumev1(repo, fp, filecount, bytecount)
142 142
143 143 # new requirements = old non-format requirements +
144 144 # new format-related remote requirements
145 145 # requirements from the streamed-in repository
146 146 repo.requirements = requirements | (
147 147 repo.requirements - repo.supportedformats)
148 148 repo._applyopenerreqs()
149 149 repo._writerequirements()
150 150
151 151 if rbranchmap:
152 152 branchmap.replacecache(repo, rbranchmap)
153 153
154 154 repo.invalidate()
155 155
156 156 def allowservergeneration(ui):
157 157 """Whether streaming clones are allowed from the server."""
158 158 return ui.configbool('server', 'uncompressed', True, untrusted=True)
159 159
160 160 # This is it's own function so extensions can override it.
161 161 def _walkstreamfiles(repo):
162 162 return repo.store.walk()
163 163
164 164 def generatev1(repo):
165 165 """Emit content for version 1 of a streaming clone.
166 166
167 167 This returns a 3-tuple of (file count, byte size, data iterator).
168 168
169 169 The data iterator consists of N entries for each file being transferred.
170 170 Each file entry starts as a line with the file name and integer size
171 171 delimited by a null byte.
172 172
173 173 The raw file data follows. Following the raw file data is the next file
174 174 entry, or EOF.
175 175
176 176 When used on the wire protocol, an additional line indicating protocol
177 177 success will be prepended to the stream. This function is not responsible
178 178 for adding it.
179 179
180 180 This function will obtain a repository lock to ensure a consistent view of
181 181 the store is captured. It therefore may raise LockError.
182 182 """
183 183 entries = []
184 184 total_bytes = 0
185 185 # Get consistent snapshot of repo, lock during scan.
186 186 with repo.lock():
187 187 repo.ui.debug('scanning\n')
188 188 for name, ename, size in _walkstreamfiles(repo):
189 189 if size:
190 190 entries.append((name, size))
191 191 total_bytes += size
192 192
193 193 repo.ui.debug('%d files, %d bytes to transfer\n' %
194 194 (len(entries), total_bytes))
195 195
196 196 svfs = repo.svfs
197 197 oldaudit = svfs.mustaudit
198 198 debugflag = repo.ui.debugflag
199 199 svfs.mustaudit = False
200 200
201 201 def emitrevlogdata():
202 202 try:
203 203 for name, size in entries:
204 204 if debugflag:
205 205 repo.ui.debug('sending %s (%d bytes)\n' % (name, size))
206 206 # partially encode name over the wire for backwards compat
207 207 yield '%s\0%d\n' % (store.encodedir(name), size)
208 208 if size <= 65536:
209 209 with svfs(name, 'rb') as fp:
210 210 yield fp.read(size)
211 211 else:
212 212 for chunk in util.filechunkiter(svfs(name), limit=size):
213 213 yield chunk
214 214 finally:
215 215 svfs.mustaudit = oldaudit
216 216
217 217 return len(entries), total_bytes, emitrevlogdata()
218 218
219 219 def generatev1wireproto(repo):
220 220 """Emit content for version 1 of streaming clone suitable for the wire.
221 221
222 222 This is the data output from ``generatev1()`` with a header line
223 223 indicating file count and byte size.
224 224 """
225 225 filecount, bytecount, it = generatev1(repo)
226 226 yield '%d %d\n' % (filecount, bytecount)
227 227 for chunk in it:
228 228 yield chunk
229 229
230 230 def generatebundlev1(repo, compression='UN'):
231 231 """Emit content for version 1 of a stream clone bundle.
232 232
233 233 The first 4 bytes of the output ("HGS1") denote this as stream clone
234 234 bundle version 1.
235 235
236 236 The next 2 bytes indicate the compression type. Only "UN" is currently
237 237 supported.
238 238
239 239 The next 16 bytes are two 64-bit big endian unsigned integers indicating
240 240 file count and byte count, respectively.
241 241
242 242 The next 2 bytes is a 16-bit big endian unsigned short declaring the length
243 243 of the requirements string, including a trailing \0. The following N bytes
244 244 are the requirements string, which is ASCII containing a comma-delimited
245 245 list of repo requirements that are needed to support the data.
246 246
247 247 The remaining content is the output of ``generatev1()`` (which may be
248 248 compressed in the future).
249 249
250 250 Returns a tuple of (requirements, data generator).
251 251 """
252 252 if compression != 'UN':
253 253 raise ValueError('we do not support the compression argument yet')
254 254
255 255 requirements = repo.requirements & repo.supportedformats
256 256 requires = ','.join(sorted(requirements))
257 257
258 258 def gen():
259 259 yield 'HGS1'
260 260 yield compression
261 261
262 262 filecount, bytecount, it = generatev1(repo)
263 263 repo.ui.status(_('writing %d bytes for %d files\n') %
264 264 (bytecount, filecount))
265 265
266 266 yield struct.pack('>QQ', filecount, bytecount)
267 267 yield struct.pack('>H', len(requires) + 1)
268 268 yield requires + '\0'
269 269
270 270 # This is where we'll add compression in the future.
271 271 assert compression == 'UN'
272 272
273 273 seen = 0
274 274 repo.ui.progress(_('bundle'), 0, total=bytecount, unit=_('bytes'))
275 275
276 276 for chunk in it:
277 277 seen += len(chunk)
278 278 repo.ui.progress(_('bundle'), seen, total=bytecount,
279 279 unit=_('bytes'))
280 280 yield chunk
281 281
282 282 repo.ui.progress(_('bundle'), None)
283 283
284 284 return requirements, gen()
285 285
286 286 def consumev1(repo, fp, filecount, bytecount):
287 287 """Apply the contents from version 1 of a streaming clone file handle.
288 288
289 289 This takes the output from "streamout" and applies it to the specified
290 290 repository.
291 291
292 292 Like "streamout," the status line added by the wire protocol is not handled
293 293 by this function.
294 294 """
295 295 with repo.lock():
296 296 repo.ui.status(_('%d files to transfer, %s of data\n') %
297 297 (filecount, util.bytecount(bytecount)))
298 298 handled_bytes = 0
299 299 repo.ui.progress(_('clone'), 0, total=bytecount, unit=_('bytes'))
300 300 start = time.time()
301 301
302 # TODO: get rid of (potential) inconsistency
303 #
304 # If transaction is started and any @filecache property is
305 # changed at this point, it causes inconsistency between
306 # in-memory cached property and streamclone-ed file on the
307 # disk. Nested transaction prevents transaction scope "clone"
308 # below from writing in-memory changes out at the end of it,
309 # even though in-memory changes are discarded at the end of it
310 # regardless of transaction nesting.
311 #
312 # But transaction nesting can't be simply prohibited, because
313 # nesting occurs also in ordinary case (e.g. enabling
314 # clonebundles).
315
302 316 with repo.transaction('clone'):
303 317 with repo.svfs.backgroundclosing(repo.ui, expectedcount=filecount):
304 318 for i in xrange(filecount):
305 319 # XXX doesn't support '\n' or '\r' in filenames
306 320 l = fp.readline()
307 321 try:
308 322 name, size = l.split('\0', 1)
309 323 size = int(size)
310 324 except (ValueError, TypeError):
311 325 raise error.ResponseError(
312 326 _('unexpected response from remote server:'), l)
313 327 if repo.ui.debugflag:
314 328 repo.ui.debug('adding %s (%s)\n' %
315 329 (name, util.bytecount(size)))
316 330 # for backwards compat, name was partially encoded
317 331 path = store.decodedir(name)
318 332 with repo.svfs(path, 'w', backgroundclose=True) as ofp:
319 333 for chunk in util.filechunkiter(fp, limit=size):
320 334 handled_bytes += len(chunk)
321 335 repo.ui.progress(_('clone'), handled_bytes,
322 336 total=bytecount, unit=_('bytes'))
323 337 ofp.write(chunk)
324 338
325 # Writing straight to files circumvented the inmemory caches
326 repo.invalidate(clearfilecache=True)
339 # force @filecache properties to be reloaded from
340 # streamclone-ed file at next access
341 repo.invalidate(clearfilecache=True)
327 342
328 343 elapsed = time.time() - start
329 344 if elapsed <= 0:
330 345 elapsed = 0.001
331 346 repo.ui.progress(_('clone'), None)
332 347 repo.ui.status(_('transferred %s in %.1f seconds (%s/sec)\n') %
333 348 (util.bytecount(bytecount), elapsed,
334 349 util.bytecount(bytecount / elapsed)))
335 350
336 351 def readbundle1header(fp):
337 352 compression = fp.read(2)
338 353 if compression != 'UN':
339 354 raise error.Abort(_('only uncompressed stream clone bundles are '
340 355 'supported; got %s') % compression)
341 356
342 357 filecount, bytecount = struct.unpack('>QQ', fp.read(16))
343 358 requireslen = struct.unpack('>H', fp.read(2))[0]
344 359 requires = fp.read(requireslen)
345 360
346 361 if not requires.endswith('\0'):
347 362 raise error.Abort(_('malformed stream clone bundle: '
348 363 'requirements not properly encoded'))
349 364
350 365 requirements = set(requires.rstrip('\0').split(','))
351 366
352 367 return filecount, bytecount, requirements
353 368
354 369 def applybundlev1(repo, fp):
355 370 """Apply the content from a stream clone bundle version 1.
356 371
357 372 We assume the 4 byte header has been read and validated and the file handle
358 373 is at the 2 byte compression identifier.
359 374 """
360 375 if len(repo):
361 376 raise error.Abort(_('cannot apply stream clone bundle on non-empty '
362 377 'repo'))
363 378
364 379 filecount, bytecount, requirements = readbundle1header(fp)
365 380 missingreqs = requirements - repo.supportedformats
366 381 if missingreqs:
367 382 raise error.Abort(_('unable to apply stream clone: '
368 383 'unsupported format: %s') %
369 384 ', '.join(sorted(missingreqs)))
370 385
371 386 consumev1(repo, fp, filecount, bytecount)
372 387
373 388 class streamcloneapplier(object):
374 389 """Class to manage applying streaming clone bundles.
375 390
376 391 We need to wrap ``applybundlev1()`` in a dedicated type to enable bundle
377 392 readers to perform bundle type-specific functionality.
378 393 """
379 394 def __init__(self, fh):
380 395 self._fh = fh
381 396
382 397 def apply(self, repo):
383 398 return applybundlev1(repo, self._fh)
@@ -1,809 +1,851 b''
1 1
2 2 $ cat << EOF >> $HGRCPATH
3 3 > [format]
4 4 > usegeneraldelta=yes
5 5 > EOF
6 6
7 7 Setting up test
8 8
9 9 $ hg init test
10 10 $ cd test
11 11 $ echo 0 > afile
12 12 $ hg add afile
13 13 $ hg commit -m "0.0"
14 14 $ echo 1 >> afile
15 15 $ hg commit -m "0.1"
16 16 $ echo 2 >> afile
17 17 $ hg commit -m "0.2"
18 18 $ echo 3 >> afile
19 19 $ hg commit -m "0.3"
20 20 $ hg update -C 0
21 21 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
22 22 $ echo 1 >> afile
23 23 $ hg commit -m "1.1"
24 24 created new head
25 25 $ echo 2 >> afile
26 26 $ hg commit -m "1.2"
27 27 $ echo "a line" > fred
28 28 $ echo 3 >> afile
29 29 $ hg add fred
30 30 $ hg commit -m "1.3"
31 31 $ hg mv afile adifferentfile
32 32 $ hg commit -m "1.3m"
33 33 $ hg update -C 3
34 34 1 files updated, 0 files merged, 2 files removed, 0 files unresolved
35 35 $ hg mv afile anotherfile
36 36 $ hg commit -m "0.3m"
37 37 $ hg verify
38 38 checking changesets
39 39 checking manifests
40 40 crosschecking files in changesets and manifests
41 41 checking files
42 42 4 files, 9 changesets, 7 total revisions
43 43 $ cd ..
44 44 $ hg init empty
45 45
46 46 Bundle and phase
47 47
48 48 $ hg -R test phase --force --secret 0
49 49 $ hg -R test bundle phase.hg empty
50 50 searching for changes
51 51 no changes found (ignored 9 secret changesets)
52 52 [1]
53 53 $ hg -R test phase --draft -r 'head()'
54 54
55 55 Bundle --all
56 56
57 57 $ hg -R test bundle --all all.hg
58 58 9 changesets found
59 59
60 60 Bundle test to full.hg
61 61
62 62 $ hg -R test bundle full.hg empty
63 63 searching for changes
64 64 9 changesets found
65 65
66 66 Unbundle full.hg in test
67 67
68 68 $ hg -R test unbundle full.hg
69 69 adding changesets
70 70 adding manifests
71 71 adding file changes
72 72 added 0 changesets with 0 changes to 4 files
73 73 (run 'hg update' to get a working copy)
74 74
75 75 Verify empty
76 76
77 77 $ hg -R empty heads
78 78 [1]
79 79 $ hg -R empty verify
80 80 checking changesets
81 81 checking manifests
82 82 crosschecking files in changesets and manifests
83 83 checking files
84 84 0 files, 0 changesets, 0 total revisions
85 85
86 86 Pull full.hg into test (using --cwd)
87 87
88 88 $ hg --cwd test pull ../full.hg
89 89 pulling from ../full.hg
90 90 searching for changes
91 91 no changes found
92 92
93 93 Verify that there are no leaked temporary files after pull (issue2797)
94 94
95 95 $ ls test/.hg | grep .hg10un
96 96 [1]
97 97
98 98 Pull full.hg into empty (using --cwd)
99 99
100 100 $ hg --cwd empty pull ../full.hg
101 101 pulling from ../full.hg
102 102 requesting all changes
103 103 adding changesets
104 104 adding manifests
105 105 adding file changes
106 106 added 9 changesets with 7 changes to 4 files (+1 heads)
107 107 (run 'hg heads' to see heads, 'hg merge' to merge)
108 108
109 109 Rollback empty
110 110
111 111 $ hg -R empty rollback
112 112 repository tip rolled back to revision -1 (undo pull)
113 113
114 114 Pull full.hg into empty again (using --cwd)
115 115
116 116 $ hg --cwd empty pull ../full.hg
117 117 pulling from ../full.hg
118 118 requesting all changes
119 119 adding changesets
120 120 adding manifests
121 121 adding file changes
122 122 added 9 changesets with 7 changes to 4 files (+1 heads)
123 123 (run 'hg heads' to see heads, 'hg merge' to merge)
124 124
125 125 Pull full.hg into test (using -R)
126 126
127 127 $ hg -R test pull full.hg
128 128 pulling from full.hg
129 129 searching for changes
130 130 no changes found
131 131
132 132 Pull full.hg into empty (using -R)
133 133
134 134 $ hg -R empty pull full.hg
135 135 pulling from full.hg
136 136 searching for changes
137 137 no changes found
138 138
139 139 Rollback empty
140 140
141 141 $ hg -R empty rollback
142 142 repository tip rolled back to revision -1 (undo pull)
143 143
144 144 Pull full.hg into empty again (using -R)
145 145
146 146 $ hg -R empty pull full.hg
147 147 pulling from full.hg
148 148 requesting all changes
149 149 adding changesets
150 150 adding manifests
151 151 adding file changes
152 152 added 9 changesets with 7 changes to 4 files (+1 heads)
153 153 (run 'hg heads' to see heads, 'hg merge' to merge)
154 154
155 155 Log -R full.hg in fresh empty
156 156
157 157 $ rm -r empty
158 158 $ hg init empty
159 159 $ cd empty
160 160 $ hg -R bundle://../full.hg log
161 161 changeset: 8:aa35859c02ea
162 162 tag: tip
163 163 parent: 3:eebf5a27f8ca
164 164 user: test
165 165 date: Thu Jan 01 00:00:00 1970 +0000
166 166 summary: 0.3m
167 167
168 168 changeset: 7:a6a34bfa0076
169 169 user: test
170 170 date: Thu Jan 01 00:00:00 1970 +0000
171 171 summary: 1.3m
172 172
173 173 changeset: 6:7373c1169842
174 174 user: test
175 175 date: Thu Jan 01 00:00:00 1970 +0000
176 176 summary: 1.3
177 177
178 178 changeset: 5:1bb50a9436a7
179 179 user: test
180 180 date: Thu Jan 01 00:00:00 1970 +0000
181 181 summary: 1.2
182 182
183 183 changeset: 4:095197eb4973
184 184 parent: 0:f9ee2f85a263
185 185 user: test
186 186 date: Thu Jan 01 00:00:00 1970 +0000
187 187 summary: 1.1
188 188
189 189 changeset: 3:eebf5a27f8ca
190 190 user: test
191 191 date: Thu Jan 01 00:00:00 1970 +0000
192 192 summary: 0.3
193 193
194 194 changeset: 2:e38ba6f5b7e0
195 195 user: test
196 196 date: Thu Jan 01 00:00:00 1970 +0000
197 197 summary: 0.2
198 198
199 199 changeset: 1:34c2bf6b0626
200 200 user: test
201 201 date: Thu Jan 01 00:00:00 1970 +0000
202 202 summary: 0.1
203 203
204 204 changeset: 0:f9ee2f85a263
205 205 user: test
206 206 date: Thu Jan 01 00:00:00 1970 +0000
207 207 summary: 0.0
208 208
209 209 Make sure bundlerepo doesn't leak tempfiles (issue2491)
210 210
211 211 $ ls .hg
212 212 00changelog.i
213 213 cache
214 214 requires
215 215 store
216 216
217 217 Pull ../full.hg into empty (with hook)
218 218
219 219 $ echo "[hooks]" >> .hg/hgrc
220 220 $ echo "changegroup = printenv.py changegroup" >> .hg/hgrc
221 221
222 222 doesn't work (yet ?)
223 223
224 224 hg -R bundle://../full.hg verify
225 225
226 226 $ hg pull bundle://../full.hg
227 227 pulling from bundle:../full.hg
228 228 requesting all changes
229 229 adding changesets
230 230 adding manifests
231 231 adding file changes
232 232 added 9 changesets with 7 changes to 4 files (+1 heads)
233 233 changegroup hook: HG_NODE=f9ee2f85a263049e9ae6d37a0e67e96194ffb735 HG_NODE_LAST=aa35859c02ea8bd48da5da68cd2740ac71afcbaf HG_SOURCE=pull HG_TXNID=TXN:* HG_URL=bundle:../full.hg (glob)
234 234 (run 'hg heads' to see heads, 'hg merge' to merge)
235 235
236 236 Rollback empty
237 237
238 238 $ hg rollback
239 239 repository tip rolled back to revision -1 (undo pull)
240 240 $ cd ..
241 241
242 242 Log -R bundle:empty+full.hg
243 243
244 244 $ hg -R bundle:empty+full.hg log --template="{rev} "; echo ""
245 245 8 7 6 5 4 3 2 1 0
246 246
247 247 Pull full.hg into empty again (using -R; with hook)
248 248
249 249 $ hg -R empty pull full.hg
250 250 pulling from full.hg
251 251 requesting all changes
252 252 adding changesets
253 253 adding manifests
254 254 adding file changes
255 255 added 9 changesets with 7 changes to 4 files (+1 heads)
256 256 changegroup hook: HG_NODE=f9ee2f85a263049e9ae6d37a0e67e96194ffb735 HG_NODE_LAST=aa35859c02ea8bd48da5da68cd2740ac71afcbaf HG_SOURCE=pull HG_TXNID=TXN:* HG_URL=bundle:empty+full.hg (glob)
257 257 (run 'hg heads' to see heads, 'hg merge' to merge)
258 258
259 259 Cannot produce streaming clone bundles with "hg bundle"
260 260
261 261 $ hg -R test bundle -t packed1 packed.hg
262 262 abort: packed bundles cannot be produced by "hg bundle"
263 263 (use 'hg debugcreatestreamclonebundle')
264 264 [255]
265 265
266 266 packed1 is produced properly
267 267
268 268 $ hg -R test debugcreatestreamclonebundle packed.hg
269 269 writing 2663 bytes for 6 files
270 270 bundle requirements: generaldelta, revlogv1
271 271
272 272 $ f -B 64 --size --sha1 --hexdump packed.hg
273 273 packed.hg: size=2826, sha1=e139f97692a142b19cdcff64a69697d5307ce6d4
274 274 0000: 48 47 53 31 55 4e 00 00 00 00 00 00 00 06 00 00 |HGS1UN..........|
275 275 0010: 00 00 00 00 0a 67 00 16 67 65 6e 65 72 61 6c 64 |.....g..generald|
276 276 0020: 65 6c 74 61 2c 72 65 76 6c 6f 67 76 31 00 64 61 |elta,revlogv1.da|
277 277 0030: 74 61 2f 61 64 69 66 66 65 72 65 6e 74 66 69 6c |ta/adifferentfil|
278 278
279 279 $ hg debugbundle --spec packed.hg
280 280 none-packed1;requirements%3Dgeneraldelta%2Crevlogv1
281 281
282 282 generaldelta requirement is listed in stream clone bundles
283 283
284 284 $ hg --config format.generaldelta=true init testgd
285 285 $ cd testgd
286 286 $ touch foo
287 287 $ hg -q commit -A -m initial
288 288 $ cd ..
289 289 $ hg -R testgd debugcreatestreamclonebundle packedgd.hg
290 290 writing 301 bytes for 3 files
291 291 bundle requirements: generaldelta, revlogv1
292 292
293 293 $ f -B 64 --size --sha1 --hexdump packedgd.hg
294 294 packedgd.hg: size=396, sha1=981f9e589799335304a5a9a44caa3623a48d2a9f
295 295 0000: 48 47 53 31 55 4e 00 00 00 00 00 00 00 03 00 00 |HGS1UN..........|
296 296 0010: 00 00 00 00 01 2d 00 16 67 65 6e 65 72 61 6c 64 |.....-..generald|
297 297 0020: 65 6c 74 61 2c 72 65 76 6c 6f 67 76 31 00 64 61 |elta,revlogv1.da|
298 298 0030: 74 61 2f 66 6f 6f 2e 69 00 36 34 0a 00 03 00 01 |ta/foo.i.64.....|
299 299
300 300 $ hg debugbundle --spec packedgd.hg
301 301 none-packed1;requirements%3Dgeneraldelta%2Crevlogv1
302 302
303 303 Unpacking packed1 bundles with "hg unbundle" isn't allowed
304 304
305 305 $ hg init packed
306 306 $ hg -R packed unbundle packed.hg
307 307 abort: packed bundles cannot be applied with "hg unbundle"
308 308 (use "hg debugapplystreamclonebundle")
309 309 [255]
310 310
311 311 packed1 can be consumed from debug command
312 312
313 (this also confirms that streamclone-ed changes are visible via
314 @filecache properties to in-process procedures before closing
315 transaction)
316
317 $ cat > $TESTTMP/showtip.py <<EOF
318 > from __future__ import absolute_import
319 >
320 > def showtip(ui, repo, hooktype, **kwargs):
321 > ui.warn('%s: %s\n' % (hooktype, repo['tip'].hex()[:12]))
322 >
323 > def reposetup(ui, repo):
324 > # this confirms (and ensures) that (empty) 00changelog.i
325 > # before streamclone is already cached as repo.changelog
326 > ui.setconfig('hooks', 'pretxnopen.showtip', showtip)
327 >
328 > # this confirms that streamclone-ed changes are visible to
329 > # in-process procedures before closing transaction
330 > ui.setconfig('hooks', 'pretxnclose.showtip', showtip)
331 >
332 > # this confirms that streamclone-ed changes are still visible
333 > # after closing transaction
334 > ui.setconfig('hooks', 'txnclose.showtip', showtip)
335 > EOF
336 $ cat >> $HGRCPATH <<EOF
337 > [extensions]
338 > showtip = $TESTTMP/showtip.py
339 > EOF
340
313 341 $ hg -R packed debugapplystreamclonebundle packed.hg
314 342 6 files to transfer, 2.60 KB of data
343 pretxnopen: 000000000000
344 pretxnclose: aa35859c02ea
315 345 transferred 2.60 KB in *.* seconds (* */sec) (glob)
346 txnclose: aa35859c02ea
347
348 (for safety, confirm visibility of streamclone-ed changes by another
349 process, too)
350
351 $ hg -R packed tip -T "{node|short}\n"
352 aa35859c02ea
353
354 $ cat >> $HGRCPATH <<EOF
355 > [extensions]
356 > showtip = !
357 > EOF
316 358
317 359 Does not work on non-empty repo
318 360
319 361 $ hg -R packed debugapplystreamclonebundle packed.hg
320 362 abort: cannot apply stream clone bundle on non-empty repo
321 363 [255]
322 364
323 365 Create partial clones
324 366
325 367 $ rm -r empty
326 368 $ hg init empty
327 369 $ hg clone -r 3 test partial
328 370 adding changesets
329 371 adding manifests
330 372 adding file changes
331 373 added 4 changesets with 4 changes to 1 files
332 374 updating to branch default
333 375 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
334 376 $ hg clone partial partial2
335 377 updating to branch default
336 378 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
337 379 $ cd partial
338 380
339 381 Log -R full.hg in partial
340 382
341 383 $ hg -R bundle://../full.hg log -T phases
342 384 changeset: 8:aa35859c02ea
343 385 tag: tip
344 386 phase: draft
345 387 parent: 3:eebf5a27f8ca
346 388 user: test
347 389 date: Thu Jan 01 00:00:00 1970 +0000
348 390 summary: 0.3m
349 391
350 392 changeset: 7:a6a34bfa0076
351 393 phase: draft
352 394 user: test
353 395 date: Thu Jan 01 00:00:00 1970 +0000
354 396 summary: 1.3m
355 397
356 398 changeset: 6:7373c1169842
357 399 phase: draft
358 400 user: test
359 401 date: Thu Jan 01 00:00:00 1970 +0000
360 402 summary: 1.3
361 403
362 404 changeset: 5:1bb50a9436a7
363 405 phase: draft
364 406 user: test
365 407 date: Thu Jan 01 00:00:00 1970 +0000
366 408 summary: 1.2
367 409
368 410 changeset: 4:095197eb4973
369 411 phase: draft
370 412 parent: 0:f9ee2f85a263
371 413 user: test
372 414 date: Thu Jan 01 00:00:00 1970 +0000
373 415 summary: 1.1
374 416
375 417 changeset: 3:eebf5a27f8ca
376 418 phase: public
377 419 user: test
378 420 date: Thu Jan 01 00:00:00 1970 +0000
379 421 summary: 0.3
380 422
381 423 changeset: 2:e38ba6f5b7e0
382 424 phase: public
383 425 user: test
384 426 date: Thu Jan 01 00:00:00 1970 +0000
385 427 summary: 0.2
386 428
387 429 changeset: 1:34c2bf6b0626
388 430 phase: public
389 431 user: test
390 432 date: Thu Jan 01 00:00:00 1970 +0000
391 433 summary: 0.1
392 434
393 435 changeset: 0:f9ee2f85a263
394 436 phase: public
395 437 user: test
396 438 date: Thu Jan 01 00:00:00 1970 +0000
397 439 summary: 0.0
398 440
399 441
400 442 Incoming full.hg in partial
401 443
402 444 $ hg incoming bundle://../full.hg
403 445 comparing with bundle:../full.hg
404 446 searching for changes
405 447 changeset: 4:095197eb4973
406 448 parent: 0:f9ee2f85a263
407 449 user: test
408 450 date: Thu Jan 01 00:00:00 1970 +0000
409 451 summary: 1.1
410 452
411 453 changeset: 5:1bb50a9436a7
412 454 user: test
413 455 date: Thu Jan 01 00:00:00 1970 +0000
414 456 summary: 1.2
415 457
416 458 changeset: 6:7373c1169842
417 459 user: test
418 460 date: Thu Jan 01 00:00:00 1970 +0000
419 461 summary: 1.3
420 462
421 463 changeset: 7:a6a34bfa0076
422 464 user: test
423 465 date: Thu Jan 01 00:00:00 1970 +0000
424 466 summary: 1.3m
425 467
426 468 changeset: 8:aa35859c02ea
427 469 tag: tip
428 470 parent: 3:eebf5a27f8ca
429 471 user: test
430 472 date: Thu Jan 01 00:00:00 1970 +0000
431 473 summary: 0.3m
432 474
433 475
434 476 Outgoing -R full.hg vs partial2 in partial
435 477
436 478 $ hg -R bundle://../full.hg outgoing ../partial2
437 479 comparing with ../partial2
438 480 searching for changes
439 481 changeset: 4:095197eb4973
440 482 parent: 0:f9ee2f85a263
441 483 user: test
442 484 date: Thu Jan 01 00:00:00 1970 +0000
443 485 summary: 1.1
444 486
445 487 changeset: 5:1bb50a9436a7
446 488 user: test
447 489 date: Thu Jan 01 00:00:00 1970 +0000
448 490 summary: 1.2
449 491
450 492 changeset: 6:7373c1169842
451 493 user: test
452 494 date: Thu Jan 01 00:00:00 1970 +0000
453 495 summary: 1.3
454 496
455 497 changeset: 7:a6a34bfa0076
456 498 user: test
457 499 date: Thu Jan 01 00:00:00 1970 +0000
458 500 summary: 1.3m
459 501
460 502 changeset: 8:aa35859c02ea
461 503 tag: tip
462 504 parent: 3:eebf5a27f8ca
463 505 user: test
464 506 date: Thu Jan 01 00:00:00 1970 +0000
465 507 summary: 0.3m
466 508
467 509
468 510 Outgoing -R does-not-exist.hg vs partial2 in partial
469 511
470 512 $ hg -R bundle://../does-not-exist.hg outgoing ../partial2
471 513 abort: *../does-not-exist.hg* (glob)
472 514 [255]
473 515 $ cd ..
474 516
475 517 hide outer repo
476 518 $ hg init
477 519
478 520 Direct clone from bundle (all-history)
479 521
480 522 $ hg clone full.hg full-clone
481 523 requesting all changes
482 524 adding changesets
483 525 adding manifests
484 526 adding file changes
485 527 added 9 changesets with 7 changes to 4 files (+1 heads)
486 528 updating to branch default
487 529 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
488 530 $ hg -R full-clone heads
489 531 changeset: 8:aa35859c02ea
490 532 tag: tip
491 533 parent: 3:eebf5a27f8ca
492 534 user: test
493 535 date: Thu Jan 01 00:00:00 1970 +0000
494 536 summary: 0.3m
495 537
496 538 changeset: 7:a6a34bfa0076
497 539 user: test
498 540 date: Thu Jan 01 00:00:00 1970 +0000
499 541 summary: 1.3m
500 542
501 543 $ rm -r full-clone
502 544
503 545 When cloning from a non-copiable repository into '', do not
504 546 recurse infinitely (issue2528)
505 547
506 548 $ hg clone full.hg ''
507 549 abort: empty destination path is not valid
508 550 [255]
509 551
510 552 test for https://bz.mercurial-scm.org/216
511 553
512 554 Unbundle incremental bundles into fresh empty in one go
513 555
514 556 $ rm -r empty
515 557 $ hg init empty
516 558 $ hg -R test bundle --base null -r 0 ../0.hg
517 559 1 changesets found
518 560 $ hg -R test bundle --base 0 -r 1 ../1.hg
519 561 1 changesets found
520 562 $ hg -R empty unbundle -u ../0.hg ../1.hg
521 563 adding changesets
522 564 adding manifests
523 565 adding file changes
524 566 added 1 changesets with 1 changes to 1 files
525 567 adding changesets
526 568 adding manifests
527 569 adding file changes
528 570 added 1 changesets with 1 changes to 1 files
529 571 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
530 572
531 573 View full contents of the bundle
532 574 $ hg -R test bundle --base null -r 3 ../partial.hg
533 575 4 changesets found
534 576 $ cd test
535 577 $ hg -R ../../partial.hg log -r "bundle()"
536 578 changeset: 0:f9ee2f85a263
537 579 user: test
538 580 date: Thu Jan 01 00:00:00 1970 +0000
539 581 summary: 0.0
540 582
541 583 changeset: 1:34c2bf6b0626
542 584 user: test
543 585 date: Thu Jan 01 00:00:00 1970 +0000
544 586 summary: 0.1
545 587
546 588 changeset: 2:e38ba6f5b7e0
547 589 user: test
548 590 date: Thu Jan 01 00:00:00 1970 +0000
549 591 summary: 0.2
550 592
551 593 changeset: 3:eebf5a27f8ca
552 594 user: test
553 595 date: Thu Jan 01 00:00:00 1970 +0000
554 596 summary: 0.3
555 597
556 598 $ cd ..
557 599
558 600 test for 540d1059c802
559 601
560 602 test for 540d1059c802
561 603
562 604 $ hg init orig
563 605 $ cd orig
564 606 $ echo foo > foo
565 607 $ hg add foo
566 608 $ hg ci -m 'add foo'
567 609
568 610 $ hg clone . ../copy
569 611 updating to branch default
570 612 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
571 613 $ hg tag foo
572 614
573 615 $ cd ../copy
574 616 $ echo >> foo
575 617 $ hg ci -m 'change foo'
576 618 $ hg bundle ../bundle.hg ../orig
577 619 searching for changes
578 620 1 changesets found
579 621
580 622 $ cd ../orig
581 623 $ hg incoming ../bundle.hg
582 624 comparing with ../bundle.hg
583 625 searching for changes
584 626 changeset: 2:ed1b79f46b9a
585 627 tag: tip
586 628 parent: 0:bbd179dfa0a7
587 629 user: test
588 630 date: Thu Jan 01 00:00:00 1970 +0000
589 631 summary: change foo
590 632
591 633 $ cd ..
592 634
593 635 test bundle with # in the filename (issue2154):
594 636
595 637 $ cp bundle.hg 'test#bundle.hg'
596 638 $ cd orig
597 639 $ hg incoming '../test#bundle.hg'
598 640 comparing with ../test
599 641 abort: unknown revision 'bundle.hg'!
600 642 [255]
601 643
602 644 note that percent encoding is not handled:
603 645
604 646 $ hg incoming ../test%23bundle.hg
605 647 abort: repository ../test%23bundle.hg not found!
606 648 [255]
607 649 $ cd ..
608 650
609 651 test to bundle revisions on the newly created branch (issue3828):
610 652
611 653 $ hg -q clone -U test test-clone
612 654 $ cd test
613 655
614 656 $ hg -q branch foo
615 657 $ hg commit -m "create foo branch"
616 658 $ hg -q outgoing ../test-clone
617 659 9:b4f5acb1ee27
618 660 $ hg -q bundle --branch foo foo.hg ../test-clone
619 661 $ hg -R foo.hg -q log -r "bundle()"
620 662 9:b4f5acb1ee27
621 663
622 664 $ cd ..
623 665
624 666 test for https://bz.mercurial-scm.org/1144
625 667
626 668 test that verify bundle does not traceback
627 669
628 670 partial history bundle, fails w/ unknown parent
629 671
630 672 $ hg -R bundle.hg verify
631 673 abort: 00changelog.i@bbd179dfa0a7: unknown parent!
632 674 [255]
633 675
634 676 full history bundle, refuses to verify non-local repo
635 677
636 678 $ hg -R all.hg verify
637 679 abort: cannot verify bundle or remote repos
638 680 [255]
639 681
640 682 but, regular verify must continue to work
641 683
642 684 $ hg -R orig verify
643 685 checking changesets
644 686 checking manifests
645 687 crosschecking files in changesets and manifests
646 688 checking files
647 689 2 files, 2 changesets, 2 total revisions
648 690
649 691 diff against bundle
650 692
651 693 $ hg init b
652 694 $ cd b
653 695 $ hg -R ../all.hg diff -r tip
654 696 diff -r aa35859c02ea anotherfile
655 697 --- a/anotherfile Thu Jan 01 00:00:00 1970 +0000
656 698 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
657 699 @@ -1,4 +0,0 @@
658 700 -0
659 701 -1
660 702 -2
661 703 -3
662 704 $ cd ..
663 705
664 706 bundle single branch
665 707
666 708 $ hg init branchy
667 709 $ cd branchy
668 710 $ echo a >a
669 711 $ echo x >x
670 712 $ hg ci -Ama
671 713 adding a
672 714 adding x
673 715 $ echo c >c
674 716 $ echo xx >x
675 717 $ hg ci -Amc
676 718 adding c
677 719 $ echo c1 >c1
678 720 $ hg ci -Amc1
679 721 adding c1
680 722 $ hg up 0
681 723 1 files updated, 0 files merged, 2 files removed, 0 files unresolved
682 724 $ echo b >b
683 725 $ hg ci -Amb
684 726 adding b
685 727 created new head
686 728 $ echo b1 >b1
687 729 $ echo xx >x
688 730 $ hg ci -Amb1
689 731 adding b1
690 732 $ hg clone -q -r2 . part
691 733
692 734 == bundling via incoming
693 735
694 736 $ hg in -R part --bundle incoming.hg --template "{node}\n" .
695 737 comparing with .
696 738 searching for changes
697 739 1a38c1b849e8b70c756d2d80b0b9a3ac0b7ea11a
698 740 057f4db07f61970e1c11e83be79e9d08adc4dc31
699 741
700 742 == bundling
701 743
702 744 $ hg bundle bundle.hg part --debug --config progress.debug=true
703 745 query 1; heads
704 746 searching for changes
705 747 all remote heads known locally
706 748 2 changesets found
707 749 list of changesets:
708 750 1a38c1b849e8b70c756d2d80b0b9a3ac0b7ea11a
709 751 057f4db07f61970e1c11e83be79e9d08adc4dc31
710 752 bundle2-output-bundle: "HG20", (1 params) 1 parts total
711 753 bundle2-output-part: "changegroup" (params: 1 mandatory 1 advisory) streamed payload
712 754 bundling: 1/2 changesets (50.00%)
713 755 bundling: 2/2 changesets (100.00%)
714 756 bundling: 1/2 manifests (50.00%)
715 757 bundling: 2/2 manifests (100.00%)
716 758 bundling: b 1/3 files (33.33%)
717 759 bundling: b1 2/3 files (66.67%)
718 760 bundling: x 3/3 files (100.00%)
719 761
720 762 == Test for issue3441
721 763
722 764 $ hg clone -q -r0 . part2
723 765 $ hg -q -R part2 pull bundle.hg
724 766 $ hg -R part2 verify
725 767 checking changesets
726 768 checking manifests
727 769 crosschecking files in changesets and manifests
728 770 checking files
729 771 4 files, 3 changesets, 5 total revisions
730 772
731 773 == Test bundling no commits
732 774
733 775 $ hg bundle -r 'public()' no-output.hg
734 776 abort: no commits to bundle
735 777 [255]
736 778
737 779 $ cd ..
738 780
739 781 When user merges to the revision existing only in the bundle,
740 782 it should show warning that second parent of the working
741 783 directory does not exist
742 784
743 785 $ hg init update2bundled
744 786 $ cd update2bundled
745 787 $ cat <<EOF >> .hg/hgrc
746 788 > [extensions]
747 789 > strip =
748 790 > EOF
749 791 $ echo "aaa" >> a
750 792 $ hg commit -A -m 0
751 793 adding a
752 794 $ echo "bbb" >> b
753 795 $ hg commit -A -m 1
754 796 adding b
755 797 $ echo "ccc" >> c
756 798 $ hg commit -A -m 2
757 799 adding c
758 800 $ hg update -r 1
759 801 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
760 802 $ echo "ddd" >> d
761 803 $ hg commit -A -m 3
762 804 adding d
763 805 created new head
764 806 $ hg update -r 2
765 807 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
766 808 $ hg log -G
767 809 o changeset: 3:8bd3e1f196af
768 810 | tag: tip
769 811 | parent: 1:a01eca7af26d
770 812 | user: test
771 813 | date: Thu Jan 01 00:00:00 1970 +0000
772 814 | summary: 3
773 815 |
774 816 | @ changeset: 2:4652c276ac4f
775 817 |/ user: test
776 818 | date: Thu Jan 01 00:00:00 1970 +0000
777 819 | summary: 2
778 820 |
779 821 o changeset: 1:a01eca7af26d
780 822 | user: test
781 823 | date: Thu Jan 01 00:00:00 1970 +0000
782 824 | summary: 1
783 825 |
784 826 o changeset: 0:4fe08cd4693e
785 827 user: test
786 828 date: Thu Jan 01 00:00:00 1970 +0000
787 829 summary: 0
788 830
789 831 $ hg bundle --base 1 -r 3 ../update2bundled.hg
790 832 1 changesets found
791 833 $ hg strip -r 3
792 834 saved backup bundle to $TESTTMP/update2bundled/.hg/strip-backup/8bd3e1f196af-017e56d8-backup.hg (glob)
793 835 $ hg merge -R ../update2bundled.hg -r 3
794 836 setting parent to node 8bd3e1f196af289b2b121be08031e76d7ae92098 that only exists in the bundle
795 837 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
796 838 (branch merge, don't forget to commit)
797 839
798 840 When user updates to the revision existing only in the bundle,
799 841 it should show warning
800 842
801 843 $ hg update -R ../update2bundled.hg --clean -r 3
802 844 setting parent to node 8bd3e1f196af289b2b121be08031e76d7ae92098 that only exists in the bundle
803 845 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
804 846
805 847 When user updates to the revision existing in the local repository
806 848 the warning shouldn't be emitted
807 849
808 850 $ hg update -R ../update2bundled.hg -r 0
809 851 0 files updated, 0 files merged, 2 files removed, 0 files unresolved
General Comments 0
You need to be logged in to leave comments. Login now