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