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