##// END OF EJS Templates
util: make wrap() require a width argument...
Matt Mackall -
r12698:7aef77e7 default
parent child Browse files
Show More
@@ -1,4451 +1,4451
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, nullid, nullrev, short
9 9 from lock import release
10 10 from i18n import _, gettext
11 11 import os, re, sys, difflib, time, tempfile
12 12 import hg, util, revlog, extensions, copies, error
13 13 import patch, help, mdiff, url, encoding, templatekw, discovery
14 14 import archival, changegroup, cmdutil, sshserver, hbisect, hgweb, hgweb.server
15 15 import merge as mergemod
16 16 import minirst, revset
17 17 import dagparser
18 18
19 19 # Commands start here, listed alphabetically
20 20
21 21 def add(ui, repo, *pats, **opts):
22 22 """add the specified files on the next commit
23 23
24 24 Schedule files to be version controlled and added to the
25 25 repository.
26 26
27 27 The files will be added to the repository at the next commit. To
28 28 undo an add before that, see :hg:`forget`.
29 29
30 30 If no names are given, add all files to the repository.
31 31
32 32 .. container:: verbose
33 33
34 34 An example showing how new (unknown) files are added
35 35 automatically by :hg:`add`::
36 36
37 37 $ ls
38 38 foo.c
39 39 $ hg status
40 40 ? foo.c
41 41 $ hg add
42 42 adding foo.c
43 43 $ hg status
44 44 A foo.c
45 45
46 46 Returns 0 if all files are successfully added.
47 47 """
48 48
49 49 m = cmdutil.match(repo, pats, opts)
50 50 rejected = cmdutil.add(ui, repo, m, opts.get('dry_run'),
51 51 opts.get('subrepos'), prefix="")
52 52 return rejected and 1 or 0
53 53
54 54 def addremove(ui, repo, *pats, **opts):
55 55 """add all new files, delete all missing files
56 56
57 57 Add all new files and remove all missing files from the
58 58 repository.
59 59
60 60 New files are ignored if they match any of the patterns in
61 61 .hgignore. As with add, these changes take effect at the next
62 62 commit.
63 63
64 64 Use the -s/--similarity option to detect renamed files. With a
65 65 parameter greater than 0, this compares every removed file with
66 66 every added file and records those similar enough as renames. This
67 67 option takes a percentage between 0 (disabled) and 100 (files must
68 68 be identical) as its parameter. Detecting renamed files this way
69 69 can be expensive. After using this option, :hg:`status -C` can be
70 70 used to check which files were identified as moved or renamed.
71 71
72 72 Returns 0 if all files are successfully added.
73 73 """
74 74 try:
75 75 sim = float(opts.get('similarity') or 100)
76 76 except ValueError:
77 77 raise util.Abort(_('similarity must be a number'))
78 78 if sim < 0 or sim > 100:
79 79 raise util.Abort(_('similarity must be between 0 and 100'))
80 80 return cmdutil.addremove(repo, pats, opts, similarity=sim / 100.0)
81 81
82 82 def annotate(ui, repo, *pats, **opts):
83 83 """show changeset information by line for each file
84 84
85 85 List changes in files, showing the revision id responsible for
86 86 each line
87 87
88 88 This command is useful for discovering when a change was made and
89 89 by whom.
90 90
91 91 Without the -a/--text option, annotate will avoid processing files
92 92 it detects as binary. With -a, annotate will annotate the file
93 93 anyway, although the results will probably be neither useful
94 94 nor desirable.
95 95
96 96 Returns 0 on success.
97 97 """
98 98 if opts.get('follow'):
99 99 # --follow is deprecated and now just an alias for -f/--file
100 100 # to mimic the behavior of Mercurial before version 1.5
101 101 opts['file'] = 1
102 102
103 103 datefunc = ui.quiet and util.shortdate or util.datestr
104 104 getdate = util.cachefunc(lambda x: datefunc(x[0].date()))
105 105
106 106 if not pats:
107 107 raise util.Abort(_('at least one filename or pattern is required'))
108 108
109 109 opmap = [('user', lambda x: ui.shortuser(x[0].user())),
110 110 ('number', lambda x: str(x[0].rev())),
111 111 ('changeset', lambda x: short(x[0].node())),
112 112 ('date', getdate),
113 113 ('file', lambda x: x[0].path()),
114 114 ]
115 115
116 116 if (not opts.get('user') and not opts.get('changeset')
117 117 and not opts.get('date') and not opts.get('file')):
118 118 opts['number'] = 1
119 119
120 120 linenumber = opts.get('line_number') is not None
121 121 if linenumber and (not opts.get('changeset')) and (not opts.get('number')):
122 122 raise util.Abort(_('at least one of -n/-c is required for -l'))
123 123
124 124 funcmap = [func for op, func in opmap if opts.get(op)]
125 125 if linenumber:
126 126 lastfunc = funcmap[-1]
127 127 funcmap[-1] = lambda x: "%s:%s" % (lastfunc(x), x[1])
128 128
129 129 ctx = repo[opts.get('rev')]
130 130 m = cmdutil.match(repo, pats, opts)
131 131 follow = not opts.get('no_follow')
132 132 for abs in ctx.walk(m):
133 133 fctx = ctx[abs]
134 134 if not opts.get('text') and util.binary(fctx.data()):
135 135 ui.write(_("%s: binary file\n") % ((pats and m.rel(abs)) or abs))
136 136 continue
137 137
138 138 lines = fctx.annotate(follow=follow, linenumber=linenumber)
139 139 pieces = []
140 140
141 141 for f in funcmap:
142 142 l = [f(n) for n, dummy in lines]
143 143 if l:
144 144 sized = [(x, encoding.colwidth(x)) for x in l]
145 145 ml = max([w for x, w in sized])
146 146 pieces.append(["%s%s" % (' ' * (ml - w), x) for x, w in sized])
147 147
148 148 if pieces:
149 149 for p, l in zip(zip(*pieces), lines):
150 150 ui.write("%s: %s" % (" ".join(p), l[1]))
151 151
152 152 def archive(ui, repo, dest, **opts):
153 153 '''create an unversioned archive of a repository revision
154 154
155 155 By default, the revision used is the parent of the working
156 156 directory; use -r/--rev to specify a different revision.
157 157
158 158 The archive type is automatically detected based on file
159 159 extension (or override using -t/--type).
160 160
161 161 Valid types are:
162 162
163 163 :``files``: a directory full of files (default)
164 164 :``tar``: tar archive, uncompressed
165 165 :``tbz2``: tar archive, compressed using bzip2
166 166 :``tgz``: tar archive, compressed using gzip
167 167 :``uzip``: zip archive, uncompressed
168 168 :``zip``: zip archive, compressed using deflate
169 169
170 170 The exact name of the destination archive or directory is given
171 171 using a format string; see :hg:`help export` for details.
172 172
173 173 Each member added to an archive file has a directory prefix
174 174 prepended. Use -p/--prefix to specify a format string for the
175 175 prefix. The default is the basename of the archive, with suffixes
176 176 removed.
177 177
178 178 Returns 0 on success.
179 179 '''
180 180
181 181 ctx = repo[opts.get('rev')]
182 182 if not ctx:
183 183 raise util.Abort(_('no working directory: please specify a revision'))
184 184 node = ctx.node()
185 185 dest = cmdutil.make_filename(repo, dest, node)
186 186 if os.path.realpath(dest) == repo.root:
187 187 raise util.Abort(_('repository root cannot be destination'))
188 188
189 189 kind = opts.get('type') or archival.guesskind(dest) or 'files'
190 190 prefix = opts.get('prefix')
191 191
192 192 if dest == '-':
193 193 if kind == 'files':
194 194 raise util.Abort(_('cannot archive plain files to stdout'))
195 195 dest = sys.stdout
196 196 if not prefix:
197 197 prefix = os.path.basename(repo.root) + '-%h'
198 198
199 199 prefix = cmdutil.make_filename(repo, prefix, node)
200 200 matchfn = cmdutil.match(repo, [], opts)
201 201 archival.archive(repo, dest, node, kind, not opts.get('no_decode'),
202 202 matchfn, prefix, subrepos=opts.get('subrepos'))
203 203
204 204 def backout(ui, repo, node=None, rev=None, **opts):
205 205 '''reverse effect of earlier changeset
206 206
207 207 Commit the backed out changes as a new changeset. The new
208 208 changeset is a child of the backed out changeset.
209 209
210 210 If you backout a changeset other than the tip, a new head is
211 211 created. This head will be the new tip and you should merge this
212 212 backout changeset with another head.
213 213
214 214 The --merge option remembers the parent of the working directory
215 215 before starting the backout, then merges the new head with that
216 216 changeset afterwards. This saves you from doing the merge by hand.
217 217 The result of this merge is not committed, as with a normal merge.
218 218
219 219 See :hg:`help dates` for a list of formats valid for -d/--date.
220 220
221 221 Returns 0 on success.
222 222 '''
223 223 if rev and node:
224 224 raise util.Abort(_("please specify just one revision"))
225 225
226 226 if not rev:
227 227 rev = node
228 228
229 229 if not rev:
230 230 raise util.Abort(_("please specify a revision to backout"))
231 231
232 232 date = opts.get('date')
233 233 if date:
234 234 opts['date'] = util.parsedate(date)
235 235
236 236 cmdutil.bail_if_changed(repo)
237 237 node = repo.lookup(rev)
238 238
239 239 op1, op2 = repo.dirstate.parents()
240 240 a = repo.changelog.ancestor(op1, node)
241 241 if a != node:
242 242 raise util.Abort(_('cannot backout change on a different branch'))
243 243
244 244 p1, p2 = repo.changelog.parents(node)
245 245 if p1 == nullid:
246 246 raise util.Abort(_('cannot backout a change with no parents'))
247 247 if p2 != nullid:
248 248 if not opts.get('parent'):
249 249 raise util.Abort(_('cannot backout a merge changeset without '
250 250 '--parent'))
251 251 p = repo.lookup(opts['parent'])
252 252 if p not in (p1, p2):
253 253 raise util.Abort(_('%s is not a parent of %s') %
254 254 (short(p), short(node)))
255 255 parent = p
256 256 else:
257 257 if opts.get('parent'):
258 258 raise util.Abort(_('cannot use --parent on non-merge changeset'))
259 259 parent = p1
260 260
261 261 # the backout should appear on the same branch
262 262 branch = repo.dirstate.branch()
263 263 hg.clean(repo, node, show_stats=False)
264 264 repo.dirstate.setbranch(branch)
265 265 revert_opts = opts.copy()
266 266 revert_opts['date'] = None
267 267 revert_opts['all'] = True
268 268 revert_opts['rev'] = hex(parent)
269 269 revert_opts['no_backup'] = None
270 270 revert(ui, repo, **revert_opts)
271 271 commit_opts = opts.copy()
272 272 commit_opts['addremove'] = False
273 273 if not commit_opts['message'] and not commit_opts['logfile']:
274 274 # we don't translate commit messages
275 275 commit_opts['message'] = "Backed out changeset %s" % short(node)
276 276 commit_opts['force_editor'] = True
277 277 commit(ui, repo, **commit_opts)
278 278 def nice(node):
279 279 return '%d:%s' % (repo.changelog.rev(node), short(node))
280 280 ui.status(_('changeset %s backs out changeset %s\n') %
281 281 (nice(repo.changelog.tip()), nice(node)))
282 282 if op1 != node:
283 283 hg.clean(repo, op1, show_stats=False)
284 284 if opts.get('merge'):
285 285 ui.status(_('merging with changeset %s\n')
286 286 % nice(repo.changelog.tip()))
287 287 hg.merge(repo, hex(repo.changelog.tip()))
288 288 else:
289 289 ui.status(_('the backout changeset is a new head - '
290 290 'do not forget to merge\n'))
291 291 ui.status(_('(use "backout --merge" '
292 292 'if you want to auto-merge)\n'))
293 293
294 294 def bisect(ui, repo, rev=None, extra=None, command=None,
295 295 reset=None, good=None, bad=None, skip=None, noupdate=None):
296 296 """subdivision search of changesets
297 297
298 298 This command helps to find changesets which introduce problems. To
299 299 use, mark the earliest changeset you know exhibits the problem as
300 300 bad, then mark the latest changeset which is free from the problem
301 301 as good. Bisect will update your working directory to a revision
302 302 for testing (unless the -U/--noupdate option is specified). Once
303 303 you have performed tests, mark the working directory as good or
304 304 bad, and bisect will either update to another candidate changeset
305 305 or announce that it has found the bad revision.
306 306
307 307 As a shortcut, you can also use the revision argument to mark a
308 308 revision as good or bad without checking it out first.
309 309
310 310 If you supply a command, it will be used for automatic bisection.
311 311 Its exit status will be used to mark revisions as good or bad:
312 312 status 0 means good, 125 means to skip the revision, 127
313 313 (command not found) will abort the bisection, and any other
314 314 non-zero exit status means the revision is bad.
315 315
316 316 Returns 0 on success.
317 317 """
318 318 def print_result(nodes, good):
319 319 displayer = cmdutil.show_changeset(ui, repo, {})
320 320 if len(nodes) == 1:
321 321 # narrowed it down to a single revision
322 322 if good:
323 323 ui.write(_("The first good revision is:\n"))
324 324 else:
325 325 ui.write(_("The first bad revision is:\n"))
326 326 displayer.show(repo[nodes[0]])
327 327 parents = repo[nodes[0]].parents()
328 328 if len(parents) > 1:
329 329 side = good and state['bad'] or state['good']
330 330 num = len(set(i.node() for i in parents) & set(side))
331 331 if num == 1:
332 332 common = parents[0].ancestor(parents[1])
333 333 ui.write(_('Not all ancestors of this changeset have been'
334 334 ' checked.\nTo check the other ancestors, start'
335 335 ' from the common ancestor, %s.\n' % common))
336 336 else:
337 337 # multiple possible revisions
338 338 if good:
339 339 ui.write(_("Due to skipped revisions, the first "
340 340 "good revision could be any of:\n"))
341 341 else:
342 342 ui.write(_("Due to skipped revisions, the first "
343 343 "bad revision could be any of:\n"))
344 344 for n in nodes:
345 345 displayer.show(repo[n])
346 346 displayer.close()
347 347
348 348 def check_state(state, interactive=True):
349 349 if not state['good'] or not state['bad']:
350 350 if (good or bad or skip or reset) and interactive:
351 351 return
352 352 if not state['good']:
353 353 raise util.Abort(_('cannot bisect (no known good revisions)'))
354 354 else:
355 355 raise util.Abort(_('cannot bisect (no known bad revisions)'))
356 356 return True
357 357
358 358 # backward compatibility
359 359 if rev in "good bad reset init".split():
360 360 ui.warn(_("(use of 'hg bisect <cmd>' is deprecated)\n"))
361 361 cmd, rev, extra = rev, extra, None
362 362 if cmd == "good":
363 363 good = True
364 364 elif cmd == "bad":
365 365 bad = True
366 366 else:
367 367 reset = True
368 368 elif extra or good + bad + skip + reset + bool(command) > 1:
369 369 raise util.Abort(_('incompatible arguments'))
370 370
371 371 if reset:
372 372 p = repo.join("bisect.state")
373 373 if os.path.exists(p):
374 374 os.unlink(p)
375 375 return
376 376
377 377 state = hbisect.load_state(repo)
378 378
379 379 if command:
380 380 changesets = 1
381 381 try:
382 382 while changesets:
383 383 # update state
384 384 status = util.system(command)
385 385 if status == 125:
386 386 transition = "skip"
387 387 elif status == 0:
388 388 transition = "good"
389 389 # status < 0 means process was killed
390 390 elif status == 127:
391 391 raise util.Abort(_("failed to execute %s") % command)
392 392 elif status < 0:
393 393 raise util.Abort(_("%s killed") % command)
394 394 else:
395 395 transition = "bad"
396 396 ctx = repo[rev or '.']
397 397 state[transition].append(ctx.node())
398 398 ui.status(_('Changeset %d:%s: %s\n') % (ctx, ctx, transition))
399 399 check_state(state, interactive=False)
400 400 # bisect
401 401 nodes, changesets, good = hbisect.bisect(repo.changelog, state)
402 402 # update to next check
403 403 cmdutil.bail_if_changed(repo)
404 404 hg.clean(repo, nodes[0], show_stats=False)
405 405 finally:
406 406 hbisect.save_state(repo, state)
407 407 print_result(nodes, good)
408 408 return
409 409
410 410 # update state
411 411
412 412 if rev:
413 413 nodes = [repo.lookup(i) for i in cmdutil.revrange(repo, [rev])]
414 414 else:
415 415 nodes = [repo.lookup('.')]
416 416
417 417 if good or bad or skip:
418 418 if good:
419 419 state['good'] += nodes
420 420 elif bad:
421 421 state['bad'] += nodes
422 422 elif skip:
423 423 state['skip'] += nodes
424 424 hbisect.save_state(repo, state)
425 425
426 426 if not check_state(state):
427 427 return
428 428
429 429 # actually bisect
430 430 nodes, changesets, good = hbisect.bisect(repo.changelog, state)
431 431 if changesets == 0:
432 432 print_result(nodes, good)
433 433 else:
434 434 assert len(nodes) == 1 # only a single node can be tested next
435 435 node = nodes[0]
436 436 # compute the approximate number of remaining tests
437 437 tests, size = 0, 2
438 438 while size <= changesets:
439 439 tests, size = tests + 1, size * 2
440 440 rev = repo.changelog.rev(node)
441 441 ui.write(_("Testing changeset %d:%s "
442 442 "(%d changesets remaining, ~%d tests)\n")
443 443 % (rev, short(node), changesets, tests))
444 444 if not noupdate:
445 445 cmdutil.bail_if_changed(repo)
446 446 return hg.clean(repo, node)
447 447
448 448 def branch(ui, repo, label=None, **opts):
449 449 """set or show the current branch name
450 450
451 451 With no argument, show the current branch name. With one argument,
452 452 set the working directory branch name (the branch will not exist
453 453 in the repository until the next commit). Standard practice
454 454 recommends that primary development take place on the 'default'
455 455 branch.
456 456
457 457 Unless -f/--force is specified, branch will not let you set a
458 458 branch name that already exists, even if it's inactive.
459 459
460 460 Use -C/--clean to reset the working directory branch to that of
461 461 the parent of the working directory, negating a previous branch
462 462 change.
463 463
464 464 Use the command :hg:`update` to switch to an existing branch. Use
465 465 :hg:`commit --close-branch` to mark this branch as closed.
466 466
467 467 Returns 0 on success.
468 468 """
469 469
470 470 if opts.get('clean'):
471 471 label = repo[None].parents()[0].branch()
472 472 repo.dirstate.setbranch(label)
473 473 ui.status(_('reset working directory to branch %s\n') % label)
474 474 elif label:
475 475 utflabel = encoding.fromlocal(label)
476 476 if not opts.get('force') and utflabel in repo.branchtags():
477 477 if label not in [p.branch() for p in repo.parents()]:
478 478 raise util.Abort(_('a branch of the same name already exists'
479 479 " (use 'hg update' to switch to it)"))
480 480 repo.dirstate.setbranch(utflabel)
481 481 ui.status(_('marked working directory as branch %s\n') % label)
482 482 else:
483 483 ui.write("%s\n" % encoding.tolocal(repo.dirstate.branch()))
484 484
485 485 def branches(ui, repo, active=False, closed=False):
486 486 """list repository named branches
487 487
488 488 List the repository's named branches, indicating which ones are
489 489 inactive. If -c/--closed is specified, also list branches which have
490 490 been marked closed (see :hg:`commit --close-branch`).
491 491
492 492 If -a/--active is specified, only show active branches. A branch
493 493 is considered active if it contains repository heads.
494 494
495 495 Use the command :hg:`update` to switch to an existing branch.
496 496
497 497 Returns 0.
498 498 """
499 499
500 500 hexfunc = ui.debugflag and hex or short
501 501 activebranches = [repo[n].branch() for n in repo.heads()]
502 502 def testactive(tag, node):
503 503 realhead = tag in activebranches
504 504 open = node in repo.branchheads(tag, closed=False)
505 505 return realhead and open
506 506 branches = sorted([(testactive(tag, node), repo.changelog.rev(node), tag)
507 507 for tag, node in repo.branchtags().items()],
508 508 reverse=True)
509 509
510 510 for isactive, node, tag in branches:
511 511 if (not active) or isactive:
512 512 encodedtag = encoding.tolocal(tag)
513 513 if ui.quiet:
514 514 ui.write("%s\n" % encodedtag)
515 515 else:
516 516 hn = repo.lookup(node)
517 517 if isactive:
518 518 label = 'branches.active'
519 519 notice = ''
520 520 elif hn not in repo.branchheads(tag, closed=False):
521 521 if not closed:
522 522 continue
523 523 label = 'branches.closed'
524 524 notice = _(' (closed)')
525 525 else:
526 526 label = 'branches.inactive'
527 527 notice = _(' (inactive)')
528 528 if tag == repo.dirstate.branch():
529 529 label = 'branches.current'
530 530 rev = str(node).rjust(31 - encoding.colwidth(encodedtag))
531 531 rev = ui.label('%s:%s' % (rev, hexfunc(hn)), 'log.changeset')
532 532 encodedtag = ui.label(encodedtag, label)
533 533 ui.write("%s %s%s\n" % (encodedtag, rev, notice))
534 534
535 535 def bundle(ui, repo, fname, dest=None, **opts):
536 536 """create a changegroup file
537 537
538 538 Generate a compressed changegroup file collecting changesets not
539 539 known to be in another repository.
540 540
541 541 If you omit the destination repository, then hg assumes the
542 542 destination will have all the nodes you specify with --base
543 543 parameters. To create a bundle containing all changesets, use
544 544 -a/--all (or --base null).
545 545
546 546 You can change compression method with the -t/--type option.
547 547 The available compression methods are: none, bzip2, and
548 548 gzip (by default, bundles are compressed using bzip2).
549 549
550 550 The bundle file can then be transferred using conventional means
551 551 and applied to another repository with the unbundle or pull
552 552 command. This is useful when direct push and pull are not
553 553 available or when exporting an entire repository is undesirable.
554 554
555 555 Applying bundles preserves all changeset contents including
556 556 permissions, copy/rename information, and revision history.
557 557
558 558 Returns 0 on success, 1 if no changes found.
559 559 """
560 560 revs = opts.get('rev') or None
561 561 if opts.get('all'):
562 562 base = ['null']
563 563 else:
564 564 base = opts.get('base')
565 565 if base:
566 566 if dest:
567 567 raise util.Abort(_("--base is incompatible with specifying "
568 568 "a destination"))
569 569 base = [repo.lookup(rev) for rev in base]
570 570 # create the right base
571 571 # XXX: nodesbetween / changegroup* should be "fixed" instead
572 572 o = []
573 573 has = set((nullid,))
574 574 for n in base:
575 575 has.update(repo.changelog.reachable(n))
576 576 if revs:
577 577 revs = [repo.lookup(rev) for rev in revs]
578 578 visit = revs[:]
579 579 has.difference_update(visit)
580 580 else:
581 581 visit = repo.changelog.heads()
582 582 seen = {}
583 583 while visit:
584 584 n = visit.pop(0)
585 585 parents = [p for p in repo.changelog.parents(n) if p not in has]
586 586 if len(parents) == 0:
587 587 if n not in has:
588 588 o.append(n)
589 589 else:
590 590 for p in parents:
591 591 if p not in seen:
592 592 seen[p] = 1
593 593 visit.append(p)
594 594 else:
595 595 dest = ui.expandpath(dest or 'default-push', dest or 'default')
596 596 dest, branches = hg.parseurl(dest, opts.get('branch'))
597 597 other = hg.repository(hg.remoteui(repo, opts), dest)
598 598 revs, checkout = hg.addbranchrevs(repo, other, branches, revs)
599 599 if revs:
600 600 revs = [repo.lookup(rev) for rev in revs]
601 601 o = discovery.findoutgoing(repo, other, force=opts.get('force'))
602 602
603 603 if not o:
604 604 ui.status(_("no changes found\n"))
605 605 return 1
606 606
607 607 if revs:
608 608 cg = repo.changegroupsubset(o, revs, 'bundle')
609 609 else:
610 610 cg = repo.changegroup(o, 'bundle')
611 611
612 612 bundletype = opts.get('type', 'bzip2').lower()
613 613 btypes = {'none': 'HG10UN', 'bzip2': 'HG10BZ', 'gzip': 'HG10GZ'}
614 614 bundletype = btypes.get(bundletype)
615 615 if bundletype not in changegroup.bundletypes:
616 616 raise util.Abort(_('unknown bundle type specified with --type'))
617 617
618 618 changegroup.writebundle(cg, fname, bundletype)
619 619
620 620 def cat(ui, repo, file1, *pats, **opts):
621 621 """output the current or given revision of files
622 622
623 623 Print the specified files as they were at the given revision. If
624 624 no revision is given, the parent of the working directory is used,
625 625 or tip if no revision is checked out.
626 626
627 627 Output may be to a file, in which case the name of the file is
628 628 given using a format string. The formatting rules are the same as
629 629 for the export command, with the following additions:
630 630
631 631 :``%s``: basename of file being printed
632 632 :``%d``: dirname of file being printed, or '.' if in repository root
633 633 :``%p``: root-relative path name of file being printed
634 634
635 635 Returns 0 on success.
636 636 """
637 637 ctx = cmdutil.revsingle(repo, opts.get('rev'))
638 638 err = 1
639 639 m = cmdutil.match(repo, (file1,) + pats, opts)
640 640 for abs in ctx.walk(m):
641 641 fp = cmdutil.make_file(repo, opts.get('output'), ctx.node(), pathname=abs)
642 642 data = ctx[abs].data()
643 643 if opts.get('decode'):
644 644 data = repo.wwritedata(abs, data)
645 645 fp.write(data)
646 646 err = 0
647 647 return err
648 648
649 649 def clone(ui, source, dest=None, **opts):
650 650 """make a copy of an existing repository
651 651
652 652 Create a copy of an existing repository in a new directory.
653 653
654 654 If no destination directory name is specified, it defaults to the
655 655 basename of the source.
656 656
657 657 The location of the source is added to the new repository's
658 658 .hg/hgrc file, as the default to be used for future pulls.
659 659
660 660 See :hg:`help urls` for valid source format details.
661 661
662 662 It is possible to specify an ``ssh://`` URL as the destination, but no
663 663 .hg/hgrc and working directory will be created on the remote side.
664 664 Please see :hg:`help urls` for important details about ``ssh://`` URLs.
665 665
666 666 A set of changesets (tags, or branch names) to pull may be specified
667 667 by listing each changeset (tag, or branch name) with -r/--rev.
668 668 If -r/--rev is used, the cloned repository will contain only a subset
669 669 of the changesets of the source repository. Only the set of changesets
670 670 defined by all -r/--rev options (including all their ancestors)
671 671 will be pulled into the destination repository.
672 672 No subsequent changesets (including subsequent tags) will be present
673 673 in the destination.
674 674
675 675 Using -r/--rev (or 'clone src#rev dest') implies --pull, even for
676 676 local source repositories.
677 677
678 678 For efficiency, hardlinks are used for cloning whenever the source
679 679 and destination are on the same filesystem (note this applies only
680 680 to the repository data, not to the working directory). Some
681 681 filesystems, such as AFS, implement hardlinking incorrectly, but
682 682 do not report errors. In these cases, use the --pull option to
683 683 avoid hardlinking.
684 684
685 685 In some cases, you can clone repositories and the working directory
686 686 using full hardlinks with ::
687 687
688 688 $ cp -al REPO REPOCLONE
689 689
690 690 This is the fastest way to clone, but it is not always safe. The
691 691 operation is not atomic (making sure REPO is not modified during
692 692 the operation is up to you) and you have to make sure your editor
693 693 breaks hardlinks (Emacs and most Linux Kernel tools do so). Also,
694 694 this is not compatible with certain extensions that place their
695 695 metadata under the .hg directory, such as mq.
696 696
697 697 Mercurial will update the working directory to the first applicable
698 698 revision from this list:
699 699
700 700 a) null if -U or the source repository has no changesets
701 701 b) if -u . and the source repository is local, the first parent of
702 702 the source repository's working directory
703 703 c) the changeset specified with -u (if a branch name, this means the
704 704 latest head of that branch)
705 705 d) the changeset specified with -r
706 706 e) the tipmost head specified with -b
707 707 f) the tipmost head specified with the url#branch source syntax
708 708 g) the tipmost head of the default branch
709 709 h) tip
710 710
711 711 Returns 0 on success.
712 712 """
713 713 if opts.get('noupdate') and opts.get('updaterev'):
714 714 raise util.Abort(_("cannot specify both --noupdate and --updaterev"))
715 715
716 716 r = hg.clone(hg.remoteui(ui, opts), source, dest,
717 717 pull=opts.get('pull'),
718 718 stream=opts.get('uncompressed'),
719 719 rev=opts.get('rev'),
720 720 update=opts.get('updaterev') or not opts.get('noupdate'),
721 721 branch=opts.get('branch'))
722 722
723 723 return r is None
724 724
725 725 def commit(ui, repo, *pats, **opts):
726 726 """commit the specified files or all outstanding changes
727 727
728 728 Commit changes to the given files into the repository. Unlike a
729 729 centralized RCS, this operation is a local operation. See
730 730 :hg:`push` for a way to actively distribute your changes.
731 731
732 732 If a list of files is omitted, all changes reported by :hg:`status`
733 733 will be committed.
734 734
735 735 If you are committing the result of a merge, do not provide any
736 736 filenames or -I/-X filters.
737 737
738 738 If no commit message is specified, Mercurial starts your
739 739 configured editor where you can enter a message. In case your
740 740 commit fails, you will find a backup of your message in
741 741 ``.hg/last-message.txt``.
742 742
743 743 See :hg:`help dates` for a list of formats valid for -d/--date.
744 744
745 745 Returns 0 on success, 1 if nothing changed.
746 746 """
747 747 extra = {}
748 748 if opts.get('close_branch'):
749 749 if repo['.'].node() not in repo.branchheads():
750 750 # The topo heads set is included in the branch heads set of the
751 751 # current branch, so it's sufficient to test branchheads
752 752 raise util.Abort(_('can only close branch heads'))
753 753 extra['close'] = 1
754 754 e = cmdutil.commiteditor
755 755 if opts.get('force_editor'):
756 756 e = cmdutil.commitforceeditor
757 757
758 758 def commitfunc(ui, repo, message, match, opts):
759 759 return repo.commit(message, opts.get('user'), opts.get('date'), match,
760 760 editor=e, extra=extra)
761 761
762 762 branch = repo[None].branch()
763 763 bheads = repo.branchheads(branch)
764 764
765 765 node = cmdutil.commit(ui, repo, commitfunc, pats, opts)
766 766 if not node:
767 767 ui.status(_("nothing changed\n"))
768 768 return 1
769 769
770 770 ctx = repo[node]
771 771 parents = ctx.parents()
772 772
773 773 if bheads and not [x for x in parents
774 774 if x.node() in bheads and x.branch() == branch]:
775 775 ui.status(_('created new head\n'))
776 776 # The message is not printed for initial roots. For the other
777 777 # changesets, it is printed in the following situations:
778 778 #
779 779 # Par column: for the 2 parents with ...
780 780 # N: null or no parent
781 781 # B: parent is on another named branch
782 782 # C: parent is a regular non head changeset
783 783 # H: parent was a branch head of the current branch
784 784 # Msg column: whether we print "created new head" message
785 785 # In the following, it is assumed that there already exists some
786 786 # initial branch heads of the current branch, otherwise nothing is
787 787 # printed anyway.
788 788 #
789 789 # Par Msg Comment
790 790 # NN y additional topo root
791 791 #
792 792 # BN y additional branch root
793 793 # CN y additional topo head
794 794 # HN n usual case
795 795 #
796 796 # BB y weird additional branch root
797 797 # CB y branch merge
798 798 # HB n merge with named branch
799 799 #
800 800 # CC y additional head from merge
801 801 # CH n merge with a head
802 802 #
803 803 # HH n head merge: head count decreases
804 804
805 805 if not opts.get('close_branch'):
806 806 for r in parents:
807 807 if r.extra().get('close') and r.branch() == branch:
808 808 ui.status(_('reopening closed branch head %d\n') % r)
809 809
810 810 if ui.debugflag:
811 811 ui.write(_('committed changeset %d:%s\n') % (int(ctx), ctx.hex()))
812 812 elif ui.verbose:
813 813 ui.write(_('committed changeset %d:%s\n') % (int(ctx), ctx))
814 814
815 815 def copy(ui, repo, *pats, **opts):
816 816 """mark files as copied for the next commit
817 817
818 818 Mark dest as having copies of source files. If dest is a
819 819 directory, copies are put in that directory. If dest is a file,
820 820 the source must be a single file.
821 821
822 822 By default, this command copies the contents of files as they
823 823 exist in the working directory. If invoked with -A/--after, the
824 824 operation is recorded, but no copying is performed.
825 825
826 826 This command takes effect with the next commit. To undo a copy
827 827 before that, see :hg:`revert`.
828 828
829 829 Returns 0 on success, 1 if errors are encountered.
830 830 """
831 831 wlock = repo.wlock(False)
832 832 try:
833 833 return cmdutil.copy(ui, repo, pats, opts)
834 834 finally:
835 835 wlock.release()
836 836
837 837 def debugancestor(ui, repo, *args):
838 838 """find the ancestor revision of two revisions in a given index"""
839 839 if len(args) == 3:
840 840 index, rev1, rev2 = args
841 841 r = revlog.revlog(util.opener(os.getcwd(), audit=False), index)
842 842 lookup = r.lookup
843 843 elif len(args) == 2:
844 844 if not repo:
845 845 raise util.Abort(_("there is no Mercurial repository here "
846 846 "(.hg not found)"))
847 847 rev1, rev2 = args
848 848 r = repo.changelog
849 849 lookup = repo.lookup
850 850 else:
851 851 raise util.Abort(_('either two or three arguments required'))
852 852 a = r.ancestor(lookup(rev1), lookup(rev2))
853 853 ui.write("%d:%s\n" % (r.rev(a), hex(a)))
854 854
855 855 def debugbuilddag(ui, repo, text,
856 856 mergeable_file=False,
857 857 appended_file=False,
858 858 overwritten_file=False,
859 859 new_file=False):
860 860 """builds a repo with a given dag from scratch in the current empty repo
861 861
862 862 Elements:
863 863
864 864 - "+n" is a linear run of n nodes based on the current default parent
865 865 - "." is a single node based on the current default parent
866 866 - "$" resets the default parent to null (implied at the start);
867 867 otherwise the default parent is always the last node created
868 868 - "<p" sets the default parent to the backref p
869 869 - "*p" is a fork at parent p, which is a backref
870 870 - "*p1/p2" is a merge of parents p1 and p2, which are backrefs
871 871 - "/p2" is a merge of the preceding node and p2
872 872 - ":tag" defines a local tag for the preceding node
873 873 - "@branch" sets the named branch for subsequent nodes
874 874 - "!command" runs the command using your shell
875 875 - "!!my command\\n" is like "!", but to the end of the line
876 876 - "#...\\n" is a comment up to the end of the line
877 877
878 878 Whitespace between the above elements is ignored.
879 879
880 880 A backref is either
881 881
882 882 - a number n, which references the node curr-n, where curr is the current
883 883 node, or
884 884 - the name of a local tag you placed earlier using ":tag", or
885 885 - empty to denote the default parent.
886 886
887 887 All string valued-elements are either strictly alphanumeric, or must
888 888 be enclosed in double quotes ("..."), with "\\" as escape character.
889 889
890 890 Note that the --overwritten-file and --appended-file options imply the
891 891 use of "HGMERGE=internal:local" during DAG buildup.
892 892 """
893 893
894 894 if not (mergeable_file or appended_file or overwritten_file or new_file):
895 895 raise util.Abort(_('need at least one of -m, -a, -o, -n'))
896 896
897 897 if len(repo.changelog) > 0:
898 898 raise util.Abort(_('repository is not empty'))
899 899
900 900 if overwritten_file or appended_file:
901 901 # we don't want to fail in merges during buildup
902 902 os.environ['HGMERGE'] = 'internal:local'
903 903
904 904 def writefile(fname, text, fmode="wb"):
905 905 f = open(fname, fmode)
906 906 try:
907 907 f.write(text)
908 908 finally:
909 909 f.close()
910 910
911 911 if mergeable_file:
912 912 linesperrev = 2
913 913 # determine number of revs in DAG
914 914 n = 0
915 915 for type, data in dagparser.parsedag(text):
916 916 if type == 'n':
917 917 n += 1
918 918 # make a file with k lines per rev
919 919 writefile("mf", "\n".join(str(i) for i in xrange(0, n * linesperrev))
920 920 + "\n")
921 921
922 922 at = -1
923 923 atbranch = 'default'
924 924 for type, data in dagparser.parsedag(text):
925 925 if type == 'n':
926 926 ui.status('node %s\n' % str(data))
927 927 id, ps = data
928 928 p1 = ps[0]
929 929 if p1 != at:
930 930 update(ui, repo, node=p1, clean=True)
931 931 at = p1
932 932 if repo.dirstate.branch() != atbranch:
933 933 branch(ui, repo, atbranch, force=True)
934 934 if len(ps) > 1:
935 935 p2 = ps[1]
936 936 merge(ui, repo, node=p2)
937 937
938 938 if mergeable_file:
939 939 f = open("mf", "rb+")
940 940 try:
941 941 lines = f.read().split("\n")
942 942 lines[id * linesperrev] += " r%i" % id
943 943 f.seek(0)
944 944 f.write("\n".join(lines))
945 945 finally:
946 946 f.close()
947 947
948 948 if appended_file:
949 949 writefile("af", "r%i\n" % id, "ab")
950 950
951 951 if overwritten_file:
952 952 writefile("of", "r%i\n" % id)
953 953
954 954 if new_file:
955 955 writefile("nf%i" % id, "r%i\n" % id)
956 956
957 957 commit(ui, repo, addremove=True, message="r%i" % id, date=(id, 0))
958 958 at = id
959 959 elif type == 'l':
960 960 id, name = data
961 961 ui.status('tag %s\n' % name)
962 962 tag(ui, repo, name, local=True)
963 963 elif type == 'a':
964 964 ui.status('branch %s\n' % data)
965 965 atbranch = data
966 966 elif type in 'cC':
967 967 r = util.system(data, cwd=repo.root)
968 968 if r:
969 969 desc, r = util.explain_exit(r)
970 970 raise util.Abort(_('%s command %s') % (data, desc))
971 971
972 972 def debugcommands(ui, cmd='', *args):
973 973 """list all available commands and options"""
974 974 for cmd, vals in sorted(table.iteritems()):
975 975 cmd = cmd.split('|')[0].strip('^')
976 976 opts = ', '.join([i[1] for i in vals[1]])
977 977 ui.write('%s: %s\n' % (cmd, opts))
978 978
979 979 def debugcomplete(ui, cmd='', **opts):
980 980 """returns the completion list associated with the given command"""
981 981
982 982 if opts.get('options'):
983 983 options = []
984 984 otables = [globalopts]
985 985 if cmd:
986 986 aliases, entry = cmdutil.findcmd(cmd, table, False)
987 987 otables.append(entry[1])
988 988 for t in otables:
989 989 for o in t:
990 990 if "(DEPRECATED)" in o[3]:
991 991 continue
992 992 if o[0]:
993 993 options.append('-%s' % o[0])
994 994 options.append('--%s' % o[1])
995 995 ui.write("%s\n" % "\n".join(options))
996 996 return
997 997
998 998 cmdlist = cmdutil.findpossible(cmd, table)
999 999 if ui.verbose:
1000 1000 cmdlist = [' '.join(c[0]) for c in cmdlist.values()]
1001 1001 ui.write("%s\n" % "\n".join(sorted(cmdlist)))
1002 1002
1003 1003 def debugfsinfo(ui, path = "."):
1004 1004 """show information detected about current filesystem"""
1005 1005 open('.debugfsinfo', 'w').write('')
1006 1006 ui.write('exec: %s\n' % (util.checkexec(path) and 'yes' or 'no'))
1007 1007 ui.write('symlink: %s\n' % (util.checklink(path) and 'yes' or 'no'))
1008 1008 ui.write('case-sensitive: %s\n' % (util.checkcase('.debugfsinfo')
1009 1009 and 'yes' or 'no'))
1010 1010 os.unlink('.debugfsinfo')
1011 1011
1012 1012 def debugrebuildstate(ui, repo, rev="tip"):
1013 1013 """rebuild the dirstate as it would look like for the given revision"""
1014 1014 ctx = repo[rev]
1015 1015 wlock = repo.wlock()
1016 1016 try:
1017 1017 repo.dirstate.rebuild(ctx.node(), ctx.manifest())
1018 1018 finally:
1019 1019 wlock.release()
1020 1020
1021 1021 def debugcheckstate(ui, repo):
1022 1022 """validate the correctness of the current dirstate"""
1023 1023 parent1, parent2 = repo.dirstate.parents()
1024 1024 m1 = repo[parent1].manifest()
1025 1025 m2 = repo[parent2].manifest()
1026 1026 errors = 0
1027 1027 for f in repo.dirstate:
1028 1028 state = repo.dirstate[f]
1029 1029 if state in "nr" and f not in m1:
1030 1030 ui.warn(_("%s in state %s, but not in manifest1\n") % (f, state))
1031 1031 errors += 1
1032 1032 if state in "a" and f in m1:
1033 1033 ui.warn(_("%s in state %s, but also in manifest1\n") % (f, state))
1034 1034 errors += 1
1035 1035 if state in "m" and f not in m1 and f not in m2:
1036 1036 ui.warn(_("%s in state %s, but not in either manifest\n") %
1037 1037 (f, state))
1038 1038 errors += 1
1039 1039 for f in m1:
1040 1040 state = repo.dirstate[f]
1041 1041 if state not in "nrm":
1042 1042 ui.warn(_("%s in manifest1, but listed as state %s") % (f, state))
1043 1043 errors += 1
1044 1044 if errors:
1045 1045 error = _(".hg/dirstate inconsistent with current parent's manifest")
1046 1046 raise util.Abort(error)
1047 1047
1048 1048 def showconfig(ui, repo, *values, **opts):
1049 1049 """show combined config settings from all hgrc files
1050 1050
1051 1051 With no arguments, print names and values of all config items.
1052 1052
1053 1053 With one argument of the form section.name, print just the value
1054 1054 of that config item.
1055 1055
1056 1056 With multiple arguments, print names and values of all config
1057 1057 items with matching section names.
1058 1058
1059 1059 With --debug, the source (filename and line number) is printed
1060 1060 for each config item.
1061 1061
1062 1062 Returns 0 on success.
1063 1063 """
1064 1064
1065 1065 for f in util.rcpath():
1066 1066 ui.debug(_('read config from: %s\n') % f)
1067 1067 untrusted = bool(opts.get('untrusted'))
1068 1068 if values:
1069 1069 sections = [v for v in values if '.' not in v]
1070 1070 items = [v for v in values if '.' in v]
1071 1071 if len(items) > 1 or items and sections:
1072 1072 raise util.Abort(_('only one config item permitted'))
1073 1073 for section, name, value in ui.walkconfig(untrusted=untrusted):
1074 1074 sectname = section + '.' + name
1075 1075 if values:
1076 1076 for v in values:
1077 1077 if v == section:
1078 1078 ui.debug('%s: ' %
1079 1079 ui.configsource(section, name, untrusted))
1080 1080 ui.write('%s=%s\n' % (sectname, value))
1081 1081 elif v == sectname:
1082 1082 ui.debug('%s: ' %
1083 1083 ui.configsource(section, name, untrusted))
1084 1084 ui.write(value, '\n')
1085 1085 else:
1086 1086 ui.debug('%s: ' %
1087 1087 ui.configsource(section, name, untrusted))
1088 1088 ui.write('%s=%s\n' % (sectname, value))
1089 1089
1090 1090 def debugpushkey(ui, repopath, namespace, *keyinfo):
1091 1091 '''access the pushkey key/value protocol
1092 1092
1093 1093 With two args, list the keys in the given namespace.
1094 1094
1095 1095 With five args, set a key to new if it currently is set to old.
1096 1096 Reports success or failure.
1097 1097 '''
1098 1098
1099 1099 target = hg.repository(ui, repopath)
1100 1100 if keyinfo:
1101 1101 key, old, new = keyinfo
1102 1102 r = target.pushkey(namespace, key, old, new)
1103 1103 ui.status(str(r) + '\n')
1104 1104 return not(r)
1105 1105 else:
1106 1106 for k, v in target.listkeys(namespace).iteritems():
1107 1107 ui.write("%s\t%s\n" % (k.encode('string-escape'),
1108 1108 v.encode('string-escape')))
1109 1109
1110 1110 def debugrevspec(ui, repo, expr):
1111 1111 '''parse and apply a revision specification'''
1112 1112 if ui.verbose:
1113 1113 tree = revset.parse(expr)
1114 1114 ui.note(tree, "\n")
1115 1115 func = revset.match(expr)
1116 1116 for c in func(repo, range(len(repo))):
1117 1117 ui.write("%s\n" % c)
1118 1118
1119 1119 def debugsetparents(ui, repo, rev1, rev2=None):
1120 1120 """manually set the parents of the current working directory
1121 1121
1122 1122 This is useful for writing repository conversion tools, but should
1123 1123 be used with care.
1124 1124
1125 1125 Returns 0 on success.
1126 1126 """
1127 1127
1128 1128 if not rev2:
1129 1129 rev2 = hex(nullid)
1130 1130
1131 1131 wlock = repo.wlock()
1132 1132 try:
1133 1133 repo.dirstate.setparents(repo.lookup(rev1), repo.lookup(rev2))
1134 1134 finally:
1135 1135 wlock.release()
1136 1136
1137 1137 def debugstate(ui, repo, nodates=None):
1138 1138 """show the contents of the current dirstate"""
1139 1139 timestr = ""
1140 1140 showdate = not nodates
1141 1141 for file_, ent in sorted(repo.dirstate._map.iteritems()):
1142 1142 if showdate:
1143 1143 if ent[3] == -1:
1144 1144 # Pad or slice to locale representation
1145 1145 locale_len = len(time.strftime("%Y-%m-%d %H:%M:%S ",
1146 1146 time.localtime(0)))
1147 1147 timestr = 'unset'
1148 1148 timestr = (timestr[:locale_len] +
1149 1149 ' ' * (locale_len - len(timestr)))
1150 1150 else:
1151 1151 timestr = time.strftime("%Y-%m-%d %H:%M:%S ",
1152 1152 time.localtime(ent[3]))
1153 1153 if ent[1] & 020000:
1154 1154 mode = 'lnk'
1155 1155 else:
1156 1156 mode = '%3o' % (ent[1] & 0777)
1157 1157 ui.write("%c %s %10d %s%s\n" % (ent[0], mode, ent[2], timestr, file_))
1158 1158 for f in repo.dirstate.copies():
1159 1159 ui.write(_("copy: %s -> %s\n") % (repo.dirstate.copied(f), f))
1160 1160
1161 1161 def debugsub(ui, repo, rev=None):
1162 1162 if rev == '':
1163 1163 rev = None
1164 1164 for k, v in sorted(repo[rev].substate.items()):
1165 1165 ui.write('path %s\n' % k)
1166 1166 ui.write(' source %s\n' % v[0])
1167 1167 ui.write(' revision %s\n' % v[1])
1168 1168
1169 1169 def debugdag(ui, repo, file_=None, *revs, **opts):
1170 1170 """format the changelog or an index DAG as a concise textual description
1171 1171
1172 1172 If you pass a revlog index, the revlog's DAG is emitted. If you list
1173 1173 revision numbers, they get labelled in the output as rN.
1174 1174
1175 1175 Otherwise, the changelog DAG of the current repo is emitted.
1176 1176 """
1177 1177 spaces = opts.get('spaces')
1178 1178 dots = opts.get('dots')
1179 1179 if file_:
1180 1180 rlog = revlog.revlog(util.opener(os.getcwd(), audit=False), file_)
1181 1181 revs = set((int(r) for r in revs))
1182 1182 def events():
1183 1183 for r in rlog:
1184 1184 yield 'n', (r, list(set(p for p in rlog.parentrevs(r) if p != -1)))
1185 1185 if r in revs:
1186 1186 yield 'l', (r, "r%i" % r)
1187 1187 elif repo:
1188 1188 cl = repo.changelog
1189 1189 tags = opts.get('tags')
1190 1190 branches = opts.get('branches')
1191 1191 if tags:
1192 1192 labels = {}
1193 1193 for l, n in repo.tags().items():
1194 1194 labels.setdefault(cl.rev(n), []).append(l)
1195 1195 def events():
1196 1196 b = "default"
1197 1197 for r in cl:
1198 1198 if branches:
1199 1199 newb = cl.read(cl.node(r))[5]['branch']
1200 1200 if newb != b:
1201 1201 yield 'a', newb
1202 1202 b = newb
1203 1203 yield 'n', (r, list(set(p for p in cl.parentrevs(r) if p != -1)))
1204 1204 if tags:
1205 1205 ls = labels.get(r)
1206 1206 if ls:
1207 1207 for l in ls:
1208 1208 yield 'l', (r, l)
1209 1209 else:
1210 1210 raise util.Abort(_('need repo for changelog dag'))
1211 1211
1212 1212 for line in dagparser.dagtextlines(events(),
1213 1213 addspaces=spaces,
1214 1214 wraplabels=True,
1215 1215 wrapannotations=True,
1216 1216 wrapnonlinear=dots,
1217 1217 usedots=dots,
1218 1218 maxlinewidth=70):
1219 1219 ui.write(line)
1220 1220 ui.write("\n")
1221 1221
1222 1222 def debugdata(ui, repo, file_, rev):
1223 1223 """dump the contents of a data file revision"""
1224 1224 r = None
1225 1225 if repo:
1226 1226 filelog = repo.file(file_)
1227 1227 if len(filelog):
1228 1228 r = filelog
1229 1229 if not r:
1230 1230 r = revlog.revlog(util.opener(os.getcwd(), audit=False), file_[:-2] + ".i")
1231 1231 try:
1232 1232 ui.write(r.revision(r.lookup(rev)))
1233 1233 except KeyError:
1234 1234 raise util.Abort(_('invalid revision identifier %s') % rev)
1235 1235
1236 1236 def debugdate(ui, date, range=None, **opts):
1237 1237 """parse and display a date"""
1238 1238 if opts["extended"]:
1239 1239 d = util.parsedate(date, util.extendeddateformats)
1240 1240 else:
1241 1241 d = util.parsedate(date)
1242 1242 ui.write("internal: %s %s\n" % d)
1243 1243 ui.write("standard: %s\n" % util.datestr(d))
1244 1244 if range:
1245 1245 m = util.matchdate(range)
1246 1246 ui.write("match: %s\n" % m(d[0]))
1247 1247
1248 1248 def debugindex(ui, repo, file_):
1249 1249 """dump the contents of an index file"""
1250 1250 r = None
1251 1251 if repo:
1252 1252 filelog = repo.file(file_)
1253 1253 if len(filelog):
1254 1254 r = filelog
1255 1255 if not r:
1256 1256 r = revlog.revlog(util.opener(os.getcwd(), audit=False), file_)
1257 1257 ui.write(" rev offset length base linkrev"
1258 1258 " nodeid p1 p2\n")
1259 1259 for i in r:
1260 1260 node = r.node(i)
1261 1261 try:
1262 1262 pp = r.parents(node)
1263 1263 except:
1264 1264 pp = [nullid, nullid]
1265 1265 ui.write("% 6d % 9d % 7d % 6d % 7d %s %s %s\n" % (
1266 1266 i, r.start(i), r.length(i), r.base(i), r.linkrev(i),
1267 1267 short(node), short(pp[0]), short(pp[1])))
1268 1268
1269 1269 def debugindexdot(ui, repo, file_):
1270 1270 """dump an index DAG as a graphviz dot file"""
1271 1271 r = None
1272 1272 if repo:
1273 1273 filelog = repo.file(file_)
1274 1274 if len(filelog):
1275 1275 r = filelog
1276 1276 if not r:
1277 1277 r = revlog.revlog(util.opener(os.getcwd(), audit=False), file_)
1278 1278 ui.write("digraph G {\n")
1279 1279 for i in r:
1280 1280 node = r.node(i)
1281 1281 pp = r.parents(node)
1282 1282 ui.write("\t%d -> %d\n" % (r.rev(pp[0]), i))
1283 1283 if pp[1] != nullid:
1284 1284 ui.write("\t%d -> %d\n" % (r.rev(pp[1]), i))
1285 1285 ui.write("}\n")
1286 1286
1287 1287 def debuginstall(ui):
1288 1288 '''test Mercurial installation
1289 1289
1290 1290 Returns 0 on success.
1291 1291 '''
1292 1292
1293 1293 def writetemp(contents):
1294 1294 (fd, name) = tempfile.mkstemp(prefix="hg-debuginstall-")
1295 1295 f = os.fdopen(fd, "wb")
1296 1296 f.write(contents)
1297 1297 f.close()
1298 1298 return name
1299 1299
1300 1300 problems = 0
1301 1301
1302 1302 # encoding
1303 1303 ui.status(_("Checking encoding (%s)...\n") % encoding.encoding)
1304 1304 try:
1305 1305 encoding.fromlocal("test")
1306 1306 except util.Abort, inst:
1307 1307 ui.write(" %s\n" % inst)
1308 1308 ui.write(_(" (check that your locale is properly set)\n"))
1309 1309 problems += 1
1310 1310
1311 1311 # compiled modules
1312 1312 ui.status(_("Checking installed modules (%s)...\n")
1313 1313 % os.path.dirname(__file__))
1314 1314 try:
1315 1315 import bdiff, mpatch, base85, osutil
1316 1316 except Exception, inst:
1317 1317 ui.write(" %s\n" % inst)
1318 1318 ui.write(_(" One or more extensions could not be found"))
1319 1319 ui.write(_(" (check that you compiled the extensions)\n"))
1320 1320 problems += 1
1321 1321
1322 1322 # templates
1323 1323 ui.status(_("Checking templates...\n"))
1324 1324 try:
1325 1325 import templater
1326 1326 templater.templater(templater.templatepath("map-cmdline.default"))
1327 1327 except Exception, inst:
1328 1328 ui.write(" %s\n" % inst)
1329 1329 ui.write(_(" (templates seem to have been installed incorrectly)\n"))
1330 1330 problems += 1
1331 1331
1332 1332 # patch
1333 1333 ui.status(_("Checking patch...\n"))
1334 1334 patchproblems = 0
1335 1335 a = "1\n2\n3\n4\n"
1336 1336 b = "1\n2\n3\ninsert\n4\n"
1337 1337 fa = writetemp(a)
1338 1338 d = mdiff.unidiff(a, None, b, None, os.path.basename(fa),
1339 1339 os.path.basename(fa))
1340 1340 fd = writetemp(d)
1341 1341
1342 1342 files = {}
1343 1343 try:
1344 1344 patch.patch(fd, ui, cwd=os.path.dirname(fa), files=files)
1345 1345 except util.Abort, e:
1346 1346 ui.write(_(" patch call failed:\n"))
1347 1347 ui.write(" " + str(e) + "\n")
1348 1348 patchproblems += 1
1349 1349 else:
1350 1350 if list(files) != [os.path.basename(fa)]:
1351 1351 ui.write(_(" unexpected patch output!\n"))
1352 1352 patchproblems += 1
1353 1353 a = open(fa).read()
1354 1354 if a != b:
1355 1355 ui.write(_(" patch test failed!\n"))
1356 1356 patchproblems += 1
1357 1357
1358 1358 if patchproblems:
1359 1359 if ui.config('ui', 'patch'):
1360 1360 ui.write(_(" (Current patch tool may be incompatible with patch,"
1361 1361 " or misconfigured. Please check your configuration"
1362 1362 " file)\n"))
1363 1363 else:
1364 1364 ui.write(_(" Internal patcher failure, please report this error"
1365 1365 " to http://mercurial.selenic.com/bts/\n"))
1366 1366 problems += patchproblems
1367 1367
1368 1368 os.unlink(fa)
1369 1369 os.unlink(fd)
1370 1370
1371 1371 # editor
1372 1372 ui.status(_("Checking commit editor...\n"))
1373 1373 editor = ui.geteditor()
1374 1374 cmdpath = util.find_exe(editor) or util.find_exe(editor.split()[0])
1375 1375 if not cmdpath:
1376 1376 if editor == 'vi':
1377 1377 ui.write(_(" No commit editor set and can't find vi in PATH\n"))
1378 1378 ui.write(_(" (specify a commit editor in your configuration"
1379 1379 " file)\n"))
1380 1380 else:
1381 1381 ui.write(_(" Can't find editor '%s' in PATH\n") % editor)
1382 1382 ui.write(_(" (specify a commit editor in your configuration"
1383 1383 " file)\n"))
1384 1384 problems += 1
1385 1385
1386 1386 # check username
1387 1387 ui.status(_("Checking username...\n"))
1388 1388 try:
1389 1389 ui.username()
1390 1390 except util.Abort, e:
1391 1391 ui.write(" %s\n" % e)
1392 1392 ui.write(_(" (specify a username in your configuration file)\n"))
1393 1393 problems += 1
1394 1394
1395 1395 if not problems:
1396 1396 ui.status(_("No problems detected\n"))
1397 1397 else:
1398 1398 ui.write(_("%s problems detected,"
1399 1399 " please check your install!\n") % problems)
1400 1400
1401 1401 return problems
1402 1402
1403 1403 def debugrename(ui, repo, file1, *pats, **opts):
1404 1404 """dump rename information"""
1405 1405
1406 1406 ctx = repo[opts.get('rev')]
1407 1407 m = cmdutil.match(repo, (file1,) + pats, opts)
1408 1408 for abs in ctx.walk(m):
1409 1409 fctx = ctx[abs]
1410 1410 o = fctx.filelog().renamed(fctx.filenode())
1411 1411 rel = m.rel(abs)
1412 1412 if o:
1413 1413 ui.write(_("%s renamed from %s:%s\n") % (rel, o[0], hex(o[1])))
1414 1414 else:
1415 1415 ui.write(_("%s not renamed\n") % rel)
1416 1416
1417 1417 def debugwalk(ui, repo, *pats, **opts):
1418 1418 """show how files match on given patterns"""
1419 1419 m = cmdutil.match(repo, pats, opts)
1420 1420 items = list(repo.walk(m))
1421 1421 if not items:
1422 1422 return
1423 1423 fmt = 'f %%-%ds %%-%ds %%s' % (
1424 1424 max([len(abs) for abs in items]),
1425 1425 max([len(m.rel(abs)) for abs in items]))
1426 1426 for abs in items:
1427 1427 line = fmt % (abs, m.rel(abs), m.exact(abs) and 'exact' or '')
1428 1428 ui.write("%s\n" % line.rstrip())
1429 1429
1430 1430 def diff(ui, repo, *pats, **opts):
1431 1431 """diff repository (or selected files)
1432 1432
1433 1433 Show differences between revisions for the specified files.
1434 1434
1435 1435 Differences between files are shown using the unified diff format.
1436 1436
1437 1437 .. note::
1438 1438 diff may generate unexpected results for merges, as it will
1439 1439 default to comparing against the working directory's first
1440 1440 parent changeset if no revisions are specified.
1441 1441
1442 1442 When two revision arguments are given, then changes are shown
1443 1443 between those revisions. If only one revision is specified then
1444 1444 that revision is compared to the working directory, and, when no
1445 1445 revisions are specified, the working directory files are compared
1446 1446 to its parent.
1447 1447
1448 1448 Alternatively you can specify -c/--change with a revision to see
1449 1449 the changes in that changeset relative to its first parent.
1450 1450
1451 1451 Without the -a/--text option, diff will avoid generating diffs of
1452 1452 files it detects as binary. With -a, diff will generate a diff
1453 1453 anyway, probably with undesirable results.
1454 1454
1455 1455 Use the -g/--git option to generate diffs in the git extended diff
1456 1456 format. For more information, read :hg:`help diffs`.
1457 1457
1458 1458 Returns 0 on success.
1459 1459 """
1460 1460
1461 1461 revs = opts.get('rev')
1462 1462 change = opts.get('change')
1463 1463 stat = opts.get('stat')
1464 1464 reverse = opts.get('reverse')
1465 1465
1466 1466 if revs and change:
1467 1467 msg = _('cannot specify --rev and --change at the same time')
1468 1468 raise util.Abort(msg)
1469 1469 elif change:
1470 1470 node2 = repo.lookup(change)
1471 1471 node1 = repo[node2].parents()[0].node()
1472 1472 else:
1473 1473 node1, node2 = cmdutil.revpair(repo, revs)
1474 1474
1475 1475 if reverse:
1476 1476 node1, node2 = node2, node1
1477 1477
1478 1478 diffopts = patch.diffopts(ui, opts)
1479 1479 m = cmdutil.match(repo, pats, opts)
1480 1480 cmdutil.diffordiffstat(ui, repo, diffopts, node1, node2, m, stat=stat,
1481 1481 listsubrepos=opts.get('subrepos'))
1482 1482
1483 1483 def export(ui, repo, *changesets, **opts):
1484 1484 """dump the header and diffs for one or more changesets
1485 1485
1486 1486 Print the changeset header and diffs for one or more revisions.
1487 1487
1488 1488 The information shown in the changeset header is: author, date,
1489 1489 branch name (if non-default), changeset hash, parent(s) and commit
1490 1490 comment.
1491 1491
1492 1492 .. note::
1493 1493 export may generate unexpected diff output for merge
1494 1494 changesets, as it will compare the merge changeset against its
1495 1495 first parent only.
1496 1496
1497 1497 Output may be to a file, in which case the name of the file is
1498 1498 given using a format string. The formatting rules are as follows:
1499 1499
1500 1500 :``%%``: literal "%" character
1501 1501 :``%H``: changeset hash (40 hexadecimal digits)
1502 1502 :``%N``: number of patches being generated
1503 1503 :``%R``: changeset revision number
1504 1504 :``%b``: basename of the exporting repository
1505 1505 :``%h``: short-form changeset hash (12 hexadecimal digits)
1506 1506 :``%n``: zero-padded sequence number, starting at 1
1507 1507 :``%r``: zero-padded changeset revision number
1508 1508
1509 1509 Without the -a/--text option, export will avoid generating diffs
1510 1510 of files it detects as binary. With -a, export will generate a
1511 1511 diff anyway, probably with undesirable results.
1512 1512
1513 1513 Use the -g/--git option to generate diffs in the git extended diff
1514 1514 format. See :hg:`help diffs` for more information.
1515 1515
1516 1516 With the --switch-parent option, the diff will be against the
1517 1517 second parent. It can be useful to review a merge.
1518 1518
1519 1519 Returns 0 on success.
1520 1520 """
1521 1521 changesets += tuple(opts.get('rev', []))
1522 1522 if not changesets:
1523 1523 raise util.Abort(_("export requires at least one changeset"))
1524 1524 revs = cmdutil.revrange(repo, changesets)
1525 1525 if len(revs) > 1:
1526 1526 ui.note(_('exporting patches:\n'))
1527 1527 else:
1528 1528 ui.note(_('exporting patch:\n'))
1529 1529 cmdutil.export(repo, revs, template=opts.get('output'),
1530 1530 switch_parent=opts.get('switch_parent'),
1531 1531 opts=patch.diffopts(ui, opts))
1532 1532
1533 1533 def forget(ui, repo, *pats, **opts):
1534 1534 """forget the specified files on the next commit
1535 1535
1536 1536 Mark the specified files so they will no longer be tracked
1537 1537 after the next commit.
1538 1538
1539 1539 This only removes files from the current branch, not from the
1540 1540 entire project history, and it does not delete them from the
1541 1541 working directory.
1542 1542
1543 1543 To undo a forget before the next commit, see :hg:`add`.
1544 1544
1545 1545 Returns 0 on success.
1546 1546 """
1547 1547
1548 1548 if not pats:
1549 1549 raise util.Abort(_('no files specified'))
1550 1550
1551 1551 m = cmdutil.match(repo, pats, opts)
1552 1552 s = repo.status(match=m, clean=True)
1553 1553 forget = sorted(s[0] + s[1] + s[3] + s[6])
1554 1554 errs = 0
1555 1555
1556 1556 for f in m.files():
1557 1557 if f not in repo.dirstate and not os.path.isdir(m.rel(f)):
1558 1558 ui.warn(_('not removing %s: file is already untracked\n')
1559 1559 % m.rel(f))
1560 1560 errs = 1
1561 1561
1562 1562 for f in forget:
1563 1563 if ui.verbose or not m.exact(f):
1564 1564 ui.status(_('removing %s\n') % m.rel(f))
1565 1565
1566 1566 repo[None].remove(forget, unlink=False)
1567 1567 return errs
1568 1568
1569 1569 def grep(ui, repo, pattern, *pats, **opts):
1570 1570 """search for a pattern in specified files and revisions
1571 1571
1572 1572 Search revisions of files for a regular expression.
1573 1573
1574 1574 This command behaves differently than Unix grep. It only accepts
1575 1575 Python/Perl regexps. It searches repository history, not the
1576 1576 working directory. It always prints the revision number in which a
1577 1577 match appears.
1578 1578
1579 1579 By default, grep only prints output for the first revision of a
1580 1580 file in which it finds a match. To get it to print every revision
1581 1581 that contains a change in match status ("-" for a match that
1582 1582 becomes a non-match, or "+" for a non-match that becomes a match),
1583 1583 use the --all flag.
1584 1584
1585 1585 Returns 0 if a match is found, 1 otherwise.
1586 1586 """
1587 1587 reflags = 0
1588 1588 if opts.get('ignore_case'):
1589 1589 reflags |= re.I
1590 1590 try:
1591 1591 regexp = re.compile(pattern, reflags)
1592 1592 except re.error, inst:
1593 1593 ui.warn(_("grep: invalid match pattern: %s\n") % inst)
1594 1594 return 1
1595 1595 sep, eol = ':', '\n'
1596 1596 if opts.get('print0'):
1597 1597 sep = eol = '\0'
1598 1598
1599 1599 getfile = util.lrucachefunc(repo.file)
1600 1600
1601 1601 def matchlines(body):
1602 1602 begin = 0
1603 1603 linenum = 0
1604 1604 while True:
1605 1605 match = regexp.search(body, begin)
1606 1606 if not match:
1607 1607 break
1608 1608 mstart, mend = match.span()
1609 1609 linenum += body.count('\n', begin, mstart) + 1
1610 1610 lstart = body.rfind('\n', begin, mstart) + 1 or begin
1611 1611 begin = body.find('\n', mend) + 1 or len(body)
1612 1612 lend = begin - 1
1613 1613 yield linenum, mstart - lstart, mend - lstart, body[lstart:lend]
1614 1614
1615 1615 class linestate(object):
1616 1616 def __init__(self, line, linenum, colstart, colend):
1617 1617 self.line = line
1618 1618 self.linenum = linenum
1619 1619 self.colstart = colstart
1620 1620 self.colend = colend
1621 1621
1622 1622 def __hash__(self):
1623 1623 return hash((self.linenum, self.line))
1624 1624
1625 1625 def __eq__(self, other):
1626 1626 return self.line == other.line
1627 1627
1628 1628 matches = {}
1629 1629 copies = {}
1630 1630 def grepbody(fn, rev, body):
1631 1631 matches[rev].setdefault(fn, [])
1632 1632 m = matches[rev][fn]
1633 1633 for lnum, cstart, cend, line in matchlines(body):
1634 1634 s = linestate(line, lnum, cstart, cend)
1635 1635 m.append(s)
1636 1636
1637 1637 def difflinestates(a, b):
1638 1638 sm = difflib.SequenceMatcher(None, a, b)
1639 1639 for tag, alo, ahi, blo, bhi in sm.get_opcodes():
1640 1640 if tag == 'insert':
1641 1641 for i in xrange(blo, bhi):
1642 1642 yield ('+', b[i])
1643 1643 elif tag == 'delete':
1644 1644 for i in xrange(alo, ahi):
1645 1645 yield ('-', a[i])
1646 1646 elif tag == 'replace':
1647 1647 for i in xrange(alo, ahi):
1648 1648 yield ('-', a[i])
1649 1649 for i in xrange(blo, bhi):
1650 1650 yield ('+', b[i])
1651 1651
1652 1652 def display(fn, ctx, pstates, states):
1653 1653 rev = ctx.rev()
1654 1654 datefunc = ui.quiet and util.shortdate or util.datestr
1655 1655 found = False
1656 1656 filerevmatches = {}
1657 1657 if opts.get('all'):
1658 1658 iter = difflinestates(pstates, states)
1659 1659 else:
1660 1660 iter = [('', l) for l in states]
1661 1661 for change, l in iter:
1662 1662 cols = [fn, str(rev)]
1663 1663 before, match, after = None, None, None
1664 1664 if opts.get('line_number'):
1665 1665 cols.append(str(l.linenum))
1666 1666 if opts.get('all'):
1667 1667 cols.append(change)
1668 1668 if opts.get('user'):
1669 1669 cols.append(ui.shortuser(ctx.user()))
1670 1670 if opts.get('date'):
1671 1671 cols.append(datefunc(ctx.date()))
1672 1672 if opts.get('files_with_matches'):
1673 1673 c = (fn, rev)
1674 1674 if c in filerevmatches:
1675 1675 continue
1676 1676 filerevmatches[c] = 1
1677 1677 else:
1678 1678 before = l.line[:l.colstart]
1679 1679 match = l.line[l.colstart:l.colend]
1680 1680 after = l.line[l.colend:]
1681 1681 ui.write(sep.join(cols))
1682 1682 if before is not None:
1683 1683 ui.write(sep + before)
1684 1684 ui.write(match, label='grep.match')
1685 1685 ui.write(after)
1686 1686 ui.write(eol)
1687 1687 found = True
1688 1688 return found
1689 1689
1690 1690 skip = {}
1691 1691 revfiles = {}
1692 1692 matchfn = cmdutil.match(repo, pats, opts)
1693 1693 found = False
1694 1694 follow = opts.get('follow')
1695 1695
1696 1696 def prep(ctx, fns):
1697 1697 rev = ctx.rev()
1698 1698 pctx = ctx.parents()[0]
1699 1699 parent = pctx.rev()
1700 1700 matches.setdefault(rev, {})
1701 1701 matches.setdefault(parent, {})
1702 1702 files = revfiles.setdefault(rev, [])
1703 1703 for fn in fns:
1704 1704 flog = getfile(fn)
1705 1705 try:
1706 1706 fnode = ctx.filenode(fn)
1707 1707 except error.LookupError:
1708 1708 continue
1709 1709
1710 1710 copied = flog.renamed(fnode)
1711 1711 copy = follow and copied and copied[0]
1712 1712 if copy:
1713 1713 copies.setdefault(rev, {})[fn] = copy
1714 1714 if fn in skip:
1715 1715 if copy:
1716 1716 skip[copy] = True
1717 1717 continue
1718 1718 files.append(fn)
1719 1719
1720 1720 if fn not in matches[rev]:
1721 1721 grepbody(fn, rev, flog.read(fnode))
1722 1722
1723 1723 pfn = copy or fn
1724 1724 if pfn not in matches[parent]:
1725 1725 try:
1726 1726 fnode = pctx.filenode(pfn)
1727 1727 grepbody(pfn, parent, flog.read(fnode))
1728 1728 except error.LookupError:
1729 1729 pass
1730 1730
1731 1731 for ctx in cmdutil.walkchangerevs(repo, matchfn, opts, prep):
1732 1732 rev = ctx.rev()
1733 1733 parent = ctx.parents()[0].rev()
1734 1734 for fn in sorted(revfiles.get(rev, [])):
1735 1735 states = matches[rev][fn]
1736 1736 copy = copies.get(rev, {}).get(fn)
1737 1737 if fn in skip:
1738 1738 if copy:
1739 1739 skip[copy] = True
1740 1740 continue
1741 1741 pstates = matches.get(parent, {}).get(copy or fn, [])
1742 1742 if pstates or states:
1743 1743 r = display(fn, ctx, pstates, states)
1744 1744 found = found or r
1745 1745 if r and not opts.get('all'):
1746 1746 skip[fn] = True
1747 1747 if copy:
1748 1748 skip[copy] = True
1749 1749 del matches[rev]
1750 1750 del revfiles[rev]
1751 1751
1752 1752 return not found
1753 1753
1754 1754 def heads(ui, repo, *branchrevs, **opts):
1755 1755 """show current repository heads or show branch heads
1756 1756
1757 1757 With no arguments, show all repository branch heads.
1758 1758
1759 1759 Repository "heads" are changesets with no child changesets. They are
1760 1760 where development generally takes place and are the usual targets
1761 1761 for update and merge operations. Branch heads are changesets that have
1762 1762 no child changeset on the same branch.
1763 1763
1764 1764 If one or more REVs are given, only branch heads on the branches
1765 1765 associated with the specified changesets are shown.
1766 1766
1767 1767 If -c/--closed is specified, also show branch heads marked closed
1768 1768 (see :hg:`commit --close-branch`).
1769 1769
1770 1770 If STARTREV is specified, only those heads that are descendants of
1771 1771 STARTREV will be displayed.
1772 1772
1773 1773 If -t/--topo is specified, named branch mechanics will be ignored and only
1774 1774 changesets without children will be shown.
1775 1775
1776 1776 Returns 0 if matching heads are found, 1 if not.
1777 1777 """
1778 1778
1779 1779 if opts.get('rev'):
1780 1780 start = repo.lookup(opts['rev'])
1781 1781 else:
1782 1782 start = None
1783 1783
1784 1784 if opts.get('topo'):
1785 1785 heads = [repo[h] for h in repo.heads(start)]
1786 1786 else:
1787 1787 heads = []
1788 1788 for b, ls in repo.branchmap().iteritems():
1789 1789 if start is None:
1790 1790 heads += [repo[h] for h in ls]
1791 1791 continue
1792 1792 startrev = repo.changelog.rev(start)
1793 1793 descendants = set(repo.changelog.descendants(startrev))
1794 1794 descendants.add(startrev)
1795 1795 rev = repo.changelog.rev
1796 1796 heads += [repo[h] for h in ls if rev(h) in descendants]
1797 1797
1798 1798 if branchrevs:
1799 1799 decode, encode = encoding.fromlocal, encoding.tolocal
1800 1800 branches = set(repo[decode(br)].branch() for br in branchrevs)
1801 1801 heads = [h for h in heads if h.branch() in branches]
1802 1802
1803 1803 if not opts.get('closed'):
1804 1804 heads = [h for h in heads if not h.extra().get('close')]
1805 1805
1806 1806 if opts.get('active') and branchrevs:
1807 1807 dagheads = repo.heads(start)
1808 1808 heads = [h for h in heads if h.node() in dagheads]
1809 1809
1810 1810 if branchrevs:
1811 1811 haveheads = set(h.branch() for h in heads)
1812 1812 if branches - haveheads:
1813 1813 headless = ', '.join(encode(b) for b in branches - haveheads)
1814 1814 msg = _('no open branch heads found on branches %s')
1815 1815 if opts.get('rev'):
1816 1816 msg += _(' (started at %s)' % opts['rev'])
1817 1817 ui.warn((msg + '\n') % headless)
1818 1818
1819 1819 if not heads:
1820 1820 return 1
1821 1821
1822 1822 heads = sorted(heads, key=lambda x: -x.rev())
1823 1823 displayer = cmdutil.show_changeset(ui, repo, opts)
1824 1824 for ctx in heads:
1825 1825 displayer.show(ctx)
1826 1826 displayer.close()
1827 1827
1828 1828 def help_(ui, name=None, with_version=False, unknowncmd=False):
1829 1829 """show help for a given topic or a help overview
1830 1830
1831 1831 With no arguments, print a list of commands with short help messages.
1832 1832
1833 1833 Given a topic, extension, or command name, print help for that
1834 1834 topic.
1835 1835
1836 1836 Returns 0 if successful.
1837 1837 """
1838 1838 option_lists = []
1839 1839 textwidth = ui.termwidth() - 2
1840 1840
1841 1841 def addglobalopts(aliases):
1842 1842 if ui.verbose:
1843 1843 option_lists.append((_("global options:"), globalopts))
1844 1844 if name == 'shortlist':
1845 1845 option_lists.append((_('use "hg help" for the full list '
1846 1846 'of commands'), ()))
1847 1847 else:
1848 1848 if name == 'shortlist':
1849 1849 msg = _('use "hg help" for the full list of commands '
1850 1850 'or "hg -v" for details')
1851 1851 elif aliases:
1852 1852 msg = _('use "hg -v help%s" to show aliases and '
1853 1853 'global options') % (name and " " + name or "")
1854 1854 else:
1855 1855 msg = _('use "hg -v help %s" to show global options') % name
1856 1856 option_lists.append((msg, ()))
1857 1857
1858 1858 def helpcmd(name):
1859 1859 if with_version:
1860 1860 version_(ui)
1861 1861 ui.write('\n')
1862 1862
1863 1863 try:
1864 1864 aliases, entry = cmdutil.findcmd(name, table, strict=unknowncmd)
1865 1865 except error.AmbiguousCommand, inst:
1866 1866 # py3k fix: except vars can't be used outside the scope of the
1867 1867 # except block, nor can be used inside a lambda. python issue4617
1868 1868 prefix = inst.args[0]
1869 1869 select = lambda c: c.lstrip('^').startswith(prefix)
1870 1870 helplist(_('list of commands:\n\n'), select)
1871 1871 return
1872 1872
1873 1873 # check if it's an invalid alias and display its error if it is
1874 1874 if getattr(entry[0], 'badalias', False):
1875 1875 if not unknowncmd:
1876 1876 entry[0](ui)
1877 1877 return
1878 1878
1879 1879 # synopsis
1880 1880 if len(entry) > 2:
1881 1881 if entry[2].startswith('hg'):
1882 1882 ui.write("%s\n" % entry[2])
1883 1883 else:
1884 1884 ui.write('hg %s %s\n' % (aliases[0], entry[2]))
1885 1885 else:
1886 1886 ui.write('hg %s\n' % aliases[0])
1887 1887
1888 1888 # aliases
1889 1889 if not ui.quiet and len(aliases) > 1:
1890 1890 ui.write(_("\naliases: %s\n") % ', '.join(aliases[1:]))
1891 1891
1892 1892 # description
1893 1893 doc = gettext(entry[0].__doc__)
1894 1894 if not doc:
1895 1895 doc = _("(no help text available)")
1896 1896 if hasattr(entry[0], 'definition'): # aliased command
1897 1897 if entry[0].definition.startswith('!'): # shell alias
1898 1898 doc = _('shell alias for::\n\n %s') % entry[0].definition[1:]
1899 1899 else:
1900 1900 doc = _('alias for: hg %s\n\n%s') % (entry[0].definition, doc)
1901 1901 if ui.quiet:
1902 1902 doc = doc.splitlines()[0]
1903 1903 keep = ui.verbose and ['verbose'] or []
1904 1904 formatted, pruned = minirst.format(doc, textwidth, keep=keep)
1905 1905 ui.write("\n%s\n" % formatted)
1906 1906 if pruned:
1907 1907 ui.write(_('\nuse "hg -v help %s" to show verbose help\n') % name)
1908 1908
1909 1909 if not ui.quiet:
1910 1910 # options
1911 1911 if entry[1]:
1912 1912 option_lists.append((_("options:\n"), entry[1]))
1913 1913
1914 1914 addglobalopts(False)
1915 1915
1916 1916 def helplist(header, select=None):
1917 1917 h = {}
1918 1918 cmds = {}
1919 1919 for c, e in table.iteritems():
1920 1920 f = c.split("|", 1)[0]
1921 1921 if select and not select(f):
1922 1922 continue
1923 1923 if (not select and name != 'shortlist' and
1924 1924 e[0].__module__ != __name__):
1925 1925 continue
1926 1926 if name == "shortlist" and not f.startswith("^"):
1927 1927 continue
1928 1928 f = f.lstrip("^")
1929 1929 if not ui.debugflag and f.startswith("debug"):
1930 1930 continue
1931 1931 doc = e[0].__doc__
1932 1932 if doc and 'DEPRECATED' in doc and not ui.verbose:
1933 1933 continue
1934 1934 doc = gettext(doc)
1935 1935 if not doc:
1936 1936 doc = _("(no help text available)")
1937 1937 h[f] = doc.splitlines()[0].rstrip()
1938 1938 cmds[f] = c.lstrip("^")
1939 1939
1940 1940 if not h:
1941 1941 ui.status(_('no commands defined\n'))
1942 1942 return
1943 1943
1944 1944 ui.status(header)
1945 1945 fns = sorted(h)
1946 1946 m = max(map(len, fns))
1947 1947 for f in fns:
1948 1948 if ui.verbose:
1949 1949 commands = cmds[f].replace("|",", ")
1950 1950 ui.write(" %s:\n %s\n"%(commands, h[f]))
1951 1951 else:
1952 ui.write('%s\n' % (util.wrap(h[f],
1952 ui.write('%s\n' % (util.wrap(h[f], textwidth,
1953 1953 initindent=' %-*s ' % (m, f),
1954 1954 hangindent=' ' * (m + 4))))
1955 1955
1956 1956 if not ui.quiet:
1957 1957 addglobalopts(True)
1958 1958
1959 1959 def helptopic(name):
1960 1960 for names, header, doc in help.helptable:
1961 1961 if name in names:
1962 1962 break
1963 1963 else:
1964 1964 raise error.UnknownCommand(name)
1965 1965
1966 1966 # description
1967 1967 if not doc:
1968 1968 doc = _("(no help text available)")
1969 1969 if hasattr(doc, '__call__'):
1970 1970 doc = doc()
1971 1971
1972 1972 ui.write("%s\n\n" % header)
1973 1973 ui.write("%s\n" % minirst.format(doc, textwidth, indent=4))
1974 1974
1975 1975 def helpext(name):
1976 1976 try:
1977 1977 mod = extensions.find(name)
1978 1978 doc = gettext(mod.__doc__) or _('no help text available')
1979 1979 except KeyError:
1980 1980 mod = None
1981 1981 doc = extensions.disabledext(name)
1982 1982 if not doc:
1983 1983 raise error.UnknownCommand(name)
1984 1984
1985 1985 if '\n' not in doc:
1986 1986 head, tail = doc, ""
1987 1987 else:
1988 1988 head, tail = doc.split('\n', 1)
1989 1989 ui.write(_('%s extension - %s\n\n') % (name.split('.')[-1], head))
1990 1990 if tail:
1991 1991 ui.write(minirst.format(tail, textwidth))
1992 1992 ui.status('\n\n')
1993 1993
1994 1994 if mod:
1995 1995 try:
1996 1996 ct = mod.cmdtable
1997 1997 except AttributeError:
1998 1998 ct = {}
1999 1999 modcmds = set([c.split('|', 1)[0] for c in ct])
2000 2000 helplist(_('list of commands:\n\n'), modcmds.__contains__)
2001 2001 else:
2002 2002 ui.write(_('use "hg help extensions" for information on enabling '
2003 2003 'extensions\n'))
2004 2004
2005 2005 def helpextcmd(name):
2006 2006 cmd, ext, mod = extensions.disabledcmd(name, ui.config('ui', 'strict'))
2007 2007 doc = gettext(mod.__doc__).splitlines()[0]
2008 2008
2009 2009 msg = help.listexts(_("'%s' is provided by the following "
2010 2010 "extension:") % cmd, {ext: doc}, len(ext),
2011 2011 indent=4)
2012 2012 ui.write(minirst.format(msg, textwidth))
2013 2013 ui.write('\n\n')
2014 2014 ui.write(_('use "hg help extensions" for information on enabling '
2015 2015 'extensions\n'))
2016 2016
2017 2017 if name and name != 'shortlist':
2018 2018 i = None
2019 2019 if unknowncmd:
2020 2020 queries = (helpextcmd,)
2021 2021 else:
2022 2022 queries = (helptopic, helpcmd, helpext, helpextcmd)
2023 2023 for f in queries:
2024 2024 try:
2025 2025 f(name)
2026 2026 i = None
2027 2027 break
2028 2028 except error.UnknownCommand, inst:
2029 2029 i = inst
2030 2030 if i:
2031 2031 raise i
2032 2032
2033 2033 else:
2034 2034 # program name
2035 2035 if ui.verbose or with_version:
2036 2036 version_(ui)
2037 2037 else:
2038 2038 ui.status(_("Mercurial Distributed SCM\n"))
2039 2039 ui.status('\n')
2040 2040
2041 2041 # list of commands
2042 2042 if name == "shortlist":
2043 2043 header = _('basic commands:\n\n')
2044 2044 else:
2045 2045 header = _('list of commands:\n\n')
2046 2046
2047 2047 helplist(header)
2048 2048 if name != 'shortlist':
2049 2049 exts, maxlength = extensions.enabled()
2050 2050 text = help.listexts(_('enabled extensions:'), exts, maxlength)
2051 2051 if text:
2052 2052 ui.write("\n%s\n" % minirst.format(text, textwidth))
2053 2053
2054 2054 # list all option lists
2055 2055 opt_output = []
2056 2056 multioccur = False
2057 2057 for title, options in option_lists:
2058 2058 opt_output.append(("\n%s" % title, None))
2059 2059 for option in options:
2060 2060 if len(option) == 5:
2061 2061 shortopt, longopt, default, desc, optlabel = option
2062 2062 else:
2063 2063 shortopt, longopt, default, desc = option
2064 2064 optlabel = _("VALUE") # default label
2065 2065
2066 2066 if _("DEPRECATED") in desc and not ui.verbose:
2067 2067 continue
2068 2068 if isinstance(default, list):
2069 2069 numqualifier = " %s [+]" % optlabel
2070 2070 multioccur = True
2071 2071 elif (default is not None) and not isinstance(default, bool):
2072 2072 numqualifier = " %s" % optlabel
2073 2073 else:
2074 2074 numqualifier = ""
2075 2075 opt_output.append(("%2s%s" %
2076 2076 (shortopt and "-%s" % shortopt,
2077 2077 longopt and " --%s%s" %
2078 2078 (longopt, numqualifier)),
2079 2079 "%s%s" % (desc,
2080 2080 default
2081 2081 and _(" (default: %s)") % default
2082 2082 or "")))
2083 2083 if multioccur:
2084 2084 msg = _("\n[+] marked option can be specified multiple times")
2085 2085 if ui.verbose and name != 'shortlist':
2086 2086 opt_output.append((msg, None))
2087 2087 else:
2088 2088 opt_output.insert(-1, (msg, None))
2089 2089
2090 2090 if not name:
2091 2091 ui.write(_("\nadditional help topics:\n\n"))
2092 2092 topics = []
2093 2093 for names, header, doc in help.helptable:
2094 2094 topics.append((sorted(names, key=len, reverse=True)[0], header))
2095 2095 topics_len = max([len(s[0]) for s in topics])
2096 2096 for t, desc in topics:
2097 2097 ui.write(" %-*s %s\n" % (topics_len, t, desc))
2098 2098
2099 2099 if opt_output:
2100 2100 colwidth = encoding.colwidth
2101 2101 # normalize: (opt or message, desc or None, width of opt)
2102 2102 entries = [desc and (opt, desc, colwidth(opt)) or (opt, None, 0)
2103 2103 for opt, desc in opt_output]
2104 2104 hanging = max([e[2] for e in entries])
2105 2105 for opt, desc, width in entries:
2106 2106 if desc:
2107 2107 initindent = ' %s%s ' % (opt, ' ' * (hanging - width))
2108 2108 hangindent = ' ' * (hanging + 3)
2109 ui.write('%s\n' % (util.wrap(desc,
2109 ui.write('%s\n' % (util.wrap(desc, textwidth,
2110 2110 initindent=initindent,
2111 2111 hangindent=hangindent)))
2112 2112 else:
2113 2113 ui.write("%s\n" % opt)
2114 2114
2115 2115 def identify(ui, repo, source=None,
2116 2116 rev=None, num=None, id=None, branch=None, tags=None):
2117 2117 """identify the working copy or specified revision
2118 2118
2119 2119 With no revision, print a summary of the current state of the
2120 2120 repository.
2121 2121
2122 2122 Specifying a path to a repository root or Mercurial bundle will
2123 2123 cause lookup to operate on that repository/bundle.
2124 2124
2125 2125 This summary identifies the repository state using one or two
2126 2126 parent hash identifiers, followed by a "+" if there are
2127 2127 uncommitted changes in the working directory, a list of tags for
2128 2128 this revision and a branch name for non-default branches.
2129 2129
2130 2130 Returns 0 if successful.
2131 2131 """
2132 2132
2133 2133 if not repo and not source:
2134 2134 raise util.Abort(_("there is no Mercurial repository here "
2135 2135 "(.hg not found)"))
2136 2136
2137 2137 hexfunc = ui.debugflag and hex or short
2138 2138 default = not (num or id or branch or tags)
2139 2139 output = []
2140 2140
2141 2141 revs = []
2142 2142 if source:
2143 2143 source, branches = hg.parseurl(ui.expandpath(source))
2144 2144 repo = hg.repository(ui, source)
2145 2145 revs, checkout = hg.addbranchrevs(repo, repo, branches, None)
2146 2146
2147 2147 if not repo.local():
2148 2148 if not rev and revs:
2149 2149 rev = revs[0]
2150 2150 if not rev:
2151 2151 rev = "tip"
2152 2152 if num or branch or tags:
2153 2153 raise util.Abort(
2154 2154 "can't query remote revision number, branch, or tags")
2155 2155 output = [hexfunc(repo.lookup(rev))]
2156 2156 elif not rev:
2157 2157 ctx = repo[None]
2158 2158 parents = ctx.parents()
2159 2159 changed = False
2160 2160 if default or id or num:
2161 2161 changed = util.any(repo.status())
2162 2162 if default or id:
2163 2163 output = ["%s%s" % ('+'.join([hexfunc(p.node()) for p in parents]),
2164 2164 (changed) and "+" or "")]
2165 2165 if num:
2166 2166 output.append("%s%s" % ('+'.join([str(p.rev()) for p in parents]),
2167 2167 (changed) and "+" or ""))
2168 2168 else:
2169 2169 ctx = repo[rev]
2170 2170 if default or id:
2171 2171 output = [hexfunc(ctx.node())]
2172 2172 if num:
2173 2173 output.append(str(ctx.rev()))
2174 2174
2175 2175 if repo.local() and default and not ui.quiet:
2176 2176 b = encoding.tolocal(ctx.branch())
2177 2177 if b != 'default':
2178 2178 output.append("(%s)" % b)
2179 2179
2180 2180 # multiple tags for a single parent separated by '/'
2181 2181 t = "/".join(ctx.tags())
2182 2182 if t:
2183 2183 output.append(t)
2184 2184
2185 2185 if branch:
2186 2186 output.append(encoding.tolocal(ctx.branch()))
2187 2187
2188 2188 if tags:
2189 2189 output.extend(ctx.tags())
2190 2190
2191 2191 ui.write("%s\n" % ' '.join(output))
2192 2192
2193 2193 def import_(ui, repo, patch1, *patches, **opts):
2194 2194 """import an ordered set of patches
2195 2195
2196 2196 Import a list of patches and commit them individually (unless
2197 2197 --no-commit is specified).
2198 2198
2199 2199 If there are outstanding changes in the working directory, import
2200 2200 will abort unless given the -f/--force flag.
2201 2201
2202 2202 You can import a patch straight from a mail message. Even patches
2203 2203 as attachments work (to use the body part, it must have type
2204 2204 text/plain or text/x-patch). From and Subject headers of email
2205 2205 message are used as default committer and commit message. All
2206 2206 text/plain body parts before first diff are added to commit
2207 2207 message.
2208 2208
2209 2209 If the imported patch was generated by :hg:`export`, user and
2210 2210 description from patch override values from message headers and
2211 2211 body. Values given on command line with -m/--message and -u/--user
2212 2212 override these.
2213 2213
2214 2214 If --exact is specified, import will set the working directory to
2215 2215 the parent of each patch before applying it, and will abort if the
2216 2216 resulting changeset has a different ID than the one recorded in
2217 2217 the patch. This may happen due to character set problems or other
2218 2218 deficiencies in the text patch format.
2219 2219
2220 2220 With -s/--similarity, hg will attempt to discover renames and
2221 2221 copies in the patch in the same way as 'addremove'.
2222 2222
2223 2223 To read a patch from standard input, use "-" as the patch name. If
2224 2224 a URL is specified, the patch will be downloaded from it.
2225 2225 See :hg:`help dates` for a list of formats valid for -d/--date.
2226 2226
2227 2227 Returns 0 on success.
2228 2228 """
2229 2229 patches = (patch1,) + patches
2230 2230
2231 2231 date = opts.get('date')
2232 2232 if date:
2233 2233 opts['date'] = util.parsedate(date)
2234 2234
2235 2235 try:
2236 2236 sim = float(opts.get('similarity') or 0)
2237 2237 except ValueError:
2238 2238 raise util.Abort(_('similarity must be a number'))
2239 2239 if sim < 0 or sim > 100:
2240 2240 raise util.Abort(_('similarity must be between 0 and 100'))
2241 2241
2242 2242 if opts.get('exact') or not opts.get('force'):
2243 2243 cmdutil.bail_if_changed(repo)
2244 2244
2245 2245 d = opts["base"]
2246 2246 strip = opts["strip"]
2247 2247 wlock = lock = None
2248 2248
2249 2249 def tryone(ui, hunk):
2250 2250 tmpname, message, user, date, branch, nodeid, p1, p2 = \
2251 2251 patch.extract(ui, hunk)
2252 2252
2253 2253 if not tmpname:
2254 2254 return None
2255 2255 commitid = _('to working directory')
2256 2256
2257 2257 try:
2258 2258 cmdline_message = cmdutil.logmessage(opts)
2259 2259 if cmdline_message:
2260 2260 # pickup the cmdline msg
2261 2261 message = cmdline_message
2262 2262 elif message:
2263 2263 # pickup the patch msg
2264 2264 message = message.strip()
2265 2265 else:
2266 2266 # launch the editor
2267 2267 message = None
2268 2268 ui.debug('message:\n%s\n' % message)
2269 2269
2270 2270 wp = repo.parents()
2271 2271 if opts.get('exact'):
2272 2272 if not nodeid or not p1:
2273 2273 raise util.Abort(_('not a Mercurial patch'))
2274 2274 p1 = repo.lookup(p1)
2275 2275 p2 = repo.lookup(p2 or hex(nullid))
2276 2276
2277 2277 if p1 != wp[0].node():
2278 2278 hg.clean(repo, p1)
2279 2279 repo.dirstate.setparents(p1, p2)
2280 2280 elif p2:
2281 2281 try:
2282 2282 p1 = repo.lookup(p1)
2283 2283 p2 = repo.lookup(p2)
2284 2284 if p1 == wp[0].node():
2285 2285 repo.dirstate.setparents(p1, p2)
2286 2286 except error.RepoError:
2287 2287 pass
2288 2288 if opts.get('exact') or opts.get('import_branch'):
2289 2289 repo.dirstate.setbranch(branch or 'default')
2290 2290
2291 2291 files = {}
2292 2292 try:
2293 2293 patch.patch(tmpname, ui, strip=strip, cwd=repo.root,
2294 2294 files=files, eolmode=None)
2295 2295 finally:
2296 2296 files = cmdutil.updatedir(ui, repo, files,
2297 2297 similarity=sim / 100.0)
2298 2298 if not opts.get('no_commit'):
2299 2299 if opts.get('exact'):
2300 2300 m = None
2301 2301 else:
2302 2302 m = cmdutil.matchfiles(repo, files or [])
2303 2303 n = repo.commit(message, opts.get('user') or user,
2304 2304 opts.get('date') or date, match=m,
2305 2305 editor=cmdutil.commiteditor)
2306 2306 if opts.get('exact'):
2307 2307 if hex(n) != nodeid:
2308 2308 repo.rollback()
2309 2309 raise util.Abort(_('patch is damaged'
2310 2310 ' or loses information'))
2311 2311 # Force a dirstate write so that the next transaction
2312 2312 # backups an up-do-date file.
2313 2313 repo.dirstate.write()
2314 2314 if n:
2315 2315 commitid = short(n)
2316 2316
2317 2317 return commitid
2318 2318 finally:
2319 2319 os.unlink(tmpname)
2320 2320
2321 2321 try:
2322 2322 wlock = repo.wlock()
2323 2323 lock = repo.lock()
2324 2324 lastcommit = None
2325 2325 for p in patches:
2326 2326 pf = os.path.join(d, p)
2327 2327
2328 2328 if pf == '-':
2329 2329 ui.status(_("applying patch from stdin\n"))
2330 2330 pf = sys.stdin
2331 2331 else:
2332 2332 ui.status(_("applying %s\n") % p)
2333 2333 pf = url.open(ui, pf)
2334 2334
2335 2335 haspatch = False
2336 2336 for hunk in patch.split(pf):
2337 2337 commitid = tryone(ui, hunk)
2338 2338 if commitid:
2339 2339 haspatch = True
2340 2340 if lastcommit:
2341 2341 ui.status(_('applied %s\n') % lastcommit)
2342 2342 lastcommit = commitid
2343 2343
2344 2344 if not haspatch:
2345 2345 raise util.Abort(_('no diffs found'))
2346 2346
2347 2347 finally:
2348 2348 release(lock, wlock)
2349 2349
2350 2350 def incoming(ui, repo, source="default", **opts):
2351 2351 """show new changesets found in source
2352 2352
2353 2353 Show new changesets found in the specified path/URL or the default
2354 2354 pull location. These are the changesets that would have been pulled
2355 2355 if a pull at the time you issued this command.
2356 2356
2357 2357 For remote repository, using --bundle avoids downloading the
2358 2358 changesets twice if the incoming is followed by a pull.
2359 2359
2360 2360 See pull for valid source format details.
2361 2361
2362 2362 Returns 0 if there are incoming changes, 1 otherwise.
2363 2363 """
2364 2364 if opts.get('bundle') and opts.get('subrepos'):
2365 2365 raise util.Abort(_('cannot combine --bundle and --subrepos'))
2366 2366
2367 2367 ret = hg.incoming(ui, repo, source, opts)
2368 2368 return ret
2369 2369
2370 2370 def init(ui, dest=".", **opts):
2371 2371 """create a new repository in the given directory
2372 2372
2373 2373 Initialize a new repository in the given directory. If the given
2374 2374 directory does not exist, it will be created.
2375 2375
2376 2376 If no directory is given, the current directory is used.
2377 2377
2378 2378 It is possible to specify an ``ssh://`` URL as the destination.
2379 2379 See :hg:`help urls` for more information.
2380 2380
2381 2381 Returns 0 on success.
2382 2382 """
2383 2383 hg.repository(hg.remoteui(ui, opts), ui.expandpath(dest), create=1)
2384 2384
2385 2385 def locate(ui, repo, *pats, **opts):
2386 2386 """locate files matching specific patterns
2387 2387
2388 2388 Print files under Mercurial control in the working directory whose
2389 2389 names match the given patterns.
2390 2390
2391 2391 By default, this command searches all directories in the working
2392 2392 directory. To search just the current directory and its
2393 2393 subdirectories, use "--include .".
2394 2394
2395 2395 If no patterns are given to match, this command prints the names
2396 2396 of all files under Mercurial control in the working directory.
2397 2397
2398 2398 If you want to feed the output of this command into the "xargs"
2399 2399 command, use the -0 option to both this command and "xargs". This
2400 2400 will avoid the problem of "xargs" treating single filenames that
2401 2401 contain whitespace as multiple filenames.
2402 2402
2403 2403 Returns 0 if a match is found, 1 otherwise.
2404 2404 """
2405 2405 end = opts.get('print0') and '\0' or '\n'
2406 2406 rev = opts.get('rev') or None
2407 2407
2408 2408 ret = 1
2409 2409 m = cmdutil.match(repo, pats, opts, default='relglob')
2410 2410 m.bad = lambda x, y: False
2411 2411 for abs in repo[rev].walk(m):
2412 2412 if not rev and abs not in repo.dirstate:
2413 2413 continue
2414 2414 if opts.get('fullpath'):
2415 2415 ui.write(repo.wjoin(abs), end)
2416 2416 else:
2417 2417 ui.write(((pats and m.rel(abs)) or abs), end)
2418 2418 ret = 0
2419 2419
2420 2420 return ret
2421 2421
2422 2422 def log(ui, repo, *pats, **opts):
2423 2423 """show revision history of entire repository or files
2424 2424
2425 2425 Print the revision history of the specified files or the entire
2426 2426 project.
2427 2427
2428 2428 File history is shown without following rename or copy history of
2429 2429 files. Use -f/--follow with a filename to follow history across
2430 2430 renames and copies. --follow without a filename will only show
2431 2431 ancestors or descendants of the starting revision. --follow-first
2432 2432 only follows the first parent of merge revisions.
2433 2433
2434 2434 If no revision range is specified, the default is tip:0 unless
2435 2435 --follow is set, in which case the working directory parent is
2436 2436 used as the starting revision. You can specify a revision set for
2437 2437 log, see :hg:`help revsets` for more information.
2438 2438
2439 2439 See :hg:`help dates` for a list of formats valid for -d/--date.
2440 2440
2441 2441 By default this command prints revision number and changeset id,
2442 2442 tags, non-trivial parents, user, date and time, and a summary for
2443 2443 each commit. When the -v/--verbose switch is used, the list of
2444 2444 changed files and full commit message are shown.
2445 2445
2446 2446 .. note::
2447 2447 log -p/--patch may generate unexpected diff output for merge
2448 2448 changesets, as it will only compare the merge changeset against
2449 2449 its first parent. Also, only files different from BOTH parents
2450 2450 will appear in files:.
2451 2451
2452 2452 Returns 0 on success.
2453 2453 """
2454 2454
2455 2455 matchfn = cmdutil.match(repo, pats, opts)
2456 2456 limit = cmdutil.loglimit(opts)
2457 2457 count = 0
2458 2458
2459 2459 endrev = None
2460 2460 if opts.get('copies') and opts.get('rev'):
2461 2461 endrev = max(cmdutil.revrange(repo, opts.get('rev'))) + 1
2462 2462
2463 2463 df = False
2464 2464 if opts["date"]:
2465 2465 df = util.matchdate(opts["date"])
2466 2466
2467 2467 branches = opts.get('branch', []) + opts.get('only_branch', [])
2468 2468 opts['branch'] = [repo.lookupbranch(b) for b in branches]
2469 2469
2470 2470 displayer = cmdutil.show_changeset(ui, repo, opts, True)
2471 2471 def prep(ctx, fns):
2472 2472 rev = ctx.rev()
2473 2473 parents = [p for p in repo.changelog.parentrevs(rev)
2474 2474 if p != nullrev]
2475 2475 if opts.get('no_merges') and len(parents) == 2:
2476 2476 return
2477 2477 if opts.get('only_merges') and len(parents) != 2:
2478 2478 return
2479 2479 if opts.get('branch') and ctx.branch() not in opts['branch']:
2480 2480 return
2481 2481 if df and not df(ctx.date()[0]):
2482 2482 return
2483 2483 if opts['user'] and not [k for k in opts['user'] if k in ctx.user()]:
2484 2484 return
2485 2485 if opts.get('keyword'):
2486 2486 for k in [kw.lower() for kw in opts['keyword']]:
2487 2487 if (k in ctx.user().lower() or
2488 2488 k in ctx.description().lower() or
2489 2489 k in " ".join(ctx.files()).lower()):
2490 2490 break
2491 2491 else:
2492 2492 return
2493 2493
2494 2494 copies = None
2495 2495 if opts.get('copies') and rev:
2496 2496 copies = []
2497 2497 getrenamed = templatekw.getrenamedfn(repo, endrev=endrev)
2498 2498 for fn in ctx.files():
2499 2499 rename = getrenamed(fn, rev)
2500 2500 if rename:
2501 2501 copies.append((fn, rename[0]))
2502 2502
2503 2503 revmatchfn = None
2504 2504 if opts.get('patch') or opts.get('stat'):
2505 2505 if opts.get('follow') or opts.get('follow_first'):
2506 2506 # note: this might be wrong when following through merges
2507 2507 revmatchfn = cmdutil.match(repo, fns, default='path')
2508 2508 else:
2509 2509 revmatchfn = matchfn
2510 2510
2511 2511 displayer.show(ctx, copies=copies, matchfn=revmatchfn)
2512 2512
2513 2513 for ctx in cmdutil.walkchangerevs(repo, matchfn, opts, prep):
2514 2514 if count == limit:
2515 2515 break
2516 2516 if displayer.flush(ctx.rev()):
2517 2517 count += 1
2518 2518 displayer.close()
2519 2519
2520 2520 def manifest(ui, repo, node=None, rev=None):
2521 2521 """output the current or given revision of the project manifest
2522 2522
2523 2523 Print a list of version controlled files for the given revision.
2524 2524 If no revision is given, the first parent of the working directory
2525 2525 is used, or the null revision if no revision is checked out.
2526 2526
2527 2527 With -v, print file permissions, symlink and executable bits.
2528 2528 With --debug, print file revision hashes.
2529 2529
2530 2530 Returns 0 on success.
2531 2531 """
2532 2532
2533 2533 if rev and node:
2534 2534 raise util.Abort(_("please specify just one revision"))
2535 2535
2536 2536 if not node:
2537 2537 node = rev
2538 2538
2539 2539 decor = {'l':'644 @ ', 'x':'755 * ', '':'644 '}
2540 2540 ctx = repo[node]
2541 2541 for f in ctx:
2542 2542 if ui.debugflag:
2543 2543 ui.write("%40s " % hex(ctx.manifest()[f]))
2544 2544 if ui.verbose:
2545 2545 ui.write(decor[ctx.flags(f)])
2546 2546 ui.write("%s\n" % f)
2547 2547
2548 2548 def merge(ui, repo, node=None, **opts):
2549 2549 """merge working directory with another revision
2550 2550
2551 2551 The current working directory is updated with all changes made in
2552 2552 the requested revision since the last common predecessor revision.
2553 2553
2554 2554 Files that changed between either parent are marked as changed for
2555 2555 the next commit and a commit must be performed before any further
2556 2556 updates to the repository are allowed. The next commit will have
2557 2557 two parents.
2558 2558
2559 2559 If no revision is specified, the working directory's parent is a
2560 2560 head revision, and the current branch contains exactly one other
2561 2561 head, the other head is merged with by default. Otherwise, an
2562 2562 explicit revision with which to merge with must be provided.
2563 2563
2564 2564 To undo an uncommitted merge, use :hg:`update --clean .` which
2565 2565 will check out a clean copy of the original merge parent, losing
2566 2566 all changes.
2567 2567
2568 2568 Returns 0 on success, 1 if there are unresolved files.
2569 2569 """
2570 2570
2571 2571 if opts.get('rev') and node:
2572 2572 raise util.Abort(_("please specify just one revision"))
2573 2573 if not node:
2574 2574 node = opts.get('rev')
2575 2575
2576 2576 if not node:
2577 2577 branch = repo.changectx(None).branch()
2578 2578 bheads = repo.branchheads(branch)
2579 2579 if len(bheads) > 2:
2580 2580 raise util.Abort(_(
2581 2581 'branch \'%s\' has %d heads - '
2582 2582 'please merge with an explicit rev\n'
2583 2583 '(run \'hg heads .\' to see heads)')
2584 2584 % (branch, len(bheads)))
2585 2585
2586 2586 parent = repo.dirstate.parents()[0]
2587 2587 if len(bheads) == 1:
2588 2588 if len(repo.heads()) > 1:
2589 2589 raise util.Abort(_(
2590 2590 'branch \'%s\' has one head - '
2591 2591 'please merge with an explicit rev\n'
2592 2592 '(run \'hg heads\' to see all heads)')
2593 2593 % branch)
2594 2594 msg = _('there is nothing to merge')
2595 2595 if parent != repo.lookup(repo[None].branch()):
2596 2596 msg = _('%s - use "hg update" instead') % msg
2597 2597 raise util.Abort(msg)
2598 2598
2599 2599 if parent not in bheads:
2600 2600 raise util.Abort(_('working dir not at a head rev - '
2601 2601 'use "hg update" or merge with an explicit rev'))
2602 2602 node = parent == bheads[0] and bheads[-1] or bheads[0]
2603 2603
2604 2604 if opts.get('preview'):
2605 2605 # find nodes that are ancestors of p2 but not of p1
2606 2606 p1 = repo.lookup('.')
2607 2607 p2 = repo.lookup(node)
2608 2608 nodes = repo.changelog.findmissing(common=[p1], heads=[p2])
2609 2609
2610 2610 displayer = cmdutil.show_changeset(ui, repo, opts)
2611 2611 for node in nodes:
2612 2612 displayer.show(repo[node])
2613 2613 displayer.close()
2614 2614 return 0
2615 2615
2616 2616 return hg.merge(repo, node, force=opts.get('force'))
2617 2617
2618 2618 def outgoing(ui, repo, dest=None, **opts):
2619 2619 """show changesets not found in the destination
2620 2620
2621 2621 Show changesets not found in the specified destination repository
2622 2622 or the default push location. These are the changesets that would
2623 2623 be pushed if a push was requested.
2624 2624
2625 2625 See pull for details of valid destination formats.
2626 2626
2627 2627 Returns 0 if there are outgoing changes, 1 otherwise.
2628 2628 """
2629 2629 ret = hg.outgoing(ui, repo, dest, opts)
2630 2630 return ret
2631 2631
2632 2632 def parents(ui, repo, file_=None, **opts):
2633 2633 """show the parents of the working directory or revision
2634 2634
2635 2635 Print the working directory's parent revisions. If a revision is
2636 2636 given via -r/--rev, the parent of that revision will be printed.
2637 2637 If a file argument is given, the revision in which the file was
2638 2638 last changed (before the working directory revision or the
2639 2639 argument to --rev if given) is printed.
2640 2640
2641 2641 Returns 0 on success.
2642 2642 """
2643 2643 rev = opts.get('rev')
2644 2644 if rev:
2645 2645 ctx = repo[rev]
2646 2646 else:
2647 2647 ctx = repo[None]
2648 2648
2649 2649 if file_:
2650 2650 m = cmdutil.match(repo, (file_,), opts)
2651 2651 if m.anypats() or len(m.files()) != 1:
2652 2652 raise util.Abort(_('can only specify an explicit filename'))
2653 2653 file_ = m.files()[0]
2654 2654 filenodes = []
2655 2655 for cp in ctx.parents():
2656 2656 if not cp:
2657 2657 continue
2658 2658 try:
2659 2659 filenodes.append(cp.filenode(file_))
2660 2660 except error.LookupError:
2661 2661 pass
2662 2662 if not filenodes:
2663 2663 raise util.Abort(_("'%s' not found in manifest!") % file_)
2664 2664 fl = repo.file(file_)
2665 2665 p = [repo.lookup(fl.linkrev(fl.rev(fn))) for fn in filenodes]
2666 2666 else:
2667 2667 p = [cp.node() for cp in ctx.parents()]
2668 2668
2669 2669 displayer = cmdutil.show_changeset(ui, repo, opts)
2670 2670 for n in p:
2671 2671 if n != nullid:
2672 2672 displayer.show(repo[n])
2673 2673 displayer.close()
2674 2674
2675 2675 def paths(ui, repo, search=None):
2676 2676 """show aliases for remote repositories
2677 2677
2678 2678 Show definition of symbolic path name NAME. If no name is given,
2679 2679 show definition of all available names.
2680 2680
2681 2681 Path names are defined in the [paths] section of your
2682 2682 configuration file and in ``/etc/mercurial/hgrc``. If run inside a
2683 2683 repository, ``.hg/hgrc`` is used, too.
2684 2684
2685 2685 The path names ``default`` and ``default-push`` have a special
2686 2686 meaning. When performing a push or pull operation, they are used
2687 2687 as fallbacks if no location is specified on the command-line.
2688 2688 When ``default-push`` is set, it will be used for push and
2689 2689 ``default`` will be used for pull; otherwise ``default`` is used
2690 2690 as the fallback for both. When cloning a repository, the clone
2691 2691 source is written as ``default`` in ``.hg/hgrc``. Note that
2692 2692 ``default`` and ``default-push`` apply to all inbound (e.g.
2693 2693 :hg:`incoming`) and outbound (e.g. :hg:`outgoing`, :hg:`email` and
2694 2694 :hg:`bundle`) operations.
2695 2695
2696 2696 See :hg:`help urls` for more information.
2697 2697
2698 2698 Returns 0 on success.
2699 2699 """
2700 2700 if search:
2701 2701 for name, path in ui.configitems("paths"):
2702 2702 if name == search:
2703 2703 ui.write("%s\n" % url.hidepassword(path))
2704 2704 return
2705 2705 ui.warn(_("not found!\n"))
2706 2706 return 1
2707 2707 else:
2708 2708 for name, path in ui.configitems("paths"):
2709 2709 ui.write("%s = %s\n" % (name, url.hidepassword(path)))
2710 2710
2711 2711 def postincoming(ui, repo, modheads, optupdate, checkout):
2712 2712 if modheads == 0:
2713 2713 return
2714 2714 if optupdate:
2715 2715 if (modheads <= 1 or len(repo.branchheads()) == 1) or checkout:
2716 2716 return hg.update(repo, checkout)
2717 2717 else:
2718 2718 ui.status(_("not updating, since new heads added\n"))
2719 2719 if modheads > 1:
2720 2720 ui.status(_("(run 'hg heads' to see heads, 'hg merge' to merge)\n"))
2721 2721 else:
2722 2722 ui.status(_("(run 'hg update' to get a working copy)\n"))
2723 2723
2724 2724 def pull(ui, repo, source="default", **opts):
2725 2725 """pull changes from the specified source
2726 2726
2727 2727 Pull changes from a remote repository to a local one.
2728 2728
2729 2729 This finds all changes from the repository at the specified path
2730 2730 or URL and adds them to a local repository (the current one unless
2731 2731 -R is specified). By default, this does not update the copy of the
2732 2732 project in the working directory.
2733 2733
2734 2734 Use :hg:`incoming` if you want to see what would have been added
2735 2735 by a pull at the time you issued this command. If you then decide
2736 2736 to add those changes to the repository, you should use :hg:`pull
2737 2737 -r X` where ``X`` is the last changeset listed by :hg:`incoming`.
2738 2738
2739 2739 If SOURCE is omitted, the 'default' path will be used.
2740 2740 See :hg:`help urls` for more information.
2741 2741
2742 2742 Returns 0 on success, 1 if an update had unresolved files.
2743 2743 """
2744 2744 source, branches = hg.parseurl(ui.expandpath(source), opts.get('branch'))
2745 2745 other = hg.repository(hg.remoteui(repo, opts), source)
2746 2746 ui.status(_('pulling from %s\n') % url.hidepassword(source))
2747 2747 revs, checkout = hg.addbranchrevs(repo, other, branches, opts.get('rev'))
2748 2748 if revs:
2749 2749 try:
2750 2750 revs = [other.lookup(rev) for rev in revs]
2751 2751 except error.CapabilityError:
2752 2752 err = _("other repository doesn't support revision lookup, "
2753 2753 "so a rev cannot be specified.")
2754 2754 raise util.Abort(err)
2755 2755
2756 2756 modheads = repo.pull(other, heads=revs, force=opts.get('force'))
2757 2757 if checkout:
2758 2758 checkout = str(repo.changelog.rev(other.lookup(checkout)))
2759 2759 return postincoming(ui, repo, modheads, opts.get('update'), checkout)
2760 2760
2761 2761 def push(ui, repo, dest=None, **opts):
2762 2762 """push changes to the specified destination
2763 2763
2764 2764 Push changesets from the local repository to the specified
2765 2765 destination.
2766 2766
2767 2767 This operation is symmetrical to pull: it is identical to a pull
2768 2768 in the destination repository from the current one.
2769 2769
2770 2770 By default, push will not allow creation of new heads at the
2771 2771 destination, since multiple heads would make it unclear which head
2772 2772 to use. In this situation, it is recommended to pull and merge
2773 2773 before pushing.
2774 2774
2775 2775 Use --new-branch if you want to allow push to create a new named
2776 2776 branch that is not present at the destination. This allows you to
2777 2777 only create a new branch without forcing other changes.
2778 2778
2779 2779 Use -f/--force to override the default behavior and push all
2780 2780 changesets on all branches.
2781 2781
2782 2782 If -r/--rev is used, the specified revision and all its ancestors
2783 2783 will be pushed to the remote repository.
2784 2784
2785 2785 Please see :hg:`help urls` for important details about ``ssh://``
2786 2786 URLs. If DESTINATION is omitted, a default path will be used.
2787 2787
2788 2788 Returns 0 if push was successful, 1 if nothing to push.
2789 2789 """
2790 2790 dest = ui.expandpath(dest or 'default-push', dest or 'default')
2791 2791 dest, branches = hg.parseurl(dest, opts.get('branch'))
2792 2792 revs, checkout = hg.addbranchrevs(repo, repo, branches, opts.get('rev'))
2793 2793 other = hg.repository(hg.remoteui(repo, opts), dest)
2794 2794 ui.status(_('pushing to %s\n') % url.hidepassword(dest))
2795 2795 if revs:
2796 2796 revs = [repo.lookup(rev) for rev in revs]
2797 2797
2798 2798 # push subrepos depth-first for coherent ordering
2799 2799 c = repo['']
2800 2800 subs = c.substate # only repos that are committed
2801 2801 for s in sorted(subs):
2802 2802 if not c.sub(s).push(opts.get('force')):
2803 2803 return False
2804 2804
2805 2805 r = repo.push(other, opts.get('force'), revs=revs,
2806 2806 newbranch=opts.get('new_branch'))
2807 2807 return r == 0
2808 2808
2809 2809 def recover(ui, repo):
2810 2810 """roll back an interrupted transaction
2811 2811
2812 2812 Recover from an interrupted commit or pull.
2813 2813
2814 2814 This command tries to fix the repository status after an
2815 2815 interrupted operation. It should only be necessary when Mercurial
2816 2816 suggests it.
2817 2817
2818 2818 Returns 0 if successful, 1 if nothing to recover or verify fails.
2819 2819 """
2820 2820 if repo.recover():
2821 2821 return hg.verify(repo)
2822 2822 return 1
2823 2823
2824 2824 def remove(ui, repo, *pats, **opts):
2825 2825 """remove the specified files on the next commit
2826 2826
2827 2827 Schedule the indicated files for removal from the repository.
2828 2828
2829 2829 This only removes files from the current branch, not from the
2830 2830 entire project history. -A/--after can be used to remove only
2831 2831 files that have already been deleted, -f/--force can be used to
2832 2832 force deletion, and -Af can be used to remove files from the next
2833 2833 revision without deleting them from the working directory.
2834 2834
2835 2835 The following table details the behavior of remove for different
2836 2836 file states (columns) and option combinations (rows). The file
2837 2837 states are Added [A], Clean [C], Modified [M] and Missing [!] (as
2838 2838 reported by :hg:`status`). The actions are Warn, Remove (from
2839 2839 branch) and Delete (from disk)::
2840 2840
2841 2841 A C M !
2842 2842 none W RD W R
2843 2843 -f R RD RD R
2844 2844 -A W W W R
2845 2845 -Af R R R R
2846 2846
2847 2847 This command schedules the files to be removed at the next commit.
2848 2848 To undo a remove before that, see :hg:`revert`.
2849 2849
2850 2850 Returns 0 on success, 1 if any warnings encountered.
2851 2851 """
2852 2852
2853 2853 ret = 0
2854 2854 after, force = opts.get('after'), opts.get('force')
2855 2855 if not pats and not after:
2856 2856 raise util.Abort(_('no files specified'))
2857 2857
2858 2858 m = cmdutil.match(repo, pats, opts)
2859 2859 s = repo.status(match=m, clean=True)
2860 2860 modified, added, deleted, clean = s[0], s[1], s[3], s[6]
2861 2861
2862 2862 for f in m.files():
2863 2863 if f not in repo.dirstate and not os.path.isdir(m.rel(f)):
2864 2864 ui.warn(_('not removing %s: file is untracked\n') % m.rel(f))
2865 2865 ret = 1
2866 2866
2867 2867 if force:
2868 2868 remove, forget = modified + deleted + clean, added
2869 2869 elif after:
2870 2870 remove, forget = deleted, []
2871 2871 for f in modified + added + clean:
2872 2872 ui.warn(_('not removing %s: file still exists (use -f'
2873 2873 ' to force removal)\n') % m.rel(f))
2874 2874 ret = 1
2875 2875 else:
2876 2876 remove, forget = deleted + clean, []
2877 2877 for f in modified:
2878 2878 ui.warn(_('not removing %s: file is modified (use -f'
2879 2879 ' to force removal)\n') % m.rel(f))
2880 2880 ret = 1
2881 2881 for f in added:
2882 2882 ui.warn(_('not removing %s: file has been marked for add (use -f'
2883 2883 ' to force removal)\n') % m.rel(f))
2884 2884 ret = 1
2885 2885
2886 2886 for f in sorted(remove + forget):
2887 2887 if ui.verbose or not m.exact(f):
2888 2888 ui.status(_('removing %s\n') % m.rel(f))
2889 2889
2890 2890 repo[None].forget(forget)
2891 2891 repo[None].remove(remove, unlink=not after)
2892 2892 return ret
2893 2893
2894 2894 def rename(ui, repo, *pats, **opts):
2895 2895 """rename files; equivalent of copy + remove
2896 2896
2897 2897 Mark dest as copies of sources; mark sources for deletion. If dest
2898 2898 is a directory, copies are put in that directory. If dest is a
2899 2899 file, there can only be one source.
2900 2900
2901 2901 By default, this command copies the contents of files as they
2902 2902 exist in the working directory. If invoked with -A/--after, the
2903 2903 operation is recorded, but no copying is performed.
2904 2904
2905 2905 This command takes effect at the next commit. To undo a rename
2906 2906 before that, see :hg:`revert`.
2907 2907
2908 2908 Returns 0 on success, 1 if errors are encountered.
2909 2909 """
2910 2910 wlock = repo.wlock(False)
2911 2911 try:
2912 2912 return cmdutil.copy(ui, repo, pats, opts, rename=True)
2913 2913 finally:
2914 2914 wlock.release()
2915 2915
2916 2916 def resolve(ui, repo, *pats, **opts):
2917 2917 """redo merges or set/view the merge status of files
2918 2918
2919 2919 Merges with unresolved conflicts are often the result of
2920 2920 non-interactive merging using the ``internal:merge`` configuration
2921 2921 setting, or a command-line merge tool like ``diff3``. The resolve
2922 2922 command is used to manage the files involved in a merge, after
2923 2923 :hg:`merge` has been run, and before :hg:`commit` is run (i.e. the
2924 2924 working directory must have two parents).
2925 2925
2926 2926 The resolve command can be used in the following ways:
2927 2927
2928 2928 - :hg:`resolve FILE...`: attempt to re-merge the specified files,
2929 2929 discarding any previous merge attempts. Re-merging is not
2930 2930 performed for files already marked as resolved. Use ``--all/-a``
2931 2931 to selects all unresolved files.
2932 2932
2933 2933 - :hg:`resolve -m [FILE]`: mark a file as having been resolved
2934 2934 (e.g. after having manually fixed-up the files). The default is
2935 2935 to mark all unresolved files.
2936 2936
2937 2937 - :hg:`resolve -u [FILE]...`: mark a file as unresolved. The
2938 2938 default is to mark all resolved files.
2939 2939
2940 2940 - :hg:`resolve -l`: list files which had or still have conflicts.
2941 2941 In the printed list, ``U`` = unresolved and ``R`` = resolved.
2942 2942
2943 2943 Note that Mercurial will not let you commit files with unresolved
2944 2944 merge conflicts. You must use :hg:`resolve -m ...` before you can
2945 2945 commit after a conflicting merge.
2946 2946
2947 2947 Returns 0 on success, 1 if any files fail a resolve attempt.
2948 2948 """
2949 2949
2950 2950 all, mark, unmark, show, nostatus = \
2951 2951 [opts.get(o) for o in 'all mark unmark list no_status'.split()]
2952 2952
2953 2953 if (show and (mark or unmark)) or (mark and unmark):
2954 2954 raise util.Abort(_("too many options specified"))
2955 2955 if pats and all:
2956 2956 raise util.Abort(_("can't specify --all and patterns"))
2957 2957 if not (all or pats or show or mark or unmark):
2958 2958 raise util.Abort(_('no files or directories specified; '
2959 2959 'use --all to remerge all files'))
2960 2960
2961 2961 ms = mergemod.mergestate(repo)
2962 2962 m = cmdutil.match(repo, pats, opts)
2963 2963 ret = 0
2964 2964
2965 2965 for f in ms:
2966 2966 if m(f):
2967 2967 if show:
2968 2968 if nostatus:
2969 2969 ui.write("%s\n" % f)
2970 2970 else:
2971 2971 ui.write("%s %s\n" % (ms[f].upper(), f),
2972 2972 label='resolve.' +
2973 2973 {'u': 'unresolved', 'r': 'resolved'}[ms[f]])
2974 2974 elif mark:
2975 2975 ms.mark(f, "r")
2976 2976 elif unmark:
2977 2977 ms.mark(f, "u")
2978 2978 else:
2979 2979 wctx = repo[None]
2980 2980 mctx = wctx.parents()[-1]
2981 2981
2982 2982 # backup pre-resolve (merge uses .orig for its own purposes)
2983 2983 a = repo.wjoin(f)
2984 2984 util.copyfile(a, a + ".resolve")
2985 2985
2986 2986 # resolve file
2987 2987 if ms.resolve(f, wctx, mctx):
2988 2988 ret = 1
2989 2989
2990 2990 # replace filemerge's .orig file with our resolve file
2991 2991 util.rename(a + ".resolve", a + ".orig")
2992 2992
2993 2993 ms.commit()
2994 2994 return ret
2995 2995
2996 2996 def revert(ui, repo, *pats, **opts):
2997 2997 """restore individual files or directories to an earlier state
2998 2998
2999 2999 .. note::
3000 3000 This command is most likely not what you are looking for.
3001 3001 revert will partially overwrite content in the working
3002 3002 directory without changing the working directory parents. Use
3003 3003 :hg:`update -r rev` to check out earlier revisions, or
3004 3004 :hg:`update --clean .` to undo a merge which has added another
3005 3005 parent.
3006 3006
3007 3007 With no revision specified, revert the named files or directories
3008 3008 to the contents they had in the parent of the working directory.
3009 3009 This restores the contents of the affected files to an unmodified
3010 3010 state and unschedules adds, removes, copies, and renames. If the
3011 3011 working directory has two parents, you must explicitly specify a
3012 3012 revision.
3013 3013
3014 3014 Using the -r/--rev option, revert the given files or directories
3015 3015 to their contents as of a specific revision. This can be helpful
3016 3016 to "roll back" some or all of an earlier change. See :hg:`help
3017 3017 dates` for a list of formats valid for -d/--date.
3018 3018
3019 3019 Revert modifies the working directory. It does not commit any
3020 3020 changes, or change the parent of the working directory. If you
3021 3021 revert to a revision other than the parent of the working
3022 3022 directory, the reverted files will thus appear modified
3023 3023 afterwards.
3024 3024
3025 3025 If a file has been deleted, it is restored. If the executable mode
3026 3026 of a file was changed, it is reset.
3027 3027
3028 3028 If names are given, all files matching the names are reverted.
3029 3029 If no arguments are given, no files are reverted.
3030 3030
3031 3031 Modified files are saved with a .orig suffix before reverting.
3032 3032 To disable these backups, use --no-backup.
3033 3033
3034 3034 Returns 0 on success.
3035 3035 """
3036 3036
3037 3037 if opts.get("date"):
3038 3038 if opts.get("rev"):
3039 3039 raise util.Abort(_("you can't specify a revision and a date"))
3040 3040 opts["rev"] = cmdutil.finddate(ui, repo, opts["date"])
3041 3041
3042 3042 if not pats and not opts.get('all'):
3043 3043 raise util.Abort(_('no files or directories specified; '
3044 3044 'use --all to revert the whole repo'))
3045 3045
3046 3046 parent, p2 = repo.dirstate.parents()
3047 3047 if not opts.get('rev') and p2 != nullid:
3048 3048 raise util.Abort(_('uncommitted merge - please provide a '
3049 3049 'specific revision'))
3050 3050 ctx = repo[opts.get('rev')]
3051 3051 node = ctx.node()
3052 3052 mf = ctx.manifest()
3053 3053 if node == parent:
3054 3054 pmf = mf
3055 3055 else:
3056 3056 pmf = None
3057 3057
3058 3058 # need all matching names in dirstate and manifest of target rev,
3059 3059 # so have to walk both. do not print errors if files exist in one
3060 3060 # but not other.
3061 3061
3062 3062 names = {}
3063 3063
3064 3064 wlock = repo.wlock()
3065 3065 try:
3066 3066 # walk dirstate.
3067 3067
3068 3068 m = cmdutil.match(repo, pats, opts)
3069 3069 m.bad = lambda x, y: False
3070 3070 for abs in repo.walk(m):
3071 3071 names[abs] = m.rel(abs), m.exact(abs)
3072 3072
3073 3073 # walk target manifest.
3074 3074
3075 3075 def badfn(path, msg):
3076 3076 if path in names:
3077 3077 return
3078 3078 path_ = path + '/'
3079 3079 for f in names:
3080 3080 if f.startswith(path_):
3081 3081 return
3082 3082 ui.warn("%s: %s\n" % (m.rel(path), msg))
3083 3083
3084 3084 m = cmdutil.match(repo, pats, opts)
3085 3085 m.bad = badfn
3086 3086 for abs in repo[node].walk(m):
3087 3087 if abs not in names:
3088 3088 names[abs] = m.rel(abs), m.exact(abs)
3089 3089
3090 3090 m = cmdutil.matchfiles(repo, names)
3091 3091 changes = repo.status(match=m)[:4]
3092 3092 modified, added, removed, deleted = map(set, changes)
3093 3093
3094 3094 # if f is a rename, also revert the source
3095 3095 cwd = repo.getcwd()
3096 3096 for f in added:
3097 3097 src = repo.dirstate.copied(f)
3098 3098 if src and src not in names and repo.dirstate[src] == 'r':
3099 3099 removed.add(src)
3100 3100 names[src] = (repo.pathto(src, cwd), True)
3101 3101
3102 3102 def removeforget(abs):
3103 3103 if repo.dirstate[abs] == 'a':
3104 3104 return _('forgetting %s\n')
3105 3105 return _('removing %s\n')
3106 3106
3107 3107 revert = ([], _('reverting %s\n'))
3108 3108 add = ([], _('adding %s\n'))
3109 3109 remove = ([], removeforget)
3110 3110 undelete = ([], _('undeleting %s\n'))
3111 3111
3112 3112 disptable = (
3113 3113 # dispatch table:
3114 3114 # file state
3115 3115 # action if in target manifest
3116 3116 # action if not in target manifest
3117 3117 # make backup if in target manifest
3118 3118 # make backup if not in target manifest
3119 3119 (modified, revert, remove, True, True),
3120 3120 (added, revert, remove, True, False),
3121 3121 (removed, undelete, None, False, False),
3122 3122 (deleted, revert, remove, False, False),
3123 3123 )
3124 3124
3125 3125 for abs, (rel, exact) in sorted(names.items()):
3126 3126 mfentry = mf.get(abs)
3127 3127 target = repo.wjoin(abs)
3128 3128 def handle(xlist, dobackup):
3129 3129 xlist[0].append(abs)
3130 3130 if (dobackup and not opts.get('no_backup') and
3131 3131 os.path.lexists(target)):
3132 3132 bakname = "%s.orig" % rel
3133 3133 ui.note(_('saving current version of %s as %s\n') %
3134 3134 (rel, bakname))
3135 3135 if not opts.get('dry_run'):
3136 3136 util.rename(target, bakname)
3137 3137 if ui.verbose or not exact:
3138 3138 msg = xlist[1]
3139 3139 if not isinstance(msg, basestring):
3140 3140 msg = msg(abs)
3141 3141 ui.status(msg % rel)
3142 3142 for table, hitlist, misslist, backuphit, backupmiss in disptable:
3143 3143 if abs not in table:
3144 3144 continue
3145 3145 # file has changed in dirstate
3146 3146 if mfentry:
3147 3147 handle(hitlist, backuphit)
3148 3148 elif misslist is not None:
3149 3149 handle(misslist, backupmiss)
3150 3150 break
3151 3151 else:
3152 3152 if abs not in repo.dirstate:
3153 3153 if mfentry:
3154 3154 handle(add, True)
3155 3155 elif exact:
3156 3156 ui.warn(_('file not managed: %s\n') % rel)
3157 3157 continue
3158 3158 # file has not changed in dirstate
3159 3159 if node == parent:
3160 3160 if exact:
3161 3161 ui.warn(_('no changes needed to %s\n') % rel)
3162 3162 continue
3163 3163 if pmf is None:
3164 3164 # only need parent manifest in this unlikely case,
3165 3165 # so do not read by default
3166 3166 pmf = repo[parent].manifest()
3167 3167 if abs in pmf:
3168 3168 if mfentry:
3169 3169 # if version of file is same in parent and target
3170 3170 # manifests, do nothing
3171 3171 if (pmf[abs] != mfentry or
3172 3172 pmf.flags(abs) != mf.flags(abs)):
3173 3173 handle(revert, False)
3174 3174 else:
3175 3175 handle(remove, False)
3176 3176
3177 3177 if not opts.get('dry_run'):
3178 3178 def checkout(f):
3179 3179 fc = ctx[f]
3180 3180 repo.wwrite(f, fc.data(), fc.flags())
3181 3181
3182 3182 audit_path = util.path_auditor(repo.root)
3183 3183 for f in remove[0]:
3184 3184 if repo.dirstate[f] == 'a':
3185 3185 repo.dirstate.forget(f)
3186 3186 continue
3187 3187 audit_path(f)
3188 3188 try:
3189 3189 util.unlink(repo.wjoin(f))
3190 3190 except OSError:
3191 3191 pass
3192 3192 repo.dirstate.remove(f)
3193 3193
3194 3194 normal = None
3195 3195 if node == parent:
3196 3196 # We're reverting to our parent. If possible, we'd like status
3197 3197 # to report the file as clean. We have to use normallookup for
3198 3198 # merges to avoid losing information about merged/dirty files.
3199 3199 if p2 != nullid:
3200 3200 normal = repo.dirstate.normallookup
3201 3201 else:
3202 3202 normal = repo.dirstate.normal
3203 3203 for f in revert[0]:
3204 3204 checkout(f)
3205 3205 if normal:
3206 3206 normal(f)
3207 3207
3208 3208 for f in add[0]:
3209 3209 checkout(f)
3210 3210 repo.dirstate.add(f)
3211 3211
3212 3212 normal = repo.dirstate.normallookup
3213 3213 if node == parent and p2 == nullid:
3214 3214 normal = repo.dirstate.normal
3215 3215 for f in undelete[0]:
3216 3216 checkout(f)
3217 3217 normal(f)
3218 3218
3219 3219 finally:
3220 3220 wlock.release()
3221 3221
3222 3222 def rollback(ui, repo, **opts):
3223 3223 """roll back the last transaction (dangerous)
3224 3224
3225 3225 This command should be used with care. There is only one level of
3226 3226 rollback, and there is no way to undo a rollback. It will also
3227 3227 restore the dirstate at the time of the last transaction, losing
3228 3228 any dirstate changes since that time. This command does not alter
3229 3229 the working directory.
3230 3230
3231 3231 Transactions are used to encapsulate the effects of all commands
3232 3232 that create new changesets or propagate existing changesets into a
3233 3233 repository. For example, the following commands are transactional,
3234 3234 and their effects can be rolled back:
3235 3235
3236 3236 - commit
3237 3237 - import
3238 3238 - pull
3239 3239 - push (with this repository as the destination)
3240 3240 - unbundle
3241 3241
3242 3242 This command is not intended for use on public repositories. Once
3243 3243 changes are visible for pull by other users, rolling a transaction
3244 3244 back locally is ineffective (someone else may already have pulled
3245 3245 the changes). Furthermore, a race is possible with readers of the
3246 3246 repository; for example an in-progress pull from the repository
3247 3247 may fail if a rollback is performed.
3248 3248
3249 3249 Returns 0 on success, 1 if no rollback data is available.
3250 3250 """
3251 3251 return repo.rollback(opts.get('dry_run'))
3252 3252
3253 3253 def root(ui, repo):
3254 3254 """print the root (top) of the current working directory
3255 3255
3256 3256 Print the root directory of the current repository.
3257 3257
3258 3258 Returns 0 on success.
3259 3259 """
3260 3260 ui.write(repo.root + "\n")
3261 3261
3262 3262 def serve(ui, repo, **opts):
3263 3263 """start stand-alone webserver
3264 3264
3265 3265 Start a local HTTP repository browser and pull server. You can use
3266 3266 this for ad-hoc sharing and browing of repositories. It is
3267 3267 recommended to use a real web server to serve a repository for
3268 3268 longer periods of time.
3269 3269
3270 3270 Please note that the server does not implement access control.
3271 3271 This means that, by default, anybody can read from the server and
3272 3272 nobody can write to it by default. Set the ``web.allow_push``
3273 3273 option to ``*`` to allow everybody to push to the server. You
3274 3274 should use a real web server if you need to authenticate users.
3275 3275
3276 3276 By default, the server logs accesses to stdout and errors to
3277 3277 stderr. Use the -A/--accesslog and -E/--errorlog options to log to
3278 3278 files.
3279 3279
3280 3280 To have the server choose a free port number to listen on, specify
3281 3281 a port number of 0; in this case, the server will print the port
3282 3282 number it uses.
3283 3283
3284 3284 Returns 0 on success.
3285 3285 """
3286 3286
3287 3287 if opts["stdio"]:
3288 3288 if repo is None:
3289 3289 raise error.RepoError(_("There is no Mercurial repository here"
3290 3290 " (.hg not found)"))
3291 3291 s = sshserver.sshserver(ui, repo)
3292 3292 s.serve_forever()
3293 3293
3294 3294 # this way we can check if something was given in the command-line
3295 3295 if opts.get('port'):
3296 3296 opts['port'] = util.getport(opts.get('port'))
3297 3297
3298 3298 baseui = repo and repo.baseui or ui
3299 3299 optlist = ("name templates style address port prefix ipv6"
3300 3300 " accesslog errorlog certificate encoding")
3301 3301 for o in optlist.split():
3302 3302 val = opts.get(o, '')
3303 3303 if val in (None, ''): # should check against default options instead
3304 3304 continue
3305 3305 baseui.setconfig("web", o, val)
3306 3306 if repo and repo.ui != baseui:
3307 3307 repo.ui.setconfig("web", o, val)
3308 3308
3309 3309 o = opts.get('web_conf') or opts.get('webdir_conf')
3310 3310 if not o:
3311 3311 if not repo:
3312 3312 raise error.RepoError(_("There is no Mercurial repository"
3313 3313 " here (.hg not found)"))
3314 3314 o = repo.root
3315 3315
3316 3316 app = hgweb.hgweb(o, baseui=ui)
3317 3317
3318 3318 class service(object):
3319 3319 def init(self):
3320 3320 util.set_signal_handler()
3321 3321 self.httpd = hgweb.server.create_server(ui, app)
3322 3322
3323 3323 if opts['port'] and not ui.verbose:
3324 3324 return
3325 3325
3326 3326 if self.httpd.prefix:
3327 3327 prefix = self.httpd.prefix.strip('/') + '/'
3328 3328 else:
3329 3329 prefix = ''
3330 3330
3331 3331 port = ':%d' % self.httpd.port
3332 3332 if port == ':80':
3333 3333 port = ''
3334 3334
3335 3335 bindaddr = self.httpd.addr
3336 3336 if bindaddr == '0.0.0.0':
3337 3337 bindaddr = '*'
3338 3338 elif ':' in bindaddr: # IPv6
3339 3339 bindaddr = '[%s]' % bindaddr
3340 3340
3341 3341 fqaddr = self.httpd.fqaddr
3342 3342 if ':' in fqaddr:
3343 3343 fqaddr = '[%s]' % fqaddr
3344 3344 if opts['port']:
3345 3345 write = ui.status
3346 3346 else:
3347 3347 write = ui.write
3348 3348 write(_('listening at http://%s%s/%s (bound to %s:%d)\n') %
3349 3349 (fqaddr, port, prefix, bindaddr, self.httpd.port))
3350 3350
3351 3351 def run(self):
3352 3352 self.httpd.serve_forever()
3353 3353
3354 3354 service = service()
3355 3355
3356 3356 cmdutil.service(opts, initfn=service.init, runfn=service.run)
3357 3357
3358 3358 def status(ui, repo, *pats, **opts):
3359 3359 """show changed files in the working directory
3360 3360
3361 3361 Show status of files in the repository. If names are given, only
3362 3362 files that match are shown. Files that are clean or ignored or
3363 3363 the source of a copy/move operation, are not listed unless
3364 3364 -c/--clean, -i/--ignored, -C/--copies or -A/--all are given.
3365 3365 Unless options described with "show only ..." are given, the
3366 3366 options -mardu are used.
3367 3367
3368 3368 Option -q/--quiet hides untracked (unknown and ignored) files
3369 3369 unless explicitly requested with -u/--unknown or -i/--ignored.
3370 3370
3371 3371 .. note::
3372 3372 status may appear to disagree with diff if permissions have
3373 3373 changed or a merge has occurred. The standard diff format does
3374 3374 not report permission changes and diff only reports changes
3375 3375 relative to one merge parent.
3376 3376
3377 3377 If one revision is given, it is used as the base revision.
3378 3378 If two revisions are given, the differences between them are
3379 3379 shown. The --change option can also be used as a shortcut to list
3380 3380 the changed files of a revision from its first parent.
3381 3381
3382 3382 The codes used to show the status of files are::
3383 3383
3384 3384 M = modified
3385 3385 A = added
3386 3386 R = removed
3387 3387 C = clean
3388 3388 ! = missing (deleted by non-hg command, but still tracked)
3389 3389 ? = not tracked
3390 3390 I = ignored
3391 3391 = origin of the previous file listed as A (added)
3392 3392
3393 3393 Returns 0 on success.
3394 3394 """
3395 3395
3396 3396 revs = opts.get('rev')
3397 3397 change = opts.get('change')
3398 3398
3399 3399 if revs and change:
3400 3400 msg = _('cannot specify --rev and --change at the same time')
3401 3401 raise util.Abort(msg)
3402 3402 elif change:
3403 3403 node2 = repo.lookup(change)
3404 3404 node1 = repo[node2].parents()[0].node()
3405 3405 else:
3406 3406 node1, node2 = cmdutil.revpair(repo, revs)
3407 3407
3408 3408 cwd = (pats and repo.getcwd()) or ''
3409 3409 end = opts.get('print0') and '\0' or '\n'
3410 3410 copy = {}
3411 3411 states = 'modified added removed deleted unknown ignored clean'.split()
3412 3412 show = [k for k in states if opts.get(k)]
3413 3413 if opts.get('all'):
3414 3414 show += ui.quiet and (states[:4] + ['clean']) or states
3415 3415 if not show:
3416 3416 show = ui.quiet and states[:4] or states[:5]
3417 3417
3418 3418 stat = repo.status(node1, node2, cmdutil.match(repo, pats, opts),
3419 3419 'ignored' in show, 'clean' in show, 'unknown' in show,
3420 3420 opts.get('subrepos'))
3421 3421 changestates = zip(states, 'MAR!?IC', stat)
3422 3422
3423 3423 if (opts.get('all') or opts.get('copies')) and not opts.get('no_status'):
3424 3424 ctxn = repo[nullid]
3425 3425 ctx1 = repo[node1]
3426 3426 ctx2 = repo[node2]
3427 3427 added = stat[1]
3428 3428 if node2 is None:
3429 3429 added = stat[0] + stat[1] # merged?
3430 3430
3431 3431 for k, v in copies.copies(repo, ctx1, ctx2, ctxn)[0].iteritems():
3432 3432 if k in added:
3433 3433 copy[k] = v
3434 3434 elif v in added:
3435 3435 copy[v] = k
3436 3436
3437 3437 for state, char, files in changestates:
3438 3438 if state in show:
3439 3439 format = "%s %%s%s" % (char, end)
3440 3440 if opts.get('no_status'):
3441 3441 format = "%%s%s" % end
3442 3442
3443 3443 for f in files:
3444 3444 ui.write(format % repo.pathto(f, cwd),
3445 3445 label='status.' + state)
3446 3446 if f in copy:
3447 3447 ui.write(' %s%s' % (repo.pathto(copy[f], cwd), end),
3448 3448 label='status.copied')
3449 3449
3450 3450 def summary(ui, repo, **opts):
3451 3451 """summarize working directory state
3452 3452
3453 3453 This generates a brief summary of the working directory state,
3454 3454 including parents, branch, commit status, and available updates.
3455 3455
3456 3456 With the --remote option, this will check the default paths for
3457 3457 incoming and outgoing changes. This can be time-consuming.
3458 3458
3459 3459 Returns 0 on success.
3460 3460 """
3461 3461
3462 3462 ctx = repo[None]
3463 3463 parents = ctx.parents()
3464 3464 pnode = parents[0].node()
3465 3465
3466 3466 for p in parents:
3467 3467 # label with log.changeset (instead of log.parent) since this
3468 3468 # shows a working directory parent *changeset*:
3469 3469 ui.write(_('parent: %d:%s ') % (p.rev(), str(p)),
3470 3470 label='log.changeset')
3471 3471 ui.write(' '.join(p.tags()), label='log.tag')
3472 3472 if p.rev() == -1:
3473 3473 if not len(repo):
3474 3474 ui.write(_(' (empty repository)'))
3475 3475 else:
3476 3476 ui.write(_(' (no revision checked out)'))
3477 3477 ui.write('\n')
3478 3478 if p.description():
3479 3479 ui.status(' ' + p.description().splitlines()[0].strip() + '\n',
3480 3480 label='log.summary')
3481 3481
3482 3482 branch = ctx.branch()
3483 3483 bheads = repo.branchheads(branch)
3484 3484 m = _('branch: %s\n') % branch
3485 3485 if branch != 'default':
3486 3486 ui.write(m, label='log.branch')
3487 3487 else:
3488 3488 ui.status(m, label='log.branch')
3489 3489
3490 3490 st = list(repo.status(unknown=True))[:6]
3491 3491
3492 3492 c = repo.dirstate.copies()
3493 3493 copied, renamed = [], []
3494 3494 for d, s in c.iteritems():
3495 3495 if s in st[2]:
3496 3496 st[2].remove(s)
3497 3497 renamed.append(d)
3498 3498 else:
3499 3499 copied.append(d)
3500 3500 if d in st[1]:
3501 3501 st[1].remove(d)
3502 3502 st.insert(3, renamed)
3503 3503 st.insert(4, copied)
3504 3504
3505 3505 ms = mergemod.mergestate(repo)
3506 3506 st.append([f for f in ms if ms[f] == 'u'])
3507 3507
3508 3508 subs = [s for s in ctx.substate if ctx.sub(s).dirty()]
3509 3509 st.append(subs)
3510 3510
3511 3511 labels = [ui.label(_('%d modified'), 'status.modified'),
3512 3512 ui.label(_('%d added'), 'status.added'),
3513 3513 ui.label(_('%d removed'), 'status.removed'),
3514 3514 ui.label(_('%d renamed'), 'status.copied'),
3515 3515 ui.label(_('%d copied'), 'status.copied'),
3516 3516 ui.label(_('%d deleted'), 'status.deleted'),
3517 3517 ui.label(_('%d unknown'), 'status.unknown'),
3518 3518 ui.label(_('%d ignored'), 'status.ignored'),
3519 3519 ui.label(_('%d unresolved'), 'resolve.unresolved'),
3520 3520 ui.label(_('%d subrepos'), 'status.modified')]
3521 3521 t = []
3522 3522 for s, l in zip(st, labels):
3523 3523 if s:
3524 3524 t.append(l % len(s))
3525 3525
3526 3526 t = ', '.join(t)
3527 3527 cleanworkdir = False
3528 3528
3529 3529 if len(parents) > 1:
3530 3530 t += _(' (merge)')
3531 3531 elif branch != parents[0].branch():
3532 3532 t += _(' (new branch)')
3533 3533 elif (parents[0].extra().get('close') and
3534 3534 pnode in repo.branchheads(branch, closed=True)):
3535 3535 t += _(' (head closed)')
3536 3536 elif not (st[0] or st[1] or st[2] or st[3] or st[4] or st[9]):
3537 3537 t += _(' (clean)')
3538 3538 cleanworkdir = True
3539 3539 elif pnode not in bheads:
3540 3540 t += _(' (new branch head)')
3541 3541
3542 3542 if cleanworkdir:
3543 3543 ui.status(_('commit: %s\n') % t.strip())
3544 3544 else:
3545 3545 ui.write(_('commit: %s\n') % t.strip())
3546 3546
3547 3547 # all ancestors of branch heads - all ancestors of parent = new csets
3548 3548 new = [0] * len(repo)
3549 3549 cl = repo.changelog
3550 3550 for a in [cl.rev(n) for n in bheads]:
3551 3551 new[a] = 1
3552 3552 for a in cl.ancestors(*[cl.rev(n) for n in bheads]):
3553 3553 new[a] = 1
3554 3554 for a in [p.rev() for p in parents]:
3555 3555 if a >= 0:
3556 3556 new[a] = 0
3557 3557 for a in cl.ancestors(*[p.rev() for p in parents]):
3558 3558 new[a] = 0
3559 3559 new = sum(new)
3560 3560
3561 3561 if new == 0:
3562 3562 ui.status(_('update: (current)\n'))
3563 3563 elif pnode not in bheads:
3564 3564 ui.write(_('update: %d new changesets (update)\n') % new)
3565 3565 else:
3566 3566 ui.write(_('update: %d new changesets, %d branch heads (merge)\n') %
3567 3567 (new, len(bheads)))
3568 3568
3569 3569 if opts.get('remote'):
3570 3570 t = []
3571 3571 source, branches = hg.parseurl(ui.expandpath('default'))
3572 3572 other = hg.repository(hg.remoteui(repo, {}), source)
3573 3573 revs, checkout = hg.addbranchrevs(repo, other, branches, opts.get('rev'))
3574 3574 ui.debug('comparing with %s\n' % url.hidepassword(source))
3575 3575 repo.ui.pushbuffer()
3576 3576 common, incoming, rheads = discovery.findcommonincoming(repo, other)
3577 3577 repo.ui.popbuffer()
3578 3578 if incoming:
3579 3579 t.append(_('1 or more incoming'))
3580 3580
3581 3581 dest, branches = hg.parseurl(ui.expandpath('default-push', 'default'))
3582 3582 revs, checkout = hg.addbranchrevs(repo, repo, branches, None)
3583 3583 other = hg.repository(hg.remoteui(repo, {}), dest)
3584 3584 ui.debug('comparing with %s\n' % url.hidepassword(dest))
3585 3585 repo.ui.pushbuffer()
3586 3586 o = discovery.findoutgoing(repo, other)
3587 3587 repo.ui.popbuffer()
3588 3588 o = repo.changelog.nodesbetween(o, None)[0]
3589 3589 if o:
3590 3590 t.append(_('%d outgoing') % len(o))
3591 3591
3592 3592 if t:
3593 3593 ui.write(_('remote: %s\n') % (', '.join(t)))
3594 3594 else:
3595 3595 ui.status(_('remote: (synced)\n'))
3596 3596
3597 3597 def tag(ui, repo, name1, *names, **opts):
3598 3598 """add one or more tags for the current or given revision
3599 3599
3600 3600 Name a particular revision using <name>.
3601 3601
3602 3602 Tags are used to name particular revisions of the repository and are
3603 3603 very useful to compare different revisions, to go back to significant
3604 3604 earlier versions or to mark branch points as releases, etc.
3605 3605
3606 3606 If no revision is given, the parent of the working directory is
3607 3607 used, or tip if no revision is checked out.
3608 3608
3609 3609 To facilitate version control, distribution, and merging of tags,
3610 3610 they are stored as a file named ".hgtags" which is managed
3611 3611 similarly to other project files and can be hand-edited if
3612 3612 necessary. The file '.hg/localtags' is used for local tags (not
3613 3613 shared among repositories).
3614 3614
3615 3615 See :hg:`help dates` for a list of formats valid for -d/--date.
3616 3616
3617 3617 Since tag names have priority over branch names during revision
3618 3618 lookup, using an existing branch name as a tag name is discouraged.
3619 3619
3620 3620 Returns 0 on success.
3621 3621 """
3622 3622
3623 3623 rev_ = "."
3624 3624 names = [t.strip() for t in (name1,) + names]
3625 3625 if len(names) != len(set(names)):
3626 3626 raise util.Abort(_('tag names must be unique'))
3627 3627 for n in names:
3628 3628 if n in ['tip', '.', 'null']:
3629 3629 raise util.Abort(_('the name \'%s\' is reserved') % n)
3630 3630 if not n:
3631 3631 raise util.Abort(_('tag names cannot consist entirely of whitespace'))
3632 3632 if opts.get('rev') and opts.get('remove'):
3633 3633 raise util.Abort(_("--rev and --remove are incompatible"))
3634 3634 if opts.get('rev'):
3635 3635 rev_ = opts['rev']
3636 3636 message = opts.get('message')
3637 3637 if opts.get('remove'):
3638 3638 expectedtype = opts.get('local') and 'local' or 'global'
3639 3639 for n in names:
3640 3640 if not repo.tagtype(n):
3641 3641 raise util.Abort(_('tag \'%s\' does not exist') % n)
3642 3642 if repo.tagtype(n) != expectedtype:
3643 3643 if expectedtype == 'global':
3644 3644 raise util.Abort(_('tag \'%s\' is not a global tag') % n)
3645 3645 else:
3646 3646 raise util.Abort(_('tag \'%s\' is not a local tag') % n)
3647 3647 rev_ = nullid
3648 3648 if not message:
3649 3649 # we don't translate commit messages
3650 3650 message = 'Removed tag %s' % ', '.join(names)
3651 3651 elif not opts.get('force'):
3652 3652 for n in names:
3653 3653 if n in repo.tags():
3654 3654 raise util.Abort(_('tag \'%s\' already exists '
3655 3655 '(use -f to force)') % n)
3656 3656 if not rev_ and repo.dirstate.parents()[1] != nullid:
3657 3657 raise util.Abort(_('uncommitted merge - please provide a '
3658 3658 'specific revision'))
3659 3659 r = repo[rev_].node()
3660 3660
3661 3661 if not message:
3662 3662 # we don't translate commit messages
3663 3663 message = ('Added tag %s for changeset %s' %
3664 3664 (', '.join(names), short(r)))
3665 3665
3666 3666 date = opts.get('date')
3667 3667 if date:
3668 3668 date = util.parsedate(date)
3669 3669
3670 3670 if opts.get('edit'):
3671 3671 message = ui.edit(message, ui.username())
3672 3672
3673 3673 repo.tag(names, r, message, opts.get('local'), opts.get('user'), date)
3674 3674
3675 3675 def tags(ui, repo):
3676 3676 """list repository tags
3677 3677
3678 3678 This lists both regular and local tags. When the -v/--verbose
3679 3679 switch is used, a third column "local" is printed for local tags.
3680 3680
3681 3681 Returns 0 on success.
3682 3682 """
3683 3683
3684 3684 hexfunc = ui.debugflag and hex or short
3685 3685 tagtype = ""
3686 3686
3687 3687 for t, n in reversed(repo.tagslist()):
3688 3688 if ui.quiet:
3689 3689 ui.write("%s\n" % t)
3690 3690 continue
3691 3691
3692 3692 try:
3693 3693 hn = hexfunc(n)
3694 3694 r = "%5d:%s" % (repo.changelog.rev(n), hn)
3695 3695 except error.LookupError:
3696 3696 r = " ?:%s" % hn
3697 3697 else:
3698 3698 spaces = " " * (30 - encoding.colwidth(t))
3699 3699 if ui.verbose:
3700 3700 if repo.tagtype(t) == 'local':
3701 3701 tagtype = " local"
3702 3702 else:
3703 3703 tagtype = ""
3704 3704 ui.write("%s%s %s%s\n" % (t, spaces, r, tagtype))
3705 3705
3706 3706 def tip(ui, repo, **opts):
3707 3707 """show the tip revision
3708 3708
3709 3709 The tip revision (usually just called the tip) is the changeset
3710 3710 most recently added to the repository (and therefore the most
3711 3711 recently changed head).
3712 3712
3713 3713 If you have just made a commit, that commit will be the tip. If
3714 3714 you have just pulled changes from another repository, the tip of
3715 3715 that repository becomes the current tip. The "tip" tag is special
3716 3716 and cannot be renamed or assigned to a different changeset.
3717 3717
3718 3718 Returns 0 on success.
3719 3719 """
3720 3720 displayer = cmdutil.show_changeset(ui, repo, opts)
3721 3721 displayer.show(repo[len(repo) - 1])
3722 3722 displayer.close()
3723 3723
3724 3724 def unbundle(ui, repo, fname1, *fnames, **opts):
3725 3725 """apply one or more changegroup files
3726 3726
3727 3727 Apply one or more compressed changegroup files generated by the
3728 3728 bundle command.
3729 3729
3730 3730 Returns 0 on success, 1 if an update has unresolved files.
3731 3731 """
3732 3732 fnames = (fname1,) + fnames
3733 3733
3734 3734 lock = repo.lock()
3735 3735 try:
3736 3736 for fname in fnames:
3737 3737 f = url.open(ui, fname)
3738 3738 gen = changegroup.readbundle(f, fname)
3739 3739 modheads = repo.addchangegroup(gen, 'unbundle', 'bundle:' + fname,
3740 3740 lock=lock)
3741 3741 finally:
3742 3742 lock.release()
3743 3743
3744 3744 return postincoming(ui, repo, modheads, opts.get('update'), None)
3745 3745
3746 3746 def update(ui, repo, node=None, rev=None, clean=False, date=None, check=False):
3747 3747 """update working directory (or switch revisions)
3748 3748
3749 3749 Update the repository's working directory to the specified
3750 3750 changeset. If no changeset is specified, update to the tip of the
3751 3751 current named branch.
3752 3752
3753 3753 If the changeset is not a descendant of the working directory's
3754 3754 parent, the update is aborted. With the -c/--check option, the
3755 3755 working directory is checked for uncommitted changes; if none are
3756 3756 found, the working directory is updated to the specified
3757 3757 changeset.
3758 3758
3759 3759 The following rules apply when the working directory contains
3760 3760 uncommitted changes:
3761 3761
3762 3762 1. If neither -c/--check nor -C/--clean is specified, and if
3763 3763 the requested changeset is an ancestor or descendant of
3764 3764 the working directory's parent, the uncommitted changes
3765 3765 are merged into the requested changeset and the merged
3766 3766 result is left uncommitted. If the requested changeset is
3767 3767 not an ancestor or descendant (that is, it is on another
3768 3768 branch), the update is aborted and the uncommitted changes
3769 3769 are preserved.
3770 3770
3771 3771 2. With the -c/--check option, the update is aborted and the
3772 3772 uncommitted changes are preserved.
3773 3773
3774 3774 3. With the -C/--clean option, uncommitted changes are discarded and
3775 3775 the working directory is updated to the requested changeset.
3776 3776
3777 3777 Use null as the changeset to remove the working directory (like
3778 3778 :hg:`clone -U`).
3779 3779
3780 3780 If you want to update just one file to an older changeset, use
3781 3781 :hg:`revert`.
3782 3782
3783 3783 See :hg:`help dates` for a list of formats valid for -d/--date.
3784 3784
3785 3785 Returns 0 on success, 1 if there are unresolved files.
3786 3786 """
3787 3787 if rev and node:
3788 3788 raise util.Abort(_("please specify just one revision"))
3789 3789
3790 3790 if not rev:
3791 3791 rev = node
3792 3792
3793 3793 if check and clean:
3794 3794 raise util.Abort(_("cannot specify both -c/--check and -C/--clean"))
3795 3795
3796 3796 if check:
3797 3797 # we could use dirty() but we can ignore merge and branch trivia
3798 3798 c = repo[None]
3799 3799 if c.modified() or c.added() or c.removed():
3800 3800 raise util.Abort(_("uncommitted local changes"))
3801 3801
3802 3802 if date:
3803 3803 if rev:
3804 3804 raise util.Abort(_("you can't specify a revision and a date"))
3805 3805 rev = cmdutil.finddate(ui, repo, date)
3806 3806
3807 3807 if clean or check:
3808 3808 return hg.clean(repo, rev)
3809 3809 else:
3810 3810 return hg.update(repo, rev)
3811 3811
3812 3812 def verify(ui, repo):
3813 3813 """verify the integrity of the repository
3814 3814
3815 3815 Verify the integrity of the current repository.
3816 3816
3817 3817 This will perform an extensive check of the repository's
3818 3818 integrity, validating the hashes and checksums of each entry in
3819 3819 the changelog, manifest, and tracked files, as well as the
3820 3820 integrity of their crosslinks and indices.
3821 3821
3822 3822 Returns 0 on success, 1 if errors are encountered.
3823 3823 """
3824 3824 return hg.verify(repo)
3825 3825
3826 3826 def version_(ui):
3827 3827 """output version and copyright information"""
3828 3828 ui.write(_("Mercurial Distributed SCM (version %s)\n")
3829 3829 % util.version())
3830 3830 ui.status(_(
3831 3831 "\nCopyright (C) 2005-2010 Matt Mackall <mpm@selenic.com> and others\n"
3832 3832 "This is free software; see the source for copying conditions. "
3833 3833 "There is NO\nwarranty; "
3834 3834 "not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n"
3835 3835 ))
3836 3836
3837 3837 # Command options and aliases are listed here, alphabetically
3838 3838
3839 3839 globalopts = [
3840 3840 ('R', 'repository', '',
3841 3841 _('repository root directory or name of overlay bundle file'),
3842 3842 _('REPO')),
3843 3843 ('', 'cwd', '',
3844 3844 _('change working directory'), _('DIR')),
3845 3845 ('y', 'noninteractive', None,
3846 3846 _('do not prompt, assume \'yes\' for any required answers')),
3847 3847 ('q', 'quiet', None, _('suppress output')),
3848 3848 ('v', 'verbose', None, _('enable additional output')),
3849 3849 ('', 'config', [],
3850 3850 _('set/override config option (use \'section.name=value\')'),
3851 3851 _('CONFIG')),
3852 3852 ('', 'debug', None, _('enable debugging output')),
3853 3853 ('', 'debugger', None, _('start debugger')),
3854 3854 ('', 'encoding', encoding.encoding, _('set the charset encoding'),
3855 3855 _('ENCODE')),
3856 3856 ('', 'encodingmode', encoding.encodingmode,
3857 3857 _('set the charset encoding mode'), _('MODE')),
3858 3858 ('', 'traceback', None, _('always print a traceback on exception')),
3859 3859 ('', 'time', None, _('time how long the command takes')),
3860 3860 ('', 'profile', None, _('print command execution profile')),
3861 3861 ('', 'version', None, _('output version information and exit')),
3862 3862 ('h', 'help', None, _('display help and exit')),
3863 3863 ]
3864 3864
3865 3865 dryrunopts = [('n', 'dry-run', None,
3866 3866 _('do not perform actions, just print output'))]
3867 3867
3868 3868 remoteopts = [
3869 3869 ('e', 'ssh', '',
3870 3870 _('specify ssh command to use'), _('CMD')),
3871 3871 ('', 'remotecmd', '',
3872 3872 _('specify hg command to run on the remote side'), _('CMD')),
3873 3873 ]
3874 3874
3875 3875 walkopts = [
3876 3876 ('I', 'include', [],
3877 3877 _('include names matching the given patterns'), _('PATTERN')),
3878 3878 ('X', 'exclude', [],
3879 3879 _('exclude names matching the given patterns'), _('PATTERN')),
3880 3880 ]
3881 3881
3882 3882 commitopts = [
3883 3883 ('m', 'message', '',
3884 3884 _('use text as commit message'), _('TEXT')),
3885 3885 ('l', 'logfile', '',
3886 3886 _('read commit message from file'), _('FILE')),
3887 3887 ]
3888 3888
3889 3889 commitopts2 = [
3890 3890 ('d', 'date', '',
3891 3891 _('record datecode as commit date'), _('DATE')),
3892 3892 ('u', 'user', '',
3893 3893 _('record the specified user as committer'), _('USER')),
3894 3894 ]
3895 3895
3896 3896 templateopts = [
3897 3897 ('', 'style', '',
3898 3898 _('display using template map file'), _('STYLE')),
3899 3899 ('', 'template', '',
3900 3900 _('display with template'), _('TEMPLATE')),
3901 3901 ]
3902 3902
3903 3903 logopts = [
3904 3904 ('p', 'patch', None, _('show patch')),
3905 3905 ('g', 'git', None, _('use git extended diff format')),
3906 3906 ('l', 'limit', '',
3907 3907 _('limit number of changes displayed'), _('NUM')),
3908 3908 ('M', 'no-merges', None, _('do not show merges')),
3909 3909 ('', 'stat', None, _('output diffstat-style summary of changes')),
3910 3910 ] + templateopts
3911 3911
3912 3912 diffopts = [
3913 3913 ('a', 'text', None, _('treat all files as text')),
3914 3914 ('g', 'git', None, _('use git extended diff format')),
3915 3915 ('', 'nodates', None, _('omit dates from diff headers'))
3916 3916 ]
3917 3917
3918 3918 diffopts2 = [
3919 3919 ('p', 'show-function', None, _('show which function each change is in')),
3920 3920 ('', 'reverse', None, _('produce a diff that undoes the changes')),
3921 3921 ('w', 'ignore-all-space', None,
3922 3922 _('ignore white space when comparing lines')),
3923 3923 ('b', 'ignore-space-change', None,
3924 3924 _('ignore changes in the amount of white space')),
3925 3925 ('B', 'ignore-blank-lines', None,
3926 3926 _('ignore changes whose lines are all blank')),
3927 3927 ('U', 'unified', '',
3928 3928 _('number of lines of context to show'), _('NUM')),
3929 3929 ('', 'stat', None, _('output diffstat-style summary of changes')),
3930 3930 ]
3931 3931
3932 3932 similarityopts = [
3933 3933 ('s', 'similarity', '',
3934 3934 _('guess renamed files by similarity (0<=s<=100)'), _('SIMILARITY'))
3935 3935 ]
3936 3936
3937 3937 subrepoopts = [
3938 3938 ('S', 'subrepos', None,
3939 3939 _('recurse into subrepositories'))
3940 3940 ]
3941 3941
3942 3942 table = {
3943 3943 "^add": (add, walkopts + subrepoopts + dryrunopts,
3944 3944 _('[OPTION]... [FILE]...')),
3945 3945 "addremove":
3946 3946 (addremove, similarityopts + walkopts + dryrunopts,
3947 3947 _('[OPTION]... [FILE]...')),
3948 3948 "^annotate|blame":
3949 3949 (annotate,
3950 3950 [('r', 'rev', '',
3951 3951 _('annotate the specified revision'), _('REV')),
3952 3952 ('', 'follow', None,
3953 3953 _('follow copies/renames and list the filename (DEPRECATED)')),
3954 3954 ('', 'no-follow', None, _("don't follow copies and renames")),
3955 3955 ('a', 'text', None, _('treat all files as text')),
3956 3956 ('u', 'user', None, _('list the author (long with -v)')),
3957 3957 ('f', 'file', None, _('list the filename')),
3958 3958 ('d', 'date', None, _('list the date (short with -q)')),
3959 3959 ('n', 'number', None, _('list the revision number (default)')),
3960 3960 ('c', 'changeset', None, _('list the changeset')),
3961 3961 ('l', 'line-number', None,
3962 3962 _('show line number at the first appearance'))
3963 3963 ] + walkopts,
3964 3964 _('[-r REV] [-f] [-a] [-u] [-d] [-n] [-c] [-l] FILE...')),
3965 3965 "archive":
3966 3966 (archive,
3967 3967 [('', 'no-decode', None, _('do not pass files through decoders')),
3968 3968 ('p', 'prefix', '',
3969 3969 _('directory prefix for files in archive'), _('PREFIX')),
3970 3970 ('r', 'rev', '',
3971 3971 _('revision to distribute'), _('REV')),
3972 3972 ('t', 'type', '',
3973 3973 _('type of distribution to create'), _('TYPE')),
3974 3974 ] + subrepoopts + walkopts,
3975 3975 _('[OPTION]... DEST')),
3976 3976 "backout":
3977 3977 (backout,
3978 3978 [('', 'merge', None,
3979 3979 _('merge with old dirstate parent after backout')),
3980 3980 ('', 'parent', '',
3981 3981 _('parent to choose when backing out merge'), _('REV')),
3982 3982 ('r', 'rev', '',
3983 3983 _('revision to backout'), _('REV')),
3984 3984 ] + walkopts + commitopts + commitopts2,
3985 3985 _('[OPTION]... [-r] REV')),
3986 3986 "bisect":
3987 3987 (bisect,
3988 3988 [('r', 'reset', False, _('reset bisect state')),
3989 3989 ('g', 'good', False, _('mark changeset good')),
3990 3990 ('b', 'bad', False, _('mark changeset bad')),
3991 3991 ('s', 'skip', False, _('skip testing changeset')),
3992 3992 ('c', 'command', '',
3993 3993 _('use command to check changeset state'), _('CMD')),
3994 3994 ('U', 'noupdate', False, _('do not update to target'))],
3995 3995 _("[-gbsr] [-U] [-c CMD] [REV]")),
3996 3996 "branch":
3997 3997 (branch,
3998 3998 [('f', 'force', None,
3999 3999 _('set branch name even if it shadows an existing branch')),
4000 4000 ('C', 'clean', None, _('reset branch name to parent branch name'))],
4001 4001 _('[-fC] [NAME]')),
4002 4002 "branches":
4003 4003 (branches,
4004 4004 [('a', 'active', False,
4005 4005 _('show only branches that have unmerged heads')),
4006 4006 ('c', 'closed', False,
4007 4007 _('show normal and closed branches'))],
4008 4008 _('[-ac]')),
4009 4009 "bundle":
4010 4010 (bundle,
4011 4011 [('f', 'force', None,
4012 4012 _('run even when the destination is unrelated')),
4013 4013 ('r', 'rev', [],
4014 4014 _('a changeset intended to be added to the destination'),
4015 4015 _('REV')),
4016 4016 ('b', 'branch', [],
4017 4017 _('a specific branch you would like to bundle'),
4018 4018 _('BRANCH')),
4019 4019 ('', 'base', [],
4020 4020 _('a base changeset assumed to be available at the destination'),
4021 4021 _('REV')),
4022 4022 ('a', 'all', None, _('bundle all changesets in the repository')),
4023 4023 ('t', 'type', 'bzip2',
4024 4024 _('bundle compression type to use'), _('TYPE')),
4025 4025 ] + remoteopts,
4026 4026 _('[-f] [-t TYPE] [-a] [-r REV]... [--base REV]... FILE [DEST]')),
4027 4027 "cat":
4028 4028 (cat,
4029 4029 [('o', 'output', '',
4030 4030 _('print output to file with formatted name'), _('FORMAT')),
4031 4031 ('r', 'rev', '',
4032 4032 _('print the given revision'), _('REV')),
4033 4033 ('', 'decode', None, _('apply any matching decode filter')),
4034 4034 ] + walkopts,
4035 4035 _('[OPTION]... FILE...')),
4036 4036 "^clone":
4037 4037 (clone,
4038 4038 [('U', 'noupdate', None,
4039 4039 _('the clone will include an empty working copy (only a repository)')),
4040 4040 ('u', 'updaterev', '',
4041 4041 _('revision, tag or branch to check out'), _('REV')),
4042 4042 ('r', 'rev', [],
4043 4043 _('include the specified changeset'), _('REV')),
4044 4044 ('b', 'branch', [],
4045 4045 _('clone only the specified branch'), _('BRANCH')),
4046 4046 ('', 'pull', None, _('use pull protocol to copy metadata')),
4047 4047 ('', 'uncompressed', None,
4048 4048 _('use uncompressed transfer (fast over LAN)')),
4049 4049 ] + remoteopts,
4050 4050 _('[OPTION]... SOURCE [DEST]')),
4051 4051 "^commit|ci":
4052 4052 (commit,
4053 4053 [('A', 'addremove', None,
4054 4054 _('mark new/missing files as added/removed before committing')),
4055 4055 ('', 'close-branch', None,
4056 4056 _('mark a branch as closed, hiding it from the branch list')),
4057 4057 ] + walkopts + commitopts + commitopts2,
4058 4058 _('[OPTION]... [FILE]...')),
4059 4059 "copy|cp":
4060 4060 (copy,
4061 4061 [('A', 'after', None, _('record a copy that has already occurred')),
4062 4062 ('f', 'force', None,
4063 4063 _('forcibly copy over an existing managed file')),
4064 4064 ] + walkopts + dryrunopts,
4065 4065 _('[OPTION]... [SOURCE]... DEST')),
4066 4066 "debugancestor": (debugancestor, [], _('[INDEX] REV1 REV2')),
4067 4067 "debugbuilddag":
4068 4068 (debugbuilddag,
4069 4069 [('m', 'mergeable-file', None, _('add single file mergeable changes')),
4070 4070 ('a', 'appended-file', None, _('add single file all revs append to')),
4071 4071 ('o', 'overwritten-file', None, _('add single file all revs overwrite')),
4072 4072 ('n', 'new-file', None, _('add new file at each rev')),
4073 4073 ],
4074 4074 _('[OPTION]... TEXT')),
4075 4075 "debugcheckstate": (debugcheckstate, [], ''),
4076 4076 "debugcommands": (debugcommands, [], _('[COMMAND]')),
4077 4077 "debugcomplete":
4078 4078 (debugcomplete,
4079 4079 [('o', 'options', None, _('show the command options'))],
4080 4080 _('[-o] CMD')),
4081 4081 "debugdag":
4082 4082 (debugdag,
4083 4083 [('t', 'tags', None, _('use tags as labels')),
4084 4084 ('b', 'branches', None, _('annotate with branch names')),
4085 4085 ('', 'dots', None, _('use dots for runs')),
4086 4086 ('s', 'spaces', None, _('separate elements by spaces')),
4087 4087 ],
4088 4088 _('[OPTION]... [FILE [REV]...]')),
4089 4089 "debugdate":
4090 4090 (debugdate,
4091 4091 [('e', 'extended', None, _('try extended date formats'))],
4092 4092 _('[-e] DATE [RANGE]')),
4093 4093 "debugdata": (debugdata, [], _('FILE REV')),
4094 4094 "debugfsinfo": (debugfsinfo, [], _('[PATH]')),
4095 4095 "debugindex": (debugindex, [], _('FILE')),
4096 4096 "debugindexdot": (debugindexdot, [], _('FILE')),
4097 4097 "debuginstall": (debuginstall, [], ''),
4098 4098 "debugpushkey": (debugpushkey, [], _('REPO NAMESPACE [KEY OLD NEW]')),
4099 4099 "debugrebuildstate":
4100 4100 (debugrebuildstate,
4101 4101 [('r', 'rev', '',
4102 4102 _('revision to rebuild to'), _('REV'))],
4103 4103 _('[-r REV] [REV]')),
4104 4104 "debugrename":
4105 4105 (debugrename,
4106 4106 [('r', 'rev', '',
4107 4107 _('revision to debug'), _('REV'))],
4108 4108 _('[-r REV] FILE')),
4109 4109 "debugrevspec":
4110 4110 (debugrevspec, [], ('REVSPEC')),
4111 4111 "debugsetparents":
4112 4112 (debugsetparents, [], _('REV1 [REV2]')),
4113 4113 "debugstate":
4114 4114 (debugstate,
4115 4115 [('', 'nodates', None, _('do not display the saved mtime'))],
4116 4116 _('[OPTION]...')),
4117 4117 "debugsub":
4118 4118 (debugsub,
4119 4119 [('r', 'rev', '',
4120 4120 _('revision to check'), _('REV'))],
4121 4121 _('[-r REV] [REV]')),
4122 4122 "debugwalk": (debugwalk, walkopts, _('[OPTION]... [FILE]...')),
4123 4123 "^diff":
4124 4124 (diff,
4125 4125 [('r', 'rev', [],
4126 4126 _('revision'), _('REV')),
4127 4127 ('c', 'change', '',
4128 4128 _('change made by revision'), _('REV'))
4129 4129 ] + diffopts + diffopts2 + walkopts + subrepoopts,
4130 4130 _('[OPTION]... ([-c REV] | [-r REV1 [-r REV2]]) [FILE]...')),
4131 4131 "^export":
4132 4132 (export,
4133 4133 [('o', 'output', '',
4134 4134 _('print output to file with formatted name'), _('FORMAT')),
4135 4135 ('', 'switch-parent', None, _('diff against the second parent')),
4136 4136 ('r', 'rev', [],
4137 4137 _('revisions to export'), _('REV')),
4138 4138 ] + diffopts,
4139 4139 _('[OPTION]... [-o OUTFILESPEC] REV...')),
4140 4140 "^forget":
4141 4141 (forget,
4142 4142 [] + walkopts,
4143 4143 _('[OPTION]... FILE...')),
4144 4144 "grep":
4145 4145 (grep,
4146 4146 [('0', 'print0', None, _('end fields with NUL')),
4147 4147 ('', 'all', None, _('print all revisions that match')),
4148 4148 ('f', 'follow', None,
4149 4149 _('follow changeset history,'
4150 4150 ' or file history across copies and renames')),
4151 4151 ('i', 'ignore-case', None, _('ignore case when matching')),
4152 4152 ('l', 'files-with-matches', None,
4153 4153 _('print only filenames and revisions that match')),
4154 4154 ('n', 'line-number', None, _('print matching line numbers')),
4155 4155 ('r', 'rev', [],
4156 4156 _('only search files changed within revision range'), _('REV')),
4157 4157 ('u', 'user', None, _('list the author (long with -v)')),
4158 4158 ('d', 'date', None, _('list the date (short with -q)')),
4159 4159 ] + walkopts,
4160 4160 _('[OPTION]... PATTERN [FILE]...')),
4161 4161 "heads":
4162 4162 (heads,
4163 4163 [('r', 'rev', '',
4164 4164 _('show only heads which are descendants of STARTREV'),
4165 4165 _('STARTREV')),
4166 4166 ('t', 'topo', False, _('show topological heads only')),
4167 4167 ('a', 'active', False,
4168 4168 _('show active branchheads only (DEPRECATED)')),
4169 4169 ('c', 'closed', False,
4170 4170 _('show normal and closed branch heads')),
4171 4171 ] + templateopts,
4172 4172 _('[-ac] [-r STARTREV] [REV]...')),
4173 4173 "help": (help_, [], _('[TOPIC]')),
4174 4174 "identify|id":
4175 4175 (identify,
4176 4176 [('r', 'rev', '',
4177 4177 _('identify the specified revision'), _('REV')),
4178 4178 ('n', 'num', None, _('show local revision number')),
4179 4179 ('i', 'id', None, _('show global revision id')),
4180 4180 ('b', 'branch', None, _('show branch')),
4181 4181 ('t', 'tags', None, _('show tags'))],
4182 4182 _('[-nibt] [-r REV] [SOURCE]')),
4183 4183 "import|patch":
4184 4184 (import_,
4185 4185 [('p', 'strip', 1,
4186 4186 _('directory strip option for patch. This has the same '
4187 4187 'meaning as the corresponding patch option'),
4188 4188 _('NUM')),
4189 4189 ('b', 'base', '',
4190 4190 _('base path'), _('PATH')),
4191 4191 ('f', 'force', None,
4192 4192 _('skip check for outstanding uncommitted changes')),
4193 4193 ('', 'no-commit', None,
4194 4194 _("don't commit, just update the working directory")),
4195 4195 ('', 'exact', None,
4196 4196 _('apply patch to the nodes from which it was generated')),
4197 4197 ('', 'import-branch', None,
4198 4198 _('use any branch information in patch (implied by --exact)'))] +
4199 4199 commitopts + commitopts2 + similarityopts,
4200 4200 _('[OPTION]... PATCH...')),
4201 4201 "incoming|in":
4202 4202 (incoming,
4203 4203 [('f', 'force', None,
4204 4204 _('run even if remote repository is unrelated')),
4205 4205 ('n', 'newest-first', None, _('show newest record first')),
4206 4206 ('', 'bundle', '',
4207 4207 _('file to store the bundles into'), _('FILE')),
4208 4208 ('r', 'rev', [],
4209 4209 _('a remote changeset intended to be added'), _('REV')),
4210 4210 ('b', 'branch', [],
4211 4211 _('a specific branch you would like to pull'), _('BRANCH')),
4212 4212 ] + logopts + remoteopts + subrepoopts,
4213 4213 _('[-p] [-n] [-M] [-f] [-r REV]...'
4214 4214 ' [--bundle FILENAME] [SOURCE]')),
4215 4215 "^init":
4216 4216 (init,
4217 4217 remoteopts,
4218 4218 _('[-e CMD] [--remotecmd CMD] [DEST]')),
4219 4219 "locate":
4220 4220 (locate,
4221 4221 [('r', 'rev', '',
4222 4222 _('search the repository as it is in REV'), _('REV')),
4223 4223 ('0', 'print0', None,
4224 4224 _('end filenames with NUL, for use with xargs')),
4225 4225 ('f', 'fullpath', None,
4226 4226 _('print complete paths from the filesystem root')),
4227 4227 ] + walkopts,
4228 4228 _('[OPTION]... [PATTERN]...')),
4229 4229 "^log|history":
4230 4230 (log,
4231 4231 [('f', 'follow', None,
4232 4232 _('follow changeset history,'
4233 4233 ' or file history across copies and renames')),
4234 4234 ('', 'follow-first', None,
4235 4235 _('only follow the first parent of merge changesets')),
4236 4236 ('d', 'date', '',
4237 4237 _('show revisions matching date spec'), _('DATE')),
4238 4238 ('C', 'copies', None, _('show copied files')),
4239 4239 ('k', 'keyword', [],
4240 4240 _('do case-insensitive search for a given text'), _('TEXT')),
4241 4241 ('r', 'rev', [],
4242 4242 _('show the specified revision or range'), _('REV')),
4243 4243 ('', 'removed', None, _('include revisions where files were removed')),
4244 4244 ('m', 'only-merges', None, _('show only merges')),
4245 4245 ('u', 'user', [],
4246 4246 _('revisions committed by user'), _('USER')),
4247 4247 ('', 'only-branch', [],
4248 4248 _('show only changesets within the given named branch (DEPRECATED)'),
4249 4249 _('BRANCH')),
4250 4250 ('b', 'branch', [],
4251 4251 _('show changesets within the given named branch'), _('BRANCH')),
4252 4252 ('P', 'prune', [],
4253 4253 _('do not display revision or any of its ancestors'), _('REV')),
4254 4254 ] + logopts + walkopts,
4255 4255 _('[OPTION]... [FILE]')),
4256 4256 "manifest":
4257 4257 (manifest,
4258 4258 [('r', 'rev', '',
4259 4259 _('revision to display'), _('REV'))],
4260 4260 _('[-r REV]')),
4261 4261 "^merge":
4262 4262 (merge,
4263 4263 [('f', 'force', None, _('force a merge with outstanding changes')),
4264 4264 ('r', 'rev', '',
4265 4265 _('revision to merge'), _('REV')),
4266 4266 ('P', 'preview', None,
4267 4267 _('review revisions to merge (no merge is performed)'))],
4268 4268 _('[-P] [-f] [[-r] REV]')),
4269 4269 "outgoing|out":
4270 4270 (outgoing,
4271 4271 [('f', 'force', None,
4272 4272 _('run even when the destination is unrelated')),
4273 4273 ('r', 'rev', [],
4274 4274 _('a changeset intended to be included in the destination'),
4275 4275 _('REV')),
4276 4276 ('n', 'newest-first', None, _('show newest record first')),
4277 4277 ('b', 'branch', [],
4278 4278 _('a specific branch you would like to push'), _('BRANCH')),
4279 4279 ] + logopts + remoteopts + subrepoopts,
4280 4280 _('[-M] [-p] [-n] [-f] [-r REV]... [DEST]')),
4281 4281 "parents":
4282 4282 (parents,
4283 4283 [('r', 'rev', '',
4284 4284 _('show parents of the specified revision'), _('REV')),
4285 4285 ] + templateopts,
4286 4286 _('[-r REV] [FILE]')),
4287 4287 "paths": (paths, [], _('[NAME]')),
4288 4288 "^pull":
4289 4289 (pull,
4290 4290 [('u', 'update', None,
4291 4291 _('update to new branch head if changesets were pulled')),
4292 4292 ('f', 'force', None,
4293 4293 _('run even when remote repository is unrelated')),
4294 4294 ('r', 'rev', [],
4295 4295 _('a remote changeset intended to be added'), _('REV')),
4296 4296 ('b', 'branch', [],
4297 4297 _('a specific branch you would like to pull'), _('BRANCH')),
4298 4298 ] + remoteopts,
4299 4299 _('[-u] [-f] [-r REV]... [-e CMD] [--remotecmd CMD] [SOURCE]')),
4300 4300 "^push":
4301 4301 (push,
4302 4302 [('f', 'force', None, _('force push')),
4303 4303 ('r', 'rev', [],
4304 4304 _('a changeset intended to be included in the destination'),
4305 4305 _('REV')),
4306 4306 ('b', 'branch', [],
4307 4307 _('a specific branch you would like to push'), _('BRANCH')),
4308 4308 ('', 'new-branch', False, _('allow pushing a new branch')),
4309 4309 ] + remoteopts,
4310 4310 _('[-f] [-r REV]... [-e CMD] [--remotecmd CMD] [DEST]')),
4311 4311 "recover": (recover, []),
4312 4312 "^remove|rm":
4313 4313 (remove,
4314 4314 [('A', 'after', None, _('record delete for missing files')),
4315 4315 ('f', 'force', None,
4316 4316 _('remove (and delete) file even if added or modified')),
4317 4317 ] + walkopts,
4318 4318 _('[OPTION]... FILE...')),
4319 4319 "rename|mv":
4320 4320 (rename,
4321 4321 [('A', 'after', None, _('record a rename that has already occurred')),
4322 4322 ('f', 'force', None,
4323 4323 _('forcibly copy over an existing managed file')),
4324 4324 ] + walkopts + dryrunopts,
4325 4325 _('[OPTION]... SOURCE... DEST')),
4326 4326 "resolve":
4327 4327 (resolve,
4328 4328 [('a', 'all', None, _('select all unresolved files')),
4329 4329 ('l', 'list', None, _('list state of files needing merge')),
4330 4330 ('m', 'mark', None, _('mark files as resolved')),
4331 4331 ('u', 'unmark', None, _('mark files as unresolved')),
4332 4332 ('n', 'no-status', None, _('hide status prefix'))]
4333 4333 + walkopts,
4334 4334 _('[OPTION]... [FILE]...')),
4335 4335 "revert":
4336 4336 (revert,
4337 4337 [('a', 'all', None, _('revert all changes when no arguments given')),
4338 4338 ('d', 'date', '',
4339 4339 _('tipmost revision matching date'), _('DATE')),
4340 4340 ('r', 'rev', '',
4341 4341 _('revert to the specified revision'), _('REV')),
4342 4342 ('', 'no-backup', None, _('do not save backup copies of files')),
4343 4343 ] + walkopts + dryrunopts,
4344 4344 _('[OPTION]... [-r REV] [NAME]...')),
4345 4345 "rollback": (rollback, dryrunopts),
4346 4346 "root": (root, []),
4347 4347 "^serve":
4348 4348 (serve,
4349 4349 [('A', 'accesslog', '',
4350 4350 _('name of access log file to write to'), _('FILE')),
4351 4351 ('d', 'daemon', None, _('run server in background')),
4352 4352 ('', 'daemon-pipefds', '',
4353 4353 _('used internally by daemon mode'), _('NUM')),
4354 4354 ('E', 'errorlog', '',
4355 4355 _('name of error log file to write to'), _('FILE')),
4356 4356 # use string type, then we can check if something was passed
4357 4357 ('p', 'port', '',
4358 4358 _('port to listen on (default: 8000)'), _('PORT')),
4359 4359 ('a', 'address', '',
4360 4360 _('address to listen on (default: all interfaces)'), _('ADDR')),
4361 4361 ('', 'prefix', '',
4362 4362 _('prefix path to serve from (default: server root)'), _('PREFIX')),
4363 4363 ('n', 'name', '',
4364 4364 _('name to show in web pages (default: working directory)'),
4365 4365 _('NAME')),
4366 4366 ('', 'web-conf', '',
4367 4367 _('name of the hgweb config file (see "hg help hgweb")'),
4368 4368 _('FILE')),
4369 4369 ('', 'webdir-conf', '',
4370 4370 _('name of the hgweb config file (DEPRECATED)'), _('FILE')),
4371 4371 ('', 'pid-file', '',
4372 4372 _('name of file to write process ID to'), _('FILE')),
4373 4373 ('', 'stdio', None, _('for remote clients')),
4374 4374 ('t', 'templates', '',
4375 4375 _('web templates to use'), _('TEMPLATE')),
4376 4376 ('', 'style', '',
4377 4377 _('template style to use'), _('STYLE')),
4378 4378 ('6', 'ipv6', None, _('use IPv6 in addition to IPv4')),
4379 4379 ('', 'certificate', '',
4380 4380 _('SSL certificate file'), _('FILE'))],
4381 4381 _('[OPTION]...')),
4382 4382 "showconfig|debugconfig":
4383 4383 (showconfig,
4384 4384 [('u', 'untrusted', None, _('show untrusted configuration options'))],
4385 4385 _('[-u] [NAME]...')),
4386 4386 "^summary|sum":
4387 4387 (summary,
4388 4388 [('', 'remote', None, _('check for push and pull'))], '[--remote]'),
4389 4389 "^status|st":
4390 4390 (status,
4391 4391 [('A', 'all', None, _('show status of all files')),
4392 4392 ('m', 'modified', None, _('show only modified files')),
4393 4393 ('a', 'added', None, _('show only added files')),
4394 4394 ('r', 'removed', None, _('show only removed files')),
4395 4395 ('d', 'deleted', None, _('show only deleted (but tracked) files')),
4396 4396 ('c', 'clean', None, _('show only files without changes')),
4397 4397 ('u', 'unknown', None, _('show only unknown (not tracked) files')),
4398 4398 ('i', 'ignored', None, _('show only ignored files')),
4399 4399 ('n', 'no-status', None, _('hide status prefix')),
4400 4400 ('C', 'copies', None, _('show source of copied files')),
4401 4401 ('0', 'print0', None,
4402 4402 _('end filenames with NUL, for use with xargs')),
4403 4403 ('', 'rev', [],
4404 4404 _('show difference from revision'), _('REV')),
4405 4405 ('', 'change', '',
4406 4406 _('list the changed files of a revision'), _('REV')),
4407 4407 ] + walkopts + subrepoopts,
4408 4408 _('[OPTION]... [FILE]...')),
4409 4409 "tag":
4410 4410 (tag,
4411 4411 [('f', 'force', None, _('replace existing tag')),
4412 4412 ('l', 'local', None, _('make the tag local')),
4413 4413 ('r', 'rev', '',
4414 4414 _('revision to tag'), _('REV')),
4415 4415 ('', 'remove', None, _('remove a tag')),
4416 4416 # -l/--local is already there, commitopts cannot be used
4417 4417 ('e', 'edit', None, _('edit commit message')),
4418 4418 ('m', 'message', '',
4419 4419 _('use <text> as commit message'), _('TEXT')),
4420 4420 ] + commitopts2,
4421 4421 _('[-f] [-l] [-m TEXT] [-d DATE] [-u USER] [-r REV] NAME...')),
4422 4422 "tags": (tags, [], ''),
4423 4423 "tip":
4424 4424 (tip,
4425 4425 [('p', 'patch', None, _('show patch')),
4426 4426 ('g', 'git', None, _('use git extended diff format')),
4427 4427 ] + templateopts,
4428 4428 _('[-p] [-g]')),
4429 4429 "unbundle":
4430 4430 (unbundle,
4431 4431 [('u', 'update', None,
4432 4432 _('update to new branch head if changesets were unbundled'))],
4433 4433 _('[-u] FILE...')),
4434 4434 "^update|up|checkout|co":
4435 4435 (update,
4436 4436 [('C', 'clean', None, _('discard uncommitted changes (no backup)')),
4437 4437 ('c', 'check', None,
4438 4438 _('update across branches if no uncommitted changes')),
4439 4439 ('d', 'date', '',
4440 4440 _('tipmost revision matching date'), _('DATE')),
4441 4441 ('r', 'rev', '',
4442 4442 _('revision'), _('REV'))],
4443 4443 _('[-c] [-C] [-d DATE] [[-r] REV]')),
4444 4444 "verify": (verify, []),
4445 4445 "version": (version_, []),
4446 4446 }
4447 4447
4448 4448 norepo = ("clone init version help debugcommands debugcomplete"
4449 4449 " debugdate debuginstall debugfsinfo debugpushkey")
4450 4450 optionalrepo = ("identify paths serve showconfig debugancestor debugdag"
4451 4451 " debugdata debugindex debugindexdot")
@@ -1,1436 +1,1434
1 1 # util.py - Mercurial utility functions and platform specfic implementations
2 2 #
3 3 # Copyright 2005 K. Thananchayan <thananck@yahoo.com>
4 4 # Copyright 2005-2007 Matt Mackall <mpm@selenic.com>
5 5 # Copyright 2006 Vadim Gelfer <vadim.gelfer@gmail.com>
6 6 #
7 7 # This software may be used and distributed according to the terms of the
8 8 # GNU General Public License version 2 or any later version.
9 9
10 10 """Mercurial utility functions and platform specfic implementations.
11 11
12 12 This contains helper routines that are independent of the SCM core and
13 13 hide platform-specific details from the core.
14 14 """
15 15
16 16 from i18n import _
17 17 import error, osutil, encoding
18 18 import errno, re, shutil, sys, tempfile, traceback
19 19 import os, stat, time, calendar, textwrap, unicodedata, signal
20 20 import imp, socket
21 21
22 22 # Python compatibility
23 23
24 24 def sha1(s):
25 25 return _fastsha1(s)
26 26
27 27 def _fastsha1(s):
28 28 # This function will import sha1 from hashlib or sha (whichever is
29 29 # available) and overwrite itself with it on the first call.
30 30 # Subsequent calls will go directly to the imported function.
31 31 if sys.version_info >= (2, 5):
32 32 from hashlib import sha1 as _sha1
33 33 else:
34 34 from sha import sha as _sha1
35 35 global _fastsha1, sha1
36 36 _fastsha1 = sha1 = _sha1
37 37 return _sha1(s)
38 38
39 39 import __builtin__
40 40
41 41 if sys.version_info[0] < 3:
42 42 def fakebuffer(sliceable, offset=0):
43 43 return sliceable[offset:]
44 44 else:
45 45 def fakebuffer(sliceable, offset=0):
46 46 return memoryview(sliceable)[offset:]
47 47 try:
48 48 buffer
49 49 except NameError:
50 50 __builtin__.buffer = fakebuffer
51 51
52 52 import subprocess
53 53 closefds = os.name == 'posix'
54 54
55 55 def popen2(cmd, env=None, newlines=False):
56 56 # Setting bufsize to -1 lets the system decide the buffer size.
57 57 # The default for bufsize is 0, meaning unbuffered. This leads to
58 58 # poor performance on Mac OS X: http://bugs.python.org/issue4194
59 59 p = subprocess.Popen(cmd, shell=True, bufsize=-1,
60 60 close_fds=closefds,
61 61 stdin=subprocess.PIPE, stdout=subprocess.PIPE,
62 62 universal_newlines=newlines,
63 63 env=env)
64 64 return p.stdin, p.stdout
65 65
66 66 def popen3(cmd, env=None, newlines=False):
67 67 p = subprocess.Popen(cmd, shell=True, bufsize=-1,
68 68 close_fds=closefds,
69 69 stdin=subprocess.PIPE, stdout=subprocess.PIPE,
70 70 stderr=subprocess.PIPE,
71 71 universal_newlines=newlines,
72 72 env=env)
73 73 return p.stdin, p.stdout, p.stderr
74 74
75 75 def version():
76 76 """Return version information if available."""
77 77 try:
78 78 import __version__
79 79 return __version__.version
80 80 except ImportError:
81 81 return 'unknown'
82 82
83 83 # used by parsedate
84 84 defaultdateformats = (
85 85 '%Y-%m-%d %H:%M:%S',
86 86 '%Y-%m-%d %I:%M:%S%p',
87 87 '%Y-%m-%d %H:%M',
88 88 '%Y-%m-%d %I:%M%p',
89 89 '%Y-%m-%d',
90 90 '%m-%d',
91 91 '%m/%d',
92 92 '%m/%d/%y',
93 93 '%m/%d/%Y',
94 94 '%a %b %d %H:%M:%S %Y',
95 95 '%a %b %d %I:%M:%S%p %Y',
96 96 '%a, %d %b %Y %H:%M:%S', # GNU coreutils "/bin/date --rfc-2822"
97 97 '%b %d %H:%M:%S %Y',
98 98 '%b %d %I:%M:%S%p %Y',
99 99 '%b %d %H:%M:%S',
100 100 '%b %d %I:%M:%S%p',
101 101 '%b %d %H:%M',
102 102 '%b %d %I:%M%p',
103 103 '%b %d %Y',
104 104 '%b %d',
105 105 '%H:%M:%S',
106 106 '%I:%M:%S%p',
107 107 '%H:%M',
108 108 '%I:%M%p',
109 109 )
110 110
111 111 extendeddateformats = defaultdateformats + (
112 112 "%Y",
113 113 "%Y-%m",
114 114 "%b",
115 115 "%b %Y",
116 116 )
117 117
118 118 def cachefunc(func):
119 119 '''cache the result of function calls'''
120 120 # XXX doesn't handle keywords args
121 121 cache = {}
122 122 if func.func_code.co_argcount == 1:
123 123 # we gain a small amount of time because
124 124 # we don't need to pack/unpack the list
125 125 def f(arg):
126 126 if arg not in cache:
127 127 cache[arg] = func(arg)
128 128 return cache[arg]
129 129 else:
130 130 def f(*args):
131 131 if args not in cache:
132 132 cache[args] = func(*args)
133 133 return cache[args]
134 134
135 135 return f
136 136
137 137 def lrucachefunc(func):
138 138 '''cache most recent results of function calls'''
139 139 cache = {}
140 140 order = []
141 141 if func.func_code.co_argcount == 1:
142 142 def f(arg):
143 143 if arg not in cache:
144 144 if len(cache) > 20:
145 145 del cache[order.pop(0)]
146 146 cache[arg] = func(arg)
147 147 else:
148 148 order.remove(arg)
149 149 order.append(arg)
150 150 return cache[arg]
151 151 else:
152 152 def f(*args):
153 153 if args not in cache:
154 154 if len(cache) > 20:
155 155 del cache[order.pop(0)]
156 156 cache[args] = func(*args)
157 157 else:
158 158 order.remove(args)
159 159 order.append(args)
160 160 return cache[args]
161 161
162 162 return f
163 163
164 164 class propertycache(object):
165 165 def __init__(self, func):
166 166 self.func = func
167 167 self.name = func.__name__
168 168 def __get__(self, obj, type=None):
169 169 result = self.func(obj)
170 170 setattr(obj, self.name, result)
171 171 return result
172 172
173 173 def pipefilter(s, cmd):
174 174 '''filter string S through command CMD, returning its output'''
175 175 p = subprocess.Popen(cmd, shell=True, close_fds=closefds,
176 176 stdin=subprocess.PIPE, stdout=subprocess.PIPE)
177 177 pout, perr = p.communicate(s)
178 178 return pout
179 179
180 180 def tempfilter(s, cmd):
181 181 '''filter string S through a pair of temporary files with CMD.
182 182 CMD is used as a template to create the real command to be run,
183 183 with the strings INFILE and OUTFILE replaced by the real names of
184 184 the temporary files generated.'''
185 185 inname, outname = None, None
186 186 try:
187 187 infd, inname = tempfile.mkstemp(prefix='hg-filter-in-')
188 188 fp = os.fdopen(infd, 'wb')
189 189 fp.write(s)
190 190 fp.close()
191 191 outfd, outname = tempfile.mkstemp(prefix='hg-filter-out-')
192 192 os.close(outfd)
193 193 cmd = cmd.replace('INFILE', inname)
194 194 cmd = cmd.replace('OUTFILE', outname)
195 195 code = os.system(cmd)
196 196 if sys.platform == 'OpenVMS' and code & 1:
197 197 code = 0
198 198 if code:
199 199 raise Abort(_("command '%s' failed: %s") %
200 200 (cmd, explain_exit(code)))
201 201 return open(outname, 'rb').read()
202 202 finally:
203 203 try:
204 204 if inname:
205 205 os.unlink(inname)
206 206 except:
207 207 pass
208 208 try:
209 209 if outname:
210 210 os.unlink(outname)
211 211 except:
212 212 pass
213 213
214 214 filtertable = {
215 215 'tempfile:': tempfilter,
216 216 'pipe:': pipefilter,
217 217 }
218 218
219 219 def filter(s, cmd):
220 220 "filter a string through a command that transforms its input to its output"
221 221 for name, fn in filtertable.iteritems():
222 222 if cmd.startswith(name):
223 223 return fn(s, cmd[len(name):].lstrip())
224 224 return pipefilter(s, cmd)
225 225
226 226 def binary(s):
227 227 """return true if a string is binary data"""
228 228 return bool(s and '\0' in s)
229 229
230 230 def increasingchunks(source, min=1024, max=65536):
231 231 '''return no less than min bytes per chunk while data remains,
232 232 doubling min after each chunk until it reaches max'''
233 233 def log2(x):
234 234 if not x:
235 235 return 0
236 236 i = 0
237 237 while x:
238 238 x >>= 1
239 239 i += 1
240 240 return i - 1
241 241
242 242 buf = []
243 243 blen = 0
244 244 for chunk in source:
245 245 buf.append(chunk)
246 246 blen += len(chunk)
247 247 if blen >= min:
248 248 if min < max:
249 249 min = min << 1
250 250 nmin = 1 << log2(blen)
251 251 if nmin > min:
252 252 min = nmin
253 253 if min > max:
254 254 min = max
255 255 yield ''.join(buf)
256 256 blen = 0
257 257 buf = []
258 258 if buf:
259 259 yield ''.join(buf)
260 260
261 261 Abort = error.Abort
262 262
263 263 def always(fn):
264 264 return True
265 265
266 266 def never(fn):
267 267 return False
268 268
269 269 def pathto(root, n1, n2):
270 270 '''return the relative path from one place to another.
271 271 root should use os.sep to separate directories
272 272 n1 should use os.sep to separate directories
273 273 n2 should use "/" to separate directories
274 274 returns an os.sep-separated path.
275 275
276 276 If n1 is a relative path, it's assumed it's
277 277 relative to root.
278 278 n2 should always be relative to root.
279 279 '''
280 280 if not n1:
281 281 return localpath(n2)
282 282 if os.path.isabs(n1):
283 283 if os.path.splitdrive(root)[0] != os.path.splitdrive(n1)[0]:
284 284 return os.path.join(root, localpath(n2))
285 285 n2 = '/'.join((pconvert(root), n2))
286 286 a, b = splitpath(n1), n2.split('/')
287 287 a.reverse()
288 288 b.reverse()
289 289 while a and b and a[-1] == b[-1]:
290 290 a.pop()
291 291 b.pop()
292 292 b.reverse()
293 293 return os.sep.join((['..'] * len(a)) + b) or '.'
294 294
295 295 def canonpath(root, cwd, myname, auditor=None):
296 296 """return the canonical path of myname, given cwd and root"""
297 297 if endswithsep(root):
298 298 rootsep = root
299 299 else:
300 300 rootsep = root + os.sep
301 301 name = myname
302 302 if not os.path.isabs(name):
303 303 name = os.path.join(root, cwd, name)
304 304 name = os.path.normpath(name)
305 305 if auditor is None:
306 306 auditor = path_auditor(root)
307 307 if name != rootsep and name.startswith(rootsep):
308 308 name = name[len(rootsep):]
309 309 auditor(name)
310 310 return pconvert(name)
311 311 elif name == root:
312 312 return ''
313 313 else:
314 314 # Determine whether `name' is in the hierarchy at or beneath `root',
315 315 # by iterating name=dirname(name) until that causes no change (can't
316 316 # check name == '/', because that doesn't work on windows). For each
317 317 # `name', compare dev/inode numbers. If they match, the list `rel'
318 318 # holds the reversed list of components making up the relative file
319 319 # name we want.
320 320 root_st = os.stat(root)
321 321 rel = []
322 322 while True:
323 323 try:
324 324 name_st = os.stat(name)
325 325 except OSError:
326 326 break
327 327 if samestat(name_st, root_st):
328 328 if not rel:
329 329 # name was actually the same as root (maybe a symlink)
330 330 return ''
331 331 rel.reverse()
332 332 name = os.path.join(*rel)
333 333 auditor(name)
334 334 return pconvert(name)
335 335 dirname, basename = os.path.split(name)
336 336 rel.append(basename)
337 337 if dirname == name:
338 338 break
339 339 name = dirname
340 340
341 341 raise Abort('%s not under root' % myname)
342 342
343 343 _hgexecutable = None
344 344
345 345 def main_is_frozen():
346 346 """return True if we are a frozen executable.
347 347
348 348 The code supports py2exe (most common, Windows only) and tools/freeze
349 349 (portable, not much used).
350 350 """
351 351 return (hasattr(sys, "frozen") or # new py2exe
352 352 hasattr(sys, "importers") or # old py2exe
353 353 imp.is_frozen("__main__")) # tools/freeze
354 354
355 355 def hgexecutable():
356 356 """return location of the 'hg' executable.
357 357
358 358 Defaults to $HG or 'hg' in the search path.
359 359 """
360 360 if _hgexecutable is None:
361 361 hg = os.environ.get('HG')
362 362 if hg:
363 363 set_hgexecutable(hg)
364 364 elif main_is_frozen():
365 365 set_hgexecutable(sys.executable)
366 366 else:
367 367 exe = find_exe('hg') or os.path.basename(sys.argv[0])
368 368 set_hgexecutable(exe)
369 369 return _hgexecutable
370 370
371 371 def set_hgexecutable(path):
372 372 """set location of the 'hg' executable"""
373 373 global _hgexecutable
374 374 _hgexecutable = path
375 375
376 376 def system(cmd, environ={}, cwd=None, onerr=None, errprefix=None, out=None):
377 377 '''enhanced shell command execution.
378 378 run with environment maybe modified, maybe in different dir.
379 379
380 380 if command fails and onerr is None, return status. if ui object,
381 381 print error message and return status, else raise onerr object as
382 382 exception.
383 383
384 384 if out is specified, it is assumed to be a file-like object that has a
385 385 write() method. stdout and stderr will be redirected to out.'''
386 386 def py2shell(val):
387 387 'convert python object into string that is useful to shell'
388 388 if val is None or val is False:
389 389 return '0'
390 390 if val is True:
391 391 return '1'
392 392 return str(val)
393 393 origcmd = cmd
394 394 if os.name == 'nt':
395 395 cmd = '"%s"' % cmd
396 396 env = dict(os.environ)
397 397 env.update((k, py2shell(v)) for k, v in environ.iteritems())
398 398 env['HG'] = hgexecutable()
399 399 if out is None:
400 400 rc = subprocess.call(cmd, shell=True, close_fds=closefds,
401 401 env=env, cwd=cwd)
402 402 else:
403 403 proc = subprocess.Popen(cmd, shell=True, close_fds=closefds,
404 404 env=env, cwd=cwd, stdout=subprocess.PIPE,
405 405 stderr=subprocess.STDOUT)
406 406 for line in proc.stdout:
407 407 out.write(line)
408 408 proc.wait()
409 409 rc = proc.returncode
410 410 if sys.platform == 'OpenVMS' and rc & 1:
411 411 rc = 0
412 412 if rc and onerr:
413 413 errmsg = '%s %s' % (os.path.basename(origcmd.split(None, 1)[0]),
414 414 explain_exit(rc)[0])
415 415 if errprefix:
416 416 errmsg = '%s: %s' % (errprefix, errmsg)
417 417 try:
418 418 onerr.warn(errmsg + '\n')
419 419 except AttributeError:
420 420 raise onerr(errmsg)
421 421 return rc
422 422
423 423 def checksignature(func):
424 424 '''wrap a function with code to check for calling errors'''
425 425 def check(*args, **kwargs):
426 426 try:
427 427 return func(*args, **kwargs)
428 428 except TypeError:
429 429 if len(traceback.extract_tb(sys.exc_info()[2])) == 1:
430 430 raise error.SignatureError
431 431 raise
432 432
433 433 return check
434 434
435 435 def unlink(f):
436 436 """unlink and remove the directory if it is empty"""
437 437 os.unlink(f)
438 438 # try removing directories that might now be empty
439 439 try:
440 440 os.removedirs(os.path.dirname(f))
441 441 except OSError:
442 442 pass
443 443
444 444 def copyfile(src, dest):
445 445 "copy a file, preserving mode and atime/mtime"
446 446 if os.path.islink(src):
447 447 try:
448 448 os.unlink(dest)
449 449 except:
450 450 pass
451 451 os.symlink(os.readlink(src), dest)
452 452 else:
453 453 try:
454 454 shutil.copyfile(src, dest)
455 455 shutil.copystat(src, dest)
456 456 except shutil.Error, inst:
457 457 raise Abort(str(inst))
458 458
459 459 def copyfiles(src, dst, hardlink=None):
460 460 """Copy a directory tree using hardlinks if possible"""
461 461
462 462 if hardlink is None:
463 463 hardlink = (os.stat(src).st_dev ==
464 464 os.stat(os.path.dirname(dst)).st_dev)
465 465
466 466 num = 0
467 467 if os.path.isdir(src):
468 468 os.mkdir(dst)
469 469 for name, kind in osutil.listdir(src):
470 470 srcname = os.path.join(src, name)
471 471 dstname = os.path.join(dst, name)
472 472 hardlink, n = copyfiles(srcname, dstname, hardlink)
473 473 num += n
474 474 else:
475 475 if hardlink:
476 476 try:
477 477 os_link(src, dst)
478 478 except (IOError, OSError):
479 479 hardlink = False
480 480 shutil.copy(src, dst)
481 481 else:
482 482 shutil.copy(src, dst)
483 483 num += 1
484 484
485 485 return hardlink, num
486 486
487 487 class path_auditor(object):
488 488 '''ensure that a filesystem path contains no banned components.
489 489 the following properties of a path are checked:
490 490
491 491 - under top-level .hg
492 492 - starts at the root of a windows drive
493 493 - contains ".."
494 494 - traverses a symlink (e.g. a/symlink_here/b)
495 495 - inside a nested repository (a callback can be used to approve
496 496 some nested repositories, e.g., subrepositories)
497 497 '''
498 498
499 499 def __init__(self, root, callback=None):
500 500 self.audited = set()
501 501 self.auditeddir = set()
502 502 self.root = root
503 503 self.callback = callback
504 504
505 505 def __call__(self, path):
506 506 if path in self.audited:
507 507 return
508 508 normpath = os.path.normcase(path)
509 509 parts = splitpath(normpath)
510 510 if (os.path.splitdrive(path)[0]
511 511 or parts[0].lower() in ('.hg', '.hg.', '')
512 512 or os.pardir in parts):
513 513 raise Abort(_("path contains illegal component: %s") % path)
514 514 if '.hg' in path.lower():
515 515 lparts = [p.lower() for p in parts]
516 516 for p in '.hg', '.hg.':
517 517 if p in lparts[1:]:
518 518 pos = lparts.index(p)
519 519 base = os.path.join(*parts[:pos])
520 520 raise Abort(_('path %r is inside repo %r') % (path, base))
521 521 def check(prefix):
522 522 curpath = os.path.join(self.root, prefix)
523 523 try:
524 524 st = os.lstat(curpath)
525 525 except OSError, err:
526 526 # EINVAL can be raised as invalid path syntax under win32.
527 527 # They must be ignored for patterns can be checked too.
528 528 if err.errno not in (errno.ENOENT, errno.ENOTDIR, errno.EINVAL):
529 529 raise
530 530 else:
531 531 if stat.S_ISLNK(st.st_mode):
532 532 raise Abort(_('path %r traverses symbolic link %r') %
533 533 (path, prefix))
534 534 elif (stat.S_ISDIR(st.st_mode) and
535 535 os.path.isdir(os.path.join(curpath, '.hg'))):
536 536 if not self.callback or not self.callback(curpath):
537 537 raise Abort(_('path %r is inside repo %r') %
538 538 (path, prefix))
539 539 parts.pop()
540 540 prefixes = []
541 541 while parts:
542 542 prefix = os.sep.join(parts)
543 543 if prefix in self.auditeddir:
544 544 break
545 545 check(prefix)
546 546 prefixes.append(prefix)
547 547 parts.pop()
548 548
549 549 self.audited.add(path)
550 550 # only add prefixes to the cache after checking everything: we don't
551 551 # want to add "foo/bar/baz" before checking if there's a "foo/.hg"
552 552 self.auditeddir.update(prefixes)
553 553
554 554 def nlinks(pathname):
555 555 """Return number of hardlinks for the given file."""
556 556 return os.lstat(pathname).st_nlink
557 557
558 558 if hasattr(os, 'link'):
559 559 os_link = os.link
560 560 else:
561 561 def os_link(src, dst):
562 562 raise OSError(0, _("Hardlinks not supported"))
563 563
564 564 def lookup_reg(key, name=None, scope=None):
565 565 return None
566 566
567 567 def hidewindow():
568 568 """Hide current shell window.
569 569
570 570 Used to hide the window opened when starting asynchronous
571 571 child process under Windows, unneeded on other systems.
572 572 """
573 573 pass
574 574
575 575 if os.name == 'nt':
576 576 from windows import *
577 577 else:
578 578 from posix import *
579 579
580 580 def makelock(info, pathname):
581 581 try:
582 582 return os.symlink(info, pathname)
583 583 except OSError, why:
584 584 if why.errno == errno.EEXIST:
585 585 raise
586 586 except AttributeError: # no symlink in os
587 587 pass
588 588
589 589 ld = os.open(pathname, os.O_CREAT | os.O_WRONLY | os.O_EXCL)
590 590 os.write(ld, info)
591 591 os.close(ld)
592 592
593 593 def readlock(pathname):
594 594 try:
595 595 return os.readlink(pathname)
596 596 except OSError, why:
597 597 if why.errno not in (errno.EINVAL, errno.ENOSYS):
598 598 raise
599 599 except AttributeError: # no symlink in os
600 600 pass
601 601 return posixfile(pathname).read()
602 602
603 603 def fstat(fp):
604 604 '''stat file object that may not have fileno method.'''
605 605 try:
606 606 return os.fstat(fp.fileno())
607 607 except AttributeError:
608 608 return os.stat(fp.name)
609 609
610 610 # File system features
611 611
612 612 def checkcase(path):
613 613 """
614 614 Check whether the given path is on a case-sensitive filesystem
615 615
616 616 Requires a path (like /foo/.hg) ending with a foldable final
617 617 directory component.
618 618 """
619 619 s1 = os.stat(path)
620 620 d, b = os.path.split(path)
621 621 p2 = os.path.join(d, b.upper())
622 622 if path == p2:
623 623 p2 = os.path.join(d, b.lower())
624 624 try:
625 625 s2 = os.stat(p2)
626 626 if s2 == s1:
627 627 return False
628 628 return True
629 629 except:
630 630 return True
631 631
632 632 _fspathcache = {}
633 633 def fspath(name, root):
634 634 '''Get name in the case stored in the filesystem
635 635
636 636 The name is either relative to root, or it is an absolute path starting
637 637 with root. Note that this function is unnecessary, and should not be
638 638 called, for case-sensitive filesystems (simply because it's expensive).
639 639 '''
640 640 # If name is absolute, make it relative
641 641 if name.lower().startswith(root.lower()):
642 642 l = len(root)
643 643 if name[l] == os.sep or name[l] == os.altsep:
644 644 l = l + 1
645 645 name = name[l:]
646 646
647 647 if not os.path.lexists(os.path.join(root, name)):
648 648 return None
649 649
650 650 seps = os.sep
651 651 if os.altsep:
652 652 seps = seps + os.altsep
653 653 # Protect backslashes. This gets silly very quickly.
654 654 seps.replace('\\','\\\\')
655 655 pattern = re.compile(r'([^%s]+)|([%s]+)' % (seps, seps))
656 656 dir = os.path.normcase(os.path.normpath(root))
657 657 result = []
658 658 for part, sep in pattern.findall(name):
659 659 if sep:
660 660 result.append(sep)
661 661 continue
662 662
663 663 if dir not in _fspathcache:
664 664 _fspathcache[dir] = os.listdir(dir)
665 665 contents = _fspathcache[dir]
666 666
667 667 lpart = part.lower()
668 668 lenp = len(part)
669 669 for n in contents:
670 670 if lenp == len(n) and n.lower() == lpart:
671 671 result.append(n)
672 672 break
673 673 else:
674 674 # Cannot happen, as the file exists!
675 675 result.append(part)
676 676 dir = os.path.join(dir, lpart)
677 677
678 678 return ''.join(result)
679 679
680 680 def checkexec(path):
681 681 """
682 682 Check whether the given path is on a filesystem with UNIX-like exec flags
683 683
684 684 Requires a directory (like /foo/.hg)
685 685 """
686 686
687 687 # VFAT on some Linux versions can flip mode but it doesn't persist
688 688 # a FS remount. Frequently we can detect it if files are created
689 689 # with exec bit on.
690 690
691 691 try:
692 692 EXECFLAGS = stat.S_IXUSR | stat.S_IXGRP | stat.S_IXOTH
693 693 fh, fn = tempfile.mkstemp(dir=path, prefix='hg-checkexec-')
694 694 try:
695 695 os.close(fh)
696 696 m = os.stat(fn).st_mode & 0777
697 697 new_file_has_exec = m & EXECFLAGS
698 698 os.chmod(fn, m ^ EXECFLAGS)
699 699 exec_flags_cannot_flip = ((os.stat(fn).st_mode & 0777) == m)
700 700 finally:
701 701 os.unlink(fn)
702 702 except (IOError, OSError):
703 703 # we don't care, the user probably won't be able to commit anyway
704 704 return False
705 705 return not (new_file_has_exec or exec_flags_cannot_flip)
706 706
707 707 def checklink(path):
708 708 """check whether the given path is on a symlink-capable filesystem"""
709 709 # mktemp is not racy because symlink creation will fail if the
710 710 # file already exists
711 711 name = tempfile.mktemp(dir=path, prefix='hg-checklink-')
712 712 try:
713 713 os.symlink(".", name)
714 714 os.unlink(name)
715 715 return True
716 716 except (OSError, AttributeError):
717 717 return False
718 718
719 719 def endswithsep(path):
720 720 '''Check path ends with os.sep or os.altsep.'''
721 721 return path.endswith(os.sep) or os.altsep and path.endswith(os.altsep)
722 722
723 723 def splitpath(path):
724 724 '''Split path by os.sep.
725 725 Note that this function does not use os.altsep because this is
726 726 an alternative of simple "xxx.split(os.sep)".
727 727 It is recommended to use os.path.normpath() before using this
728 728 function if need.'''
729 729 return path.split(os.sep)
730 730
731 731 def gui():
732 732 '''Are we running in a GUI?'''
733 733 return os.name == "nt" or os.name == "mac" or os.environ.get("DISPLAY")
734 734
735 735 def mktempcopy(name, emptyok=False, createmode=None):
736 736 """Create a temporary file with the same contents from name
737 737
738 738 The permission bits are copied from the original file.
739 739
740 740 If the temporary file is going to be truncated immediately, you
741 741 can use emptyok=True as an optimization.
742 742
743 743 Returns the name of the temporary file.
744 744 """
745 745 d, fn = os.path.split(name)
746 746 fd, temp = tempfile.mkstemp(prefix='.%s-' % fn, dir=d)
747 747 os.close(fd)
748 748 # Temporary files are created with mode 0600, which is usually not
749 749 # what we want. If the original file already exists, just copy
750 750 # its mode. Otherwise, manually obey umask.
751 751 try:
752 752 st_mode = os.lstat(name).st_mode & 0777
753 753 except OSError, inst:
754 754 if inst.errno != errno.ENOENT:
755 755 raise
756 756 st_mode = createmode
757 757 if st_mode is None:
758 758 st_mode = ~umask
759 759 st_mode &= 0666
760 760 os.chmod(temp, st_mode)
761 761 if emptyok:
762 762 return temp
763 763 try:
764 764 try:
765 765 ifp = posixfile(name, "rb")
766 766 except IOError, inst:
767 767 if inst.errno == errno.ENOENT:
768 768 return temp
769 769 if not getattr(inst, 'filename', None):
770 770 inst.filename = name
771 771 raise
772 772 ofp = posixfile(temp, "wb")
773 773 for chunk in filechunkiter(ifp):
774 774 ofp.write(chunk)
775 775 ifp.close()
776 776 ofp.close()
777 777 except:
778 778 try: os.unlink(temp)
779 779 except: pass
780 780 raise
781 781 return temp
782 782
783 783 class atomictempfile(object):
784 784 """file-like object that atomically updates a file
785 785
786 786 All writes will be redirected to a temporary copy of the original
787 787 file. When rename is called, the copy is renamed to the original
788 788 name, making the changes visible.
789 789 """
790 790 def __init__(self, name, mode='w+b', createmode=None):
791 791 self.__name = name
792 792 self._fp = None
793 793 self.temp = mktempcopy(name, emptyok=('w' in mode),
794 794 createmode=createmode)
795 795 self._fp = posixfile(self.temp, mode)
796 796
797 797 def __getattr__(self, name):
798 798 return getattr(self._fp, name)
799 799
800 800 def rename(self):
801 801 if not self._fp.closed:
802 802 self._fp.close()
803 803 rename(self.temp, localpath(self.__name))
804 804
805 805 def __del__(self):
806 806 if not self._fp:
807 807 return
808 808 if not self._fp.closed:
809 809 try:
810 810 os.unlink(self.temp)
811 811 except: pass
812 812 self._fp.close()
813 813
814 814 def makedirs(name, mode=None):
815 815 """recursive directory creation with parent mode inheritance"""
816 816 try:
817 817 os.mkdir(name)
818 818 if mode is not None:
819 819 os.chmod(name, mode)
820 820 return
821 821 except OSError, err:
822 822 if err.errno == errno.EEXIST:
823 823 return
824 824 if err.errno != errno.ENOENT:
825 825 raise
826 826 parent = os.path.abspath(os.path.dirname(name))
827 827 makedirs(parent, mode)
828 828 makedirs(name, mode)
829 829
830 830 class opener(object):
831 831 """Open files relative to a base directory
832 832
833 833 This class is used to hide the details of COW semantics and
834 834 remote file access from higher level code.
835 835 """
836 836 def __init__(self, base, audit=True):
837 837 self.base = base
838 838 if audit:
839 839 self.auditor = path_auditor(base)
840 840 else:
841 841 self.auditor = always
842 842 self.createmode = None
843 843
844 844 @propertycache
845 845 def _can_symlink(self):
846 846 return checklink(self.base)
847 847
848 848 def _fixfilemode(self, name):
849 849 if self.createmode is None:
850 850 return
851 851 os.chmod(name, self.createmode & 0666)
852 852
853 853 def __call__(self, path, mode="r", text=False, atomictemp=False):
854 854 self.auditor(path)
855 855 f = os.path.join(self.base, path)
856 856
857 857 if not text and "b" not in mode:
858 858 mode += "b" # for that other OS
859 859
860 860 nlink = -1
861 861 if mode not in ("r", "rb"):
862 862 try:
863 863 nlink = nlinks(f)
864 864 except OSError:
865 865 nlink = 0
866 866 d = os.path.dirname(f)
867 867 if not os.path.isdir(d):
868 868 makedirs(d, self.createmode)
869 869 if atomictemp:
870 870 return atomictempfile(f, mode, self.createmode)
871 871 if nlink > 1:
872 872 rename(mktempcopy(f), f)
873 873 fp = posixfile(f, mode)
874 874 if nlink == 0:
875 875 self._fixfilemode(f)
876 876 return fp
877 877
878 878 def symlink(self, src, dst):
879 879 self.auditor(dst)
880 880 linkname = os.path.join(self.base, dst)
881 881 try:
882 882 os.unlink(linkname)
883 883 except OSError:
884 884 pass
885 885
886 886 dirname = os.path.dirname(linkname)
887 887 if not os.path.exists(dirname):
888 888 makedirs(dirname, self.createmode)
889 889
890 890 if self._can_symlink:
891 891 try:
892 892 os.symlink(src, linkname)
893 893 except OSError, err:
894 894 raise OSError(err.errno, _('could not symlink to %r: %s') %
895 895 (src, err.strerror), linkname)
896 896 else:
897 897 f = self(dst, "w")
898 898 f.write(src)
899 899 f.close()
900 900 self._fixfilemode(dst)
901 901
902 902 class chunkbuffer(object):
903 903 """Allow arbitrary sized chunks of data to be efficiently read from an
904 904 iterator over chunks of arbitrary size."""
905 905
906 906 def __init__(self, in_iter):
907 907 """in_iter is the iterator that's iterating over the input chunks.
908 908 targetsize is how big a buffer to try to maintain."""
909 909 def splitbig(chunks):
910 910 for chunk in chunks:
911 911 if len(chunk) > 2**20:
912 912 pos = 0
913 913 while pos < len(chunk):
914 914 end = pos + 2 ** 18
915 915 yield chunk[pos:end]
916 916 pos = end
917 917 else:
918 918 yield chunk
919 919 self.iter = splitbig(in_iter)
920 920 self._queue = []
921 921
922 922 def read(self, l):
923 923 """Read L bytes of data from the iterator of chunks of data.
924 924 Returns less than L bytes if the iterator runs dry."""
925 925 left = l
926 926 buf = ''
927 927 queue = self._queue
928 928 while left > 0:
929 929 # refill the queue
930 930 if not queue:
931 931 target = 2**18
932 932 for chunk in self.iter:
933 933 queue.append(chunk)
934 934 target -= len(chunk)
935 935 if target <= 0:
936 936 break
937 937 if not queue:
938 938 break
939 939
940 940 chunk = queue.pop(0)
941 941 left -= len(chunk)
942 942 if left < 0:
943 943 queue.insert(0, chunk[left:])
944 944 buf += chunk[:left]
945 945 else:
946 946 buf += chunk
947 947
948 948 return buf
949 949
950 950 def filechunkiter(f, size=65536, limit=None):
951 951 """Create a generator that produces the data in the file size
952 952 (default 65536) bytes at a time, up to optional limit (default is
953 953 to read all data). Chunks may be less than size bytes if the
954 954 chunk is the last chunk in the file, or the file is a socket or
955 955 some other type of file that sometimes reads less data than is
956 956 requested."""
957 957 assert size >= 0
958 958 assert limit is None or limit >= 0
959 959 while True:
960 960 if limit is None:
961 961 nbytes = size
962 962 else:
963 963 nbytes = min(limit, size)
964 964 s = nbytes and f.read(nbytes)
965 965 if not s:
966 966 break
967 967 if limit:
968 968 limit -= len(s)
969 969 yield s
970 970
971 971 def makedate():
972 972 lt = time.localtime()
973 973 if lt[8] == 1 and time.daylight:
974 974 tz = time.altzone
975 975 else:
976 976 tz = time.timezone
977 977 return time.mktime(lt), tz
978 978
979 979 def datestr(date=None, format='%a %b %d %H:%M:%S %Y %1%2'):
980 980 """represent a (unixtime, offset) tuple as a localized time.
981 981 unixtime is seconds since the epoch, and offset is the time zone's
982 982 number of seconds away from UTC. if timezone is false, do not
983 983 append time zone to string."""
984 984 t, tz = date or makedate()
985 985 if "%1" in format or "%2" in format:
986 986 sign = (tz > 0) and "-" or "+"
987 987 minutes = abs(tz) // 60
988 988 format = format.replace("%1", "%c%02d" % (sign, minutes // 60))
989 989 format = format.replace("%2", "%02d" % (minutes % 60))
990 990 s = time.strftime(format, time.gmtime(float(t) - tz))
991 991 return s
992 992
993 993 def shortdate(date=None):
994 994 """turn (timestamp, tzoff) tuple into iso 8631 date."""
995 995 return datestr(date, format='%Y-%m-%d')
996 996
997 997 def strdate(string, format, defaults=[]):
998 998 """parse a localized time string and return a (unixtime, offset) tuple.
999 999 if the string cannot be parsed, ValueError is raised."""
1000 1000 def timezone(string):
1001 1001 tz = string.split()[-1]
1002 1002 if tz[0] in "+-" and len(tz) == 5 and tz[1:].isdigit():
1003 1003 sign = (tz[0] == "+") and 1 or -1
1004 1004 hours = int(tz[1:3])
1005 1005 minutes = int(tz[3:5])
1006 1006 return -sign * (hours * 60 + minutes) * 60
1007 1007 if tz == "GMT" or tz == "UTC":
1008 1008 return 0
1009 1009 return None
1010 1010
1011 1011 # NOTE: unixtime = localunixtime + offset
1012 1012 offset, date = timezone(string), string
1013 1013 if offset != None:
1014 1014 date = " ".join(string.split()[:-1])
1015 1015
1016 1016 # add missing elements from defaults
1017 1017 for part in defaults:
1018 1018 found = [True for p in part if ("%"+p) in format]
1019 1019 if not found:
1020 1020 date += "@" + defaults[part]
1021 1021 format += "@%" + part[0]
1022 1022
1023 1023 timetuple = time.strptime(date, format)
1024 1024 localunixtime = int(calendar.timegm(timetuple))
1025 1025 if offset is None:
1026 1026 # local timezone
1027 1027 unixtime = int(time.mktime(timetuple))
1028 1028 offset = unixtime - localunixtime
1029 1029 else:
1030 1030 unixtime = localunixtime + offset
1031 1031 return unixtime, offset
1032 1032
1033 1033 def parsedate(date, formats=None, defaults=None):
1034 1034 """parse a localized date/time string and return a (unixtime, offset) tuple.
1035 1035
1036 1036 The date may be a "unixtime offset" string or in one of the specified
1037 1037 formats. If the date already is a (unixtime, offset) tuple, it is returned.
1038 1038 """
1039 1039 if not date:
1040 1040 return 0, 0
1041 1041 if isinstance(date, tuple) and len(date) == 2:
1042 1042 return date
1043 1043 if not formats:
1044 1044 formats = defaultdateformats
1045 1045 date = date.strip()
1046 1046 try:
1047 1047 when, offset = map(int, date.split(' '))
1048 1048 except ValueError:
1049 1049 # fill out defaults
1050 1050 if not defaults:
1051 1051 defaults = {}
1052 1052 now = makedate()
1053 1053 for part in "d mb yY HI M S".split():
1054 1054 if part not in defaults:
1055 1055 if part[0] in "HMS":
1056 1056 defaults[part] = "00"
1057 1057 else:
1058 1058 defaults[part] = datestr(now, "%" + part[0])
1059 1059
1060 1060 for format in formats:
1061 1061 try:
1062 1062 when, offset = strdate(date, format, defaults)
1063 1063 except (ValueError, OverflowError):
1064 1064 pass
1065 1065 else:
1066 1066 break
1067 1067 else:
1068 1068 raise Abort(_('invalid date: %r') % date)
1069 1069 # validate explicit (probably user-specified) date and
1070 1070 # time zone offset. values must fit in signed 32 bits for
1071 1071 # current 32-bit linux runtimes. timezones go from UTC-12
1072 1072 # to UTC+14
1073 1073 if abs(when) > 0x7fffffff:
1074 1074 raise Abort(_('date exceeds 32 bits: %d') % when)
1075 1075 if offset < -50400 or offset > 43200:
1076 1076 raise Abort(_('impossible time zone offset: %d') % offset)
1077 1077 return when, offset
1078 1078
1079 1079 def matchdate(date):
1080 1080 """Return a function that matches a given date match specifier
1081 1081
1082 1082 Formats include:
1083 1083
1084 1084 '{date}' match a given date to the accuracy provided
1085 1085
1086 1086 '<{date}' on or before a given date
1087 1087
1088 1088 '>{date}' on or after a given date
1089 1089
1090 1090 """
1091 1091
1092 1092 def lower(date):
1093 1093 d = dict(mb="1", d="1")
1094 1094 return parsedate(date, extendeddateformats, d)[0]
1095 1095
1096 1096 def upper(date):
1097 1097 d = dict(mb="12", HI="23", M="59", S="59")
1098 1098 for days in "31 30 29".split():
1099 1099 try:
1100 1100 d["d"] = days
1101 1101 return parsedate(date, extendeddateformats, d)[0]
1102 1102 except:
1103 1103 pass
1104 1104 d["d"] = "28"
1105 1105 return parsedate(date, extendeddateformats, d)[0]
1106 1106
1107 1107 date = date.strip()
1108 1108 if date[0] == "<":
1109 1109 when = upper(date[1:])
1110 1110 return lambda x: x <= when
1111 1111 elif date[0] == ">":
1112 1112 when = lower(date[1:])
1113 1113 return lambda x: x >= when
1114 1114 elif date[0] == "-":
1115 1115 try:
1116 1116 days = int(date[1:])
1117 1117 except ValueError:
1118 1118 raise Abort(_("invalid day spec: %s") % date[1:])
1119 1119 when = makedate()[0] - days * 3600 * 24
1120 1120 return lambda x: x >= when
1121 1121 elif " to " in date:
1122 1122 a, b = date.split(" to ")
1123 1123 start, stop = lower(a), upper(b)
1124 1124 return lambda x: x >= start and x <= stop
1125 1125 else:
1126 1126 start, stop = lower(date), upper(date)
1127 1127 return lambda x: x >= start and x <= stop
1128 1128
1129 1129 def shortuser(user):
1130 1130 """Return a short representation of a user name or email address."""
1131 1131 f = user.find('@')
1132 1132 if f >= 0:
1133 1133 user = user[:f]
1134 1134 f = user.find('<')
1135 1135 if f >= 0:
1136 1136 user = user[f + 1:]
1137 1137 f = user.find(' ')
1138 1138 if f >= 0:
1139 1139 user = user[:f]
1140 1140 f = user.find('.')
1141 1141 if f >= 0:
1142 1142 user = user[:f]
1143 1143 return user
1144 1144
1145 1145 def email(author):
1146 1146 '''get email of author.'''
1147 1147 r = author.find('>')
1148 1148 if r == -1:
1149 1149 r = None
1150 1150 return author[author.find('<') + 1:r]
1151 1151
1152 1152 def ellipsis(text, maxlength=400):
1153 1153 """Trim string to at most maxlength (default: 400) characters."""
1154 1154 if len(text) <= maxlength:
1155 1155 return text
1156 1156 else:
1157 1157 return "%s..." % (text[:maxlength - 3])
1158 1158
1159 1159 def walkrepos(path, followsym=False, seen_dirs=None, recurse=False):
1160 1160 '''yield every hg repository under path, recursively.'''
1161 1161 def errhandler(err):
1162 1162 if err.filename == path:
1163 1163 raise err
1164 1164 if followsym and hasattr(os.path, 'samestat'):
1165 1165 def _add_dir_if_not_there(dirlst, dirname):
1166 1166 match = False
1167 1167 samestat = os.path.samestat
1168 1168 dirstat = os.stat(dirname)
1169 1169 for lstdirstat in dirlst:
1170 1170 if samestat(dirstat, lstdirstat):
1171 1171 match = True
1172 1172 break
1173 1173 if not match:
1174 1174 dirlst.append(dirstat)
1175 1175 return not match
1176 1176 else:
1177 1177 followsym = False
1178 1178
1179 1179 if (seen_dirs is None) and followsym:
1180 1180 seen_dirs = []
1181 1181 _add_dir_if_not_there(seen_dirs, path)
1182 1182 for root, dirs, files in os.walk(path, topdown=True, onerror=errhandler):
1183 1183 dirs.sort()
1184 1184 if '.hg' in dirs:
1185 1185 yield root # found a repository
1186 1186 qroot = os.path.join(root, '.hg', 'patches')
1187 1187 if os.path.isdir(os.path.join(qroot, '.hg')):
1188 1188 yield qroot # we have a patch queue repo here
1189 1189 if recurse:
1190 1190 # avoid recursing inside the .hg directory
1191 1191 dirs.remove('.hg')
1192 1192 else:
1193 1193 dirs[:] = [] # don't descend further
1194 1194 elif followsym:
1195 1195 newdirs = []
1196 1196 for d in dirs:
1197 1197 fname = os.path.join(root, d)
1198 1198 if _add_dir_if_not_there(seen_dirs, fname):
1199 1199 if os.path.islink(fname):
1200 1200 for hgname in walkrepos(fname, True, seen_dirs):
1201 1201 yield hgname
1202 1202 else:
1203 1203 newdirs.append(d)
1204 1204 dirs[:] = newdirs
1205 1205
1206 1206 _rcpath = None
1207 1207
1208 1208 def os_rcpath():
1209 1209 '''return default os-specific hgrc search path'''
1210 1210 path = system_rcpath()
1211 1211 path.extend(user_rcpath())
1212 1212 path = [os.path.normpath(f) for f in path]
1213 1213 return path
1214 1214
1215 1215 def rcpath():
1216 1216 '''return hgrc search path. if env var HGRCPATH is set, use it.
1217 1217 for each item in path, if directory, use files ending in .rc,
1218 1218 else use item.
1219 1219 make HGRCPATH empty to only look in .hg/hgrc of current repo.
1220 1220 if no HGRCPATH, use default os-specific path.'''
1221 1221 global _rcpath
1222 1222 if _rcpath is None:
1223 1223 if 'HGRCPATH' in os.environ:
1224 1224 _rcpath = []
1225 1225 for p in os.environ['HGRCPATH'].split(os.pathsep):
1226 1226 if not p:
1227 1227 continue
1228 1228 p = expandpath(p)
1229 1229 if os.path.isdir(p):
1230 1230 for f, kind in osutil.listdir(p):
1231 1231 if f.endswith('.rc'):
1232 1232 _rcpath.append(os.path.join(p, f))
1233 1233 else:
1234 1234 _rcpath.append(p)
1235 1235 else:
1236 1236 _rcpath = os_rcpath()
1237 1237 return _rcpath
1238 1238
1239 1239 def bytecount(nbytes):
1240 1240 '''return byte count formatted as readable string, with units'''
1241 1241
1242 1242 units = (
1243 1243 (100, 1 << 30, _('%.0f GB')),
1244 1244 (10, 1 << 30, _('%.1f GB')),
1245 1245 (1, 1 << 30, _('%.2f GB')),
1246 1246 (100, 1 << 20, _('%.0f MB')),
1247 1247 (10, 1 << 20, _('%.1f MB')),
1248 1248 (1, 1 << 20, _('%.2f MB')),
1249 1249 (100, 1 << 10, _('%.0f KB')),
1250 1250 (10, 1 << 10, _('%.1f KB')),
1251 1251 (1, 1 << 10, _('%.2f KB')),
1252 1252 (1, 1, _('%.0f bytes')),
1253 1253 )
1254 1254
1255 1255 for multiplier, divisor, format in units:
1256 1256 if nbytes >= divisor * multiplier:
1257 1257 return format % (nbytes / float(divisor))
1258 1258 return units[-1][2] % nbytes
1259 1259
1260 1260 def drop_scheme(scheme, path):
1261 1261 sc = scheme + ':'
1262 1262 if path.startswith(sc):
1263 1263 path = path[len(sc):]
1264 1264 if path.startswith('//'):
1265 1265 if scheme == 'file':
1266 1266 i = path.find('/', 2)
1267 1267 if i == -1:
1268 1268 return ''
1269 1269 # On Windows, absolute paths are rooted at the current drive
1270 1270 # root. On POSIX they are rooted at the file system root.
1271 1271 if os.name == 'nt':
1272 1272 droot = os.path.splitdrive(os.getcwd())[0] + '/'
1273 1273 path = os.path.join(droot, path[i + 1:])
1274 1274 else:
1275 1275 path = path[i:]
1276 1276 else:
1277 1277 path = path[2:]
1278 1278 return path
1279 1279
1280 1280 def uirepr(s):
1281 1281 # Avoid double backslash in Windows path repr()
1282 1282 return repr(s).replace('\\\\', '\\')
1283 1283
1284 1284 #### naming convention of below implementation follows 'textwrap' module
1285 1285
1286 1286 class MBTextWrapper(textwrap.TextWrapper):
1287 1287 def __init__(self, **kwargs):
1288 1288 textwrap.TextWrapper.__init__(self, **kwargs)
1289 1289
1290 1290 def _cutdown(self, str, space_left):
1291 1291 l = 0
1292 1292 ucstr = unicode(str, encoding.encoding)
1293 1293 w = unicodedata.east_asian_width
1294 1294 for i in xrange(len(ucstr)):
1295 1295 l += w(ucstr[i]) in 'WFA' and 2 or 1
1296 1296 if space_left < l:
1297 1297 return (ucstr[:i].encode(encoding.encoding),
1298 1298 ucstr[i:].encode(encoding.encoding))
1299 1299 return str, ''
1300 1300
1301 1301 # ----------------------------------------
1302 1302 # overriding of base class
1303 1303
1304 1304 def _handle_long_word(self, reversed_chunks, cur_line, cur_len, width):
1305 1305 space_left = max(width - cur_len, 1)
1306 1306
1307 1307 if self.break_long_words:
1308 1308 cut, res = self._cutdown(reversed_chunks[-1], space_left)
1309 1309 cur_line.append(cut)
1310 1310 reversed_chunks[-1] = res
1311 1311 elif not cur_line:
1312 1312 cur_line.append(reversed_chunks.pop())
1313 1313
1314 1314 #### naming convention of above implementation follows 'textwrap' module
1315 1315
1316 def wrap(line, width=None, initindent='', hangindent=''):
1317 if width is None:
1318 width = termwidth() - 2
1316 def wrap(line, width, initindent='', hangindent=''):
1319 1317 maxindent = max(len(hangindent), len(initindent))
1320 1318 if width <= maxindent:
1321 1319 # adjust for weird terminal size
1322 1320 width = max(78, maxindent + 1)
1323 1321 wrapper = MBTextWrapper(width=width,
1324 1322 initial_indent=initindent,
1325 1323 subsequent_indent=hangindent)
1326 1324 return wrapper.fill(line)
1327 1325
1328 1326 def iterlines(iterator):
1329 1327 for chunk in iterator:
1330 1328 for line in chunk.splitlines():
1331 1329 yield line
1332 1330
1333 1331 def expandpath(path):
1334 1332 return os.path.expanduser(os.path.expandvars(path))
1335 1333
1336 1334 def hgcmd():
1337 1335 """Return the command used to execute current hg
1338 1336
1339 1337 This is different from hgexecutable() because on Windows we want
1340 1338 to avoid things opening new shell windows like batch files, so we
1341 1339 get either the python call or current executable.
1342 1340 """
1343 1341 if main_is_frozen():
1344 1342 return [sys.executable]
1345 1343 return gethgcmd()
1346 1344
1347 1345 def rundetached(args, condfn):
1348 1346 """Execute the argument list in a detached process.
1349 1347
1350 1348 condfn is a callable which is called repeatedly and should return
1351 1349 True once the child process is known to have started successfully.
1352 1350 At this point, the child process PID is returned. If the child
1353 1351 process fails to start or finishes before condfn() evaluates to
1354 1352 True, return -1.
1355 1353 """
1356 1354 # Windows case is easier because the child process is either
1357 1355 # successfully starting and validating the condition or exiting
1358 1356 # on failure. We just poll on its PID. On Unix, if the child
1359 1357 # process fails to start, it will be left in a zombie state until
1360 1358 # the parent wait on it, which we cannot do since we expect a long
1361 1359 # running process on success. Instead we listen for SIGCHLD telling
1362 1360 # us our child process terminated.
1363 1361 terminated = set()
1364 1362 def handler(signum, frame):
1365 1363 terminated.add(os.wait())
1366 1364 prevhandler = None
1367 1365 if hasattr(signal, 'SIGCHLD'):
1368 1366 prevhandler = signal.signal(signal.SIGCHLD, handler)
1369 1367 try:
1370 1368 pid = spawndetached(args)
1371 1369 while not condfn():
1372 1370 if ((pid in terminated or not testpid(pid))
1373 1371 and not condfn()):
1374 1372 return -1
1375 1373 time.sleep(0.1)
1376 1374 return pid
1377 1375 finally:
1378 1376 if prevhandler is not None:
1379 1377 signal.signal(signal.SIGCHLD, prevhandler)
1380 1378
1381 1379 try:
1382 1380 any, all = any, all
1383 1381 except NameError:
1384 1382 def any(iterable):
1385 1383 for i in iterable:
1386 1384 if i:
1387 1385 return True
1388 1386 return False
1389 1387
1390 1388 def all(iterable):
1391 1389 for i in iterable:
1392 1390 if not i:
1393 1391 return False
1394 1392 return True
1395 1393
1396 1394 def interpolate(prefix, mapping, s, fn=None):
1397 1395 """Return the result of interpolating items in the mapping into string s.
1398 1396
1399 1397 prefix is a single character string, or a two character string with
1400 1398 a backslash as the first character if the prefix needs to be escaped in
1401 1399 a regular expression.
1402 1400
1403 1401 fn is an optional function that will be applied to the replacement text
1404 1402 just before replacement.
1405 1403 """
1406 1404 fn = fn or (lambda s: s)
1407 1405 r = re.compile(r'%s(%s)' % (prefix, '|'.join(mapping.keys())))
1408 1406 return r.sub(lambda x: fn(mapping[x.group()[1:]]), s)
1409 1407
1410 1408 def getport(port):
1411 1409 """Return the port for a given network service.
1412 1410
1413 1411 If port is an integer, it's returned as is. If it's a string, it's
1414 1412 looked up using socket.getservbyname(). If there's no matching
1415 1413 service, util.Abort is raised.
1416 1414 """
1417 1415 try:
1418 1416 return int(port)
1419 1417 except ValueError:
1420 1418 pass
1421 1419
1422 1420 try:
1423 1421 return socket.getservbyname(port)
1424 1422 except socket.error:
1425 1423 raise Abort(_("no port number associated with service '%s'") % port)
1426 1424
1427 1425 _booleans = {'1': True, 'yes': True, 'true': True, 'on': True, 'always': True,
1428 1426 '0': False, 'no': False, 'false': False, 'off': False,
1429 1427 'never': False}
1430 1428
1431 1429 def parsebool(s):
1432 1430 """Parse s into a boolean.
1433 1431
1434 1432 If s is not a valid boolean, returns None.
1435 1433 """
1436 1434 return _booleans.get(s.lower(), None)
General Comments 0
You need to be logged in to leave comments. Login now