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