##// END OF EJS Templates
bookmarks: change bookmark within a transaction...
Pierre-Yves David -
r25744:e78a80f8 default
parent child Browse files
Show More
@@ -1,6536 +1,6539
1 1 # commands.py - command processing for mercurial
2 2 #
3 3 # Copyright 2005-2007 Matt Mackall <mpm@selenic.com>
4 4 #
5 5 # This software may be used and distributed according to the terms of the
6 6 # GNU General Public License version 2 or any later version.
7 7
8 8 from node import hex, bin, nullid, nullrev, short
9 9 from lock import release
10 10 from i18n import _
11 11 import os, re, difflib, time, tempfile, errno, shlex
12 12 import sys, socket
13 13 import hg, scmutil, util, revlog, copies, error, bookmarks
14 14 import patch, help, encoding, templatekw, discovery
15 15 import archival, changegroup, cmdutil, hbisect
16 16 import sshserver, hgweb, commandserver
17 17 import extensions
18 18 from hgweb import server as hgweb_server
19 19 import merge as mergemod
20 20 import minirst, revset, fileset
21 21 import dagparser, context, simplemerge, graphmod, copies
22 22 import random
23 23 import setdiscovery, treediscovery, dagutil, pvec, localrepo
24 import phases, obsolete, exchange, bundle2, repair
24 import phases, obsolete, exchange, bundle2, repair, lock as lockmod
25 25 import ui as uimod
26 26
27 27 table = {}
28 28
29 29 command = cmdutil.command(table)
30 30
31 31 # Space delimited list of commands that don't require local repositories.
32 32 # This should be populated by passing norepo=True into the @command decorator.
33 33 norepo = ''
34 34 # Space delimited list of commands that optionally require local repositories.
35 35 # This should be populated by passing optionalrepo=True into the @command
36 36 # decorator.
37 37 optionalrepo = ''
38 38 # Space delimited list of commands that will examine arguments looking for
39 39 # a repository. This should be populated by passing inferrepo=True into the
40 40 # @command decorator.
41 41 inferrepo = ''
42 42
43 43 # label constants
44 44 # until 3.5, bookmarks.current was the advertised name, not
45 45 # bookmarks.active, so we must use both to avoid breaking old
46 46 # custom styles
47 47 activebookmarklabel = 'bookmarks.active bookmarks.current'
48 48
49 49 # common command options
50 50
51 51 globalopts = [
52 52 ('R', 'repository', '',
53 53 _('repository root directory or name of overlay bundle file'),
54 54 _('REPO')),
55 55 ('', 'cwd', '',
56 56 _('change working directory'), _('DIR')),
57 57 ('y', 'noninteractive', None,
58 58 _('do not prompt, automatically pick the first choice for all prompts')),
59 59 ('q', 'quiet', None, _('suppress output')),
60 60 ('v', 'verbose', None, _('enable additional output')),
61 61 ('', 'config', [],
62 62 _('set/override config option (use \'section.name=value\')'),
63 63 _('CONFIG')),
64 64 ('', 'debug', None, _('enable debugging output')),
65 65 ('', 'debugger', None, _('start debugger')),
66 66 ('', 'encoding', encoding.encoding, _('set the charset encoding'),
67 67 _('ENCODE')),
68 68 ('', 'encodingmode', encoding.encodingmode,
69 69 _('set the charset encoding mode'), _('MODE')),
70 70 ('', 'traceback', None, _('always print a traceback on exception')),
71 71 ('', 'time', None, _('time how long the command takes')),
72 72 ('', 'profile', None, _('print command execution profile')),
73 73 ('', 'version', None, _('output version information and exit')),
74 74 ('h', 'help', None, _('display help and exit')),
75 75 ('', 'hidden', False, _('consider hidden changesets')),
76 76 ]
77 77
78 78 dryrunopts = [('n', 'dry-run', None,
79 79 _('do not perform actions, just print output'))]
80 80
81 81 remoteopts = [
82 82 ('e', 'ssh', '',
83 83 _('specify ssh command to use'), _('CMD')),
84 84 ('', 'remotecmd', '',
85 85 _('specify hg command to run on the remote side'), _('CMD')),
86 86 ('', 'insecure', None,
87 87 _('do not verify server certificate (ignoring web.cacerts config)')),
88 88 ]
89 89
90 90 walkopts = [
91 91 ('I', 'include', [],
92 92 _('include names matching the given patterns'), _('PATTERN')),
93 93 ('X', 'exclude', [],
94 94 _('exclude names matching the given patterns'), _('PATTERN')),
95 95 ]
96 96
97 97 commitopts = [
98 98 ('m', 'message', '',
99 99 _('use text as commit message'), _('TEXT')),
100 100 ('l', 'logfile', '',
101 101 _('read commit message from file'), _('FILE')),
102 102 ]
103 103
104 104 commitopts2 = [
105 105 ('d', 'date', '',
106 106 _('record the specified date as commit date'), _('DATE')),
107 107 ('u', 'user', '',
108 108 _('record the specified user as committer'), _('USER')),
109 109 ]
110 110
111 111 # hidden for now
112 112 formatteropts = [
113 113 ('T', 'template', '',
114 114 _('display with template (EXPERIMENTAL)'), _('TEMPLATE')),
115 115 ]
116 116
117 117 templateopts = [
118 118 ('', 'style', '',
119 119 _('display using template map file (DEPRECATED)'), _('STYLE')),
120 120 ('T', 'template', '',
121 121 _('display with template'), _('TEMPLATE')),
122 122 ]
123 123
124 124 logopts = [
125 125 ('p', 'patch', None, _('show patch')),
126 126 ('g', 'git', None, _('use git extended diff format')),
127 127 ('l', 'limit', '',
128 128 _('limit number of changes displayed'), _('NUM')),
129 129 ('M', 'no-merges', None, _('do not show merges')),
130 130 ('', 'stat', None, _('output diffstat-style summary of changes')),
131 131 ('G', 'graph', None, _("show the revision DAG")),
132 132 ] + templateopts
133 133
134 134 diffopts = [
135 135 ('a', 'text', None, _('treat all files as text')),
136 136 ('g', 'git', None, _('use git extended diff format')),
137 137 ('', 'nodates', None, _('omit dates from diff headers'))
138 138 ]
139 139
140 140 diffwsopts = [
141 141 ('w', 'ignore-all-space', None,
142 142 _('ignore white space when comparing lines')),
143 143 ('b', 'ignore-space-change', None,
144 144 _('ignore changes in the amount of white space')),
145 145 ('B', 'ignore-blank-lines', None,
146 146 _('ignore changes whose lines are all blank')),
147 147 ]
148 148
149 149 diffopts2 = [
150 150 ('', 'noprefix', None, _('omit a/ and b/ prefixes from filenames')),
151 151 ('p', 'show-function', None, _('show which function each change is in')),
152 152 ('', 'reverse', None, _('produce a diff that undoes the changes')),
153 153 ] + diffwsopts + [
154 154 ('U', 'unified', '',
155 155 _('number of lines of context to show'), _('NUM')),
156 156 ('', 'stat', None, _('output diffstat-style summary of changes')),
157 157 ('', 'root', '', _('produce diffs relative to subdirectory'), _('DIR')),
158 158 ]
159 159
160 160 mergetoolopts = [
161 161 ('t', 'tool', '', _('specify merge tool')),
162 162 ]
163 163
164 164 similarityopts = [
165 165 ('s', 'similarity', '',
166 166 _('guess renamed files by similarity (0<=s<=100)'), _('SIMILARITY'))
167 167 ]
168 168
169 169 subrepoopts = [
170 170 ('S', 'subrepos', None,
171 171 _('recurse into subrepositories'))
172 172 ]
173 173
174 174 # Commands start here, listed alphabetically
175 175
176 176 @command('^add',
177 177 walkopts + subrepoopts + dryrunopts,
178 178 _('[OPTION]... [FILE]...'),
179 179 inferrepo=True)
180 180 def add(ui, repo, *pats, **opts):
181 181 """add the specified files on the next commit
182 182
183 183 Schedule files to be version controlled and added to the
184 184 repository.
185 185
186 186 The files will be added to the repository at the next commit. To
187 187 undo an add before that, see :hg:`forget`.
188 188
189 189 If no names are given, add all files to the repository.
190 190
191 191 .. container:: verbose
192 192
193 193 An example showing how new (unknown) files are added
194 194 automatically by :hg:`add`::
195 195
196 196 $ ls
197 197 foo.c
198 198 $ hg status
199 199 ? foo.c
200 200 $ hg add
201 201 adding foo.c
202 202 $ hg status
203 203 A foo.c
204 204
205 205 Returns 0 if all files are successfully added.
206 206 """
207 207
208 208 m = scmutil.match(repo[None], pats, opts)
209 209 rejected = cmdutil.add(ui, repo, m, "", False, **opts)
210 210 return rejected and 1 or 0
211 211
212 212 @command('addremove',
213 213 similarityopts + subrepoopts + walkopts + dryrunopts,
214 214 _('[OPTION]... [FILE]...'),
215 215 inferrepo=True)
216 216 def addremove(ui, repo, *pats, **opts):
217 217 """add all new files, delete all missing files
218 218
219 219 Add all new files and remove all missing files from the
220 220 repository.
221 221
222 222 New files are ignored if they match any of the patterns in
223 223 ``.hgignore``. As with add, these changes take effect at the next
224 224 commit.
225 225
226 226 Use the -s/--similarity option to detect renamed files. This
227 227 option takes a percentage between 0 (disabled) and 100 (files must
228 228 be identical) as its parameter. With a parameter greater than 0,
229 229 this compares every removed file with every added file and records
230 230 those similar enough as renames. Detecting renamed files this way
231 231 can be expensive. After using this option, :hg:`status -C` can be
232 232 used to check which files were identified as moved or renamed. If
233 233 not specified, -s/--similarity defaults to 100 and only renames of
234 234 identical files are detected.
235 235
236 236 Returns 0 if all files are successfully added.
237 237 """
238 238 try:
239 239 sim = float(opts.get('similarity') or 100)
240 240 except ValueError:
241 241 raise util.Abort(_('similarity must be a number'))
242 242 if sim < 0 or sim > 100:
243 243 raise util.Abort(_('similarity must be between 0 and 100'))
244 244 matcher = scmutil.match(repo[None], pats, opts)
245 245 return scmutil.addremove(repo, matcher, "", opts, similarity=sim / 100.0)
246 246
247 247 @command('^annotate|blame',
248 248 [('r', 'rev', '', _('annotate the specified revision'), _('REV')),
249 249 ('', 'follow', None,
250 250 _('follow copies/renames and list the filename (DEPRECATED)')),
251 251 ('', 'no-follow', None, _("don't follow copies and renames")),
252 252 ('a', 'text', None, _('treat all files as text')),
253 253 ('u', 'user', None, _('list the author (long with -v)')),
254 254 ('f', 'file', None, _('list the filename')),
255 255 ('d', 'date', None, _('list the date (short with -q)')),
256 256 ('n', 'number', None, _('list the revision number (default)')),
257 257 ('c', 'changeset', None, _('list the changeset')),
258 258 ('l', 'line-number', None, _('show line number at the first appearance'))
259 259 ] + diffwsopts + walkopts + formatteropts,
260 260 _('[-r REV] [-f] [-a] [-u] [-d] [-n] [-c] [-l] FILE...'),
261 261 inferrepo=True)
262 262 def annotate(ui, repo, *pats, **opts):
263 263 """show changeset information by line for each file
264 264
265 265 List changes in files, showing the revision id responsible for
266 266 each line
267 267
268 268 This command is useful for discovering when a change was made and
269 269 by whom.
270 270
271 271 Without the -a/--text option, annotate will avoid processing files
272 272 it detects as binary. With -a, annotate will annotate the file
273 273 anyway, although the results will probably be neither useful
274 274 nor desirable.
275 275
276 276 Returns 0 on success.
277 277 """
278 278 if not pats:
279 279 raise util.Abort(_('at least one filename or pattern is required'))
280 280
281 281 if opts.get('follow'):
282 282 # --follow is deprecated and now just an alias for -f/--file
283 283 # to mimic the behavior of Mercurial before version 1.5
284 284 opts['file'] = True
285 285
286 286 ctx = scmutil.revsingle(repo, opts.get('rev'))
287 287
288 288 fm = ui.formatter('annotate', opts)
289 289 if ui.quiet:
290 290 datefunc = util.shortdate
291 291 else:
292 292 datefunc = util.datestr
293 293 if ctx.rev() is None:
294 294 def hexfn(node):
295 295 if node is None:
296 296 return None
297 297 else:
298 298 return fm.hexfunc(node)
299 299 if opts.get('changeset'):
300 300 # omit "+" suffix which is appended to node hex
301 301 def formatrev(rev):
302 302 if rev is None:
303 303 return '%d' % ctx.p1().rev()
304 304 else:
305 305 return '%d' % rev
306 306 else:
307 307 def formatrev(rev):
308 308 if rev is None:
309 309 return '%d+' % ctx.p1().rev()
310 310 else:
311 311 return '%d ' % rev
312 312 def formathex(hex):
313 313 if hex is None:
314 314 return '%s+' % fm.hexfunc(ctx.p1().node())
315 315 else:
316 316 return '%s ' % hex
317 317 else:
318 318 hexfn = fm.hexfunc
319 319 formatrev = formathex = str
320 320
321 321 opmap = [('user', ' ', lambda x: x[0].user(), ui.shortuser),
322 322 ('number', ' ', lambda x: x[0].rev(), formatrev),
323 323 ('changeset', ' ', lambda x: hexfn(x[0].node()), formathex),
324 324 ('date', ' ', lambda x: x[0].date(), util.cachefunc(datefunc)),
325 325 ('file', ' ', lambda x: x[0].path(), str),
326 326 ('line_number', ':', lambda x: x[1], str),
327 327 ]
328 328 fieldnamemap = {'number': 'rev', 'changeset': 'node'}
329 329
330 330 if (not opts.get('user') and not opts.get('changeset')
331 331 and not opts.get('date') and not opts.get('file')):
332 332 opts['number'] = True
333 333
334 334 linenumber = opts.get('line_number') is not None
335 335 if linenumber and (not opts.get('changeset')) and (not opts.get('number')):
336 336 raise util.Abort(_('at least one of -n/-c is required for -l'))
337 337
338 338 if fm:
339 339 def makefunc(get, fmt):
340 340 return get
341 341 else:
342 342 def makefunc(get, fmt):
343 343 return lambda x: fmt(get(x))
344 344 funcmap = [(makefunc(get, fmt), sep) for op, sep, get, fmt in opmap
345 345 if opts.get(op)]
346 346 funcmap[0] = (funcmap[0][0], '') # no separator in front of first column
347 347 fields = ' '.join(fieldnamemap.get(op, op) for op, sep, get, fmt in opmap
348 348 if opts.get(op))
349 349
350 350 def bad(x, y):
351 351 raise util.Abort("%s: %s" % (x, y))
352 352
353 353 m = scmutil.match(ctx, pats, opts, badfn=bad)
354 354
355 355 follow = not opts.get('no_follow')
356 356 diffopts = patch.difffeatureopts(ui, opts, section='annotate',
357 357 whitespace=True)
358 358 for abs in ctx.walk(m):
359 359 fctx = ctx[abs]
360 360 if not opts.get('text') and util.binary(fctx.data()):
361 361 fm.plain(_("%s: binary file\n") % ((pats and m.rel(abs)) or abs))
362 362 continue
363 363
364 364 lines = fctx.annotate(follow=follow, linenumber=linenumber,
365 365 diffopts=diffopts)
366 366 formats = []
367 367 pieces = []
368 368
369 369 for f, sep in funcmap:
370 370 l = [f(n) for n, dummy in lines]
371 371 if l:
372 372 if fm:
373 373 formats.append(['%s' for x in l])
374 374 else:
375 375 sizes = [encoding.colwidth(x) for x in l]
376 376 ml = max(sizes)
377 377 formats.append([sep + ' ' * (ml - w) + '%s' for w in sizes])
378 378 pieces.append(l)
379 379
380 380 for f, p, l in zip(zip(*formats), zip(*pieces), lines):
381 381 fm.startitem()
382 382 fm.write(fields, "".join(f), *p)
383 383 fm.write('line', ": %s", l[1])
384 384
385 385 if lines and not lines[-1][1].endswith('\n'):
386 386 fm.plain('\n')
387 387
388 388 fm.end()
389 389
390 390 @command('archive',
391 391 [('', 'no-decode', None, _('do not pass files through decoders')),
392 392 ('p', 'prefix', '', _('directory prefix for files in archive'),
393 393 _('PREFIX')),
394 394 ('r', 'rev', '', _('revision to distribute'), _('REV')),
395 395 ('t', 'type', '', _('type of distribution to create'), _('TYPE')),
396 396 ] + subrepoopts + walkopts,
397 397 _('[OPTION]... DEST'))
398 398 def archive(ui, repo, dest, **opts):
399 399 '''create an unversioned archive of a repository revision
400 400
401 401 By default, the revision used is the parent of the working
402 402 directory; use -r/--rev to specify a different revision.
403 403
404 404 The archive type is automatically detected based on file
405 405 extension (or override using -t/--type).
406 406
407 407 .. container:: verbose
408 408
409 409 Examples:
410 410
411 411 - create a zip file containing the 1.0 release::
412 412
413 413 hg archive -r 1.0 project-1.0.zip
414 414
415 415 - create a tarball excluding .hg files::
416 416
417 417 hg archive project.tar.gz -X ".hg*"
418 418
419 419 Valid types are:
420 420
421 421 :``files``: a directory full of files (default)
422 422 :``tar``: tar archive, uncompressed
423 423 :``tbz2``: tar archive, compressed using bzip2
424 424 :``tgz``: tar archive, compressed using gzip
425 425 :``uzip``: zip archive, uncompressed
426 426 :``zip``: zip archive, compressed using deflate
427 427
428 428 The exact name of the destination archive or directory is given
429 429 using a format string; see :hg:`help export` for details.
430 430
431 431 Each member added to an archive file has a directory prefix
432 432 prepended. Use -p/--prefix to specify a format string for the
433 433 prefix. The default is the basename of the archive, with suffixes
434 434 removed.
435 435
436 436 Returns 0 on success.
437 437 '''
438 438
439 439 ctx = scmutil.revsingle(repo, opts.get('rev'))
440 440 if not ctx:
441 441 raise util.Abort(_('no working directory: please specify a revision'))
442 442 node = ctx.node()
443 443 dest = cmdutil.makefilename(repo, dest, node)
444 444 if os.path.realpath(dest) == repo.root:
445 445 raise util.Abort(_('repository root cannot be destination'))
446 446
447 447 kind = opts.get('type') or archival.guesskind(dest) or 'files'
448 448 prefix = opts.get('prefix')
449 449
450 450 if dest == '-':
451 451 if kind == 'files':
452 452 raise util.Abort(_('cannot archive plain files to stdout'))
453 453 dest = cmdutil.makefileobj(repo, dest)
454 454 if not prefix:
455 455 prefix = os.path.basename(repo.root) + '-%h'
456 456
457 457 prefix = cmdutil.makefilename(repo, prefix, node)
458 458 matchfn = scmutil.match(ctx, [], opts)
459 459 archival.archive(repo, dest, node, kind, not opts.get('no_decode'),
460 460 matchfn, prefix, subrepos=opts.get('subrepos'))
461 461
462 462 @command('backout',
463 463 [('', 'merge', None, _('merge with old dirstate parent after backout')),
464 464 ('', 'commit', None, _('commit if no conflicts were encountered')),
465 465 ('', 'parent', '',
466 466 _('parent to choose when backing out merge (DEPRECATED)'), _('REV')),
467 467 ('r', 'rev', '', _('revision to backout'), _('REV')),
468 468 ('e', 'edit', False, _('invoke editor on commit messages')),
469 469 ] + mergetoolopts + walkopts + commitopts + commitopts2,
470 470 _('[OPTION]... [-r] REV'))
471 471 def backout(ui, repo, node=None, rev=None, commit=False, **opts):
472 472 '''reverse effect of earlier changeset
473 473
474 474 Prepare a new changeset with the effect of REV undone in the
475 475 current working directory.
476 476
477 477 If REV is the parent of the working directory, then this new changeset
478 478 is committed automatically. Otherwise, hg needs to merge the
479 479 changes and the merged result is left uncommitted.
480 480
481 481 .. note::
482 482
483 483 backout cannot be used to fix either an unwanted or
484 484 incorrect merge.
485 485
486 486 .. container:: verbose
487 487
488 488 By default, the pending changeset will have one parent,
489 489 maintaining a linear history. With --merge, the pending
490 490 changeset will instead have two parents: the old parent of the
491 491 working directory and a new child of REV that simply undoes REV.
492 492
493 493 Before version 1.7, the behavior without --merge was equivalent
494 494 to specifying --merge followed by :hg:`update --clean .` to
495 495 cancel the merge and leave the child of REV as a head to be
496 496 merged separately.
497 497
498 498 See :hg:`help dates` for a list of formats valid for -d/--date.
499 499
500 500 Returns 0 on success, 1 if nothing to backout or there are unresolved
501 501 files.
502 502 '''
503 503 if rev and node:
504 504 raise util.Abort(_("please specify just one revision"))
505 505
506 506 if not rev:
507 507 rev = node
508 508
509 509 if not rev:
510 510 raise util.Abort(_("please specify a revision to backout"))
511 511
512 512 date = opts.get('date')
513 513 if date:
514 514 opts['date'] = util.parsedate(date)
515 515
516 516 cmdutil.checkunfinished(repo)
517 517 cmdutil.bailifchanged(repo)
518 518 node = scmutil.revsingle(repo, rev).node()
519 519
520 520 op1, op2 = repo.dirstate.parents()
521 521 if not repo.changelog.isancestor(node, op1):
522 522 raise util.Abort(_('cannot backout change that is not an ancestor'))
523 523
524 524 p1, p2 = repo.changelog.parents(node)
525 525 if p1 == nullid:
526 526 raise util.Abort(_('cannot backout a change with no parents'))
527 527 if p2 != nullid:
528 528 if not opts.get('parent'):
529 529 raise util.Abort(_('cannot backout a merge changeset'))
530 530 p = repo.lookup(opts['parent'])
531 531 if p not in (p1, p2):
532 532 raise util.Abort(_('%s is not a parent of %s') %
533 533 (short(p), short(node)))
534 534 parent = p
535 535 else:
536 536 if opts.get('parent'):
537 537 raise util.Abort(_('cannot use --parent on non-merge changeset'))
538 538 parent = p1
539 539
540 540 # the backout should appear on the same branch
541 541 wlock = repo.wlock()
542 542 try:
543 543 branch = repo.dirstate.branch()
544 544 bheads = repo.branchheads(branch)
545 545 rctx = scmutil.revsingle(repo, hex(parent))
546 546 if not opts.get('merge') and op1 != node:
547 547 try:
548 548 ui.setconfig('ui', 'forcemerge', opts.get('tool', ''),
549 549 'backout')
550 550 repo.dirstate.beginparentchange()
551 551 stats = mergemod.update(repo, parent, True, True, False,
552 552 node, False)
553 553 repo.setparents(op1, op2)
554 554 repo.dirstate.endparentchange()
555 555 hg._showstats(repo, stats)
556 556 if stats[3]:
557 557 repo.ui.status(_("use 'hg resolve' to retry unresolved "
558 558 "file merges\n"))
559 559 return 1
560 560 elif not commit:
561 561 msg = _("changeset %s backed out, "
562 562 "don't forget to commit.\n")
563 563 ui.status(msg % short(node))
564 564 return 0
565 565 finally:
566 566 ui.setconfig('ui', 'forcemerge', '', '')
567 567 else:
568 568 hg.clean(repo, node, show_stats=False)
569 569 repo.dirstate.setbranch(branch)
570 570 cmdutil.revert(ui, repo, rctx, repo.dirstate.parents())
571 571
572 572
573 573 def commitfunc(ui, repo, message, match, opts):
574 574 editform = 'backout'
575 575 e = cmdutil.getcommiteditor(editform=editform, **opts)
576 576 if not message:
577 577 # we don't translate commit messages
578 578 message = "Backed out changeset %s" % short(node)
579 579 e = cmdutil.getcommiteditor(edit=True, editform=editform)
580 580 return repo.commit(message, opts.get('user'), opts.get('date'),
581 581 match, editor=e)
582 582 newnode = cmdutil.commit(ui, repo, commitfunc, [], opts)
583 583 if not newnode:
584 584 ui.status(_("nothing changed\n"))
585 585 return 1
586 586 cmdutil.commitstatus(repo, newnode, branch, bheads)
587 587
588 588 def nice(node):
589 589 return '%d:%s' % (repo.changelog.rev(node), short(node))
590 590 ui.status(_('changeset %s backs out changeset %s\n') %
591 591 (nice(repo.changelog.tip()), nice(node)))
592 592 if opts.get('merge') and op1 != node:
593 593 hg.clean(repo, op1, show_stats=False)
594 594 ui.status(_('merging with changeset %s\n')
595 595 % nice(repo.changelog.tip()))
596 596 try:
597 597 ui.setconfig('ui', 'forcemerge', opts.get('tool', ''),
598 598 'backout')
599 599 return hg.merge(repo, hex(repo.changelog.tip()))
600 600 finally:
601 601 ui.setconfig('ui', 'forcemerge', '', '')
602 602 finally:
603 603 wlock.release()
604 604 return 0
605 605
606 606 @command('bisect',
607 607 [('r', 'reset', False, _('reset bisect state')),
608 608 ('g', 'good', False, _('mark changeset good')),
609 609 ('b', 'bad', False, _('mark changeset bad')),
610 610 ('s', 'skip', False, _('skip testing changeset')),
611 611 ('e', 'extend', False, _('extend the bisect range')),
612 612 ('c', 'command', '', _('use command to check changeset state'), _('CMD')),
613 613 ('U', 'noupdate', False, _('do not update to target'))],
614 614 _("[-gbsr] [-U] [-c CMD] [REV]"))
615 615 def bisect(ui, repo, rev=None, extra=None, command=None,
616 616 reset=None, good=None, bad=None, skip=None, extend=None,
617 617 noupdate=None):
618 618 """subdivision search of changesets
619 619
620 620 This command helps to find changesets which introduce problems. To
621 621 use, mark the earliest changeset you know exhibits the problem as
622 622 bad, then mark the latest changeset which is free from the problem
623 623 as good. Bisect will update your working directory to a revision
624 624 for testing (unless the -U/--noupdate option is specified). Once
625 625 you have performed tests, mark the working directory as good or
626 626 bad, and bisect will either update to another candidate changeset
627 627 or announce that it has found the bad revision.
628 628
629 629 As a shortcut, you can also use the revision argument to mark a
630 630 revision as good or bad without checking it out first.
631 631
632 632 If you supply a command, it will be used for automatic bisection.
633 633 The environment variable HG_NODE will contain the ID of the
634 634 changeset being tested. The exit status of the command will be
635 635 used to mark revisions as good or bad: status 0 means good, 125
636 636 means to skip the revision, 127 (command not found) will abort the
637 637 bisection, and any other non-zero exit status means the revision
638 638 is bad.
639 639
640 640 .. container:: verbose
641 641
642 642 Some examples:
643 643
644 644 - start a bisection with known bad revision 34, and good revision 12::
645 645
646 646 hg bisect --bad 34
647 647 hg bisect --good 12
648 648
649 649 - advance the current bisection by marking current revision as good or
650 650 bad::
651 651
652 652 hg bisect --good
653 653 hg bisect --bad
654 654
655 655 - mark the current revision, or a known revision, to be skipped (e.g. if
656 656 that revision is not usable because of another issue)::
657 657
658 658 hg bisect --skip
659 659 hg bisect --skip 23
660 660
661 661 - skip all revisions that do not touch directories ``foo`` or ``bar``::
662 662
663 663 hg bisect --skip "!( file('path:foo') & file('path:bar') )"
664 664
665 665 - forget the current bisection::
666 666
667 667 hg bisect --reset
668 668
669 669 - use 'make && make tests' to automatically find the first broken
670 670 revision::
671 671
672 672 hg bisect --reset
673 673 hg bisect --bad 34
674 674 hg bisect --good 12
675 675 hg bisect --command "make && make tests"
676 676
677 677 - see all changesets whose states are already known in the current
678 678 bisection::
679 679
680 680 hg log -r "bisect(pruned)"
681 681
682 682 - see the changeset currently being bisected (especially useful
683 683 if running with -U/--noupdate)::
684 684
685 685 hg log -r "bisect(current)"
686 686
687 687 - see all changesets that took part in the current bisection::
688 688
689 689 hg log -r "bisect(range)"
690 690
691 691 - you can even get a nice graph::
692 692
693 693 hg log --graph -r "bisect(range)"
694 694
695 695 See :hg:`help revsets` for more about the `bisect()` keyword.
696 696
697 697 Returns 0 on success.
698 698 """
699 699 def extendbisectrange(nodes, good):
700 700 # bisect is incomplete when it ends on a merge node and
701 701 # one of the parent was not checked.
702 702 parents = repo[nodes[0]].parents()
703 703 if len(parents) > 1:
704 704 if good:
705 705 side = state['bad']
706 706 else:
707 707 side = state['good']
708 708 num = len(set(i.node() for i in parents) & set(side))
709 709 if num == 1:
710 710 return parents[0].ancestor(parents[1])
711 711 return None
712 712
713 713 def print_result(nodes, good):
714 714 displayer = cmdutil.show_changeset(ui, repo, {})
715 715 if len(nodes) == 1:
716 716 # narrowed it down to a single revision
717 717 if good:
718 718 ui.write(_("The first good revision is:\n"))
719 719 else:
720 720 ui.write(_("The first bad revision is:\n"))
721 721 displayer.show(repo[nodes[0]])
722 722 extendnode = extendbisectrange(nodes, good)
723 723 if extendnode is not None:
724 724 ui.write(_('Not all ancestors of this changeset have been'
725 725 ' checked.\nUse bisect --extend to continue the '
726 726 'bisection from\nthe common ancestor, %s.\n')
727 727 % extendnode)
728 728 else:
729 729 # multiple possible revisions
730 730 if good:
731 731 ui.write(_("Due to skipped revisions, the first "
732 732 "good revision could be any of:\n"))
733 733 else:
734 734 ui.write(_("Due to skipped revisions, the first "
735 735 "bad revision could be any of:\n"))
736 736 for n in nodes:
737 737 displayer.show(repo[n])
738 738 displayer.close()
739 739
740 740 def check_state(state, interactive=True):
741 741 if not state['good'] or not state['bad']:
742 742 if (good or bad or skip or reset) and interactive:
743 743 return
744 744 if not state['good']:
745 745 raise util.Abort(_('cannot bisect (no known good revisions)'))
746 746 else:
747 747 raise util.Abort(_('cannot bisect (no known bad revisions)'))
748 748 return True
749 749
750 750 # backward compatibility
751 751 if rev in "good bad reset init".split():
752 752 ui.warn(_("(use of 'hg bisect <cmd>' is deprecated)\n"))
753 753 cmd, rev, extra = rev, extra, None
754 754 if cmd == "good":
755 755 good = True
756 756 elif cmd == "bad":
757 757 bad = True
758 758 else:
759 759 reset = True
760 760 elif extra or good + bad + skip + reset + extend + bool(command) > 1:
761 761 raise util.Abort(_('incompatible arguments'))
762 762
763 763 cmdutil.checkunfinished(repo)
764 764
765 765 if reset:
766 766 p = repo.join("bisect.state")
767 767 if os.path.exists(p):
768 768 os.unlink(p)
769 769 return
770 770
771 771 state = hbisect.load_state(repo)
772 772
773 773 if command:
774 774 changesets = 1
775 775 if noupdate:
776 776 try:
777 777 node = state['current'][0]
778 778 except LookupError:
779 779 raise util.Abort(_('current bisect revision is unknown - '
780 780 'start a new bisect to fix'))
781 781 else:
782 782 node, p2 = repo.dirstate.parents()
783 783 if p2 != nullid:
784 784 raise util.Abort(_('current bisect revision is a merge'))
785 785 try:
786 786 while changesets:
787 787 # update state
788 788 state['current'] = [node]
789 789 hbisect.save_state(repo, state)
790 790 status = ui.system(command, environ={'HG_NODE': hex(node)})
791 791 if status == 125:
792 792 transition = "skip"
793 793 elif status == 0:
794 794 transition = "good"
795 795 # status < 0 means process was killed
796 796 elif status == 127:
797 797 raise util.Abort(_("failed to execute %s") % command)
798 798 elif status < 0:
799 799 raise util.Abort(_("%s killed") % command)
800 800 else:
801 801 transition = "bad"
802 802 ctx = scmutil.revsingle(repo, rev, node)
803 803 rev = None # clear for future iterations
804 804 state[transition].append(ctx.node())
805 805 ui.status(_('changeset %d:%s: %s\n') % (ctx, ctx, transition))
806 806 check_state(state, interactive=False)
807 807 # bisect
808 808 nodes, changesets, bgood = hbisect.bisect(repo.changelog, state)
809 809 # update to next check
810 810 node = nodes[0]
811 811 if not noupdate:
812 812 cmdutil.bailifchanged(repo)
813 813 hg.clean(repo, node, show_stats=False)
814 814 finally:
815 815 state['current'] = [node]
816 816 hbisect.save_state(repo, state)
817 817 print_result(nodes, bgood)
818 818 return
819 819
820 820 # update state
821 821
822 822 if rev:
823 823 nodes = [repo.lookup(i) for i in scmutil.revrange(repo, [rev])]
824 824 else:
825 825 nodes = [repo.lookup('.')]
826 826
827 827 if good or bad or skip:
828 828 if good:
829 829 state['good'] += nodes
830 830 elif bad:
831 831 state['bad'] += nodes
832 832 elif skip:
833 833 state['skip'] += nodes
834 834 hbisect.save_state(repo, state)
835 835
836 836 if not check_state(state):
837 837 return
838 838
839 839 # actually bisect
840 840 nodes, changesets, good = hbisect.bisect(repo.changelog, state)
841 841 if extend:
842 842 if not changesets:
843 843 extendnode = extendbisectrange(nodes, good)
844 844 if extendnode is not None:
845 845 ui.write(_("Extending search to changeset %d:%s\n")
846 846 % (extendnode.rev(), extendnode))
847 847 state['current'] = [extendnode.node()]
848 848 hbisect.save_state(repo, state)
849 849 if noupdate:
850 850 return
851 851 cmdutil.bailifchanged(repo)
852 852 return hg.clean(repo, extendnode.node())
853 853 raise util.Abort(_("nothing to extend"))
854 854
855 855 if changesets == 0:
856 856 print_result(nodes, good)
857 857 else:
858 858 assert len(nodes) == 1 # only a single node can be tested next
859 859 node = nodes[0]
860 860 # compute the approximate number of remaining tests
861 861 tests, size = 0, 2
862 862 while size <= changesets:
863 863 tests, size = tests + 1, size * 2
864 864 rev = repo.changelog.rev(node)
865 865 ui.write(_("Testing changeset %d:%s "
866 866 "(%d changesets remaining, ~%d tests)\n")
867 867 % (rev, short(node), changesets, tests))
868 868 state['current'] = [node]
869 869 hbisect.save_state(repo, state)
870 870 if not noupdate:
871 871 cmdutil.bailifchanged(repo)
872 872 return hg.clean(repo, node)
873 873
874 874 @command('bookmarks|bookmark',
875 875 [('f', 'force', False, _('force')),
876 876 ('r', 'rev', '', _('revision'), _('REV')),
877 877 ('d', 'delete', False, _('delete a given bookmark')),
878 878 ('m', 'rename', '', _('rename a given bookmark'), _('NAME')),
879 879 ('i', 'inactive', False, _('mark a bookmark inactive')),
880 880 ] + formatteropts,
881 881 _('hg bookmarks [OPTIONS]... [NAME]...'))
882 882 def bookmark(ui, repo, *names, **opts):
883 883 '''create a new bookmark or list existing bookmarks
884 884
885 885 Bookmarks are labels on changesets to help track lines of development.
886 886 Bookmarks are unversioned and can be moved, renamed and deleted.
887 887 Deleting or moving a bookmark has no effect on the associated changesets.
888 888
889 889 Creating or updating to a bookmark causes it to be marked as 'active'.
890 890 The active bookmark is indicated with a '*'.
891 891 When a commit is made, the active bookmark will advance to the new commit.
892 892 A plain :hg:`update` will also advance an active bookmark, if possible.
893 893 Updating away from a bookmark will cause it to be deactivated.
894 894
895 895 Bookmarks can be pushed and pulled between repositories (see
896 896 :hg:`help push` and :hg:`help pull`). If a shared bookmark has
897 897 diverged, a new 'divergent bookmark' of the form 'name@path' will
898 898 be created. Using :hg:`merge` will resolve the divergence.
899 899
900 900 A bookmark named '@' has the special property that :hg:`clone` will
901 901 check it out by default if it exists.
902 902
903 903 .. container:: verbose
904 904
905 905 Examples:
906 906
907 907 - create an active bookmark for a new line of development::
908 908
909 909 hg book new-feature
910 910
911 911 - create an inactive bookmark as a place marker::
912 912
913 913 hg book -i reviewed
914 914
915 915 - create an inactive bookmark on another changeset::
916 916
917 917 hg book -r .^ tested
918 918
919 919 - move the '@' bookmark from another branch::
920 920
921 921 hg book -f @
922 922 '''
923 923 force = opts.get('force')
924 924 rev = opts.get('rev')
925 925 delete = opts.get('delete')
926 926 rename = opts.get('rename')
927 927 inactive = opts.get('inactive')
928 928
929 929 def checkformat(mark):
930 930 mark = mark.strip()
931 931 if not mark:
932 932 raise util.Abort(_("bookmark names cannot consist entirely of "
933 933 "whitespace"))
934 934 scmutil.checknewlabel(repo, mark, 'bookmark')
935 935 return mark
936 936
937 937 def checkconflict(repo, mark, cur, force=False, target=None):
938 938 if mark in marks and not force:
939 939 if target:
940 940 if marks[mark] == target and target == cur:
941 941 # re-activating a bookmark
942 942 return
943 943 anc = repo.changelog.ancestors([repo[target].rev()])
944 944 bmctx = repo[marks[mark]]
945 945 divs = [repo[b].node() for b in marks
946 946 if b.split('@', 1)[0] == mark.split('@', 1)[0]]
947 947
948 948 # allow resolving a single divergent bookmark even if moving
949 949 # the bookmark across branches when a revision is specified
950 950 # that contains a divergent bookmark
951 951 if bmctx.rev() not in anc and target in divs:
952 952 bookmarks.deletedivergent(repo, [target], mark)
953 953 return
954 954
955 955 deletefrom = [b for b in divs
956 956 if repo[b].rev() in anc or b == target]
957 957 bookmarks.deletedivergent(repo, deletefrom, mark)
958 958 if bookmarks.validdest(repo, bmctx, repo[target]):
959 959 ui.status(_("moving bookmark '%s' forward from %s\n") %
960 960 (mark, short(bmctx.node())))
961 961 return
962 962 raise util.Abort(_("bookmark '%s' already exists "
963 963 "(use -f to force)") % mark)
964 964 if ((mark in repo.branchmap() or mark == repo.dirstate.branch())
965 965 and not force):
966 966 raise util.Abort(
967 967 _("a bookmark cannot have the name of an existing branch"))
968 968
969 969 if delete and rename:
970 970 raise util.Abort(_("--delete and --rename are incompatible"))
971 971 if delete and rev:
972 972 raise util.Abort(_("--rev is incompatible with --delete"))
973 973 if rename and rev:
974 974 raise util.Abort(_("--rev is incompatible with --rename"))
975 975 if not names and (delete or rev):
976 976 raise util.Abort(_("bookmark name required"))
977 977
978 978 if delete or rename or names or inactive:
979 wlock = repo.wlock()
979 wlock = lock = tr = None
980 980 try:
981 wlock = repo.wlock()
982 lock = repo.lock()
981 983 cur = repo.changectx('.').node()
982 984 marks = repo._bookmarks
983 985 if delete:
986 tr = repo.transaction('bookmark')
984 987 for mark in names:
985 988 if mark not in marks:
986 989 raise util.Abort(_("bookmark '%s' does not exist") %
987 990 mark)
988 991 if mark == repo._activebookmark:
989 992 bookmarks.deactivate(repo)
990 993 del marks[mark]
991 marks.write()
992 994
993 995 elif rename:
996 tr = repo.transaction('bookmark')
994 997 if not names:
995 998 raise util.Abort(_("new bookmark name required"))
996 999 elif len(names) > 1:
997 1000 raise util.Abort(_("only one new bookmark name allowed"))
998 1001 mark = checkformat(names[0])
999 1002 if rename not in marks:
1000 1003 raise util.Abort(_("bookmark '%s' does not exist") % rename)
1001 1004 checkconflict(repo, mark, cur, force)
1002 1005 marks[mark] = marks[rename]
1003 1006 if repo._activebookmark == rename and not inactive:
1004 1007 bookmarks.activate(repo, mark)
1005 1008 del marks[rename]
1006 marks.write()
1007
1008 1009 elif names:
1010 tr = repo.transaction('bookmark')
1009 1011 newact = None
1010 1012 for mark in names:
1011 1013 mark = checkformat(mark)
1012 1014 if newact is None:
1013 1015 newact = mark
1014 1016 if inactive and mark == repo._activebookmark:
1015 1017 bookmarks.deactivate(repo)
1016 1018 return
1017 1019 tgt = cur
1018 1020 if rev:
1019 1021 tgt = scmutil.revsingle(repo, rev).node()
1020 1022 checkconflict(repo, mark, cur, force, tgt)
1021 1023 marks[mark] = tgt
1022 1024 if not inactive and cur == marks[newact] and not rev:
1023 1025 bookmarks.activate(repo, newact)
1024 1026 elif cur != tgt and newact == repo._activebookmark:
1025 1027 bookmarks.deactivate(repo)
1026 marks.write()
1027
1028 1028 elif inactive:
1029 1029 if len(marks) == 0:
1030 1030 ui.status(_("no bookmarks set\n"))
1031 1031 elif not repo._activebookmark:
1032 1032 ui.status(_("no active bookmark\n"))
1033 1033 else:
1034 1034 bookmarks.deactivate(repo)
1035 if tr is not None:
1036 marks.recordchange(tr)
1037 tr.close()
1035 1038 finally:
1036 wlock.release()
1039 lockmod.release(tr, lock, wlock)
1037 1040 else: # show bookmarks
1038 1041 fm = ui.formatter('bookmarks', opts)
1039 1042 hexfn = fm.hexfunc
1040 1043 marks = repo._bookmarks
1041 1044 if len(marks) == 0 and not fm:
1042 1045 ui.status(_("no bookmarks set\n"))
1043 1046 for bmark, n in sorted(marks.iteritems()):
1044 1047 active = repo._activebookmark
1045 1048 if bmark == active:
1046 1049 prefix, label = '*', activebookmarklabel
1047 1050 else:
1048 1051 prefix, label = ' ', ''
1049 1052
1050 1053 fm.startitem()
1051 1054 if not ui.quiet:
1052 1055 fm.plain(' %s ' % prefix, label=label)
1053 1056 fm.write('bookmark', '%s', bmark, label=label)
1054 1057 pad = " " * (25 - encoding.colwidth(bmark))
1055 1058 fm.condwrite(not ui.quiet, 'rev node', pad + ' %d:%s',
1056 1059 repo.changelog.rev(n), hexfn(n), label=label)
1057 1060 fm.data(active=(bmark == active))
1058 1061 fm.plain('\n')
1059 1062 fm.end()
1060 1063
1061 1064 @command('branch',
1062 1065 [('f', 'force', None,
1063 1066 _('set branch name even if it shadows an existing branch')),
1064 1067 ('C', 'clean', None, _('reset branch name to parent branch name'))],
1065 1068 _('[-fC] [NAME]'))
1066 1069 def branch(ui, repo, label=None, **opts):
1067 1070 """set or show the current branch name
1068 1071
1069 1072 .. note::
1070 1073
1071 1074 Branch names are permanent and global. Use :hg:`bookmark` to create a
1072 1075 light-weight bookmark instead. See :hg:`help glossary` for more
1073 1076 information about named branches and bookmarks.
1074 1077
1075 1078 With no argument, show the current branch name. With one argument,
1076 1079 set the working directory branch name (the branch will not exist
1077 1080 in the repository until the next commit). Standard practice
1078 1081 recommends that primary development take place on the 'default'
1079 1082 branch.
1080 1083
1081 1084 Unless -f/--force is specified, branch will not let you set a
1082 1085 branch name that already exists.
1083 1086
1084 1087 Use -C/--clean to reset the working directory branch to that of
1085 1088 the parent of the working directory, negating a previous branch
1086 1089 change.
1087 1090
1088 1091 Use the command :hg:`update` to switch to an existing branch. Use
1089 1092 :hg:`commit --close-branch` to mark this branch head as closed.
1090 1093 When all heads of the branch are closed, the branch will be
1091 1094 considered closed.
1092 1095
1093 1096 Returns 0 on success.
1094 1097 """
1095 1098 if label:
1096 1099 label = label.strip()
1097 1100
1098 1101 if not opts.get('clean') and not label:
1099 1102 ui.write("%s\n" % repo.dirstate.branch())
1100 1103 return
1101 1104
1102 1105 wlock = repo.wlock()
1103 1106 try:
1104 1107 if opts.get('clean'):
1105 1108 label = repo[None].p1().branch()
1106 1109 repo.dirstate.setbranch(label)
1107 1110 ui.status(_('reset working directory to branch %s\n') % label)
1108 1111 elif label:
1109 1112 if not opts.get('force') and label in repo.branchmap():
1110 1113 if label not in [p.branch() for p in repo.parents()]:
1111 1114 raise util.Abort(_('a branch of the same name already'
1112 1115 ' exists'),
1113 1116 # i18n: "it" refers to an existing branch
1114 1117 hint=_("use 'hg update' to switch to it"))
1115 1118 scmutil.checknewlabel(repo, label, 'branch')
1116 1119 repo.dirstate.setbranch(label)
1117 1120 ui.status(_('marked working directory as branch %s\n') % label)
1118 1121
1119 1122 # find any open named branches aside from default
1120 1123 others = [n for n, h, t, c in repo.branchmap().iterbranches()
1121 1124 if n != "default" and not c]
1122 1125 if not others:
1123 1126 ui.status(_('(branches are permanent and global, '
1124 1127 'did you want a bookmark?)\n'))
1125 1128 finally:
1126 1129 wlock.release()
1127 1130
1128 1131 @command('branches',
1129 1132 [('a', 'active', False,
1130 1133 _('show only branches that have unmerged heads (DEPRECATED)')),
1131 1134 ('c', 'closed', False, _('show normal and closed branches')),
1132 1135 ] + formatteropts,
1133 1136 _('[-ac]'))
1134 1137 def branches(ui, repo, active=False, closed=False, **opts):
1135 1138 """list repository named branches
1136 1139
1137 1140 List the repository's named branches, indicating which ones are
1138 1141 inactive. If -c/--closed is specified, also list branches which have
1139 1142 been marked closed (see :hg:`commit --close-branch`).
1140 1143
1141 1144 Use the command :hg:`update` to switch to an existing branch.
1142 1145
1143 1146 Returns 0.
1144 1147 """
1145 1148
1146 1149 fm = ui.formatter('branches', opts)
1147 1150 hexfunc = fm.hexfunc
1148 1151
1149 1152 allheads = set(repo.heads())
1150 1153 branches = []
1151 1154 for tag, heads, tip, isclosed in repo.branchmap().iterbranches():
1152 1155 isactive = not isclosed and bool(set(heads) & allheads)
1153 1156 branches.append((tag, repo[tip], isactive, not isclosed))
1154 1157 branches.sort(key=lambda i: (i[2], i[1].rev(), i[0], i[3]),
1155 1158 reverse=True)
1156 1159
1157 1160 for tag, ctx, isactive, isopen in branches:
1158 1161 if active and not isactive:
1159 1162 continue
1160 1163 if isactive:
1161 1164 label = 'branches.active'
1162 1165 notice = ''
1163 1166 elif not isopen:
1164 1167 if not closed:
1165 1168 continue
1166 1169 label = 'branches.closed'
1167 1170 notice = _(' (closed)')
1168 1171 else:
1169 1172 label = 'branches.inactive'
1170 1173 notice = _(' (inactive)')
1171 1174 current = (tag == repo.dirstate.branch())
1172 1175 if current:
1173 1176 label = 'branches.current'
1174 1177
1175 1178 fm.startitem()
1176 1179 fm.write('branch', '%s', tag, label=label)
1177 1180 rev = ctx.rev()
1178 1181 padsize = max(31 - len(str(rev)) - encoding.colwidth(tag), 0)
1179 1182 fmt = ' ' * padsize + ' %d:%s'
1180 1183 fm.condwrite(not ui.quiet, 'rev node', fmt, rev, hexfunc(ctx.node()),
1181 1184 label='log.changeset changeset.%s' % ctx.phasestr())
1182 1185 fm.data(active=isactive, closed=not isopen, current=current)
1183 1186 if not ui.quiet:
1184 1187 fm.plain(notice)
1185 1188 fm.plain('\n')
1186 1189 fm.end()
1187 1190
1188 1191 @command('bundle',
1189 1192 [('f', 'force', None, _('run even when the destination is unrelated')),
1190 1193 ('r', 'rev', [], _('a changeset intended to be added to the destination'),
1191 1194 _('REV')),
1192 1195 ('b', 'branch', [], _('a specific branch you would like to bundle'),
1193 1196 _('BRANCH')),
1194 1197 ('', 'base', [],
1195 1198 _('a base changeset assumed to be available at the destination'),
1196 1199 _('REV')),
1197 1200 ('a', 'all', None, _('bundle all changesets in the repository')),
1198 1201 ('t', 'type', 'bzip2', _('bundle compression type to use'), _('TYPE')),
1199 1202 ] + remoteopts,
1200 1203 _('[-f] [-t TYPE] [-a] [-r REV]... [--base REV]... FILE [DEST]'))
1201 1204 def bundle(ui, repo, fname, dest=None, **opts):
1202 1205 """create a changegroup file
1203 1206
1204 1207 Generate a compressed changegroup file collecting changesets not
1205 1208 known to be in another repository.
1206 1209
1207 1210 If you omit the destination repository, then hg assumes the
1208 1211 destination will have all the nodes you specify with --base
1209 1212 parameters. To create a bundle containing all changesets, use
1210 1213 -a/--all (or --base null).
1211 1214
1212 1215 You can change compression method with the -t/--type option.
1213 1216 The available compression methods are: none, bzip2, and
1214 1217 gzip (by default, bundles are compressed using bzip2).
1215 1218
1216 1219 The bundle file can then be transferred using conventional means
1217 1220 and applied to another repository with the unbundle or pull
1218 1221 command. This is useful when direct push and pull are not
1219 1222 available or when exporting an entire repository is undesirable.
1220 1223
1221 1224 Applying bundles preserves all changeset contents including
1222 1225 permissions, copy/rename information, and revision history.
1223 1226
1224 1227 Returns 0 on success, 1 if no changes found.
1225 1228 """
1226 1229 revs = None
1227 1230 if 'rev' in opts:
1228 1231 revs = scmutil.revrange(repo, opts['rev'])
1229 1232
1230 1233 bundletype = opts.get('type', 'bzip2').lower()
1231 1234 btypes = {'none': 'HG10UN',
1232 1235 'bzip2': 'HG10BZ',
1233 1236 'gzip': 'HG10GZ',
1234 1237 'bundle2': 'HG20'}
1235 1238 bundletype = btypes.get(bundletype)
1236 1239 if bundletype not in changegroup.bundletypes:
1237 1240 raise util.Abort(_('unknown bundle type specified with --type'))
1238 1241
1239 1242 if opts.get('all'):
1240 1243 base = ['null']
1241 1244 else:
1242 1245 base = scmutil.revrange(repo, opts.get('base'))
1243 1246 # TODO: get desired bundlecaps from command line.
1244 1247 bundlecaps = None
1245 1248 if base:
1246 1249 if dest:
1247 1250 raise util.Abort(_("--base is incompatible with specifying "
1248 1251 "a destination"))
1249 1252 common = [repo.lookup(rev) for rev in base]
1250 1253 heads = revs and map(repo.lookup, revs) or revs
1251 1254 cg = changegroup.getchangegroup(repo, 'bundle', heads=heads,
1252 1255 common=common, bundlecaps=bundlecaps)
1253 1256 outgoing = None
1254 1257 else:
1255 1258 dest = ui.expandpath(dest or 'default-push', dest or 'default')
1256 1259 dest, branches = hg.parseurl(dest, opts.get('branch'))
1257 1260 other = hg.peer(repo, opts, dest)
1258 1261 revs, checkout = hg.addbranchrevs(repo, repo, branches, revs)
1259 1262 heads = revs and map(repo.lookup, revs) or revs
1260 1263 outgoing = discovery.findcommonoutgoing(repo, other,
1261 1264 onlyheads=heads,
1262 1265 force=opts.get('force'),
1263 1266 portable=True)
1264 1267 cg = changegroup.getlocalchangegroup(repo, 'bundle', outgoing,
1265 1268 bundlecaps)
1266 1269 if not cg:
1267 1270 scmutil.nochangesfound(ui, repo, outgoing and outgoing.excluded)
1268 1271 return 1
1269 1272
1270 1273 changegroup.writebundle(ui, cg, fname, bundletype)
1271 1274
1272 1275 @command('cat',
1273 1276 [('o', 'output', '',
1274 1277 _('print output to file with formatted name'), _('FORMAT')),
1275 1278 ('r', 'rev', '', _('print the given revision'), _('REV')),
1276 1279 ('', 'decode', None, _('apply any matching decode filter')),
1277 1280 ] + walkopts,
1278 1281 _('[OPTION]... FILE...'),
1279 1282 inferrepo=True)
1280 1283 def cat(ui, repo, file1, *pats, **opts):
1281 1284 """output the current or given revision of files
1282 1285
1283 1286 Print the specified files as they were at the given revision. If
1284 1287 no revision is given, the parent of the working directory is used.
1285 1288
1286 1289 Output may be to a file, in which case the name of the file is
1287 1290 given using a format string. The formatting rules as follows:
1288 1291
1289 1292 :``%%``: literal "%" character
1290 1293 :``%s``: basename of file being printed
1291 1294 :``%d``: dirname of file being printed, or '.' if in repository root
1292 1295 :``%p``: root-relative path name of file being printed
1293 1296 :``%H``: changeset hash (40 hexadecimal digits)
1294 1297 :``%R``: changeset revision number
1295 1298 :``%h``: short-form changeset hash (12 hexadecimal digits)
1296 1299 :``%r``: zero-padded changeset revision number
1297 1300 :``%b``: basename of the exporting repository
1298 1301
1299 1302 Returns 0 on success.
1300 1303 """
1301 1304 ctx = scmutil.revsingle(repo, opts.get('rev'))
1302 1305 m = scmutil.match(ctx, (file1,) + pats, opts)
1303 1306
1304 1307 return cmdutil.cat(ui, repo, ctx, m, '', **opts)
1305 1308
1306 1309 @command('^clone',
1307 1310 [('U', 'noupdate', None, _('the clone will include an empty working '
1308 1311 'directory (only a repository)')),
1309 1312 ('u', 'updaterev', '', _('revision, tag or branch to check out'), _('REV')),
1310 1313 ('r', 'rev', [], _('include the specified changeset'), _('REV')),
1311 1314 ('b', 'branch', [], _('clone only the specified branch'), _('BRANCH')),
1312 1315 ('', 'pull', None, _('use pull protocol to copy metadata')),
1313 1316 ('', 'uncompressed', None, _('use uncompressed transfer (fast over LAN)')),
1314 1317 ] + remoteopts,
1315 1318 _('[OPTION]... SOURCE [DEST]'),
1316 1319 norepo=True)
1317 1320 def clone(ui, source, dest=None, **opts):
1318 1321 """make a copy of an existing repository
1319 1322
1320 1323 Create a copy of an existing repository in a new directory.
1321 1324
1322 1325 If no destination directory name is specified, it defaults to the
1323 1326 basename of the source.
1324 1327
1325 1328 The location of the source is added to the new repository's
1326 1329 ``.hg/hgrc`` file, as the default to be used for future pulls.
1327 1330
1328 1331 Only local paths and ``ssh://`` URLs are supported as
1329 1332 destinations. For ``ssh://`` destinations, no working directory or
1330 1333 ``.hg/hgrc`` will be created on the remote side.
1331 1334
1332 1335 To pull only a subset of changesets, specify one or more revisions
1333 1336 identifiers with -r/--rev or branches with -b/--branch. The
1334 1337 resulting clone will contain only the specified changesets and
1335 1338 their ancestors. These options (or 'clone src#rev dest') imply
1336 1339 --pull, even for local source repositories. Note that specifying a
1337 1340 tag will include the tagged changeset but not the changeset
1338 1341 containing the tag.
1339 1342
1340 1343 If the source repository has a bookmark called '@' set, that
1341 1344 revision will be checked out in the new repository by default.
1342 1345
1343 1346 To check out a particular version, use -u/--update, or
1344 1347 -U/--noupdate to create a clone with no working directory.
1345 1348
1346 1349 .. container:: verbose
1347 1350
1348 1351 For efficiency, hardlinks are used for cloning whenever the
1349 1352 source and destination are on the same filesystem (note this
1350 1353 applies only to the repository data, not to the working
1351 1354 directory). Some filesystems, such as AFS, implement hardlinking
1352 1355 incorrectly, but do not report errors. In these cases, use the
1353 1356 --pull option to avoid hardlinking.
1354 1357
1355 1358 In some cases, you can clone repositories and the working
1356 1359 directory using full hardlinks with ::
1357 1360
1358 1361 $ cp -al REPO REPOCLONE
1359 1362
1360 1363 This is the fastest way to clone, but it is not always safe. The
1361 1364 operation is not atomic (making sure REPO is not modified during
1362 1365 the operation is up to you) and you have to make sure your
1363 1366 editor breaks hardlinks (Emacs and most Linux Kernel tools do
1364 1367 so). Also, this is not compatible with certain extensions that
1365 1368 place their metadata under the .hg directory, such as mq.
1366 1369
1367 1370 Mercurial will update the working directory to the first applicable
1368 1371 revision from this list:
1369 1372
1370 1373 a) null if -U or the source repository has no changesets
1371 1374 b) if -u . and the source repository is local, the first parent of
1372 1375 the source repository's working directory
1373 1376 c) the changeset specified with -u (if a branch name, this means the
1374 1377 latest head of that branch)
1375 1378 d) the changeset specified with -r
1376 1379 e) the tipmost head specified with -b
1377 1380 f) the tipmost head specified with the url#branch source syntax
1378 1381 g) the revision marked with the '@' bookmark, if present
1379 1382 h) the tipmost head of the default branch
1380 1383 i) tip
1381 1384
1382 1385 Examples:
1383 1386
1384 1387 - clone a remote repository to a new directory named hg/::
1385 1388
1386 1389 hg clone http://selenic.com/hg
1387 1390
1388 1391 - create a lightweight local clone::
1389 1392
1390 1393 hg clone project/ project-feature/
1391 1394
1392 1395 - clone from an absolute path on an ssh server (note double-slash)::
1393 1396
1394 1397 hg clone ssh://user@server//home/projects/alpha/
1395 1398
1396 1399 - do a high-speed clone over a LAN while checking out a
1397 1400 specified version::
1398 1401
1399 1402 hg clone --uncompressed http://server/repo -u 1.5
1400 1403
1401 1404 - create a repository without changesets after a particular revision::
1402 1405
1403 1406 hg clone -r 04e544 experimental/ good/
1404 1407
1405 1408 - clone (and track) a particular named branch::
1406 1409
1407 1410 hg clone http://selenic.com/hg#stable
1408 1411
1409 1412 See :hg:`help urls` for details on specifying URLs.
1410 1413
1411 1414 Returns 0 on success.
1412 1415 """
1413 1416 if opts.get('noupdate') and opts.get('updaterev'):
1414 1417 raise util.Abort(_("cannot specify both --noupdate and --updaterev"))
1415 1418
1416 1419 r = hg.clone(ui, opts, source, dest,
1417 1420 pull=opts.get('pull'),
1418 1421 stream=opts.get('uncompressed'),
1419 1422 rev=opts.get('rev'),
1420 1423 update=opts.get('updaterev') or not opts.get('noupdate'),
1421 1424 branch=opts.get('branch'))
1422 1425
1423 1426 return r is None
1424 1427
1425 1428 @command('^commit|ci',
1426 1429 [('A', 'addremove', None,
1427 1430 _('mark new/missing files as added/removed before committing')),
1428 1431 ('', 'close-branch', None,
1429 1432 _('mark a branch head as closed')),
1430 1433 ('', 'amend', None, _('amend the parent of the working directory')),
1431 1434 ('s', 'secret', None, _('use the secret phase for committing')),
1432 1435 ('e', 'edit', None, _('invoke editor on commit messages')),
1433 1436 ('i', 'interactive', None, _('use interactive mode')),
1434 1437 ] + walkopts + commitopts + commitopts2 + subrepoopts,
1435 1438 _('[OPTION]... [FILE]...'),
1436 1439 inferrepo=True)
1437 1440 def commit(ui, repo, *pats, **opts):
1438 1441 """commit the specified files or all outstanding changes
1439 1442
1440 1443 Commit changes to the given files into the repository. Unlike a
1441 1444 centralized SCM, this operation is a local operation. See
1442 1445 :hg:`push` for a way to actively distribute your changes.
1443 1446
1444 1447 If a list of files is omitted, all changes reported by :hg:`status`
1445 1448 will be committed.
1446 1449
1447 1450 If you are committing the result of a merge, do not provide any
1448 1451 filenames or -I/-X filters.
1449 1452
1450 1453 If no commit message is specified, Mercurial starts your
1451 1454 configured editor where you can enter a message. In case your
1452 1455 commit fails, you will find a backup of your message in
1453 1456 ``.hg/last-message.txt``.
1454 1457
1455 1458 The --close-branch flag can be used to mark the current branch
1456 1459 head closed. When all heads of a branch are closed, the branch
1457 1460 will be considered closed and no longer listed.
1458 1461
1459 1462 The --amend flag can be used to amend the parent of the
1460 1463 working directory with a new commit that contains the changes
1461 1464 in the parent in addition to those currently reported by :hg:`status`,
1462 1465 if there are any. The old commit is stored in a backup bundle in
1463 1466 ``.hg/strip-backup`` (see :hg:`help bundle` and :hg:`help unbundle`
1464 1467 on how to restore it).
1465 1468
1466 1469 Message, user and date are taken from the amended commit unless
1467 1470 specified. When a message isn't specified on the command line,
1468 1471 the editor will open with the message of the amended commit.
1469 1472
1470 1473 It is not possible to amend public changesets (see :hg:`help phases`)
1471 1474 or changesets that have children.
1472 1475
1473 1476 See :hg:`help dates` for a list of formats valid for -d/--date.
1474 1477
1475 1478 Returns 0 on success, 1 if nothing changed.
1476 1479 """
1477 1480 if opts.get('interactive'):
1478 1481 opts.pop('interactive')
1479 1482 cmdutil.dorecord(ui, repo, commit, 'commit', False,
1480 1483 cmdutil.recordfilter, *pats, **opts)
1481 1484 return
1482 1485
1483 1486 if opts.get('subrepos'):
1484 1487 if opts.get('amend'):
1485 1488 raise util.Abort(_('cannot amend with --subrepos'))
1486 1489 # Let --subrepos on the command line override config setting.
1487 1490 ui.setconfig('ui', 'commitsubrepos', True, 'commit')
1488 1491
1489 1492 cmdutil.checkunfinished(repo, commit=True)
1490 1493
1491 1494 branch = repo[None].branch()
1492 1495 bheads = repo.branchheads(branch)
1493 1496
1494 1497 extra = {}
1495 1498 if opts.get('close_branch'):
1496 1499 extra['close'] = 1
1497 1500
1498 1501 if not bheads:
1499 1502 raise util.Abort(_('can only close branch heads'))
1500 1503 elif opts.get('amend'):
1501 1504 if repo.parents()[0].p1().branch() != branch and \
1502 1505 repo.parents()[0].p2().branch() != branch:
1503 1506 raise util.Abort(_('can only close branch heads'))
1504 1507
1505 1508 if opts.get('amend'):
1506 1509 if ui.configbool('ui', 'commitsubrepos'):
1507 1510 raise util.Abort(_('cannot amend with ui.commitsubrepos enabled'))
1508 1511
1509 1512 old = repo['.']
1510 1513 if not old.mutable():
1511 1514 raise util.Abort(_('cannot amend public changesets'))
1512 1515 if len(repo[None].parents()) > 1:
1513 1516 raise util.Abort(_('cannot amend while merging'))
1514 1517 allowunstable = obsolete.isenabled(repo, obsolete.allowunstableopt)
1515 1518 if not allowunstable and old.children():
1516 1519 raise util.Abort(_('cannot amend changeset with children'))
1517 1520
1518 1521 # commitfunc is used only for temporary amend commit by cmdutil.amend
1519 1522 def commitfunc(ui, repo, message, match, opts):
1520 1523 return repo.commit(message,
1521 1524 opts.get('user') or old.user(),
1522 1525 opts.get('date') or old.date(),
1523 1526 match,
1524 1527 extra=extra)
1525 1528
1526 1529 node = cmdutil.amend(ui, repo, commitfunc, old, extra, pats, opts)
1527 1530 if node == old.node():
1528 1531 ui.status(_("nothing changed\n"))
1529 1532 return 1
1530 1533 else:
1531 1534 def commitfunc(ui, repo, message, match, opts):
1532 1535 backup = ui.backupconfig('phases', 'new-commit')
1533 1536 baseui = repo.baseui
1534 1537 basebackup = baseui.backupconfig('phases', 'new-commit')
1535 1538 try:
1536 1539 if opts.get('secret'):
1537 1540 ui.setconfig('phases', 'new-commit', 'secret', 'commit')
1538 1541 # Propagate to subrepos
1539 1542 baseui.setconfig('phases', 'new-commit', 'secret', 'commit')
1540 1543
1541 1544 editform = cmdutil.mergeeditform(repo[None], 'commit.normal')
1542 1545 editor = cmdutil.getcommiteditor(editform=editform, **opts)
1543 1546 return repo.commit(message, opts.get('user'), opts.get('date'),
1544 1547 match,
1545 1548 editor=editor,
1546 1549 extra=extra)
1547 1550 finally:
1548 1551 ui.restoreconfig(backup)
1549 1552 repo.baseui.restoreconfig(basebackup)
1550 1553
1551 1554
1552 1555 node = cmdutil.commit(ui, repo, commitfunc, pats, opts)
1553 1556
1554 1557 if not node:
1555 1558 stat = repo.status(match=scmutil.match(repo[None], pats, opts))
1556 1559 if stat[3]:
1557 1560 ui.status(_("nothing changed (%d missing files, see "
1558 1561 "'hg status')\n") % len(stat[3]))
1559 1562 else:
1560 1563 ui.status(_("nothing changed\n"))
1561 1564 return 1
1562 1565
1563 1566 cmdutil.commitstatus(repo, node, branch, bheads, opts)
1564 1567
1565 1568 @command('config|showconfig|debugconfig',
1566 1569 [('u', 'untrusted', None, _('show untrusted configuration options')),
1567 1570 ('e', 'edit', None, _('edit user config')),
1568 1571 ('l', 'local', None, _('edit repository config')),
1569 1572 ('g', 'global', None, _('edit global config'))],
1570 1573 _('[-u] [NAME]...'),
1571 1574 optionalrepo=True)
1572 1575 def config(ui, repo, *values, **opts):
1573 1576 """show combined config settings from all hgrc files
1574 1577
1575 1578 With no arguments, print names and values of all config items.
1576 1579
1577 1580 With one argument of the form section.name, print just the value
1578 1581 of that config item.
1579 1582
1580 1583 With multiple arguments, print names and values of all config
1581 1584 items with matching section names.
1582 1585
1583 1586 With --edit, start an editor on the user-level config file. With
1584 1587 --global, edit the system-wide config file. With --local, edit the
1585 1588 repository-level config file.
1586 1589
1587 1590 With --debug, the source (filename and line number) is printed
1588 1591 for each config item.
1589 1592
1590 1593 See :hg:`help config` for more information about config files.
1591 1594
1592 1595 Returns 0 on success, 1 if NAME does not exist.
1593 1596
1594 1597 """
1595 1598
1596 1599 if opts.get('edit') or opts.get('local') or opts.get('global'):
1597 1600 if opts.get('local') and opts.get('global'):
1598 1601 raise util.Abort(_("can't use --local and --global together"))
1599 1602
1600 1603 if opts.get('local'):
1601 1604 if not repo:
1602 1605 raise util.Abort(_("can't use --local outside a repository"))
1603 1606 paths = [repo.join('hgrc')]
1604 1607 elif opts.get('global'):
1605 1608 paths = scmutil.systemrcpath()
1606 1609 else:
1607 1610 paths = scmutil.userrcpath()
1608 1611
1609 1612 for f in paths:
1610 1613 if os.path.exists(f):
1611 1614 break
1612 1615 else:
1613 1616 if opts.get('global'):
1614 1617 samplehgrc = uimod.samplehgrcs['global']
1615 1618 elif opts.get('local'):
1616 1619 samplehgrc = uimod.samplehgrcs['local']
1617 1620 else:
1618 1621 samplehgrc = uimod.samplehgrcs['user']
1619 1622
1620 1623 f = paths[0]
1621 1624 fp = open(f, "w")
1622 1625 fp.write(samplehgrc)
1623 1626 fp.close()
1624 1627
1625 1628 editor = ui.geteditor()
1626 1629 ui.system("%s \"%s\"" % (editor, f),
1627 1630 onerr=util.Abort, errprefix=_("edit failed"))
1628 1631 return
1629 1632
1630 1633 for f in scmutil.rcpath():
1631 1634 ui.debug('read config from: %s\n' % f)
1632 1635 untrusted = bool(opts.get('untrusted'))
1633 1636 if values:
1634 1637 sections = [v for v in values if '.' not in v]
1635 1638 items = [v for v in values if '.' in v]
1636 1639 if len(items) > 1 or items and sections:
1637 1640 raise util.Abort(_('only one config item permitted'))
1638 1641 matched = False
1639 1642 for section, name, value in ui.walkconfig(untrusted=untrusted):
1640 1643 value = str(value).replace('\n', '\\n')
1641 1644 sectname = section + '.' + name
1642 1645 if values:
1643 1646 for v in values:
1644 1647 if v == section:
1645 1648 ui.debug('%s: ' %
1646 1649 ui.configsource(section, name, untrusted))
1647 1650 ui.write('%s=%s\n' % (sectname, value))
1648 1651 matched = True
1649 1652 elif v == sectname:
1650 1653 ui.debug('%s: ' %
1651 1654 ui.configsource(section, name, untrusted))
1652 1655 ui.write(value, '\n')
1653 1656 matched = True
1654 1657 else:
1655 1658 ui.debug('%s: ' %
1656 1659 ui.configsource(section, name, untrusted))
1657 1660 ui.write('%s=%s\n' % (sectname, value))
1658 1661 matched = True
1659 1662 if matched:
1660 1663 return 0
1661 1664 return 1
1662 1665
1663 1666 @command('copy|cp',
1664 1667 [('A', 'after', None, _('record a copy that has already occurred')),
1665 1668 ('f', 'force', None, _('forcibly copy over an existing managed file')),
1666 1669 ] + walkopts + dryrunopts,
1667 1670 _('[OPTION]... [SOURCE]... DEST'))
1668 1671 def copy(ui, repo, *pats, **opts):
1669 1672 """mark files as copied for the next commit
1670 1673
1671 1674 Mark dest as having copies of source files. If dest is a
1672 1675 directory, copies are put in that directory. If dest is a file,
1673 1676 the source must be a single file.
1674 1677
1675 1678 By default, this command copies the contents of files as they
1676 1679 exist in the working directory. If invoked with -A/--after, the
1677 1680 operation is recorded, but no copying is performed.
1678 1681
1679 1682 This command takes effect with the next commit. To undo a copy
1680 1683 before that, see :hg:`revert`.
1681 1684
1682 1685 Returns 0 on success, 1 if errors are encountered.
1683 1686 """
1684 1687 wlock = repo.wlock(False)
1685 1688 try:
1686 1689 return cmdutil.copy(ui, repo, pats, opts)
1687 1690 finally:
1688 1691 wlock.release()
1689 1692
1690 1693 @command('debugancestor', [], _('[INDEX] REV1 REV2'), optionalrepo=True)
1691 1694 def debugancestor(ui, repo, *args):
1692 1695 """find the ancestor revision of two revisions in a given index"""
1693 1696 if len(args) == 3:
1694 1697 index, rev1, rev2 = args
1695 1698 r = revlog.revlog(scmutil.opener(os.getcwd(), audit=False), index)
1696 1699 lookup = r.lookup
1697 1700 elif len(args) == 2:
1698 1701 if not repo:
1699 1702 raise util.Abort(_("there is no Mercurial repository here "
1700 1703 "(.hg not found)"))
1701 1704 rev1, rev2 = args
1702 1705 r = repo.changelog
1703 1706 lookup = repo.lookup
1704 1707 else:
1705 1708 raise util.Abort(_('either two or three arguments required'))
1706 1709 a = r.ancestor(lookup(rev1), lookup(rev2))
1707 1710 ui.write("%d:%s\n" % (r.rev(a), hex(a)))
1708 1711
1709 1712 @command('debugbuilddag',
1710 1713 [('m', 'mergeable-file', None, _('add single file mergeable changes')),
1711 1714 ('o', 'overwritten-file', None, _('add single file all revs overwrite')),
1712 1715 ('n', 'new-file', None, _('add new file at each rev'))],
1713 1716 _('[OPTION]... [TEXT]'))
1714 1717 def debugbuilddag(ui, repo, text=None,
1715 1718 mergeable_file=False,
1716 1719 overwritten_file=False,
1717 1720 new_file=False):
1718 1721 """builds a repo with a given DAG from scratch in the current empty repo
1719 1722
1720 1723 The description of the DAG is read from stdin if not given on the
1721 1724 command line.
1722 1725
1723 1726 Elements:
1724 1727
1725 1728 - "+n" is a linear run of n nodes based on the current default parent
1726 1729 - "." is a single node based on the current default parent
1727 1730 - "$" resets the default parent to null (implied at the start);
1728 1731 otherwise the default parent is always the last node created
1729 1732 - "<p" sets the default parent to the backref p
1730 1733 - "*p" is a fork at parent p, which is a backref
1731 1734 - "*p1/p2" is a merge of parents p1 and p2, which are backrefs
1732 1735 - "/p2" is a merge of the preceding node and p2
1733 1736 - ":tag" defines a local tag for the preceding node
1734 1737 - "@branch" sets the named branch for subsequent nodes
1735 1738 - "#...\\n" is a comment up to the end of the line
1736 1739
1737 1740 Whitespace between the above elements is ignored.
1738 1741
1739 1742 A backref is either
1740 1743
1741 1744 - a number n, which references the node curr-n, where curr is the current
1742 1745 node, or
1743 1746 - the name of a local tag you placed earlier using ":tag", or
1744 1747 - empty to denote the default parent.
1745 1748
1746 1749 All string valued-elements are either strictly alphanumeric, or must
1747 1750 be enclosed in double quotes ("..."), with "\\" as escape character.
1748 1751 """
1749 1752
1750 1753 if text is None:
1751 1754 ui.status(_("reading DAG from stdin\n"))
1752 1755 text = ui.fin.read()
1753 1756
1754 1757 cl = repo.changelog
1755 1758 if len(cl) > 0:
1756 1759 raise util.Abort(_('repository is not empty'))
1757 1760
1758 1761 # determine number of revs in DAG
1759 1762 total = 0
1760 1763 for type, data in dagparser.parsedag(text):
1761 1764 if type == 'n':
1762 1765 total += 1
1763 1766
1764 1767 if mergeable_file:
1765 1768 linesperrev = 2
1766 1769 # make a file with k lines per rev
1767 1770 initialmergedlines = [str(i) for i in xrange(0, total * linesperrev)]
1768 1771 initialmergedlines.append("")
1769 1772
1770 1773 tags = []
1771 1774
1772 1775 lock = tr = None
1773 1776 try:
1774 1777 lock = repo.lock()
1775 1778 tr = repo.transaction("builddag")
1776 1779
1777 1780 at = -1
1778 1781 atbranch = 'default'
1779 1782 nodeids = []
1780 1783 id = 0
1781 1784 ui.progress(_('building'), id, unit=_('revisions'), total=total)
1782 1785 for type, data in dagparser.parsedag(text):
1783 1786 if type == 'n':
1784 1787 ui.note(('node %s\n' % str(data)))
1785 1788 id, ps = data
1786 1789
1787 1790 files = []
1788 1791 fctxs = {}
1789 1792
1790 1793 p2 = None
1791 1794 if mergeable_file:
1792 1795 fn = "mf"
1793 1796 p1 = repo[ps[0]]
1794 1797 if len(ps) > 1:
1795 1798 p2 = repo[ps[1]]
1796 1799 pa = p1.ancestor(p2)
1797 1800 base, local, other = [x[fn].data() for x in (pa, p1,
1798 1801 p2)]
1799 1802 m3 = simplemerge.Merge3Text(base, local, other)
1800 1803 ml = [l.strip() for l in m3.merge_lines()]
1801 1804 ml.append("")
1802 1805 elif at > 0:
1803 1806 ml = p1[fn].data().split("\n")
1804 1807 else:
1805 1808 ml = initialmergedlines
1806 1809 ml[id * linesperrev] += " r%i" % id
1807 1810 mergedtext = "\n".join(ml)
1808 1811 files.append(fn)
1809 1812 fctxs[fn] = context.memfilectx(repo, fn, mergedtext)
1810 1813
1811 1814 if overwritten_file:
1812 1815 fn = "of"
1813 1816 files.append(fn)
1814 1817 fctxs[fn] = context.memfilectx(repo, fn, "r%i\n" % id)
1815 1818
1816 1819 if new_file:
1817 1820 fn = "nf%i" % id
1818 1821 files.append(fn)
1819 1822 fctxs[fn] = context.memfilectx(repo, fn, "r%i\n" % id)
1820 1823 if len(ps) > 1:
1821 1824 if not p2:
1822 1825 p2 = repo[ps[1]]
1823 1826 for fn in p2:
1824 1827 if fn.startswith("nf"):
1825 1828 files.append(fn)
1826 1829 fctxs[fn] = p2[fn]
1827 1830
1828 1831 def fctxfn(repo, cx, path):
1829 1832 return fctxs.get(path)
1830 1833
1831 1834 if len(ps) == 0 or ps[0] < 0:
1832 1835 pars = [None, None]
1833 1836 elif len(ps) == 1:
1834 1837 pars = [nodeids[ps[0]], None]
1835 1838 else:
1836 1839 pars = [nodeids[p] for p in ps]
1837 1840 cx = context.memctx(repo, pars, "r%i" % id, files, fctxfn,
1838 1841 date=(id, 0),
1839 1842 user="debugbuilddag",
1840 1843 extra={'branch': atbranch})
1841 1844 nodeid = repo.commitctx(cx)
1842 1845 nodeids.append(nodeid)
1843 1846 at = id
1844 1847 elif type == 'l':
1845 1848 id, name = data
1846 1849 ui.note(('tag %s\n' % name))
1847 1850 tags.append("%s %s\n" % (hex(repo.changelog.node(id)), name))
1848 1851 elif type == 'a':
1849 1852 ui.note(('branch %s\n' % data))
1850 1853 atbranch = data
1851 1854 ui.progress(_('building'), id, unit=_('revisions'), total=total)
1852 1855 tr.close()
1853 1856
1854 1857 if tags:
1855 1858 repo.vfs.write("localtags", "".join(tags))
1856 1859 finally:
1857 1860 ui.progress(_('building'), None)
1858 1861 release(tr, lock)
1859 1862
1860 1863 @command('debugbundle',
1861 1864 [('a', 'all', None, _('show all details'))],
1862 1865 _('FILE'),
1863 1866 norepo=True)
1864 1867 def debugbundle(ui, bundlepath, all=None, **opts):
1865 1868 """lists the contents of a bundle"""
1866 1869 f = hg.openpath(ui, bundlepath)
1867 1870 try:
1868 1871 gen = exchange.readbundle(ui, f, bundlepath)
1869 1872 if isinstance(gen, bundle2.unbundle20):
1870 1873 return _debugbundle2(ui, gen, all=all, **opts)
1871 1874 if all:
1872 1875 ui.write(("format: id, p1, p2, cset, delta base, len(delta)\n"))
1873 1876
1874 1877 def showchunks(named):
1875 1878 ui.write("\n%s\n" % named)
1876 1879 chain = None
1877 1880 while True:
1878 1881 chunkdata = gen.deltachunk(chain)
1879 1882 if not chunkdata:
1880 1883 break
1881 1884 node = chunkdata['node']
1882 1885 p1 = chunkdata['p1']
1883 1886 p2 = chunkdata['p2']
1884 1887 cs = chunkdata['cs']
1885 1888 deltabase = chunkdata['deltabase']
1886 1889 delta = chunkdata['delta']
1887 1890 ui.write("%s %s %s %s %s %s\n" %
1888 1891 (hex(node), hex(p1), hex(p2),
1889 1892 hex(cs), hex(deltabase), len(delta)))
1890 1893 chain = node
1891 1894
1892 1895 chunkdata = gen.changelogheader()
1893 1896 showchunks("changelog")
1894 1897 chunkdata = gen.manifestheader()
1895 1898 showchunks("manifest")
1896 1899 while True:
1897 1900 chunkdata = gen.filelogheader()
1898 1901 if not chunkdata:
1899 1902 break
1900 1903 fname = chunkdata['filename']
1901 1904 showchunks(fname)
1902 1905 else:
1903 1906 if isinstance(gen, bundle2.unbundle20):
1904 1907 raise util.Abort(_('use debugbundle2 for this file'))
1905 1908 chunkdata = gen.changelogheader()
1906 1909 chain = None
1907 1910 while True:
1908 1911 chunkdata = gen.deltachunk(chain)
1909 1912 if not chunkdata:
1910 1913 break
1911 1914 node = chunkdata['node']
1912 1915 ui.write("%s\n" % hex(node))
1913 1916 chain = node
1914 1917 finally:
1915 1918 f.close()
1916 1919
1917 1920 def _debugbundle2(ui, gen, **opts):
1918 1921 """lists the contents of a bundle2"""
1919 1922 if not isinstance(gen, bundle2.unbundle20):
1920 1923 raise util.Abort(_('not a bundle2 file'))
1921 1924 ui.write(('Stream params: %s\n' % repr(gen.params)))
1922 1925 for part in gen.iterparts():
1923 1926 ui.write('%s -- %r\n' % (part.type, repr(part.params)))
1924 1927 if part.type == 'changegroup':
1925 1928 version = part.params.get('version', '01')
1926 1929 cg = changegroup.packermap[version][1](part, 'UN')
1927 1930 chunkdata = cg.changelogheader()
1928 1931 chain = None
1929 1932 while True:
1930 1933 chunkdata = cg.deltachunk(chain)
1931 1934 if not chunkdata:
1932 1935 break
1933 1936 node = chunkdata['node']
1934 1937 ui.write(" %s\n" % hex(node))
1935 1938 chain = node
1936 1939
1937 1940 @command('debugcheckstate', [], '')
1938 1941 def debugcheckstate(ui, repo):
1939 1942 """validate the correctness of the current dirstate"""
1940 1943 parent1, parent2 = repo.dirstate.parents()
1941 1944 m1 = repo[parent1].manifest()
1942 1945 m2 = repo[parent2].manifest()
1943 1946 errors = 0
1944 1947 for f in repo.dirstate:
1945 1948 state = repo.dirstate[f]
1946 1949 if state in "nr" and f not in m1:
1947 1950 ui.warn(_("%s in state %s, but not in manifest1\n") % (f, state))
1948 1951 errors += 1
1949 1952 if state in "a" and f in m1:
1950 1953 ui.warn(_("%s in state %s, but also in manifest1\n") % (f, state))
1951 1954 errors += 1
1952 1955 if state in "m" and f not in m1 and f not in m2:
1953 1956 ui.warn(_("%s in state %s, but not in either manifest\n") %
1954 1957 (f, state))
1955 1958 errors += 1
1956 1959 for f in m1:
1957 1960 state = repo.dirstate[f]
1958 1961 if state not in "nrm":
1959 1962 ui.warn(_("%s in manifest1, but listed as state %s") % (f, state))
1960 1963 errors += 1
1961 1964 if errors:
1962 1965 error = _(".hg/dirstate inconsistent with current parent's manifest")
1963 1966 raise util.Abort(error)
1964 1967
1965 1968 @command('debugcommands', [], _('[COMMAND]'), norepo=True)
1966 1969 def debugcommands(ui, cmd='', *args):
1967 1970 """list all available commands and options"""
1968 1971 for cmd, vals in sorted(table.iteritems()):
1969 1972 cmd = cmd.split('|')[0].strip('^')
1970 1973 opts = ', '.join([i[1] for i in vals[1]])
1971 1974 ui.write('%s: %s\n' % (cmd, opts))
1972 1975
1973 1976 @command('debugcomplete',
1974 1977 [('o', 'options', None, _('show the command options'))],
1975 1978 _('[-o] CMD'),
1976 1979 norepo=True)
1977 1980 def debugcomplete(ui, cmd='', **opts):
1978 1981 """returns the completion list associated with the given command"""
1979 1982
1980 1983 if opts.get('options'):
1981 1984 options = []
1982 1985 otables = [globalopts]
1983 1986 if cmd:
1984 1987 aliases, entry = cmdutil.findcmd(cmd, table, False)
1985 1988 otables.append(entry[1])
1986 1989 for t in otables:
1987 1990 for o in t:
1988 1991 if "(DEPRECATED)" in o[3]:
1989 1992 continue
1990 1993 if o[0]:
1991 1994 options.append('-%s' % o[0])
1992 1995 options.append('--%s' % o[1])
1993 1996 ui.write("%s\n" % "\n".join(options))
1994 1997 return
1995 1998
1996 1999 cmdlist, unused_allcmds = cmdutil.findpossible(cmd, table)
1997 2000 if ui.verbose:
1998 2001 cmdlist = [' '.join(c[0]) for c in cmdlist.values()]
1999 2002 ui.write("%s\n" % "\n".join(sorted(cmdlist)))
2000 2003
2001 2004 @command('debugdag',
2002 2005 [('t', 'tags', None, _('use tags as labels')),
2003 2006 ('b', 'branches', None, _('annotate with branch names')),
2004 2007 ('', 'dots', None, _('use dots for runs')),
2005 2008 ('s', 'spaces', None, _('separate elements by spaces'))],
2006 2009 _('[OPTION]... [FILE [REV]...]'),
2007 2010 optionalrepo=True)
2008 2011 def debugdag(ui, repo, file_=None, *revs, **opts):
2009 2012 """format the changelog or an index DAG as a concise textual description
2010 2013
2011 2014 If you pass a revlog index, the revlog's DAG is emitted. If you list
2012 2015 revision numbers, they get labeled in the output as rN.
2013 2016
2014 2017 Otherwise, the changelog DAG of the current repo is emitted.
2015 2018 """
2016 2019 spaces = opts.get('spaces')
2017 2020 dots = opts.get('dots')
2018 2021 if file_:
2019 2022 rlog = revlog.revlog(scmutil.opener(os.getcwd(), audit=False), file_)
2020 2023 revs = set((int(r) for r in revs))
2021 2024 def events():
2022 2025 for r in rlog:
2023 2026 yield 'n', (r, list(p for p in rlog.parentrevs(r)
2024 2027 if p != -1))
2025 2028 if r in revs:
2026 2029 yield 'l', (r, "r%i" % r)
2027 2030 elif repo:
2028 2031 cl = repo.changelog
2029 2032 tags = opts.get('tags')
2030 2033 branches = opts.get('branches')
2031 2034 if tags:
2032 2035 labels = {}
2033 2036 for l, n in repo.tags().items():
2034 2037 labels.setdefault(cl.rev(n), []).append(l)
2035 2038 def events():
2036 2039 b = "default"
2037 2040 for r in cl:
2038 2041 if branches:
2039 2042 newb = cl.read(cl.node(r))[5]['branch']
2040 2043 if newb != b:
2041 2044 yield 'a', newb
2042 2045 b = newb
2043 2046 yield 'n', (r, list(p for p in cl.parentrevs(r)
2044 2047 if p != -1))
2045 2048 if tags:
2046 2049 ls = labels.get(r)
2047 2050 if ls:
2048 2051 for l in ls:
2049 2052 yield 'l', (r, l)
2050 2053 else:
2051 2054 raise util.Abort(_('need repo for changelog dag'))
2052 2055
2053 2056 for line in dagparser.dagtextlines(events(),
2054 2057 addspaces=spaces,
2055 2058 wraplabels=True,
2056 2059 wrapannotations=True,
2057 2060 wrapnonlinear=dots,
2058 2061 usedots=dots,
2059 2062 maxlinewidth=70):
2060 2063 ui.write(line)
2061 2064 ui.write("\n")
2062 2065
2063 2066 @command('debugdata',
2064 2067 [('c', 'changelog', False, _('open changelog')),
2065 2068 ('m', 'manifest', False, _('open manifest')),
2066 2069 ('', 'dir', False, _('open directory manifest'))],
2067 2070 _('-c|-m|FILE REV'))
2068 2071 def debugdata(ui, repo, file_, rev=None, **opts):
2069 2072 """dump the contents of a data file revision"""
2070 2073 if opts.get('changelog') or opts.get('manifest'):
2071 2074 file_, rev = None, file_
2072 2075 elif rev is None:
2073 2076 raise error.CommandError('debugdata', _('invalid arguments'))
2074 2077 r = cmdutil.openrevlog(repo, 'debugdata', file_, opts)
2075 2078 try:
2076 2079 ui.write(r.revision(r.lookup(rev)))
2077 2080 except KeyError:
2078 2081 raise util.Abort(_('invalid revision identifier %s') % rev)
2079 2082
2080 2083 @command('debugdate',
2081 2084 [('e', 'extended', None, _('try extended date formats'))],
2082 2085 _('[-e] DATE [RANGE]'),
2083 2086 norepo=True, optionalrepo=True)
2084 2087 def debugdate(ui, date, range=None, **opts):
2085 2088 """parse and display a date"""
2086 2089 if opts["extended"]:
2087 2090 d = util.parsedate(date, util.extendeddateformats)
2088 2091 else:
2089 2092 d = util.parsedate(date)
2090 2093 ui.write(("internal: %s %s\n") % d)
2091 2094 ui.write(("standard: %s\n") % util.datestr(d))
2092 2095 if range:
2093 2096 m = util.matchdate(range)
2094 2097 ui.write(("match: %s\n") % m(d[0]))
2095 2098
2096 2099 @command('debugdiscovery',
2097 2100 [('', 'old', None, _('use old-style discovery')),
2098 2101 ('', 'nonheads', None,
2099 2102 _('use old-style discovery with non-heads included')),
2100 2103 ] + remoteopts,
2101 2104 _('[-l REV] [-r REV] [-b BRANCH]... [OTHER]'))
2102 2105 def debugdiscovery(ui, repo, remoteurl="default", **opts):
2103 2106 """runs the changeset discovery protocol in isolation"""
2104 2107 remoteurl, branches = hg.parseurl(ui.expandpath(remoteurl),
2105 2108 opts.get('branch'))
2106 2109 remote = hg.peer(repo, opts, remoteurl)
2107 2110 ui.status(_('comparing with %s\n') % util.hidepassword(remoteurl))
2108 2111
2109 2112 # make sure tests are repeatable
2110 2113 random.seed(12323)
2111 2114
2112 2115 def doit(localheads, remoteheads, remote=remote):
2113 2116 if opts.get('old'):
2114 2117 if localheads:
2115 2118 raise util.Abort('cannot use localheads with old style '
2116 2119 'discovery')
2117 2120 if not util.safehasattr(remote, 'branches'):
2118 2121 # enable in-client legacy support
2119 2122 remote = localrepo.locallegacypeer(remote.local())
2120 2123 common, _in, hds = treediscovery.findcommonincoming(repo, remote,
2121 2124 force=True)
2122 2125 common = set(common)
2123 2126 if not opts.get('nonheads'):
2124 2127 ui.write(("unpruned common: %s\n") %
2125 2128 " ".join(sorted(short(n) for n in common)))
2126 2129 dag = dagutil.revlogdag(repo.changelog)
2127 2130 all = dag.ancestorset(dag.internalizeall(common))
2128 2131 common = dag.externalizeall(dag.headsetofconnecteds(all))
2129 2132 else:
2130 2133 common, any, hds = setdiscovery.findcommonheads(ui, repo, remote)
2131 2134 common = set(common)
2132 2135 rheads = set(hds)
2133 2136 lheads = set(repo.heads())
2134 2137 ui.write(("common heads: %s\n") %
2135 2138 " ".join(sorted(short(n) for n in common)))
2136 2139 if lheads <= common:
2137 2140 ui.write(("local is subset\n"))
2138 2141 elif rheads <= common:
2139 2142 ui.write(("remote is subset\n"))
2140 2143
2141 2144 serverlogs = opts.get('serverlog')
2142 2145 if serverlogs:
2143 2146 for filename in serverlogs:
2144 2147 logfile = open(filename, 'r')
2145 2148 try:
2146 2149 line = logfile.readline()
2147 2150 while line:
2148 2151 parts = line.strip().split(';')
2149 2152 op = parts[1]
2150 2153 if op == 'cg':
2151 2154 pass
2152 2155 elif op == 'cgss':
2153 2156 doit(parts[2].split(' '), parts[3].split(' '))
2154 2157 elif op == 'unb':
2155 2158 doit(parts[3].split(' '), parts[2].split(' '))
2156 2159 line = logfile.readline()
2157 2160 finally:
2158 2161 logfile.close()
2159 2162
2160 2163 else:
2161 2164 remoterevs, _checkout = hg.addbranchrevs(repo, remote, branches,
2162 2165 opts.get('remote_head'))
2163 2166 localrevs = opts.get('local_head')
2164 2167 doit(localrevs, remoterevs)
2165 2168
2166 2169 @command('debugfileset',
2167 2170 [('r', 'rev', '', _('apply the filespec on this revision'), _('REV'))],
2168 2171 _('[-r REV] FILESPEC'))
2169 2172 def debugfileset(ui, repo, expr, **opts):
2170 2173 '''parse and apply a fileset specification'''
2171 2174 ctx = scmutil.revsingle(repo, opts.get('rev'), None)
2172 2175 if ui.verbose:
2173 2176 tree = fileset.parse(expr)
2174 2177 ui.note(fileset.prettyformat(tree), "\n")
2175 2178
2176 2179 for f in ctx.getfileset(expr):
2177 2180 ui.write("%s\n" % f)
2178 2181
2179 2182 @command('debugfsinfo', [], _('[PATH]'), norepo=True)
2180 2183 def debugfsinfo(ui, path="."):
2181 2184 """show information detected about current filesystem"""
2182 2185 util.writefile('.debugfsinfo', '')
2183 2186 ui.write(('exec: %s\n') % (util.checkexec(path) and 'yes' or 'no'))
2184 2187 ui.write(('symlink: %s\n') % (util.checklink(path) and 'yes' or 'no'))
2185 2188 ui.write(('hardlink: %s\n') % (util.checknlink(path) and 'yes' or 'no'))
2186 2189 ui.write(('case-sensitive: %s\n') % (util.checkcase('.debugfsinfo')
2187 2190 and 'yes' or 'no'))
2188 2191 os.unlink('.debugfsinfo')
2189 2192
2190 2193 @command('debuggetbundle',
2191 2194 [('H', 'head', [], _('id of head node'), _('ID')),
2192 2195 ('C', 'common', [], _('id of common node'), _('ID')),
2193 2196 ('t', 'type', 'bzip2', _('bundle compression type to use'), _('TYPE'))],
2194 2197 _('REPO FILE [-H|-C ID]...'),
2195 2198 norepo=True)
2196 2199 def debuggetbundle(ui, repopath, bundlepath, head=None, common=None, **opts):
2197 2200 """retrieves a bundle from a repo
2198 2201
2199 2202 Every ID must be a full-length hex node id string. Saves the bundle to the
2200 2203 given file.
2201 2204 """
2202 2205 repo = hg.peer(ui, opts, repopath)
2203 2206 if not repo.capable('getbundle'):
2204 2207 raise util.Abort("getbundle() not supported by target repository")
2205 2208 args = {}
2206 2209 if common:
2207 2210 args['common'] = [bin(s) for s in common]
2208 2211 if head:
2209 2212 args['heads'] = [bin(s) for s in head]
2210 2213 # TODO: get desired bundlecaps from command line.
2211 2214 args['bundlecaps'] = None
2212 2215 bundle = repo.getbundle('debug', **args)
2213 2216
2214 2217 bundletype = opts.get('type', 'bzip2').lower()
2215 2218 btypes = {'none': 'HG10UN',
2216 2219 'bzip2': 'HG10BZ',
2217 2220 'gzip': 'HG10GZ',
2218 2221 'bundle2': 'HG20'}
2219 2222 bundletype = btypes.get(bundletype)
2220 2223 if bundletype not in changegroup.bundletypes:
2221 2224 raise util.Abort(_('unknown bundle type specified with --type'))
2222 2225 changegroup.writebundle(ui, bundle, bundlepath, bundletype)
2223 2226
2224 2227 @command('debugignore', [], '')
2225 2228 def debugignore(ui, repo, *values, **opts):
2226 2229 """display the combined ignore pattern"""
2227 2230 ignore = repo.dirstate._ignore
2228 2231 includepat = getattr(ignore, 'includepat', None)
2229 2232 if includepat is not None:
2230 2233 ui.write("%s\n" % includepat)
2231 2234 else:
2232 2235 raise util.Abort(_("no ignore patterns found"))
2233 2236
2234 2237 @command('debugindex',
2235 2238 [('c', 'changelog', False, _('open changelog')),
2236 2239 ('m', 'manifest', False, _('open manifest')),
2237 2240 ('', 'dir', False, _('open directory manifest')),
2238 2241 ('f', 'format', 0, _('revlog format'), _('FORMAT'))],
2239 2242 _('[-f FORMAT] -c|-m|FILE'),
2240 2243 optionalrepo=True)
2241 2244 def debugindex(ui, repo, file_=None, **opts):
2242 2245 """dump the contents of an index file"""
2243 2246 r = cmdutil.openrevlog(repo, 'debugindex', file_, opts)
2244 2247 format = opts.get('format', 0)
2245 2248 if format not in (0, 1):
2246 2249 raise util.Abort(_("unknown format %d") % format)
2247 2250
2248 2251 generaldelta = r.version & revlog.REVLOGGENERALDELTA
2249 2252 if generaldelta:
2250 2253 basehdr = ' delta'
2251 2254 else:
2252 2255 basehdr = ' base'
2253 2256
2254 2257 if ui.debugflag:
2255 2258 shortfn = hex
2256 2259 else:
2257 2260 shortfn = short
2258 2261
2259 2262 # There might not be anything in r, so have a sane default
2260 2263 idlen = 12
2261 2264 for i in r:
2262 2265 idlen = len(shortfn(r.node(i)))
2263 2266 break
2264 2267
2265 2268 if format == 0:
2266 2269 ui.write(" rev offset length " + basehdr + " linkrev"
2267 2270 " %s %s p2\n" % ("nodeid".ljust(idlen), "p1".ljust(idlen)))
2268 2271 elif format == 1:
2269 2272 ui.write(" rev flag offset length"
2270 2273 " size " + basehdr + " link p1 p2"
2271 2274 " %s\n" % "nodeid".rjust(idlen))
2272 2275
2273 2276 for i in r:
2274 2277 node = r.node(i)
2275 2278 if generaldelta:
2276 2279 base = r.deltaparent(i)
2277 2280 else:
2278 2281 base = r.chainbase(i)
2279 2282 if format == 0:
2280 2283 try:
2281 2284 pp = r.parents(node)
2282 2285 except Exception:
2283 2286 pp = [nullid, nullid]
2284 2287 ui.write("% 6d % 9d % 7d % 6d % 7d %s %s %s\n" % (
2285 2288 i, r.start(i), r.length(i), base, r.linkrev(i),
2286 2289 shortfn(node), shortfn(pp[0]), shortfn(pp[1])))
2287 2290 elif format == 1:
2288 2291 pr = r.parentrevs(i)
2289 2292 ui.write("% 6d %04x % 8d % 8d % 8d % 6d % 6d % 6d % 6d %s\n" % (
2290 2293 i, r.flags(i), r.start(i), r.length(i), r.rawsize(i),
2291 2294 base, r.linkrev(i), pr[0], pr[1], shortfn(node)))
2292 2295
2293 2296 @command('debugindexdot', [], _('FILE'), optionalrepo=True)
2294 2297 def debugindexdot(ui, repo, file_):
2295 2298 """dump an index DAG as a graphviz dot file"""
2296 2299 r = None
2297 2300 if repo:
2298 2301 filelog = repo.file(file_)
2299 2302 if len(filelog):
2300 2303 r = filelog
2301 2304 if not r:
2302 2305 r = revlog.revlog(scmutil.opener(os.getcwd(), audit=False), file_)
2303 2306 ui.write(("digraph G {\n"))
2304 2307 for i in r:
2305 2308 node = r.node(i)
2306 2309 pp = r.parents(node)
2307 2310 ui.write("\t%d -> %d\n" % (r.rev(pp[0]), i))
2308 2311 if pp[1] != nullid:
2309 2312 ui.write("\t%d -> %d\n" % (r.rev(pp[1]), i))
2310 2313 ui.write("}\n")
2311 2314
2312 2315 @command('debuginstall', [], '', norepo=True)
2313 2316 def debuginstall(ui):
2314 2317 '''test Mercurial installation
2315 2318
2316 2319 Returns 0 on success.
2317 2320 '''
2318 2321
2319 2322 def writetemp(contents):
2320 2323 (fd, name) = tempfile.mkstemp(prefix="hg-debuginstall-")
2321 2324 f = os.fdopen(fd, "wb")
2322 2325 f.write(contents)
2323 2326 f.close()
2324 2327 return name
2325 2328
2326 2329 problems = 0
2327 2330
2328 2331 # encoding
2329 2332 ui.status(_("checking encoding (%s)...\n") % encoding.encoding)
2330 2333 try:
2331 2334 encoding.fromlocal("test")
2332 2335 except util.Abort as inst:
2333 2336 ui.write(" %s\n" % inst)
2334 2337 ui.write(_(" (check that your locale is properly set)\n"))
2335 2338 problems += 1
2336 2339
2337 2340 # Python
2338 2341 ui.status(_("checking Python executable (%s)\n") % sys.executable)
2339 2342 ui.status(_("checking Python version (%s)\n")
2340 2343 % ("%s.%s.%s" % sys.version_info[:3]))
2341 2344 ui.status(_("checking Python lib (%s)...\n")
2342 2345 % os.path.dirname(os.__file__))
2343 2346
2344 2347 # compiled modules
2345 2348 ui.status(_("checking installed modules (%s)...\n")
2346 2349 % os.path.dirname(__file__))
2347 2350 try:
2348 2351 import bdiff, mpatch, base85, osutil
2349 2352 dir(bdiff), dir(mpatch), dir(base85), dir(osutil) # quiet pyflakes
2350 2353 except Exception as inst:
2351 2354 ui.write(" %s\n" % inst)
2352 2355 ui.write(_(" One or more extensions could not be found"))
2353 2356 ui.write(_(" (check that you compiled the extensions)\n"))
2354 2357 problems += 1
2355 2358
2356 2359 # templates
2357 2360 import templater
2358 2361 p = templater.templatepaths()
2359 2362 ui.status(_("checking templates (%s)...\n") % ' '.join(p))
2360 2363 if p:
2361 2364 m = templater.templatepath("map-cmdline.default")
2362 2365 if m:
2363 2366 # template found, check if it is working
2364 2367 try:
2365 2368 templater.templater(m)
2366 2369 except Exception as inst:
2367 2370 ui.write(" %s\n" % inst)
2368 2371 p = None
2369 2372 else:
2370 2373 ui.write(_(" template 'default' not found\n"))
2371 2374 p = None
2372 2375 else:
2373 2376 ui.write(_(" no template directories found\n"))
2374 2377 if not p:
2375 2378 ui.write(_(" (templates seem to have been installed incorrectly)\n"))
2376 2379 problems += 1
2377 2380
2378 2381 # editor
2379 2382 ui.status(_("checking commit editor...\n"))
2380 2383 editor = ui.geteditor()
2381 2384 editor = util.expandpath(editor)
2382 2385 cmdpath = util.findexe(shlex.split(editor)[0])
2383 2386 if not cmdpath:
2384 2387 if editor == 'vi':
2385 2388 ui.write(_(" No commit editor set and can't find vi in PATH\n"))
2386 2389 ui.write(_(" (specify a commit editor in your configuration"
2387 2390 " file)\n"))
2388 2391 else:
2389 2392 ui.write(_(" Can't find editor '%s' in PATH\n") % editor)
2390 2393 ui.write(_(" (specify a commit editor in your configuration"
2391 2394 " file)\n"))
2392 2395 problems += 1
2393 2396
2394 2397 # check username
2395 2398 ui.status(_("checking username...\n"))
2396 2399 try:
2397 2400 ui.username()
2398 2401 except util.Abort as e:
2399 2402 ui.write(" %s\n" % e)
2400 2403 ui.write(_(" (specify a username in your configuration file)\n"))
2401 2404 problems += 1
2402 2405
2403 2406 if not problems:
2404 2407 ui.status(_("no problems detected\n"))
2405 2408 else:
2406 2409 ui.write(_("%s problems detected,"
2407 2410 " please check your install!\n") % problems)
2408 2411
2409 2412 return problems
2410 2413
2411 2414 @command('debugknown', [], _('REPO ID...'), norepo=True)
2412 2415 def debugknown(ui, repopath, *ids, **opts):
2413 2416 """test whether node ids are known to a repo
2414 2417
2415 2418 Every ID must be a full-length hex node id string. Returns a list of 0s
2416 2419 and 1s indicating unknown/known.
2417 2420 """
2418 2421 repo = hg.peer(ui, opts, repopath)
2419 2422 if not repo.capable('known'):
2420 2423 raise util.Abort("known() not supported by target repository")
2421 2424 flags = repo.known([bin(s) for s in ids])
2422 2425 ui.write("%s\n" % ("".join([f and "1" or "0" for f in flags])))
2423 2426
2424 2427 @command('debuglabelcomplete', [], _('LABEL...'))
2425 2428 def debuglabelcomplete(ui, repo, *args):
2426 2429 '''backwards compatibility with old bash completion scripts (DEPRECATED)'''
2427 2430 debugnamecomplete(ui, repo, *args)
2428 2431
2429 2432 @command('debugnamecomplete', [], _('NAME...'))
2430 2433 def debugnamecomplete(ui, repo, *args):
2431 2434 '''complete "names" - tags, open branch names, bookmark names'''
2432 2435
2433 2436 names = set()
2434 2437 # since we previously only listed open branches, we will handle that
2435 2438 # specially (after this for loop)
2436 2439 for name, ns in repo.names.iteritems():
2437 2440 if name != 'branches':
2438 2441 names.update(ns.listnames(repo))
2439 2442 names.update(tag for (tag, heads, tip, closed)
2440 2443 in repo.branchmap().iterbranches() if not closed)
2441 2444 completions = set()
2442 2445 if not args:
2443 2446 args = ['']
2444 2447 for a in args:
2445 2448 completions.update(n for n in names if n.startswith(a))
2446 2449 ui.write('\n'.join(sorted(completions)))
2447 2450 ui.write('\n')
2448 2451
2449 2452 @command('debuglocks',
2450 2453 [('L', 'force-lock', None, _('free the store lock (DANGEROUS)')),
2451 2454 ('W', 'force-wlock', None,
2452 2455 _('free the working state lock (DANGEROUS)'))],
2453 2456 _('[OPTION]...'))
2454 2457 def debuglocks(ui, repo, **opts):
2455 2458 """show or modify state of locks
2456 2459
2457 2460 By default, this command will show which locks are held. This
2458 2461 includes the user and process holding the lock, the amount of time
2459 2462 the lock has been held, and the machine name where the process is
2460 2463 running if it's not local.
2461 2464
2462 2465 Locks protect the integrity of Mercurial's data, so should be
2463 2466 treated with care. System crashes or other interruptions may cause
2464 2467 locks to not be properly released, though Mercurial will usually
2465 2468 detect and remove such stale locks automatically.
2466 2469
2467 2470 However, detecting stale locks may not always be possible (for
2468 2471 instance, on a shared filesystem). Removing locks may also be
2469 2472 blocked by filesystem permissions.
2470 2473
2471 2474 Returns 0 if no locks are held.
2472 2475
2473 2476 """
2474 2477
2475 2478 if opts.get('force_lock'):
2476 2479 repo.svfs.unlink('lock')
2477 2480 if opts.get('force_wlock'):
2478 2481 repo.vfs.unlink('wlock')
2479 2482 if opts.get('force_lock') or opts.get('force_lock'):
2480 2483 return 0
2481 2484
2482 2485 now = time.time()
2483 2486 held = 0
2484 2487
2485 2488 def report(vfs, name, method):
2486 2489 # this causes stale locks to get reaped for more accurate reporting
2487 2490 try:
2488 2491 l = method(False)
2489 2492 except error.LockHeld:
2490 2493 l = None
2491 2494
2492 2495 if l:
2493 2496 l.release()
2494 2497 else:
2495 2498 try:
2496 2499 stat = vfs.lstat(name)
2497 2500 age = now - stat.st_mtime
2498 2501 user = util.username(stat.st_uid)
2499 2502 locker = vfs.readlock(name)
2500 2503 if ":" in locker:
2501 2504 host, pid = locker.split(':')
2502 2505 if host == socket.gethostname():
2503 2506 locker = 'user %s, process %s' % (user, pid)
2504 2507 else:
2505 2508 locker = 'user %s, process %s, host %s' \
2506 2509 % (user, pid, host)
2507 2510 ui.write("%-6s %s (%ds)\n" % (name + ":", locker, age))
2508 2511 return 1
2509 2512 except OSError as e:
2510 2513 if e.errno != errno.ENOENT:
2511 2514 raise
2512 2515
2513 2516 ui.write("%-6s free\n" % (name + ":"))
2514 2517 return 0
2515 2518
2516 2519 held += report(repo.svfs, "lock", repo.lock)
2517 2520 held += report(repo.vfs, "wlock", repo.wlock)
2518 2521
2519 2522 return held
2520 2523
2521 2524 @command('debugobsolete',
2522 2525 [('', 'flags', 0, _('markers flag')),
2523 2526 ('', 'record-parents', False,
2524 2527 _('record parent information for the precursor')),
2525 2528 ('r', 'rev', [], _('display markers relevant to REV')),
2526 2529 ] + commitopts2,
2527 2530 _('[OBSOLETED [REPLACEMENT] [REPL... ]'))
2528 2531 def debugobsolete(ui, repo, precursor=None, *successors, **opts):
2529 2532 """create arbitrary obsolete marker
2530 2533
2531 2534 With no arguments, displays the list of obsolescence markers."""
2532 2535
2533 2536 def parsenodeid(s):
2534 2537 try:
2535 2538 # We do not use revsingle/revrange functions here to accept
2536 2539 # arbitrary node identifiers, possibly not present in the
2537 2540 # local repository.
2538 2541 n = bin(s)
2539 2542 if len(n) != len(nullid):
2540 2543 raise TypeError()
2541 2544 return n
2542 2545 except TypeError:
2543 2546 raise util.Abort('changeset references must be full hexadecimal '
2544 2547 'node identifiers')
2545 2548
2546 2549 if precursor is not None:
2547 2550 if opts['rev']:
2548 2551 raise util.Abort('cannot select revision when creating marker')
2549 2552 metadata = {}
2550 2553 metadata['user'] = opts['user'] or ui.username()
2551 2554 succs = tuple(parsenodeid(succ) for succ in successors)
2552 2555 l = repo.lock()
2553 2556 try:
2554 2557 tr = repo.transaction('debugobsolete')
2555 2558 try:
2556 2559 date = opts.get('date')
2557 2560 if date:
2558 2561 date = util.parsedate(date)
2559 2562 else:
2560 2563 date = None
2561 2564 prec = parsenodeid(precursor)
2562 2565 parents = None
2563 2566 if opts['record_parents']:
2564 2567 if prec not in repo.unfiltered():
2565 2568 raise util.Abort('cannot used --record-parents on '
2566 2569 'unknown changesets')
2567 2570 parents = repo.unfiltered()[prec].parents()
2568 2571 parents = tuple(p.node() for p in parents)
2569 2572 repo.obsstore.create(tr, prec, succs, opts['flags'],
2570 2573 parents=parents, date=date,
2571 2574 metadata=metadata)
2572 2575 tr.close()
2573 2576 except ValueError as exc:
2574 2577 raise util.Abort(_('bad obsmarker input: %s') % exc)
2575 2578 finally:
2576 2579 tr.release()
2577 2580 finally:
2578 2581 l.release()
2579 2582 else:
2580 2583 if opts['rev']:
2581 2584 revs = scmutil.revrange(repo, opts['rev'])
2582 2585 nodes = [repo[r].node() for r in revs]
2583 2586 markers = list(obsolete.getmarkers(repo, nodes=nodes))
2584 2587 markers.sort(key=lambda x: x._data)
2585 2588 else:
2586 2589 markers = obsolete.getmarkers(repo)
2587 2590
2588 2591 for m in markers:
2589 2592 cmdutil.showmarker(ui, m)
2590 2593
2591 2594 @command('debugpathcomplete',
2592 2595 [('f', 'full', None, _('complete an entire path')),
2593 2596 ('n', 'normal', None, _('show only normal files')),
2594 2597 ('a', 'added', None, _('show only added files')),
2595 2598 ('r', 'removed', None, _('show only removed files'))],
2596 2599 _('FILESPEC...'))
2597 2600 def debugpathcomplete(ui, repo, *specs, **opts):
2598 2601 '''complete part or all of a tracked path
2599 2602
2600 2603 This command supports shells that offer path name completion. It
2601 2604 currently completes only files already known to the dirstate.
2602 2605
2603 2606 Completion extends only to the next path segment unless
2604 2607 --full is specified, in which case entire paths are used.'''
2605 2608
2606 2609 def complete(path, acceptable):
2607 2610 dirstate = repo.dirstate
2608 2611 spec = os.path.normpath(os.path.join(os.getcwd(), path))
2609 2612 rootdir = repo.root + os.sep
2610 2613 if spec != repo.root and not spec.startswith(rootdir):
2611 2614 return [], []
2612 2615 if os.path.isdir(spec):
2613 2616 spec += '/'
2614 2617 spec = spec[len(rootdir):]
2615 2618 fixpaths = os.sep != '/'
2616 2619 if fixpaths:
2617 2620 spec = spec.replace(os.sep, '/')
2618 2621 speclen = len(spec)
2619 2622 fullpaths = opts['full']
2620 2623 files, dirs = set(), set()
2621 2624 adddir, addfile = dirs.add, files.add
2622 2625 for f, st in dirstate.iteritems():
2623 2626 if f.startswith(spec) and st[0] in acceptable:
2624 2627 if fixpaths:
2625 2628 f = f.replace('/', os.sep)
2626 2629 if fullpaths:
2627 2630 addfile(f)
2628 2631 continue
2629 2632 s = f.find(os.sep, speclen)
2630 2633 if s >= 0:
2631 2634 adddir(f[:s])
2632 2635 else:
2633 2636 addfile(f)
2634 2637 return files, dirs
2635 2638
2636 2639 acceptable = ''
2637 2640 if opts['normal']:
2638 2641 acceptable += 'nm'
2639 2642 if opts['added']:
2640 2643 acceptable += 'a'
2641 2644 if opts['removed']:
2642 2645 acceptable += 'r'
2643 2646 cwd = repo.getcwd()
2644 2647 if not specs:
2645 2648 specs = ['.']
2646 2649
2647 2650 files, dirs = set(), set()
2648 2651 for spec in specs:
2649 2652 f, d = complete(spec, acceptable or 'nmar')
2650 2653 files.update(f)
2651 2654 dirs.update(d)
2652 2655 files.update(dirs)
2653 2656 ui.write('\n'.join(repo.pathto(p, cwd) for p in sorted(files)))
2654 2657 ui.write('\n')
2655 2658
2656 2659 @command('debugpushkey', [], _('REPO NAMESPACE [KEY OLD NEW]'), norepo=True)
2657 2660 def debugpushkey(ui, repopath, namespace, *keyinfo, **opts):
2658 2661 '''access the pushkey key/value protocol
2659 2662
2660 2663 With two args, list the keys in the given namespace.
2661 2664
2662 2665 With five args, set a key to new if it currently is set to old.
2663 2666 Reports success or failure.
2664 2667 '''
2665 2668
2666 2669 target = hg.peer(ui, {}, repopath)
2667 2670 if keyinfo:
2668 2671 key, old, new = keyinfo
2669 2672 r = target.pushkey(namespace, key, old, new)
2670 2673 ui.status(str(r) + '\n')
2671 2674 return not r
2672 2675 else:
2673 2676 for k, v in sorted(target.listkeys(namespace).iteritems()):
2674 2677 ui.write("%s\t%s\n" % (k.encode('string-escape'),
2675 2678 v.encode('string-escape')))
2676 2679
2677 2680 @command('debugpvec', [], _('A B'))
2678 2681 def debugpvec(ui, repo, a, b=None):
2679 2682 ca = scmutil.revsingle(repo, a)
2680 2683 cb = scmutil.revsingle(repo, b)
2681 2684 pa = pvec.ctxpvec(ca)
2682 2685 pb = pvec.ctxpvec(cb)
2683 2686 if pa == pb:
2684 2687 rel = "="
2685 2688 elif pa > pb:
2686 2689 rel = ">"
2687 2690 elif pa < pb:
2688 2691 rel = "<"
2689 2692 elif pa | pb:
2690 2693 rel = "|"
2691 2694 ui.write(_("a: %s\n") % pa)
2692 2695 ui.write(_("b: %s\n") % pb)
2693 2696 ui.write(_("depth(a): %d depth(b): %d\n") % (pa._depth, pb._depth))
2694 2697 ui.write(_("delta: %d hdist: %d distance: %d relation: %s\n") %
2695 2698 (abs(pa._depth - pb._depth), pvec._hamming(pa._vec, pb._vec),
2696 2699 pa.distance(pb), rel))
2697 2700
2698 2701 @command('debugrebuilddirstate|debugrebuildstate',
2699 2702 [('r', 'rev', '', _('revision to rebuild to'), _('REV'))],
2700 2703 _('[-r REV]'))
2701 2704 def debugrebuilddirstate(ui, repo, rev):
2702 2705 """rebuild the dirstate as it would look like for the given revision
2703 2706
2704 2707 If no revision is specified the first current parent will be used.
2705 2708
2706 2709 The dirstate will be set to the files of the given revision.
2707 2710 The actual working directory content or existing dirstate
2708 2711 information such as adds or removes is not considered.
2709 2712
2710 2713 One use of this command is to make the next :hg:`status` invocation
2711 2714 check the actual file content.
2712 2715 """
2713 2716 ctx = scmutil.revsingle(repo, rev)
2714 2717 wlock = repo.wlock()
2715 2718 try:
2716 2719 repo.dirstate.rebuild(ctx.node(), ctx.manifest())
2717 2720 finally:
2718 2721 wlock.release()
2719 2722
2720 2723 @command('debugrebuildfncache', [], '')
2721 2724 def debugrebuildfncache(ui, repo):
2722 2725 """rebuild the fncache file"""
2723 2726 repair.rebuildfncache(ui, repo)
2724 2727
2725 2728 @command('debugrename',
2726 2729 [('r', 'rev', '', _('revision to debug'), _('REV'))],
2727 2730 _('[-r REV] FILE'))
2728 2731 def debugrename(ui, repo, file1, *pats, **opts):
2729 2732 """dump rename information"""
2730 2733
2731 2734 ctx = scmutil.revsingle(repo, opts.get('rev'))
2732 2735 m = scmutil.match(ctx, (file1,) + pats, opts)
2733 2736 for abs in ctx.walk(m):
2734 2737 fctx = ctx[abs]
2735 2738 o = fctx.filelog().renamed(fctx.filenode())
2736 2739 rel = m.rel(abs)
2737 2740 if o:
2738 2741 ui.write(_("%s renamed from %s:%s\n") % (rel, o[0], hex(o[1])))
2739 2742 else:
2740 2743 ui.write(_("%s not renamed\n") % rel)
2741 2744
2742 2745 @command('debugrevlog',
2743 2746 [('c', 'changelog', False, _('open changelog')),
2744 2747 ('m', 'manifest', False, _('open manifest')),
2745 2748 ('', 'dir', False, _('open directory manifest')),
2746 2749 ('d', 'dump', False, _('dump index data'))],
2747 2750 _('-c|-m|FILE'),
2748 2751 optionalrepo=True)
2749 2752 def debugrevlog(ui, repo, file_=None, **opts):
2750 2753 """show data and statistics about a revlog"""
2751 2754 r = cmdutil.openrevlog(repo, 'debugrevlog', file_, opts)
2752 2755
2753 2756 if opts.get("dump"):
2754 2757 numrevs = len(r)
2755 2758 ui.write("# rev p1rev p2rev start end deltastart base p1 p2"
2756 2759 " rawsize totalsize compression heads chainlen\n")
2757 2760 ts = 0
2758 2761 heads = set()
2759 2762
2760 2763 for rev in xrange(numrevs):
2761 2764 dbase = r.deltaparent(rev)
2762 2765 if dbase == -1:
2763 2766 dbase = rev
2764 2767 cbase = r.chainbase(rev)
2765 2768 clen = r.chainlen(rev)
2766 2769 p1, p2 = r.parentrevs(rev)
2767 2770 rs = r.rawsize(rev)
2768 2771 ts = ts + rs
2769 2772 heads -= set(r.parentrevs(rev))
2770 2773 heads.add(rev)
2771 2774 ui.write("%5d %5d %5d %5d %5d %10d %4d %4d %4d %7d %9d "
2772 2775 "%11d %5d %8d\n" %
2773 2776 (rev, p1, p2, r.start(rev), r.end(rev),
2774 2777 r.start(dbase), r.start(cbase),
2775 2778 r.start(p1), r.start(p2),
2776 2779 rs, ts, ts / r.end(rev), len(heads), clen))
2777 2780 return 0
2778 2781
2779 2782 v = r.version
2780 2783 format = v & 0xFFFF
2781 2784 flags = []
2782 2785 gdelta = False
2783 2786 if v & revlog.REVLOGNGINLINEDATA:
2784 2787 flags.append('inline')
2785 2788 if v & revlog.REVLOGGENERALDELTA:
2786 2789 gdelta = True
2787 2790 flags.append('generaldelta')
2788 2791 if not flags:
2789 2792 flags = ['(none)']
2790 2793
2791 2794 nummerges = 0
2792 2795 numfull = 0
2793 2796 numprev = 0
2794 2797 nump1 = 0
2795 2798 nump2 = 0
2796 2799 numother = 0
2797 2800 nump1prev = 0
2798 2801 nump2prev = 0
2799 2802 chainlengths = []
2800 2803
2801 2804 datasize = [None, 0, 0L]
2802 2805 fullsize = [None, 0, 0L]
2803 2806 deltasize = [None, 0, 0L]
2804 2807
2805 2808 def addsize(size, l):
2806 2809 if l[0] is None or size < l[0]:
2807 2810 l[0] = size
2808 2811 if size > l[1]:
2809 2812 l[1] = size
2810 2813 l[2] += size
2811 2814
2812 2815 numrevs = len(r)
2813 2816 for rev in xrange(numrevs):
2814 2817 p1, p2 = r.parentrevs(rev)
2815 2818 delta = r.deltaparent(rev)
2816 2819 if format > 0:
2817 2820 addsize(r.rawsize(rev), datasize)
2818 2821 if p2 != nullrev:
2819 2822 nummerges += 1
2820 2823 size = r.length(rev)
2821 2824 if delta == nullrev:
2822 2825 chainlengths.append(0)
2823 2826 numfull += 1
2824 2827 addsize(size, fullsize)
2825 2828 else:
2826 2829 chainlengths.append(chainlengths[delta] + 1)
2827 2830 addsize(size, deltasize)
2828 2831 if delta == rev - 1:
2829 2832 numprev += 1
2830 2833 if delta == p1:
2831 2834 nump1prev += 1
2832 2835 elif delta == p2:
2833 2836 nump2prev += 1
2834 2837 elif delta == p1:
2835 2838 nump1 += 1
2836 2839 elif delta == p2:
2837 2840 nump2 += 1
2838 2841 elif delta != nullrev:
2839 2842 numother += 1
2840 2843
2841 2844 # Adjust size min value for empty cases
2842 2845 for size in (datasize, fullsize, deltasize):
2843 2846 if size[0] is None:
2844 2847 size[0] = 0
2845 2848
2846 2849 numdeltas = numrevs - numfull
2847 2850 numoprev = numprev - nump1prev - nump2prev
2848 2851 totalrawsize = datasize[2]
2849 2852 datasize[2] /= numrevs
2850 2853 fulltotal = fullsize[2]
2851 2854 fullsize[2] /= numfull
2852 2855 deltatotal = deltasize[2]
2853 2856 if numrevs - numfull > 0:
2854 2857 deltasize[2] /= numrevs - numfull
2855 2858 totalsize = fulltotal + deltatotal
2856 2859 avgchainlen = sum(chainlengths) / numrevs
2857 2860 maxchainlen = max(chainlengths)
2858 2861 compratio = totalrawsize / totalsize
2859 2862
2860 2863 basedfmtstr = '%%%dd\n'
2861 2864 basepcfmtstr = '%%%dd %s(%%5.2f%%%%)\n'
2862 2865
2863 2866 def dfmtstr(max):
2864 2867 return basedfmtstr % len(str(max))
2865 2868 def pcfmtstr(max, padding=0):
2866 2869 return basepcfmtstr % (len(str(max)), ' ' * padding)
2867 2870
2868 2871 def pcfmt(value, total):
2869 2872 return (value, 100 * float(value) / total)
2870 2873
2871 2874 ui.write(('format : %d\n') % format)
2872 2875 ui.write(('flags : %s\n') % ', '.join(flags))
2873 2876
2874 2877 ui.write('\n')
2875 2878 fmt = pcfmtstr(totalsize)
2876 2879 fmt2 = dfmtstr(totalsize)
2877 2880 ui.write(('revisions : ') + fmt2 % numrevs)
2878 2881 ui.write((' merges : ') + fmt % pcfmt(nummerges, numrevs))
2879 2882 ui.write((' normal : ') + fmt % pcfmt(numrevs - nummerges, numrevs))
2880 2883 ui.write(('revisions : ') + fmt2 % numrevs)
2881 2884 ui.write((' full : ') + fmt % pcfmt(numfull, numrevs))
2882 2885 ui.write((' deltas : ') + fmt % pcfmt(numdeltas, numrevs))
2883 2886 ui.write(('revision size : ') + fmt2 % totalsize)
2884 2887 ui.write((' full : ') + fmt % pcfmt(fulltotal, totalsize))
2885 2888 ui.write((' deltas : ') + fmt % pcfmt(deltatotal, totalsize))
2886 2889
2887 2890 ui.write('\n')
2888 2891 fmt = dfmtstr(max(avgchainlen, compratio))
2889 2892 ui.write(('avg chain length : ') + fmt % avgchainlen)
2890 2893 ui.write(('max chain length : ') + fmt % maxchainlen)
2891 2894 ui.write(('compression ratio : ') + fmt % compratio)
2892 2895
2893 2896 if format > 0:
2894 2897 ui.write('\n')
2895 2898 ui.write(('uncompressed data size (min/max/avg) : %d / %d / %d\n')
2896 2899 % tuple(datasize))
2897 2900 ui.write(('full revision size (min/max/avg) : %d / %d / %d\n')
2898 2901 % tuple(fullsize))
2899 2902 ui.write(('delta size (min/max/avg) : %d / %d / %d\n')
2900 2903 % tuple(deltasize))
2901 2904
2902 2905 if numdeltas > 0:
2903 2906 ui.write('\n')
2904 2907 fmt = pcfmtstr(numdeltas)
2905 2908 fmt2 = pcfmtstr(numdeltas, 4)
2906 2909 ui.write(('deltas against prev : ') + fmt % pcfmt(numprev, numdeltas))
2907 2910 if numprev > 0:
2908 2911 ui.write((' where prev = p1 : ') + fmt2 % pcfmt(nump1prev,
2909 2912 numprev))
2910 2913 ui.write((' where prev = p2 : ') + fmt2 % pcfmt(nump2prev,
2911 2914 numprev))
2912 2915 ui.write((' other : ') + fmt2 % pcfmt(numoprev,
2913 2916 numprev))
2914 2917 if gdelta:
2915 2918 ui.write(('deltas against p1 : ')
2916 2919 + fmt % pcfmt(nump1, numdeltas))
2917 2920 ui.write(('deltas against p2 : ')
2918 2921 + fmt % pcfmt(nump2, numdeltas))
2919 2922 ui.write(('deltas against other : ') + fmt % pcfmt(numother,
2920 2923 numdeltas))
2921 2924
2922 2925 @command('debugrevspec',
2923 2926 [('', 'optimize', None, _('print parsed tree after optimizing'))],
2924 2927 ('REVSPEC'))
2925 2928 def debugrevspec(ui, repo, expr, **opts):
2926 2929 """parse and apply a revision specification
2927 2930
2928 2931 Use --verbose to print the parsed tree before and after aliases
2929 2932 expansion.
2930 2933 """
2931 2934 if ui.verbose:
2932 2935 tree = revset.parse(expr)
2933 2936 ui.note(revset.prettyformat(tree), "\n")
2934 2937 newtree = revset.findaliases(ui, tree)
2935 2938 if newtree != tree:
2936 2939 ui.note(revset.prettyformat(newtree), "\n")
2937 2940 tree = newtree
2938 2941 newtree = revset.foldconcat(tree)
2939 2942 if newtree != tree:
2940 2943 ui.note(revset.prettyformat(newtree), "\n")
2941 2944 if opts["optimize"]:
2942 2945 weight, optimizedtree = revset.optimize(newtree, True)
2943 2946 ui.note("* optimized:\n", revset.prettyformat(optimizedtree), "\n")
2944 2947 func = revset.match(ui, expr)
2945 2948 revs = func(repo)
2946 2949 if ui.verbose:
2947 2950 ui.note("* set:\n", revset.prettyformatset(revs), "\n")
2948 2951 for c in revs:
2949 2952 ui.write("%s\n" % c)
2950 2953
2951 2954 @command('debugsetparents', [], _('REV1 [REV2]'))
2952 2955 def debugsetparents(ui, repo, rev1, rev2=None):
2953 2956 """manually set the parents of the current working directory
2954 2957
2955 2958 This is useful for writing repository conversion tools, but should
2956 2959 be used with care. For example, neither the working directory nor the
2957 2960 dirstate is updated, so file status may be incorrect after running this
2958 2961 command.
2959 2962
2960 2963 Returns 0 on success.
2961 2964 """
2962 2965
2963 2966 r1 = scmutil.revsingle(repo, rev1).node()
2964 2967 r2 = scmutil.revsingle(repo, rev2, 'null').node()
2965 2968
2966 2969 wlock = repo.wlock()
2967 2970 try:
2968 2971 repo.dirstate.beginparentchange()
2969 2972 repo.setparents(r1, r2)
2970 2973 repo.dirstate.endparentchange()
2971 2974 finally:
2972 2975 wlock.release()
2973 2976
2974 2977 @command('debugdirstate|debugstate',
2975 2978 [('', 'nodates', None, _('do not display the saved mtime')),
2976 2979 ('', 'datesort', None, _('sort by saved mtime'))],
2977 2980 _('[OPTION]...'))
2978 2981 def debugstate(ui, repo, nodates=None, datesort=None):
2979 2982 """show the contents of the current dirstate"""
2980 2983 timestr = ""
2981 2984 if datesort:
2982 2985 keyfunc = lambda x: (x[1][3], x[0]) # sort by mtime, then by filename
2983 2986 else:
2984 2987 keyfunc = None # sort by filename
2985 2988 for file_, ent in sorted(repo.dirstate._map.iteritems(), key=keyfunc):
2986 2989 if ent[3] == -1:
2987 2990 timestr = 'unset '
2988 2991 elif nodates:
2989 2992 timestr = 'set '
2990 2993 else:
2991 2994 timestr = time.strftime("%Y-%m-%d %H:%M:%S ",
2992 2995 time.localtime(ent[3]))
2993 2996 if ent[1] & 0o20000:
2994 2997 mode = 'lnk'
2995 2998 else:
2996 2999 mode = '%3o' % (ent[1] & 0o777 & ~util.umask)
2997 3000 ui.write("%c %s %10d %s%s\n" % (ent[0], mode, ent[2], timestr, file_))
2998 3001 for f in repo.dirstate.copies():
2999 3002 ui.write(_("copy: %s -> %s\n") % (repo.dirstate.copied(f), f))
3000 3003
3001 3004 @command('debugsub',
3002 3005 [('r', 'rev', '',
3003 3006 _('revision to check'), _('REV'))],
3004 3007 _('[-r REV] [REV]'))
3005 3008 def debugsub(ui, repo, rev=None):
3006 3009 ctx = scmutil.revsingle(repo, rev, None)
3007 3010 for k, v in sorted(ctx.substate.items()):
3008 3011 ui.write(('path %s\n') % k)
3009 3012 ui.write((' source %s\n') % v[0])
3010 3013 ui.write((' revision %s\n') % v[1])
3011 3014
3012 3015 @command('debugsuccessorssets',
3013 3016 [],
3014 3017 _('[REV]'))
3015 3018 def debugsuccessorssets(ui, repo, *revs):
3016 3019 """show set of successors for revision
3017 3020
3018 3021 A successors set of changeset A is a consistent group of revisions that
3019 3022 succeed A. It contains non-obsolete changesets only.
3020 3023
3021 3024 In most cases a changeset A has a single successors set containing a single
3022 3025 successor (changeset A replaced by A').
3023 3026
3024 3027 A changeset that is made obsolete with no successors are called "pruned".
3025 3028 Such changesets have no successors sets at all.
3026 3029
3027 3030 A changeset that has been "split" will have a successors set containing
3028 3031 more than one successor.
3029 3032
3030 3033 A changeset that has been rewritten in multiple different ways is called
3031 3034 "divergent". Such changesets have multiple successor sets (each of which
3032 3035 may also be split, i.e. have multiple successors).
3033 3036
3034 3037 Results are displayed as follows::
3035 3038
3036 3039 <rev1>
3037 3040 <successors-1A>
3038 3041 <rev2>
3039 3042 <successors-2A>
3040 3043 <successors-2B1> <successors-2B2> <successors-2B3>
3041 3044
3042 3045 Here rev2 has two possible (i.e. divergent) successors sets. The first
3043 3046 holds one element, whereas the second holds three (i.e. the changeset has
3044 3047 been split).
3045 3048 """
3046 3049 # passed to successorssets caching computation from one call to another
3047 3050 cache = {}
3048 3051 ctx2str = str
3049 3052 node2str = short
3050 3053 if ui.debug():
3051 3054 def ctx2str(ctx):
3052 3055 return ctx.hex()
3053 3056 node2str = hex
3054 3057 for rev in scmutil.revrange(repo, revs):
3055 3058 ctx = repo[rev]
3056 3059 ui.write('%s\n'% ctx2str(ctx))
3057 3060 for succsset in obsolete.successorssets(repo, ctx.node(), cache):
3058 3061 if succsset:
3059 3062 ui.write(' ')
3060 3063 ui.write(node2str(succsset[0]))
3061 3064 for node in succsset[1:]:
3062 3065 ui.write(' ')
3063 3066 ui.write(node2str(node))
3064 3067 ui.write('\n')
3065 3068
3066 3069 @command('debugwalk', walkopts, _('[OPTION]... [FILE]...'), inferrepo=True)
3067 3070 def debugwalk(ui, repo, *pats, **opts):
3068 3071 """show how files match on given patterns"""
3069 3072 m = scmutil.match(repo[None], pats, opts)
3070 3073 items = list(repo.walk(m))
3071 3074 if not items:
3072 3075 return
3073 3076 f = lambda fn: fn
3074 3077 if ui.configbool('ui', 'slash') and os.sep != '/':
3075 3078 f = lambda fn: util.normpath(fn)
3076 3079 fmt = 'f %%-%ds %%-%ds %%s' % (
3077 3080 max([len(abs) for abs in items]),
3078 3081 max([len(m.rel(abs)) for abs in items]))
3079 3082 for abs in items:
3080 3083 line = fmt % (abs, f(m.rel(abs)), m.exact(abs) and 'exact' or '')
3081 3084 ui.write("%s\n" % line.rstrip())
3082 3085
3083 3086 @command('debugwireargs',
3084 3087 [('', 'three', '', 'three'),
3085 3088 ('', 'four', '', 'four'),
3086 3089 ('', 'five', '', 'five'),
3087 3090 ] + remoteopts,
3088 3091 _('REPO [OPTIONS]... [ONE [TWO]]'),
3089 3092 norepo=True)
3090 3093 def debugwireargs(ui, repopath, *vals, **opts):
3091 3094 repo = hg.peer(ui, opts, repopath)
3092 3095 for opt in remoteopts:
3093 3096 del opts[opt[1]]
3094 3097 args = {}
3095 3098 for k, v in opts.iteritems():
3096 3099 if v:
3097 3100 args[k] = v
3098 3101 # run twice to check that we don't mess up the stream for the next command
3099 3102 res1 = repo.debugwireargs(*vals, **args)
3100 3103 res2 = repo.debugwireargs(*vals, **args)
3101 3104 ui.write("%s\n" % res1)
3102 3105 if res1 != res2:
3103 3106 ui.warn("%s\n" % res2)
3104 3107
3105 3108 @command('^diff',
3106 3109 [('r', 'rev', [], _('revision'), _('REV')),
3107 3110 ('c', 'change', '', _('change made by revision'), _('REV'))
3108 3111 ] + diffopts + diffopts2 + walkopts + subrepoopts,
3109 3112 _('[OPTION]... ([-c REV] | [-r REV1 [-r REV2]]) [FILE]...'),
3110 3113 inferrepo=True)
3111 3114 def diff(ui, repo, *pats, **opts):
3112 3115 """diff repository (or selected files)
3113 3116
3114 3117 Show differences between revisions for the specified files.
3115 3118
3116 3119 Differences between files are shown using the unified diff format.
3117 3120
3118 3121 .. note::
3119 3122
3120 3123 diff may generate unexpected results for merges, as it will
3121 3124 default to comparing against the working directory's first
3122 3125 parent changeset if no revisions are specified.
3123 3126
3124 3127 When two revision arguments are given, then changes are shown
3125 3128 between those revisions. If only one revision is specified then
3126 3129 that revision is compared to the working directory, and, when no
3127 3130 revisions are specified, the working directory files are compared
3128 3131 to its parent.
3129 3132
3130 3133 Alternatively you can specify -c/--change with a revision to see
3131 3134 the changes in that changeset relative to its first parent.
3132 3135
3133 3136 Without the -a/--text option, diff will avoid generating diffs of
3134 3137 files it detects as binary. With -a, diff will generate a diff
3135 3138 anyway, probably with undesirable results.
3136 3139
3137 3140 Use the -g/--git option to generate diffs in the git extended diff
3138 3141 format. For more information, read :hg:`help diffs`.
3139 3142
3140 3143 .. container:: verbose
3141 3144
3142 3145 Examples:
3143 3146
3144 3147 - compare a file in the current working directory to its parent::
3145 3148
3146 3149 hg diff foo.c
3147 3150
3148 3151 - compare two historical versions of a directory, with rename info::
3149 3152
3150 3153 hg diff --git -r 1.0:1.2 lib/
3151 3154
3152 3155 - get change stats relative to the last change on some date::
3153 3156
3154 3157 hg diff --stat -r "date('may 2')"
3155 3158
3156 3159 - diff all newly-added files that contain a keyword::
3157 3160
3158 3161 hg diff "set:added() and grep(GNU)"
3159 3162
3160 3163 - compare a revision and its parents::
3161 3164
3162 3165 hg diff -c 9353 # compare against first parent
3163 3166 hg diff -r 9353^:9353 # same using revset syntax
3164 3167 hg diff -r 9353^2:9353 # compare against the second parent
3165 3168
3166 3169 Returns 0 on success.
3167 3170 """
3168 3171
3169 3172 revs = opts.get('rev')
3170 3173 change = opts.get('change')
3171 3174 stat = opts.get('stat')
3172 3175 reverse = opts.get('reverse')
3173 3176
3174 3177 if revs and change:
3175 3178 msg = _('cannot specify --rev and --change at the same time')
3176 3179 raise util.Abort(msg)
3177 3180 elif change:
3178 3181 node2 = scmutil.revsingle(repo, change, None).node()
3179 3182 node1 = repo[node2].p1().node()
3180 3183 else:
3181 3184 node1, node2 = scmutil.revpair(repo, revs)
3182 3185
3183 3186 if reverse:
3184 3187 node1, node2 = node2, node1
3185 3188
3186 3189 diffopts = patch.diffallopts(ui, opts)
3187 3190 m = scmutil.match(repo[node2], pats, opts)
3188 3191 cmdutil.diffordiffstat(ui, repo, diffopts, node1, node2, m, stat=stat,
3189 3192 listsubrepos=opts.get('subrepos'),
3190 3193 root=opts.get('root'))
3191 3194
3192 3195 @command('^export',
3193 3196 [('o', 'output', '',
3194 3197 _('print output to file with formatted name'), _('FORMAT')),
3195 3198 ('', 'switch-parent', None, _('diff against the second parent')),
3196 3199 ('r', 'rev', [], _('revisions to export'), _('REV')),
3197 3200 ] + diffopts,
3198 3201 _('[OPTION]... [-o OUTFILESPEC] [-r] [REV]...'))
3199 3202 def export(ui, repo, *changesets, **opts):
3200 3203 """dump the header and diffs for one or more changesets
3201 3204
3202 3205 Print the changeset header and diffs for one or more revisions.
3203 3206 If no revision is given, the parent of the working directory is used.
3204 3207
3205 3208 The information shown in the changeset header is: author, date,
3206 3209 branch name (if non-default), changeset hash, parent(s) and commit
3207 3210 comment.
3208 3211
3209 3212 .. note::
3210 3213
3211 3214 export may generate unexpected diff output for merge
3212 3215 changesets, as it will compare the merge changeset against its
3213 3216 first parent only.
3214 3217
3215 3218 Output may be to a file, in which case the name of the file is
3216 3219 given using a format string. The formatting rules are as follows:
3217 3220
3218 3221 :``%%``: literal "%" character
3219 3222 :``%H``: changeset hash (40 hexadecimal digits)
3220 3223 :``%N``: number of patches being generated
3221 3224 :``%R``: changeset revision number
3222 3225 :``%b``: basename of the exporting repository
3223 3226 :``%h``: short-form changeset hash (12 hexadecimal digits)
3224 3227 :``%m``: first line of the commit message (only alphanumeric characters)
3225 3228 :``%n``: zero-padded sequence number, starting at 1
3226 3229 :``%r``: zero-padded changeset revision number
3227 3230
3228 3231 Without the -a/--text option, export will avoid generating diffs
3229 3232 of files it detects as binary. With -a, export will generate a
3230 3233 diff anyway, probably with undesirable results.
3231 3234
3232 3235 Use the -g/--git option to generate diffs in the git extended diff
3233 3236 format. See :hg:`help diffs` for more information.
3234 3237
3235 3238 With the --switch-parent option, the diff will be against the
3236 3239 second parent. It can be useful to review a merge.
3237 3240
3238 3241 .. container:: verbose
3239 3242
3240 3243 Examples:
3241 3244
3242 3245 - use export and import to transplant a bugfix to the current
3243 3246 branch::
3244 3247
3245 3248 hg export -r 9353 | hg import -
3246 3249
3247 3250 - export all the changesets between two revisions to a file with
3248 3251 rename information::
3249 3252
3250 3253 hg export --git -r 123:150 > changes.txt
3251 3254
3252 3255 - split outgoing changes into a series of patches with
3253 3256 descriptive names::
3254 3257
3255 3258 hg export -r "outgoing()" -o "%n-%m.patch"
3256 3259
3257 3260 Returns 0 on success.
3258 3261 """
3259 3262 changesets += tuple(opts.get('rev', []))
3260 3263 if not changesets:
3261 3264 changesets = ['.']
3262 3265 revs = scmutil.revrange(repo, changesets)
3263 3266 if not revs:
3264 3267 raise util.Abort(_("export requires at least one changeset"))
3265 3268 if len(revs) > 1:
3266 3269 ui.note(_('exporting patches:\n'))
3267 3270 else:
3268 3271 ui.note(_('exporting patch:\n'))
3269 3272 cmdutil.export(repo, revs, template=opts.get('output'),
3270 3273 switch_parent=opts.get('switch_parent'),
3271 3274 opts=patch.diffallopts(ui, opts))
3272 3275
3273 3276 @command('files',
3274 3277 [('r', 'rev', '', _('search the repository as it is in REV'), _('REV')),
3275 3278 ('0', 'print0', None, _('end filenames with NUL, for use with xargs')),
3276 3279 ] + walkopts + formatteropts + subrepoopts,
3277 3280 _('[OPTION]... [PATTERN]...'))
3278 3281 def files(ui, repo, *pats, **opts):
3279 3282 """list tracked files
3280 3283
3281 3284 Print files under Mercurial control in the working directory or
3282 3285 specified revision whose names match the given patterns (excluding
3283 3286 removed files).
3284 3287
3285 3288 If no patterns are given to match, this command prints the names
3286 3289 of all files under Mercurial control in the working directory.
3287 3290
3288 3291 .. container:: verbose
3289 3292
3290 3293 Examples:
3291 3294
3292 3295 - list all files under the current directory::
3293 3296
3294 3297 hg files .
3295 3298
3296 3299 - shows sizes and flags for current revision::
3297 3300
3298 3301 hg files -vr .
3299 3302
3300 3303 - list all files named README::
3301 3304
3302 3305 hg files -I "**/README"
3303 3306
3304 3307 - list all binary files::
3305 3308
3306 3309 hg files "set:binary()"
3307 3310
3308 3311 - find files containing a regular expression::
3309 3312
3310 3313 hg files "set:grep('bob')"
3311 3314
3312 3315 - search tracked file contents with xargs and grep::
3313 3316
3314 3317 hg files -0 | xargs -0 grep foo
3315 3318
3316 3319 See :hg:`help patterns` and :hg:`help filesets` for more information
3317 3320 on specifying file patterns.
3318 3321
3319 3322 Returns 0 if a match is found, 1 otherwise.
3320 3323
3321 3324 """
3322 3325 ctx = scmutil.revsingle(repo, opts.get('rev'), None)
3323 3326
3324 3327 end = '\n'
3325 3328 if opts.get('print0'):
3326 3329 end = '\0'
3327 3330 fm = ui.formatter('files', opts)
3328 3331 fmt = '%s' + end
3329 3332
3330 3333 m = scmutil.match(ctx, pats, opts)
3331 3334 ret = cmdutil.files(ui, ctx, m, fm, fmt, opts.get('subrepos'))
3332 3335
3333 3336 fm.end()
3334 3337
3335 3338 return ret
3336 3339
3337 3340 @command('^forget', walkopts, _('[OPTION]... FILE...'), inferrepo=True)
3338 3341 def forget(ui, repo, *pats, **opts):
3339 3342 """forget the specified files on the next commit
3340 3343
3341 3344 Mark the specified files so they will no longer be tracked
3342 3345 after the next commit.
3343 3346
3344 3347 This only removes files from the current branch, not from the
3345 3348 entire project history, and it does not delete them from the
3346 3349 working directory.
3347 3350
3348 3351 To delete the file from the working directory, see :hg:`remove`.
3349 3352
3350 3353 To undo a forget before the next commit, see :hg:`add`.
3351 3354
3352 3355 .. container:: verbose
3353 3356
3354 3357 Examples:
3355 3358
3356 3359 - forget newly-added binary files::
3357 3360
3358 3361 hg forget "set:added() and binary()"
3359 3362
3360 3363 - forget files that would be excluded by .hgignore::
3361 3364
3362 3365 hg forget "set:hgignore()"
3363 3366
3364 3367 Returns 0 on success.
3365 3368 """
3366 3369
3367 3370 if not pats:
3368 3371 raise util.Abort(_('no files specified'))
3369 3372
3370 3373 m = scmutil.match(repo[None], pats, opts)
3371 3374 rejected = cmdutil.forget(ui, repo, m, prefix="", explicitonly=False)[0]
3372 3375 return rejected and 1 or 0
3373 3376
3374 3377 @command(
3375 3378 'graft',
3376 3379 [('r', 'rev', [], _('revisions to graft'), _('REV')),
3377 3380 ('c', 'continue', False, _('resume interrupted graft')),
3378 3381 ('e', 'edit', False, _('invoke editor on commit messages')),
3379 3382 ('', 'log', None, _('append graft info to log message')),
3380 3383 ('f', 'force', False, _('force graft')),
3381 3384 ('D', 'currentdate', False,
3382 3385 _('record the current date as commit date')),
3383 3386 ('U', 'currentuser', False,
3384 3387 _('record the current user as committer'), _('DATE'))]
3385 3388 + commitopts2 + mergetoolopts + dryrunopts,
3386 3389 _('[OPTION]... [-r] REV...'))
3387 3390 def graft(ui, repo, *revs, **opts):
3388 3391 '''copy changes from other branches onto the current branch
3389 3392
3390 3393 This command uses Mercurial's merge logic to copy individual
3391 3394 changes from other branches without merging branches in the
3392 3395 history graph. This is sometimes known as 'backporting' or
3393 3396 'cherry-picking'. By default, graft will copy user, date, and
3394 3397 description from the source changesets.
3395 3398
3396 3399 Changesets that are ancestors of the current revision, that have
3397 3400 already been grafted, or that are merges will be skipped.
3398 3401
3399 3402 If --log is specified, log messages will have a comment appended
3400 3403 of the form::
3401 3404
3402 3405 (grafted from CHANGESETHASH)
3403 3406
3404 3407 If --force is specified, revisions will be grafted even if they
3405 3408 are already ancestors of or have been grafted to the destination.
3406 3409 This is useful when the revisions have since been backed out.
3407 3410
3408 3411 If a graft merge results in conflicts, the graft process is
3409 3412 interrupted so that the current merge can be manually resolved.
3410 3413 Once all conflicts are addressed, the graft process can be
3411 3414 continued with the -c/--continue option.
3412 3415
3413 3416 .. note::
3414 3417
3415 3418 The -c/--continue option does not reapply earlier options, except
3416 3419 for --force.
3417 3420
3418 3421 .. container:: verbose
3419 3422
3420 3423 Examples:
3421 3424
3422 3425 - copy a single change to the stable branch and edit its description::
3423 3426
3424 3427 hg update stable
3425 3428 hg graft --edit 9393
3426 3429
3427 3430 - graft a range of changesets with one exception, updating dates::
3428 3431
3429 3432 hg graft -D "2085::2093 and not 2091"
3430 3433
3431 3434 - continue a graft after resolving conflicts::
3432 3435
3433 3436 hg graft -c
3434 3437
3435 3438 - show the source of a grafted changeset::
3436 3439
3437 3440 hg log --debug -r .
3438 3441
3439 3442 See :hg:`help revisions` and :hg:`help revsets` for more about
3440 3443 specifying revisions.
3441 3444
3442 3445 Returns 0 on successful completion.
3443 3446 '''
3444 3447
3445 3448 revs = list(revs)
3446 3449 revs.extend(opts['rev'])
3447 3450
3448 3451 if not opts.get('user') and opts.get('currentuser'):
3449 3452 opts['user'] = ui.username()
3450 3453 if not opts.get('date') and opts.get('currentdate'):
3451 3454 opts['date'] = "%d %d" % util.makedate()
3452 3455
3453 3456 editor = cmdutil.getcommiteditor(editform='graft', **opts)
3454 3457
3455 3458 cont = False
3456 3459 if opts['continue']:
3457 3460 cont = True
3458 3461 if revs:
3459 3462 raise util.Abort(_("can't specify --continue and revisions"))
3460 3463 # read in unfinished revisions
3461 3464 try:
3462 3465 nodes = repo.vfs.read('graftstate').splitlines()
3463 3466 revs = [repo[node].rev() for node in nodes]
3464 3467 except IOError as inst:
3465 3468 if inst.errno != errno.ENOENT:
3466 3469 raise
3467 3470 raise util.Abort(_("no graft state found, can't continue"))
3468 3471 else:
3469 3472 cmdutil.checkunfinished(repo)
3470 3473 cmdutil.bailifchanged(repo)
3471 3474 if not revs:
3472 3475 raise util.Abort(_('no revisions specified'))
3473 3476 revs = scmutil.revrange(repo, revs)
3474 3477
3475 3478 skipped = set()
3476 3479 # check for merges
3477 3480 for rev in repo.revs('%ld and merge()', revs):
3478 3481 ui.warn(_('skipping ungraftable merge revision %s\n') % rev)
3479 3482 skipped.add(rev)
3480 3483 revs = [r for r in revs if r not in skipped]
3481 3484 if not revs:
3482 3485 return -1
3483 3486
3484 3487 # Don't check in the --continue case, in effect retaining --force across
3485 3488 # --continues. That's because without --force, any revisions we decided to
3486 3489 # skip would have been filtered out here, so they wouldn't have made their
3487 3490 # way to the graftstate. With --force, any revisions we would have otherwise
3488 3491 # skipped would not have been filtered out, and if they hadn't been applied
3489 3492 # already, they'd have been in the graftstate.
3490 3493 if not (cont or opts.get('force')):
3491 3494 # check for ancestors of dest branch
3492 3495 crev = repo['.'].rev()
3493 3496 ancestors = repo.changelog.ancestors([crev], inclusive=True)
3494 3497 # Cannot use x.remove(y) on smart set, this has to be a list.
3495 3498 # XXX make this lazy in the future
3496 3499 revs = list(revs)
3497 3500 # don't mutate while iterating, create a copy
3498 3501 for rev in list(revs):
3499 3502 if rev in ancestors:
3500 3503 ui.warn(_('skipping ancestor revision %d:%s\n') %
3501 3504 (rev, repo[rev]))
3502 3505 # XXX remove on list is slow
3503 3506 revs.remove(rev)
3504 3507 if not revs:
3505 3508 return -1
3506 3509
3507 3510 # analyze revs for earlier grafts
3508 3511 ids = {}
3509 3512 for ctx in repo.set("%ld", revs):
3510 3513 ids[ctx.hex()] = ctx.rev()
3511 3514 n = ctx.extra().get('source')
3512 3515 if n:
3513 3516 ids[n] = ctx.rev()
3514 3517
3515 3518 # check ancestors for earlier grafts
3516 3519 ui.debug('scanning for duplicate grafts\n')
3517 3520
3518 3521 for rev in repo.changelog.findmissingrevs(revs, [crev]):
3519 3522 ctx = repo[rev]
3520 3523 n = ctx.extra().get('source')
3521 3524 if n in ids:
3522 3525 try:
3523 3526 r = repo[n].rev()
3524 3527 except error.RepoLookupError:
3525 3528 r = None
3526 3529 if r in revs:
3527 3530 ui.warn(_('skipping revision %d:%s '
3528 3531 '(already grafted to %d:%s)\n')
3529 3532 % (r, repo[r], rev, ctx))
3530 3533 revs.remove(r)
3531 3534 elif ids[n] in revs:
3532 3535 if r is None:
3533 3536 ui.warn(_('skipping already grafted revision %d:%s '
3534 3537 '(%d:%s also has unknown origin %s)\n')
3535 3538 % (ids[n], repo[ids[n]], rev, ctx, n[:12]))
3536 3539 else:
3537 3540 ui.warn(_('skipping already grafted revision %d:%s '
3538 3541 '(%d:%s also has origin %d:%s)\n')
3539 3542 % (ids[n], repo[ids[n]], rev, ctx, r, n[:12]))
3540 3543 revs.remove(ids[n])
3541 3544 elif ctx.hex() in ids:
3542 3545 r = ids[ctx.hex()]
3543 3546 ui.warn(_('skipping already grafted revision %d:%s '
3544 3547 '(was grafted from %d:%s)\n') %
3545 3548 (r, repo[r], rev, ctx))
3546 3549 revs.remove(r)
3547 3550 if not revs:
3548 3551 return -1
3549 3552
3550 3553 wlock = repo.wlock()
3551 3554 try:
3552 3555 for pos, ctx in enumerate(repo.set("%ld", revs)):
3553 3556 desc = '%d:%s "%s"' % (ctx.rev(), ctx,
3554 3557 ctx.description().split('\n', 1)[0])
3555 3558 names = repo.nodetags(ctx.node()) + repo.nodebookmarks(ctx.node())
3556 3559 if names:
3557 3560 desc += ' (%s)' % ' '.join(names)
3558 3561 ui.status(_('grafting %s\n') % desc)
3559 3562 if opts.get('dry_run'):
3560 3563 continue
3561 3564
3562 3565 source = ctx.extra().get('source')
3563 3566 extra = {}
3564 3567 if source:
3565 3568 extra['source'] = source
3566 3569 extra['intermediate-source'] = ctx.hex()
3567 3570 else:
3568 3571 extra['source'] = ctx.hex()
3569 3572 user = ctx.user()
3570 3573 if opts.get('user'):
3571 3574 user = opts['user']
3572 3575 date = ctx.date()
3573 3576 if opts.get('date'):
3574 3577 date = opts['date']
3575 3578 message = ctx.description()
3576 3579 if opts.get('log'):
3577 3580 message += '\n(grafted from %s)' % ctx.hex()
3578 3581
3579 3582 # we don't merge the first commit when continuing
3580 3583 if not cont:
3581 3584 # perform the graft merge with p1(rev) as 'ancestor'
3582 3585 try:
3583 3586 # ui.forcemerge is an internal variable, do not document
3584 3587 repo.ui.setconfig('ui', 'forcemerge', opts.get('tool', ''),
3585 3588 'graft')
3586 3589 stats = mergemod.graft(repo, ctx, ctx.p1(),
3587 3590 ['local', 'graft'])
3588 3591 finally:
3589 3592 repo.ui.setconfig('ui', 'forcemerge', '', 'graft')
3590 3593 # report any conflicts
3591 3594 if stats and stats[3] > 0:
3592 3595 # write out state for --continue
3593 3596 nodelines = [repo[rev].hex() + "\n" for rev in revs[pos:]]
3594 3597 repo.vfs.write('graftstate', ''.join(nodelines))
3595 3598 raise util.Abort(
3596 3599 _("unresolved conflicts, can't continue"),
3597 3600 hint=_('use hg resolve and hg graft --continue'))
3598 3601 else:
3599 3602 cont = False
3600 3603
3601 3604 # commit
3602 3605 node = repo.commit(text=message, user=user,
3603 3606 date=date, extra=extra, editor=editor)
3604 3607 if node is None:
3605 3608 ui.warn(
3606 3609 _('note: graft of %d:%s created no changes to commit\n') %
3607 3610 (ctx.rev(), ctx))
3608 3611 finally:
3609 3612 wlock.release()
3610 3613
3611 3614 # remove state when we complete successfully
3612 3615 if not opts.get('dry_run'):
3613 3616 util.unlinkpath(repo.join('graftstate'), ignoremissing=True)
3614 3617
3615 3618 return 0
3616 3619
3617 3620 @command('grep',
3618 3621 [('0', 'print0', None, _('end fields with NUL')),
3619 3622 ('', 'all', None, _('print all revisions that match')),
3620 3623 ('a', 'text', None, _('treat all files as text')),
3621 3624 ('f', 'follow', None,
3622 3625 _('follow changeset history,'
3623 3626 ' or file history across copies and renames')),
3624 3627 ('i', 'ignore-case', None, _('ignore case when matching')),
3625 3628 ('l', 'files-with-matches', None,
3626 3629 _('print only filenames and revisions that match')),
3627 3630 ('n', 'line-number', None, _('print matching line numbers')),
3628 3631 ('r', 'rev', [],
3629 3632 _('only search files changed within revision range'), _('REV')),
3630 3633 ('u', 'user', None, _('list the author (long with -v)')),
3631 3634 ('d', 'date', None, _('list the date (short with -q)')),
3632 3635 ] + walkopts,
3633 3636 _('[OPTION]... PATTERN [FILE]...'),
3634 3637 inferrepo=True)
3635 3638 def grep(ui, repo, pattern, *pats, **opts):
3636 3639 """search for a pattern in specified files and revisions
3637 3640
3638 3641 Search revisions of files for a regular expression.
3639 3642
3640 3643 This command behaves differently than Unix grep. It only accepts
3641 3644 Python/Perl regexps. It searches repository history, not the
3642 3645 working directory. It always prints the revision number in which a
3643 3646 match appears.
3644 3647
3645 3648 By default, grep only prints output for the first revision of a
3646 3649 file in which it finds a match. To get it to print every revision
3647 3650 that contains a change in match status ("-" for a match that
3648 3651 becomes a non-match, or "+" for a non-match that becomes a match),
3649 3652 use the --all flag.
3650 3653
3651 3654 Returns 0 if a match is found, 1 otherwise.
3652 3655 """
3653 3656 reflags = re.M
3654 3657 if opts.get('ignore_case'):
3655 3658 reflags |= re.I
3656 3659 try:
3657 3660 regexp = util.re.compile(pattern, reflags)
3658 3661 except re.error as inst:
3659 3662 ui.warn(_("grep: invalid match pattern: %s\n") % inst)
3660 3663 return 1
3661 3664 sep, eol = ':', '\n'
3662 3665 if opts.get('print0'):
3663 3666 sep = eol = '\0'
3664 3667
3665 3668 getfile = util.lrucachefunc(repo.file)
3666 3669
3667 3670 def matchlines(body):
3668 3671 begin = 0
3669 3672 linenum = 0
3670 3673 while begin < len(body):
3671 3674 match = regexp.search(body, begin)
3672 3675 if not match:
3673 3676 break
3674 3677 mstart, mend = match.span()
3675 3678 linenum += body.count('\n', begin, mstart) + 1
3676 3679 lstart = body.rfind('\n', begin, mstart) + 1 or begin
3677 3680 begin = body.find('\n', mend) + 1 or len(body) + 1
3678 3681 lend = begin - 1
3679 3682 yield linenum, mstart - lstart, mend - lstart, body[lstart:lend]
3680 3683
3681 3684 class linestate(object):
3682 3685 def __init__(self, line, linenum, colstart, colend):
3683 3686 self.line = line
3684 3687 self.linenum = linenum
3685 3688 self.colstart = colstart
3686 3689 self.colend = colend
3687 3690
3688 3691 def __hash__(self):
3689 3692 return hash((self.linenum, self.line))
3690 3693
3691 3694 def __eq__(self, other):
3692 3695 return self.line == other.line
3693 3696
3694 3697 def __iter__(self):
3695 3698 yield (self.line[:self.colstart], '')
3696 3699 yield (self.line[self.colstart:self.colend], 'grep.match')
3697 3700 rest = self.line[self.colend:]
3698 3701 while rest != '':
3699 3702 match = regexp.search(rest)
3700 3703 if not match:
3701 3704 yield (rest, '')
3702 3705 break
3703 3706 mstart, mend = match.span()
3704 3707 yield (rest[:mstart], '')
3705 3708 yield (rest[mstart:mend], 'grep.match')
3706 3709 rest = rest[mend:]
3707 3710
3708 3711 matches = {}
3709 3712 copies = {}
3710 3713 def grepbody(fn, rev, body):
3711 3714 matches[rev].setdefault(fn, [])
3712 3715 m = matches[rev][fn]
3713 3716 for lnum, cstart, cend, line in matchlines(body):
3714 3717 s = linestate(line, lnum, cstart, cend)
3715 3718 m.append(s)
3716 3719
3717 3720 def difflinestates(a, b):
3718 3721 sm = difflib.SequenceMatcher(None, a, b)
3719 3722 for tag, alo, ahi, blo, bhi in sm.get_opcodes():
3720 3723 if tag == 'insert':
3721 3724 for i in xrange(blo, bhi):
3722 3725 yield ('+', b[i])
3723 3726 elif tag == 'delete':
3724 3727 for i in xrange(alo, ahi):
3725 3728 yield ('-', a[i])
3726 3729 elif tag == 'replace':
3727 3730 for i in xrange(alo, ahi):
3728 3731 yield ('-', a[i])
3729 3732 for i in xrange(blo, bhi):
3730 3733 yield ('+', b[i])
3731 3734
3732 3735 def display(fn, ctx, pstates, states):
3733 3736 rev = ctx.rev()
3734 3737 if ui.quiet:
3735 3738 datefunc = util.shortdate
3736 3739 else:
3737 3740 datefunc = util.datestr
3738 3741 found = False
3739 3742 @util.cachefunc
3740 3743 def binary():
3741 3744 flog = getfile(fn)
3742 3745 return util.binary(flog.read(ctx.filenode(fn)))
3743 3746
3744 3747 if opts.get('all'):
3745 3748 iter = difflinestates(pstates, states)
3746 3749 else:
3747 3750 iter = [('', l) for l in states]
3748 3751 for change, l in iter:
3749 3752 cols = [(fn, 'grep.filename'), (str(rev), 'grep.rev')]
3750 3753
3751 3754 if opts.get('line_number'):
3752 3755 cols.append((str(l.linenum), 'grep.linenumber'))
3753 3756 if opts.get('all'):
3754 3757 cols.append((change, 'grep.change'))
3755 3758 if opts.get('user'):
3756 3759 cols.append((ui.shortuser(ctx.user()), 'grep.user'))
3757 3760 if opts.get('date'):
3758 3761 cols.append((datefunc(ctx.date()), 'grep.date'))
3759 3762 for col, label in cols[:-1]:
3760 3763 ui.write(col, label=label)
3761 3764 ui.write(sep, label='grep.sep')
3762 3765 ui.write(cols[-1][0], label=cols[-1][1])
3763 3766 if not opts.get('files_with_matches'):
3764 3767 ui.write(sep, label='grep.sep')
3765 3768 if not opts.get('text') and binary():
3766 3769 ui.write(" Binary file matches")
3767 3770 else:
3768 3771 for s, label in l:
3769 3772 ui.write(s, label=label)
3770 3773 ui.write(eol)
3771 3774 found = True
3772 3775 if opts.get('files_with_matches'):
3773 3776 break
3774 3777 return found
3775 3778
3776 3779 skip = {}
3777 3780 revfiles = {}
3778 3781 matchfn = scmutil.match(repo[None], pats, opts)
3779 3782 found = False
3780 3783 follow = opts.get('follow')
3781 3784
3782 3785 def prep(ctx, fns):
3783 3786 rev = ctx.rev()
3784 3787 pctx = ctx.p1()
3785 3788 parent = pctx.rev()
3786 3789 matches.setdefault(rev, {})
3787 3790 matches.setdefault(parent, {})
3788 3791 files = revfiles.setdefault(rev, [])
3789 3792 for fn in fns:
3790 3793 flog = getfile(fn)
3791 3794 try:
3792 3795 fnode = ctx.filenode(fn)
3793 3796 except error.LookupError:
3794 3797 continue
3795 3798
3796 3799 copied = flog.renamed(fnode)
3797 3800 copy = follow and copied and copied[0]
3798 3801 if copy:
3799 3802 copies.setdefault(rev, {})[fn] = copy
3800 3803 if fn in skip:
3801 3804 if copy:
3802 3805 skip[copy] = True
3803 3806 continue
3804 3807 files.append(fn)
3805 3808
3806 3809 if fn not in matches[rev]:
3807 3810 grepbody(fn, rev, flog.read(fnode))
3808 3811
3809 3812 pfn = copy or fn
3810 3813 if pfn not in matches[parent]:
3811 3814 try:
3812 3815 fnode = pctx.filenode(pfn)
3813 3816 grepbody(pfn, parent, flog.read(fnode))
3814 3817 except error.LookupError:
3815 3818 pass
3816 3819
3817 3820 for ctx in cmdutil.walkchangerevs(repo, matchfn, opts, prep):
3818 3821 rev = ctx.rev()
3819 3822 parent = ctx.p1().rev()
3820 3823 for fn in sorted(revfiles.get(rev, [])):
3821 3824 states = matches[rev][fn]
3822 3825 copy = copies.get(rev, {}).get(fn)
3823 3826 if fn in skip:
3824 3827 if copy:
3825 3828 skip[copy] = True
3826 3829 continue
3827 3830 pstates = matches.get(parent, {}).get(copy or fn, [])
3828 3831 if pstates or states:
3829 3832 r = display(fn, ctx, pstates, states)
3830 3833 found = found or r
3831 3834 if r and not opts.get('all'):
3832 3835 skip[fn] = True
3833 3836 if copy:
3834 3837 skip[copy] = True
3835 3838 del matches[rev]
3836 3839 del revfiles[rev]
3837 3840
3838 3841 return not found
3839 3842
3840 3843 @command('heads',
3841 3844 [('r', 'rev', '',
3842 3845 _('show only heads which are descendants of STARTREV'), _('STARTREV')),
3843 3846 ('t', 'topo', False, _('show topological heads only')),
3844 3847 ('a', 'active', False, _('show active branchheads only (DEPRECATED)')),
3845 3848 ('c', 'closed', False, _('show normal and closed branch heads')),
3846 3849 ] + templateopts,
3847 3850 _('[-ct] [-r STARTREV] [REV]...'))
3848 3851 def heads(ui, repo, *branchrevs, **opts):
3849 3852 """show branch heads
3850 3853
3851 3854 With no arguments, show all open branch heads in the repository.
3852 3855 Branch heads are changesets that have no descendants on the
3853 3856 same branch. They are where development generally takes place and
3854 3857 are the usual targets for update and merge operations.
3855 3858
3856 3859 If one or more REVs are given, only open branch heads on the
3857 3860 branches associated with the specified changesets are shown. This
3858 3861 means that you can use :hg:`heads .` to see the heads on the
3859 3862 currently checked-out branch.
3860 3863
3861 3864 If -c/--closed is specified, also show branch heads marked closed
3862 3865 (see :hg:`commit --close-branch`).
3863 3866
3864 3867 If STARTREV is specified, only those heads that are descendants of
3865 3868 STARTREV will be displayed.
3866 3869
3867 3870 If -t/--topo is specified, named branch mechanics will be ignored and only
3868 3871 topological heads (changesets with no children) will be shown.
3869 3872
3870 3873 Returns 0 if matching heads are found, 1 if not.
3871 3874 """
3872 3875
3873 3876 start = None
3874 3877 if 'rev' in opts:
3875 3878 start = scmutil.revsingle(repo, opts['rev'], None).node()
3876 3879
3877 3880 if opts.get('topo'):
3878 3881 heads = [repo[h] for h in repo.heads(start)]
3879 3882 else:
3880 3883 heads = []
3881 3884 for branch in repo.branchmap():
3882 3885 heads += repo.branchheads(branch, start, opts.get('closed'))
3883 3886 heads = [repo[h] for h in heads]
3884 3887
3885 3888 if branchrevs:
3886 3889 branches = set(repo[br].branch() for br in branchrevs)
3887 3890 heads = [h for h in heads if h.branch() in branches]
3888 3891
3889 3892 if opts.get('active') and branchrevs:
3890 3893 dagheads = repo.heads(start)
3891 3894 heads = [h for h in heads if h.node() in dagheads]
3892 3895
3893 3896 if branchrevs:
3894 3897 haveheads = set(h.branch() for h in heads)
3895 3898 if branches - haveheads:
3896 3899 headless = ', '.join(b for b in branches - haveheads)
3897 3900 msg = _('no open branch heads found on branches %s')
3898 3901 if opts.get('rev'):
3899 3902 msg += _(' (started at %s)') % opts['rev']
3900 3903 ui.warn((msg + '\n') % headless)
3901 3904
3902 3905 if not heads:
3903 3906 return 1
3904 3907
3905 3908 heads = sorted(heads, key=lambda x: -x.rev())
3906 3909 displayer = cmdutil.show_changeset(ui, repo, opts)
3907 3910 for ctx in heads:
3908 3911 displayer.show(ctx)
3909 3912 displayer.close()
3910 3913
3911 3914 @command('help',
3912 3915 [('e', 'extension', None, _('show only help for extensions')),
3913 3916 ('c', 'command', None, _('show only help for commands')),
3914 3917 ('k', 'keyword', '', _('show topics matching keyword')),
3915 3918 ],
3916 3919 _('[-ec] [TOPIC]'),
3917 3920 norepo=True)
3918 3921 def help_(ui, name=None, **opts):
3919 3922 """show help for a given topic or a help overview
3920 3923
3921 3924 With no arguments, print a list of commands with short help messages.
3922 3925
3923 3926 Given a topic, extension, or command name, print help for that
3924 3927 topic.
3925 3928
3926 3929 Returns 0 if successful.
3927 3930 """
3928 3931
3929 3932 textwidth = min(ui.termwidth(), 80) - 2
3930 3933
3931 3934 keep = []
3932 3935 if ui.verbose:
3933 3936 keep.append('verbose')
3934 3937 if sys.platform.startswith('win'):
3935 3938 keep.append('windows')
3936 3939 elif sys.platform == 'OpenVMS':
3937 3940 keep.append('vms')
3938 3941 elif sys.platform == 'plan9':
3939 3942 keep.append('plan9')
3940 3943 else:
3941 3944 keep.append('unix')
3942 3945 keep.append(sys.platform.lower())
3943 3946
3944 3947 section = None
3945 3948 if name and '.' in name:
3946 3949 name, section = name.split('.', 1)
3947 3950
3948 3951 text = help.help_(ui, name, **opts)
3949 3952
3950 3953 formatted, pruned = minirst.format(text, textwidth, keep=keep,
3951 3954 section=section)
3952 3955 if section and not formatted:
3953 3956 raise util.Abort(_("help section not found"))
3954 3957
3955 3958 if 'verbose' in pruned:
3956 3959 keep.append('omitted')
3957 3960 else:
3958 3961 keep.append('notomitted')
3959 3962 formatted, pruned = minirst.format(text, textwidth, keep=keep,
3960 3963 section=section)
3961 3964 ui.write(formatted)
3962 3965
3963 3966
3964 3967 @command('identify|id',
3965 3968 [('r', 'rev', '',
3966 3969 _('identify the specified revision'), _('REV')),
3967 3970 ('n', 'num', None, _('show local revision number')),
3968 3971 ('i', 'id', None, _('show global revision id')),
3969 3972 ('b', 'branch', None, _('show branch')),
3970 3973 ('t', 'tags', None, _('show tags')),
3971 3974 ('B', 'bookmarks', None, _('show bookmarks')),
3972 3975 ] + remoteopts,
3973 3976 _('[-nibtB] [-r REV] [SOURCE]'),
3974 3977 optionalrepo=True)
3975 3978 def identify(ui, repo, source=None, rev=None,
3976 3979 num=None, id=None, branch=None, tags=None, bookmarks=None, **opts):
3977 3980 """identify the working directory or specified revision
3978 3981
3979 3982 Print a summary identifying the repository state at REV using one or
3980 3983 two parent hash identifiers, followed by a "+" if the working
3981 3984 directory has uncommitted changes, the branch name (if not default),
3982 3985 a list of tags, and a list of bookmarks.
3983 3986
3984 3987 When REV is not given, print a summary of the current state of the
3985 3988 repository.
3986 3989
3987 3990 Specifying a path to a repository root or Mercurial bundle will
3988 3991 cause lookup to operate on that repository/bundle.
3989 3992
3990 3993 .. container:: verbose
3991 3994
3992 3995 Examples:
3993 3996
3994 3997 - generate a build identifier for the working directory::
3995 3998
3996 3999 hg id --id > build-id.dat
3997 4000
3998 4001 - find the revision corresponding to a tag::
3999 4002
4000 4003 hg id -n -r 1.3
4001 4004
4002 4005 - check the most recent revision of a remote repository::
4003 4006
4004 4007 hg id -r tip http://selenic.com/hg/
4005 4008
4006 4009 Returns 0 if successful.
4007 4010 """
4008 4011
4009 4012 if not repo and not source:
4010 4013 raise util.Abort(_("there is no Mercurial repository here "
4011 4014 "(.hg not found)"))
4012 4015
4013 4016 if ui.debugflag:
4014 4017 hexfunc = hex
4015 4018 else:
4016 4019 hexfunc = short
4017 4020 default = not (num or id or branch or tags or bookmarks)
4018 4021 output = []
4019 4022 revs = []
4020 4023
4021 4024 if source:
4022 4025 source, branches = hg.parseurl(ui.expandpath(source))
4023 4026 peer = hg.peer(repo or ui, opts, source) # only pass ui when no repo
4024 4027 repo = peer.local()
4025 4028 revs, checkout = hg.addbranchrevs(repo, peer, branches, None)
4026 4029
4027 4030 if not repo:
4028 4031 if num or branch or tags:
4029 4032 raise util.Abort(
4030 4033 _("can't query remote revision number, branch, or tags"))
4031 4034 if not rev and revs:
4032 4035 rev = revs[0]
4033 4036 if not rev:
4034 4037 rev = "tip"
4035 4038
4036 4039 remoterev = peer.lookup(rev)
4037 4040 if default or id:
4038 4041 output = [hexfunc(remoterev)]
4039 4042
4040 4043 def getbms():
4041 4044 bms = []
4042 4045
4043 4046 if 'bookmarks' in peer.listkeys('namespaces'):
4044 4047 hexremoterev = hex(remoterev)
4045 4048 bms = [bm for bm, bmr in peer.listkeys('bookmarks').iteritems()
4046 4049 if bmr == hexremoterev]
4047 4050
4048 4051 return sorted(bms)
4049 4052
4050 4053 if bookmarks:
4051 4054 output.extend(getbms())
4052 4055 elif default and not ui.quiet:
4053 4056 # multiple bookmarks for a single parent separated by '/'
4054 4057 bm = '/'.join(getbms())
4055 4058 if bm:
4056 4059 output.append(bm)
4057 4060 else:
4058 4061 ctx = scmutil.revsingle(repo, rev, None)
4059 4062
4060 4063 if ctx.rev() is None:
4061 4064 ctx = repo[None]
4062 4065 parents = ctx.parents()
4063 4066 taglist = []
4064 4067 for p in parents:
4065 4068 taglist.extend(p.tags())
4066 4069
4067 4070 changed = ""
4068 4071 if default or id or num:
4069 4072 if (any(repo.status())
4070 4073 or any(ctx.sub(s).dirty() for s in ctx.substate)):
4071 4074 changed = '+'
4072 4075 if default or id:
4073 4076 output = ["%s%s" %
4074 4077 ('+'.join([hexfunc(p.node()) for p in parents]), changed)]
4075 4078 if num:
4076 4079 output.append("%s%s" %
4077 4080 ('+'.join([str(p.rev()) for p in parents]), changed))
4078 4081 else:
4079 4082 if default or id:
4080 4083 output = [hexfunc(ctx.node())]
4081 4084 if num:
4082 4085 output.append(str(ctx.rev()))
4083 4086 taglist = ctx.tags()
4084 4087
4085 4088 if default and not ui.quiet:
4086 4089 b = ctx.branch()
4087 4090 if b != 'default':
4088 4091 output.append("(%s)" % b)
4089 4092
4090 4093 # multiple tags for a single parent separated by '/'
4091 4094 t = '/'.join(taglist)
4092 4095 if t:
4093 4096 output.append(t)
4094 4097
4095 4098 # multiple bookmarks for a single parent separated by '/'
4096 4099 bm = '/'.join(ctx.bookmarks())
4097 4100 if bm:
4098 4101 output.append(bm)
4099 4102 else:
4100 4103 if branch:
4101 4104 output.append(ctx.branch())
4102 4105
4103 4106 if tags:
4104 4107 output.extend(taglist)
4105 4108
4106 4109 if bookmarks:
4107 4110 output.extend(ctx.bookmarks())
4108 4111
4109 4112 ui.write("%s\n" % ' '.join(output))
4110 4113
4111 4114 @command('import|patch',
4112 4115 [('p', 'strip', 1,
4113 4116 _('directory strip option for patch. This has the same '
4114 4117 'meaning as the corresponding patch option'), _('NUM')),
4115 4118 ('b', 'base', '', _('base path (DEPRECATED)'), _('PATH')),
4116 4119 ('e', 'edit', False, _('invoke editor on commit messages')),
4117 4120 ('f', 'force', None,
4118 4121 _('skip check for outstanding uncommitted changes (DEPRECATED)')),
4119 4122 ('', 'no-commit', None,
4120 4123 _("don't commit, just update the working directory")),
4121 4124 ('', 'bypass', None,
4122 4125 _("apply patch without touching the working directory")),
4123 4126 ('', 'partial', None,
4124 4127 _('commit even if some hunks fail')),
4125 4128 ('', 'exact', None,
4126 4129 _('apply patch to the nodes from which it was generated')),
4127 4130 ('', 'prefix', '',
4128 4131 _('apply patch to subdirectory'), _('DIR')),
4129 4132 ('', 'import-branch', None,
4130 4133 _('use any branch information in patch (implied by --exact)'))] +
4131 4134 commitopts + commitopts2 + similarityopts,
4132 4135 _('[OPTION]... PATCH...'))
4133 4136 def import_(ui, repo, patch1=None, *patches, **opts):
4134 4137 """import an ordered set of patches
4135 4138
4136 4139 Import a list of patches and commit them individually (unless
4137 4140 --no-commit is specified).
4138 4141
4139 4142 Because import first applies changes to the working directory,
4140 4143 import will abort if there are outstanding changes.
4141 4144
4142 4145 You can import a patch straight from a mail message. Even patches
4143 4146 as attachments work (to use the body part, it must have type
4144 4147 text/plain or text/x-patch). From and Subject headers of email
4145 4148 message are used as default committer and commit message. All
4146 4149 text/plain body parts before first diff are added to commit
4147 4150 message.
4148 4151
4149 4152 If the imported patch was generated by :hg:`export`, user and
4150 4153 description from patch override values from message headers and
4151 4154 body. Values given on command line with -m/--message and -u/--user
4152 4155 override these.
4153 4156
4154 4157 If --exact is specified, import will set the working directory to
4155 4158 the parent of each patch before applying it, and will abort if the
4156 4159 resulting changeset has a different ID than the one recorded in
4157 4160 the patch. This may happen due to character set problems or other
4158 4161 deficiencies in the text patch format.
4159 4162
4160 4163 Use --bypass to apply and commit patches directly to the
4161 4164 repository, not touching the working directory. Without --exact,
4162 4165 patches will be applied on top of the working directory parent
4163 4166 revision.
4164 4167
4165 4168 With -s/--similarity, hg will attempt to discover renames and
4166 4169 copies in the patch in the same way as :hg:`addremove`.
4167 4170
4168 4171 Use --partial to ensure a changeset will be created from the patch
4169 4172 even if some hunks fail to apply. Hunks that fail to apply will be
4170 4173 written to a <target-file>.rej file. Conflicts can then be resolved
4171 4174 by hand before :hg:`commit --amend` is run to update the created
4172 4175 changeset. This flag exists to let people import patches that
4173 4176 partially apply without losing the associated metadata (author,
4174 4177 date, description, ...). Note that when none of the hunk applies
4175 4178 cleanly, :hg:`import --partial` will create an empty changeset,
4176 4179 importing only the patch metadata.
4177 4180
4178 4181 It is possible to use external patch programs to perform the patch
4179 4182 by setting the ``ui.patch`` configuration option. For the default
4180 4183 internal tool, the fuzz can also be configured via ``patch.fuzz``.
4181 4184 See :hg:`help config` for more information about configuration
4182 4185 files and how to use these options.
4183 4186
4184 4187 To read a patch from standard input, use "-" as the patch name. If
4185 4188 a URL is specified, the patch will be downloaded from it.
4186 4189 See :hg:`help dates` for a list of formats valid for -d/--date.
4187 4190
4188 4191 .. container:: verbose
4189 4192
4190 4193 Examples:
4191 4194
4192 4195 - import a traditional patch from a website and detect renames::
4193 4196
4194 4197 hg import -s 80 http://example.com/bugfix.patch
4195 4198
4196 4199 - import a changeset from an hgweb server::
4197 4200
4198 4201 hg import http://www.selenic.com/hg/rev/5ca8c111e9aa
4199 4202
4200 4203 - import all the patches in an Unix-style mbox::
4201 4204
4202 4205 hg import incoming-patches.mbox
4203 4206
4204 4207 - attempt to exactly restore an exported changeset (not always
4205 4208 possible)::
4206 4209
4207 4210 hg import --exact proposed-fix.patch
4208 4211
4209 4212 - use an external tool to apply a patch which is too fuzzy for
4210 4213 the default internal tool.
4211 4214
4212 4215 hg import --config ui.patch="patch --merge" fuzzy.patch
4213 4216
4214 4217 - change the default fuzzing from 2 to a less strict 7
4215 4218
4216 4219 hg import --config ui.fuzz=7 fuzz.patch
4217 4220
4218 4221 Returns 0 on success, 1 on partial success (see --partial).
4219 4222 """
4220 4223
4221 4224 if not patch1:
4222 4225 raise util.Abort(_('need at least one patch to import'))
4223 4226
4224 4227 patches = (patch1,) + patches
4225 4228
4226 4229 date = opts.get('date')
4227 4230 if date:
4228 4231 opts['date'] = util.parsedate(date)
4229 4232
4230 4233 update = not opts.get('bypass')
4231 4234 if not update and opts.get('no_commit'):
4232 4235 raise util.Abort(_('cannot use --no-commit with --bypass'))
4233 4236 try:
4234 4237 sim = float(opts.get('similarity') or 0)
4235 4238 except ValueError:
4236 4239 raise util.Abort(_('similarity must be a number'))
4237 4240 if sim < 0 or sim > 100:
4238 4241 raise util.Abort(_('similarity must be between 0 and 100'))
4239 4242 if sim and not update:
4240 4243 raise util.Abort(_('cannot use --similarity with --bypass'))
4241 4244 if opts.get('exact') and opts.get('edit'):
4242 4245 raise util.Abort(_('cannot use --exact with --edit'))
4243 4246 if opts.get('exact') and opts.get('prefix'):
4244 4247 raise util.Abort(_('cannot use --exact with --prefix'))
4245 4248
4246 4249 if update:
4247 4250 cmdutil.checkunfinished(repo)
4248 4251 if (opts.get('exact') or not opts.get('force')) and update:
4249 4252 cmdutil.bailifchanged(repo)
4250 4253
4251 4254 base = opts["base"]
4252 4255 wlock = dsguard = lock = tr = None
4253 4256 msgs = []
4254 4257 ret = 0
4255 4258
4256 4259
4257 4260 try:
4258 4261 try:
4259 4262 wlock = repo.wlock()
4260 4263 dsguard = cmdutil.dirstateguard(repo, 'import')
4261 4264 if not opts.get('no_commit'):
4262 4265 lock = repo.lock()
4263 4266 tr = repo.transaction('import')
4264 4267 parents = repo.parents()
4265 4268 for patchurl in patches:
4266 4269 if patchurl == '-':
4267 4270 ui.status(_('applying patch from stdin\n'))
4268 4271 patchfile = ui.fin
4269 4272 patchurl = 'stdin' # for error message
4270 4273 else:
4271 4274 patchurl = os.path.join(base, patchurl)
4272 4275 ui.status(_('applying %s\n') % patchurl)
4273 4276 patchfile = hg.openpath(ui, patchurl)
4274 4277
4275 4278 haspatch = False
4276 4279 for hunk in patch.split(patchfile):
4277 4280 (msg, node, rej) = cmdutil.tryimportone(ui, repo, hunk,
4278 4281 parents, opts,
4279 4282 msgs, hg.clean)
4280 4283 if msg:
4281 4284 haspatch = True
4282 4285 ui.note(msg + '\n')
4283 4286 if update or opts.get('exact'):
4284 4287 parents = repo.parents()
4285 4288 else:
4286 4289 parents = [repo[node]]
4287 4290 if rej:
4288 4291 ui.write_err(_("patch applied partially\n"))
4289 4292 ui.write_err(_("(fix the .rej files and run "
4290 4293 "`hg commit --amend`)\n"))
4291 4294 ret = 1
4292 4295 break
4293 4296
4294 4297 if not haspatch:
4295 4298 raise util.Abort(_('%s: no diffs found') % patchurl)
4296 4299
4297 4300 if tr:
4298 4301 tr.close()
4299 4302 if msgs:
4300 4303 repo.savecommitmessage('\n* * *\n'.join(msgs))
4301 4304 dsguard.close()
4302 4305 return ret
4303 4306 finally:
4304 4307 # TODO: get rid of this meaningless try/finally enclosing.
4305 4308 # this is kept only to reduce changes in a patch.
4306 4309 pass
4307 4310 finally:
4308 4311 if tr:
4309 4312 tr.release()
4310 4313 release(lock, dsguard, wlock)
4311 4314
4312 4315 @command('incoming|in',
4313 4316 [('f', 'force', None,
4314 4317 _('run even if remote repository is unrelated')),
4315 4318 ('n', 'newest-first', None, _('show newest record first')),
4316 4319 ('', 'bundle', '',
4317 4320 _('file to store the bundles into'), _('FILE')),
4318 4321 ('r', 'rev', [], _('a remote changeset intended to be added'), _('REV')),
4319 4322 ('B', 'bookmarks', False, _("compare bookmarks")),
4320 4323 ('b', 'branch', [],
4321 4324 _('a specific branch you would like to pull'), _('BRANCH')),
4322 4325 ] + logopts + remoteopts + subrepoopts,
4323 4326 _('[-p] [-n] [-M] [-f] [-r REV]... [--bundle FILENAME] [SOURCE]'))
4324 4327 def incoming(ui, repo, source="default", **opts):
4325 4328 """show new changesets found in source
4326 4329
4327 4330 Show new changesets found in the specified path/URL or the default
4328 4331 pull location. These are the changesets that would have been pulled
4329 4332 if a pull at the time you issued this command.
4330 4333
4331 4334 See pull for valid source format details.
4332 4335
4333 4336 .. container:: verbose
4334 4337
4335 4338 With -B/--bookmarks, the result of bookmark comparison between
4336 4339 local and remote repositories is displayed. With -v/--verbose,
4337 4340 status is also displayed for each bookmark like below::
4338 4341
4339 4342 BM1 01234567890a added
4340 4343 BM2 1234567890ab advanced
4341 4344 BM3 234567890abc diverged
4342 4345 BM4 34567890abcd changed
4343 4346
4344 4347 The action taken locally when pulling depends on the
4345 4348 status of each bookmark:
4346 4349
4347 4350 :``added``: pull will create it
4348 4351 :``advanced``: pull will update it
4349 4352 :``diverged``: pull will create a divergent bookmark
4350 4353 :``changed``: result depends on remote changesets
4351 4354
4352 4355 From the point of view of pulling behavior, bookmark
4353 4356 existing only in the remote repository are treated as ``added``,
4354 4357 even if it is in fact locally deleted.
4355 4358
4356 4359 .. container:: verbose
4357 4360
4358 4361 For remote repository, using --bundle avoids downloading the
4359 4362 changesets twice if the incoming is followed by a pull.
4360 4363
4361 4364 Examples:
4362 4365
4363 4366 - show incoming changes with patches and full description::
4364 4367
4365 4368 hg incoming -vp
4366 4369
4367 4370 - show incoming changes excluding merges, store a bundle::
4368 4371
4369 4372 hg in -vpM --bundle incoming.hg
4370 4373 hg pull incoming.hg
4371 4374
4372 4375 - briefly list changes inside a bundle::
4373 4376
4374 4377 hg in changes.hg -T "{desc|firstline}\\n"
4375 4378
4376 4379 Returns 0 if there are incoming changes, 1 otherwise.
4377 4380 """
4378 4381 if opts.get('graph'):
4379 4382 cmdutil.checkunsupportedgraphflags([], opts)
4380 4383 def display(other, chlist, displayer):
4381 4384 revdag = cmdutil.graphrevs(other, chlist, opts)
4382 4385 showparents = [ctx.node() for ctx in repo[None].parents()]
4383 4386 cmdutil.displaygraph(ui, revdag, displayer, showparents,
4384 4387 graphmod.asciiedges)
4385 4388
4386 4389 hg._incoming(display, lambda: 1, ui, repo, source, opts, buffered=True)
4387 4390 return 0
4388 4391
4389 4392 if opts.get('bundle') and opts.get('subrepos'):
4390 4393 raise util.Abort(_('cannot combine --bundle and --subrepos'))
4391 4394
4392 4395 if opts.get('bookmarks'):
4393 4396 source, branches = hg.parseurl(ui.expandpath(source),
4394 4397 opts.get('branch'))
4395 4398 other = hg.peer(repo, opts, source)
4396 4399 if 'bookmarks' not in other.listkeys('namespaces'):
4397 4400 ui.warn(_("remote doesn't support bookmarks\n"))
4398 4401 return 0
4399 4402 ui.status(_('comparing with %s\n') % util.hidepassword(source))
4400 4403 return bookmarks.incoming(ui, repo, other)
4401 4404
4402 4405 repo._subtoppath = ui.expandpath(source)
4403 4406 try:
4404 4407 return hg.incoming(ui, repo, source, opts)
4405 4408 finally:
4406 4409 del repo._subtoppath
4407 4410
4408 4411
4409 4412 @command('^init', remoteopts, _('[-e CMD] [--remotecmd CMD] [DEST]'),
4410 4413 norepo=True)
4411 4414 def init(ui, dest=".", **opts):
4412 4415 """create a new repository in the given directory
4413 4416
4414 4417 Initialize a new repository in the given directory. If the given
4415 4418 directory does not exist, it will be created.
4416 4419
4417 4420 If no directory is given, the current directory is used.
4418 4421
4419 4422 It is possible to specify an ``ssh://`` URL as the destination.
4420 4423 See :hg:`help urls` for more information.
4421 4424
4422 4425 Returns 0 on success.
4423 4426 """
4424 4427 hg.peer(ui, opts, ui.expandpath(dest), create=True)
4425 4428
4426 4429 @command('locate',
4427 4430 [('r', 'rev', '', _('search the repository as it is in REV'), _('REV')),
4428 4431 ('0', 'print0', None, _('end filenames with NUL, for use with xargs')),
4429 4432 ('f', 'fullpath', None, _('print complete paths from the filesystem root')),
4430 4433 ] + walkopts,
4431 4434 _('[OPTION]... [PATTERN]...'))
4432 4435 def locate(ui, repo, *pats, **opts):
4433 4436 """locate files matching specific patterns (DEPRECATED)
4434 4437
4435 4438 Print files under Mercurial control in the working directory whose
4436 4439 names match the given patterns.
4437 4440
4438 4441 By default, this command searches all directories in the working
4439 4442 directory. To search just the current directory and its
4440 4443 subdirectories, use "--include .".
4441 4444
4442 4445 If no patterns are given to match, this command prints the names
4443 4446 of all files under Mercurial control in the working directory.
4444 4447
4445 4448 If you want to feed the output of this command into the "xargs"
4446 4449 command, use the -0 option to both this command and "xargs". This
4447 4450 will avoid the problem of "xargs" treating single filenames that
4448 4451 contain whitespace as multiple filenames.
4449 4452
4450 4453 See :hg:`help files` for a more versatile command.
4451 4454
4452 4455 Returns 0 if a match is found, 1 otherwise.
4453 4456 """
4454 4457 if opts.get('print0'):
4455 4458 end = '\0'
4456 4459 else:
4457 4460 end = '\n'
4458 4461 rev = scmutil.revsingle(repo, opts.get('rev'), None).node()
4459 4462
4460 4463 ret = 1
4461 4464 ctx = repo[rev]
4462 4465 m = scmutil.match(ctx, pats, opts, default='relglob',
4463 4466 badfn=lambda x, y: False)
4464 4467
4465 4468 for abs in ctx.matches(m):
4466 4469 if opts.get('fullpath'):
4467 4470 ui.write(repo.wjoin(abs), end)
4468 4471 else:
4469 4472 ui.write(((pats and m.rel(abs)) or abs), end)
4470 4473 ret = 0
4471 4474
4472 4475 return ret
4473 4476
4474 4477 @command('^log|history',
4475 4478 [('f', 'follow', None,
4476 4479 _('follow changeset history, or file history across copies and renames')),
4477 4480 ('', 'follow-first', None,
4478 4481 _('only follow the first parent of merge changesets (DEPRECATED)')),
4479 4482 ('d', 'date', '', _('show revisions matching date spec'), _('DATE')),
4480 4483 ('C', 'copies', None, _('show copied files')),
4481 4484 ('k', 'keyword', [],
4482 4485 _('do case-insensitive search for a given text'), _('TEXT')),
4483 4486 ('r', 'rev', [], _('show the specified revision or revset'), _('REV')),
4484 4487 ('', 'removed', None, _('include revisions where files were removed')),
4485 4488 ('m', 'only-merges', None, _('show only merges (DEPRECATED)')),
4486 4489 ('u', 'user', [], _('revisions committed by user'), _('USER')),
4487 4490 ('', 'only-branch', [],
4488 4491 _('show only changesets within the given named branch (DEPRECATED)'),
4489 4492 _('BRANCH')),
4490 4493 ('b', 'branch', [],
4491 4494 _('show changesets within the given named branch'), _('BRANCH')),
4492 4495 ('P', 'prune', [],
4493 4496 _('do not display revision or any of its ancestors'), _('REV')),
4494 4497 ] + logopts + walkopts,
4495 4498 _('[OPTION]... [FILE]'),
4496 4499 inferrepo=True)
4497 4500 def log(ui, repo, *pats, **opts):
4498 4501 """show revision history of entire repository or files
4499 4502
4500 4503 Print the revision history of the specified files or the entire
4501 4504 project.
4502 4505
4503 4506 If no revision range is specified, the default is ``tip:0`` unless
4504 4507 --follow is set, in which case the working directory parent is
4505 4508 used as the starting revision.
4506 4509
4507 4510 File history is shown without following rename or copy history of
4508 4511 files. Use -f/--follow with a filename to follow history across
4509 4512 renames and copies. --follow without a filename will only show
4510 4513 ancestors or descendants of the starting revision.
4511 4514
4512 4515 By default this command prints revision number and changeset id,
4513 4516 tags, non-trivial parents, user, date and time, and a summary for
4514 4517 each commit. When the -v/--verbose switch is used, the list of
4515 4518 changed files and full commit message are shown.
4516 4519
4517 4520 With --graph the revisions are shown as an ASCII art DAG with the most
4518 4521 recent changeset at the top.
4519 4522 'o' is a changeset, '@' is a working directory parent, 'x' is obsolete,
4520 4523 and '+' represents a fork where the changeset from the lines below is a
4521 4524 parent of the 'o' merge on the same line.
4522 4525
4523 4526 .. note::
4524 4527
4525 4528 log -p/--patch may generate unexpected diff output for merge
4526 4529 changesets, as it will only compare the merge changeset against
4527 4530 its first parent. Also, only files different from BOTH parents
4528 4531 will appear in files:.
4529 4532
4530 4533 .. note::
4531 4534
4532 4535 for performance reasons, log FILE may omit duplicate changes
4533 4536 made on branches and will not show removals or mode changes. To
4534 4537 see all such changes, use the --removed switch.
4535 4538
4536 4539 .. container:: verbose
4537 4540
4538 4541 Some examples:
4539 4542
4540 4543 - changesets with full descriptions and file lists::
4541 4544
4542 4545 hg log -v
4543 4546
4544 4547 - changesets ancestral to the working directory::
4545 4548
4546 4549 hg log -f
4547 4550
4548 4551 - last 10 commits on the current branch::
4549 4552
4550 4553 hg log -l 10 -b .
4551 4554
4552 4555 - changesets showing all modifications of a file, including removals::
4553 4556
4554 4557 hg log --removed file.c
4555 4558
4556 4559 - all changesets that touch a directory, with diffs, excluding merges::
4557 4560
4558 4561 hg log -Mp lib/
4559 4562
4560 4563 - all revision numbers that match a keyword::
4561 4564
4562 4565 hg log -k bug --template "{rev}\\n"
4563 4566
4564 4567 - list available log templates::
4565 4568
4566 4569 hg log -T list
4567 4570
4568 4571 - check if a given changeset is included in a tagged release::
4569 4572
4570 4573 hg log -r "a21ccf and ancestor(1.9)"
4571 4574
4572 4575 - find all changesets by some user in a date range::
4573 4576
4574 4577 hg log -k alice -d "may 2008 to jul 2008"
4575 4578
4576 4579 - summary of all changesets after the last tag::
4577 4580
4578 4581 hg log -r "last(tagged())::" --template "{desc|firstline}\\n"
4579 4582
4580 4583 See :hg:`help dates` for a list of formats valid for -d/--date.
4581 4584
4582 4585 See :hg:`help revisions` and :hg:`help revsets` for more about
4583 4586 specifying revisions.
4584 4587
4585 4588 See :hg:`help templates` for more about pre-packaged styles and
4586 4589 specifying custom templates.
4587 4590
4588 4591 Returns 0 on success.
4589 4592
4590 4593 """
4591 4594 if opts.get('follow') and opts.get('rev'):
4592 4595 opts['rev'] = [revset.formatspec('reverse(::%lr)', opts.get('rev'))]
4593 4596 del opts['follow']
4594 4597
4595 4598 if opts.get('graph'):
4596 4599 return cmdutil.graphlog(ui, repo, *pats, **opts)
4597 4600
4598 4601 revs, expr, filematcher = cmdutil.getlogrevs(repo, pats, opts)
4599 4602 limit = cmdutil.loglimit(opts)
4600 4603 count = 0
4601 4604
4602 4605 getrenamed = None
4603 4606 if opts.get('copies'):
4604 4607 endrev = None
4605 4608 if opts.get('rev'):
4606 4609 endrev = scmutil.revrange(repo, opts.get('rev')).max() + 1
4607 4610 getrenamed = templatekw.getrenamedfn(repo, endrev=endrev)
4608 4611
4609 4612 displayer = cmdutil.show_changeset(ui, repo, opts, buffered=True)
4610 4613 for rev in revs:
4611 4614 if count == limit:
4612 4615 break
4613 4616 ctx = repo[rev]
4614 4617 copies = None
4615 4618 if getrenamed is not None and rev:
4616 4619 copies = []
4617 4620 for fn in ctx.files():
4618 4621 rename = getrenamed(fn, rev)
4619 4622 if rename:
4620 4623 copies.append((fn, rename[0]))
4621 4624 if filematcher:
4622 4625 revmatchfn = filematcher(ctx.rev())
4623 4626 else:
4624 4627 revmatchfn = None
4625 4628 displayer.show(ctx, copies=copies, matchfn=revmatchfn)
4626 4629 if displayer.flush(rev):
4627 4630 count += 1
4628 4631
4629 4632 displayer.close()
4630 4633
4631 4634 @command('manifest',
4632 4635 [('r', 'rev', '', _('revision to display'), _('REV')),
4633 4636 ('', 'all', False, _("list files from all revisions"))]
4634 4637 + formatteropts,
4635 4638 _('[-r REV]'))
4636 4639 def manifest(ui, repo, node=None, rev=None, **opts):
4637 4640 """output the current or given revision of the project manifest
4638 4641
4639 4642 Print a list of version controlled files for the given revision.
4640 4643 If no revision is given, the first parent of the working directory
4641 4644 is used, or the null revision if no revision is checked out.
4642 4645
4643 4646 With -v, print file permissions, symlink and executable bits.
4644 4647 With --debug, print file revision hashes.
4645 4648
4646 4649 If option --all is specified, the list of all files from all revisions
4647 4650 is printed. This includes deleted and renamed files.
4648 4651
4649 4652 Returns 0 on success.
4650 4653 """
4651 4654
4652 4655 fm = ui.formatter('manifest', opts)
4653 4656
4654 4657 if opts.get('all'):
4655 4658 if rev or node:
4656 4659 raise util.Abort(_("can't specify a revision with --all"))
4657 4660
4658 4661 res = []
4659 4662 prefix = "data/"
4660 4663 suffix = ".i"
4661 4664 plen = len(prefix)
4662 4665 slen = len(suffix)
4663 4666 lock = repo.lock()
4664 4667 try:
4665 4668 for fn, b, size in repo.store.datafiles():
4666 4669 if size != 0 and fn[-slen:] == suffix and fn[:plen] == prefix:
4667 4670 res.append(fn[plen:-slen])
4668 4671 finally:
4669 4672 lock.release()
4670 4673 for f in res:
4671 4674 fm.startitem()
4672 4675 fm.write("path", '%s\n', f)
4673 4676 fm.end()
4674 4677 return
4675 4678
4676 4679 if rev and node:
4677 4680 raise util.Abort(_("please specify just one revision"))
4678 4681
4679 4682 if not node:
4680 4683 node = rev
4681 4684
4682 4685 char = {'l': '@', 'x': '*', '': ''}
4683 4686 mode = {'l': '644', 'x': '755', '': '644'}
4684 4687 ctx = scmutil.revsingle(repo, node)
4685 4688 mf = ctx.manifest()
4686 4689 for f in ctx:
4687 4690 fm.startitem()
4688 4691 fl = ctx[f].flags()
4689 4692 fm.condwrite(ui.debugflag, 'hash', '%s ', hex(mf[f]))
4690 4693 fm.condwrite(ui.verbose, 'mode type', '%s %1s ', mode[fl], char[fl])
4691 4694 fm.write('path', '%s\n', f)
4692 4695 fm.end()
4693 4696
4694 4697 @command('^merge',
4695 4698 [('f', 'force', None,
4696 4699 _('force a merge including outstanding changes (DEPRECATED)')),
4697 4700 ('r', 'rev', '', _('revision to merge'), _('REV')),
4698 4701 ('P', 'preview', None,
4699 4702 _('review revisions to merge (no merge is performed)'))
4700 4703 ] + mergetoolopts,
4701 4704 _('[-P] [-f] [[-r] REV]'))
4702 4705 def merge(ui, repo, node=None, **opts):
4703 4706 """merge another revision into working directory
4704 4707
4705 4708 The current working directory is updated with all changes made in
4706 4709 the requested revision since the last common predecessor revision.
4707 4710
4708 4711 Files that changed between either parent are marked as changed for
4709 4712 the next commit and a commit must be performed before any further
4710 4713 updates to the repository are allowed. The next commit will have
4711 4714 two parents.
4712 4715
4713 4716 ``--tool`` can be used to specify the merge tool used for file
4714 4717 merges. It overrides the HGMERGE environment variable and your
4715 4718 configuration files. See :hg:`help merge-tools` for options.
4716 4719
4717 4720 If no revision is specified, the working directory's parent is a
4718 4721 head revision, and the current branch contains exactly one other
4719 4722 head, the other head is merged with by default. Otherwise, an
4720 4723 explicit revision with which to merge with must be provided.
4721 4724
4722 4725 :hg:`resolve` must be used to resolve unresolved files.
4723 4726
4724 4727 To undo an uncommitted merge, use :hg:`update --clean .` which
4725 4728 will check out a clean copy of the original merge parent, losing
4726 4729 all changes.
4727 4730
4728 4731 Returns 0 on success, 1 if there are unresolved files.
4729 4732 """
4730 4733
4731 4734 if opts.get('rev') and node:
4732 4735 raise util.Abort(_("please specify just one revision"))
4733 4736 if not node:
4734 4737 node = opts.get('rev')
4735 4738
4736 4739 if node:
4737 4740 node = scmutil.revsingle(repo, node).node()
4738 4741
4739 4742 if not node and repo._activebookmark:
4740 4743 bmheads = repo.bookmarkheads(repo._activebookmark)
4741 4744 curhead = repo[repo._activebookmark].node()
4742 4745 if len(bmheads) == 2:
4743 4746 if curhead == bmheads[0]:
4744 4747 node = bmheads[1]
4745 4748 else:
4746 4749 node = bmheads[0]
4747 4750 elif len(bmheads) > 2:
4748 4751 raise util.Abort(_("multiple matching bookmarks to merge - "
4749 4752 "please merge with an explicit rev or bookmark"),
4750 4753 hint=_("run 'hg heads' to see all heads"))
4751 4754 elif len(bmheads) <= 1:
4752 4755 raise util.Abort(_("no matching bookmark to merge - "
4753 4756 "please merge with an explicit rev or bookmark"),
4754 4757 hint=_("run 'hg heads' to see all heads"))
4755 4758
4756 4759 if not node and not repo._activebookmark:
4757 4760 branch = repo[None].branch()
4758 4761 bheads = repo.branchheads(branch)
4759 4762 nbhs = [bh for bh in bheads if not repo[bh].bookmarks()]
4760 4763
4761 4764 if len(nbhs) > 2:
4762 4765 raise util.Abort(_("branch '%s' has %d heads - "
4763 4766 "please merge with an explicit rev")
4764 4767 % (branch, len(bheads)),
4765 4768 hint=_("run 'hg heads .' to see heads"))
4766 4769
4767 4770 parent = repo.dirstate.p1()
4768 4771 if len(nbhs) <= 1:
4769 4772 if len(bheads) > 1:
4770 4773 raise util.Abort(_("heads are bookmarked - "
4771 4774 "please merge with an explicit rev"),
4772 4775 hint=_("run 'hg heads' to see all heads"))
4773 4776 if len(repo.heads()) > 1:
4774 4777 raise util.Abort(_("branch '%s' has one head - "
4775 4778 "please merge with an explicit rev")
4776 4779 % branch,
4777 4780 hint=_("run 'hg heads' to see all heads"))
4778 4781 msg, hint = _('nothing to merge'), None
4779 4782 if parent != repo.lookup(branch):
4780 4783 hint = _("use 'hg update' instead")
4781 4784 raise util.Abort(msg, hint=hint)
4782 4785
4783 4786 if parent not in bheads:
4784 4787 raise util.Abort(_('working directory not at a head revision'),
4785 4788 hint=_("use 'hg update' or merge with an "
4786 4789 "explicit revision"))
4787 4790 if parent == nbhs[0]:
4788 4791 node = nbhs[-1]
4789 4792 else:
4790 4793 node = nbhs[0]
4791 4794
4792 4795 if opts.get('preview'):
4793 4796 # find nodes that are ancestors of p2 but not of p1
4794 4797 p1 = repo.lookup('.')
4795 4798 p2 = repo.lookup(node)
4796 4799 nodes = repo.changelog.findmissing(common=[p1], heads=[p2])
4797 4800
4798 4801 displayer = cmdutil.show_changeset(ui, repo, opts)
4799 4802 for node in nodes:
4800 4803 displayer.show(repo[node])
4801 4804 displayer.close()
4802 4805 return 0
4803 4806
4804 4807 try:
4805 4808 # ui.forcemerge is an internal variable, do not document
4806 4809 repo.ui.setconfig('ui', 'forcemerge', opts.get('tool', ''), 'merge')
4807 4810 return hg.merge(repo, node, force=opts.get('force'))
4808 4811 finally:
4809 4812 ui.setconfig('ui', 'forcemerge', '', 'merge')
4810 4813
4811 4814 @command('outgoing|out',
4812 4815 [('f', 'force', None, _('run even when the destination is unrelated')),
4813 4816 ('r', 'rev', [],
4814 4817 _('a changeset intended to be included in the destination'), _('REV')),
4815 4818 ('n', 'newest-first', None, _('show newest record first')),
4816 4819 ('B', 'bookmarks', False, _('compare bookmarks')),
4817 4820 ('b', 'branch', [], _('a specific branch you would like to push'),
4818 4821 _('BRANCH')),
4819 4822 ] + logopts + remoteopts + subrepoopts,
4820 4823 _('[-M] [-p] [-n] [-f] [-r REV]... [DEST]'))
4821 4824 def outgoing(ui, repo, dest=None, **opts):
4822 4825 """show changesets not found in the destination
4823 4826
4824 4827 Show changesets not found in the specified destination repository
4825 4828 or the default push location. These are the changesets that would
4826 4829 be pushed if a push was requested.
4827 4830
4828 4831 See pull for details of valid destination formats.
4829 4832
4830 4833 .. container:: verbose
4831 4834
4832 4835 With -B/--bookmarks, the result of bookmark comparison between
4833 4836 local and remote repositories is displayed. With -v/--verbose,
4834 4837 status is also displayed for each bookmark like below::
4835 4838
4836 4839 BM1 01234567890a added
4837 4840 BM2 deleted
4838 4841 BM3 234567890abc advanced
4839 4842 BM4 34567890abcd diverged
4840 4843 BM5 4567890abcde changed
4841 4844
4842 4845 The action taken when pushing depends on the
4843 4846 status of each bookmark:
4844 4847
4845 4848 :``added``: push with ``-B`` will create it
4846 4849 :``deleted``: push with ``-B`` will delete it
4847 4850 :``advanced``: push will update it
4848 4851 :``diverged``: push with ``-B`` will update it
4849 4852 :``changed``: push with ``-B`` will update it
4850 4853
4851 4854 From the point of view of pushing behavior, bookmarks
4852 4855 existing only in the remote repository are treated as
4853 4856 ``deleted``, even if it is in fact added remotely.
4854 4857
4855 4858 Returns 0 if there are outgoing changes, 1 otherwise.
4856 4859 """
4857 4860 if opts.get('graph'):
4858 4861 cmdutil.checkunsupportedgraphflags([], opts)
4859 4862 o, other = hg._outgoing(ui, repo, dest, opts)
4860 4863 if not o:
4861 4864 cmdutil.outgoinghooks(ui, repo, other, opts, o)
4862 4865 return
4863 4866
4864 4867 revdag = cmdutil.graphrevs(repo, o, opts)
4865 4868 displayer = cmdutil.show_changeset(ui, repo, opts, buffered=True)
4866 4869 showparents = [ctx.node() for ctx in repo[None].parents()]
4867 4870 cmdutil.displaygraph(ui, revdag, displayer, showparents,
4868 4871 graphmod.asciiedges)
4869 4872 cmdutil.outgoinghooks(ui, repo, other, opts, o)
4870 4873 return 0
4871 4874
4872 4875 if opts.get('bookmarks'):
4873 4876 dest = ui.expandpath(dest or 'default-push', dest or 'default')
4874 4877 dest, branches = hg.parseurl(dest, opts.get('branch'))
4875 4878 other = hg.peer(repo, opts, dest)
4876 4879 if 'bookmarks' not in other.listkeys('namespaces'):
4877 4880 ui.warn(_("remote doesn't support bookmarks\n"))
4878 4881 return 0
4879 4882 ui.status(_('comparing with %s\n') % util.hidepassword(dest))
4880 4883 return bookmarks.outgoing(ui, repo, other)
4881 4884
4882 4885 repo._subtoppath = ui.expandpath(dest or 'default-push', dest or 'default')
4883 4886 try:
4884 4887 return hg.outgoing(ui, repo, dest, opts)
4885 4888 finally:
4886 4889 del repo._subtoppath
4887 4890
4888 4891 @command('parents',
4889 4892 [('r', 'rev', '', _('show parents of the specified revision'), _('REV')),
4890 4893 ] + templateopts,
4891 4894 _('[-r REV] [FILE]'),
4892 4895 inferrepo=True)
4893 4896 def parents(ui, repo, file_=None, **opts):
4894 4897 """show the parents of the working directory or revision (DEPRECATED)
4895 4898
4896 4899 Print the working directory's parent revisions. If a revision is
4897 4900 given via -r/--rev, the parent of that revision will be printed.
4898 4901 If a file argument is given, the revision in which the file was
4899 4902 last changed (before the working directory revision or the
4900 4903 argument to --rev if given) is printed.
4901 4904
4902 4905 See :hg:`summary` and :hg:`help revsets` for related information.
4903 4906
4904 4907 Returns 0 on success.
4905 4908 """
4906 4909
4907 4910 ctx = scmutil.revsingle(repo, opts.get('rev'), None)
4908 4911
4909 4912 if file_:
4910 4913 m = scmutil.match(ctx, (file_,), opts)
4911 4914 if m.anypats() or len(m.files()) != 1:
4912 4915 raise util.Abort(_('can only specify an explicit filename'))
4913 4916 file_ = m.files()[0]
4914 4917 filenodes = []
4915 4918 for cp in ctx.parents():
4916 4919 if not cp:
4917 4920 continue
4918 4921 try:
4919 4922 filenodes.append(cp.filenode(file_))
4920 4923 except error.LookupError:
4921 4924 pass
4922 4925 if not filenodes:
4923 4926 raise util.Abort(_("'%s' not found in manifest!") % file_)
4924 4927 p = []
4925 4928 for fn in filenodes:
4926 4929 fctx = repo.filectx(file_, fileid=fn)
4927 4930 p.append(fctx.node())
4928 4931 else:
4929 4932 p = [cp.node() for cp in ctx.parents()]
4930 4933
4931 4934 displayer = cmdutil.show_changeset(ui, repo, opts)
4932 4935 for n in p:
4933 4936 if n != nullid:
4934 4937 displayer.show(repo[n])
4935 4938 displayer.close()
4936 4939
4937 4940 @command('paths', [], _('[NAME]'), optionalrepo=True)
4938 4941 def paths(ui, repo, search=None):
4939 4942 """show aliases for remote repositories
4940 4943
4941 4944 Show definition of symbolic path name NAME. If no name is given,
4942 4945 show definition of all available names.
4943 4946
4944 4947 Option -q/--quiet suppresses all output when searching for NAME
4945 4948 and shows only the path names when listing all definitions.
4946 4949
4947 4950 Path names are defined in the [paths] section of your
4948 4951 configuration file and in ``/etc/mercurial/hgrc``. If run inside a
4949 4952 repository, ``.hg/hgrc`` is used, too.
4950 4953
4951 4954 The path names ``default`` and ``default-push`` have a special
4952 4955 meaning. When performing a push or pull operation, they are used
4953 4956 as fallbacks if no location is specified on the command-line.
4954 4957 When ``default-push`` is set, it will be used for push and
4955 4958 ``default`` will be used for pull; otherwise ``default`` is used
4956 4959 as the fallback for both. When cloning a repository, the clone
4957 4960 source is written as ``default`` in ``.hg/hgrc``. Note that
4958 4961 ``default`` and ``default-push`` apply to all inbound (e.g.
4959 4962 :hg:`incoming`) and outbound (e.g. :hg:`outgoing`, :hg:`email` and
4960 4963 :hg:`bundle`) operations.
4961 4964
4962 4965 See :hg:`help urls` for more information.
4963 4966
4964 4967 Returns 0 on success.
4965 4968 """
4966 4969 if search:
4967 4970 for name, path in sorted(ui.paths.iteritems()):
4968 4971 if name == search:
4969 4972 ui.status("%s\n" % util.hidepassword(path.loc))
4970 4973 return
4971 4974 if not ui.quiet:
4972 4975 ui.warn(_("not found!\n"))
4973 4976 return 1
4974 4977 else:
4975 4978 for name, path in sorted(ui.paths.iteritems()):
4976 4979 if ui.quiet:
4977 4980 ui.write("%s\n" % name)
4978 4981 else:
4979 4982 ui.write("%s = %s\n" % (name,
4980 4983 util.hidepassword(path.loc)))
4981 4984
4982 4985 @command('phase',
4983 4986 [('p', 'public', False, _('set changeset phase to public')),
4984 4987 ('d', 'draft', False, _('set changeset phase to draft')),
4985 4988 ('s', 'secret', False, _('set changeset phase to secret')),
4986 4989 ('f', 'force', False, _('allow to move boundary backward')),
4987 4990 ('r', 'rev', [], _('target revision'), _('REV')),
4988 4991 ],
4989 4992 _('[-p|-d|-s] [-f] [-r] [REV...]'))
4990 4993 def phase(ui, repo, *revs, **opts):
4991 4994 """set or show the current phase name
4992 4995
4993 4996 With no argument, show the phase name of the current revision(s).
4994 4997
4995 4998 With one of -p/--public, -d/--draft or -s/--secret, change the
4996 4999 phase value of the specified revisions.
4997 5000
4998 5001 Unless -f/--force is specified, :hg:`phase` won't move changeset from a
4999 5002 lower phase to an higher phase. Phases are ordered as follows::
5000 5003
5001 5004 public < draft < secret
5002 5005
5003 5006 Returns 0 on success, 1 if no phases were changed or some could not
5004 5007 be changed.
5005 5008
5006 5009 (For more information about the phases concept, see :hg:`help phases`.)
5007 5010 """
5008 5011 # search for a unique phase argument
5009 5012 targetphase = None
5010 5013 for idx, name in enumerate(phases.phasenames):
5011 5014 if opts[name]:
5012 5015 if targetphase is not None:
5013 5016 raise util.Abort(_('only one phase can be specified'))
5014 5017 targetphase = idx
5015 5018
5016 5019 # look for specified revision
5017 5020 revs = list(revs)
5018 5021 revs.extend(opts['rev'])
5019 5022 if not revs:
5020 5023 # display both parents as the second parent phase can influence
5021 5024 # the phase of a merge commit
5022 5025 revs = [c.rev() for c in repo[None].parents()]
5023 5026
5024 5027 revs = scmutil.revrange(repo, revs)
5025 5028
5026 5029 lock = None
5027 5030 ret = 0
5028 5031 if targetphase is None:
5029 5032 # display
5030 5033 for r in revs:
5031 5034 ctx = repo[r]
5032 5035 ui.write('%i: %s\n' % (ctx.rev(), ctx.phasestr()))
5033 5036 else:
5034 5037 tr = None
5035 5038 lock = repo.lock()
5036 5039 try:
5037 5040 tr = repo.transaction("phase")
5038 5041 # set phase
5039 5042 if not revs:
5040 5043 raise util.Abort(_('empty revision set'))
5041 5044 nodes = [repo[r].node() for r in revs]
5042 5045 # moving revision from public to draft may hide them
5043 5046 # We have to check result on an unfiltered repository
5044 5047 unfi = repo.unfiltered()
5045 5048 getphase = unfi._phasecache.phase
5046 5049 olddata = [getphase(unfi, r) for r in unfi]
5047 5050 phases.advanceboundary(repo, tr, targetphase, nodes)
5048 5051 if opts['force']:
5049 5052 phases.retractboundary(repo, tr, targetphase, nodes)
5050 5053 tr.close()
5051 5054 finally:
5052 5055 if tr is not None:
5053 5056 tr.release()
5054 5057 lock.release()
5055 5058 getphase = unfi._phasecache.phase
5056 5059 newdata = [getphase(unfi, r) for r in unfi]
5057 5060 changes = sum(newdata[r] != olddata[r] for r in unfi)
5058 5061 cl = unfi.changelog
5059 5062 rejected = [n for n in nodes
5060 5063 if newdata[cl.rev(n)] < targetphase]
5061 5064 if rejected:
5062 5065 ui.warn(_('cannot move %i changesets to a higher '
5063 5066 'phase, use --force\n') % len(rejected))
5064 5067 ret = 1
5065 5068 if changes:
5066 5069 msg = _('phase changed for %i changesets\n') % changes
5067 5070 if ret:
5068 5071 ui.status(msg)
5069 5072 else:
5070 5073 ui.note(msg)
5071 5074 else:
5072 5075 ui.warn(_('no phases changed\n'))
5073 5076 ret = 1
5074 5077 return ret
5075 5078
5076 5079 def postincoming(ui, repo, modheads, optupdate, checkout):
5077 5080 if modheads == 0:
5078 5081 return
5079 5082 if optupdate:
5080 5083 checkout, movemarkfrom = bookmarks.calculateupdate(ui, repo, checkout)
5081 5084 try:
5082 5085 ret = hg.update(repo, checkout)
5083 5086 except util.Abort as inst:
5084 5087 ui.warn(_("not updating: %s\n") % str(inst))
5085 5088 if inst.hint:
5086 5089 ui.warn(_("(%s)\n") % inst.hint)
5087 5090 return 0
5088 5091 if not ret and not checkout:
5089 5092 if bookmarks.update(repo, [movemarkfrom], repo['.'].node()):
5090 5093 ui.status(_("updating bookmark %s\n") % repo._activebookmark)
5091 5094 return ret
5092 5095 if modheads > 1:
5093 5096 currentbranchheads = len(repo.branchheads())
5094 5097 if currentbranchheads == modheads:
5095 5098 ui.status(_("(run 'hg heads' to see heads, 'hg merge' to merge)\n"))
5096 5099 elif currentbranchheads > 1:
5097 5100 ui.status(_("(run 'hg heads .' to see heads, 'hg merge' to "
5098 5101 "merge)\n"))
5099 5102 else:
5100 5103 ui.status(_("(run 'hg heads' to see heads)\n"))
5101 5104 else:
5102 5105 ui.status(_("(run 'hg update' to get a working copy)\n"))
5103 5106
5104 5107 @command('^pull',
5105 5108 [('u', 'update', None,
5106 5109 _('update to new branch head if changesets were pulled')),
5107 5110 ('f', 'force', None, _('run even when remote repository is unrelated')),
5108 5111 ('r', 'rev', [], _('a remote changeset intended to be added'), _('REV')),
5109 5112 ('B', 'bookmark', [], _("bookmark to pull"), _('BOOKMARK')),
5110 5113 ('b', 'branch', [], _('a specific branch you would like to pull'),
5111 5114 _('BRANCH')),
5112 5115 ] + remoteopts,
5113 5116 _('[-u] [-f] [-r REV]... [-e CMD] [--remotecmd CMD] [SOURCE]'))
5114 5117 def pull(ui, repo, source="default", **opts):
5115 5118 """pull changes from the specified source
5116 5119
5117 5120 Pull changes from a remote repository to a local one.
5118 5121
5119 5122 This finds all changes from the repository at the specified path
5120 5123 or URL and adds them to a local repository (the current one unless
5121 5124 -R is specified). By default, this does not update the copy of the
5122 5125 project in the working directory.
5123 5126
5124 5127 Use :hg:`incoming` if you want to see what would have been added
5125 5128 by a pull at the time you issued this command. If you then decide
5126 5129 to add those changes to the repository, you should use :hg:`pull
5127 5130 -r X` where ``X`` is the last changeset listed by :hg:`incoming`.
5128 5131
5129 5132 If SOURCE is omitted, the 'default' path will be used.
5130 5133 See :hg:`help urls` for more information.
5131 5134
5132 5135 Returns 0 on success, 1 if an update had unresolved files.
5133 5136 """
5134 5137 source, branches = hg.parseurl(ui.expandpath(source), opts.get('branch'))
5135 5138 ui.status(_('pulling from %s\n') % util.hidepassword(source))
5136 5139 other = hg.peer(repo, opts, source)
5137 5140 try:
5138 5141 revs, checkout = hg.addbranchrevs(repo, other, branches,
5139 5142 opts.get('rev'))
5140 5143
5141 5144
5142 5145 pullopargs = {}
5143 5146 if opts.get('bookmark'):
5144 5147 if not revs:
5145 5148 revs = []
5146 5149 # The list of bookmark used here is not the one used to actually
5147 5150 # update the bookmark name. This can result in the revision pulled
5148 5151 # not ending up with the name of the bookmark because of a race
5149 5152 # condition on the server. (See issue 4689 for details)
5150 5153 remotebookmarks = other.listkeys('bookmarks')
5151 5154 pullopargs['remotebookmarks'] = remotebookmarks
5152 5155 for b in opts['bookmark']:
5153 5156 if b not in remotebookmarks:
5154 5157 raise util.Abort(_('remote bookmark %s not found!') % b)
5155 5158 revs.append(remotebookmarks[b])
5156 5159
5157 5160 if revs:
5158 5161 try:
5159 5162 # When 'rev' is a bookmark name, we cannot guarantee that it
5160 5163 # will be updated with that name because of a race condition
5161 5164 # server side. (See issue 4689 for details)
5162 5165 oldrevs = revs
5163 5166 revs = [] # actually, nodes
5164 5167 for r in oldrevs:
5165 5168 node = other.lookup(r)
5166 5169 revs.append(node)
5167 5170 if r == checkout:
5168 5171 checkout = node
5169 5172 except error.CapabilityError:
5170 5173 err = _("other repository doesn't support revision lookup, "
5171 5174 "so a rev cannot be specified.")
5172 5175 raise util.Abort(err)
5173 5176
5174 5177 modheads = exchange.pull(repo, other, heads=revs,
5175 5178 force=opts.get('force'),
5176 5179 bookmarks=opts.get('bookmark', ()),
5177 5180 opargs=pullopargs).cgresult
5178 5181 if checkout:
5179 5182 checkout = str(repo.changelog.rev(checkout))
5180 5183 repo._subtoppath = source
5181 5184 try:
5182 5185 ret = postincoming(ui, repo, modheads, opts.get('update'), checkout)
5183 5186
5184 5187 finally:
5185 5188 del repo._subtoppath
5186 5189
5187 5190 finally:
5188 5191 other.close()
5189 5192 return ret
5190 5193
5191 5194 @command('^push',
5192 5195 [('f', 'force', None, _('force push')),
5193 5196 ('r', 'rev', [],
5194 5197 _('a changeset intended to be included in the destination'),
5195 5198 _('REV')),
5196 5199 ('B', 'bookmark', [], _("bookmark to push"), _('BOOKMARK')),
5197 5200 ('b', 'branch', [],
5198 5201 _('a specific branch you would like to push'), _('BRANCH')),
5199 5202 ('', 'new-branch', False, _('allow pushing a new branch')),
5200 5203 ] + remoteopts,
5201 5204 _('[-f] [-r REV]... [-e CMD] [--remotecmd CMD] [DEST]'))
5202 5205 def push(ui, repo, dest=None, **opts):
5203 5206 """push changes to the specified destination
5204 5207
5205 5208 Push changesets from the local repository to the specified
5206 5209 destination.
5207 5210
5208 5211 This operation is symmetrical to pull: it is identical to a pull
5209 5212 in the destination repository from the current one.
5210 5213
5211 5214 By default, push will not allow creation of new heads at the
5212 5215 destination, since multiple heads would make it unclear which head
5213 5216 to use. In this situation, it is recommended to pull and merge
5214 5217 before pushing.
5215 5218
5216 5219 Use --new-branch if you want to allow push to create a new named
5217 5220 branch that is not present at the destination. This allows you to
5218 5221 only create a new branch without forcing other changes.
5219 5222
5220 5223 .. note::
5221 5224
5222 5225 Extra care should be taken with the -f/--force option,
5223 5226 which will push all new heads on all branches, an action which will
5224 5227 almost always cause confusion for collaborators.
5225 5228
5226 5229 If -r/--rev is used, the specified revision and all its ancestors
5227 5230 will be pushed to the remote repository.
5228 5231
5229 5232 If -B/--bookmark is used, the specified bookmarked revision, its
5230 5233 ancestors, and the bookmark will be pushed to the remote
5231 5234 repository.
5232 5235
5233 5236 Please see :hg:`help urls` for important details about ``ssh://``
5234 5237 URLs. If DESTINATION is omitted, a default path will be used.
5235 5238
5236 5239 Returns 0 if push was successful, 1 if nothing to push.
5237 5240 """
5238 5241
5239 5242 if opts.get('bookmark'):
5240 5243 ui.setconfig('bookmarks', 'pushing', opts['bookmark'], 'push')
5241 5244 for b in opts['bookmark']:
5242 5245 # translate -B options to -r so changesets get pushed
5243 5246 if b in repo._bookmarks:
5244 5247 opts.setdefault('rev', []).append(b)
5245 5248 else:
5246 5249 # if we try to push a deleted bookmark, translate it to null
5247 5250 # this lets simultaneous -r, -b options continue working
5248 5251 opts.setdefault('rev', []).append("null")
5249 5252
5250 5253 dest = ui.expandpath(dest or 'default-push', dest or 'default')
5251 5254 dest, branches = hg.parseurl(dest, opts.get('branch'))
5252 5255 ui.status(_('pushing to %s\n') % util.hidepassword(dest))
5253 5256 revs, checkout = hg.addbranchrevs(repo, repo, branches, opts.get('rev'))
5254 5257 try:
5255 5258 other = hg.peer(repo, opts, dest)
5256 5259 except error.RepoError:
5257 5260 if dest == "default-push":
5258 5261 raise util.Abort(_("default repository not configured!"),
5259 5262 hint=_('see the "path" section in "hg help config"'))
5260 5263 else:
5261 5264 raise
5262 5265
5263 5266 if revs:
5264 5267 revs = [repo.lookup(r) for r in scmutil.revrange(repo, revs)]
5265 5268 if not revs:
5266 5269 raise util.Abort(_("specified revisions evaluate to an empty set"),
5267 5270 hint=_("use different revision arguments"))
5268 5271
5269 5272 repo._subtoppath = dest
5270 5273 try:
5271 5274 # push subrepos depth-first for coherent ordering
5272 5275 c = repo['']
5273 5276 subs = c.substate # only repos that are committed
5274 5277 for s in sorted(subs):
5275 5278 result = c.sub(s).push(opts)
5276 5279 if result == 0:
5277 5280 return not result
5278 5281 finally:
5279 5282 del repo._subtoppath
5280 5283 pushop = exchange.push(repo, other, opts.get('force'), revs=revs,
5281 5284 newbranch=opts.get('new_branch'),
5282 5285 bookmarks=opts.get('bookmark', ()))
5283 5286
5284 5287 result = not pushop.cgresult
5285 5288
5286 5289 if pushop.bkresult is not None:
5287 5290 if pushop.bkresult == 2:
5288 5291 result = 2
5289 5292 elif not result and pushop.bkresult:
5290 5293 result = 2
5291 5294
5292 5295 return result
5293 5296
5294 5297 @command('recover', [])
5295 5298 def recover(ui, repo):
5296 5299 """roll back an interrupted transaction
5297 5300
5298 5301 Recover from an interrupted commit or pull.
5299 5302
5300 5303 This command tries to fix the repository status after an
5301 5304 interrupted operation. It should only be necessary when Mercurial
5302 5305 suggests it.
5303 5306
5304 5307 Returns 0 if successful, 1 if nothing to recover or verify fails.
5305 5308 """
5306 5309 if repo.recover():
5307 5310 return hg.verify(repo)
5308 5311 return 1
5309 5312
5310 5313 @command('^remove|rm',
5311 5314 [('A', 'after', None, _('record delete for missing files')),
5312 5315 ('f', 'force', None,
5313 5316 _('remove (and delete) file even if added or modified')),
5314 5317 ] + subrepoopts + walkopts,
5315 5318 _('[OPTION]... FILE...'),
5316 5319 inferrepo=True)
5317 5320 def remove(ui, repo, *pats, **opts):
5318 5321 """remove the specified files on the next commit
5319 5322
5320 5323 Schedule the indicated files for removal from the current branch.
5321 5324
5322 5325 This command schedules the files to be removed at the next commit.
5323 5326 To undo a remove before that, see :hg:`revert`. To undo added
5324 5327 files, see :hg:`forget`.
5325 5328
5326 5329 .. container:: verbose
5327 5330
5328 5331 -A/--after can be used to remove only files that have already
5329 5332 been deleted, -f/--force can be used to force deletion, and -Af
5330 5333 can be used to remove files from the next revision without
5331 5334 deleting them from the working directory.
5332 5335
5333 5336 The following table details the behavior of remove for different
5334 5337 file states (columns) and option combinations (rows). The file
5335 5338 states are Added [A], Clean [C], Modified [M] and Missing [!]
5336 5339 (as reported by :hg:`status`). The actions are Warn, Remove
5337 5340 (from branch) and Delete (from disk):
5338 5341
5339 5342 ========= == == == ==
5340 5343 opt/state A C M !
5341 5344 ========= == == == ==
5342 5345 none W RD W R
5343 5346 -f R RD RD R
5344 5347 -A W W W R
5345 5348 -Af R R R R
5346 5349 ========= == == == ==
5347 5350
5348 5351 Note that remove never deletes files in Added [A] state from the
5349 5352 working directory, not even if option --force is specified.
5350 5353
5351 5354 Returns 0 on success, 1 if any warnings encountered.
5352 5355 """
5353 5356
5354 5357 after, force = opts.get('after'), opts.get('force')
5355 5358 if not pats and not after:
5356 5359 raise util.Abort(_('no files specified'))
5357 5360
5358 5361 m = scmutil.match(repo[None], pats, opts)
5359 5362 subrepos = opts.get('subrepos')
5360 5363 return cmdutil.remove(ui, repo, m, "", after, force, subrepos)
5361 5364
5362 5365 @command('rename|move|mv',
5363 5366 [('A', 'after', None, _('record a rename that has already occurred')),
5364 5367 ('f', 'force', None, _('forcibly copy over an existing managed file')),
5365 5368 ] + walkopts + dryrunopts,
5366 5369 _('[OPTION]... SOURCE... DEST'))
5367 5370 def rename(ui, repo, *pats, **opts):
5368 5371 """rename files; equivalent of copy + remove
5369 5372
5370 5373 Mark dest as copies of sources; mark sources for deletion. If dest
5371 5374 is a directory, copies are put in that directory. If dest is a
5372 5375 file, there can only be one source.
5373 5376
5374 5377 By default, this command copies the contents of files as they
5375 5378 exist in the working directory. If invoked with -A/--after, the
5376 5379 operation is recorded, but no copying is performed.
5377 5380
5378 5381 This command takes effect at the next commit. To undo a rename
5379 5382 before that, see :hg:`revert`.
5380 5383
5381 5384 Returns 0 on success, 1 if errors are encountered.
5382 5385 """
5383 5386 wlock = repo.wlock(False)
5384 5387 try:
5385 5388 return cmdutil.copy(ui, repo, pats, opts, rename=True)
5386 5389 finally:
5387 5390 wlock.release()
5388 5391
5389 5392 @command('resolve',
5390 5393 [('a', 'all', None, _('select all unresolved files')),
5391 5394 ('l', 'list', None, _('list state of files needing merge')),
5392 5395 ('m', 'mark', None, _('mark files as resolved')),
5393 5396 ('u', 'unmark', None, _('mark files as unresolved')),
5394 5397 ('n', 'no-status', None, _('hide status prefix'))]
5395 5398 + mergetoolopts + walkopts + formatteropts,
5396 5399 _('[OPTION]... [FILE]...'),
5397 5400 inferrepo=True)
5398 5401 def resolve(ui, repo, *pats, **opts):
5399 5402 """redo merges or set/view the merge status of files
5400 5403
5401 5404 Merges with unresolved conflicts are often the result of
5402 5405 non-interactive merging using the ``internal:merge`` configuration
5403 5406 setting, or a command-line merge tool like ``diff3``. The resolve
5404 5407 command is used to manage the files involved in a merge, after
5405 5408 :hg:`merge` has been run, and before :hg:`commit` is run (i.e. the
5406 5409 working directory must have two parents). See :hg:`help
5407 5410 merge-tools` for information on configuring merge tools.
5408 5411
5409 5412 The resolve command can be used in the following ways:
5410 5413
5411 5414 - :hg:`resolve [--tool TOOL] FILE...`: attempt to re-merge the specified
5412 5415 files, discarding any previous merge attempts. Re-merging is not
5413 5416 performed for files already marked as resolved. Use ``--all/-a``
5414 5417 to select all unresolved files. ``--tool`` can be used to specify
5415 5418 the merge tool used for the given files. It overrides the HGMERGE
5416 5419 environment variable and your configuration files. Previous file
5417 5420 contents are saved with a ``.orig`` suffix.
5418 5421
5419 5422 - :hg:`resolve -m [FILE]`: mark a file as having been resolved
5420 5423 (e.g. after having manually fixed-up the files). The default is
5421 5424 to mark all unresolved files.
5422 5425
5423 5426 - :hg:`resolve -u [FILE]...`: mark a file as unresolved. The
5424 5427 default is to mark all resolved files.
5425 5428
5426 5429 - :hg:`resolve -l`: list files which had or still have conflicts.
5427 5430 In the printed list, ``U`` = unresolved and ``R`` = resolved.
5428 5431
5429 5432 Note that Mercurial will not let you commit files with unresolved
5430 5433 merge conflicts. You must use :hg:`resolve -m ...` before you can
5431 5434 commit after a conflicting merge.
5432 5435
5433 5436 Returns 0 on success, 1 if any files fail a resolve attempt.
5434 5437 """
5435 5438
5436 5439 all, mark, unmark, show, nostatus = \
5437 5440 [opts.get(o) for o in 'all mark unmark list no_status'.split()]
5438 5441
5439 5442 if (show and (mark or unmark)) or (mark and unmark):
5440 5443 raise util.Abort(_("too many options specified"))
5441 5444 if pats and all:
5442 5445 raise util.Abort(_("can't specify --all and patterns"))
5443 5446 if not (all or pats or show or mark or unmark):
5444 5447 raise util.Abort(_('no files or directories specified'),
5445 5448 hint=('use --all to remerge all files'))
5446 5449
5447 5450 if show:
5448 5451 fm = ui.formatter('resolve', opts)
5449 5452 ms = mergemod.mergestate(repo)
5450 5453 m = scmutil.match(repo[None], pats, opts)
5451 5454 for f in ms:
5452 5455 if not m(f):
5453 5456 continue
5454 5457 l = 'resolve.' + {'u': 'unresolved', 'r': 'resolved'}[ms[f]]
5455 5458 fm.startitem()
5456 5459 fm.condwrite(not nostatus, 'status', '%s ', ms[f].upper(), label=l)
5457 5460 fm.write('path', '%s\n', f, label=l)
5458 5461 fm.end()
5459 5462 return 0
5460 5463
5461 5464 wlock = repo.wlock()
5462 5465 try:
5463 5466 ms = mergemod.mergestate(repo)
5464 5467
5465 5468 if not (ms.active() or repo.dirstate.p2() != nullid):
5466 5469 raise util.Abort(
5467 5470 _('resolve command not applicable when not merging'))
5468 5471
5469 5472 m = scmutil.match(repo[None], pats, opts)
5470 5473 ret = 0
5471 5474 didwork = False
5472 5475
5473 5476 for f in ms:
5474 5477 if not m(f):
5475 5478 continue
5476 5479
5477 5480 didwork = True
5478 5481
5479 5482 if mark:
5480 5483 ms.mark(f, "r")
5481 5484 elif unmark:
5482 5485 ms.mark(f, "u")
5483 5486 else:
5484 5487 wctx = repo[None]
5485 5488
5486 5489 # backup pre-resolve (merge uses .orig for its own purposes)
5487 5490 a = repo.wjoin(f)
5488 5491 util.copyfile(a, a + ".resolve")
5489 5492
5490 5493 try:
5491 5494 # resolve file
5492 5495 ui.setconfig('ui', 'forcemerge', opts.get('tool', ''),
5493 5496 'resolve')
5494 5497 if ms.resolve(f, wctx):
5495 5498 ret = 1
5496 5499 finally:
5497 5500 ui.setconfig('ui', 'forcemerge', '', 'resolve')
5498 5501 ms.commit()
5499 5502
5500 5503 # replace filemerge's .orig file with our resolve file
5501 5504 util.rename(a + ".resolve", a + ".orig")
5502 5505
5503 5506 ms.commit()
5504 5507
5505 5508 if not didwork and pats:
5506 5509 ui.warn(_("arguments do not match paths that need resolving\n"))
5507 5510
5508 5511 finally:
5509 5512 wlock.release()
5510 5513
5511 5514 # Nudge users into finishing an unfinished operation
5512 5515 if not list(ms.unresolved()):
5513 5516 ui.status(_('(no more unresolved files)\n'))
5514 5517
5515 5518 return ret
5516 5519
5517 5520 @command('revert',
5518 5521 [('a', 'all', None, _('revert all changes when no arguments given')),
5519 5522 ('d', 'date', '', _('tipmost revision matching date'), _('DATE')),
5520 5523 ('r', 'rev', '', _('revert to the specified revision'), _('REV')),
5521 5524 ('C', 'no-backup', None, _('do not save backup copies of files')),
5522 5525 ('i', 'interactive', None,
5523 5526 _('interactively select the changes (EXPERIMENTAL)')),
5524 5527 ] + walkopts + dryrunopts,
5525 5528 _('[OPTION]... [-r REV] [NAME]...'))
5526 5529 def revert(ui, repo, *pats, **opts):
5527 5530 """restore files to their checkout state
5528 5531
5529 5532 .. note::
5530 5533
5531 5534 To check out earlier revisions, you should use :hg:`update REV`.
5532 5535 To cancel an uncommitted merge (and lose your changes),
5533 5536 use :hg:`update --clean .`.
5534 5537
5535 5538 With no revision specified, revert the specified files or directories
5536 5539 to the contents they had in the parent of the working directory.
5537 5540 This restores the contents of files to an unmodified
5538 5541 state and unschedules adds, removes, copies, and renames. If the
5539 5542 working directory has two parents, you must explicitly specify a
5540 5543 revision.
5541 5544
5542 5545 Using the -r/--rev or -d/--date options, revert the given files or
5543 5546 directories to their states as of a specific revision. Because
5544 5547 revert does not change the working directory parents, this will
5545 5548 cause these files to appear modified. This can be helpful to "back
5546 5549 out" some or all of an earlier change. See :hg:`backout` for a
5547 5550 related method.
5548 5551
5549 5552 Modified files are saved with a .orig suffix before reverting.
5550 5553 To disable these backups, use --no-backup.
5551 5554
5552 5555 See :hg:`help dates` for a list of formats valid for -d/--date.
5553 5556
5554 5557 Returns 0 on success.
5555 5558 """
5556 5559
5557 5560 if opts.get("date"):
5558 5561 if opts.get("rev"):
5559 5562 raise util.Abort(_("you can't specify a revision and a date"))
5560 5563 opts["rev"] = cmdutil.finddate(ui, repo, opts["date"])
5561 5564
5562 5565 parent, p2 = repo.dirstate.parents()
5563 5566 if not opts.get('rev') and p2 != nullid:
5564 5567 # revert after merge is a trap for new users (issue2915)
5565 5568 raise util.Abort(_('uncommitted merge with no revision specified'),
5566 5569 hint=_('use "hg update" or see "hg help revert"'))
5567 5570
5568 5571 ctx = scmutil.revsingle(repo, opts.get('rev'))
5569 5572
5570 5573 if (not (pats or opts.get('include') or opts.get('exclude') or
5571 5574 opts.get('all') or opts.get('interactive'))):
5572 5575 msg = _("no files or directories specified")
5573 5576 if p2 != nullid:
5574 5577 hint = _("uncommitted merge, use --all to discard all changes,"
5575 5578 " or 'hg update -C .' to abort the merge")
5576 5579 raise util.Abort(msg, hint=hint)
5577 5580 dirty = any(repo.status())
5578 5581 node = ctx.node()
5579 5582 if node != parent:
5580 5583 if dirty:
5581 5584 hint = _("uncommitted changes, use --all to discard all"
5582 5585 " changes, or 'hg update %s' to update") % ctx.rev()
5583 5586 else:
5584 5587 hint = _("use --all to revert all files,"
5585 5588 " or 'hg update %s' to update") % ctx.rev()
5586 5589 elif dirty:
5587 5590 hint = _("uncommitted changes, use --all to discard all changes")
5588 5591 else:
5589 5592 hint = _("use --all to revert all files")
5590 5593 raise util.Abort(msg, hint=hint)
5591 5594
5592 5595 return cmdutil.revert(ui, repo, ctx, (parent, p2), *pats, **opts)
5593 5596
5594 5597 @command('rollback', dryrunopts +
5595 5598 [('f', 'force', False, _('ignore safety measures'))])
5596 5599 def rollback(ui, repo, **opts):
5597 5600 """roll back the last transaction (DANGEROUS) (DEPRECATED)
5598 5601
5599 5602 Please use :hg:`commit --amend` instead of rollback to correct
5600 5603 mistakes in the last commit.
5601 5604
5602 5605 This command should be used with care. There is only one level of
5603 5606 rollback, and there is no way to undo a rollback. It will also
5604 5607 restore the dirstate at the time of the last transaction, losing
5605 5608 any dirstate changes since that time. This command does not alter
5606 5609 the working directory.
5607 5610
5608 5611 Transactions are used to encapsulate the effects of all commands
5609 5612 that create new changesets or propagate existing changesets into a
5610 5613 repository.
5611 5614
5612 5615 .. container:: verbose
5613 5616
5614 5617 For example, the following commands are transactional, and their
5615 5618 effects can be rolled back:
5616 5619
5617 5620 - commit
5618 5621 - import
5619 5622 - pull
5620 5623 - push (with this repository as the destination)
5621 5624 - unbundle
5622 5625
5623 5626 To avoid permanent data loss, rollback will refuse to rollback a
5624 5627 commit transaction if it isn't checked out. Use --force to
5625 5628 override this protection.
5626 5629
5627 5630 This command is not intended for use on public repositories. Once
5628 5631 changes are visible for pull by other users, rolling a transaction
5629 5632 back locally is ineffective (someone else may already have pulled
5630 5633 the changes). Furthermore, a race is possible with readers of the
5631 5634 repository; for example an in-progress pull from the repository
5632 5635 may fail if a rollback is performed.
5633 5636
5634 5637 Returns 0 on success, 1 if no rollback data is available.
5635 5638 """
5636 5639 return repo.rollback(dryrun=opts.get('dry_run'),
5637 5640 force=opts.get('force'))
5638 5641
5639 5642 @command('root', [])
5640 5643 def root(ui, repo):
5641 5644 """print the root (top) of the current working directory
5642 5645
5643 5646 Print the root directory of the current repository.
5644 5647
5645 5648 Returns 0 on success.
5646 5649 """
5647 5650 ui.write(repo.root + "\n")
5648 5651
5649 5652 @command('^serve',
5650 5653 [('A', 'accesslog', '', _('name of access log file to write to'),
5651 5654 _('FILE')),
5652 5655 ('d', 'daemon', None, _('run server in background')),
5653 5656 ('', 'daemon-pipefds', '', _('used internally by daemon mode'), _('FILE')),
5654 5657 ('E', 'errorlog', '', _('name of error log file to write to'), _('FILE')),
5655 5658 # use string type, then we can check if something was passed
5656 5659 ('p', 'port', '', _('port to listen on (default: 8000)'), _('PORT')),
5657 5660 ('a', 'address', '', _('address to listen on (default: all interfaces)'),
5658 5661 _('ADDR')),
5659 5662 ('', 'prefix', '', _('prefix path to serve from (default: server root)'),
5660 5663 _('PREFIX')),
5661 5664 ('n', 'name', '',
5662 5665 _('name to show in web pages (default: working directory)'), _('NAME')),
5663 5666 ('', 'web-conf', '',
5664 5667 _('name of the hgweb config file (see "hg help hgweb")'), _('FILE')),
5665 5668 ('', 'webdir-conf', '', _('name of the hgweb config file (DEPRECATED)'),
5666 5669 _('FILE')),
5667 5670 ('', 'pid-file', '', _('name of file to write process ID to'), _('FILE')),
5668 5671 ('', 'stdio', None, _('for remote clients')),
5669 5672 ('', 'cmdserver', '', _('for remote clients'), _('MODE')),
5670 5673 ('t', 'templates', '', _('web templates to use'), _('TEMPLATE')),
5671 5674 ('', 'style', '', _('template style to use'), _('STYLE')),
5672 5675 ('6', 'ipv6', None, _('use IPv6 in addition to IPv4')),
5673 5676 ('', 'certificate', '', _('SSL certificate file'), _('FILE'))],
5674 5677 _('[OPTION]...'),
5675 5678 optionalrepo=True)
5676 5679 def serve(ui, repo, **opts):
5677 5680 """start stand-alone webserver
5678 5681
5679 5682 Start a local HTTP repository browser and pull server. You can use
5680 5683 this for ad-hoc sharing and browsing of repositories. It is
5681 5684 recommended to use a real web server to serve a repository for
5682 5685 longer periods of time.
5683 5686
5684 5687 Please note that the server does not implement access control.
5685 5688 This means that, by default, anybody can read from the server and
5686 5689 nobody can write to it by default. Set the ``web.allow_push``
5687 5690 option to ``*`` to allow everybody to push to the server. You
5688 5691 should use a real web server if you need to authenticate users.
5689 5692
5690 5693 By default, the server logs accesses to stdout and errors to
5691 5694 stderr. Use the -A/--accesslog and -E/--errorlog options to log to
5692 5695 files.
5693 5696
5694 5697 To have the server choose a free port number to listen on, specify
5695 5698 a port number of 0; in this case, the server will print the port
5696 5699 number it uses.
5697 5700
5698 5701 Returns 0 on success.
5699 5702 """
5700 5703
5701 5704 if opts["stdio"] and opts["cmdserver"]:
5702 5705 raise util.Abort(_("cannot use --stdio with --cmdserver"))
5703 5706
5704 5707 if opts["stdio"]:
5705 5708 if repo is None:
5706 5709 raise error.RepoError(_("there is no Mercurial repository here"
5707 5710 " (.hg not found)"))
5708 5711 s = sshserver.sshserver(ui, repo)
5709 5712 s.serve_forever()
5710 5713
5711 5714 if opts["cmdserver"]:
5712 5715 service = commandserver.createservice(ui, repo, opts)
5713 5716 return cmdutil.service(opts, initfn=service.init, runfn=service.run)
5714 5717
5715 5718 # this way we can check if something was given in the command-line
5716 5719 if opts.get('port'):
5717 5720 opts['port'] = util.getport(opts.get('port'))
5718 5721
5719 5722 if repo:
5720 5723 baseui = repo.baseui
5721 5724 else:
5722 5725 baseui = ui
5723 5726 optlist = ("name templates style address port prefix ipv6"
5724 5727 " accesslog errorlog certificate encoding")
5725 5728 for o in optlist.split():
5726 5729 val = opts.get(o, '')
5727 5730 if val in (None, ''): # should check against default options instead
5728 5731 continue
5729 5732 baseui.setconfig("web", o, val, 'serve')
5730 5733 if repo and repo.ui != baseui:
5731 5734 repo.ui.setconfig("web", o, val, 'serve')
5732 5735
5733 5736 o = opts.get('web_conf') or opts.get('webdir_conf')
5734 5737 if not o:
5735 5738 if not repo:
5736 5739 raise error.RepoError(_("there is no Mercurial repository"
5737 5740 " here (.hg not found)"))
5738 5741 o = repo
5739 5742
5740 5743 app = hgweb.hgweb(o, baseui=baseui)
5741 5744 service = httpservice(ui, app, opts)
5742 5745 cmdutil.service(opts, initfn=service.init, runfn=service.run)
5743 5746
5744 5747 class httpservice(object):
5745 5748 def __init__(self, ui, app, opts):
5746 5749 self.ui = ui
5747 5750 self.app = app
5748 5751 self.opts = opts
5749 5752
5750 5753 def init(self):
5751 5754 util.setsignalhandler()
5752 5755 self.httpd = hgweb_server.create_server(self.ui, self.app)
5753 5756
5754 5757 if self.opts['port'] and not self.ui.verbose:
5755 5758 return
5756 5759
5757 5760 if self.httpd.prefix:
5758 5761 prefix = self.httpd.prefix.strip('/') + '/'
5759 5762 else:
5760 5763 prefix = ''
5761 5764
5762 5765 port = ':%d' % self.httpd.port
5763 5766 if port == ':80':
5764 5767 port = ''
5765 5768
5766 5769 bindaddr = self.httpd.addr
5767 5770 if bindaddr == '0.0.0.0':
5768 5771 bindaddr = '*'
5769 5772 elif ':' in bindaddr: # IPv6
5770 5773 bindaddr = '[%s]' % bindaddr
5771 5774
5772 5775 fqaddr = self.httpd.fqaddr
5773 5776 if ':' in fqaddr:
5774 5777 fqaddr = '[%s]' % fqaddr
5775 5778 if self.opts['port']:
5776 5779 write = self.ui.status
5777 5780 else:
5778 5781 write = self.ui.write
5779 5782 write(_('listening at http://%s%s/%s (bound to %s:%d)\n') %
5780 5783 (fqaddr, port, prefix, bindaddr, self.httpd.port))
5781 5784 self.ui.flush() # avoid buffering of status message
5782 5785
5783 5786 def run(self):
5784 5787 self.httpd.serve_forever()
5785 5788
5786 5789
5787 5790 @command('^status|st',
5788 5791 [('A', 'all', None, _('show status of all files')),
5789 5792 ('m', 'modified', None, _('show only modified files')),
5790 5793 ('a', 'added', None, _('show only added files')),
5791 5794 ('r', 'removed', None, _('show only removed files')),
5792 5795 ('d', 'deleted', None, _('show only deleted (but tracked) files')),
5793 5796 ('c', 'clean', None, _('show only files without changes')),
5794 5797 ('u', 'unknown', None, _('show only unknown (not tracked) files')),
5795 5798 ('i', 'ignored', None, _('show only ignored files')),
5796 5799 ('n', 'no-status', None, _('hide status prefix')),
5797 5800 ('C', 'copies', None, _('show source of copied files')),
5798 5801 ('0', 'print0', None, _('end filenames with NUL, for use with xargs')),
5799 5802 ('', 'rev', [], _('show difference from revision'), _('REV')),
5800 5803 ('', 'change', '', _('list the changed files of a revision'), _('REV')),
5801 5804 ] + walkopts + subrepoopts + formatteropts,
5802 5805 _('[OPTION]... [FILE]...'),
5803 5806 inferrepo=True)
5804 5807 def status(ui, repo, *pats, **opts):
5805 5808 """show changed files in the working directory
5806 5809
5807 5810 Show status of files in the repository. If names are given, only
5808 5811 files that match are shown. Files that are clean or ignored or
5809 5812 the source of a copy/move operation, are not listed unless
5810 5813 -c/--clean, -i/--ignored, -C/--copies or -A/--all are given.
5811 5814 Unless options described with "show only ..." are given, the
5812 5815 options -mardu are used.
5813 5816
5814 5817 Option -q/--quiet hides untracked (unknown and ignored) files
5815 5818 unless explicitly requested with -u/--unknown or -i/--ignored.
5816 5819
5817 5820 .. note::
5818 5821
5819 5822 status may appear to disagree with diff if permissions have
5820 5823 changed or a merge has occurred. The standard diff format does
5821 5824 not report permission changes and diff only reports changes
5822 5825 relative to one merge parent.
5823 5826
5824 5827 If one revision is given, it is used as the base revision.
5825 5828 If two revisions are given, the differences between them are
5826 5829 shown. The --change option can also be used as a shortcut to list
5827 5830 the changed files of a revision from its first parent.
5828 5831
5829 5832 The codes used to show the status of files are::
5830 5833
5831 5834 M = modified
5832 5835 A = added
5833 5836 R = removed
5834 5837 C = clean
5835 5838 ! = missing (deleted by non-hg command, but still tracked)
5836 5839 ? = not tracked
5837 5840 I = ignored
5838 5841 = origin of the previous file (with --copies)
5839 5842
5840 5843 .. container:: verbose
5841 5844
5842 5845 Examples:
5843 5846
5844 5847 - show changes in the working directory relative to a
5845 5848 changeset::
5846 5849
5847 5850 hg status --rev 9353
5848 5851
5849 5852 - show changes in the working directory relative to the
5850 5853 current directory (see :hg:`help patterns` for more information)::
5851 5854
5852 5855 hg status re:
5853 5856
5854 5857 - show all changes including copies in an existing changeset::
5855 5858
5856 5859 hg status --copies --change 9353
5857 5860
5858 5861 - get a NUL separated list of added files, suitable for xargs::
5859 5862
5860 5863 hg status -an0
5861 5864
5862 5865 Returns 0 on success.
5863 5866 """
5864 5867
5865 5868 revs = opts.get('rev')
5866 5869 change = opts.get('change')
5867 5870
5868 5871 if revs and change:
5869 5872 msg = _('cannot specify --rev and --change at the same time')
5870 5873 raise util.Abort(msg)
5871 5874 elif change:
5872 5875 node2 = scmutil.revsingle(repo, change, None).node()
5873 5876 node1 = repo[node2].p1().node()
5874 5877 else:
5875 5878 node1, node2 = scmutil.revpair(repo, revs)
5876 5879
5877 5880 if pats:
5878 5881 cwd = repo.getcwd()
5879 5882 else:
5880 5883 cwd = ''
5881 5884
5882 5885 if opts.get('print0'):
5883 5886 end = '\0'
5884 5887 else:
5885 5888 end = '\n'
5886 5889 copy = {}
5887 5890 states = 'modified added removed deleted unknown ignored clean'.split()
5888 5891 show = [k for k in states if opts.get(k)]
5889 5892 if opts.get('all'):
5890 5893 show += ui.quiet and (states[:4] + ['clean']) or states
5891 5894 if not show:
5892 5895 if ui.quiet:
5893 5896 show = states[:4]
5894 5897 else:
5895 5898 show = states[:5]
5896 5899
5897 5900 m = scmutil.match(repo[node2], pats, opts)
5898 5901 stat = repo.status(node1, node2, m,
5899 5902 'ignored' in show, 'clean' in show, 'unknown' in show,
5900 5903 opts.get('subrepos'))
5901 5904 changestates = zip(states, 'MAR!?IC', stat)
5902 5905
5903 5906 if (opts.get('all') or opts.get('copies')
5904 5907 or ui.configbool('ui', 'statuscopies')) and not opts.get('no_status'):
5905 5908 copy = copies.pathcopies(repo[node1], repo[node2], m)
5906 5909
5907 5910 fm = ui.formatter('status', opts)
5908 5911 fmt = '%s' + end
5909 5912 showchar = not opts.get('no_status')
5910 5913
5911 5914 for state, char, files in changestates:
5912 5915 if state in show:
5913 5916 label = 'status.' + state
5914 5917 for f in files:
5915 5918 fm.startitem()
5916 5919 fm.condwrite(showchar, 'status', '%s ', char, label=label)
5917 5920 fm.write('path', fmt, repo.pathto(f, cwd), label=label)
5918 5921 if f in copy:
5919 5922 fm.write("copy", ' %s' + end, repo.pathto(copy[f], cwd),
5920 5923 label='status.copied')
5921 5924 fm.end()
5922 5925
5923 5926 @command('^summary|sum',
5924 5927 [('', 'remote', None, _('check for push and pull'))], '[--remote]')
5925 5928 def summary(ui, repo, **opts):
5926 5929 """summarize working directory state
5927 5930
5928 5931 This generates a brief summary of the working directory state,
5929 5932 including parents, branch, commit status, phase and available updates.
5930 5933
5931 5934 With the --remote option, this will check the default paths for
5932 5935 incoming and outgoing changes. This can be time-consuming.
5933 5936
5934 5937 Returns 0 on success.
5935 5938 """
5936 5939
5937 5940 ctx = repo[None]
5938 5941 parents = ctx.parents()
5939 5942 pnode = parents[0].node()
5940 5943 marks = []
5941 5944
5942 5945 for p in parents:
5943 5946 # label with log.changeset (instead of log.parent) since this
5944 5947 # shows a working directory parent *changeset*:
5945 5948 # i18n: column positioning for "hg summary"
5946 5949 ui.write(_('parent: %d:%s ') % (p.rev(), str(p)),
5947 5950 label='log.changeset changeset.%s' % p.phasestr())
5948 5951 ui.write(' '.join(p.tags()), label='log.tag')
5949 5952 if p.bookmarks():
5950 5953 marks.extend(p.bookmarks())
5951 5954 if p.rev() == -1:
5952 5955 if not len(repo):
5953 5956 ui.write(_(' (empty repository)'))
5954 5957 else:
5955 5958 ui.write(_(' (no revision checked out)'))
5956 5959 ui.write('\n')
5957 5960 if p.description():
5958 5961 ui.status(' ' + p.description().splitlines()[0].strip() + '\n',
5959 5962 label='log.summary')
5960 5963
5961 5964 branch = ctx.branch()
5962 5965 bheads = repo.branchheads(branch)
5963 5966 # i18n: column positioning for "hg summary"
5964 5967 m = _('branch: %s\n') % branch
5965 5968 if branch != 'default':
5966 5969 ui.write(m, label='log.branch')
5967 5970 else:
5968 5971 ui.status(m, label='log.branch')
5969 5972
5970 5973 if marks:
5971 5974 active = repo._activebookmark
5972 5975 # i18n: column positioning for "hg summary"
5973 5976 ui.write(_('bookmarks:'), label='log.bookmark')
5974 5977 if active is not None:
5975 5978 if active in marks:
5976 5979 ui.write(' *' + active, label=activebookmarklabel)
5977 5980 marks.remove(active)
5978 5981 else:
5979 5982 ui.write(' [%s]' % active, label=activebookmarklabel)
5980 5983 for m in marks:
5981 5984 ui.write(' ' + m, label='log.bookmark')
5982 5985 ui.write('\n', label='log.bookmark')
5983 5986
5984 5987 status = repo.status(unknown=True)
5985 5988
5986 5989 c = repo.dirstate.copies()
5987 5990 copied, renamed = [], []
5988 5991 for d, s in c.iteritems():
5989 5992 if s in status.removed:
5990 5993 status.removed.remove(s)
5991 5994 renamed.append(d)
5992 5995 else:
5993 5996 copied.append(d)
5994 5997 if d in status.added:
5995 5998 status.added.remove(d)
5996 5999
5997 6000 ms = mergemod.mergestate(repo)
5998 6001 unresolved = [f for f in ms if ms[f] == 'u']
5999 6002
6000 6003 subs = [s for s in ctx.substate if ctx.sub(s).dirty()]
6001 6004
6002 6005 labels = [(ui.label(_('%d modified'), 'status.modified'), status.modified),
6003 6006 (ui.label(_('%d added'), 'status.added'), status.added),
6004 6007 (ui.label(_('%d removed'), 'status.removed'), status.removed),
6005 6008 (ui.label(_('%d renamed'), 'status.copied'), renamed),
6006 6009 (ui.label(_('%d copied'), 'status.copied'), copied),
6007 6010 (ui.label(_('%d deleted'), 'status.deleted'), status.deleted),
6008 6011 (ui.label(_('%d unknown'), 'status.unknown'), status.unknown),
6009 6012 (ui.label(_('%d unresolved'), 'resolve.unresolved'), unresolved),
6010 6013 (ui.label(_('%d subrepos'), 'status.modified'), subs)]
6011 6014 t = []
6012 6015 for l, s in labels:
6013 6016 if s:
6014 6017 t.append(l % len(s))
6015 6018
6016 6019 t = ', '.join(t)
6017 6020 cleanworkdir = False
6018 6021
6019 6022 if repo.vfs.exists('updatestate'):
6020 6023 t += _(' (interrupted update)')
6021 6024 elif len(parents) > 1:
6022 6025 t += _(' (merge)')
6023 6026 elif branch != parents[0].branch():
6024 6027 t += _(' (new branch)')
6025 6028 elif (parents[0].closesbranch() and
6026 6029 pnode in repo.branchheads(branch, closed=True)):
6027 6030 t += _(' (head closed)')
6028 6031 elif not (status.modified or status.added or status.removed or renamed or
6029 6032 copied or subs):
6030 6033 t += _(' (clean)')
6031 6034 cleanworkdir = True
6032 6035 elif pnode not in bheads:
6033 6036 t += _(' (new branch head)')
6034 6037
6035 6038 if parents:
6036 6039 pendingphase = max(p.phase() for p in parents)
6037 6040 else:
6038 6041 pendingphase = phases.public
6039 6042
6040 6043 if pendingphase > phases.newcommitphase(ui):
6041 6044 t += ' (%s)' % phases.phasenames[pendingphase]
6042 6045
6043 6046 if cleanworkdir:
6044 6047 # i18n: column positioning for "hg summary"
6045 6048 ui.status(_('commit: %s\n') % t.strip())
6046 6049 else:
6047 6050 # i18n: column positioning for "hg summary"
6048 6051 ui.write(_('commit: %s\n') % t.strip())
6049 6052
6050 6053 # all ancestors of branch heads - all ancestors of parent = new csets
6051 6054 new = len(repo.changelog.findmissing([pctx.node() for pctx in parents],
6052 6055 bheads))
6053 6056
6054 6057 if new == 0:
6055 6058 # i18n: column positioning for "hg summary"
6056 6059 ui.status(_('update: (current)\n'))
6057 6060 elif pnode not in bheads:
6058 6061 # i18n: column positioning for "hg summary"
6059 6062 ui.write(_('update: %d new changesets (update)\n') % new)
6060 6063 else:
6061 6064 # i18n: column positioning for "hg summary"
6062 6065 ui.write(_('update: %d new changesets, %d branch heads (merge)\n') %
6063 6066 (new, len(bheads)))
6064 6067
6065 6068 t = []
6066 6069 draft = len(repo.revs('draft()'))
6067 6070 if draft:
6068 6071 t.append(_('%d draft') % draft)
6069 6072 secret = len(repo.revs('secret()'))
6070 6073 if secret:
6071 6074 t.append(_('%d secret') % secret)
6072 6075
6073 6076 if draft or secret:
6074 6077 ui.status(_('phases: %s\n') % ', '.join(t))
6075 6078
6076 6079 cmdutil.summaryhooks(ui, repo)
6077 6080
6078 6081 if opts.get('remote'):
6079 6082 needsincoming, needsoutgoing = True, True
6080 6083 else:
6081 6084 needsincoming, needsoutgoing = False, False
6082 6085 for i, o in cmdutil.summaryremotehooks(ui, repo, opts, None):
6083 6086 if i:
6084 6087 needsincoming = True
6085 6088 if o:
6086 6089 needsoutgoing = True
6087 6090 if not needsincoming and not needsoutgoing:
6088 6091 return
6089 6092
6090 6093 def getincoming():
6091 6094 source, branches = hg.parseurl(ui.expandpath('default'))
6092 6095 sbranch = branches[0]
6093 6096 try:
6094 6097 other = hg.peer(repo, {}, source)
6095 6098 except error.RepoError:
6096 6099 if opts.get('remote'):
6097 6100 raise
6098 6101 return source, sbranch, None, None, None
6099 6102 revs, checkout = hg.addbranchrevs(repo, other, branches, None)
6100 6103 if revs:
6101 6104 revs = [other.lookup(rev) for rev in revs]
6102 6105 ui.debug('comparing with %s\n' % util.hidepassword(source))
6103 6106 repo.ui.pushbuffer()
6104 6107 commoninc = discovery.findcommonincoming(repo, other, heads=revs)
6105 6108 repo.ui.popbuffer()
6106 6109 return source, sbranch, other, commoninc, commoninc[1]
6107 6110
6108 6111 if needsincoming:
6109 6112 source, sbranch, sother, commoninc, incoming = getincoming()
6110 6113 else:
6111 6114 source = sbranch = sother = commoninc = incoming = None
6112 6115
6113 6116 def getoutgoing():
6114 6117 dest, branches = hg.parseurl(ui.expandpath('default-push', 'default'))
6115 6118 dbranch = branches[0]
6116 6119 revs, checkout = hg.addbranchrevs(repo, repo, branches, None)
6117 6120 if source != dest:
6118 6121 try:
6119 6122 dother = hg.peer(repo, {}, dest)
6120 6123 except error.RepoError:
6121 6124 if opts.get('remote'):
6122 6125 raise
6123 6126 return dest, dbranch, None, None
6124 6127 ui.debug('comparing with %s\n' % util.hidepassword(dest))
6125 6128 elif sother is None:
6126 6129 # there is no explicit destination peer, but source one is invalid
6127 6130 return dest, dbranch, None, None
6128 6131 else:
6129 6132 dother = sother
6130 6133 if (source != dest or (sbranch is not None and sbranch != dbranch)):
6131 6134 common = None
6132 6135 else:
6133 6136 common = commoninc
6134 6137 if revs:
6135 6138 revs = [repo.lookup(rev) for rev in revs]
6136 6139 repo.ui.pushbuffer()
6137 6140 outgoing = discovery.findcommonoutgoing(repo, dother, onlyheads=revs,
6138 6141 commoninc=common)
6139 6142 repo.ui.popbuffer()
6140 6143 return dest, dbranch, dother, outgoing
6141 6144
6142 6145 if needsoutgoing:
6143 6146 dest, dbranch, dother, outgoing = getoutgoing()
6144 6147 else:
6145 6148 dest = dbranch = dother = outgoing = None
6146 6149
6147 6150 if opts.get('remote'):
6148 6151 t = []
6149 6152 if incoming:
6150 6153 t.append(_('1 or more incoming'))
6151 6154 o = outgoing.missing
6152 6155 if o:
6153 6156 t.append(_('%d outgoing') % len(o))
6154 6157 other = dother or sother
6155 6158 if 'bookmarks' in other.listkeys('namespaces'):
6156 6159 counts = bookmarks.summary(repo, other)
6157 6160 if counts[0] > 0:
6158 6161 t.append(_('%d incoming bookmarks') % counts[0])
6159 6162 if counts[1] > 0:
6160 6163 t.append(_('%d outgoing bookmarks') % counts[1])
6161 6164
6162 6165 if t:
6163 6166 # i18n: column positioning for "hg summary"
6164 6167 ui.write(_('remote: %s\n') % (', '.join(t)))
6165 6168 else:
6166 6169 # i18n: column positioning for "hg summary"
6167 6170 ui.status(_('remote: (synced)\n'))
6168 6171
6169 6172 cmdutil.summaryremotehooks(ui, repo, opts,
6170 6173 ((source, sbranch, sother, commoninc),
6171 6174 (dest, dbranch, dother, outgoing)))
6172 6175
6173 6176 @command('tag',
6174 6177 [('f', 'force', None, _('force tag')),
6175 6178 ('l', 'local', None, _('make the tag local')),
6176 6179 ('r', 'rev', '', _('revision to tag'), _('REV')),
6177 6180 ('', 'remove', None, _('remove a tag')),
6178 6181 # -l/--local is already there, commitopts cannot be used
6179 6182 ('e', 'edit', None, _('invoke editor on commit messages')),
6180 6183 ('m', 'message', '', _('use text as commit message'), _('TEXT')),
6181 6184 ] + commitopts2,
6182 6185 _('[-f] [-l] [-m TEXT] [-d DATE] [-u USER] [-r REV] NAME...'))
6183 6186 def tag(ui, repo, name1, *names, **opts):
6184 6187 """add one or more tags for the current or given revision
6185 6188
6186 6189 Name a particular revision using <name>.
6187 6190
6188 6191 Tags are used to name particular revisions of the repository and are
6189 6192 very useful to compare different revisions, to go back to significant
6190 6193 earlier versions or to mark branch points as releases, etc. Changing
6191 6194 an existing tag is normally disallowed; use -f/--force to override.
6192 6195
6193 6196 If no revision is given, the parent of the working directory is
6194 6197 used.
6195 6198
6196 6199 To facilitate version control, distribution, and merging of tags,
6197 6200 they are stored as a file named ".hgtags" which is managed similarly
6198 6201 to other project files and can be hand-edited if necessary. This
6199 6202 also means that tagging creates a new commit. The file
6200 6203 ".hg/localtags" is used for local tags (not shared among
6201 6204 repositories).
6202 6205
6203 6206 Tag commits are usually made at the head of a branch. If the parent
6204 6207 of the working directory is not a branch head, :hg:`tag` aborts; use
6205 6208 -f/--force to force the tag commit to be based on a non-head
6206 6209 changeset.
6207 6210
6208 6211 See :hg:`help dates` for a list of formats valid for -d/--date.
6209 6212
6210 6213 Since tag names have priority over branch names during revision
6211 6214 lookup, using an existing branch name as a tag name is discouraged.
6212 6215
6213 6216 Returns 0 on success.
6214 6217 """
6215 6218 wlock = lock = None
6216 6219 try:
6217 6220 wlock = repo.wlock()
6218 6221 lock = repo.lock()
6219 6222 rev_ = "."
6220 6223 names = [t.strip() for t in (name1,) + names]
6221 6224 if len(names) != len(set(names)):
6222 6225 raise util.Abort(_('tag names must be unique'))
6223 6226 for n in names:
6224 6227 scmutil.checknewlabel(repo, n, 'tag')
6225 6228 if not n:
6226 6229 raise util.Abort(_('tag names cannot consist entirely of '
6227 6230 'whitespace'))
6228 6231 if opts.get('rev') and opts.get('remove'):
6229 6232 raise util.Abort(_("--rev and --remove are incompatible"))
6230 6233 if opts.get('rev'):
6231 6234 rev_ = opts['rev']
6232 6235 message = opts.get('message')
6233 6236 if opts.get('remove'):
6234 6237 if opts.get('local'):
6235 6238 expectedtype = 'local'
6236 6239 else:
6237 6240 expectedtype = 'global'
6238 6241
6239 6242 for n in names:
6240 6243 if not repo.tagtype(n):
6241 6244 raise util.Abort(_("tag '%s' does not exist") % n)
6242 6245 if repo.tagtype(n) != expectedtype:
6243 6246 if expectedtype == 'global':
6244 6247 raise util.Abort(_("tag '%s' is not a global tag") % n)
6245 6248 else:
6246 6249 raise util.Abort(_("tag '%s' is not a local tag") % n)
6247 6250 rev_ = nullid
6248 6251 if not message:
6249 6252 # we don't translate commit messages
6250 6253 message = 'Removed tag %s' % ', '.join(names)
6251 6254 elif not opts.get('force'):
6252 6255 for n in names:
6253 6256 if n in repo.tags():
6254 6257 raise util.Abort(_("tag '%s' already exists "
6255 6258 "(use -f to force)") % n)
6256 6259 if not opts.get('local'):
6257 6260 p1, p2 = repo.dirstate.parents()
6258 6261 if p2 != nullid:
6259 6262 raise util.Abort(_('uncommitted merge'))
6260 6263 bheads = repo.branchheads()
6261 6264 if not opts.get('force') and bheads and p1 not in bheads:
6262 6265 raise util.Abort(_('not at a branch head (use -f to force)'))
6263 6266 r = scmutil.revsingle(repo, rev_).node()
6264 6267
6265 6268 if not message:
6266 6269 # we don't translate commit messages
6267 6270 message = ('Added tag %s for changeset %s' %
6268 6271 (', '.join(names), short(r)))
6269 6272
6270 6273 date = opts.get('date')
6271 6274 if date:
6272 6275 date = util.parsedate(date)
6273 6276
6274 6277 if opts.get('remove'):
6275 6278 editform = 'tag.remove'
6276 6279 else:
6277 6280 editform = 'tag.add'
6278 6281 editor = cmdutil.getcommiteditor(editform=editform, **opts)
6279 6282
6280 6283 # don't allow tagging the null rev
6281 6284 if (not opts.get('remove') and
6282 6285 scmutil.revsingle(repo, rev_).rev() == nullrev):
6283 6286 raise util.Abort(_("cannot tag null revision"))
6284 6287
6285 6288 repo.tag(names, r, message, opts.get('local'), opts.get('user'), date,
6286 6289 editor=editor)
6287 6290 finally:
6288 6291 release(lock, wlock)
6289 6292
6290 6293 @command('tags', formatteropts, '')
6291 6294 def tags(ui, repo, **opts):
6292 6295 """list repository tags
6293 6296
6294 6297 This lists both regular and local tags. When the -v/--verbose
6295 6298 switch is used, a third column "local" is printed for local tags.
6296 6299
6297 6300 Returns 0 on success.
6298 6301 """
6299 6302
6300 6303 fm = ui.formatter('tags', opts)
6301 6304 hexfunc = fm.hexfunc
6302 6305 tagtype = ""
6303 6306
6304 6307 for t, n in reversed(repo.tagslist()):
6305 6308 hn = hexfunc(n)
6306 6309 label = 'tags.normal'
6307 6310 tagtype = ''
6308 6311 if repo.tagtype(t) == 'local':
6309 6312 label = 'tags.local'
6310 6313 tagtype = 'local'
6311 6314
6312 6315 fm.startitem()
6313 6316 fm.write('tag', '%s', t, label=label)
6314 6317 fmt = " " * (30 - encoding.colwidth(t)) + ' %5d:%s'
6315 6318 fm.condwrite(not ui.quiet, 'rev node', fmt,
6316 6319 repo.changelog.rev(n), hn, label=label)
6317 6320 fm.condwrite(ui.verbose and tagtype, 'type', ' %s',
6318 6321 tagtype, label=label)
6319 6322 fm.plain('\n')
6320 6323 fm.end()
6321 6324
6322 6325 @command('tip',
6323 6326 [('p', 'patch', None, _('show patch')),
6324 6327 ('g', 'git', None, _('use git extended diff format')),
6325 6328 ] + templateopts,
6326 6329 _('[-p] [-g]'))
6327 6330 def tip(ui, repo, **opts):
6328 6331 """show the tip revision (DEPRECATED)
6329 6332
6330 6333 The tip revision (usually just called the tip) is the changeset
6331 6334 most recently added to the repository (and therefore the most
6332 6335 recently changed head).
6333 6336
6334 6337 If you have just made a commit, that commit will be the tip. If
6335 6338 you have just pulled changes from another repository, the tip of
6336 6339 that repository becomes the current tip. The "tip" tag is special
6337 6340 and cannot be renamed or assigned to a different changeset.
6338 6341
6339 6342 This command is deprecated, please use :hg:`heads` instead.
6340 6343
6341 6344 Returns 0 on success.
6342 6345 """
6343 6346 displayer = cmdutil.show_changeset(ui, repo, opts)
6344 6347 displayer.show(repo['tip'])
6345 6348 displayer.close()
6346 6349
6347 6350 @command('unbundle',
6348 6351 [('u', 'update', None,
6349 6352 _('update to new branch head if changesets were unbundled'))],
6350 6353 _('[-u] FILE...'))
6351 6354 def unbundle(ui, repo, fname1, *fnames, **opts):
6352 6355 """apply one or more changegroup files
6353 6356
6354 6357 Apply one or more compressed changegroup files generated by the
6355 6358 bundle command.
6356 6359
6357 6360 Returns 0 on success, 1 if an update has unresolved files.
6358 6361 """
6359 6362 fnames = (fname1,) + fnames
6360 6363
6361 6364 lock = repo.lock()
6362 6365 try:
6363 6366 for fname in fnames:
6364 6367 f = hg.openpath(ui, fname)
6365 6368 gen = exchange.readbundle(ui, f, fname)
6366 6369 if isinstance(gen, bundle2.unbundle20):
6367 6370 tr = repo.transaction('unbundle')
6368 6371 try:
6369 6372 op = bundle2.processbundle(repo, gen, lambda: tr)
6370 6373 tr.close()
6371 6374 finally:
6372 6375 if tr:
6373 6376 tr.release()
6374 6377 changes = [r.get('result', 0)
6375 6378 for r in op.records['changegroup']]
6376 6379 modheads = changegroup.combineresults(changes)
6377 6380 else:
6378 6381 modheads = changegroup.addchangegroup(repo, gen, 'unbundle',
6379 6382 'bundle:' + fname)
6380 6383 finally:
6381 6384 lock.release()
6382 6385
6383 6386 return postincoming(ui, repo, modheads, opts.get('update'), None)
6384 6387
6385 6388 @command('^update|up|checkout|co',
6386 6389 [('C', 'clean', None, _('discard uncommitted changes (no backup)')),
6387 6390 ('c', 'check', None,
6388 6391 _('update across branches if no uncommitted changes')),
6389 6392 ('d', 'date', '', _('tipmost revision matching date'), _('DATE')),
6390 6393 ('r', 'rev', '', _('revision'), _('REV'))
6391 6394 ] + mergetoolopts,
6392 6395 _('[-c] [-C] [-d DATE] [[-r] REV]'))
6393 6396 def update(ui, repo, node=None, rev=None, clean=False, date=None, check=False,
6394 6397 tool=None):
6395 6398 """update working directory (or switch revisions)
6396 6399
6397 6400 Update the repository's working directory to the specified
6398 6401 changeset. If no changeset is specified, update to the tip of the
6399 6402 current named branch and move the active bookmark (see :hg:`help
6400 6403 bookmarks`).
6401 6404
6402 6405 Update sets the working directory's parent revision to the specified
6403 6406 changeset (see :hg:`help parents`).
6404 6407
6405 6408 If the changeset is not a descendant or ancestor of the working
6406 6409 directory's parent, the update is aborted. With the -c/--check
6407 6410 option, the working directory is checked for uncommitted changes; if
6408 6411 none are found, the working directory is updated to the specified
6409 6412 changeset.
6410 6413
6411 6414 .. container:: verbose
6412 6415
6413 6416 The following rules apply when the working directory contains
6414 6417 uncommitted changes:
6415 6418
6416 6419 1. If neither -c/--check nor -C/--clean is specified, and if
6417 6420 the requested changeset is an ancestor or descendant of
6418 6421 the working directory's parent, the uncommitted changes
6419 6422 are merged into the requested changeset and the merged
6420 6423 result is left uncommitted. If the requested changeset is
6421 6424 not an ancestor or descendant (that is, it is on another
6422 6425 branch), the update is aborted and the uncommitted changes
6423 6426 are preserved.
6424 6427
6425 6428 2. With the -c/--check option, the update is aborted and the
6426 6429 uncommitted changes are preserved.
6427 6430
6428 6431 3. With the -C/--clean option, uncommitted changes are discarded and
6429 6432 the working directory is updated to the requested changeset.
6430 6433
6431 6434 To cancel an uncommitted merge (and lose your changes), use
6432 6435 :hg:`update --clean .`.
6433 6436
6434 6437 Use null as the changeset to remove the working directory (like
6435 6438 :hg:`clone -U`).
6436 6439
6437 6440 If you want to revert just one file to an older revision, use
6438 6441 :hg:`revert [-r REV] NAME`.
6439 6442
6440 6443 See :hg:`help dates` for a list of formats valid for -d/--date.
6441 6444
6442 6445 Returns 0 on success, 1 if there are unresolved files.
6443 6446 """
6444 6447 if rev and node:
6445 6448 raise util.Abort(_("please specify just one revision"))
6446 6449
6447 6450 if rev is None or rev == '':
6448 6451 rev = node
6449 6452
6450 6453 cmdutil.clearunfinished(repo)
6451 6454
6452 6455 # with no argument, we also move the active bookmark, if any
6453 6456 rev, movemarkfrom = bookmarks.calculateupdate(ui, repo, rev)
6454 6457
6455 6458 # if we defined a bookmark, we have to remember the original bookmark name
6456 6459 brev = rev
6457 6460 rev = scmutil.revsingle(repo, rev, rev).rev()
6458 6461
6459 6462 if check and clean:
6460 6463 raise util.Abort(_("cannot specify both -c/--check and -C/--clean"))
6461 6464
6462 6465 if date:
6463 6466 if rev is not None:
6464 6467 raise util.Abort(_("you can't specify a revision and a date"))
6465 6468 rev = cmdutil.finddate(ui, repo, date)
6466 6469
6467 6470 if check:
6468 6471 cmdutil.bailifchanged(repo, merge=False)
6469 6472 if rev is None:
6470 6473 rev = repo[repo[None].branch()].rev()
6471 6474
6472 6475 repo.ui.setconfig('ui', 'forcemerge', tool, 'update')
6473 6476
6474 6477 if clean:
6475 6478 ret = hg.clean(repo, rev)
6476 6479 else:
6477 6480 ret = hg.update(repo, rev)
6478 6481
6479 6482 if not ret and movemarkfrom:
6480 6483 if bookmarks.update(repo, [movemarkfrom], repo['.'].node()):
6481 6484 ui.status(_("updating bookmark %s\n") % repo._activebookmark)
6482 6485 elif brev in repo._bookmarks:
6483 6486 bookmarks.activate(repo, brev)
6484 6487 ui.status(_("(activating bookmark %s)\n") % brev)
6485 6488 elif brev:
6486 6489 if repo._activebookmark:
6487 6490 ui.status(_("(leaving bookmark %s)\n") %
6488 6491 repo._activebookmark)
6489 6492 bookmarks.deactivate(repo)
6490 6493
6491 6494 return ret
6492 6495
6493 6496 @command('verify', [])
6494 6497 def verify(ui, repo):
6495 6498 """verify the integrity of the repository
6496 6499
6497 6500 Verify the integrity of the current repository.
6498 6501
6499 6502 This will perform an extensive check of the repository's
6500 6503 integrity, validating the hashes and checksums of each entry in
6501 6504 the changelog, manifest, and tracked files, as well as the
6502 6505 integrity of their crosslinks and indices.
6503 6506
6504 6507 Please see http://mercurial.selenic.com/wiki/RepositoryCorruption
6505 6508 for more information about recovery from corruption of the
6506 6509 repository.
6507 6510
6508 6511 Returns 0 on success, 1 if errors are encountered.
6509 6512 """
6510 6513 return hg.verify(repo)
6511 6514
6512 6515 @command('version', [], norepo=True)
6513 6516 def version_(ui):
6514 6517 """output version and copyright information"""
6515 6518 ui.write(_("Mercurial Distributed SCM (version %s)\n")
6516 6519 % util.version())
6517 6520 ui.status(_(
6518 6521 "(see http://mercurial.selenic.com for more information)\n"
6519 6522 "\nCopyright (C) 2005-2015 Matt Mackall and others\n"
6520 6523 "This is free software; see the source for copying conditions. "
6521 6524 "There is NO\nwarranty; "
6522 6525 "not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n"
6523 6526 ))
6524 6527
6525 6528 ui.note(_("\nEnabled extensions:\n\n"))
6526 6529 if ui.verbose:
6527 6530 # format names and versions into columns
6528 6531 names = []
6529 6532 vers = []
6530 6533 for name, module in extensions.extensions():
6531 6534 names.append(name)
6532 6535 vers.append(extensions.moduleversion(module))
6533 6536 if names:
6534 6537 maxnamelen = max(len(n) for n in names)
6535 6538 for i, name in enumerate(names):
6536 6539 ui.write(" %-*s %s\n" % (maxnamelen, name, vers[i]))
@@ -1,117 +1,65
1 1 $ echo "[extensions]" >> $HGRCPATH
2 2 $ echo "mq=" >> $HGRCPATH
3 3
4 4 $ hg init
5 5
6 6 $ echo qqq>qqq.txt
7 7
8 8 rollback dry run without rollback information
9 9
10 10 $ hg rollback
11 11 no rollback information available
12 12 [1]
13 13
14 14 add file
15 15
16 16 $ hg add
17 17 adding qqq.txt
18 18
19 19 commit first revision
20 20
21 21 $ hg ci -m 1
22 22
23 23 set bookmark
24 24
25 25 $ hg book test
26 26
27 27 $ echo www>>qqq.txt
28 28
29 29 commit second revision
30 30
31 31 $ hg ci -m 2
32 32
33 33 set bookmark
34 34
35 35 $ hg book test2
36 36
37 37 update to -2 (deactivates the active bookmark)
38 38
39 39 $ hg update -r -2
40 40 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
41 41 (leaving bookmark test2)
42 42
43 43 $ echo eee>>qqq.txt
44 44
45 45 commit new head
46 46
47 47 $ hg ci -m 3
48 48 created new head
49 49
50 50 bookmarks updated?
51 51
52 52 $ hg book
53 53 test 1:25e1ee7a0081
54 54 test2 1:25e1ee7a0081
55 55
56 56 strip to revision 1
57 57
58 58 $ hg strip 1
59 59 saved backup bundle to $TESTTMP/.hg/strip-backup/*-backup.hg (glob)
60 60
61 61 list bookmarks
62 62
63 63 $ hg book
64 64 test 0:5c9ad3787638
65 65 test2 0:5c9ad3787638
66
67 immediate rollback and reentrancy issue
68
69 $ echo "mq=!" >> $HGRCPATH
70 $ hg init repo
71 $ cd repo
72 $ echo a > a
73 $ hg ci -Am adda
74 adding a
75 $ echo b > b
76 $ hg ci -Am addb
77 adding b
78 $ hg bookmarks markb
79 $ hg rollback
80 repository tip rolled back to revision 0 (undo commit)
81 working directory now based on revision 0
82
83 are you there?
84
85 $ hg bookmarks
86 no bookmarks set
87
88 can we commit? (issue2692)
89
90 $ echo c > c
91 $ hg ci -Am rockon
92 adding c
93
94 can you be added again?
95
96 $ hg bookmarks markb
97 $ hg bookmarks
98 * markb 1:fdb34407462c
99
100 rollback dry run with rollback information
101
102 $ hg rollback -n
103 repository tip rolled back to revision 0 (undo commit)
104 $ hg bookmarks
105 * markb 1:fdb34407462c
106
107 rollback dry run with rollback information and no commit undo
108
109 $ rm .hg/store/undo
110 $ hg rollback -n
111 no rollback information available
112 [1]
113 $ hg bookmarks
114 * markb 1:fdb34407462c
115
116 $ cd ..
117
@@ -1,696 +1,712
1 1 $ hg init
2 2
3 3 no bookmarks
4 4
5 5 $ hg bookmarks
6 6 no bookmarks set
7 7
8 8 $ hg bookmarks -Tjson
9 9 [
10 10 ]
11 11
12 12 bookmark rev -1
13 13
14 14 $ hg bookmark X
15 15
16 16 list bookmarks
17 17
18 18 $ hg bookmarks
19 19 * X -1:000000000000
20 20
21 21 list bookmarks with color
22 22
23 23 $ hg --config extensions.color= --config color.mode=ansi \
24 24 > bookmarks --color=always
25 25 \x1b[0;32m * \x1b[0m\x1b[0;32mX\x1b[0m\x1b[0;32m -1:000000000000\x1b[0m (esc)
26 26
27 27 $ echo a > a
28 28 $ hg add a
29 29 $ hg commit -m 0
30 30
31 31 bookmark X moved to rev 0
32 32
33 33 $ hg bookmarks
34 34 * X 0:f7b1eb17ad24
35 35
36 36 look up bookmark
37 37
38 38 $ hg log -r X
39 39 changeset: 0:f7b1eb17ad24
40 40 bookmark: X
41 41 tag: tip
42 42 user: test
43 43 date: Thu Jan 01 00:00:00 1970 +0000
44 44 summary: 0
45 45
46 46
47 47 second bookmark for rev 0, command should work even with ui.strict on
48 48
49 49 $ hg --config ui.strict=1 bookmark X2
50 50
51 51 bookmark rev -1 again
52 52
53 53 $ hg bookmark -r null Y
54 54
55 55 list bookmarks
56 56
57 57 $ hg bookmarks
58 58 X 0:f7b1eb17ad24
59 59 * X2 0:f7b1eb17ad24
60 60 Y -1:000000000000
61 61
62 62 $ echo b > b
63 63 $ hg add b
64 64 $ hg commit -m 1
65 65
66 66 $ hg bookmarks -Tjson
67 67 [
68 68 {
69 69 "active": false,
70 70 "bookmark": "X",
71 71 "node": "f7b1eb17ad24730a1651fccd46c43826d1bbc2ac",
72 72 "rev": 0
73 73 },
74 74 {
75 75 "active": true,
76 76 "bookmark": "X2",
77 77 "node": "925d80f479bb026b0fb3deb27503780b13f74123",
78 78 "rev": 1
79 79 },
80 80 {
81 81 "active": false,
82 82 "bookmark": "Y",
83 83 "node": "0000000000000000000000000000000000000000",
84 84 "rev": -1
85 85 }
86 86 ]
87 87
88 88 bookmarks revset
89 89
90 90 $ hg log -r 'bookmark()'
91 91 changeset: 0:f7b1eb17ad24
92 92 bookmark: X
93 93 user: test
94 94 date: Thu Jan 01 00:00:00 1970 +0000
95 95 summary: 0
96 96
97 97 changeset: 1:925d80f479bb
98 98 bookmark: X2
99 99 tag: tip
100 100 user: test
101 101 date: Thu Jan 01 00:00:00 1970 +0000
102 102 summary: 1
103 103
104 104 $ hg log -r 'bookmark(Y)'
105 105 $ hg log -r 'bookmark(X2)'
106 106 changeset: 1:925d80f479bb
107 107 bookmark: X2
108 108 tag: tip
109 109 user: test
110 110 date: Thu Jan 01 00:00:00 1970 +0000
111 111 summary: 1
112 112
113 113 $ hg log -r 'bookmark("re:X")'
114 114 changeset: 0:f7b1eb17ad24
115 115 bookmark: X
116 116 user: test
117 117 date: Thu Jan 01 00:00:00 1970 +0000
118 118 summary: 0
119 119
120 120 changeset: 1:925d80f479bb
121 121 bookmark: X2
122 122 tag: tip
123 123 user: test
124 124 date: Thu Jan 01 00:00:00 1970 +0000
125 125 summary: 1
126 126
127 127 $ hg log -r 'bookmark("literal:X")'
128 128 changeset: 0:f7b1eb17ad24
129 129 bookmark: X
130 130 user: test
131 131 date: Thu Jan 01 00:00:00 1970 +0000
132 132 summary: 0
133 133
134 134
135 135 $ hg log -r 'bookmark(unknown)'
136 136 abort: bookmark 'unknown' does not exist!
137 137 [255]
138 138 $ hg log -r 'bookmark("re:unknown")'
139 139 abort: no bookmarks exist that match 'unknown'!
140 140 [255]
141 141 $ hg log -r 'present(bookmark("literal:unknown"))'
142 142 $ hg log -r 'present(bookmark("re:unknown"))'
143 143
144 144 $ hg help revsets | grep 'bookmark('
145 145 "bookmark([name])"
146 146
147 147 bookmarks X and X2 moved to rev 1, Y at rev -1
148 148
149 149 $ hg bookmarks
150 150 X 0:f7b1eb17ad24
151 151 * X2 1:925d80f479bb
152 152 Y -1:000000000000
153 153
154 154 bookmark rev 0 again
155 155
156 156 $ hg bookmark -r 0 Z
157 157
158 158 $ hg update X
159 159 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
160 160 (activating bookmark X)
161 161 $ echo c > c
162 162 $ hg add c
163 163 $ hg commit -m 2
164 164 created new head
165 165
166 166 bookmarks X moved to rev 2, Y at rev -1, Z at rev 0
167 167
168 168 $ hg bookmarks
169 169 * X 2:db815d6d32e6
170 170 X2 1:925d80f479bb
171 171 Y -1:000000000000
172 172 Z 0:f7b1eb17ad24
173 173
174 174 rename nonexistent bookmark
175 175
176 176 $ hg bookmark -m A B
177 177 abort: bookmark 'A' does not exist
178 178 [255]
179 179
180 180 rename to existent bookmark
181 181
182 182 $ hg bookmark -m X Y
183 183 abort: bookmark 'Y' already exists (use -f to force)
184 184 [255]
185 185
186 186 force rename to existent bookmark
187 187
188 188 $ hg bookmark -f -m X Y
189 189
190 190 list bookmarks
191 191
192 192 $ hg bookmark
193 193 X2 1:925d80f479bb
194 194 * Y 2:db815d6d32e6
195 195 Z 0:f7b1eb17ad24
196 196
197 197 bookmarks from a revset
198 198 $ hg bookmark -r '.^1' REVSET
199 199 $ hg bookmark -r ':tip' TIP
200 200 $ hg up -q TIP
201 201 $ hg bookmarks
202 202 REVSET 0:f7b1eb17ad24
203 203 * TIP 2:db815d6d32e6
204 204 X2 1:925d80f479bb
205 205 Y 2:db815d6d32e6
206 206 Z 0:f7b1eb17ad24
207 207
208 208 $ hg bookmark -d REVSET
209 209 $ hg bookmark -d TIP
210 210
211 211 rename without new name or multiple names
212 212
213 213 $ hg bookmark -m Y
214 214 abort: new bookmark name required
215 215 [255]
216 216 $ hg bookmark -m Y Y2 Y3
217 217 abort: only one new bookmark name allowed
218 218 [255]
219 219
220 220 delete without name
221 221
222 222 $ hg bookmark -d
223 223 abort: bookmark name required
224 224 [255]
225 225
226 226 delete nonexistent bookmark
227 227
228 228 $ hg bookmark -d A
229 229 abort: bookmark 'A' does not exist
230 230 [255]
231 231
232 232 bookmark name with spaces should be stripped
233 233
234 234 $ hg bookmark ' x y '
235 235
236 236 list bookmarks
237 237
238 238 $ hg bookmarks
239 239 X2 1:925d80f479bb
240 240 Y 2:db815d6d32e6
241 241 Z 0:f7b1eb17ad24
242 242 * x y 2:db815d6d32e6
243 243
244 244 look up stripped bookmark name
245 245
246 246 $ hg log -r '"x y"'
247 247 changeset: 2:db815d6d32e6
248 248 bookmark: Y
249 249 bookmark: x y
250 250 tag: tip
251 251 parent: 0:f7b1eb17ad24
252 252 user: test
253 253 date: Thu Jan 01 00:00:00 1970 +0000
254 254 summary: 2
255 255
256 256
257 257 reject bookmark name with newline
258 258
259 259 $ hg bookmark '
260 260 > '
261 261 abort: bookmark names cannot consist entirely of whitespace
262 262 [255]
263 263
264 264 $ hg bookmark -m Z '
265 265 > '
266 266 abort: bookmark names cannot consist entirely of whitespace
267 267 [255]
268 268
269 269 bookmark with reserved name
270 270
271 271 $ hg bookmark tip
272 272 abort: the name 'tip' is reserved
273 273 [255]
274 274
275 275 $ hg bookmark .
276 276 abort: the name '.' is reserved
277 277 [255]
278 278
279 279 $ hg bookmark null
280 280 abort: the name 'null' is reserved
281 281 [255]
282 282
283 283
284 284 bookmark with existing name
285 285
286 286 $ hg bookmark X2
287 287 abort: bookmark 'X2' already exists (use -f to force)
288 288 [255]
289 289
290 290 $ hg bookmark -m Y Z
291 291 abort: bookmark 'Z' already exists (use -f to force)
292 292 [255]
293 293
294 294 bookmark with name of branch
295 295
296 296 $ hg bookmark default
297 297 abort: a bookmark cannot have the name of an existing branch
298 298 [255]
299 299
300 300 $ hg bookmark -m Y default
301 301 abort: a bookmark cannot have the name of an existing branch
302 302 [255]
303 303
304 304 bookmark with integer name
305 305
306 306 $ hg bookmark 10
307 307 abort: cannot use an integer as a name
308 308 [255]
309 309
310 310 incompatible options
311 311
312 312 $ hg bookmark -m Y -d Z
313 313 abort: --delete and --rename are incompatible
314 314 [255]
315 315
316 316 $ hg bookmark -r 1 -d Z
317 317 abort: --rev is incompatible with --delete
318 318 [255]
319 319
320 320 $ hg bookmark -r 1 -m Z Y
321 321 abort: --rev is incompatible with --rename
322 322 [255]
323 323
324 324 force bookmark with existing name
325 325
326 326 $ hg bookmark -f X2
327 327
328 328 force bookmark back to where it was, should deactivate it
329 329
330 330 $ hg bookmark -fr1 X2
331 331 $ hg bookmarks
332 332 X2 1:925d80f479bb
333 333 Y 2:db815d6d32e6
334 334 Z 0:f7b1eb17ad24
335 335 x y 2:db815d6d32e6
336 336
337 337 forward bookmark to descendant without --force
338 338
339 339 $ hg bookmark Z
340 340 moving bookmark 'Z' forward from f7b1eb17ad24
341 341
342 342 list bookmarks
343 343
344 344 $ hg bookmark
345 345 X2 1:925d80f479bb
346 346 Y 2:db815d6d32e6
347 347 * Z 2:db815d6d32e6
348 348 x y 2:db815d6d32e6
349 349
350 350 revision but no bookmark name
351 351
352 352 $ hg bookmark -r .
353 353 abort: bookmark name required
354 354 [255]
355 355
356 356 bookmark name with whitespace only
357 357
358 358 $ hg bookmark ' '
359 359 abort: bookmark names cannot consist entirely of whitespace
360 360 [255]
361 361
362 362 $ hg bookmark -m Y ' '
363 363 abort: bookmark names cannot consist entirely of whitespace
364 364 [255]
365 365
366 366 invalid bookmark
367 367
368 368 $ hg bookmark 'foo:bar'
369 369 abort: ':' cannot be used in a name
370 370 [255]
371 371
372 372 $ hg bookmark 'foo
373 373 > bar'
374 374 abort: '\n' cannot be used in a name
375 375 [255]
376 376
377 377 the bookmark extension should be ignored now that it is part of core
378 378
379 379 $ echo "[extensions]" >> $HGRCPATH
380 380 $ echo "bookmarks=" >> $HGRCPATH
381 381 $ hg bookmarks
382 382 X2 1:925d80f479bb
383 383 Y 2:db815d6d32e6
384 384 * Z 2:db815d6d32e6
385 385 x y 2:db815d6d32e6
386 386
387 387 test summary
388 388
389 389 $ hg summary
390 390 parent: 2:db815d6d32e6 tip
391 391 2
392 392 branch: default
393 393 bookmarks: *Z Y x y
394 394 commit: (clean)
395 395 update: 1 new changesets, 2 branch heads (merge)
396 396 phases: 3 draft
397 397
398 398 test id
399 399
400 400 $ hg id
401 401 db815d6d32e6 tip Y/Z/x y
402 402
403 403 test rollback
404 404
405 405 $ echo foo > f1
406 $ hg bookmark tmp-rollback
406 407 $ hg ci -Amr
407 408 adding f1
408 $ hg bookmark -f Y -r 1
409 $ hg bookmark -f Z -r 1
409 $ hg bookmarks
410 X2 1:925d80f479bb
411 Y 2:db815d6d32e6
412 Z 2:db815d6d32e6
413 * tmp-rollback 3:2bf5cfec5864
414 x y 2:db815d6d32e6
410 415 $ hg rollback
411 416 repository tip rolled back to revision 2 (undo commit)
412 417 working directory now based on revision 2
413 418 $ hg bookmarks
414 419 X2 1:925d80f479bb
415 420 Y 2:db815d6d32e6
416 421 Z 2:db815d6d32e6
422 * tmp-rollback 2:db815d6d32e6
417 423 x y 2:db815d6d32e6
424 $ hg bookmark -f Z -r 1
425 $ hg rollback
426 repository tip rolled back to revision 2 (undo bookmark)
427 $ hg bookmarks
428 X2 1:925d80f479bb
429 Y 2:db815d6d32e6
430 Z 2:db815d6d32e6
431 * tmp-rollback 2:db815d6d32e6
432 x y 2:db815d6d32e6
433 $ hg bookmark -d tmp-rollback
418 434
419 435 activate bookmark on working dir parent without --force
420 436
421 437 $ hg bookmark --inactive Z
422 438 $ hg bookmark Z
423 439
424 440 test clone
425 441
426 442 $ hg bookmark -r 2 -i @
427 443 $ hg bookmark -r 2 -i a@
428 444 $ hg bookmarks
429 445 @ 2:db815d6d32e6
430 446 X2 1:925d80f479bb
431 447 Y 2:db815d6d32e6
432 448 * Z 2:db815d6d32e6
433 449 a@ 2:db815d6d32e6
434 450 x y 2:db815d6d32e6
435 451 $ hg clone . cloned-bookmarks
436 452 updating to bookmark @
437 453 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
438 454 $ hg -R cloned-bookmarks bookmarks
439 455 * @ 2:db815d6d32e6
440 456 X2 1:925d80f479bb
441 457 Y 2:db815d6d32e6
442 458 Z 2:db815d6d32e6
443 459 a@ 2:db815d6d32e6
444 460 x y 2:db815d6d32e6
445 461
446 462 test clone with pull protocol
447 463
448 464 $ hg clone --pull . cloned-bookmarks-pull
449 465 requesting all changes
450 466 adding changesets
451 467 adding manifests
452 468 adding file changes
453 469 added 3 changesets with 3 changes to 3 files (+1 heads)
454 470 updating to bookmark @
455 471 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
456 472 $ hg -R cloned-bookmarks-pull bookmarks
457 473 * @ 2:db815d6d32e6
458 474 X2 1:925d80f479bb
459 475 Y 2:db815d6d32e6
460 476 Z 2:db815d6d32e6
461 477 a@ 2:db815d6d32e6
462 478 x y 2:db815d6d32e6
463 479
464 480 delete multiple bookmarks at once
465 481
466 482 $ hg bookmark -d @ a@
467 483
468 484 test clone with a bookmark named "default" (issue3677)
469 485
470 486 $ hg bookmark -r 1 -f -i default
471 487 $ hg clone . cloned-bookmark-default
472 488 updating to branch default
473 489 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
474 490 $ hg -R cloned-bookmark-default bookmarks
475 491 X2 1:925d80f479bb
476 492 Y 2:db815d6d32e6
477 493 Z 2:db815d6d32e6
478 494 default 1:925d80f479bb
479 495 x y 2:db815d6d32e6
480 496 $ hg -R cloned-bookmark-default parents -q
481 497 2:db815d6d32e6
482 498 $ hg bookmark -d default
483 499
484 500 test clone with a specific revision
485 501
486 502 $ hg clone -r 925d80 . cloned-bookmarks-rev
487 503 adding changesets
488 504 adding manifests
489 505 adding file changes
490 506 added 2 changesets with 2 changes to 2 files
491 507 updating to branch default
492 508 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
493 509 $ hg -R cloned-bookmarks-rev bookmarks
494 510 X2 1:925d80f479bb
495 511
496 512 test clone with update to a bookmark
497 513
498 514 $ hg clone -u Z . cloned-bookmarks-update
499 515 updating to branch default
500 516 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
501 517 $ hg -R cloned-bookmarks-update bookmarks
502 518 X2 1:925d80f479bb
503 519 Y 2:db815d6d32e6
504 520 * Z 2:db815d6d32e6
505 521 x y 2:db815d6d32e6
506 522
507 523 create bundle with two heads
508 524
509 525 $ hg clone . tobundle
510 526 updating to branch default
511 527 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
512 528 $ echo x > tobundle/x
513 529 $ hg -R tobundle add tobundle/x
514 530 $ hg -R tobundle commit -m'x'
515 531 $ hg -R tobundle update -r -2
516 532 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
517 533 $ echo y > tobundle/y
518 534 $ hg -R tobundle branch test
519 535 marked working directory as branch test
520 536 (branches are permanent and global, did you want a bookmark?)
521 537 $ hg -R tobundle add tobundle/y
522 538 $ hg -R tobundle commit -m'y'
523 539 $ hg -R tobundle bundle tobundle.hg
524 540 searching for changes
525 541 2 changesets found
526 542 $ hg unbundle tobundle.hg
527 543 adding changesets
528 544 adding manifests
529 545 adding file changes
530 546 added 2 changesets with 2 changes to 2 files (+1 heads)
531 547 (run 'hg heads' to see heads, 'hg merge' to merge)
532 548
533 549 update to active bookmark if it's not the parent
534 550
535 551 $ hg summary
536 552 parent: 2:db815d6d32e6
537 553 2
538 554 branch: default
539 555 bookmarks: *Z Y x y
540 556 commit: 1 added, 1 unknown (new branch head)
541 557 update: 2 new changesets (update)
542 558 phases: 5 draft
543 559 $ hg update
544 560 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
545 561 updating bookmark Z
546 562 $ hg bookmarks
547 563 X2 1:925d80f479bb
548 564 Y 2:db815d6d32e6
549 565 * Z 3:125c9a1d6df6
550 566 x y 2:db815d6d32e6
551 567
552 568 pull --update works the same as pull && update
553 569
554 570 $ hg bookmark -r3 Y
555 571 moving bookmark 'Y' forward from db815d6d32e6
556 572 $ hg -R cloned-bookmarks-update update Y
557 573 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
558 574 (activating bookmark Y)
559 575 $ hg -R cloned-bookmarks-update pull --update .
560 576 pulling from .
561 577 searching for changes
562 578 adding changesets
563 579 adding manifests
564 580 adding file changes
565 581 added 2 changesets with 2 changes to 2 files (+1 heads)
566 582 updating bookmark Y
567 583 updating bookmark Z
568 584 updating to active bookmark Y
569 585 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
570 586
571 587 test wrongly formated bookmark
572 588
573 589 $ echo '' >> .hg/bookmarks
574 590 $ hg bookmarks
575 591 X2 1:925d80f479bb
576 592 Y 3:125c9a1d6df6
577 593 * Z 3:125c9a1d6df6
578 594 x y 2:db815d6d32e6
579 595 $ echo "Ican'thasformatedlines" >> .hg/bookmarks
580 596 $ hg bookmarks
581 597 malformed line in .hg/bookmarks: "Ican'thasformatedlines"
582 598 X2 1:925d80f479bb
583 599 Y 3:125c9a1d6df6
584 600 * Z 3:125c9a1d6df6
585 601 x y 2:db815d6d32e6
586 602
587 603 test missing revisions
588 604
589 605 $ echo "925d80f479bc z" > .hg/bookmarks
590 606 $ hg book
591 607 no bookmarks set
592 608
593 609 test stripping a non-checked-out but bookmarked revision
594 610
595 611 $ hg log --graph
596 612 o changeset: 4:9ba5f110a0b3
597 613 | branch: test
598 614 | tag: tip
599 615 | parent: 2:db815d6d32e6
600 616 | user: test
601 617 | date: Thu Jan 01 00:00:00 1970 +0000
602 618 | summary: y
603 619 |
604 620 | @ changeset: 3:125c9a1d6df6
605 621 |/ user: test
606 622 | date: Thu Jan 01 00:00:00 1970 +0000
607 623 | summary: x
608 624 |
609 625 o changeset: 2:db815d6d32e6
610 626 | parent: 0:f7b1eb17ad24
611 627 | user: test
612 628 | date: Thu Jan 01 00:00:00 1970 +0000
613 629 | summary: 2
614 630 |
615 631 | o changeset: 1:925d80f479bb
616 632 |/ user: test
617 633 | date: Thu Jan 01 00:00:00 1970 +0000
618 634 | summary: 1
619 635 |
620 636 o changeset: 0:f7b1eb17ad24
621 637 user: test
622 638 date: Thu Jan 01 00:00:00 1970 +0000
623 639 summary: 0
624 640
625 641 $ hg book should-end-on-two
626 642 $ hg co --clean 4
627 643 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
628 644 (leaving bookmark should-end-on-two)
629 645 $ hg book four
630 646 $ hg --config extensions.mq= strip 3
631 647 saved backup bundle to * (glob)
632 648 should-end-on-two should end up pointing to revision 2, as that's the
633 649 tipmost surviving ancestor of the stripped revision.
634 650 $ hg log --graph
635 651 @ changeset: 3:9ba5f110a0b3
636 652 | branch: test
637 653 | bookmark: four
638 654 | tag: tip
639 655 | user: test
640 656 | date: Thu Jan 01 00:00:00 1970 +0000
641 657 | summary: y
642 658 |
643 659 o changeset: 2:db815d6d32e6
644 660 | bookmark: should-end-on-two
645 661 | parent: 0:f7b1eb17ad24
646 662 | user: test
647 663 | date: Thu Jan 01 00:00:00 1970 +0000
648 664 | summary: 2
649 665 |
650 666 | o changeset: 1:925d80f479bb
651 667 |/ user: test
652 668 | date: Thu Jan 01 00:00:00 1970 +0000
653 669 | summary: 1
654 670 |
655 671 o changeset: 0:f7b1eb17ad24
656 672 user: test
657 673 date: Thu Jan 01 00:00:00 1970 +0000
658 674 summary: 0
659 675
660 676
661 677 test clearing divergent bookmarks of linear ancestors
662 678
663 679 $ hg bookmark Z -r 0
664 680 $ hg bookmark Z@1 -r 1
665 681 $ hg bookmark Z@2 -r 2
666 682 $ hg bookmark Z@3 -r 3
667 683 $ hg book
668 684 Z 0:f7b1eb17ad24
669 685 Z@1 1:925d80f479bb
670 686 Z@2 2:db815d6d32e6
671 687 Z@3 3:9ba5f110a0b3
672 688 * four 3:9ba5f110a0b3
673 689 should-end-on-two 2:db815d6d32e6
674 690 $ hg bookmark Z
675 691 moving bookmark 'Z' forward from f7b1eb17ad24
676 692 $ hg book
677 693 * Z 3:9ba5f110a0b3
678 694 Z@1 1:925d80f479bb
679 695 four 3:9ba5f110a0b3
680 696 should-end-on-two 2:db815d6d32e6
681 697
682 698 test clearing only a single divergent bookmark across branches
683 699
684 700 $ hg book foo -r 1
685 701 $ hg book foo@1 -r 0
686 702 $ hg book foo@2 -r 2
687 703 $ hg book foo@3 -r 3
688 704 $ hg book foo -r foo@3
689 705 $ hg book
690 706 * Z 3:9ba5f110a0b3
691 707 Z@1 1:925d80f479bb
692 708 foo 3:9ba5f110a0b3
693 709 foo@1 0:f7b1eb17ad24
694 710 foo@2 2:db815d6d32e6
695 711 four 3:9ba5f110a0b3
696 712 should-end-on-two 2:db815d6d32e6
@@ -1,869 +1,899
1 1 Test exchange of common information using bundle2
2 2
3 3
4 4 $ getmainid() {
5 5 > hg -R main log --template '{node}\n' --rev "$1"
6 6 > }
7 7
8 8 enable obsolescence
9 9
10 10 $ cat > $TESTTMP/bundle2-pushkey-hook.sh << EOF
11 11 > echo pushkey: lock state after \"\$HG_NAMESPACE\"
12 12 > hg debuglock
13 13 > EOF
14 14
15 15 $ cat >> $HGRCPATH << EOF
16 16 > [experimental]
17 17 > evolution=createmarkers,exchange
18 18 > bundle2-exp=True
19 19 > bundle2-output-capture=True
20 20 > [ui]
21 21 > ssh=python "$TESTDIR/dummyssh"
22 22 > logtemplate={rev}:{node|short} {phase} {author} {bookmarks} {desc|firstline}
23 23 > [web]
24 24 > push_ssl = false
25 25 > allow_push = *
26 26 > [phases]
27 27 > publish=False
28 28 > [hooks]
29 29 > pretxnclose.tip = hg log -r tip -T "pre-close-tip:{node|short} {phase} {bookmarks}\n"
30 30 > txnclose.tip = hg log -r tip -T "postclose-tip:{node|short} {phase} {bookmarks}\n"
31 31 > txnclose.env = sh -c "HG_LOCAL= printenv.py txnclose"
32 32 > pushkey= sh "$TESTTMP/bundle2-pushkey-hook.sh"
33 33 > EOF
34 34
35 35 The extension requires a repo (currently unused)
36 36
37 37 $ hg init main
38 38 $ cd main
39 39 $ touch a
40 40 $ hg add a
41 41 $ hg commit -m 'a'
42 42 pre-close-tip:3903775176ed draft
43 43 postclose-tip:3903775176ed draft
44 44 txnclose hook: HG_PHASES_MOVED=1 HG_TXNID=TXN:* HG_TXNNAME=commit (glob)
45 45
46 46 $ hg unbundle $TESTDIR/bundles/rebase.hg
47 47 adding changesets
48 48 adding manifests
49 49 adding file changes
50 50 added 8 changesets with 7 changes to 7 files (+3 heads)
51 51 pre-close-tip:02de42196ebe draft
52 52 postclose-tip:02de42196ebe draft
53 53 txnclose hook: HG_NODE=cd010b8cd998f3981a5a8115f94f8da4ab506089 HG_PHASES_MOVED=1 HG_SOURCE=unbundle HG_TXNID=TXN:* HG_TXNNAME=unbundle (glob)
54 54 bundle:*/tests/bundles/rebase.hg HG_URL=bundle:*/tests/bundles/rebase.hg (glob)
55 55 (run 'hg heads' to see heads, 'hg merge' to merge)
56 56
57 57 $ cd ..
58 58
59 59 Real world exchange
60 60 =====================
61 61
62 62 Add more obsolescence information
63 63
64 64 $ hg -R main debugobsolete -d '0 0' 1111111111111111111111111111111111111111 `getmainid 9520eea781bc`
65 65 pre-close-tip:02de42196ebe draft
66 66 postclose-tip:02de42196ebe draft
67 67 txnclose hook: HG_NEW_OBSMARKERS=1 HG_TXNID=TXN:* HG_TXNNAME=debugobsolete (glob)
68 68 $ hg -R main debugobsolete -d '0 0' 2222222222222222222222222222222222222222 `getmainid 24b6387c8c8c`
69 69 pre-close-tip:02de42196ebe draft
70 70 postclose-tip:02de42196ebe draft
71 71 txnclose hook: HG_NEW_OBSMARKERS=1 HG_TXNID=TXN:* HG_TXNNAME=debugobsolete (glob)
72 72
73 73 clone --pull
74 74
75 75 $ hg -R main phase --public cd010b8cd998
76 76 pre-close-tip:02de42196ebe draft
77 77 postclose-tip:02de42196ebe draft
78 78 txnclose hook: HG_PHASES_MOVED=1 HG_TXNID=TXN:* HG_TXNNAME=phase (glob)
79 79 $ hg clone main other --pull --rev 9520eea781bc
80 80 adding changesets
81 81 adding manifests
82 82 adding file changes
83 83 added 2 changesets with 2 changes to 2 files
84 84 1 new obsolescence markers
85 85 pre-close-tip:9520eea781bc draft
86 86 postclose-tip:9520eea781bc draft
87 87 txnclose hook: HG_NEW_OBSMARKERS=1 HG_NODE=cd010b8cd998f3981a5a8115f94f8da4ab506089 HG_PHASES_MOVED=1 HG_SOURCE=pull HG_TXNID=TXN:* HG_TXNNAME=pull (glob)
88 88 file:/*/$TESTTMP/main HG_URL=file:$TESTTMP/main (glob)
89 89 updating to branch default
90 90 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
91 91 $ hg -R other log -G
92 92 @ 1:9520eea781bc draft Nicolas Dumazet <nicdumz.commits@gmail.com> E
93 93 |
94 94 o 0:cd010b8cd998 public Nicolas Dumazet <nicdumz.commits@gmail.com> A
95 95
96 96 $ hg -R other debugobsolete
97 97 1111111111111111111111111111111111111111 9520eea781bcca16c1e15acc0ba14335a0e8e5ba 0 (Thu Jan 01 00:00:00 1970 +0000) {'user': 'test'}
98 98
99 99 pull
100 100
101 101 $ hg -R main phase --public 9520eea781bc
102 102 pre-close-tip:02de42196ebe draft
103 103 postclose-tip:02de42196ebe draft
104 104 txnclose hook: HG_PHASES_MOVED=1 HG_TXNID=TXN:* HG_TXNNAME=phase (glob)
105 105 $ hg -R other pull -r 24b6387c8c8c
106 106 pulling from $TESTTMP/main (glob)
107 107 searching for changes
108 108 adding changesets
109 109 adding manifests
110 110 adding file changes
111 111 added 1 changesets with 1 changes to 1 files (+1 heads)
112 112 1 new obsolescence markers
113 113 pre-close-tip:24b6387c8c8c draft
114 114 postclose-tip:24b6387c8c8c draft
115 115 txnclose hook: HG_NEW_OBSMARKERS=1 HG_NODE=24b6387c8c8cae37178880f3fa95ded3cb1cf785 HG_PHASES_MOVED=1 HG_SOURCE=pull HG_TXNID=TXN:* HG_TXNNAME=pull (glob)
116 116 file:/*/$TESTTMP/main HG_URL=file:$TESTTMP/main (glob)
117 117 (run 'hg heads' to see heads, 'hg merge' to merge)
118 118 $ hg -R other log -G
119 119 o 2:24b6387c8c8c draft Nicolas Dumazet <nicdumz.commits@gmail.com> F
120 120 |
121 121 | @ 1:9520eea781bc draft Nicolas Dumazet <nicdumz.commits@gmail.com> E
122 122 |/
123 123 o 0:cd010b8cd998 public Nicolas Dumazet <nicdumz.commits@gmail.com> A
124 124
125 125 $ hg -R other debugobsolete
126 126 1111111111111111111111111111111111111111 9520eea781bcca16c1e15acc0ba14335a0e8e5ba 0 (Thu Jan 01 00:00:00 1970 +0000) {'user': 'test'}
127 127 2222222222222222222222222222222222222222 24b6387c8c8cae37178880f3fa95ded3cb1cf785 0 (Thu Jan 01 00:00:00 1970 +0000) {'user': 'test'}
128 128
129 129 pull empty (with phase movement)
130 130
131 131 $ hg -R main phase --public 24b6387c8c8c
132 132 pre-close-tip:02de42196ebe draft
133 133 postclose-tip:02de42196ebe draft
134 134 txnclose hook: HG_PHASES_MOVED=1 HG_TXNID=TXN:* HG_TXNNAME=phase (glob)
135 135 $ hg -R other pull -r 24b6387c8c8c
136 136 pulling from $TESTTMP/main (glob)
137 137 no changes found
138 138 pre-close-tip:24b6387c8c8c public
139 139 postclose-tip:24b6387c8c8c public
140 140 txnclose hook: HG_NEW_OBSMARKERS=0 HG_PHASES_MOVED=1 HG_SOURCE=pull HG_TXNID=TXN:* HG_TXNNAME=pull (glob)
141 141 file:/*/$TESTTMP/main HG_URL=file:$TESTTMP/main (glob)
142 142 $ hg -R other log -G
143 143 o 2:24b6387c8c8c public Nicolas Dumazet <nicdumz.commits@gmail.com> F
144 144 |
145 145 | @ 1:9520eea781bc draft Nicolas Dumazet <nicdumz.commits@gmail.com> E
146 146 |/
147 147 o 0:cd010b8cd998 public Nicolas Dumazet <nicdumz.commits@gmail.com> A
148 148
149 149 $ hg -R other debugobsolete
150 150 1111111111111111111111111111111111111111 9520eea781bcca16c1e15acc0ba14335a0e8e5ba 0 (Thu Jan 01 00:00:00 1970 +0000) {'user': 'test'}
151 151 2222222222222222222222222222222222222222 24b6387c8c8cae37178880f3fa95ded3cb1cf785 0 (Thu Jan 01 00:00:00 1970 +0000) {'user': 'test'}
152 152
153 153 pull empty
154 154
155 155 $ hg -R other pull -r 24b6387c8c8c
156 156 pulling from $TESTTMP/main (glob)
157 157 no changes found
158 158 pre-close-tip:24b6387c8c8c public
159 159 postclose-tip:24b6387c8c8c public
160 160 txnclose hook: HG_NEW_OBSMARKERS=0 HG_SOURCE=pull HG_TXNID=TXN:* HG_TXNNAME=pull (glob)
161 161 file:/*/$TESTTMP/main HG_URL=file:$TESTTMP/main (glob)
162 162 $ hg -R other log -G
163 163 o 2:24b6387c8c8c public Nicolas Dumazet <nicdumz.commits@gmail.com> F
164 164 |
165 165 | @ 1:9520eea781bc draft Nicolas Dumazet <nicdumz.commits@gmail.com> E
166 166 |/
167 167 o 0:cd010b8cd998 public Nicolas Dumazet <nicdumz.commits@gmail.com> A
168 168
169 169 $ hg -R other debugobsolete
170 170 1111111111111111111111111111111111111111 9520eea781bcca16c1e15acc0ba14335a0e8e5ba 0 (Thu Jan 01 00:00:00 1970 +0000) {'user': 'test'}
171 171 2222222222222222222222222222222222222222 24b6387c8c8cae37178880f3fa95ded3cb1cf785 0 (Thu Jan 01 00:00:00 1970 +0000) {'user': 'test'}
172 172
173 173 add extra data to test their exchange during push
174 174
175 175 $ hg -R main bookmark --rev eea13746799a book_eea1
176 pre-close-tip:02de42196ebe draft
177 postclose-tip:02de42196ebe draft
178 txnclose hook: HG_BOOKMARK_MOVED=1 HG_TXNID=TXN:* HG_TXNNAME=bookmark (glob)
176 179 $ hg -R main debugobsolete -d '0 0' 3333333333333333333333333333333333333333 `getmainid eea13746799a`
177 180 pre-close-tip:02de42196ebe draft
178 181 postclose-tip:02de42196ebe draft
179 182 txnclose hook: HG_NEW_OBSMARKERS=1 HG_TXNID=TXN:* HG_TXNNAME=debugobsolete (glob)
180 183 $ hg -R main bookmark --rev 02de42196ebe book_02de
184 pre-close-tip:02de42196ebe draft book_02de
185 postclose-tip:02de42196ebe draft book_02de
186 txnclose hook: HG_BOOKMARK_MOVED=1 HG_TXNID=TXN:* HG_TXNNAME=bookmark (glob)
181 187 $ hg -R main debugobsolete -d '0 0' 4444444444444444444444444444444444444444 `getmainid 02de42196ebe`
182 188 pre-close-tip:02de42196ebe draft book_02de
183 189 postclose-tip:02de42196ebe draft book_02de
184 190 txnclose hook: HG_NEW_OBSMARKERS=1 HG_TXNID=TXN:* HG_TXNNAME=debugobsolete (glob)
185 191 $ hg -R main bookmark --rev 42ccdea3bb16 book_42cc
192 pre-close-tip:02de42196ebe draft book_02de
193 postclose-tip:02de42196ebe draft book_02de
194 txnclose hook: HG_BOOKMARK_MOVED=1 HG_TXNID=TXN:* HG_TXNNAME=bookmark (glob)
186 195 $ hg -R main debugobsolete -d '0 0' 5555555555555555555555555555555555555555 `getmainid 42ccdea3bb16`
187 196 pre-close-tip:02de42196ebe draft book_02de
188 197 postclose-tip:02de42196ebe draft book_02de
189 198 txnclose hook: HG_NEW_OBSMARKERS=1 HG_TXNID=TXN:* HG_TXNNAME=debugobsolete (glob)
190 199 $ hg -R main bookmark --rev 5fddd98957c8 book_5fdd
200 pre-close-tip:02de42196ebe draft book_02de
201 postclose-tip:02de42196ebe draft book_02de
202 txnclose hook: HG_BOOKMARK_MOVED=1 HG_TXNID=TXN:* HG_TXNNAME=bookmark (glob)
191 203 $ hg -R main debugobsolete -d '0 0' 6666666666666666666666666666666666666666 `getmainid 5fddd98957c8`
192 204 pre-close-tip:02de42196ebe draft book_02de
193 205 postclose-tip:02de42196ebe draft book_02de
194 206 txnclose hook: HG_NEW_OBSMARKERS=1 HG_TXNID=TXN:* HG_TXNNAME=debugobsolete (glob)
195 207 $ hg -R main bookmark --rev 32af7686d403 book_32af
208 pre-close-tip:02de42196ebe draft book_02de
209 postclose-tip:02de42196ebe draft book_02de
210 txnclose hook: HG_BOOKMARK_MOVED=1 HG_TXNID=TXN:* HG_TXNNAME=bookmark (glob)
196 211 $ hg -R main debugobsolete -d '0 0' 7777777777777777777777777777777777777777 `getmainid 32af7686d403`
197 212 pre-close-tip:02de42196ebe draft book_02de
198 213 postclose-tip:02de42196ebe draft book_02de
199 214 txnclose hook: HG_NEW_OBSMARKERS=1 HG_TXNID=TXN:* HG_TXNNAME=debugobsolete (glob)
200 215
201 216 $ hg -R other bookmark --rev cd010b8cd998 book_eea1
217 pre-close-tip:24b6387c8c8c public
218 postclose-tip:24b6387c8c8c public
219 txnclose hook: HG_BOOKMARK_MOVED=1 HG_TXNID=TXN:* HG_TXNNAME=bookmark (glob)
202 220 $ hg -R other bookmark --rev cd010b8cd998 book_02de
221 pre-close-tip:24b6387c8c8c public
222 postclose-tip:24b6387c8c8c public
223 txnclose hook: HG_BOOKMARK_MOVED=1 HG_TXNID=TXN:* HG_TXNNAME=bookmark (glob)
203 224 $ hg -R other bookmark --rev cd010b8cd998 book_42cc
225 pre-close-tip:24b6387c8c8c public
226 postclose-tip:24b6387c8c8c public
227 txnclose hook: HG_BOOKMARK_MOVED=1 HG_TXNID=TXN:* HG_TXNNAME=bookmark (glob)
204 228 $ hg -R other bookmark --rev cd010b8cd998 book_5fdd
229 pre-close-tip:24b6387c8c8c public
230 postclose-tip:24b6387c8c8c public
231 txnclose hook: HG_BOOKMARK_MOVED=1 HG_TXNID=TXN:* HG_TXNNAME=bookmark (glob)
205 232 $ hg -R other bookmark --rev cd010b8cd998 book_32af
233 pre-close-tip:24b6387c8c8c public
234 postclose-tip:24b6387c8c8c public
235 txnclose hook: HG_BOOKMARK_MOVED=1 HG_TXNID=TXN:* HG_TXNNAME=bookmark (glob)
206 236
207 237 $ hg -R main phase --public eea13746799a
208 238 pre-close-tip:02de42196ebe draft book_02de
209 239 postclose-tip:02de42196ebe draft book_02de
210 240 txnclose hook: HG_PHASES_MOVED=1 HG_TXNID=TXN:* HG_TXNNAME=phase (glob)
211 241
212 242 push
213 243 $ hg -R main push other --rev eea13746799a --bookmark book_eea1
214 244 pushing to other
215 245 searching for changes
216 246 remote: adding changesets
217 247 remote: adding manifests
218 248 remote: adding file changes
219 249 remote: added 1 changesets with 0 changes to 0 files (-1 heads)
220 250 remote: 1 new obsolescence markers
221 251 remote: pre-close-tip:eea13746799a public book_eea1
222 252 remote: pushkey: lock state after "phases"
223 253 remote: lock: free
224 254 remote: wlock: free
225 255 remote: pushkey: lock state after "bookmarks"
226 256 remote: lock: free
227 257 remote: wlock: free
228 258 remote: postclose-tip:eea13746799a public book_eea1
229 259 remote: txnclose hook: HG_BOOKMARK_MOVED=1 HG_BUNDLE2=1 HG_NEW_OBSMARKERS=1 HG_NODE=eea13746799a9e0bfd88f29d3c2e9dc9389f524f HG_PHASES_MOVED=1 HG_SOURCE=push HG_TXNID=TXN:* HG_TXNNAME=push HG_URL=push (glob)
230 260 updating bookmark book_eea1
231 261 pre-close-tip:02de42196ebe draft book_02de
232 262 postclose-tip:02de42196ebe draft book_02de
233 263 txnclose hook: HG_SOURCE=push-response HG_TXNID=TXN:* HG_TXNNAME=push-response (glob)
234 264 file:/*/$TESTTMP/other HG_URL=file:$TESTTMP/other (glob)
235 265 $ hg -R other log -G
236 266 o 3:eea13746799a public Nicolas Dumazet <nicdumz.commits@gmail.com> book_eea1 G
237 267 |\
238 268 | o 2:24b6387c8c8c public Nicolas Dumazet <nicdumz.commits@gmail.com> F
239 269 | |
240 270 @ | 1:9520eea781bc public Nicolas Dumazet <nicdumz.commits@gmail.com> E
241 271 |/
242 272 o 0:cd010b8cd998 public Nicolas Dumazet <nicdumz.commits@gmail.com> book_02de book_32af book_42cc book_5fdd A
243 273
244 274 $ hg -R other debugobsolete
245 275 1111111111111111111111111111111111111111 9520eea781bcca16c1e15acc0ba14335a0e8e5ba 0 (Thu Jan 01 00:00:00 1970 +0000) {'user': 'test'}
246 276 2222222222222222222222222222222222222222 24b6387c8c8cae37178880f3fa95ded3cb1cf785 0 (Thu Jan 01 00:00:00 1970 +0000) {'user': 'test'}
247 277 3333333333333333333333333333333333333333 eea13746799a9e0bfd88f29d3c2e9dc9389f524f 0 (Thu Jan 01 00:00:00 1970 +0000) {'user': 'test'}
248 278
249 279 pull over ssh
250 280
251 281 $ hg -R other pull ssh://user@dummy/main -r 02de42196ebe --bookmark book_02de
252 282 pulling from ssh://user@dummy/main
253 283 searching for changes
254 284 adding changesets
255 285 adding manifests
256 286 adding file changes
257 287 added 1 changesets with 1 changes to 1 files (+1 heads)
258 288 1 new obsolescence markers
259 289 updating bookmark book_02de
260 290 pre-close-tip:02de42196ebe draft book_02de
261 291 postclose-tip:02de42196ebe draft book_02de
262 292 txnclose hook: HG_BOOKMARK_MOVED=1 HG_NEW_OBSMARKERS=1 HG_NODE=02de42196ebee42ef284b6780a87cdc96e8eaab6 HG_PHASES_MOVED=1 HG_SOURCE=pull HG_TXNID=TXN:* HG_TXNNAME=pull (glob)
263 293 ssh://user@dummy/main HG_URL=ssh://user@dummy/main
264 294 (run 'hg heads' to see heads, 'hg merge' to merge)
265 295 $ hg -R other debugobsolete
266 296 1111111111111111111111111111111111111111 9520eea781bcca16c1e15acc0ba14335a0e8e5ba 0 (Thu Jan 01 00:00:00 1970 +0000) {'user': 'test'}
267 297 2222222222222222222222222222222222222222 24b6387c8c8cae37178880f3fa95ded3cb1cf785 0 (Thu Jan 01 00:00:00 1970 +0000) {'user': 'test'}
268 298 3333333333333333333333333333333333333333 eea13746799a9e0bfd88f29d3c2e9dc9389f524f 0 (Thu Jan 01 00:00:00 1970 +0000) {'user': 'test'}
269 299 4444444444444444444444444444444444444444 02de42196ebee42ef284b6780a87cdc96e8eaab6 0 (Thu Jan 01 00:00:00 1970 +0000) {'user': 'test'}
270 300
271 301 pull over http
272 302
273 303 $ hg -R main serve -p $HGPORT -d --pid-file=main.pid -E main-error.log
274 304 $ cat main.pid >> $DAEMON_PIDS
275 305
276 306 $ hg -R other pull http://localhost:$HGPORT/ -r 42ccdea3bb16 --bookmark book_42cc
277 307 pulling from http://localhost:$HGPORT/
278 308 searching for changes
279 309 adding changesets
280 310 adding manifests
281 311 adding file changes
282 312 added 1 changesets with 1 changes to 1 files (+1 heads)
283 313 1 new obsolescence markers
284 314 updating bookmark book_42cc
285 315 pre-close-tip:42ccdea3bb16 draft book_42cc
286 316 postclose-tip:42ccdea3bb16 draft book_42cc
287 317 txnclose hook: HG_BOOKMARK_MOVED=1 HG_NEW_OBSMARKERS=1 HG_NODE=42ccdea3bb16d28e1848c95fe2e44c000f3f21b1 HG_PHASES_MOVED=1 HG_SOURCE=pull HG_TXNID=TXN:* HG_TXNNAME=pull (glob)
288 318 http://localhost:$HGPORT/ HG_URL=http://localhost:$HGPORT/
289 319 (run 'hg heads .' to see heads, 'hg merge' to merge)
290 320 $ cat main-error.log
291 321 $ hg -R other debugobsolete
292 322 1111111111111111111111111111111111111111 9520eea781bcca16c1e15acc0ba14335a0e8e5ba 0 (Thu Jan 01 00:00:00 1970 +0000) {'user': 'test'}
293 323 2222222222222222222222222222222222222222 24b6387c8c8cae37178880f3fa95ded3cb1cf785 0 (Thu Jan 01 00:00:00 1970 +0000) {'user': 'test'}
294 324 3333333333333333333333333333333333333333 eea13746799a9e0bfd88f29d3c2e9dc9389f524f 0 (Thu Jan 01 00:00:00 1970 +0000) {'user': 'test'}
295 325 4444444444444444444444444444444444444444 02de42196ebee42ef284b6780a87cdc96e8eaab6 0 (Thu Jan 01 00:00:00 1970 +0000) {'user': 'test'}
296 326 5555555555555555555555555555555555555555 42ccdea3bb16d28e1848c95fe2e44c000f3f21b1 0 (Thu Jan 01 00:00:00 1970 +0000) {'user': 'test'}
297 327
298 328 push over ssh
299 329
300 330 $ hg -R main push ssh://user@dummy/other -r 5fddd98957c8 --bookmark book_5fdd
301 331 pushing to ssh://user@dummy/other
302 332 searching for changes
303 333 remote: adding changesets
304 334 remote: adding manifests
305 335 remote: adding file changes
306 336 remote: added 1 changesets with 1 changes to 1 files
307 337 remote: 1 new obsolescence markers
308 338 remote: pre-close-tip:5fddd98957c8 draft book_5fdd
309 339 remote: pushkey: lock state after "bookmarks"
310 340 remote: lock: free
311 341 remote: wlock: free
312 342 remote: postclose-tip:5fddd98957c8 draft book_5fdd
313 343 remote: txnclose hook: HG_BOOKMARK_MOVED=1 HG_BUNDLE2=1 HG_NEW_OBSMARKERS=1 HG_NODE=5fddd98957c8a54a4d436dfe1da9d87f21a1b97b HG_SOURCE=serve HG_TXNID=TXN:* HG_TXNNAME=serve HG_URL=remote:ssh:127.0.0.1 (glob)
314 344 updating bookmark book_5fdd
315 345 pre-close-tip:02de42196ebe draft book_02de
316 346 postclose-tip:02de42196ebe draft book_02de
317 347 txnclose hook: HG_SOURCE=push-response HG_TXNID=TXN:* HG_TXNNAME=push-response (glob)
318 348 ssh://user@dummy/other HG_URL=ssh://user@dummy/other
319 349 $ hg -R other log -G
320 350 o 6:5fddd98957c8 draft Nicolas Dumazet <nicdumz.commits@gmail.com> book_5fdd C
321 351 |
322 352 o 5:42ccdea3bb16 draft Nicolas Dumazet <nicdumz.commits@gmail.com> book_42cc B
323 353 |
324 354 | o 4:02de42196ebe draft Nicolas Dumazet <nicdumz.commits@gmail.com> book_02de H
325 355 | |
326 356 | | o 3:eea13746799a public Nicolas Dumazet <nicdumz.commits@gmail.com> book_eea1 G
327 357 | |/|
328 358 | o | 2:24b6387c8c8c public Nicolas Dumazet <nicdumz.commits@gmail.com> F
329 359 |/ /
330 360 | @ 1:9520eea781bc public Nicolas Dumazet <nicdumz.commits@gmail.com> E
331 361 |/
332 362 o 0:cd010b8cd998 public Nicolas Dumazet <nicdumz.commits@gmail.com> book_32af A
333 363
334 364 $ hg -R other debugobsolete
335 365 1111111111111111111111111111111111111111 9520eea781bcca16c1e15acc0ba14335a0e8e5ba 0 (Thu Jan 01 00:00:00 1970 +0000) {'user': 'test'}
336 366 2222222222222222222222222222222222222222 24b6387c8c8cae37178880f3fa95ded3cb1cf785 0 (Thu Jan 01 00:00:00 1970 +0000) {'user': 'test'}
337 367 3333333333333333333333333333333333333333 eea13746799a9e0bfd88f29d3c2e9dc9389f524f 0 (Thu Jan 01 00:00:00 1970 +0000) {'user': 'test'}
338 368 4444444444444444444444444444444444444444 02de42196ebee42ef284b6780a87cdc96e8eaab6 0 (Thu Jan 01 00:00:00 1970 +0000) {'user': 'test'}
339 369 5555555555555555555555555555555555555555 42ccdea3bb16d28e1848c95fe2e44c000f3f21b1 0 (Thu Jan 01 00:00:00 1970 +0000) {'user': 'test'}
340 370 6666666666666666666666666666666666666666 5fddd98957c8a54a4d436dfe1da9d87f21a1b97b 0 (Thu Jan 01 00:00:00 1970 +0000) {'user': 'test'}
341 371
342 372 push over http
343 373
344 374 $ hg -R other serve -p $HGPORT2 -d --pid-file=other.pid -E other-error.log
345 375 $ cat other.pid >> $DAEMON_PIDS
346 376
347 377 $ hg -R main phase --public 32af7686d403
348 378 pre-close-tip:02de42196ebe draft book_02de
349 379 postclose-tip:02de42196ebe draft book_02de
350 380 txnclose hook: HG_PHASES_MOVED=1 HG_TXNID=TXN:* HG_TXNNAME=phase (glob)
351 381 $ hg -R main push http://localhost:$HGPORT2/ -r 32af7686d403 --bookmark book_32af
352 382 pushing to http://localhost:$HGPORT2/
353 383 searching for changes
354 384 remote: adding changesets
355 385 remote: adding manifests
356 386 remote: adding file changes
357 387 remote: added 1 changesets with 1 changes to 1 files
358 388 remote: 1 new obsolescence markers
359 389 remote: pre-close-tip:32af7686d403 public book_32af
360 390 remote: pushkey: lock state after "phases"
361 391 remote: lock: free
362 392 remote: wlock: free
363 393 remote: pushkey: lock state after "bookmarks"
364 394 remote: lock: free
365 395 remote: wlock: free
366 396 remote: postclose-tip:32af7686d403 public book_32af
367 397 remote: txnclose hook: HG_BOOKMARK_MOVED=1 HG_BUNDLE2=1 HG_NEW_OBSMARKERS=1 HG_NODE=32af7686d403cf45b5d95f2d70cebea587ac806a HG_PHASES_MOVED=1 HG_SOURCE=serve HG_TXNID=TXN:* HG_TXNNAME=serve HG_URL=remote:http:127.0.0.1: (glob)
368 398 updating bookmark book_32af
369 399 pre-close-tip:02de42196ebe draft book_02de
370 400 postclose-tip:02de42196ebe draft book_02de
371 401 txnclose hook: HG_SOURCE=push-response HG_TXNID=TXN:* HG_TXNNAME=push-response (glob)
372 402 http://localhost:$HGPORT2/ HG_URL=http://localhost:$HGPORT2/
373 403 $ cat other-error.log
374 404
375 405 Check final content.
376 406
377 407 $ hg -R other log -G
378 408 o 7:32af7686d403 public Nicolas Dumazet <nicdumz.commits@gmail.com> book_32af D
379 409 |
380 410 o 6:5fddd98957c8 public Nicolas Dumazet <nicdumz.commits@gmail.com> book_5fdd C
381 411 |
382 412 o 5:42ccdea3bb16 public Nicolas Dumazet <nicdumz.commits@gmail.com> book_42cc B
383 413 |
384 414 | o 4:02de42196ebe draft Nicolas Dumazet <nicdumz.commits@gmail.com> book_02de H
385 415 | |
386 416 | | o 3:eea13746799a public Nicolas Dumazet <nicdumz.commits@gmail.com> book_eea1 G
387 417 | |/|
388 418 | o | 2:24b6387c8c8c public Nicolas Dumazet <nicdumz.commits@gmail.com> F
389 419 |/ /
390 420 | @ 1:9520eea781bc public Nicolas Dumazet <nicdumz.commits@gmail.com> E
391 421 |/
392 422 o 0:cd010b8cd998 public Nicolas Dumazet <nicdumz.commits@gmail.com> A
393 423
394 424 $ hg -R other debugobsolete
395 425 1111111111111111111111111111111111111111 9520eea781bcca16c1e15acc0ba14335a0e8e5ba 0 (Thu Jan 01 00:00:00 1970 +0000) {'user': 'test'}
396 426 2222222222222222222222222222222222222222 24b6387c8c8cae37178880f3fa95ded3cb1cf785 0 (Thu Jan 01 00:00:00 1970 +0000) {'user': 'test'}
397 427 3333333333333333333333333333333333333333 eea13746799a9e0bfd88f29d3c2e9dc9389f524f 0 (Thu Jan 01 00:00:00 1970 +0000) {'user': 'test'}
398 428 4444444444444444444444444444444444444444 02de42196ebee42ef284b6780a87cdc96e8eaab6 0 (Thu Jan 01 00:00:00 1970 +0000) {'user': 'test'}
399 429 5555555555555555555555555555555555555555 42ccdea3bb16d28e1848c95fe2e44c000f3f21b1 0 (Thu Jan 01 00:00:00 1970 +0000) {'user': 'test'}
400 430 6666666666666666666666666666666666666666 5fddd98957c8a54a4d436dfe1da9d87f21a1b97b 0 (Thu Jan 01 00:00:00 1970 +0000) {'user': 'test'}
401 431 7777777777777777777777777777777777777777 32af7686d403cf45b5d95f2d70cebea587ac806a 0 (Thu Jan 01 00:00:00 1970 +0000) {'user': 'test'}
402 432
403 433 (check that no 'pending' files remain)
404 434
405 435 $ ls -1 other/.hg/bookmarks*
406 436 other/.hg/bookmarks
407 437 $ ls -1 other/.hg/store/phaseroots*
408 438 other/.hg/store/phaseroots
409 439 $ ls -1 other/.hg/store/00changelog.i*
410 440 other/.hg/store/00changelog.i
411 441
412 442 Error Handling
413 443 ==============
414 444
415 445 Check that errors are properly returned to the client during push.
416 446
417 447 Setting up
418 448
419 449 $ cat > failpush.py << EOF
420 450 > """A small extension that makes push fails when using bundle2
421 451 >
422 452 > used to test error handling in bundle2
423 453 > """
424 454 >
425 455 > from mercurial import util
426 456 > from mercurial import bundle2
427 457 > from mercurial import exchange
428 458 > from mercurial import extensions
429 459 >
430 460 > def _pushbundle2failpart(pushop, bundler):
431 461 > reason = pushop.ui.config('failpush', 'reason', None)
432 462 > part = None
433 463 > if reason == 'abort':
434 464 > bundler.newpart('test:abort')
435 465 > if reason == 'unknown':
436 466 > bundler.newpart('test:unknown')
437 467 > if reason == 'race':
438 468 > # 20 Bytes of crap
439 469 > bundler.newpart('check:heads', data='01234567890123456789')
440 470 >
441 471 > @bundle2.parthandler("test:abort")
442 472 > def handleabort(op, part):
443 473 > raise util.Abort('Abandon ship!', hint="don't panic")
444 474 >
445 475 > def uisetup(ui):
446 476 > exchange.b2partsgenmapping['failpart'] = _pushbundle2failpart
447 477 > exchange.b2partsgenorder.insert(0, 'failpart')
448 478 >
449 479 > EOF
450 480
451 481 $ cd main
452 482 $ hg up tip
453 483 3 files updated, 0 files merged, 1 files removed, 0 files unresolved
454 484 $ echo 'I' > I
455 485 $ hg add I
456 486 $ hg ci -m 'I'
457 487 pre-close-tip:e7ec4e813ba6 draft
458 488 postclose-tip:e7ec4e813ba6 draft
459 489 txnclose hook: HG_TXNID=TXN:* HG_TXNNAME=commit (glob)
460 490 $ hg id
461 491 e7ec4e813ba6 tip
462 492 $ cd ..
463 493
464 494 $ cat << EOF >> $HGRCPATH
465 495 > [extensions]
466 496 > failpush=$TESTTMP/failpush.py
467 497 > EOF
468 498
469 499 $ killdaemons.py
470 500 $ hg -R other serve -p $HGPORT2 -d --pid-file=other.pid -E other-error.log
471 501 $ cat other.pid >> $DAEMON_PIDS
472 502
473 503 Doing the actual push: Abort error
474 504
475 505 $ cat << EOF >> $HGRCPATH
476 506 > [failpush]
477 507 > reason = abort
478 508 > EOF
479 509
480 510 $ hg -R main push other -r e7ec4e813ba6
481 511 pushing to other
482 512 searching for changes
483 513 abort: Abandon ship!
484 514 (don't panic)
485 515 [255]
486 516
487 517 $ hg -R main push ssh://user@dummy/other -r e7ec4e813ba6
488 518 pushing to ssh://user@dummy/other
489 519 searching for changes
490 520 abort: Abandon ship!
491 521 (don't panic)
492 522 [255]
493 523
494 524 $ hg -R main push http://localhost:$HGPORT2/ -r e7ec4e813ba6
495 525 pushing to http://localhost:$HGPORT2/
496 526 searching for changes
497 527 abort: Abandon ship!
498 528 (don't panic)
499 529 [255]
500 530
501 531
502 532 Doing the actual push: unknown mandatory parts
503 533
504 534 $ cat << EOF >> $HGRCPATH
505 535 > [failpush]
506 536 > reason = unknown
507 537 > EOF
508 538
509 539 $ hg -R main push other -r e7ec4e813ba6
510 540 pushing to other
511 541 searching for changes
512 542 abort: missing support for test:unknown
513 543 [255]
514 544
515 545 $ hg -R main push ssh://user@dummy/other -r e7ec4e813ba6
516 546 pushing to ssh://user@dummy/other
517 547 searching for changes
518 548 abort: missing support for test:unknown
519 549 [255]
520 550
521 551 $ hg -R main push http://localhost:$HGPORT2/ -r e7ec4e813ba6
522 552 pushing to http://localhost:$HGPORT2/
523 553 searching for changes
524 554 abort: missing support for test:unknown
525 555 [255]
526 556
527 557 Doing the actual push: race
528 558
529 559 $ cat << EOF >> $HGRCPATH
530 560 > [failpush]
531 561 > reason = race
532 562 > EOF
533 563
534 564 $ hg -R main push other -r e7ec4e813ba6
535 565 pushing to other
536 566 searching for changes
537 567 abort: push failed:
538 568 'repository changed while pushing - please try again'
539 569 [255]
540 570
541 571 $ hg -R main push ssh://user@dummy/other -r e7ec4e813ba6
542 572 pushing to ssh://user@dummy/other
543 573 searching for changes
544 574 abort: push failed:
545 575 'repository changed while pushing - please try again'
546 576 [255]
547 577
548 578 $ hg -R main push http://localhost:$HGPORT2/ -r e7ec4e813ba6
549 579 pushing to http://localhost:$HGPORT2/
550 580 searching for changes
551 581 abort: push failed:
552 582 'repository changed while pushing - please try again'
553 583 [255]
554 584
555 585 Doing the actual push: hook abort
556 586
557 587 $ cat << EOF >> $HGRCPATH
558 588 > [failpush]
559 589 > reason =
560 590 > [hooks]
561 591 > pretxnclose.failpush = sh -c "echo 'You shall not pass!'; false"
562 592 > txnabort.failpush = sh -c "echo 'Cleaning up the mess...'"
563 593 > EOF
564 594
565 595 $ killdaemons.py
566 596 $ hg -R other serve -p $HGPORT2 -d --pid-file=other.pid -E other-error.log
567 597 $ cat other.pid >> $DAEMON_PIDS
568 598
569 599 $ hg -R main push other -r e7ec4e813ba6
570 600 pushing to other
571 601 searching for changes
572 602 remote: adding changesets
573 603 remote: adding manifests
574 604 remote: adding file changes
575 605 remote: added 1 changesets with 1 changes to 1 files
576 606 remote: pre-close-tip:e7ec4e813ba6 draft
577 607 remote: You shall not pass!
578 608 remote: transaction abort!
579 609 remote: Cleaning up the mess...
580 610 remote: rollback completed
581 611 abort: pretxnclose.failpush hook exited with status 1
582 612 [255]
583 613
584 614 $ hg -R main push ssh://user@dummy/other -r e7ec4e813ba6
585 615 pushing to ssh://user@dummy/other
586 616 searching for changes
587 617 remote: adding changesets
588 618 remote: adding manifests
589 619 remote: adding file changes
590 620 remote: added 1 changesets with 1 changes to 1 files
591 621 remote: pre-close-tip:e7ec4e813ba6 draft
592 622 remote: You shall not pass!
593 623 remote: transaction abort!
594 624 remote: Cleaning up the mess...
595 625 remote: rollback completed
596 626 abort: pretxnclose.failpush hook exited with status 1
597 627 [255]
598 628
599 629 $ hg -R main push http://localhost:$HGPORT2/ -r e7ec4e813ba6
600 630 pushing to http://localhost:$HGPORT2/
601 631 searching for changes
602 632 remote: adding changesets
603 633 remote: adding manifests
604 634 remote: adding file changes
605 635 remote: added 1 changesets with 1 changes to 1 files
606 636 remote: pre-close-tip:e7ec4e813ba6 draft
607 637 remote: You shall not pass!
608 638 remote: transaction abort!
609 639 remote: Cleaning up the mess...
610 640 remote: rollback completed
611 641 abort: pretxnclose.failpush hook exited with status 1
612 642 [255]
613 643
614 644 (check that no 'pending' files remain)
615 645
616 646 $ ls -1 other/.hg/bookmarks*
617 647 other/.hg/bookmarks
618 648 $ ls -1 other/.hg/store/phaseroots*
619 649 other/.hg/store/phaseroots
620 650 $ ls -1 other/.hg/store/00changelog.i*
621 651 other/.hg/store/00changelog.i
622 652
623 653 Check error from hook during the unbundling process itself
624 654
625 655 $ cat << EOF >> $HGRCPATH
626 656 > pretxnchangegroup = sh -c "echo 'Fail early!'; false"
627 657 > EOF
628 658 $ killdaemons.py # reload http config
629 659 $ hg -R other serve -p $HGPORT2 -d --pid-file=other.pid -E other-error.log
630 660 $ cat other.pid >> $DAEMON_PIDS
631 661
632 662 $ hg -R main push other -r e7ec4e813ba6
633 663 pushing to other
634 664 searching for changes
635 665 remote: adding changesets
636 666 remote: adding manifests
637 667 remote: adding file changes
638 668 remote: added 1 changesets with 1 changes to 1 files
639 669 remote: Fail early!
640 670 remote: transaction abort!
641 671 remote: Cleaning up the mess...
642 672 remote: rollback completed
643 673 abort: pretxnchangegroup hook exited with status 1
644 674 [255]
645 675 $ hg -R main push ssh://user@dummy/other -r e7ec4e813ba6
646 676 pushing to ssh://user@dummy/other
647 677 searching for changes
648 678 remote: adding changesets
649 679 remote: adding manifests
650 680 remote: adding file changes
651 681 remote: added 1 changesets with 1 changes to 1 files
652 682 remote: Fail early!
653 683 remote: transaction abort!
654 684 remote: Cleaning up the mess...
655 685 remote: rollback completed
656 686 abort: pretxnchangegroup hook exited with status 1
657 687 [255]
658 688 $ hg -R main push http://localhost:$HGPORT2/ -r e7ec4e813ba6
659 689 pushing to http://localhost:$HGPORT2/
660 690 searching for changes
661 691 remote: adding changesets
662 692 remote: adding manifests
663 693 remote: adding file changes
664 694 remote: added 1 changesets with 1 changes to 1 files
665 695 remote: Fail early!
666 696 remote: transaction abort!
667 697 remote: Cleaning up the mess...
668 698 remote: rollback completed
669 699 abort: pretxnchangegroup hook exited with status 1
670 700 [255]
671 701
672 702 Check output capture control.
673 703
674 704 (should be still forced for http, disabled for local and ssh)
675 705
676 706 $ cat >> $HGRCPATH << EOF
677 707 > [experimental]
678 708 > bundle2-output-capture=False
679 709 > EOF
680 710
681 711 $ hg -R main push other -r e7ec4e813ba6
682 712 pushing to other
683 713 searching for changes
684 714 adding changesets
685 715 adding manifests
686 716 adding file changes
687 717 added 1 changesets with 1 changes to 1 files
688 718 Fail early!
689 719 transaction abort!
690 720 Cleaning up the mess...
691 721 rollback completed
692 722 abort: pretxnchangegroup hook exited with status 1
693 723 [255]
694 724 $ hg -R main push ssh://user@dummy/other -r e7ec4e813ba6
695 725 pushing to ssh://user@dummy/other
696 726 searching for changes
697 727 remote: adding changesets
698 728 remote: adding manifests
699 729 remote: adding file changes
700 730 remote: added 1 changesets with 1 changes to 1 files
701 731 remote: Fail early!
702 732 remote: transaction abort!
703 733 remote: Cleaning up the mess...
704 734 remote: rollback completed
705 735 abort: pretxnchangegroup hook exited with status 1
706 736 [255]
707 737 $ hg -R main push http://localhost:$HGPORT2/ -r e7ec4e813ba6
708 738 pushing to http://localhost:$HGPORT2/
709 739 searching for changes
710 740 remote: adding changesets
711 741 remote: adding manifests
712 742 remote: adding file changes
713 743 remote: added 1 changesets with 1 changes to 1 files
714 744 remote: Fail early!
715 745 remote: transaction abort!
716 746 remote: Cleaning up the mess...
717 747 remote: rollback completed
718 748 abort: pretxnchangegroup hook exited with status 1
719 749 [255]
720 750
721 751 Check abort from mandatory pushkey
722 752
723 753 $ cat > mandatorypart.py << EOF
724 754 > from mercurial import exchange
725 755 > from mercurial import pushkey
726 756 > from mercurial import node
727 757 > from mercurial import error
728 758 > @exchange.b2partsgenerator('failingpuskey')
729 759 > def addfailingpushey(pushop, bundler):
730 760 > enc = pushkey.encode
731 761 > part = bundler.newpart('pushkey')
732 762 > part.addparam('namespace', enc('phases'))
733 763 > part.addparam('key', enc(pushop.repo['cd010b8cd998'].hex()))
734 764 > part.addparam('old', enc(str(0))) # successful update
735 765 > part.addparam('new', enc(str(0)))
736 766 > def fail(pushop, exc):
737 767 > raise error.Abort('Correct phase push failed (because hooks)')
738 768 > pushop.pkfailcb[part.id] = fail
739 769 > EOF
740 770 $ cat >> $HGRCPATH << EOF
741 771 > [hooks]
742 772 > pretxnchangegroup=
743 773 > pretxnclose.failpush=
744 774 > prepushkey.failpush = sh -c "echo 'do not push the key !'; false"
745 775 > [extensions]
746 776 > mandatorypart=$TESTTMP/mandatorypart.py
747 777 > EOF
748 778 $ "$TESTDIR/killdaemons.py" $DAEMON_PIDS # reload http config
749 779 $ hg -R other serve -p $HGPORT2 -d --pid-file=other.pid -E other-error.log
750 780 $ cat other.pid >> $DAEMON_PIDS
751 781
752 782 (Failure from a hook)
753 783
754 784 $ hg -R main push other -r e7ec4e813ba6
755 785 pushing to other
756 786 searching for changes
757 787 adding changesets
758 788 adding manifests
759 789 adding file changes
760 790 added 1 changesets with 1 changes to 1 files
761 791 do not push the key !
762 792 pushkey-abort: prepushkey.failpush hook exited with status 1
763 793 transaction abort!
764 794 Cleaning up the mess...
765 795 rollback completed
766 796 abort: Correct phase push failed (because hooks)
767 797 [255]
768 798 $ hg -R main push ssh://user@dummy/other -r e7ec4e813ba6
769 799 pushing to ssh://user@dummy/other
770 800 searching for changes
771 801 remote: adding changesets
772 802 remote: adding manifests
773 803 remote: adding file changes
774 804 remote: added 1 changesets with 1 changes to 1 files
775 805 remote: do not push the key !
776 806 remote: pushkey-abort: prepushkey.failpush hook exited with status 1
777 807 remote: transaction abort!
778 808 remote: Cleaning up the mess...
779 809 remote: rollback completed
780 810 abort: Correct phase push failed (because hooks)
781 811 [255]
782 812 $ hg -R main push http://localhost:$HGPORT2/ -r e7ec4e813ba6
783 813 pushing to http://localhost:$HGPORT2/
784 814 searching for changes
785 815 remote: adding changesets
786 816 remote: adding manifests
787 817 remote: adding file changes
788 818 remote: added 1 changesets with 1 changes to 1 files
789 819 remote: do not push the key !
790 820 remote: pushkey-abort: prepushkey.failpush hook exited with status 1
791 821 remote: transaction abort!
792 822 remote: Cleaning up the mess...
793 823 remote: rollback completed
794 824 abort: Correct phase push failed (because hooks)
795 825 [255]
796 826
797 827 (Failure from a the pushkey)
798 828
799 829 $ cat > mandatorypart.py << EOF
800 830 > from mercurial import exchange
801 831 > from mercurial import pushkey
802 832 > from mercurial import node
803 833 > from mercurial import error
804 834 > @exchange.b2partsgenerator('failingpuskey')
805 835 > def addfailingpushey(pushop, bundler):
806 836 > enc = pushkey.encode
807 837 > part = bundler.newpart('pushkey')
808 838 > part.addparam('namespace', enc('phases'))
809 839 > part.addparam('key', enc(pushop.repo['cd010b8cd998'].hex()))
810 840 > part.addparam('old', enc(str(4))) # will fail
811 841 > part.addparam('new', enc(str(3)))
812 842 > def fail(pushop, exc):
813 843 > raise error.Abort('Clown phase push failed')
814 844 > pushop.pkfailcb[part.id] = fail
815 845 > EOF
816 846 $ cat >> $HGRCPATH << EOF
817 847 > [hooks]
818 848 > prepushkey.failpush =
819 849 > EOF
820 850 $ "$TESTDIR/killdaemons.py" $DAEMON_PIDS # reload http config
821 851 $ hg -R other serve -p $HGPORT2 -d --pid-file=other.pid -E other-error.log
822 852 $ cat other.pid >> $DAEMON_PIDS
823 853
824 854 $ hg -R main push other -r e7ec4e813ba6
825 855 pushing to other
826 856 searching for changes
827 857 adding changesets
828 858 adding manifests
829 859 adding file changes
830 860 added 1 changesets with 1 changes to 1 files
831 861 transaction abort!
832 862 Cleaning up the mess...
833 863 rollback completed
834 864 pushkey: lock state after "phases"
835 865 lock: free
836 866 wlock: free
837 867 abort: Clown phase push failed
838 868 [255]
839 869 $ hg -R main push ssh://user@dummy/other -r e7ec4e813ba6
840 870 pushing to ssh://user@dummy/other
841 871 searching for changes
842 872 remote: adding changesets
843 873 remote: adding manifests
844 874 remote: adding file changes
845 875 remote: added 1 changesets with 1 changes to 1 files
846 876 remote: transaction abort!
847 877 remote: Cleaning up the mess...
848 878 remote: rollback completed
849 879 remote: pushkey: lock state after "phases"
850 880 remote: lock: free
851 881 remote: wlock: free
852 882 abort: Clown phase push failed
853 883 [255]
854 884 $ hg -R main push http://localhost:$HGPORT2/ -r e7ec4e813ba6
855 885 pushing to http://localhost:$HGPORT2/
856 886 searching for changes
857 887 remote: adding changesets
858 888 remote: adding manifests
859 889 remote: adding file changes
860 890 remote: added 1 changesets with 1 changes to 1 files
861 891 remote: transaction abort!
862 892 remote: Cleaning up the mess...
863 893 remote: rollback completed
864 894 remote: pushkey: lock state after "phases"
865 895 remote: lock: free
866 896 remote: wlock: free
867 897 abort: Clown phase push failed
868 898 [255]
869 899
@@ -1,704 +1,710
1 1 commit hooks can see env vars
2 2 (and post-transaction one are run unlocked)
3 3
4 4 $ cat << EOF >> $HGRCPATH
5 5 > [experimental]
6 6 > # drop me once bundle2 is the default,
7 7 > # added to get test change early.
8 8 > bundle2-exp = True
9 9 > EOF
10 10
11 11 $ cat > $TESTTMP/txnabort.checkargs.py <<EOF
12 12 > def showargs(ui, repo, hooktype, **kwargs):
13 13 > ui.write('%s python hook: %s\n' % (hooktype, ','.join(sorted(kwargs))))
14 14 > EOF
15 15
16 16 $ hg init a
17 17 $ cd a
18 18 $ cat > .hg/hgrc <<EOF
19 19 > [hooks]
20 20 > commit = sh -c "HG_LOCAL= HG_TAG= printenv.py commit"
21 21 > commit.b = sh -c "HG_LOCAL= HG_TAG= printenv.py commit.b"
22 22 > precommit = sh -c "HG_LOCAL= HG_NODE= HG_TAG= printenv.py precommit"
23 23 > pretxncommit = sh -c "HG_LOCAL= HG_TAG= printenv.py pretxncommit"
24 24 > pretxncommit.tip = hg -q tip
25 25 > pre-identify = printenv.py pre-identify 1
26 26 > pre-cat = printenv.py pre-cat
27 27 > post-cat = printenv.py post-cat
28 28 > pretxnopen = sh -c "HG_LOCAL= HG_TAG= printenv.py pretxnopen"
29 29 > pretxnclose = sh -c "HG_LOCAL= HG_TAG= printenv.py pretxnclose"
30 30 > txnclose = sh -c "HG_LOCAL= HG_TAG= printenv.py txnclose"
31 31 > txnabort.0 = python:$TESTTMP/txnabort.checkargs.py:showargs
32 32 > txnabort.1 = sh -c "HG_LOCAL= HG_TAG= printenv.py txnabort"
33 33 > txnclose.checklock = sh -c "hg debuglock > /dev/null"
34 34 > EOF
35 35 $ echo a > a
36 36 $ hg add a
37 37 $ hg commit -m a
38 38 precommit hook: HG_PARENT1=0000000000000000000000000000000000000000
39 39 pretxnopen hook: HG_TXNID=TXN:* HG_TXNNAME=commit (glob)
40 40 pretxncommit hook: HG_NODE=cb9a9f314b8b07ba71012fcdbc544b5a4d82ff5b HG_PARENT1=0000000000000000000000000000000000000000 HG_PENDING=$TESTTMP/a
41 41 0:cb9a9f314b8b
42 42 pretxnclose hook: HG_PENDING=$TESTTMP/a HG_PHASES_MOVED=1 HG_TXNID=TXN:* HG_TXNNAME=commit (glob)
43 43 txnclose hook: HG_PHASES_MOVED=1 HG_TXNID=TXN:* HG_TXNNAME=commit (glob)
44 44 commit hook: HG_NODE=cb9a9f314b8b07ba71012fcdbc544b5a4d82ff5b HG_PARENT1=0000000000000000000000000000000000000000
45 45 commit.b hook: HG_NODE=cb9a9f314b8b07ba71012fcdbc544b5a4d82ff5b HG_PARENT1=0000000000000000000000000000000000000000
46 46
47 47 $ hg clone . ../b
48 48 updating to branch default
49 49 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
50 50 $ cd ../b
51 51
52 52 changegroup hooks can see env vars
53 53
54 54 $ cat > .hg/hgrc <<EOF
55 55 > [hooks]
56 56 > prechangegroup = printenv.py prechangegroup
57 57 > changegroup = printenv.py changegroup
58 58 > incoming = printenv.py incoming
59 59 > EOF
60 60
61 61 pretxncommit and commit hooks can see both parents of merge
62 62
63 63 $ cd ../a
64 64 $ echo b >> a
65 65 $ hg commit -m a1 -d "1 0"
66 66 precommit hook: HG_PARENT1=cb9a9f314b8b07ba71012fcdbc544b5a4d82ff5b
67 67 pretxnopen hook: HG_TXNID=TXN:* HG_TXNNAME=commit (glob)
68 68 pretxncommit hook: HG_NODE=ab228980c14deea8b9555d91c9581127383e40fd HG_PARENT1=cb9a9f314b8b07ba71012fcdbc544b5a4d82ff5b HG_PENDING=$TESTTMP/a
69 69 1:ab228980c14d
70 70 pretxnclose hook: HG_PENDING=$TESTTMP/a HG_TXNID=TXN:* HG_TXNNAME=commit (glob)
71 71 txnclose hook: HG_TXNID=TXN:* HG_TXNNAME=commit (glob)
72 72 commit hook: HG_NODE=ab228980c14deea8b9555d91c9581127383e40fd HG_PARENT1=cb9a9f314b8b07ba71012fcdbc544b5a4d82ff5b
73 73 commit.b hook: HG_NODE=ab228980c14deea8b9555d91c9581127383e40fd HG_PARENT1=cb9a9f314b8b07ba71012fcdbc544b5a4d82ff5b
74 74 $ hg update -C 0
75 75 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
76 76 $ echo b > b
77 77 $ hg add b
78 78 $ hg commit -m b -d '1 0'
79 79 precommit hook: HG_PARENT1=cb9a9f314b8b07ba71012fcdbc544b5a4d82ff5b
80 80 pretxnopen hook: HG_TXNID=TXN:* HG_TXNNAME=commit (glob)
81 81 pretxncommit hook: HG_NODE=ee9deb46ab31e4cc3310f3cf0c3d668e4d8fffc2 HG_PARENT1=cb9a9f314b8b07ba71012fcdbc544b5a4d82ff5b HG_PENDING=$TESTTMP/a
82 82 2:ee9deb46ab31
83 83 pretxnclose hook: HG_PENDING=$TESTTMP/a HG_TXNID=TXN:* HG_TXNNAME=commit (glob)
84 84 txnclose hook: HG_TXNID=TXN:* HG_TXNNAME=commit (glob)
85 85 commit hook: HG_NODE=ee9deb46ab31e4cc3310f3cf0c3d668e4d8fffc2 HG_PARENT1=cb9a9f314b8b07ba71012fcdbc544b5a4d82ff5b
86 86 commit.b hook: HG_NODE=ee9deb46ab31e4cc3310f3cf0c3d668e4d8fffc2 HG_PARENT1=cb9a9f314b8b07ba71012fcdbc544b5a4d82ff5b
87 87 created new head
88 88 $ hg merge 1
89 89 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
90 90 (branch merge, don't forget to commit)
91 91 $ hg commit -m merge -d '2 0'
92 92 precommit hook: HG_PARENT1=ee9deb46ab31e4cc3310f3cf0c3d668e4d8fffc2 HG_PARENT2=ab228980c14deea8b9555d91c9581127383e40fd
93 93 pretxnopen hook: HG_TXNID=TXN:* HG_TXNNAME=commit (glob)
94 94 pretxncommit hook: HG_NODE=07f3376c1e655977439df2a814e3cc14b27abac2 HG_PARENT1=ee9deb46ab31e4cc3310f3cf0c3d668e4d8fffc2 HG_PARENT2=ab228980c14deea8b9555d91c9581127383e40fd HG_PENDING=$TESTTMP/a
95 95 3:07f3376c1e65
96 96 pretxnclose hook: HG_PENDING=$TESTTMP/a HG_TXNID=TXN:* HG_TXNNAME=commit (glob)
97 97 txnclose hook: HG_TXNID=TXN:* HG_TXNNAME=commit (glob)
98 98 commit hook: HG_NODE=07f3376c1e655977439df2a814e3cc14b27abac2 HG_PARENT1=ee9deb46ab31e4cc3310f3cf0c3d668e4d8fffc2 HG_PARENT2=ab228980c14deea8b9555d91c9581127383e40fd
99 99 commit.b hook: HG_NODE=07f3376c1e655977439df2a814e3cc14b27abac2 HG_PARENT1=ee9deb46ab31e4cc3310f3cf0c3d668e4d8fffc2 HG_PARENT2=ab228980c14deea8b9555d91c9581127383e40fd
100 100
101 101 test generic hooks
102 102
103 103 $ hg id
104 104 pre-identify hook: HG_ARGS=id HG_OPTS={'bookmarks': None, 'branch': None, 'id': None, 'insecure': None, 'num': None, 'remotecmd': '', 'rev': '', 'ssh': '', 'tags': None} HG_PATS=[]
105 105 abort: pre-identify hook exited with status 1
106 106 [255]
107 107 $ hg cat b
108 108 pre-cat hook: HG_ARGS=cat b HG_OPTS={'decode': None, 'exclude': [], 'include': [], 'output': '', 'rev': ''} HG_PATS=['b']
109 109 b
110 110 post-cat hook: HG_ARGS=cat b HG_OPTS={'decode': None, 'exclude': [], 'include': [], 'output': '', 'rev': ''} HG_PATS=['b'] HG_RESULT=0
111 111
112 112 $ cd ../b
113 113 $ hg pull ../a
114 114 pulling from ../a
115 115 searching for changes
116 116 prechangegroup hook: HG_SOURCE=pull HG_TXNID=TXN:* HG_URL=file:$TESTTMP/a (glob)
117 117 adding changesets
118 118 adding manifests
119 119 adding file changes
120 120 added 3 changesets with 2 changes to 2 files
121 121 changegroup hook: HG_NODE=ab228980c14deea8b9555d91c9581127383e40fd HG_SOURCE=pull HG_TXNID=TXN:* HG_URL=file:$TESTTMP/a (glob)
122 122 incoming hook: HG_NODE=ab228980c14deea8b9555d91c9581127383e40fd HG_SOURCE=pull HG_TXNID=TXN:* HG_URL=file:$TESTTMP/a (glob)
123 123 incoming hook: HG_NODE=ee9deb46ab31e4cc3310f3cf0c3d668e4d8fffc2 HG_SOURCE=pull HG_TXNID=TXN:* HG_URL=file:$TESTTMP/a (glob)
124 124 incoming hook: HG_NODE=07f3376c1e655977439df2a814e3cc14b27abac2 HG_SOURCE=pull HG_TXNID=TXN:* HG_URL=file:$TESTTMP/a (glob)
125 125 (run 'hg update' to get a working copy)
126 126
127 127 tag hooks can see env vars
128 128
129 129 $ cd ../a
130 130 $ cat >> .hg/hgrc <<EOF
131 131 > pretag = printenv.py pretag
132 132 > tag = sh -c "HG_PARENT1= HG_PARENT2= printenv.py tag"
133 133 > EOF
134 134 $ hg tag -d '3 0' a
135 135 pretag hook: HG_LOCAL=0 HG_NODE=07f3376c1e655977439df2a814e3cc14b27abac2 HG_TAG=a
136 136 precommit hook: HG_PARENT1=07f3376c1e655977439df2a814e3cc14b27abac2
137 137 pretxnopen hook: HG_TXNID=TXN:* HG_TXNNAME=commit (glob)
138 138 pretxncommit hook: HG_NODE=539e4b31b6dc99b3cfbaa6b53cbc1c1f9a1e3a10 HG_PARENT1=07f3376c1e655977439df2a814e3cc14b27abac2 HG_PENDING=$TESTTMP/a
139 139 4:539e4b31b6dc
140 140 pretxnclose hook: HG_PENDING=$TESTTMP/a HG_TXNID=TXN:* HG_TXNNAME=commit (glob)
141 141 tag hook: HG_LOCAL=0 HG_NODE=07f3376c1e655977439df2a814e3cc14b27abac2 HG_TAG=a
142 142 txnclose hook: HG_TXNID=TXN:* HG_TXNNAME=commit (glob)
143 143 commit hook: HG_NODE=539e4b31b6dc99b3cfbaa6b53cbc1c1f9a1e3a10 HG_PARENT1=07f3376c1e655977439df2a814e3cc14b27abac2
144 144 commit.b hook: HG_NODE=539e4b31b6dc99b3cfbaa6b53cbc1c1f9a1e3a10 HG_PARENT1=07f3376c1e655977439df2a814e3cc14b27abac2
145 145 $ hg tag -l la
146 146 pretag hook: HG_LOCAL=1 HG_NODE=539e4b31b6dc99b3cfbaa6b53cbc1c1f9a1e3a10 HG_TAG=la
147 147 tag hook: HG_LOCAL=1 HG_NODE=539e4b31b6dc99b3cfbaa6b53cbc1c1f9a1e3a10 HG_TAG=la
148 148
149 149 pretag hook can forbid tagging
150 150
151 151 $ echo "pretag.forbid = printenv.py pretag.forbid 1" >> .hg/hgrc
152 152 $ hg tag -d '4 0' fa
153 153 pretag hook: HG_LOCAL=0 HG_NODE=539e4b31b6dc99b3cfbaa6b53cbc1c1f9a1e3a10 HG_TAG=fa
154 154 pretag.forbid hook: HG_LOCAL=0 HG_NODE=539e4b31b6dc99b3cfbaa6b53cbc1c1f9a1e3a10 HG_TAG=fa
155 155 abort: pretag.forbid hook exited with status 1
156 156 [255]
157 157 $ hg tag -l fla
158 158 pretag hook: HG_LOCAL=1 HG_NODE=539e4b31b6dc99b3cfbaa6b53cbc1c1f9a1e3a10 HG_TAG=fla
159 159 pretag.forbid hook: HG_LOCAL=1 HG_NODE=539e4b31b6dc99b3cfbaa6b53cbc1c1f9a1e3a10 HG_TAG=fla
160 160 abort: pretag.forbid hook exited with status 1
161 161 [255]
162 162
163 163 pretxncommit hook can see changeset, can roll back txn, changeset no
164 164 more there after
165 165
166 166 $ echo "pretxncommit.forbid0 = hg tip -q" >> .hg/hgrc
167 167 $ echo "pretxncommit.forbid1 = printenv.py pretxncommit.forbid 1" >> .hg/hgrc
168 168 $ echo z > z
169 169 $ hg add z
170 170 $ hg -q tip
171 171 4:539e4b31b6dc
172 172 $ hg commit -m 'fail' -d '4 0'
173 173 precommit hook: HG_PARENT1=539e4b31b6dc99b3cfbaa6b53cbc1c1f9a1e3a10
174 174 pretxnopen hook: HG_TXNID=TXN:* HG_TXNNAME=commit (glob)
175 175 pretxncommit hook: HG_NODE=6f611f8018c10e827fee6bd2bc807f937e761567 HG_PARENT1=539e4b31b6dc99b3cfbaa6b53cbc1c1f9a1e3a10 HG_PENDING=$TESTTMP/a
176 176 5:6f611f8018c1
177 177 5:6f611f8018c1
178 178 pretxncommit.forbid hook: HG_NODE=6f611f8018c10e827fee6bd2bc807f937e761567 HG_PARENT1=539e4b31b6dc99b3cfbaa6b53cbc1c1f9a1e3a10 HG_PENDING=$TESTTMP/a
179 179 transaction abort!
180 180 txnabort python hook: txnid,txnname
181 181 txnabort hook: HG_TXNID=TXN:* HG_TXNNAME=commit (glob)
182 182 rollback completed
183 183 abort: pretxncommit.forbid1 hook exited with status 1
184 184 [255]
185 185 $ hg -q tip
186 186 4:539e4b31b6dc
187 187
188 188 (Check that no 'changelog.i.a' file were left behind)
189 189
190 190 $ ls -1 .hg/store/
191 191 00changelog.i
192 192 00manifest.i
193 193 data
194 194 fncache
195 195 journal.phaseroots
196 196 phaseroots
197 197 undo
198 198 undo.backup.fncache
199 199 undo.backupfiles
200 200 undo.phaseroots
201 201
202 202
203 203 precommit hook can prevent commit
204 204
205 205 $ echo "precommit.forbid = printenv.py precommit.forbid 1" >> .hg/hgrc
206 206 $ hg commit -m 'fail' -d '4 0'
207 207 precommit hook: HG_PARENT1=539e4b31b6dc99b3cfbaa6b53cbc1c1f9a1e3a10
208 208 precommit.forbid hook: HG_PARENT1=539e4b31b6dc99b3cfbaa6b53cbc1c1f9a1e3a10
209 209 abort: precommit.forbid hook exited with status 1
210 210 [255]
211 211 $ hg -q tip
212 212 4:539e4b31b6dc
213 213
214 214 preupdate hook can prevent update
215 215
216 216 $ echo "preupdate = printenv.py preupdate" >> .hg/hgrc
217 217 $ hg update 1
218 218 preupdate hook: HG_PARENT1=ab228980c14d
219 219 0 files updated, 0 files merged, 2 files removed, 0 files unresolved
220 220
221 221 update hook
222 222
223 223 $ echo "update = printenv.py update" >> .hg/hgrc
224 224 $ hg update
225 225 preupdate hook: HG_PARENT1=539e4b31b6dc
226 226 update hook: HG_ERROR=0 HG_PARENT1=539e4b31b6dc
227 227 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
228 228
229 229 pushkey hook
230 230
231 231 $ echo "pushkey = printenv.py pushkey" >> .hg/hgrc
232 232 $ cd ../b
233 233 $ hg bookmark -r null foo
234 234 $ hg push -B foo ../a
235 235 pushing to ../a
236 236 searching for changes
237 237 no changes found
238 238 pretxnopen hook: HG_TXNID=TXN:* HG_TXNNAME=push (glob)
239 239 pretxnclose hook: HG_BOOKMARK_MOVED=1 HG_BUNDLE2=1 HG_PENDING=$TESTTMP/a HG_SOURCE=push HG_TXNID=TXN:* HG_TXNNAME=push HG_URL=push (glob)
240 240 pushkey hook: HG_KEY=foo HG_NAMESPACE=bookmarks HG_NEW=0000000000000000000000000000000000000000 HG_RET=1
241 241 txnclose hook: HG_BOOKMARK_MOVED=1 HG_BUNDLE2=1 HG_SOURCE=push HG_TXNID=TXN:* HG_TXNNAME=push HG_URL=push (glob)
242 242 exporting bookmark foo
243 243 [1]
244 244 $ cd ../a
245 245
246 246 listkeys hook
247 247
248 248 $ echo "listkeys = printenv.py listkeys" >> .hg/hgrc
249 249 $ hg bookmark -r null bar
250 pretxnopen hook: HG_TXNID=TXN:* HG_TXNNAME=bookmark (glob)
251 pretxnclose hook: HG_BOOKMARK_MOVED=1 HG_PENDING=$TESTTMP/a HG_TXNID=TXN:* HG_TXNNAME=bookmark (glob)
252 txnclose hook: HG_BOOKMARK_MOVED=1 HG_TXNID=TXN:* HG_TXNNAME=bookmark (glob)
250 253 $ cd ../b
251 254 $ hg pull -B bar ../a
252 255 pulling from ../a
253 256 listkeys hook: HG_NAMESPACE=bookmarks HG_VALUES={'bar': '0000000000000000000000000000000000000000', 'foo': '0000000000000000000000000000000000000000'}
254 257 no changes found
255 258 listkeys hook: HG_NAMESPACE=phase HG_VALUES={}
256 259 adding remote bookmark bar
257 260 listkeys hook: HG_NAMESPACE=phases HG_VALUES={'cb9a9f314b8b07ba71012fcdbc544b5a4d82ff5b': '1', 'publishing': 'True'}
258 261 $ cd ../a
259 262
260 263 test that prepushkey can prevent incoming keys
261 264
262 265 $ echo "prepushkey = printenv.py prepushkey.forbid 1" >> .hg/hgrc
263 266 $ cd ../b
264 267 $ hg bookmark -r null baz
265 268 $ hg push -B baz ../a
266 269 pushing to ../a
267 270 searching for changes
268 271 listkeys hook: HG_NAMESPACE=phases HG_VALUES={'cb9a9f314b8b07ba71012fcdbc544b5a4d82ff5b': '1', 'publishing': 'True'}
269 272 listkeys hook: HG_NAMESPACE=bookmarks HG_VALUES={'bar': '0000000000000000000000000000000000000000', 'foo': '0000000000000000000000000000000000000000'}
270 273 no changes found
271 274 pretxnopen hook: HG_TXNID=TXN:* HG_TXNNAME=push (glob)
272 275 prepushkey.forbid hook: HG_BUNDLE2=1 HG_KEY=baz HG_NAMESPACE=bookmarks HG_NEW=0000000000000000000000000000000000000000 HG_SOURCE=push HG_TXNID=TXN:* HG_URL=push (glob)
273 276 pushkey-abort: prepushkey hook exited with status 1
274 277 abort: exporting bookmark baz failed!
275 278 [255]
276 279 $ cd ../a
277 280
278 281 test that prelistkeys can prevent listing keys
279 282
280 283 $ echo "prelistkeys = printenv.py prelistkeys.forbid 1" >> .hg/hgrc
281 284 $ hg bookmark -r null quux
285 pretxnopen hook: HG_TXNID=TXN:* HG_TXNNAME=bookmark (glob)
286 pretxnclose hook: HG_BOOKMARK_MOVED=1 HG_PENDING=$TESTTMP/a HG_TXNID=TXN:* HG_TXNNAME=bookmark (glob)
287 txnclose hook: HG_BOOKMARK_MOVED=1 HG_TXNID=TXN:* HG_TXNNAME=bookmark (glob)
282 288 $ cd ../b
283 289 $ hg pull -B quux ../a
284 290 pulling from ../a
285 291 prelistkeys.forbid hook: HG_NAMESPACE=bookmarks
286 292 abort: prelistkeys hook exited with status 1
287 293 [255]
288 294 $ cd ../a
289 295 $ rm .hg/hgrc
290 296
291 297 prechangegroup hook can prevent incoming changes
292 298
293 299 $ cd ../b
294 300 $ hg -q tip
295 301 3:07f3376c1e65
296 302 $ cat > .hg/hgrc <<EOF
297 303 > [hooks]
298 304 > prechangegroup.forbid = printenv.py prechangegroup.forbid 1
299 305 > EOF
300 306 $ hg pull ../a
301 307 pulling from ../a
302 308 searching for changes
303 309 prechangegroup.forbid hook: HG_SOURCE=pull HG_TXNID=TXN:* HG_URL=file:$TESTTMP/a (glob)
304 310 abort: prechangegroup.forbid hook exited with status 1
305 311 [255]
306 312
307 313 pretxnchangegroup hook can see incoming changes, can roll back txn,
308 314 incoming changes no longer there after
309 315
310 316 $ cat > .hg/hgrc <<EOF
311 317 > [hooks]
312 318 > pretxnchangegroup.forbid0 = hg tip -q
313 319 > pretxnchangegroup.forbid1 = printenv.py pretxnchangegroup.forbid 1
314 320 > EOF
315 321 $ hg pull ../a
316 322 pulling from ../a
317 323 searching for changes
318 324 adding changesets
319 325 adding manifests
320 326 adding file changes
321 327 added 1 changesets with 1 changes to 1 files
322 328 4:539e4b31b6dc
323 329 pretxnchangegroup.forbid hook: HG_NODE=539e4b31b6dc99b3cfbaa6b53cbc1c1f9a1e3a10 HG_PENDING=$TESTTMP/b HG_SOURCE=pull HG_TXNID=TXN:* HG_URL=file:$TESTTMP/a (glob)
324 330 transaction abort!
325 331 rollback completed
326 332 abort: pretxnchangegroup.forbid1 hook exited with status 1
327 333 [255]
328 334 $ hg -q tip
329 335 3:07f3376c1e65
330 336
331 337 outgoing hooks can see env vars
332 338
333 339 $ rm .hg/hgrc
334 340 $ cat > ../a/.hg/hgrc <<EOF
335 341 > [hooks]
336 342 > preoutgoing = printenv.py preoutgoing
337 343 > outgoing = printenv.py outgoing
338 344 > EOF
339 345 $ hg pull ../a
340 346 pulling from ../a
341 347 searching for changes
342 348 preoutgoing hook: HG_SOURCE=pull
343 349 outgoing hook: HG_NODE=539e4b31b6dc99b3cfbaa6b53cbc1c1f9a1e3a10 HG_SOURCE=pull
344 350 adding changesets
345 351 adding manifests
346 352 adding file changes
347 353 added 1 changesets with 1 changes to 1 files
348 354 adding remote bookmark quux
349 355 (run 'hg update' to get a working copy)
350 356 $ hg rollback
351 357 repository tip rolled back to revision 3 (undo pull)
352 358
353 359 preoutgoing hook can prevent outgoing changes
354 360
355 361 $ echo "preoutgoing.forbid = printenv.py preoutgoing.forbid 1" >> ../a/.hg/hgrc
356 362 $ hg pull ../a
357 363 pulling from ../a
358 364 searching for changes
359 365 preoutgoing hook: HG_SOURCE=pull
360 366 preoutgoing.forbid hook: HG_SOURCE=pull
361 367 abort: preoutgoing.forbid hook exited with status 1
362 368 [255]
363 369
364 370 outgoing hooks work for local clones
365 371
366 372 $ cd ..
367 373 $ cat > a/.hg/hgrc <<EOF
368 374 > [hooks]
369 375 > preoutgoing = printenv.py preoutgoing
370 376 > outgoing = printenv.py outgoing
371 377 > EOF
372 378 $ hg clone a c
373 379 preoutgoing hook: HG_SOURCE=clone
374 380 outgoing hook: HG_NODE=0000000000000000000000000000000000000000 HG_SOURCE=clone
375 381 updating to branch default
376 382 3 files updated, 0 files merged, 0 files removed, 0 files unresolved
377 383 $ rm -rf c
378 384
379 385 preoutgoing hook can prevent outgoing changes for local clones
380 386
381 387 $ echo "preoutgoing.forbid = printenv.py preoutgoing.forbid 1" >> a/.hg/hgrc
382 388 $ hg clone a zzz
383 389 preoutgoing hook: HG_SOURCE=clone
384 390 preoutgoing.forbid hook: HG_SOURCE=clone
385 391 abort: preoutgoing.forbid hook exited with status 1
386 392 [255]
387 393
388 394 $ cd "$TESTTMP/b"
389 395
390 396 $ cat > hooktests.py <<EOF
391 397 > from mercurial import util
392 398 >
393 399 > uncallable = 0
394 400 >
395 401 > def printargs(args):
396 402 > args.pop('ui', None)
397 403 > args.pop('repo', None)
398 404 > a = list(args.items())
399 405 > a.sort()
400 406 > print 'hook args:'
401 407 > for k, v in a:
402 408 > print ' ', k, v
403 409 >
404 410 > def passhook(**args):
405 411 > printargs(args)
406 412 >
407 413 > def failhook(**args):
408 414 > printargs(args)
409 415 > return True
410 416 >
411 417 > class LocalException(Exception):
412 418 > pass
413 419 >
414 420 > def raisehook(**args):
415 421 > raise LocalException('exception from hook')
416 422 >
417 423 > def aborthook(**args):
418 424 > raise util.Abort('raise abort from hook')
419 425 >
420 426 > def brokenhook(**args):
421 427 > return 1 + {}
422 428 >
423 429 > def verbosehook(ui, **args):
424 430 > ui.note('verbose output from hook\n')
425 431 >
426 432 > def printtags(ui, repo, **args):
427 433 > print sorted(repo.tags())
428 434 >
429 435 > class container:
430 436 > unreachable = 1
431 437 > EOF
432 438
433 439 test python hooks
434 440
435 441 #if windows
436 442 $ PYTHONPATH="$TESTTMP/b;$PYTHONPATH"
437 443 #else
438 444 $ PYTHONPATH="$TESTTMP/b:$PYTHONPATH"
439 445 #endif
440 446 $ export PYTHONPATH
441 447
442 448 $ echo '[hooks]' > ../a/.hg/hgrc
443 449 $ echo 'preoutgoing.broken = python:hooktests.brokenhook' >> ../a/.hg/hgrc
444 450 $ hg pull ../a 2>&1 | grep 'raised an exception'
445 451 error: preoutgoing.broken hook raised an exception: unsupported operand type(s) for +: 'int' and 'dict'
446 452
447 453 $ echo '[hooks]' > ../a/.hg/hgrc
448 454 $ echo 'preoutgoing.raise = python:hooktests.raisehook' >> ../a/.hg/hgrc
449 455 $ hg pull ../a 2>&1 | grep 'raised an exception'
450 456 error: preoutgoing.raise hook raised an exception: exception from hook
451 457
452 458 $ echo '[hooks]' > ../a/.hg/hgrc
453 459 $ echo 'preoutgoing.abort = python:hooktests.aborthook' >> ../a/.hg/hgrc
454 460 $ hg pull ../a
455 461 pulling from ../a
456 462 searching for changes
457 463 error: preoutgoing.abort hook failed: raise abort from hook
458 464 abort: raise abort from hook
459 465 [255]
460 466
461 467 $ echo '[hooks]' > ../a/.hg/hgrc
462 468 $ echo 'preoutgoing.fail = python:hooktests.failhook' >> ../a/.hg/hgrc
463 469 $ hg pull ../a
464 470 pulling from ../a
465 471 searching for changes
466 472 hook args:
467 473 hooktype preoutgoing
468 474 source pull
469 475 abort: preoutgoing.fail hook failed
470 476 [255]
471 477
472 478 $ echo '[hooks]' > ../a/.hg/hgrc
473 479 $ echo 'preoutgoing.uncallable = python:hooktests.uncallable' >> ../a/.hg/hgrc
474 480 $ hg pull ../a
475 481 pulling from ../a
476 482 searching for changes
477 483 abort: preoutgoing.uncallable hook is invalid ("hooktests.uncallable" is not callable)
478 484 [255]
479 485
480 486 $ echo '[hooks]' > ../a/.hg/hgrc
481 487 $ echo 'preoutgoing.nohook = python:hooktests.nohook' >> ../a/.hg/hgrc
482 488 $ hg pull ../a
483 489 pulling from ../a
484 490 searching for changes
485 491 abort: preoutgoing.nohook hook is invalid ("hooktests.nohook" is not defined)
486 492 [255]
487 493
488 494 $ echo '[hooks]' > ../a/.hg/hgrc
489 495 $ echo 'preoutgoing.nomodule = python:nomodule' >> ../a/.hg/hgrc
490 496 $ hg pull ../a
491 497 pulling from ../a
492 498 searching for changes
493 499 abort: preoutgoing.nomodule hook is invalid ("nomodule" not in a module)
494 500 [255]
495 501
496 502 $ echo '[hooks]' > ../a/.hg/hgrc
497 503 $ echo 'preoutgoing.badmodule = python:nomodule.nowhere' >> ../a/.hg/hgrc
498 504 $ hg pull ../a
499 505 pulling from ../a
500 506 searching for changes
501 507 abort: preoutgoing.badmodule hook is invalid (import of "nomodule" failed)
502 508 [255]
503 509
504 510 $ echo '[hooks]' > ../a/.hg/hgrc
505 511 $ echo 'preoutgoing.unreachable = python:hooktests.container.unreachable' >> ../a/.hg/hgrc
506 512 $ hg pull ../a
507 513 pulling from ../a
508 514 searching for changes
509 515 abort: preoutgoing.unreachable hook is invalid (import of "hooktests.container" failed)
510 516 [255]
511 517
512 518 $ echo '[hooks]' > ../a/.hg/hgrc
513 519 $ echo 'preoutgoing.pass = python:hooktests.passhook' >> ../a/.hg/hgrc
514 520 $ hg pull ../a
515 521 pulling from ../a
516 522 searching for changes
517 523 hook args:
518 524 hooktype preoutgoing
519 525 source pull
520 526 adding changesets
521 527 adding manifests
522 528 adding file changes
523 529 added 1 changesets with 1 changes to 1 files
524 530 adding remote bookmark quux
525 531 (run 'hg update' to get a working copy)
526 532
527 533 make sure --traceback works
528 534
529 535 $ echo '[hooks]' > .hg/hgrc
530 536 $ echo 'commit.abort = python:hooktests.aborthook' >> .hg/hgrc
531 537
532 538 $ echo aa > a
533 539 $ hg --traceback commit -d '0 0' -ma 2>&1 | grep '^Traceback'
534 540 Traceback (most recent call last):
535 541
536 542 $ cd ..
537 543 $ hg init c
538 544 $ cd c
539 545
540 546 $ cat > hookext.py <<EOF
541 547 > def autohook(**args):
542 548 > print "Automatically installed hook"
543 549 >
544 550 > def reposetup(ui, repo):
545 551 > repo.ui.setconfig("hooks", "commit.auto", autohook)
546 552 > EOF
547 553 $ echo '[extensions]' >> .hg/hgrc
548 554 $ echo 'hookext = hookext.py' >> .hg/hgrc
549 555
550 556 $ touch foo
551 557 $ hg add foo
552 558 $ hg ci -d '0 0' -m 'add foo'
553 559 Automatically installed hook
554 560 $ echo >> foo
555 561 $ hg ci --debug -d '0 0' -m 'change foo'
556 562 committing files:
557 563 foo
558 564 committing manifest
559 565 committing changelog
560 566 calling hook commit.auto: hgext_hookext.autohook
561 567 Automatically installed hook
562 568 committed changeset 1:52998019f6252a2b893452765fcb0a47351a5708
563 569
564 570 $ hg showconfig hooks
565 571 hooks.commit.auto=<function autohook at *> (glob)
566 572
567 573 test python hook configured with python:[file]:[hook] syntax
568 574
569 575 $ cd ..
570 576 $ mkdir d
571 577 $ cd d
572 578 $ hg init repo
573 579 $ mkdir hooks
574 580
575 581 $ cd hooks
576 582 $ cat > testhooks.py <<EOF
577 583 > def testhook(**args):
578 584 > print 'hook works'
579 585 > EOF
580 586 $ echo '[hooks]' > ../repo/.hg/hgrc
581 587 $ echo "pre-commit.test = python:`pwd`/testhooks.py:testhook" >> ../repo/.hg/hgrc
582 588
583 589 $ cd ../repo
584 590 $ hg commit -d '0 0'
585 591 hook works
586 592 nothing changed
587 593 [1]
588 594
589 595 $ echo '[hooks]' > .hg/hgrc
590 596 $ echo "update.ne = python:`pwd`/nonexistent.py:testhook" >> .hg/hgrc
591 597 $ echo "pre-identify.npmd = python:`pwd`/:no_python_module_dir" >> .hg/hgrc
592 598
593 599 $ hg up null
594 600 loading update.ne hook failed:
595 601 abort: No such file or directory: $TESTTMP/d/repo/nonexistent.py
596 602 [255]
597 603
598 604 $ hg id
599 605 loading pre-identify.npmd hook failed:
600 606 abort: No module named repo!
601 607 [255]
602 608
603 609 $ cd ../../b
604 610
605 611 make sure --traceback works on hook import failure
606 612
607 613 $ cat > importfail.py <<EOF
608 614 > import somebogusmodule
609 615 > # dereference something in the module to force demandimport to load it
610 616 > somebogusmodule.whatever
611 617 > EOF
612 618
613 619 $ echo '[hooks]' > .hg/hgrc
614 620 $ echo 'precommit.importfail = python:importfail.whatever' >> .hg/hgrc
615 621
616 622 $ echo a >> a
617 623 $ hg --traceback commit -ma 2>&1 | egrep -v '^( +File| [a-zA-Z(])'
618 624 exception from first failed import attempt:
619 625 Traceback (most recent call last):
620 626 ImportError: No module named somebogusmodule
621 627 exception from second failed import attempt:
622 628 Traceback (most recent call last):
623 629 ImportError: No module named hgext_importfail
624 630 Traceback (most recent call last):
625 631 Abort: precommit.importfail hook is invalid (import of "importfail" failed)
626 632 abort: precommit.importfail hook is invalid (import of "importfail" failed)
627 633
628 634 Issue1827: Hooks Update & Commit not completely post operation
629 635
630 636 commit and update hooks should run after command completion. The largefiles
631 637 use demonstrates a recursive wlock, showing the hook doesn't run until the
632 638 final release (and dirstate flush).
633 639
634 640 $ echo '[hooks]' > .hg/hgrc
635 641 $ echo 'commit = hg id' >> .hg/hgrc
636 642 $ echo 'update = hg id' >> .hg/hgrc
637 643 $ echo bb > a
638 644 $ hg ci -ma
639 645 223eafe2750c tip
640 646 $ hg up 0 --config extensions.largefiles=
641 647 cb9a9f314b8b
642 648 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
643 649
644 650 make sure --verbose (and --quiet/--debug etc.) are propagated to the local ui
645 651 that is passed to pre/post hooks
646 652
647 653 $ echo '[hooks]' > .hg/hgrc
648 654 $ echo 'pre-identify = python:hooktests.verbosehook' >> .hg/hgrc
649 655 $ hg id
650 656 cb9a9f314b8b
651 657 $ hg id --verbose
652 658 calling hook pre-identify: hooktests.verbosehook
653 659 verbose output from hook
654 660 cb9a9f314b8b
655 661
656 662 Ensure hooks can be prioritized
657 663
658 664 $ echo '[hooks]' > .hg/hgrc
659 665 $ echo 'pre-identify.a = python:hooktests.verbosehook' >> .hg/hgrc
660 666 $ echo 'pre-identify.b = python:hooktests.verbosehook' >> .hg/hgrc
661 667 $ echo 'priority.pre-identify.b = 1' >> .hg/hgrc
662 668 $ echo 'pre-identify.c = python:hooktests.verbosehook' >> .hg/hgrc
663 669 $ hg id --verbose
664 670 calling hook pre-identify.b: hooktests.verbosehook
665 671 verbose output from hook
666 672 calling hook pre-identify.a: hooktests.verbosehook
667 673 verbose output from hook
668 674 calling hook pre-identify.c: hooktests.verbosehook
669 675 verbose output from hook
670 676 cb9a9f314b8b
671 677
672 678 new tags must be visible in pretxncommit (issue3210)
673 679
674 680 $ echo 'pretxncommit.printtags = python:hooktests.printtags' >> .hg/hgrc
675 681 $ hg tag -f foo
676 682 ['a', 'foo', 'tip']
677 683
678 684 new commits must be visible in pretxnchangegroup (issue3428)
679 685
680 686 $ cd ..
681 687 $ hg init to
682 688 $ echo '[hooks]' >> to/.hg/hgrc
683 689 $ echo 'pretxnchangegroup = hg --traceback tip' >> to/.hg/hgrc
684 690 $ echo a >> to/a
685 691 $ hg --cwd to ci -Ama
686 692 adding a
687 693 $ hg clone to from
688 694 updating to branch default
689 695 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
690 696 $ echo aa >> from/a
691 697 $ hg --cwd from ci -mb
692 698 $ hg --cwd from push
693 699 pushing to $TESTTMP/to (glob)
694 700 searching for changes
695 701 adding changesets
696 702 adding manifests
697 703 adding file changes
698 704 added 1 changesets with 1 changes to 1 files
699 705 changeset: 1:9836a07b9b9d
700 706 tag: tip
701 707 user: test
702 708 date: Thu Jan 01 00:00:00 1970 +0000
703 709 summary: b
704 710
@@ -1,198 +1,198
1 1 setup repo
2 2 $ hg init t
3 3 $ cd t
4 4 $ echo a > a
5 5 $ hg commit -Am'add a'
6 6 adding a
7 7 $ hg verify
8 8 checking changesets
9 9 checking manifests
10 10 crosschecking files in changesets and manifests
11 11 checking files
12 12 1 files, 1 changesets, 1 total revisions
13 13 $ hg parents
14 14 changeset: 0:1f0dee641bb7
15 15 tag: tip
16 16 user: test
17 17 date: Thu Jan 01 00:00:00 1970 +0000
18 18 summary: add a
19 19
20 20
21 21 rollback to null revision
22 22 $ hg status
23 23 $ hg rollback
24 24 repository tip rolled back to revision -1 (undo commit)
25 25 working directory now based on revision -1
26 26 $ hg verify
27 27 checking changesets
28 28 checking manifests
29 29 crosschecking files in changesets and manifests
30 30 checking files
31 31 0 files, 0 changesets, 0 total revisions
32 32 $ hg parents
33 33 $ hg status
34 34 A a
35 35
36 36 Two changesets this time so we rollback to a real changeset
37 37 $ hg commit -m'add a again'
38 38 $ echo a >> a
39 39 $ hg commit -m'modify a'
40 40
41 41 Test issue 902 (current branch is preserved)
42 42 $ hg branch test
43 43 marked working directory as branch test
44 44 (branches are permanent and global, did you want a bookmark?)
45 45 $ hg rollback
46 46 repository tip rolled back to revision 0 (undo commit)
47 47 working directory now based on revision 0
48 48 $ hg branch
49 49 default
50 50
51 51 Test issue 1635 (commit message saved)
52 52 $ cat .hg/last-message.txt ; echo
53 53 modify a
54 54
55 55 Test rollback of hg before issue 902 was fixed
56 56
57 57 $ hg commit -m "test3"
58 58 $ hg branch test
59 59 marked working directory as branch test
60 60 (branches are permanent and global, did you want a bookmark?)
61 61 $ rm .hg/undo.branch
62 62 $ hg rollback
63 63 repository tip rolled back to revision 0 (undo commit)
64 64 named branch could not be reset: current branch is still 'test'
65 65 working directory now based on revision 0
66 66 $ hg branch
67 67 test
68 68
69 69 working dir unaffected by rollback: do not restore dirstate et. al.
70 70 $ hg log --template '{rev} {branch} {desc|firstline}\n'
71 71 0 default add a again
72 72 $ hg status
73 73 M a
74 74 $ hg bookmark foo
75 75 $ hg commit -m'modify a again'
76 76 $ echo b > b
77 $ hg bookmark bar -r default #making bar active, before the transaction
77 78 $ hg commit -Am'add b'
78 79 adding b
79 80 $ hg log --template '{rev} {branch} {desc|firstline}\n'
80 81 2 test add b
81 82 1 test modify a again
82 83 0 default add a again
83 $ hg update default
84 $ hg update bar
84 85 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
85 (leaving bookmark foo)
86 $ hg bookmark bar
86 (activating bookmark bar)
87 87 $ cat .hg/undo.branch ; echo
88 88 test
89 89 $ hg rollback -f
90 90 repository tip rolled back to revision 1 (undo commit)
91 91 $ hg id -n
92 92 0
93 93 $ hg branch
94 94 default
95 95 $ cat .hg/bookmarks.current ; echo
96 96 bar
97 $ hg bookmark --delete foo
97 $ hg bookmark --delete foo bar
98 98
99 99 rollback by pretxncommit saves commit message (issue1635)
100 100
101 101 $ echo a >> a
102 102 $ hg --config hooks.pretxncommit=false commit -m"precious commit message"
103 103 transaction abort!
104 104 rollback completed
105 105 abort: pretxncommit hook exited with status * (glob)
106 106 [255]
107 107 $ cat .hg/last-message.txt ; echo
108 108 precious commit message
109 109
110 110 same thing, but run $EDITOR
111 111
112 112 $ cat > editor.sh << '__EOF__'
113 113 > echo "another precious commit message" > "$1"
114 114 > __EOF__
115 115 $ HGEDITOR="\"sh\" \"`pwd`/editor.sh\"" hg --config hooks.pretxncommit=false commit 2>&1
116 116 transaction abort!
117 117 rollback completed
118 118 note: commit message saved in .hg/last-message.txt
119 119 abort: pretxncommit hook exited with status * (glob)
120 120 [255]
121 121 $ cat .hg/last-message.txt
122 122 another precious commit message
123 123
124 124 test rollback on served repository
125 125
126 126 #if serve
127 127 $ hg commit -m "precious commit message"
128 128 $ hg serve -p $HGPORT -d --pid-file=hg.pid -A access.log -E errors.log
129 129 $ cat hg.pid >> $DAEMON_PIDS
130 130 $ cd ..
131 131 $ hg clone http://localhost:$HGPORT u
132 132 requesting all changes
133 133 adding changesets
134 134 adding manifests
135 135 adding file changes
136 136 added 3 changesets with 2 changes to 1 files (+1 heads)
137 137 updating to branch default
138 138 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
139 139 $ cd u
140 140 $ hg id default
141 141 068774709090
142 142
143 143 now rollback and observe that 'hg serve' reloads the repository and
144 144 presents the correct tip changeset:
145 145
146 146 $ hg -R ../t rollback
147 147 repository tip rolled back to revision 1 (undo commit)
148 148 working directory now based on revision 0
149 149 $ hg id default
150 150 791dd2169706
151 151 #endif
152 152
153 153 update to older changeset and then refuse rollback, because
154 154 that would lose data (issue2998)
155 155 $ cd ../t
156 156 $ hg -q update
157 157 $ rm `hg status -un`
158 158 $ template='{rev}:{node|short} [{branch}] {desc|firstline}\n'
159 159 $ echo 'valuable new file' > b
160 160 $ echo 'valuable modification' >> a
161 161 $ hg commit -A -m'a valuable change'
162 162 adding b
163 163 $ hg update 0
164 164 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
165 165 $ hg rollback
166 166 abort: rollback of last commit while not checked out may lose data
167 167 (use -f to force)
168 168 [255]
169 169 $ hg tip -q
170 170 2:4d9cd3795eea
171 171 $ hg rollback -f
172 172 repository tip rolled back to revision 1 (undo commit)
173 173 $ hg status
174 174 $ hg log --removed b # yep, it's gone
175 175
176 176 same again, but emulate an old client that doesn't write undo.desc
177 177 $ hg -q update
178 178 $ echo 'valuable modification redux' >> a
179 179 $ hg commit -m'a valuable change redux'
180 180 $ rm .hg/undo.desc
181 181 $ hg update 0
182 182 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
183 183 $ hg rollback
184 184 rolling back unknown transaction
185 185 $ cat a
186 186 a
187 187
188 188 corrupt journal test
189 189 $ echo "foo" > .hg/store/journal
190 190 $ hg recover
191 191 rolling back interrupted transaction
192 192 couldn't read journal entry 'foo\n'!
193 193 checking changesets
194 194 checking manifests
195 195 crosschecking files in changesets and manifests
196 196 checking files
197 197 1 files, 2 changesets, 2 total revisions
198 198
General Comments 0
You need to be logged in to leave comments. Login now