##// END OF EJS Templates
py3: make test-contrib-perf.t work on python 3...
Pulkit Goyal -
r40250:b456b2e0 default
parent child Browse files
Show More
@@ -1,2123 +1,2126 b''
1 1 # perf.py - performance test routines
2 2 '''helper extension to measure performance'''
3 3
4 4 # "historical portability" policy of perf.py:
5 5 #
6 6 # We have to do:
7 7 # - make perf.py "loadable" with as wide Mercurial version as possible
8 8 # This doesn't mean that perf commands work correctly with that Mercurial.
9 9 # BTW, perf.py itself has been available since 1.1 (or eb240755386d).
10 10 # - make historical perf command work correctly with as wide Mercurial
11 11 # version as possible
12 12 #
13 13 # We have to do, if possible with reasonable cost:
14 14 # - make recent perf command for historical feature work correctly
15 15 # with early Mercurial
16 16 #
17 17 # We don't have to do:
18 18 # - make perf command for recent feature work correctly with early
19 19 # Mercurial
20 20
21 21 from __future__ import absolute_import
22 22 import contextlib
23 23 import functools
24 24 import gc
25 25 import os
26 26 import random
27 27 import struct
28 28 import sys
29 29 import threading
30 30 import time
31 31 from mercurial import (
32 32 changegroup,
33 33 cmdutil,
34 34 commands,
35 35 copies,
36 36 error,
37 37 extensions,
38 38 mdiff,
39 39 merge,
40 40 revlog,
41 41 util,
42 42 )
43 43
44 44 # for "historical portability":
45 45 # try to import modules separately (in dict order), and ignore
46 46 # failure, because these aren't available with early Mercurial
47 47 try:
48 48 from mercurial import branchmap # since 2.5 (or bcee63733aad)
49 49 except ImportError:
50 50 pass
51 51 try:
52 52 from mercurial import obsolete # since 2.3 (or ad0d6c2b3279)
53 53 except ImportError:
54 54 pass
55 55 try:
56 56 from mercurial import registrar # since 3.7 (or 37d50250b696)
57 57 dir(registrar) # forcibly load it
58 58 except ImportError:
59 59 registrar = None
60 60 try:
61 61 from mercurial import repoview # since 2.5 (or 3a6ddacb7198)
62 62 except ImportError:
63 63 pass
64 64 try:
65 65 from mercurial import scmutil # since 1.9 (or 8b252e826c68)
66 66 except ImportError:
67 67 pass
68 68
69 69 def identity(a):
70 70 return a
71 71
72 72 try:
73 73 from mercurial import pycompat
74 74 getargspec = pycompat.getargspec # added to module after 4.5
75 75 _byteskwargs = pycompat.byteskwargs # since 4.1 (or fbc3f73dc802)
76 76 _sysstr = pycompat.sysstr # since 4.0 (or 2219f4f82ede)
77 77 _xrange = pycompat.xrange # since 4.8 (or 7eba8f83129b)
78 fsencode = pycompat.fsencode # since 3.9 (or f4a5e0e86a7e)
78 79 if pycompat.ispy3:
79 80 _maxint = sys.maxsize # per py3 docs for replacing maxint
80 81 else:
81 82 _maxint = sys.maxint
82 83 except (ImportError, AttributeError):
83 84 import inspect
84 85 getargspec = inspect.getargspec
85 86 _byteskwargs = identity
87 fsencode = identity # no py3 support
86 88 _maxint = sys.maxint # no py3 support
87 89 _sysstr = lambda x: x # no py3 support
88 90 _xrange = xrange
89 91
90 92 try:
91 93 # 4.7+
92 94 queue = pycompat.queue.Queue
93 95 except (AttributeError, ImportError):
94 96 # <4.7.
95 97 try:
96 98 queue = pycompat.queue
97 99 except (AttributeError, ImportError):
98 100 queue = util.queue
99 101
100 102 try:
101 103 from mercurial import logcmdutil
102 104 makelogtemplater = logcmdutil.maketemplater
103 105 except (AttributeError, ImportError):
104 106 try:
105 107 makelogtemplater = cmdutil.makelogtemplater
106 108 except (AttributeError, ImportError):
107 109 makelogtemplater = None
108 110
109 111 # for "historical portability":
110 112 # define util.safehasattr forcibly, because util.safehasattr has been
111 113 # available since 1.9.3 (or 94b200a11cf7)
112 114 _undefined = object()
113 115 def safehasattr(thing, attr):
114 116 return getattr(thing, _sysstr(attr), _undefined) is not _undefined
115 117 setattr(util, 'safehasattr', safehasattr)
116 118
117 119 # for "historical portability":
118 120 # define util.timer forcibly, because util.timer has been available
119 121 # since ae5d60bb70c9
120 122 if safehasattr(time, 'perf_counter'):
121 123 util.timer = time.perf_counter
122 124 elif os.name == b'nt':
123 125 util.timer = time.clock
124 126 else:
125 127 util.timer = time.time
126 128
127 129 # for "historical portability":
128 130 # use locally defined empty option list, if formatteropts isn't
129 131 # available, because commands.formatteropts has been available since
130 132 # 3.2 (or 7a7eed5176a4), even though formatting itself has been
131 133 # available since 2.2 (or ae5f92e154d3)
132 134 formatteropts = getattr(cmdutil, "formatteropts",
133 135 getattr(commands, "formatteropts", []))
134 136
135 137 # for "historical portability":
136 138 # use locally defined option list, if debugrevlogopts isn't available,
137 139 # because commands.debugrevlogopts has been available since 3.7 (or
138 140 # 5606f7d0d063), even though cmdutil.openrevlog() has been available
139 141 # since 1.9 (or a79fea6b3e77).
140 142 revlogopts = getattr(cmdutil, "debugrevlogopts",
141 143 getattr(commands, "debugrevlogopts", [
142 144 (b'c', b'changelog', False, (b'open changelog')),
143 145 (b'm', b'manifest', False, (b'open manifest')),
144 146 (b'', b'dir', False, (b'open directory manifest')),
145 147 ]))
146 148
147 149 cmdtable = {}
148 150
149 151 # for "historical portability":
150 152 # define parsealiases locally, because cmdutil.parsealiases has been
151 153 # available since 1.5 (or 6252852b4332)
152 154 def parsealiases(cmd):
153 155 return cmd.lstrip(b"^").split(b"|")
154 156
155 157 if safehasattr(registrar, 'command'):
156 158 command = registrar.command(cmdtable)
157 159 elif safehasattr(cmdutil, 'command'):
158 160 command = cmdutil.command(cmdtable)
159 161 if b'norepo' not in getargspec(command).args:
160 162 # for "historical portability":
161 163 # wrap original cmdutil.command, because "norepo" option has
162 164 # been available since 3.1 (or 75a96326cecb)
163 165 _command = command
164 166 def command(name, options=(), synopsis=None, norepo=False):
165 167 if norepo:
166 168 commands.norepo += b' %s' % b' '.join(parsealiases(name))
167 169 return _command(name, list(options), synopsis)
168 170 else:
169 171 # for "historical portability":
170 172 # define "@command" annotation locally, because cmdutil.command
171 173 # has been available since 1.9 (or 2daa5179e73f)
172 174 def command(name, options=(), synopsis=None, norepo=False):
173 175 def decorator(func):
174 176 if synopsis:
175 177 cmdtable[name] = func, list(options), synopsis
176 178 else:
177 179 cmdtable[name] = func, list(options)
178 180 if norepo:
179 181 commands.norepo += b' %s' % b' '.join(parsealiases(name))
180 182 return func
181 183 return decorator
182 184
183 185 try:
184 186 import mercurial.registrar
185 187 import mercurial.configitems
186 188 configtable = {}
187 189 configitem = mercurial.registrar.configitem(configtable)
188 190 configitem(b'perf', b'presleep',
189 191 default=mercurial.configitems.dynamicdefault,
190 192 )
191 193 configitem(b'perf', b'stub',
192 194 default=mercurial.configitems.dynamicdefault,
193 195 )
194 196 configitem(b'perf', b'parentscount',
195 197 default=mercurial.configitems.dynamicdefault,
196 198 )
197 199 configitem(b'perf', b'all-timing',
198 200 default=mercurial.configitems.dynamicdefault,
199 201 )
200 202 except (ImportError, AttributeError):
201 203 pass
202 204
203 205 def getlen(ui):
204 206 if ui.configbool(b"perf", b"stub", False):
205 207 return lambda x: 1
206 208 return len
207 209
208 210 def gettimer(ui, opts=None):
209 211 """return a timer function and formatter: (timer, formatter)
210 212
211 213 This function exists to gather the creation of formatter in a single
212 214 place instead of duplicating it in all performance commands."""
213 215
214 216 # enforce an idle period before execution to counteract power management
215 217 # experimental config: perf.presleep
216 218 time.sleep(getint(ui, b"perf", b"presleep", 1))
217 219
218 220 if opts is None:
219 221 opts = {}
220 222 # redirect all to stderr unless buffer api is in use
221 223 if not ui._buffers:
222 224 ui = ui.copy()
223 225 uifout = safeattrsetter(ui, b'fout', ignoremissing=True)
224 226 if uifout:
225 227 # for "historical portability":
226 228 # ui.fout/ferr have been available since 1.9 (or 4e1ccd4c2b6d)
227 229 uifout.set(ui.ferr)
228 230
229 231 # get a formatter
230 232 uiformatter = getattr(ui, 'formatter', None)
231 233 if uiformatter:
232 234 fm = uiformatter(b'perf', opts)
233 235 else:
234 236 # for "historical portability":
235 237 # define formatter locally, because ui.formatter has been
236 238 # available since 2.2 (or ae5f92e154d3)
237 239 from mercurial import node
238 240 class defaultformatter(object):
239 241 """Minimized composition of baseformatter and plainformatter
240 242 """
241 243 def __init__(self, ui, topic, opts):
242 244 self._ui = ui
243 245 if ui.debugflag:
244 246 self.hexfunc = node.hex
245 247 else:
246 248 self.hexfunc = node.short
247 249 def __nonzero__(self):
248 250 return False
249 251 __bool__ = __nonzero__
250 252 def startitem(self):
251 253 pass
252 254 def data(self, **data):
253 255 pass
254 256 def write(self, fields, deftext, *fielddata, **opts):
255 257 self._ui.write(deftext % fielddata, **opts)
256 258 def condwrite(self, cond, fields, deftext, *fielddata, **opts):
257 259 if cond:
258 260 self._ui.write(deftext % fielddata, **opts)
259 261 def plain(self, text, **opts):
260 262 self._ui.write(text, **opts)
261 263 def end(self):
262 264 pass
263 265 fm = defaultformatter(ui, b'perf', opts)
264 266
265 267 # stub function, runs code only once instead of in a loop
266 268 # experimental config: perf.stub
267 269 if ui.configbool(b"perf", b"stub", False):
268 270 return functools.partial(stub_timer, fm), fm
269 271
270 272 # experimental config: perf.all-timing
271 273 displayall = ui.configbool(b"perf", b"all-timing", False)
272 274 return functools.partial(_timer, fm, displayall=displayall), fm
273 275
274 276 def stub_timer(fm, func, title=None):
275 277 func()
276 278
277 279 @contextlib.contextmanager
278 280 def timeone():
279 281 r = []
280 282 ostart = os.times()
281 283 cstart = util.timer()
282 284 yield r
283 285 cstop = util.timer()
284 286 ostop = os.times()
285 287 a, b = ostart, ostop
286 288 r.append((cstop - cstart, b[0] - a[0], b[1]-a[1]))
287 289
288 290 def _timer(fm, func, title=None, displayall=False):
289 291 gc.collect()
290 292 results = []
291 293 begin = util.timer()
292 294 count = 0
293 295 while True:
294 296 with timeone() as item:
295 297 r = func()
296 298 count += 1
297 299 results.append(item[0])
298 300 cstop = util.timer()
299 301 if cstop - begin > 3 and count >= 100:
300 302 break
301 303 if cstop - begin > 10 and count >= 3:
302 304 break
303 305
304 306 formatone(fm, results, title=title, result=r,
305 307 displayall=displayall)
306 308
307 309 def formatone(fm, timings, title=None, result=None, displayall=False):
308 310
309 311 count = len(timings)
310 312
311 313 fm.startitem()
312 314
313 315 if title:
314 316 fm.write(b'title', b'! %s\n', title)
315 317 if result:
316 318 fm.write(b'result', b'! result: %s\n', result)
317 319 def display(role, entry):
318 320 prefix = b''
319 321 if role != b'best':
320 322 prefix = b'%s.' % role
321 323 fm.plain(b'!')
322 324 fm.write(prefix + b'wall', b' wall %f', entry[0])
323 325 fm.write(prefix + b'comb', b' comb %f', entry[1] + entry[2])
324 326 fm.write(prefix + b'user', b' user %f', entry[1])
325 327 fm.write(prefix + b'sys', b' sys %f', entry[2])
326 328 fm.write(prefix + b'count', b' (%s of %%d)' % role, count)
327 329 fm.plain(b'\n')
328 330 timings.sort()
329 331 min_val = timings[0]
330 332 display(b'best', min_val)
331 333 if displayall:
332 334 max_val = timings[-1]
333 335 display(b'max', max_val)
334 336 avg = tuple([sum(x) / count for x in zip(*timings)])
335 337 display(b'avg', avg)
336 338 median = timings[len(timings) // 2]
337 339 display(b'median', median)
338 340
339 341 # utilities for historical portability
340 342
341 343 def getint(ui, section, name, default):
342 344 # for "historical portability":
343 345 # ui.configint has been available since 1.9 (or fa2b596db182)
344 346 v = ui.config(section, name, None)
345 347 if v is None:
346 348 return default
347 349 try:
348 350 return int(v)
349 351 except ValueError:
350 352 raise error.ConfigError((b"%s.%s is not an integer ('%s')")
351 353 % (section, name, v))
352 354
353 355 def safeattrsetter(obj, name, ignoremissing=False):
354 356 """Ensure that 'obj' has 'name' attribute before subsequent setattr
355 357
356 358 This function is aborted, if 'obj' doesn't have 'name' attribute
357 359 at runtime. This avoids overlooking removal of an attribute, which
358 360 breaks assumption of performance measurement, in the future.
359 361
360 362 This function returns the object to (1) assign a new value, and
361 363 (2) restore an original value to the attribute.
362 364
363 365 If 'ignoremissing' is true, missing 'name' attribute doesn't cause
364 366 abortion, and this function returns None. This is useful to
365 367 examine an attribute, which isn't ensured in all Mercurial
366 368 versions.
367 369 """
368 370 if not util.safehasattr(obj, name):
369 371 if ignoremissing:
370 372 return None
371 373 raise error.Abort((b"missing attribute %s of %s might break assumption"
372 374 b" of performance measurement") % (name, obj))
373 375
374 376 origvalue = getattr(obj, _sysstr(name))
375 377 class attrutil(object):
376 378 def set(self, newvalue):
377 379 setattr(obj, _sysstr(name), newvalue)
378 380 def restore(self):
379 381 setattr(obj, _sysstr(name), origvalue)
380 382
381 383 return attrutil()
382 384
383 385 # utilities to examine each internal API changes
384 386
385 387 def getbranchmapsubsettable():
386 388 # for "historical portability":
387 389 # subsettable is defined in:
388 390 # - branchmap since 2.9 (or 175c6fd8cacc)
389 391 # - repoview since 2.5 (or 59a9f18d4587)
390 392 for mod in (branchmap, repoview):
391 393 subsettable = getattr(mod, 'subsettable', None)
392 394 if subsettable:
393 395 return subsettable
394 396
395 397 # bisecting in bcee63733aad::59a9f18d4587 can reach here (both
396 398 # branchmap and repoview modules exist, but subsettable attribute
397 399 # doesn't)
398 400 raise error.Abort((b"perfbranchmap not available with this Mercurial"),
399 401 hint=b"use 2.5 or later")
400 402
401 403 def getsvfs(repo):
402 404 """Return appropriate object to access files under .hg/store
403 405 """
404 406 # for "historical portability":
405 407 # repo.svfs has been available since 2.3 (or 7034365089bf)
406 408 svfs = getattr(repo, 'svfs', None)
407 409 if svfs:
408 410 return svfs
409 411 else:
410 412 return getattr(repo, 'sopener')
411 413
412 414 def getvfs(repo):
413 415 """Return appropriate object to access files under .hg
414 416 """
415 417 # for "historical portability":
416 418 # repo.vfs has been available since 2.3 (or 7034365089bf)
417 419 vfs = getattr(repo, 'vfs', None)
418 420 if vfs:
419 421 return vfs
420 422 else:
421 423 return getattr(repo, 'opener')
422 424
423 425 def repocleartagscachefunc(repo):
424 426 """Return the function to clear tags cache according to repo internal API
425 427 """
426 428 if util.safehasattr(repo, b'_tagscache'): # since 2.0 (or 9dca7653b525)
427 429 # in this case, setattr(repo, '_tagscache', None) or so isn't
428 430 # correct way to clear tags cache, because existing code paths
429 431 # expect _tagscache to be a structured object.
430 432 def clearcache():
431 433 # _tagscache has been filteredpropertycache since 2.5 (or
432 434 # 98c867ac1330), and delattr() can't work in such case
433 435 if b'_tagscache' in vars(repo):
434 436 del repo.__dict__[b'_tagscache']
435 437 return clearcache
436 438
437 439 repotags = safeattrsetter(repo, b'_tags', ignoremissing=True)
438 440 if repotags: # since 1.4 (or 5614a628d173)
439 441 return lambda : repotags.set(None)
440 442
441 443 repotagscache = safeattrsetter(repo, b'tagscache', ignoremissing=True)
442 444 if repotagscache: # since 0.6 (or d7df759d0e97)
443 445 return lambda : repotagscache.set(None)
444 446
445 447 # Mercurial earlier than 0.6 (or d7df759d0e97) logically reaches
446 448 # this point, but it isn't so problematic, because:
447 449 # - repo.tags of such Mercurial isn't "callable", and repo.tags()
448 450 # in perftags() causes failure soon
449 451 # - perf.py itself has been available since 1.1 (or eb240755386d)
450 452 raise error.Abort((b"tags API of this hg command is unknown"))
451 453
452 454 # utilities to clear cache
453 455
454 456 def clearfilecache(repo, attrname):
455 457 unfi = repo.unfiltered()
456 458 if attrname in vars(unfi):
457 459 delattr(unfi, attrname)
458 460 unfi._filecache.pop(attrname, None)
459 461
460 462 # perf commands
461 463
462 464 @command(b'perfwalk', formatteropts)
463 465 def perfwalk(ui, repo, *pats, **opts):
464 466 opts = _byteskwargs(opts)
465 467 timer, fm = gettimer(ui, opts)
466 468 m = scmutil.match(repo[None], pats, {})
467 469 timer(lambda: len(list(repo.dirstate.walk(m, subrepos=[], unknown=True,
468 470 ignored=False))))
469 471 fm.end()
470 472
471 473 @command(b'perfannotate', formatteropts)
472 474 def perfannotate(ui, repo, f, **opts):
473 475 opts = _byteskwargs(opts)
474 476 timer, fm = gettimer(ui, opts)
475 477 fc = repo[b'.'][f]
476 478 timer(lambda: len(fc.annotate(True)))
477 479 fm.end()
478 480
479 481 @command(b'perfstatus',
480 482 [(b'u', b'unknown', False,
481 483 b'ask status to look for unknown files')] + formatteropts)
482 484 def perfstatus(ui, repo, **opts):
483 485 opts = _byteskwargs(opts)
484 486 #m = match.always(repo.root, repo.getcwd())
485 487 #timer(lambda: sum(map(len, repo.dirstate.status(m, [], False, False,
486 488 # False))))
487 489 timer, fm = gettimer(ui, opts)
488 490 timer(lambda: sum(map(len, repo.status(unknown=opts[b'unknown']))))
489 491 fm.end()
490 492
491 493 @command(b'perfaddremove', formatteropts)
492 494 def perfaddremove(ui, repo, **opts):
493 495 opts = _byteskwargs(opts)
494 496 timer, fm = gettimer(ui, opts)
495 497 try:
496 498 oldquiet = repo.ui.quiet
497 499 repo.ui.quiet = True
498 500 matcher = scmutil.match(repo[None])
499 501 opts[b'dry_run'] = True
500 502 timer(lambda: scmutil.addremove(repo, matcher, b"", opts))
501 503 finally:
502 504 repo.ui.quiet = oldquiet
503 505 fm.end()
504 506
505 507 def clearcaches(cl):
506 508 # behave somewhat consistently across internal API changes
507 509 if util.safehasattr(cl, b'clearcaches'):
508 510 cl.clearcaches()
509 511 elif util.safehasattr(cl, b'_nodecache'):
510 512 from mercurial.node import nullid, nullrev
511 513 cl._nodecache = {nullid: nullrev}
512 514 cl._nodepos = None
513 515
514 516 @command(b'perfheads', formatteropts)
515 517 def perfheads(ui, repo, **opts):
516 518 opts = _byteskwargs(opts)
517 519 timer, fm = gettimer(ui, opts)
518 520 cl = repo.changelog
519 521 def d():
520 522 len(cl.headrevs())
521 523 clearcaches(cl)
522 524 timer(d)
523 525 fm.end()
524 526
525 527 @command(b'perftags', formatteropts)
526 528 def perftags(ui, repo, **opts):
527 529 import mercurial.changelog
528 530 import mercurial.manifest
529 531
530 532 opts = _byteskwargs(opts)
531 533 timer, fm = gettimer(ui, opts)
532 534 svfs = getsvfs(repo)
533 535 repocleartagscache = repocleartagscachefunc(repo)
534 536 def t():
535 537 repo.changelog = mercurial.changelog.changelog(svfs)
536 538 rootmanifest = mercurial.manifest.manifestrevlog(svfs)
537 539 repo.manifestlog = mercurial.manifest.manifestlog(svfs, repo,
538 540 rootmanifest)
539 541 repocleartagscache()
540 542 return len(repo.tags())
541 543 timer(t)
542 544 fm.end()
543 545
544 546 @command(b'perfancestors', formatteropts)
545 547 def perfancestors(ui, repo, **opts):
546 548 opts = _byteskwargs(opts)
547 549 timer, fm = gettimer(ui, opts)
548 550 heads = repo.changelog.headrevs()
549 551 def d():
550 552 for a in repo.changelog.ancestors(heads):
551 553 pass
552 554 timer(d)
553 555 fm.end()
554 556
555 557 @command(b'perfancestorset', formatteropts)
556 558 def perfancestorset(ui, repo, revset, **opts):
557 559 opts = _byteskwargs(opts)
558 560 timer, fm = gettimer(ui, opts)
559 561 revs = repo.revs(revset)
560 562 heads = repo.changelog.headrevs()
561 563 def d():
562 564 s = repo.changelog.ancestors(heads)
563 565 for rev in revs:
564 566 rev in s
565 567 timer(d)
566 568 fm.end()
567 569
568 570 @command(b'perfbookmarks', formatteropts)
569 571 def perfbookmarks(ui, repo, **opts):
570 572 """benchmark parsing bookmarks from disk to memory"""
571 573 opts = _byteskwargs(opts)
572 574 timer, fm = gettimer(ui, opts)
573 575 def d():
574 576 clearfilecache(repo, b'_bookmarks')
575 577 repo._bookmarks
576 578 timer(d)
577 579 fm.end()
578 580
579 581 @command(b'perfbundleread', formatteropts, b'BUNDLE')
580 582 def perfbundleread(ui, repo, bundlepath, **opts):
581 583 """Benchmark reading of bundle files.
582 584
583 585 This command is meant to isolate the I/O part of bundle reading as
584 586 much as possible.
585 587 """
586 588 from mercurial import (
587 589 bundle2,
588 590 exchange,
589 591 streamclone,
590 592 )
591 593
592 594 opts = _byteskwargs(opts)
593 595
594 596 def makebench(fn):
595 597 def run():
596 598 with open(bundlepath, b'rb') as fh:
597 599 bundle = exchange.readbundle(ui, fh, bundlepath)
598 600 fn(bundle)
599 601
600 602 return run
601 603
602 604 def makereadnbytes(size):
603 605 def run():
604 606 with open(bundlepath, b'rb') as fh:
605 607 bundle = exchange.readbundle(ui, fh, bundlepath)
606 608 while bundle.read(size):
607 609 pass
608 610
609 611 return run
610 612
611 613 def makestdioread(size):
612 614 def run():
613 615 with open(bundlepath, b'rb') as fh:
614 616 while fh.read(size):
615 617 pass
616 618
617 619 return run
618 620
619 621 # bundle1
620 622
621 623 def deltaiter(bundle):
622 624 for delta in bundle.deltaiter():
623 625 pass
624 626
625 627 def iterchunks(bundle):
626 628 for chunk in bundle.getchunks():
627 629 pass
628 630
629 631 # bundle2
630 632
631 633 def forwardchunks(bundle):
632 634 for chunk in bundle._forwardchunks():
633 635 pass
634 636
635 637 def iterparts(bundle):
636 638 for part in bundle.iterparts():
637 639 pass
638 640
639 641 def iterpartsseekable(bundle):
640 642 for part in bundle.iterparts(seekable=True):
641 643 pass
642 644
643 645 def seek(bundle):
644 646 for part in bundle.iterparts(seekable=True):
645 647 part.seek(0, os.SEEK_END)
646 648
647 649 def makepartreadnbytes(size):
648 650 def run():
649 651 with open(bundlepath, b'rb') as fh:
650 652 bundle = exchange.readbundle(ui, fh, bundlepath)
651 653 for part in bundle.iterparts():
652 654 while part.read(size):
653 655 pass
654 656
655 657 return run
656 658
657 659 benches = [
658 660 (makestdioread(8192), b'read(8k)'),
659 661 (makestdioread(16384), b'read(16k)'),
660 662 (makestdioread(32768), b'read(32k)'),
661 663 (makestdioread(131072), b'read(128k)'),
662 664 ]
663 665
664 666 with open(bundlepath, b'rb') as fh:
665 667 bundle = exchange.readbundle(ui, fh, bundlepath)
666 668
667 669 if isinstance(bundle, changegroup.cg1unpacker):
668 670 benches.extend([
669 671 (makebench(deltaiter), b'cg1 deltaiter()'),
670 672 (makebench(iterchunks), b'cg1 getchunks()'),
671 673 (makereadnbytes(8192), b'cg1 read(8k)'),
672 674 (makereadnbytes(16384), b'cg1 read(16k)'),
673 675 (makereadnbytes(32768), b'cg1 read(32k)'),
674 676 (makereadnbytes(131072), b'cg1 read(128k)'),
675 677 ])
676 678 elif isinstance(bundle, bundle2.unbundle20):
677 679 benches.extend([
678 680 (makebench(forwardchunks), b'bundle2 forwardchunks()'),
679 681 (makebench(iterparts), b'bundle2 iterparts()'),
680 682 (makebench(iterpartsseekable), b'bundle2 iterparts() seekable'),
681 683 (makebench(seek), b'bundle2 part seek()'),
682 684 (makepartreadnbytes(8192), b'bundle2 part read(8k)'),
683 685 (makepartreadnbytes(16384), b'bundle2 part read(16k)'),
684 686 (makepartreadnbytes(32768), b'bundle2 part read(32k)'),
685 687 (makepartreadnbytes(131072), b'bundle2 part read(128k)'),
686 688 ])
687 689 elif isinstance(bundle, streamclone.streamcloneapplier):
688 690 raise error.Abort(b'stream clone bundles not supported')
689 691 else:
690 692 raise error.Abort(b'unhandled bundle type: %s' % type(bundle))
691 693
692 694 for fn, title in benches:
693 695 timer, fm = gettimer(ui, opts)
694 696 timer(fn, title=title)
695 697 fm.end()
696 698
697 699 @command(b'perfchangegroupchangelog', formatteropts +
698 700 [(b'', b'version', b'02', b'changegroup version'),
699 701 (b'r', b'rev', b'', b'revisions to add to changegroup')])
700 702 def perfchangegroupchangelog(ui, repo, version=b'02', rev=None, **opts):
701 703 """Benchmark producing a changelog group for a changegroup.
702 704
703 705 This measures the time spent processing the changelog during a
704 706 bundle operation. This occurs during `hg bundle` and on a server
705 707 processing a `getbundle` wire protocol request (handles clones
706 708 and pull requests).
707 709
708 710 By default, all revisions are added to the changegroup.
709 711 """
710 712 opts = _byteskwargs(opts)
711 713 cl = repo.changelog
712 714 nodes = [cl.lookup(r) for r in repo.revs(rev or b'all()')]
713 715 bundler = changegroup.getbundler(version, repo)
714 716
715 717 def d():
716 718 state, chunks = bundler._generatechangelog(cl, nodes)
717 719 for chunk in chunks:
718 720 pass
719 721
720 722 timer, fm = gettimer(ui, opts)
721 723
722 724 # Terminal printing can interfere with timing. So disable it.
723 725 with ui.configoverride({(b'progress', b'disable'): True}):
724 726 timer(d)
725 727
726 728 fm.end()
727 729
728 730 @command(b'perfdirs', formatteropts)
729 731 def perfdirs(ui, repo, **opts):
730 732 opts = _byteskwargs(opts)
731 733 timer, fm = gettimer(ui, opts)
732 734 dirstate = repo.dirstate
733 735 b'a' in dirstate
734 736 def d():
735 737 dirstate.hasdir(b'a')
736 738 del dirstate._map._dirs
737 739 timer(d)
738 740 fm.end()
739 741
740 742 @command(b'perfdirstate', formatteropts)
741 743 def perfdirstate(ui, repo, **opts):
742 744 opts = _byteskwargs(opts)
743 745 timer, fm = gettimer(ui, opts)
744 746 b"a" in repo.dirstate
745 747 def d():
746 748 repo.dirstate.invalidate()
747 749 b"a" in repo.dirstate
748 750 timer(d)
749 751 fm.end()
750 752
751 753 @command(b'perfdirstatedirs', formatteropts)
752 754 def perfdirstatedirs(ui, repo, **opts):
753 755 opts = _byteskwargs(opts)
754 756 timer, fm = gettimer(ui, opts)
755 757 b"a" in repo.dirstate
756 758 def d():
757 759 repo.dirstate.hasdir(b"a")
758 760 del repo.dirstate._map._dirs
759 761 timer(d)
760 762 fm.end()
761 763
762 764 @command(b'perfdirstatefoldmap', formatteropts)
763 765 def perfdirstatefoldmap(ui, repo, **opts):
764 766 opts = _byteskwargs(opts)
765 767 timer, fm = gettimer(ui, opts)
766 768 dirstate = repo.dirstate
767 769 b'a' in dirstate
768 770 def d():
769 771 dirstate._map.filefoldmap.get(b'a')
770 772 del dirstate._map.filefoldmap
771 773 timer(d)
772 774 fm.end()
773 775
774 776 @command(b'perfdirfoldmap', formatteropts)
775 777 def perfdirfoldmap(ui, repo, **opts):
776 778 opts = _byteskwargs(opts)
777 779 timer, fm = gettimer(ui, opts)
778 780 dirstate = repo.dirstate
779 781 b'a' in dirstate
780 782 def d():
781 783 dirstate._map.dirfoldmap.get(b'a')
782 784 del dirstate._map.dirfoldmap
783 785 del dirstate._map._dirs
784 786 timer(d)
785 787 fm.end()
786 788
787 789 @command(b'perfdirstatewrite', formatteropts)
788 790 def perfdirstatewrite(ui, repo, **opts):
789 791 opts = _byteskwargs(opts)
790 792 timer, fm = gettimer(ui, opts)
791 793 ds = repo.dirstate
792 794 b"a" in ds
793 795 def d():
794 796 ds._dirty = True
795 797 ds.write(repo.currenttransaction())
796 798 timer(d)
797 799 fm.end()
798 800
799 801 @command(b'perfmergecalculate',
800 802 [(b'r', b'rev', b'.', b'rev to merge against')] + formatteropts)
801 803 def perfmergecalculate(ui, repo, rev, **opts):
802 804 opts = _byteskwargs(opts)
803 805 timer, fm = gettimer(ui, opts)
804 806 wctx = repo[None]
805 807 rctx = scmutil.revsingle(repo, rev, rev)
806 808 ancestor = wctx.ancestor(rctx)
807 809 # we don't want working dir files to be stat'd in the benchmark, so prime
808 810 # that cache
809 811 wctx.dirty()
810 812 def d():
811 813 # acceptremote is True because we don't want prompts in the middle of
812 814 # our benchmark
813 815 merge.calculateupdates(repo, wctx, rctx, [ancestor], False, False,
814 816 acceptremote=True, followcopies=True)
815 817 timer(d)
816 818 fm.end()
817 819
818 820 @command(b'perfpathcopies', [], b"REV REV")
819 821 def perfpathcopies(ui, repo, rev1, rev2, **opts):
820 822 opts = _byteskwargs(opts)
821 823 timer, fm = gettimer(ui, opts)
822 824 ctx1 = scmutil.revsingle(repo, rev1, rev1)
823 825 ctx2 = scmutil.revsingle(repo, rev2, rev2)
824 826 def d():
825 827 copies.pathcopies(ctx1, ctx2)
826 828 timer(d)
827 829 fm.end()
828 830
829 831 @command(b'perfphases',
830 832 [(b'', b'full', False, b'include file reading time too'),
831 833 ], b"")
832 834 def perfphases(ui, repo, **opts):
833 835 """benchmark phasesets computation"""
834 836 opts = _byteskwargs(opts)
835 837 timer, fm = gettimer(ui, opts)
836 838 _phases = repo._phasecache
837 839 full = opts.get(b'full')
838 840 def d():
839 841 phases = _phases
840 842 if full:
841 843 clearfilecache(repo, b'_phasecache')
842 844 phases = repo._phasecache
843 845 phases.invalidate()
844 846 phases.loadphaserevs(repo)
845 847 timer(d)
846 848 fm.end()
847 849
848 850 @command(b'perfphasesremote',
849 851 [], b"[DEST]")
850 852 def perfphasesremote(ui, repo, dest=None, **opts):
851 853 """benchmark time needed to analyse phases of the remote server"""
852 854 from mercurial.node import (
853 855 bin,
854 856 )
855 857 from mercurial import (
856 858 exchange,
857 859 hg,
858 860 phases,
859 861 )
860 862 opts = _byteskwargs(opts)
861 863 timer, fm = gettimer(ui, opts)
862 864
863 865 path = ui.paths.getpath(dest, default=(b'default-push', b'default'))
864 866 if not path:
865 867 raise error.Abort((b'default repository not configured!'),
866 868 hint=(b"see 'hg help config.paths'"))
867 869 dest = path.pushloc or path.loc
868 870 branches = (path.branch, opts.get(b'branch') or [])
869 871 ui.status((b'analysing phase of %s\n') % util.hidepassword(dest))
870 872 revs, checkout = hg.addbranchrevs(repo, repo, branches, opts.get(b'rev'))
871 873 other = hg.peer(repo, opts, dest)
872 874
873 875 # easier to perform discovery through the operation
874 876 op = exchange.pushoperation(repo, other)
875 877 exchange._pushdiscoverychangeset(op)
876 878
877 879 remotesubset = op.fallbackheads
878 880
879 881 with other.commandexecutor() as e:
880 882 remotephases = e.callcommand(b'listkeys',
881 883 {b'namespace': b'phases'}).result()
882 884 del other
883 885 publishing = remotephases.get(b'publishing', False)
884 886 if publishing:
885 887 ui.status((b'publishing: yes\n'))
886 888 else:
887 889 ui.status((b'publishing: no\n'))
888 890
889 891 nodemap = repo.changelog.nodemap
890 892 nonpublishroots = 0
891 893 for nhex, phase in remotephases.iteritems():
892 894 if nhex == b'publishing': # ignore data related to publish option
893 895 continue
894 896 node = bin(nhex)
895 897 if node in nodemap and int(phase):
896 898 nonpublishroots += 1
897 899 ui.status((b'number of roots: %d\n') % len(remotephases))
898 900 ui.status((b'number of known non public roots: %d\n') % nonpublishroots)
899 901 def d():
900 902 phases.remotephasessummary(repo,
901 903 remotesubset,
902 904 remotephases)
903 905 timer(d)
904 906 fm.end()
905 907
906 908 @command(b'perfmanifest',[
907 909 (b'm', b'manifest-rev', False, b'Look up a manifest node revision'),
908 910 (b'', b'clear-disk', False, b'clear on-disk caches too'),
909 911 ] + formatteropts, b'REV|NODE')
910 912 def perfmanifest(ui, repo, rev, manifest_rev=False, clear_disk=False, **opts):
911 913 """benchmark the time to read a manifest from disk and return a usable
912 914 dict-like object
913 915
914 916 Manifest caches are cleared before retrieval."""
915 917 opts = _byteskwargs(opts)
916 918 timer, fm = gettimer(ui, opts)
917 919 if not manifest_rev:
918 920 ctx = scmutil.revsingle(repo, rev, rev)
919 921 t = ctx.manifestnode()
920 922 else:
921 923 from mercurial.node import bin
922 924
923 925 if len(rev) == 40:
924 926 t = bin(rev)
925 927 else:
926 928 try:
927 929 rev = int(rev)
928 930
929 931 if util.safehasattr(repo.manifestlog, b'getstorage'):
930 932 t = repo.manifestlog.getstorage(b'').node(rev)
931 933 else:
932 934 t = repo.manifestlog._revlog.lookup(rev)
933 935 except ValueError:
934 936 raise error.Abort(b'manifest revision must be integer or full '
935 937 b'node')
936 938 def d():
937 939 repo.manifestlog.clearcaches(clear_persisted_data=clear_disk)
938 940 repo.manifestlog[t].read()
939 941 timer(d)
940 942 fm.end()
941 943
942 944 @command(b'perfchangeset', formatteropts)
943 945 def perfchangeset(ui, repo, rev, **opts):
944 946 opts = _byteskwargs(opts)
945 947 timer, fm = gettimer(ui, opts)
946 948 n = scmutil.revsingle(repo, rev).node()
947 949 def d():
948 950 repo.changelog.read(n)
949 951 #repo.changelog._cache = None
950 952 timer(d)
951 953 fm.end()
952 954
953 955 @command(b'perfindex', formatteropts)
954 956 def perfindex(ui, repo, **opts):
955 957 import mercurial.revlog
956 958 opts = _byteskwargs(opts)
957 959 timer, fm = gettimer(ui, opts)
958 960 mercurial.revlog._prereadsize = 2**24 # disable lazy parser in old hg
959 961 n = repo[b"tip"].node()
960 962 svfs = getsvfs(repo)
961 963 def d():
962 964 cl = mercurial.revlog.revlog(svfs, b"00changelog.i")
963 965 cl.rev(n)
964 966 timer(d)
965 967 fm.end()
966 968
967 969 @command(b'perfstartup', formatteropts)
968 970 def perfstartup(ui, repo, **opts):
969 971 opts = _byteskwargs(opts)
970 972 timer, fm = gettimer(ui, opts)
971 cmd = sys.argv[0]
973 cmd = fsencode(sys.argv[0])
972 974 def d():
973 975 if os.name != r'nt':
974 976 os.system(b"HGRCPATH= %s version -q > /dev/null" % cmd)
975 977 else:
976 978 os.environ[r'HGRCPATH'] = r' '
977 979 os.system(r"%s version -q > NUL" % cmd)
978 980 timer(d)
979 981 fm.end()
980 982
981 983 @command(b'perfparents', formatteropts)
982 984 def perfparents(ui, repo, **opts):
983 985 opts = _byteskwargs(opts)
984 986 timer, fm = gettimer(ui, opts)
985 987 # control the number of commits perfparents iterates over
986 988 # experimental config: perf.parentscount
987 989 count = getint(ui, b"perf", b"parentscount", 1000)
988 990 if len(repo.changelog) < count:
989 991 raise error.Abort(b"repo needs %d commits for this test" % count)
990 992 repo = repo.unfiltered()
991 993 nl = [repo.changelog.node(i) for i in _xrange(count)]
992 994 def d():
993 995 for n in nl:
994 996 repo.changelog.parents(n)
995 997 timer(d)
996 998 fm.end()
997 999
998 1000 @command(b'perfctxfiles', formatteropts)
999 1001 def perfctxfiles(ui, repo, x, **opts):
1000 1002 opts = _byteskwargs(opts)
1001 1003 x = int(x)
1002 1004 timer, fm = gettimer(ui, opts)
1003 1005 def d():
1004 1006 len(repo[x].files())
1005 1007 timer(d)
1006 1008 fm.end()
1007 1009
1008 1010 @command(b'perfrawfiles', formatteropts)
1009 1011 def perfrawfiles(ui, repo, x, **opts):
1010 1012 opts = _byteskwargs(opts)
1011 1013 x = int(x)
1012 1014 timer, fm = gettimer(ui, opts)
1013 1015 cl = repo.changelog
1014 1016 def d():
1015 1017 len(cl.read(x)[3])
1016 1018 timer(d)
1017 1019 fm.end()
1018 1020
1019 1021 @command(b'perflookup', formatteropts)
1020 1022 def perflookup(ui, repo, rev, **opts):
1021 1023 opts = _byteskwargs(opts)
1022 1024 timer, fm = gettimer(ui, opts)
1023 1025 timer(lambda: len(repo.lookup(rev)))
1024 1026 fm.end()
1025 1027
1026 1028 @command(b'perflinelogedits',
1027 1029 [(b'n', b'edits', 10000, b'number of edits'),
1028 1030 (b'', b'max-hunk-lines', 10, b'max lines in a hunk'),
1029 1031 ], norepo=True)
1030 1032 def perflinelogedits(ui, **opts):
1031 1033 from mercurial import linelog
1032 1034
1033 1035 opts = _byteskwargs(opts)
1034 1036
1035 1037 edits = opts[b'edits']
1036 1038 maxhunklines = opts[b'max_hunk_lines']
1037 1039
1038 1040 maxb1 = 100000
1039 1041 random.seed(0)
1040 1042 randint = random.randint
1041 1043 currentlines = 0
1042 1044 arglist = []
1043 1045 for rev in _xrange(edits):
1044 1046 a1 = randint(0, currentlines)
1045 1047 a2 = randint(a1, min(currentlines, a1 + maxhunklines))
1046 1048 b1 = randint(0, maxb1)
1047 1049 b2 = randint(b1, b1 + maxhunklines)
1048 1050 currentlines += (b2 - b1) - (a2 - a1)
1049 1051 arglist.append((rev, a1, a2, b1, b2))
1050 1052
1051 1053 def d():
1052 1054 ll = linelog.linelog()
1053 1055 for args in arglist:
1054 1056 ll.replacelines(*args)
1055 1057
1056 1058 timer, fm = gettimer(ui, opts)
1057 1059 timer(d)
1058 1060 fm.end()
1059 1061
1060 1062 @command(b'perfrevrange', formatteropts)
1061 1063 def perfrevrange(ui, repo, *specs, **opts):
1062 1064 opts = _byteskwargs(opts)
1063 1065 timer, fm = gettimer(ui, opts)
1064 1066 revrange = scmutil.revrange
1065 1067 timer(lambda: len(revrange(repo, specs)))
1066 1068 fm.end()
1067 1069
1068 1070 @command(b'perfnodelookup', formatteropts)
1069 1071 def perfnodelookup(ui, repo, rev, **opts):
1070 1072 opts = _byteskwargs(opts)
1071 1073 timer, fm = gettimer(ui, opts)
1072 1074 import mercurial.revlog
1073 1075 mercurial.revlog._prereadsize = 2**24 # disable lazy parser in old hg
1074 1076 n = scmutil.revsingle(repo, rev).node()
1075 1077 cl = mercurial.revlog.revlog(getsvfs(repo), b"00changelog.i")
1076 1078 def d():
1077 1079 cl.rev(n)
1078 1080 clearcaches(cl)
1079 1081 timer(d)
1080 1082 fm.end()
1081 1083
1082 1084 @command(b'perflog',
1083 1085 [(b'', b'rename', False, b'ask log to follow renames')
1084 1086 ] + formatteropts)
1085 1087 def perflog(ui, repo, rev=None, **opts):
1086 1088 opts = _byteskwargs(opts)
1087 1089 if rev is None:
1088 1090 rev=[]
1089 1091 timer, fm = gettimer(ui, opts)
1090 1092 ui.pushbuffer()
1091 1093 timer(lambda: commands.log(ui, repo, rev=rev, date=b'', user=b'',
1092 1094 copies=opts.get(b'rename')))
1093 1095 ui.popbuffer()
1094 1096 fm.end()
1095 1097
1096 1098 @command(b'perfmoonwalk', formatteropts)
1097 1099 def perfmoonwalk(ui, repo, **opts):
1098 1100 """benchmark walking the changelog backwards
1099 1101
1100 1102 This also loads the changelog data for each revision in the changelog.
1101 1103 """
1102 1104 opts = _byteskwargs(opts)
1103 1105 timer, fm = gettimer(ui, opts)
1104 1106 def moonwalk():
1105 1107 for i in repo.changelog.revs(start=(len(repo) - 1), stop=-1):
1106 1108 ctx = repo[i]
1107 1109 ctx.branch() # read changelog data (in addition to the index)
1108 1110 timer(moonwalk)
1109 1111 fm.end()
1110 1112
1111 1113 @command(b'perftemplating',
1112 1114 [(b'r', b'rev', [], b'revisions to run the template on'),
1113 1115 ] + formatteropts)
1114 1116 def perftemplating(ui, repo, testedtemplate=None, **opts):
1115 1117 """test the rendering time of a given template"""
1116 1118 if makelogtemplater is None:
1117 1119 raise error.Abort((b"perftemplating not available with this Mercurial"),
1118 1120 hint=b"use 4.3 or later")
1119 1121
1120 1122 opts = _byteskwargs(opts)
1121 1123
1122 1124 nullui = ui.copy()
1123 1125 nullui.fout = open(os.devnull, r'wb')
1124 1126 nullui.disablepager()
1125 1127 revs = opts.get(b'rev')
1126 1128 if not revs:
1127 1129 revs = [b'all()']
1128 1130 revs = list(scmutil.revrange(repo, revs))
1129 1131
1130 1132 defaulttemplate = (b'{date|shortdate} [{rev}:{node|short}]'
1131 1133 b' {author|person}: {desc|firstline}\n')
1132 1134 if testedtemplate is None:
1133 1135 testedtemplate = defaulttemplate
1134 1136 displayer = makelogtemplater(nullui, repo, testedtemplate)
1135 1137 def format():
1136 1138 for r in revs:
1137 1139 ctx = repo[r]
1138 1140 displayer.show(ctx)
1139 1141 displayer.flush(ctx)
1140 1142
1141 1143 timer, fm = gettimer(ui, opts)
1142 1144 timer(format)
1143 1145 fm.end()
1144 1146
1145 1147 @command(b'perfcca', formatteropts)
1146 1148 def perfcca(ui, repo, **opts):
1147 1149 opts = _byteskwargs(opts)
1148 1150 timer, fm = gettimer(ui, opts)
1149 1151 timer(lambda: scmutil.casecollisionauditor(ui, False, repo.dirstate))
1150 1152 fm.end()
1151 1153
1152 1154 @command(b'perffncacheload', formatteropts)
1153 1155 def perffncacheload(ui, repo, **opts):
1154 1156 opts = _byteskwargs(opts)
1155 1157 timer, fm = gettimer(ui, opts)
1156 1158 s = repo.store
1157 1159 def d():
1158 1160 s.fncache._load()
1159 1161 timer(d)
1160 1162 fm.end()
1161 1163
1162 1164 @command(b'perffncachewrite', formatteropts)
1163 1165 def perffncachewrite(ui, repo, **opts):
1164 1166 opts = _byteskwargs(opts)
1165 1167 timer, fm = gettimer(ui, opts)
1166 1168 s = repo.store
1167 1169 lock = repo.lock()
1168 1170 s.fncache._load()
1169 1171 tr = repo.transaction(b'perffncachewrite')
1170 1172 tr.addbackup(b'fncache')
1171 1173 def d():
1172 1174 s.fncache._dirty = True
1173 1175 s.fncache.write(tr)
1174 1176 timer(d)
1175 1177 tr.close()
1176 1178 lock.release()
1177 1179 fm.end()
1178 1180
1179 1181 @command(b'perffncacheencode', formatteropts)
1180 1182 def perffncacheencode(ui, repo, **opts):
1181 1183 opts = _byteskwargs(opts)
1182 1184 timer, fm = gettimer(ui, opts)
1183 1185 s = repo.store
1184 1186 s.fncache._load()
1185 1187 def d():
1186 1188 for p in s.fncache.entries:
1187 1189 s.encode(p)
1188 1190 timer(d)
1189 1191 fm.end()
1190 1192
1191 1193 def _bdiffworker(q, blocks, xdiff, ready, done):
1192 1194 while not done.is_set():
1193 1195 pair = q.get()
1194 1196 while pair is not None:
1195 1197 if xdiff:
1196 1198 mdiff.bdiff.xdiffblocks(*pair)
1197 1199 elif blocks:
1198 1200 mdiff.bdiff.blocks(*pair)
1199 1201 else:
1200 1202 mdiff.textdiff(*pair)
1201 1203 q.task_done()
1202 1204 pair = q.get()
1203 1205 q.task_done() # for the None one
1204 1206 with ready:
1205 1207 ready.wait()
1206 1208
1207 1209 def _manifestrevision(repo, mnode):
1208 1210 ml = repo.manifestlog
1209 1211
1210 1212 if util.safehasattr(ml, b'getstorage'):
1211 1213 store = ml.getstorage(b'')
1212 1214 else:
1213 1215 store = ml._revlog
1214 1216
1215 1217 return store.revision(mnode)
1216 1218
1217 1219 @command(b'perfbdiff', revlogopts + formatteropts + [
1218 1220 (b'', b'count', 1, b'number of revisions to test (when using --startrev)'),
1219 1221 (b'', b'alldata', False, b'test bdiffs for all associated revisions'),
1220 1222 (b'', b'threads', 0, b'number of thread to use (disable with 0)'),
1221 1223 (b'', b'blocks', False, b'test computing diffs into blocks'),
1222 1224 (b'', b'xdiff', False, b'use xdiff algorithm'),
1223 1225 ],
1224 1226
1225 1227 b'-c|-m|FILE REV')
1226 1228 def perfbdiff(ui, repo, file_, rev=None, count=None, threads=0, **opts):
1227 1229 """benchmark a bdiff between revisions
1228 1230
1229 1231 By default, benchmark a bdiff between its delta parent and itself.
1230 1232
1231 1233 With ``--count``, benchmark bdiffs between delta parents and self for N
1232 1234 revisions starting at the specified revision.
1233 1235
1234 1236 With ``--alldata``, assume the requested revision is a changeset and
1235 1237 measure bdiffs for all changes related to that changeset (manifest
1236 1238 and filelogs).
1237 1239 """
1238 1240 opts = _byteskwargs(opts)
1239 1241
1240 1242 if opts[b'xdiff'] and not opts[b'blocks']:
1241 1243 raise error.CommandError(b'perfbdiff', b'--xdiff requires --blocks')
1242 1244
1243 1245 if opts[b'alldata']:
1244 1246 opts[b'changelog'] = True
1245 1247
1246 1248 if opts.get(b'changelog') or opts.get(b'manifest'):
1247 1249 file_, rev = None, file_
1248 1250 elif rev is None:
1249 1251 raise error.CommandError(b'perfbdiff', b'invalid arguments')
1250 1252
1251 1253 blocks = opts[b'blocks']
1252 1254 xdiff = opts[b'xdiff']
1253 1255 textpairs = []
1254 1256
1255 1257 r = cmdutil.openrevlog(repo, b'perfbdiff', file_, opts)
1256 1258
1257 1259 startrev = r.rev(r.lookup(rev))
1258 1260 for rev in range(startrev, min(startrev + count, len(r) - 1)):
1259 1261 if opts[b'alldata']:
1260 1262 # Load revisions associated with changeset.
1261 1263 ctx = repo[rev]
1262 1264 mtext = _manifestrevision(repo, ctx.manifestnode())
1263 1265 for pctx in ctx.parents():
1264 1266 pman = _manifestrevision(repo, pctx.manifestnode())
1265 1267 textpairs.append((pman, mtext))
1266 1268
1267 1269 # Load filelog revisions by iterating manifest delta.
1268 1270 man = ctx.manifest()
1269 1271 pman = ctx.p1().manifest()
1270 1272 for filename, change in pman.diff(man).items():
1271 1273 fctx = repo.file(filename)
1272 1274 f1 = fctx.revision(change[0][0] or -1)
1273 1275 f2 = fctx.revision(change[1][0] or -1)
1274 1276 textpairs.append((f1, f2))
1275 1277 else:
1276 1278 dp = r.deltaparent(rev)
1277 1279 textpairs.append((r.revision(dp), r.revision(rev)))
1278 1280
1279 1281 withthreads = threads > 0
1280 1282 if not withthreads:
1281 1283 def d():
1282 1284 for pair in textpairs:
1283 1285 if xdiff:
1284 1286 mdiff.bdiff.xdiffblocks(*pair)
1285 1287 elif blocks:
1286 1288 mdiff.bdiff.blocks(*pair)
1287 1289 else:
1288 1290 mdiff.textdiff(*pair)
1289 1291 else:
1290 1292 q = queue()
1291 1293 for i in _xrange(threads):
1292 1294 q.put(None)
1293 1295 ready = threading.Condition()
1294 1296 done = threading.Event()
1295 1297 for i in _xrange(threads):
1296 1298 threading.Thread(target=_bdiffworker,
1297 1299 args=(q, blocks, xdiff, ready, done)).start()
1298 1300 q.join()
1299 1301 def d():
1300 1302 for pair in textpairs:
1301 1303 q.put(pair)
1302 1304 for i in _xrange(threads):
1303 1305 q.put(None)
1304 1306 with ready:
1305 1307 ready.notify_all()
1306 1308 q.join()
1307 1309 timer, fm = gettimer(ui, opts)
1308 1310 timer(d)
1309 1311 fm.end()
1310 1312
1311 1313 if withthreads:
1312 1314 done.set()
1313 1315 for i in _xrange(threads):
1314 1316 q.put(None)
1315 1317 with ready:
1316 1318 ready.notify_all()
1317 1319
1318 1320 @command(b'perfunidiff', revlogopts + formatteropts + [
1319 1321 (b'', b'count', 1, b'number of revisions to test (when using --startrev)'),
1320 1322 (b'', b'alldata', False, b'test unidiffs for all associated revisions'),
1321 1323 ], b'-c|-m|FILE REV')
1322 1324 def perfunidiff(ui, repo, file_, rev=None, count=None, **opts):
1323 1325 """benchmark a unified diff between revisions
1324 1326
1325 1327 This doesn't include any copy tracing - it's just a unified diff
1326 1328 of the texts.
1327 1329
1328 1330 By default, benchmark a diff between its delta parent and itself.
1329 1331
1330 1332 With ``--count``, benchmark diffs between delta parents and self for N
1331 1333 revisions starting at the specified revision.
1332 1334
1333 1335 With ``--alldata``, assume the requested revision is a changeset and
1334 1336 measure diffs for all changes related to that changeset (manifest
1335 1337 and filelogs).
1336 1338 """
1337 1339 opts = _byteskwargs(opts)
1338 1340 if opts[b'alldata']:
1339 1341 opts[b'changelog'] = True
1340 1342
1341 1343 if opts.get(b'changelog') or opts.get(b'manifest'):
1342 1344 file_, rev = None, file_
1343 1345 elif rev is None:
1344 1346 raise error.CommandError(b'perfunidiff', b'invalid arguments')
1345 1347
1346 1348 textpairs = []
1347 1349
1348 1350 r = cmdutil.openrevlog(repo, b'perfunidiff', file_, opts)
1349 1351
1350 1352 startrev = r.rev(r.lookup(rev))
1351 1353 for rev in range(startrev, min(startrev + count, len(r) - 1)):
1352 1354 if opts[b'alldata']:
1353 1355 # Load revisions associated with changeset.
1354 1356 ctx = repo[rev]
1355 1357 mtext = _manifestrevision(repo, ctx.manifestnode())
1356 1358 for pctx in ctx.parents():
1357 1359 pman = _manifestrevision(repo, pctx.manifestnode())
1358 1360 textpairs.append((pman, mtext))
1359 1361
1360 1362 # Load filelog revisions by iterating manifest delta.
1361 1363 man = ctx.manifest()
1362 1364 pman = ctx.p1().manifest()
1363 1365 for filename, change in pman.diff(man).items():
1364 1366 fctx = repo.file(filename)
1365 1367 f1 = fctx.revision(change[0][0] or -1)
1366 1368 f2 = fctx.revision(change[1][0] or -1)
1367 1369 textpairs.append((f1, f2))
1368 1370 else:
1369 1371 dp = r.deltaparent(rev)
1370 1372 textpairs.append((r.revision(dp), r.revision(rev)))
1371 1373
1372 1374 def d():
1373 1375 for left, right in textpairs:
1374 1376 # The date strings don't matter, so we pass empty strings.
1375 1377 headerlines, hunks = mdiff.unidiff(
1376 1378 left, b'', right, b'', b'left', b'right', binary=False)
1377 1379 # consume iterators in roughly the way patch.py does
1378 1380 b'\n'.join(headerlines)
1379 1381 b''.join(sum((list(hlines) for hrange, hlines in hunks), []))
1380 1382 timer, fm = gettimer(ui, opts)
1381 1383 timer(d)
1382 1384 fm.end()
1383 1385
1384 1386 @command(b'perfdiffwd', formatteropts)
1385 1387 def perfdiffwd(ui, repo, **opts):
1386 1388 """Profile diff of working directory changes"""
1387 1389 opts = _byteskwargs(opts)
1388 1390 timer, fm = gettimer(ui, opts)
1389 1391 options = {
1390 b'w': b'ignore_all_space',
1391 b'b': b'ignore_space_change',
1392 b'B': b'ignore_blank_lines',
1392 'w': 'ignore_all_space',
1393 'b': 'ignore_space_change',
1394 'B': 'ignore_blank_lines',
1393 1395 }
1394 1396
1395 for diffopt in (b'', b'w', b'b', b'B', b'wB'):
1397 for diffopt in ('', 'w', 'b', 'B', 'wB'):
1396 1398 opts = dict((options[c], b'1') for c in diffopt)
1397 1399 def d():
1398 1400 ui.pushbuffer()
1399 1401 commands.diff(ui, repo, **opts)
1400 1402 ui.popbuffer()
1403 diffopt = diffopt.encode('ascii')
1401 1404 title = b'diffopts: %s' % (diffopt and (b'-' + diffopt) or b'none')
1402 1405 timer(d, title)
1403 1406 fm.end()
1404 1407
1405 1408 @command(b'perfrevlogindex', revlogopts + formatteropts,
1406 1409 b'-c|-m|FILE')
1407 1410 def perfrevlogindex(ui, repo, file_=None, **opts):
1408 1411 """Benchmark operations against a revlog index.
1409 1412
1410 1413 This tests constructing a revlog instance, reading index data,
1411 1414 parsing index data, and performing various operations related to
1412 1415 index data.
1413 1416 """
1414 1417
1415 1418 opts = _byteskwargs(opts)
1416 1419
1417 1420 rl = cmdutil.openrevlog(repo, b'perfrevlogindex', file_, opts)
1418 1421
1419 1422 opener = getattr(rl, 'opener') # trick linter
1420 1423 indexfile = rl.indexfile
1421 1424 data = opener.read(indexfile)
1422 1425
1423 1426 header = struct.unpack(b'>I', data[0:4])[0]
1424 1427 version = header & 0xFFFF
1425 1428 if version == 1:
1426 1429 revlogio = revlog.revlogio()
1427 1430 inline = header & (1 << 16)
1428 1431 else:
1429 1432 raise error.Abort((b'unsupported revlog version: %d') % version)
1430 1433
1431 1434 rllen = len(rl)
1432 1435
1433 1436 node0 = rl.node(0)
1434 1437 node25 = rl.node(rllen // 4)
1435 1438 node50 = rl.node(rllen // 2)
1436 1439 node75 = rl.node(rllen // 4 * 3)
1437 1440 node100 = rl.node(rllen - 1)
1438 1441
1439 1442 allrevs = range(rllen)
1440 1443 allrevsrev = list(reversed(allrevs))
1441 1444 allnodes = [rl.node(rev) for rev in range(rllen)]
1442 1445 allnodesrev = list(reversed(allnodes))
1443 1446
1444 1447 def constructor():
1445 1448 revlog.revlog(opener, indexfile)
1446 1449
1447 1450 def read():
1448 1451 with opener(indexfile) as fh:
1449 1452 fh.read()
1450 1453
1451 1454 def parseindex():
1452 1455 revlogio.parseindex(data, inline)
1453 1456
1454 1457 def getentry(revornode):
1455 1458 index = revlogio.parseindex(data, inline)[0]
1456 1459 index[revornode]
1457 1460
1458 1461 def getentries(revs, count=1):
1459 1462 index = revlogio.parseindex(data, inline)[0]
1460 1463
1461 1464 for i in range(count):
1462 1465 for rev in revs:
1463 1466 index[rev]
1464 1467
1465 1468 def resolvenode(node):
1466 1469 nodemap = revlogio.parseindex(data, inline)[1]
1467 1470 # This only works for the C code.
1468 1471 if nodemap is None:
1469 1472 return
1470 1473
1471 1474 try:
1472 1475 nodemap[node]
1473 1476 except error.RevlogError:
1474 1477 pass
1475 1478
1476 1479 def resolvenodes(nodes, count=1):
1477 1480 nodemap = revlogio.parseindex(data, inline)[1]
1478 1481 if nodemap is None:
1479 1482 return
1480 1483
1481 1484 for i in range(count):
1482 1485 for node in nodes:
1483 1486 try:
1484 1487 nodemap[node]
1485 1488 except error.RevlogError:
1486 1489 pass
1487 1490
1488 1491 benches = [
1489 1492 (constructor, b'revlog constructor'),
1490 1493 (read, b'read'),
1491 1494 (parseindex, b'create index object'),
1492 1495 (lambda: getentry(0), b'retrieve index entry for rev 0'),
1493 1496 (lambda: resolvenode(b'a' * 20), b'look up missing node'),
1494 1497 (lambda: resolvenode(node0), b'look up node at rev 0'),
1495 1498 (lambda: resolvenode(node25), b'look up node at 1/4 len'),
1496 1499 (lambda: resolvenode(node50), b'look up node at 1/2 len'),
1497 1500 (lambda: resolvenode(node75), b'look up node at 3/4 len'),
1498 1501 (lambda: resolvenode(node100), b'look up node at tip'),
1499 1502 # 2x variation is to measure caching impact.
1500 1503 (lambda: resolvenodes(allnodes),
1501 1504 b'look up all nodes (forward)'),
1502 1505 (lambda: resolvenodes(allnodes, 2),
1503 1506 b'look up all nodes 2x (forward)'),
1504 1507 (lambda: resolvenodes(allnodesrev),
1505 1508 b'look up all nodes (reverse)'),
1506 1509 (lambda: resolvenodes(allnodesrev, 2),
1507 1510 b'look up all nodes 2x (reverse)'),
1508 1511 (lambda: getentries(allrevs),
1509 1512 b'retrieve all index entries (forward)'),
1510 1513 (lambda: getentries(allrevs, 2),
1511 1514 b'retrieve all index entries 2x (forward)'),
1512 1515 (lambda: getentries(allrevsrev),
1513 1516 b'retrieve all index entries (reverse)'),
1514 1517 (lambda: getentries(allrevsrev, 2),
1515 1518 b'retrieve all index entries 2x (reverse)'),
1516 1519 ]
1517 1520
1518 1521 for fn, title in benches:
1519 1522 timer, fm = gettimer(ui, opts)
1520 1523 timer(fn, title=title)
1521 1524 fm.end()
1522 1525
1523 1526 @command(b'perfrevlogrevisions', revlogopts + formatteropts +
1524 1527 [(b'd', b'dist', 100, b'distance between the revisions'),
1525 1528 (b's', b'startrev', 0, b'revision to start reading at'),
1526 1529 (b'', b'reverse', False, b'read in reverse')],
1527 1530 b'-c|-m|FILE')
1528 1531 def perfrevlogrevisions(ui, repo, file_=None, startrev=0, reverse=False,
1529 1532 **opts):
1530 1533 """Benchmark reading a series of revisions from a revlog.
1531 1534
1532 1535 By default, we read every ``-d/--dist`` revision from 0 to tip of
1533 1536 the specified revlog.
1534 1537
1535 1538 The start revision can be defined via ``-s/--startrev``.
1536 1539 """
1537 1540 opts = _byteskwargs(opts)
1538 1541
1539 1542 rl = cmdutil.openrevlog(repo, b'perfrevlogrevisions', file_, opts)
1540 1543 rllen = getlen(ui)(rl)
1541 1544
1542 1545 if startrev < 0:
1543 1546 startrev = rllen + startrev
1544 1547
1545 1548 def d():
1546 1549 rl.clearcaches()
1547 1550
1548 1551 beginrev = startrev
1549 1552 endrev = rllen
1550 1553 dist = opts[b'dist']
1551 1554
1552 1555 if reverse:
1553 1556 beginrev, endrev = endrev, beginrev
1554 1557 dist = -1 * dist
1555 1558
1556 1559 for x in _xrange(beginrev, endrev, dist):
1557 1560 # Old revisions don't support passing int.
1558 1561 n = rl.node(x)
1559 1562 rl.revision(n)
1560 1563
1561 1564 timer, fm = gettimer(ui, opts)
1562 1565 timer(d)
1563 1566 fm.end()
1564 1567
1565 1568 @command(b'perfrevlogchunks', revlogopts + formatteropts +
1566 1569 [(b'e', b'engines', b'', b'compression engines to use'),
1567 1570 (b's', b'startrev', 0, b'revision to start at')],
1568 1571 b'-c|-m|FILE')
1569 1572 def perfrevlogchunks(ui, repo, file_=None, engines=None, startrev=0, **opts):
1570 1573 """Benchmark operations on revlog chunks.
1571 1574
1572 1575 Logically, each revlog is a collection of fulltext revisions. However,
1573 1576 stored within each revlog are "chunks" of possibly compressed data. This
1574 1577 data needs to be read and decompressed or compressed and written.
1575 1578
1576 1579 This command measures the time it takes to read+decompress and recompress
1577 1580 chunks in a revlog. It effectively isolates I/O and compression performance.
1578 1581 For measurements of higher-level operations like resolving revisions,
1579 1582 see ``perfrevlogrevisions`` and ``perfrevlogrevision``.
1580 1583 """
1581 1584 opts = _byteskwargs(opts)
1582 1585
1583 1586 rl = cmdutil.openrevlog(repo, b'perfrevlogchunks', file_, opts)
1584 1587
1585 1588 # _chunkraw was renamed to _getsegmentforrevs.
1586 1589 try:
1587 1590 segmentforrevs = rl._getsegmentforrevs
1588 1591 except AttributeError:
1589 1592 segmentforrevs = rl._chunkraw
1590 1593
1591 1594 # Verify engines argument.
1592 1595 if engines:
1593 1596 engines = set(e.strip() for e in engines.split(b','))
1594 1597 for engine in engines:
1595 1598 try:
1596 1599 util.compressionengines[engine]
1597 1600 except KeyError:
1598 1601 raise error.Abort(b'unknown compression engine: %s' % engine)
1599 1602 else:
1600 1603 engines = []
1601 1604 for e in util.compengines:
1602 1605 engine = util.compengines[e]
1603 1606 try:
1604 1607 if engine.available():
1605 1608 engine.revlogcompressor().compress(b'dummy')
1606 1609 engines.append(e)
1607 1610 except NotImplementedError:
1608 1611 pass
1609 1612
1610 1613 revs = list(rl.revs(startrev, len(rl) - 1))
1611 1614
1612 1615 def rlfh(rl):
1613 1616 if rl._inline:
1614 1617 return getsvfs(repo)(rl.indexfile)
1615 1618 else:
1616 1619 return getsvfs(repo)(rl.datafile)
1617 1620
1618 1621 def doread():
1619 1622 rl.clearcaches()
1620 1623 for rev in revs:
1621 1624 segmentforrevs(rev, rev)
1622 1625
1623 1626 def doreadcachedfh():
1624 1627 rl.clearcaches()
1625 1628 fh = rlfh(rl)
1626 1629 for rev in revs:
1627 1630 segmentforrevs(rev, rev, df=fh)
1628 1631
1629 1632 def doreadbatch():
1630 1633 rl.clearcaches()
1631 1634 segmentforrevs(revs[0], revs[-1])
1632 1635
1633 1636 def doreadbatchcachedfh():
1634 1637 rl.clearcaches()
1635 1638 fh = rlfh(rl)
1636 1639 segmentforrevs(revs[0], revs[-1], df=fh)
1637 1640
1638 1641 def dochunk():
1639 1642 rl.clearcaches()
1640 1643 fh = rlfh(rl)
1641 1644 for rev in revs:
1642 1645 rl._chunk(rev, df=fh)
1643 1646
1644 1647 chunks = [None]
1645 1648
1646 1649 def dochunkbatch():
1647 1650 rl.clearcaches()
1648 1651 fh = rlfh(rl)
1649 1652 # Save chunks as a side-effect.
1650 1653 chunks[0] = rl._chunks(revs, df=fh)
1651 1654
1652 1655 def docompress(compressor):
1653 1656 rl.clearcaches()
1654 1657
1655 1658 try:
1656 1659 # Swap in the requested compression engine.
1657 1660 oldcompressor = rl._compressor
1658 1661 rl._compressor = compressor
1659 1662 for chunk in chunks[0]:
1660 1663 rl.compress(chunk)
1661 1664 finally:
1662 1665 rl._compressor = oldcompressor
1663 1666
1664 1667 benches = [
1665 1668 (lambda: doread(), b'read'),
1666 1669 (lambda: doreadcachedfh(), b'read w/ reused fd'),
1667 1670 (lambda: doreadbatch(), b'read batch'),
1668 1671 (lambda: doreadbatchcachedfh(), b'read batch w/ reused fd'),
1669 1672 (lambda: dochunk(), b'chunk'),
1670 1673 (lambda: dochunkbatch(), b'chunk batch'),
1671 1674 ]
1672 1675
1673 1676 for engine in sorted(engines):
1674 1677 compressor = util.compengines[engine].revlogcompressor()
1675 1678 benches.append((functools.partial(docompress, compressor),
1676 1679 b'compress w/ %s' % engine))
1677 1680
1678 1681 for fn, title in benches:
1679 1682 timer, fm = gettimer(ui, opts)
1680 1683 timer(fn, title=title)
1681 1684 fm.end()
1682 1685
1683 1686 @command(b'perfrevlogrevision', revlogopts + formatteropts +
1684 1687 [(b'', b'cache', False, b'use caches instead of clearing')],
1685 1688 b'-c|-m|FILE REV')
1686 1689 def perfrevlogrevision(ui, repo, file_, rev=None, cache=None, **opts):
1687 1690 """Benchmark obtaining a revlog revision.
1688 1691
1689 1692 Obtaining a revlog revision consists of roughly the following steps:
1690 1693
1691 1694 1. Compute the delta chain
1692 1695 2. Obtain the raw chunks for that delta chain
1693 1696 3. Decompress each raw chunk
1694 1697 4. Apply binary patches to obtain fulltext
1695 1698 5. Verify hash of fulltext
1696 1699
1697 1700 This command measures the time spent in each of these phases.
1698 1701 """
1699 1702 opts = _byteskwargs(opts)
1700 1703
1701 1704 if opts.get(b'changelog') or opts.get(b'manifest'):
1702 1705 file_, rev = None, file_
1703 1706 elif rev is None:
1704 1707 raise error.CommandError(b'perfrevlogrevision', b'invalid arguments')
1705 1708
1706 1709 r = cmdutil.openrevlog(repo, b'perfrevlogrevision', file_, opts)
1707 1710
1708 1711 # _chunkraw was renamed to _getsegmentforrevs.
1709 1712 try:
1710 1713 segmentforrevs = r._getsegmentforrevs
1711 1714 except AttributeError:
1712 1715 segmentforrevs = r._chunkraw
1713 1716
1714 1717 node = r.lookup(rev)
1715 1718 rev = r.rev(node)
1716 1719
1717 1720 def getrawchunks(data, chain):
1718 1721 start = r.start
1719 1722 length = r.length
1720 1723 inline = r._inline
1721 1724 iosize = r._io.size
1722 1725 buffer = util.buffer
1723 1726 offset = start(chain[0])
1724 1727
1725 1728 chunks = []
1726 1729 ladd = chunks.append
1727 1730
1728 1731 for rev in chain:
1729 1732 chunkstart = start(rev)
1730 1733 if inline:
1731 1734 chunkstart += (rev + 1) * iosize
1732 1735 chunklength = length(rev)
1733 1736 ladd(buffer(data, chunkstart - offset, chunklength))
1734 1737
1735 1738 return chunks
1736 1739
1737 1740 def dodeltachain(rev):
1738 1741 if not cache:
1739 1742 r.clearcaches()
1740 1743 r._deltachain(rev)
1741 1744
1742 1745 def doread(chain):
1743 1746 if not cache:
1744 1747 r.clearcaches()
1745 1748 segmentforrevs(chain[0], chain[-1])
1746 1749
1747 1750 def dorawchunks(data, chain):
1748 1751 if not cache:
1749 1752 r.clearcaches()
1750 1753 getrawchunks(data, chain)
1751 1754
1752 1755 def dodecompress(chunks):
1753 1756 decomp = r.decompress
1754 1757 for chunk in chunks:
1755 1758 decomp(chunk)
1756 1759
1757 1760 def dopatch(text, bins):
1758 1761 if not cache:
1759 1762 r.clearcaches()
1760 1763 mdiff.patches(text, bins)
1761 1764
1762 1765 def dohash(text):
1763 1766 if not cache:
1764 1767 r.clearcaches()
1765 1768 r.checkhash(text, node, rev=rev)
1766 1769
1767 1770 def dorevision():
1768 1771 if not cache:
1769 1772 r.clearcaches()
1770 1773 r.revision(node)
1771 1774
1772 1775 chain = r._deltachain(rev)[0]
1773 1776 data = segmentforrevs(chain[0], chain[-1])[1]
1774 1777 rawchunks = getrawchunks(data, chain)
1775 1778 bins = r._chunks(chain)
1776 text = str(bins[0])
1779 text = bytes(bins[0])
1777 1780 bins = bins[1:]
1778 1781 text = mdiff.patches(text, bins)
1779 1782
1780 1783 benches = [
1781 1784 (lambda: dorevision(), b'full'),
1782 1785 (lambda: dodeltachain(rev), b'deltachain'),
1783 1786 (lambda: doread(chain), b'read'),
1784 1787 (lambda: dorawchunks(data, chain), b'rawchunks'),
1785 1788 (lambda: dodecompress(rawchunks), b'decompress'),
1786 1789 (lambda: dopatch(text, bins), b'patch'),
1787 1790 (lambda: dohash(text), b'hash'),
1788 1791 ]
1789 1792
1790 1793 for fn, title in benches:
1791 1794 timer, fm = gettimer(ui, opts)
1792 1795 timer(fn, title=title)
1793 1796 fm.end()
1794 1797
1795 1798 @command(b'perfrevset',
1796 1799 [(b'C', b'clear', False, b'clear volatile cache between each call.'),
1797 1800 (b'', b'contexts', False, b'obtain changectx for each revision')]
1798 1801 + formatteropts, b"REVSET")
1799 1802 def perfrevset(ui, repo, expr, clear=False, contexts=False, **opts):
1800 1803 """benchmark the execution time of a revset
1801 1804
1802 1805 Use the --clean option if need to evaluate the impact of build volatile
1803 1806 revisions set cache on the revset execution. Volatile cache hold filtered
1804 1807 and obsolete related cache."""
1805 1808 opts = _byteskwargs(opts)
1806 1809
1807 1810 timer, fm = gettimer(ui, opts)
1808 1811 def d():
1809 1812 if clear:
1810 1813 repo.invalidatevolatilesets()
1811 1814 if contexts:
1812 1815 for ctx in repo.set(expr): pass
1813 1816 else:
1814 1817 for r in repo.revs(expr): pass
1815 1818 timer(d)
1816 1819 fm.end()
1817 1820
1818 1821 @command(b'perfvolatilesets',
1819 1822 [(b'', b'clear-obsstore', False, b'drop obsstore between each call.'),
1820 1823 ] + formatteropts)
1821 1824 def perfvolatilesets(ui, repo, *names, **opts):
1822 1825 """benchmark the computation of various volatile set
1823 1826
1824 1827 Volatile set computes element related to filtering and obsolescence."""
1825 1828 opts = _byteskwargs(opts)
1826 1829 timer, fm = gettimer(ui, opts)
1827 1830 repo = repo.unfiltered()
1828 1831
1829 1832 def getobs(name):
1830 1833 def d():
1831 1834 repo.invalidatevolatilesets()
1832 1835 if opts[b'clear_obsstore']:
1833 1836 clearfilecache(repo, b'obsstore')
1834 1837 obsolete.getrevs(repo, name)
1835 1838 return d
1836 1839
1837 1840 allobs = sorted(obsolete.cachefuncs)
1838 1841 if names:
1839 1842 allobs = [n for n in allobs if n in names]
1840 1843
1841 1844 for name in allobs:
1842 1845 timer(getobs(name), title=name)
1843 1846
1844 1847 def getfiltered(name):
1845 1848 def d():
1846 1849 repo.invalidatevolatilesets()
1847 1850 if opts[b'clear_obsstore']:
1848 1851 clearfilecache(repo, b'obsstore')
1849 1852 repoview.filterrevs(repo, name)
1850 1853 return d
1851 1854
1852 1855 allfilter = sorted(repoview.filtertable)
1853 1856 if names:
1854 1857 allfilter = [n for n in allfilter if n in names]
1855 1858
1856 1859 for name in allfilter:
1857 1860 timer(getfiltered(name), title=name)
1858 1861 fm.end()
1859 1862
1860 1863 @command(b'perfbranchmap',
1861 1864 [(b'f', b'full', False,
1862 1865 b'Includes build time of subset'),
1863 1866 (b'', b'clear-revbranch', False,
1864 1867 b'purge the revbranch cache between computation'),
1865 1868 ] + formatteropts)
1866 1869 def perfbranchmap(ui, repo, *filternames, **opts):
1867 1870 """benchmark the update of a branchmap
1868 1871
1869 1872 This benchmarks the full repo.branchmap() call with read and write disabled
1870 1873 """
1871 1874 opts = _byteskwargs(opts)
1872 1875 full = opts.get(b"full", False)
1873 1876 clear_revbranch = opts.get(b"clear_revbranch", False)
1874 1877 timer, fm = gettimer(ui, opts)
1875 1878 def getbranchmap(filtername):
1876 1879 """generate a benchmark function for the filtername"""
1877 1880 if filtername is None:
1878 1881 view = repo
1879 1882 else:
1880 1883 view = repo.filtered(filtername)
1881 1884 def d():
1882 1885 if clear_revbranch:
1883 1886 repo.revbranchcache()._clear()
1884 1887 if full:
1885 1888 view._branchcaches.clear()
1886 1889 else:
1887 1890 view._branchcaches.pop(filtername, None)
1888 1891 view.branchmap()
1889 1892 return d
1890 1893 # add filter in smaller subset to bigger subset
1891 1894 possiblefilters = set(repoview.filtertable)
1892 1895 if filternames:
1893 1896 possiblefilters &= set(filternames)
1894 1897 subsettable = getbranchmapsubsettable()
1895 1898 allfilters = []
1896 1899 while possiblefilters:
1897 1900 for name in possiblefilters:
1898 1901 subset = subsettable.get(name)
1899 1902 if subset not in possiblefilters:
1900 1903 break
1901 1904 else:
1902 1905 assert False, b'subset cycle %s!' % possiblefilters
1903 1906 allfilters.append(name)
1904 1907 possiblefilters.remove(name)
1905 1908
1906 1909 # warm the cache
1907 1910 if not full:
1908 1911 for name in allfilters:
1909 1912 repo.filtered(name).branchmap()
1910 1913 if not filternames or b'unfiltered' in filternames:
1911 1914 # add unfiltered
1912 1915 allfilters.append(None)
1913 1916
1914 1917 branchcacheread = safeattrsetter(branchmap, b'read')
1915 1918 branchcachewrite = safeattrsetter(branchmap.branchcache, b'write')
1916 1919 branchcacheread.set(lambda repo: None)
1917 1920 branchcachewrite.set(lambda bc, repo: None)
1918 1921 try:
1919 1922 for name in allfilters:
1920 1923 printname = name
1921 1924 if name is None:
1922 1925 printname = b'unfiltered'
1923 1926 timer(getbranchmap(name), title=str(printname))
1924 1927 finally:
1925 1928 branchcacheread.restore()
1926 1929 branchcachewrite.restore()
1927 1930 fm.end()
1928 1931
1929 1932 @command(b'perfbranchmapload', [
1930 1933 (b'f', b'filter', b'', b'Specify repoview filter'),
1931 1934 (b'', b'list', False, b'List brachmap filter caches'),
1932 1935 ] + formatteropts)
1933 1936 def perfbranchmapread(ui, repo, filter=b'', list=False, **opts):
1934 1937 """benchmark reading the branchmap"""
1935 1938 opts = _byteskwargs(opts)
1936 1939
1937 1940 if list:
1938 1941 for name, kind, st in repo.cachevfs.readdir(stat=True):
1939 1942 if name.startswith(b'branch2'):
1940 1943 filtername = name.partition(b'-')[2] or b'unfiltered'
1941 1944 ui.status(b'%s - %s\n'
1942 1945 % (filtername, util.bytecount(st.st_size)))
1943 1946 return
1944 1947 if filter:
1945 1948 repo = repoview.repoview(repo, filter)
1946 1949 else:
1947 1950 repo = repo.unfiltered()
1948 1951 # try once without timer, the filter may not be cached
1949 1952 if branchmap.read(repo) is None:
1950 1953 raise error.Abort(b'No brachmap cached for %s repo'
1951 1954 % (filter or b'unfiltered'))
1952 1955 timer, fm = gettimer(ui, opts)
1953 1956 timer(lambda: branchmap.read(repo) and None)
1954 1957 fm.end()
1955 1958
1956 1959 @command(b'perfloadmarkers')
1957 1960 def perfloadmarkers(ui, repo):
1958 1961 """benchmark the time to parse the on-disk markers for a repo
1959 1962
1960 1963 Result is the number of markers in the repo."""
1961 1964 timer, fm = gettimer(ui)
1962 1965 svfs = getsvfs(repo)
1963 1966 timer(lambda: len(obsolete.obsstore(svfs)))
1964 1967 fm.end()
1965 1968
1966 1969 @command(b'perflrucachedict', formatteropts +
1967 1970 [(b'', b'costlimit', 0, b'maximum total cost of items in cache'),
1968 1971 (b'', b'mincost', 0, b'smallest cost of items in cache'),
1969 1972 (b'', b'maxcost', 100, b'maximum cost of items in cache'),
1970 1973 (b'', b'size', 4, b'size of cache'),
1971 1974 (b'', b'gets', 10000, b'number of key lookups'),
1972 1975 (b'', b'sets', 10000, b'number of key sets'),
1973 1976 (b'', b'mixed', 10000, b'number of mixed mode operations'),
1974 1977 (b'', b'mixedgetfreq', 50, b'frequency of get vs set ops in mixed mode')],
1975 1978 norepo=True)
1976 1979 def perflrucache(ui, mincost=0, maxcost=100, costlimit=0, size=4,
1977 1980 gets=10000, sets=10000, mixed=10000, mixedgetfreq=50, **opts):
1978 1981 opts = _byteskwargs(opts)
1979 1982
1980 1983 def doinit():
1981 1984 for i in _xrange(10000):
1982 1985 util.lrucachedict(size)
1983 1986
1984 1987 costrange = list(range(mincost, maxcost + 1))
1985 1988
1986 1989 values = []
1987 1990 for i in _xrange(size):
1988 1991 values.append(random.randint(0, _maxint))
1989 1992
1990 1993 # Get mode fills the cache and tests raw lookup performance with no
1991 1994 # eviction.
1992 1995 getseq = []
1993 1996 for i in _xrange(gets):
1994 1997 getseq.append(random.choice(values))
1995 1998
1996 1999 def dogets():
1997 2000 d = util.lrucachedict(size)
1998 2001 for v in values:
1999 2002 d[v] = v
2000 2003 for key in getseq:
2001 2004 value = d[key]
2002 2005 value # silence pyflakes warning
2003 2006
2004 2007 def dogetscost():
2005 2008 d = util.lrucachedict(size, maxcost=costlimit)
2006 2009 for i, v in enumerate(values):
2007 2010 d.insert(v, v, cost=costs[i])
2008 2011 for key in getseq:
2009 2012 try:
2010 2013 value = d[key]
2011 2014 value # silence pyflakes warning
2012 2015 except KeyError:
2013 2016 pass
2014 2017
2015 2018 # Set mode tests insertion speed with cache eviction.
2016 2019 setseq = []
2017 2020 costs = []
2018 2021 for i in _xrange(sets):
2019 2022 setseq.append(random.randint(0, _maxint))
2020 2023 costs.append(random.choice(costrange))
2021 2024
2022 2025 def doinserts():
2023 2026 d = util.lrucachedict(size)
2024 2027 for v in setseq:
2025 2028 d.insert(v, v)
2026 2029
2027 2030 def doinsertscost():
2028 2031 d = util.lrucachedict(size, maxcost=costlimit)
2029 2032 for i, v in enumerate(setseq):
2030 2033 d.insert(v, v, cost=costs[i])
2031 2034
2032 2035 def dosets():
2033 2036 d = util.lrucachedict(size)
2034 2037 for v in setseq:
2035 2038 d[v] = v
2036 2039
2037 2040 # Mixed mode randomly performs gets and sets with eviction.
2038 2041 mixedops = []
2039 2042 for i in _xrange(mixed):
2040 2043 r = random.randint(0, 100)
2041 2044 if r < mixedgetfreq:
2042 2045 op = 0
2043 2046 else:
2044 2047 op = 1
2045 2048
2046 2049 mixedops.append((op,
2047 2050 random.randint(0, size * 2),
2048 2051 random.choice(costrange)))
2049 2052
2050 2053 def domixed():
2051 2054 d = util.lrucachedict(size)
2052 2055
2053 2056 for op, v, cost in mixedops:
2054 2057 if op == 0:
2055 2058 try:
2056 2059 d[v]
2057 2060 except KeyError:
2058 2061 pass
2059 2062 else:
2060 2063 d[v] = v
2061 2064
2062 2065 def domixedcost():
2063 2066 d = util.lrucachedict(size, maxcost=costlimit)
2064 2067
2065 2068 for op, v, cost in mixedops:
2066 2069 if op == 0:
2067 2070 try:
2068 2071 d[v]
2069 2072 except KeyError:
2070 2073 pass
2071 2074 else:
2072 2075 d.insert(v, v, cost=cost)
2073 2076
2074 2077 benches = [
2075 2078 (doinit, b'init'),
2076 2079 ]
2077 2080
2078 2081 if costlimit:
2079 2082 benches.extend([
2080 2083 (dogetscost, b'gets w/ cost limit'),
2081 2084 (doinsertscost, b'inserts w/ cost limit'),
2082 2085 (domixedcost, b'mixed w/ cost limit'),
2083 2086 ])
2084 2087 else:
2085 2088 benches.extend([
2086 2089 (dogets, b'gets'),
2087 2090 (doinserts, b'inserts'),
2088 2091 (dosets, b'sets'),
2089 2092 (domixed, b'mixed')
2090 2093 ])
2091 2094
2092 2095 for fn, title in benches:
2093 2096 timer, fm = gettimer(ui, opts)
2094 2097 timer(fn, title=title)
2095 2098 fm.end()
2096 2099
2097 2100 @command(b'perfwrite', formatteropts)
2098 2101 def perfwrite(ui, repo, **opts):
2099 2102 """microbenchmark ui.write
2100 2103 """
2101 2104 opts = _byteskwargs(opts)
2102 2105
2103 2106 timer, fm = gettimer(ui, opts)
2104 2107 def write():
2105 2108 for i in range(100000):
2106 2109 ui.write((b'Testing write performance\n'))
2107 2110 timer(write)
2108 2111 fm.end()
2109 2112
2110 2113 def uisetup(ui):
2111 2114 if (util.safehasattr(cmdutil, b'openrevlog') and
2112 2115 not util.safehasattr(commands, b'debugrevlogopts')):
2113 2116 # for "historical portability":
2114 2117 # In this case, Mercurial should be 1.9 (or a79fea6b3e77) -
2115 2118 # 3.7 (or 5606f7d0d063). Therefore, '--dir' option for
2116 2119 # openrevlog() should cause failure, because it has been
2117 2120 # available since 3.5 (or 49c583ca48c4).
2118 2121 def openrevlog(orig, repo, cmd, file_, opts):
2119 2122 if opts.get(b'dir') and not util.safehasattr(repo, b'dirlog'):
2120 2123 raise error.Abort(b"This version doesn't support --dir option",
2121 2124 hint=b"use 3.5 or later")
2122 2125 return orig(repo, cmd, file_, opts)
2123 2126 extensions.wrapfunction(cmdutil, b'openrevlog', openrevlog)
@@ -1,599 +1,600 b''
1 1 test-abort-checkin.t
2 2 test-absorb-filefixupstate.py
3 3 test-absorb-phase.t
4 4 test-absorb-rename.t
5 5 test-absorb-strip.t
6 6 test-absorb.t
7 7 test-add.t
8 8 test-addremove-similar.t
9 9 test-addremove.t
10 10 test-alias.t
11 11 test-amend-subrepo.t
12 12 test-amend.t
13 13 test-ancestor.py
14 14 test-annotate.py
15 15 test-annotate.t
16 16 test-archive-symlinks.t
17 17 test-atomictempfile.py
18 18 test-audit-path.t
19 19 test-audit-subrepo.t
20 20 test-automv.t
21 21 test-backout.t
22 22 test-backwards-remove.t
23 23 test-bad-pull.t
24 24 test-basic.t
25 25 test-bdiff.py
26 26 test-bheads.t
27 27 test-bisect.t
28 28 test-bisect2.t
29 29 test-bisect3.t
30 30 test-blackbox.t
31 31 test-bookmarks-current.t
32 32 test-bookmarks-merge.t
33 33 test-bookmarks-pushpull.t
34 34 test-bookmarks-rebase.t
35 35 test-bookmarks-strip.t
36 36 test-bookmarks.t
37 37 test-branch-change.t
38 38 test-branch-option.t
39 39 test-branch-tag-confict.t
40 40 test-branches.t
41 41 test-bundle-phases.t
42 42 test-bundle-r.t
43 43 test-bundle-type.t
44 44 test-bundle-vs-outgoing.t
45 45 test-bundle.t
46 46 test-bundle2-exchange.t
47 47 test-bundle2-format.t
48 48 test-bundle2-multiple-changegroups.t
49 49 test-bundle2-pushback.t
50 50 test-bundle2-remote-changegroup.t
51 51 test-cappedreader.py
52 52 test-casecollision.t
53 53 test-cat.t
54 54 test-cbor.py
55 55 test-censor.t
56 56 test-changelog-exec.t
57 57 test-check-code.t
58 58 test-check-commit.t
59 59 test-check-execute.t
60 60 test-check-interfaces.py
61 61 test-check-module-imports.t
62 62 test-check-py3-compat.t
63 63 test-check-pyflakes.t
64 64 test-check-pylint.t
65 65 test-check-shbang.t
66 66 test-children.t
67 67 test-clone-cgi.t
68 68 test-clone-pull-corruption.t
69 69 test-clone-r.t
70 70 test-clone-uncompressed.t
71 71 test-clone-update-order.t
72 72 test-clonebundles.t
73 73 test-commit-amend.t
74 74 test-commit-interactive.t
75 75 test-commit-multiple.t
76 76 test-commit-unresolved.t
77 77 test-commit.t
78 78 test-committer.t
79 79 test-completion.t
80 80 test-config-env.py
81 81 test-config.t
82 82 test-conflict.t
83 83 test-confused-revert.t
84 84 test-context.py
85 85 test-contrib-check-code.t
86 86 test-contrib-check-commit.t
87 87 test-contrib-dumprevlog.t
88 test-contrib-perf.t
88 89 test-contrib-testparseutil.t
89 90 test-convert-authormap.t
90 91 test-convert-clonebranches.t
91 92 test-convert-cvs-branch.t
92 93 test-convert-cvs-detectmerge.t
93 94 test-convert-cvs-synthetic.t
94 95 test-convert-cvs.t
95 96 test-convert-cvsnt-mergepoints.t
96 97 test-convert-datesort.t
97 98 test-convert-filemap.t
98 99 test-convert-hg-sink.t
99 100 test-convert-hg-source.t
100 101 test-convert-hg-startrev.t
101 102 test-convert-splicemap.t
102 103 test-convert-tagsbranch-topology.t
103 104 test-copy-move-merge.t
104 105 test-copy.t
105 106 test-copytrace-heuristics.t
106 107 test-debugbuilddag.t
107 108 test-debugbundle.t
108 109 test-debugcommands.t
109 110 test-debugextensions.t
110 111 test-debugindexdot.t
111 112 test-debugrename.t
112 113 test-default-push.t
113 114 test-diff-antipatience.t
114 115 test-diff-binary-file.t
115 116 test-diff-change.t
116 117 test-diff-copy-depth.t
117 118 test-diff-hashes.t
118 119 test-diff-ignore-whitespace.t
119 120 test-diff-indent-heuristic.t
120 121 test-diff-issue2761.t
121 122 test-diff-newlines.t
122 123 test-diff-reverse.t
123 124 test-diff-subdir.t
124 125 test-diff-unified.t
125 126 test-diff-upgrade.t
126 127 test-diffdir.t
127 128 test-diffstat.t
128 129 test-directaccess.t
129 130 test-dirstate-backup.t
130 131 test-dirstate-nonnormalset.t
131 132 test-dirstate.t
132 133 test-dispatch.py
133 134 test-doctest.py
134 135 test-double-merge.t
135 136 test-drawdag.t
136 137 test-duplicateoptions.py
137 138 test-editor-filename.t
138 139 test-empty-dir.t
139 140 test-empty-file.t
140 141 test-empty-group.t
141 142 test-empty.t
142 143 test-encode.t
143 144 test-encoding-func.py
144 145 test-encoding.t
145 146 test-eol-add.t
146 147 test-eol-clone.t
147 148 test-eol-hook.t
148 149 test-eol-patch.t
149 150 test-eol-tag.t
150 151 test-eol-update.t
151 152 test-eol.t
152 153 test-eolfilename.t
153 154 test-excessive-merge.t
154 155 test-exchange-obsmarkers-case-A1.t
155 156 test-exchange-obsmarkers-case-A2.t
156 157 test-exchange-obsmarkers-case-A3.t
157 158 test-exchange-obsmarkers-case-A4.t
158 159 test-exchange-obsmarkers-case-A5.t
159 160 test-exchange-obsmarkers-case-A6.t
160 161 test-exchange-obsmarkers-case-A7.t
161 162 test-exchange-obsmarkers-case-B1.t
162 163 test-exchange-obsmarkers-case-B2.t
163 164 test-exchange-obsmarkers-case-B3.t
164 165 test-exchange-obsmarkers-case-B4.t
165 166 test-exchange-obsmarkers-case-B5.t
166 167 test-exchange-obsmarkers-case-B6.t
167 168 test-exchange-obsmarkers-case-B7.t
168 169 test-exchange-obsmarkers-case-C1.t
169 170 test-exchange-obsmarkers-case-C2.t
170 171 test-exchange-obsmarkers-case-C3.t
171 172 test-exchange-obsmarkers-case-C4.t
172 173 test-exchange-obsmarkers-case-D1.t
173 174 test-exchange-obsmarkers-case-D2.t
174 175 test-exchange-obsmarkers-case-D3.t
175 176 test-exchange-obsmarkers-case-D4.t
176 177 test-execute-bit.t
177 178 test-export.t
178 179 test-extdata.t
179 180 test-extdiff.t
180 181 test-extensions-afterloaded.t
181 182 test-extensions-wrapfunction.py
182 183 test-extra-filelog-entry.t
183 184 test-fetch.t
184 185 test-filebranch.t
185 186 test-filecache.py
186 187 test-filelog.py
187 188 test-fileset-generated.t
188 189 test-fileset.t
189 190 test-fix-topology.t
190 191 test-flags.t
191 192 test-generaldelta.t
192 193 test-getbundle.t
193 194 test-git-export.t
194 195 test-glog-beautifygraph.t
195 196 test-glog-topological.t
196 197 test-glog.t
197 198 test-gpg.t
198 199 test-graft.t
199 200 test-grep.t
200 201 test-hg-parseurl.py
201 202 test-hghave.t
202 203 test-hgignore.t
203 204 test-hgk.t
204 205 test-hgrc.t
205 206 test-hgweb-annotate-whitespace.t
206 207 test-hgweb-bundle.t
207 208 test-hgweb-csp.t
208 209 test-hgweb-descend-empties.t
209 210 test-hgweb-diffs.t
210 211 test-hgweb-empty.t
211 212 test-hgweb-filelog.t
212 213 test-hgweb-non-interactive.t
213 214 test-hgweb-raw.t
214 215 test-hgweb-removed.t
215 216 test-hgweb.t
216 217 test-hgwebdir-paths.py
217 218 test-hgwebdirsym.t
218 219 test-histedit-arguments.t
219 220 test-histedit-base.t
220 221 test-histedit-bookmark-motion.t
221 222 test-histedit-commute.t
222 223 test-histedit-drop.t
223 224 test-histedit-edit.t
224 225 test-histedit-fold-non-commute.t
225 226 test-histedit-fold.t
226 227 test-histedit-no-backup.t
227 228 test-histedit-no-change.t
228 229 test-histedit-non-commute-abort.t
229 230 test-histedit-non-commute.t
230 231 test-histedit-obsolete.t
231 232 test-histedit-outgoing.t
232 233 test-histedit-templates.t
233 234 test-http-branchmap.t
234 235 test-http-bundle1.t
235 236 test-http-clone-r.t
236 237 test-http-permissions.t
237 238 test-http.t
238 239 test-hybridencode.py
239 240 test-i18n.t
240 241 test-identify.t
241 242 test-impexp-branch.t
242 243 test-import-bypass.t
243 244 test-import-eol.t
244 245 test-import-merge.t
245 246 test-import-unknown.t
246 247 test-import.t
247 248 test-imports-checker.t
248 249 test-incoming-outgoing.t
249 250 test-inherit-mode.t
250 251 test-init.t
251 252 test-issue1089.t
252 253 test-issue1102.t
253 254 test-issue1175.t
254 255 test-issue1306.t
255 256 test-issue1438.t
256 257 test-issue1502.t
257 258 test-issue1802.t
258 259 test-issue1877.t
259 260 test-issue1993.t
260 261 test-issue2137.t
261 262 test-issue3084.t
262 263 test-issue4074.t
263 264 test-issue522.t
264 265 test-issue586.t
265 266 test-issue5979.t
266 267 test-issue612.t
267 268 test-issue619.t
268 269 test-issue660.t
269 270 test-issue672.t
270 271 test-issue842.t
271 272 test-journal-exists.t
272 273 test-journal-share.t
273 274 test-journal.t
274 275 test-known.t
275 276 test-largefiles-cache.t
276 277 test-largefiles-misc.t
277 278 test-largefiles-small-disk.t
278 279 test-largefiles-update.t
279 280 test-largefiles.t
280 281 test-lfs-largefiles.t
281 282 test-lfs-pointer.py
282 283 test-linelog.py
283 284 test-linerange.py
284 285 test-locate.t
285 286 test-lock-badness.t
286 287 test-log-linerange.t
287 288 test-log.t
288 289 test-logexchange.t
289 290 test-lrucachedict.py
290 291 test-mactext.t
291 292 test-mailmap.t
292 293 test-manifest-merging.t
293 294 test-manifest.py
294 295 test-manifest.t
295 296 test-match.py
296 297 test-mdiff.py
297 298 test-merge-changedelete.t
298 299 test-merge-closedheads.t
299 300 test-merge-commit.t
300 301 test-merge-criss-cross.t
301 302 test-merge-default.t
302 303 test-merge-force.t
303 304 test-merge-halt.t
304 305 test-merge-internal-tools-pattern.t
305 306 test-merge-local.t
306 307 test-merge-no-file-change.t
307 308 test-merge-remove.t
308 309 test-merge-revert.t
309 310 test-merge-revert2.t
310 311 test-merge-subrepos.t
311 312 test-merge-symlinks.t
312 313 test-merge-tools.t
313 314 test-merge-types.t
314 315 test-merge1.t
315 316 test-merge10.t
316 317 test-merge2.t
317 318 test-merge4.t
318 319 test-merge5.t
319 320 test-merge6.t
320 321 test-merge7.t
321 322 test-merge8.t
322 323 test-merge9.t
323 324 test-minifileset.py
324 325 test-minirst.py
325 326 test-mq-git.t
326 327 test-mq-guards.t
327 328 test-mq-header-date.t
328 329 test-mq-header-from.t
329 330 test-mq-merge.t
330 331 test-mq-pull-from-bundle.t
331 332 test-mq-qclone-http.t
332 333 test-mq-qdelete.t
333 334 test-mq-qdiff.t
334 335 test-mq-qfold.t
335 336 test-mq-qgoto.t
336 337 test-mq-qimport-fail-cleanup.t
337 338 test-mq-qnew.t
338 339 test-mq-qpush-exact.t
339 340 test-mq-qpush-fail.t
340 341 test-mq-qqueue.t
341 342 test-mq-qrefresh-interactive.t
342 343 test-mq-qrefresh-replace-log-message.t
343 344 test-mq-qrefresh.t
344 345 test-mq-qrename.t
345 346 test-mq-qsave.t
346 347 test-mq-safety.t
347 348 test-mq-subrepo.t
348 349 test-mq-symlinks.t
349 350 test-mq.t
350 351 test-mv-cp-st-diff.t
351 352 test-narrow-acl.t
352 353 test-narrow-archive.t
353 354 test-narrow-clone-no-ellipsis.t
354 355 test-narrow-clone-non-narrow-server.t
355 356 test-narrow-clone-nonlinear.t
356 357 test-narrow-clone.t
357 358 test-narrow-commit.t
358 359 test-narrow-copies.t
359 360 test-narrow-debugcommands.t
360 361 test-narrow-debugrebuilddirstate.t
361 362 test-narrow-exchange-merges.t
362 363 test-narrow-exchange.t
363 364 test-narrow-expanddirstate.t
364 365 test-narrow-merge.t
365 366 test-narrow-patch.t
366 367 test-narrow-patterns.t
367 368 test-narrow-pull.t
368 369 test-narrow-rebase.t
369 370 test-narrow-shallow-merges.t
370 371 test-narrow-shallow.t
371 372 test-narrow-strip.t
372 373 test-narrow-trackedcmd.t
373 374 test-narrow-update.t
374 375 test-narrow-widen-no-ellipsis.t
375 376 test-narrow-widen.t
376 377 test-narrow.t
377 378 test-nested-repo.t
378 379 test-newbranch.t
379 380 test-newercgi.t
380 381 test-nointerrupt.t
381 382 test-obshistory.t
382 383 test-obsmarker-template.t
383 384 test-obsmarkers-effectflag.t
384 385 test-obsolete-bounds-checking.t
385 386 test-obsolete-bundle-strip.t
386 387 test-obsolete-changeset-exchange.t
387 388 test-obsolete-checkheads.t
388 389 test-obsolete-distributed.t
389 390 test-obsolete-divergent.t
390 391 test-obsolete-tag-cache.t
391 392 test-obsolete.t
392 393 test-pager.t
393 394 test-parents.t
394 395 test-parseindex2.py
395 396 test-patch-offset.t
396 397 test-patch.t
397 398 test-patchbomb-bookmark.t
398 399 test-patchbomb-tls.t
399 400 test-patchbomb.t
400 401 test-pathconflicts-merge.t
401 402 test-pathconflicts-update.t
402 403 test-pathencode.py
403 404 test-pending.t
404 405 test-permissions.t
405 406 test-phases-exchange.t
406 407 test-phases.t
407 408 test-pull-branch.t
408 409 test-pull-http.t
409 410 test-pull-permission.t
410 411 test-pull-pull-corruption.t
411 412 test-pull-r.t
412 413 test-pull-update.t
413 414 test-pull.t
414 415 test-purge.t
415 416 test-push-cgi.t
416 417 test-push-checkheads-partial-C1.t
417 418 test-push-checkheads-partial-C2.t
418 419 test-push-checkheads-partial-C3.t
419 420 test-push-checkheads-partial-C4.t
420 421 test-push-checkheads-pruned-B1.t
421 422 test-push-checkheads-pruned-B2.t
422 423 test-push-checkheads-pruned-B3.t
423 424 test-push-checkheads-pruned-B4.t
424 425 test-push-checkheads-pruned-B5.t
425 426 test-push-checkheads-pruned-B6.t
426 427 test-push-checkheads-pruned-B7.t
427 428 test-push-checkheads-pruned-B8.t
428 429 test-push-checkheads-superceed-A1.t
429 430 test-push-checkheads-superceed-A2.t
430 431 test-push-checkheads-superceed-A3.t
431 432 test-push-checkheads-superceed-A4.t
432 433 test-push-checkheads-superceed-A5.t
433 434 test-push-checkheads-superceed-A6.t
434 435 test-push-checkheads-superceed-A7.t
435 436 test-push-checkheads-superceed-A8.t
436 437 test-push-checkheads-unpushed-D1.t
437 438 test-push-checkheads-unpushed-D2.t
438 439 test-push-checkheads-unpushed-D3.t
439 440 test-push-checkheads-unpushed-D4.t
440 441 test-push-checkheads-unpushed-D5.t
441 442 test-push-checkheads-unpushed-D6.t
442 443 test-push-checkheads-unpushed-D7.t
443 444 test-push-http.t
444 445 test-push-warn.t
445 446 test-push.t
446 447 test-pushvars.t
447 448 test-qrecord.t
448 449 test-rebase-abort.t
449 450 test-rebase-backup.t
450 451 test-rebase-base-flag.t
451 452 test-rebase-bookmarks.t
452 453 test-rebase-brute-force.t
453 454 test-rebase-cache.t
454 455 test-rebase-check-restore.t
455 456 test-rebase-collapse.t
456 457 test-rebase-conflicts.t
457 458 test-rebase-dest.t
458 459 test-rebase-detach.t
459 460 test-rebase-emptycommit.t
460 461 test-rebase-inmemory.t
461 462 test-rebase-interruptions.t
462 463 test-rebase-issue-noparam-single-rev.t
463 464 test-rebase-legacy.t
464 465 test-rebase-mq-skip.t
465 466 test-rebase-mq.t
466 467 test-rebase-named-branches.t
467 468 test-rebase-newancestor.t
468 469 test-rebase-obsolete.t
469 470 test-rebase-parameters.t
470 471 test-rebase-partial.t
471 472 test-rebase-pull.t
472 473 test-rebase-rename.t
473 474 test-rebase-scenario-global.t
474 475 test-rebase-templates.t
475 476 test-rebase-transaction.t
476 477 test-rebuildstate.t
477 478 test-record.t
478 479 test-relink.t
479 480 test-remove.t
480 481 test-removeemptydirs.t
481 482 test-rename-after-merge.t
482 483 test-rename-dir-merge.t
483 484 test-rename-merge1.t
484 485 test-rename-merge2.t
485 486 test-rename.t
486 487 test-repair-strip.t
487 488 test-repo-compengines.t
488 489 test-resolve.t
489 490 test-revert-flags.t
490 491 test-revert-interactive.t
491 492 test-revert-unknown.t
492 493 test-revert.t
493 494 test-revisions.t
494 495 test-revlog-ancestry.py
495 496 test-revlog-group-emptyiter.t
496 497 test-revlog-mmapindex.t
497 498 test-revlog-packentry.t
498 499 test-revlog-raw.py
499 500 test-revlog-v2.t
500 501 test-revset-dirstate-parents.t
501 502 test-revset-legacy-lookup.t
502 503 test-revset-outgoing.t
503 504 test-rollback.t
504 505 test-run-tests.py
505 506 test-run-tests.t
506 507 test-schemes.t
507 508 test-serve.t
508 509 test-setdiscovery.t
509 510 test-share.t
510 511 test-shelve.t
511 512 test-show-stack.t
512 513 test-show-work.t
513 514 test-show.t
514 515 test-simple-update.t
515 516 test-simplekeyvaluefile.py
516 517 test-simplemerge.py
517 518 test-single-head.t
518 519 test-sparse-clear.t
519 520 test-sparse-clone.t
520 521 test-sparse-import.t
521 522 test-sparse-merges.t
522 523 test-sparse-profiles.t
523 524 test-sparse-requirement.t
524 525 test-sparse-verbose-json.t
525 526 test-sparse.t
526 527 test-split.t
527 528 test-ssh-bundle1.t
528 529 test-ssh-clone-r.t
529 530 test-ssh-proto-unbundle.t
530 531 test-ssh-proto.t
531 532 test-ssh.t
532 533 test-sshserver.py
533 534 test-stack.t
534 535 test-status-inprocess.py
535 536 test-status-rev.t
536 537 test-status-terse.t
537 538 test-storage.py
538 539 test-stream-bundle-v2.t
539 540 test-strict.t
540 541 test-strip-cross.t
541 542 test-strip.t
542 543 test-subrepo-deep-nested-change.t
543 544 test-subrepo-missing.t
544 545 test-subrepo-paths.t
545 546 test-subrepo-recursion.t
546 547 test-subrepo-relative-path.t
547 548 test-subrepo.t
548 549 test-symlink-os-yes-fs-no.py
549 550 test-symlink-placeholder.t
550 551 test-symlinks.t
551 552 test-tag.t
552 553 test-tags.t
553 554 test-template-basic.t
554 555 test-template-functions.t
555 556 test-template-keywords.t
556 557 test-template-map.t
557 558 test-transplant.t
558 559 test-treemanifest.t
559 560 test-ui-color.py
560 561 test-ui-config.py
561 562 test-ui-verbosity.py
562 563 test-unamend.t
563 564 test-unbundlehash.t
564 565 test-uncommit.t
565 566 test-unified-test.t
566 567 test-unionrepo.t
567 568 test-unrelated-pull.t
568 569 test-up-local-change.t
569 570 test-update-branches.t
570 571 test-update-dest.t
571 572 test-update-issue1456.t
572 573 test-update-names.t
573 574 test-update-reverse.t
574 575 test-upgrade-repo.t
575 576 test-url-download.t
576 577 test-url-rev.t
577 578 test-url.py
578 579 test-username-newline.t
579 580 test-util.py
580 581 test-verify.t
581 582 test-walk.t
582 583 test-walkrepo.py
583 584 test-websub.t
584 585 test-win32text.t
585 586 test-wireproto-clientreactor.py
586 587 test-wireproto-command-branchmap.t
587 588 test-wireproto-command-changesetdata.t
588 589 test-wireproto-command-filedata.t
589 590 test-wireproto-command-filesdata.t
590 591 test-wireproto-command-heads.t
591 592 test-wireproto-command-listkeys.t
592 593 test-wireproto-command-lookup.t
593 594 test-wireproto-command-manifestdata.t
594 595 test-wireproto-command-pushkey.t
595 596 test-wireproto-framing.py
596 597 test-wireproto-serverreactor.py
597 598 test-wireproto.py
598 599 test-wsgirequest.py
599 600 test-xdg.t
General Comments 0
You need to be logged in to leave comments. Login now