##// END OF EJS Templates
Merge with mpm
Brendan Cully -
r3819:6099cfa7 merge default
parent child Browse files
Show More
@@ -1,3114 +1,3133
1 1 # commands.py - command processing for mercurial
2 2 #
3 3 # Copyright 2005, 2006 Matt Mackall <mpm@selenic.com>
4 4 #
5 5 # This software may be used and distributed according to the terms
6 6 # of the GNU General Public License, incorporated herein by reference.
7 7
8 8 from demandload import demandload
9 9 from node import *
10 10 from i18n import gettext as _
11 11 demandload(globals(), "bisect os re sys signal imp urllib pdb shlex stat")
12 12 demandload(globals(), "fancyopts ui hg util lock revlog bundlerepo")
13 13 demandload(globals(), "difflib patch time help")
14 14 demandload(globals(), "traceback errno version atexit")
15 15 demandload(globals(), "archival changegroup cmdutil hgweb.server sshserver")
16 16
17 17 class UnknownCommand(Exception):
18 18 """Exception raised if command is not in the command table."""
19 19 class AmbiguousCommand(Exception):
20 20 """Exception raised if command shortcut matches more than one command."""
21 21
22 22 def bail_if_changed(repo):
23 23 modified, added, removed, deleted = repo.status()[:4]
24 24 if modified or added or removed or deleted:
25 25 raise util.Abort(_("outstanding uncommitted changes"))
26 26
27 27 def logmessage(opts):
28 28 """ get the log message according to -m and -l option """
29 29 message = opts['message']
30 30 logfile = opts['logfile']
31 31
32 32 if message and logfile:
33 33 raise util.Abort(_('options --message and --logfile are mutually '
34 34 'exclusive'))
35 35 if not message and logfile:
36 36 try:
37 37 if logfile == '-':
38 38 message = sys.stdin.read()
39 39 else:
40 40 message = open(logfile).read()
41 41 except IOError, inst:
42 42 raise util.Abort(_("can't read commit message '%s': %s") %
43 43 (logfile, inst.strerror))
44 44 return message
45 45
46 46 def setremoteconfig(ui, opts):
47 47 "copy remote options to ui tree"
48 48 if opts.get('ssh'):
49 49 ui.setconfig("ui", "ssh", opts['ssh'])
50 50 if opts.get('remotecmd'):
51 51 ui.setconfig("ui", "remotecmd", opts['remotecmd'])
52 52
53 53 # Commands start here, listed alphabetically
54 54
55 55 def add(ui, repo, *pats, **opts):
56 56 """add the specified files on the next commit
57 57
58 58 Schedule files to be version controlled and added to the repository.
59 59
60 60 The files will be added to the repository at the next commit.
61 61
62 62 If no names are given, add all files in the repository.
63 63 """
64 64
65 65 names = []
66 66 for src, abs, rel, exact in cmdutil.walk(repo, pats, opts):
67 67 if exact:
68 68 if ui.verbose:
69 69 ui.status(_('adding %s\n') % rel)
70 70 names.append(abs)
71 71 elif repo.dirstate.state(abs) == '?':
72 72 ui.status(_('adding %s\n') % rel)
73 73 names.append(abs)
74 74 if not opts.get('dry_run'):
75 75 repo.add(names)
76 76
77 77 def addremove(ui, repo, *pats, **opts):
78 78 """add all new files, delete all missing files
79 79
80 80 Add all new files and remove all missing files from the repository.
81 81
82 82 New files are ignored if they match any of the patterns in .hgignore. As
83 83 with add, these changes take effect at the next commit.
84 84
85 85 Use the -s option to detect renamed files. With a parameter > 0,
86 86 this compares every removed file with every added file and records
87 87 those similar enough as renames. This option takes a percentage
88 88 between 0 (disabled) and 100 (files must be identical) as its
89 89 parameter. Detecting renamed files this way can be expensive.
90 90 """
91 91 sim = float(opts.get('similarity') or 0)
92 92 if sim < 0 or sim > 100:
93 93 raise util.Abort(_('similarity must be between 0 and 100'))
94 94 return cmdutil.addremove(repo, pats, opts, similarity=sim/100.)
95 95
96 96 def annotate(ui, repo, *pats, **opts):
97 97 """show changeset information per file line
98 98
99 99 List changes in files, showing the revision id responsible for each line
100 100
101 101 This command is useful to discover who did a change or when a change took
102 102 place.
103 103
104 104 Without the -a option, annotate will avoid processing files it
105 105 detects as binary. With -a, annotate will generate an annotation
106 106 anyway, probably with undesirable results.
107 107 """
108 108 getdate = util.cachefunc(lambda x: util.datestr(x.date()))
109 109
110 110 if not pats:
111 111 raise util.Abort(_('at least one file name or pattern required'))
112 112
113 113 opmap = [['user', lambda x: ui.shortuser(x.user())],
114 114 ['number', lambda x: str(x.rev())],
115 115 ['changeset', lambda x: short(x.node())],
116 116 ['date', getdate], ['follow', lambda x: x.path()]]
117 117 if (not opts['user'] and not opts['changeset'] and not opts['date']
118 118 and not opts['follow']):
119 119 opts['number'] = 1
120 120
121 121 ctx = repo.changectx(opts['rev'])
122 122
123 123 for src, abs, rel, exact in cmdutil.walk(repo, pats, opts,
124 124 node=ctx.node()):
125 125 fctx = ctx.filectx(abs)
126 126 if not opts['text'] and util.binary(fctx.data()):
127 127 ui.write(_("%s: binary file\n") % ((pats and rel) or abs))
128 128 continue
129 129
130 130 lines = fctx.annotate(follow=opts.get('follow'))
131 131 pieces = []
132 132
133 133 for o, f in opmap:
134 134 if opts[o]:
135 135 l = [f(n) for n, dummy in lines]
136 136 if l:
137 137 m = max(map(len, l))
138 138 pieces.append(["%*s" % (m, x) for x in l])
139 139
140 140 if pieces:
141 141 for p, l in zip(zip(*pieces), lines):
142 142 ui.write("%s: %s" % (" ".join(p), l[1]))
143 143
144 144 def archive(ui, repo, dest, **opts):
145 145 '''create unversioned archive of a repository revision
146 146
147 147 By default, the revision used is the parent of the working
148 148 directory; use "-r" to specify a different revision.
149 149
150 150 To specify the type of archive to create, use "-t". Valid
151 151 types are:
152 152
153 153 "files" (default): a directory full of files
154 154 "tar": tar archive, uncompressed
155 155 "tbz2": tar archive, compressed using bzip2
156 156 "tgz": tar archive, compressed using gzip
157 157 "uzip": zip archive, uncompressed
158 158 "zip": zip archive, compressed using deflate
159 159
160 160 The exact name of the destination archive or directory is given
161 161 using a format string; see "hg help export" for details.
162 162
163 163 Each member added to an archive file has a directory prefix
164 164 prepended. Use "-p" to specify a format string for the prefix.
165 165 The default is the basename of the archive, with suffixes removed.
166 166 '''
167 167
168 168 node = repo.changectx(opts['rev']).node()
169 169 dest = cmdutil.make_filename(repo, dest, node)
170 170 if os.path.realpath(dest) == repo.root:
171 171 raise util.Abort(_('repository root cannot be destination'))
172 172 dummy, matchfn, dummy = cmdutil.matchpats(repo, [], opts)
173 173 kind = opts.get('type') or 'files'
174 174 prefix = opts['prefix']
175 175 if dest == '-':
176 176 if kind == 'files':
177 177 raise util.Abort(_('cannot archive plain files to stdout'))
178 178 dest = sys.stdout
179 179 if not prefix: prefix = os.path.basename(repo.root) + '-%h'
180 180 prefix = cmdutil.make_filename(repo, prefix, node)
181 181 archival.archive(repo, dest, node, kind, not opts['no_decode'],
182 182 matchfn, prefix)
183 183
184 184 def backout(ui, repo, rev, **opts):
185 185 '''reverse effect of earlier changeset
186 186
187 187 Commit the backed out changes as a new changeset. The new
188 188 changeset is a child of the backed out changeset.
189 189
190 190 If you back out a changeset other than the tip, a new head is
191 191 created. This head is the parent of the working directory. If
192 192 you back out an old changeset, your working directory will appear
193 193 old after the backout. You should merge the backout changeset
194 194 with another head.
195 195
196 196 The --merge option remembers the parent of the working directory
197 197 before starting the backout, then merges the new head with that
198 198 changeset afterwards. This saves you from doing the merge by
199 199 hand. The result of this merge is not committed, as for a normal
200 200 merge.'''
201 201
202 202 bail_if_changed(repo)
203 203 op1, op2 = repo.dirstate.parents()
204 204 if op2 != nullid:
205 205 raise util.Abort(_('outstanding uncommitted merge'))
206 206 node = repo.lookup(rev)
207 207 p1, p2 = repo.changelog.parents(node)
208 208 if p1 == nullid:
209 209 raise util.Abort(_('cannot back out a change with no parents'))
210 210 if p2 != nullid:
211 211 if not opts['parent']:
212 212 raise util.Abort(_('cannot back out a merge changeset without '
213 213 '--parent'))
214 214 p = repo.lookup(opts['parent'])
215 215 if p not in (p1, p2):
216 216 raise util.Abort(_('%s is not a parent of %s') %
217 217 (short(p), short(node)))
218 218 parent = p
219 219 else:
220 220 if opts['parent']:
221 221 raise util.Abort(_('cannot use --parent on non-merge changeset'))
222 222 parent = p1
223 223 hg.clean(repo, node, show_stats=False)
224 224 revert_opts = opts.copy()
225 225 revert_opts['all'] = True
226 226 revert_opts['rev'] = hex(parent)
227 227 revert(ui, repo, **revert_opts)
228 228 commit_opts = opts.copy()
229 229 commit_opts['addremove'] = False
230 230 if not commit_opts['message'] and not commit_opts['logfile']:
231 231 commit_opts['message'] = _("Backed out changeset %s") % (hex(node))
232 232 commit_opts['force_editor'] = True
233 233 commit(ui, repo, **commit_opts)
234 234 def nice(node):
235 235 return '%d:%s' % (repo.changelog.rev(node), short(node))
236 236 ui.status(_('changeset %s backs out changeset %s\n') %
237 237 (nice(repo.changelog.tip()), nice(node)))
238 238 if op1 != node:
239 239 if opts['merge']:
240 240 ui.status(_('merging with changeset %s\n') % nice(op1))
241 241 n = _lookup(repo, hex(op1))
242 242 hg.merge(repo, n)
243 243 else:
244 244 ui.status(_('the backout changeset is a new head - '
245 245 'do not forget to merge\n'))
246 246 ui.status(_('(use "backout --merge" '
247 247 'if you want to auto-merge)\n'))
248 248
249 249 def branch(ui, repo, label=None):
250 250 """set or show the current branch name
251 251
252 252 With <name>, set the current branch name. Otherwise, show the
253 253 current branch name.
254 254 """
255 255
256 256 if label is not None:
257 257 repo.opener("branch", "w").write(label)
258 258 else:
259 259 b = repo.workingctx().branch()
260 260 if b:
261 261 ui.write("%s\n" % b)
262 262
263 263 def branches(ui, repo):
264 264 """list repository named branches
265 265
266 266 List the repository's named branches.
267 267 """
268 268 b = repo.branchtags()
269 269 l = [(-repo.changelog.rev(n), n, t) for t, n in b.items()]
270 270 l.sort()
271 271 for r, n, t in l:
272 272 hexfunc = ui.debugflag and hex or short
273 273 if ui.quiet:
274 274 ui.write("%s\n" % t)
275 275 else:
276 276 t = util.localsub(t, 30)
277 277 t += " " * (30 - util.locallen(t))
278 278 ui.write("%s %s:%s\n" % (t, -r, hexfunc(n)))
279 279
280 280 def bundle(ui, repo, fname, dest=None, **opts):
281 281 """create a changegroup file
282 282
283 283 Generate a compressed changegroup file collecting changesets not
284 284 found in the other repository.
285 285
286 286 If no destination repository is specified the destination is assumed
287 287 to have all the nodes specified by one or more --base parameters.
288 288
289 289 The bundle file can then be transferred using conventional means and
290 290 applied to another repository with the unbundle or pull command.
291 291 This is useful when direct push and pull are not available or when
292 292 exporting an entire repository is undesirable.
293 293
294 294 Applying bundles preserves all changeset contents including
295 295 permissions, copy/rename information, and revision history.
296 296 """
297 297 revs = opts.get('rev') or None
298 298 if revs:
299 299 revs = [repo.lookup(rev) for rev in revs]
300 300 base = opts.get('base')
301 301 if base:
302 302 if dest:
303 303 raise util.Abort(_("--base is incompatible with specifiying "
304 304 "a destination"))
305 305 base = [repo.lookup(rev) for rev in base]
306 306 # create the right base
307 307 # XXX: nodesbetween / changegroup* should be "fixed" instead
308 308 o = []
309 309 has = {nullid: None}
310 310 for n in base:
311 311 has.update(repo.changelog.reachable(n))
312 312 if revs:
313 313 visit = list(revs)
314 314 else:
315 315 visit = repo.changelog.heads()
316 316 seen = {}
317 317 while visit:
318 318 n = visit.pop(0)
319 319 parents = [p for p in repo.changelog.parents(n) if p not in has]
320 320 if len(parents) == 0:
321 321 o.insert(0, n)
322 322 else:
323 323 for p in parents:
324 324 if p not in seen:
325 325 seen[p] = 1
326 326 visit.append(p)
327 327 else:
328 328 setremoteconfig(ui, opts)
329 329 dest = ui.expandpath(dest or 'default-push', dest or 'default')
330 330 other = hg.repository(ui, dest)
331 331 o = repo.findoutgoing(other, force=opts['force'])
332 332
333 333 if revs:
334 334 cg = repo.changegroupsubset(o, revs, 'bundle')
335 335 else:
336 336 cg = repo.changegroup(o, 'bundle')
337 337 changegroup.writebundle(cg, fname, "HG10BZ")
338 338
339 339 def cat(ui, repo, file1, *pats, **opts):
340 340 """output the latest or given revisions of files
341 341
342 342 Print the specified files as they were at the given revision.
343 343 If no revision is given then working dir parent is used, or tip
344 344 if no revision is checked out.
345 345
346 346 Output may be to a file, in which case the name of the file is
347 347 given using a format string. The formatting rules are the same as
348 348 for the export command, with the following additions:
349 349
350 350 %s basename of file being printed
351 351 %d dirname of file being printed, or '.' if in repo root
352 352 %p root-relative path name of file being printed
353 353 """
354 354 ctx = repo.changectx(opts['rev'])
355 355 for src, abs, rel, exact in cmdutil.walk(repo, (file1,) + pats, opts,
356 356 ctx.node()):
357 357 fp = cmdutil.make_file(repo, opts['output'], ctx.node(), pathname=abs)
358 358 fp.write(ctx.filectx(abs).data())
359 359
360 360 def clone(ui, source, dest=None, **opts):
361 361 """make a copy of an existing repository
362 362
363 363 Create a copy of an existing repository in a new directory.
364 364
365 365 If no destination directory name is specified, it defaults to the
366 366 basename of the source.
367 367
368 368 The location of the source is added to the new repository's
369 369 .hg/hgrc file, as the default to be used for future pulls.
370 370
371 371 For efficiency, hardlinks are used for cloning whenever the source
372 372 and destination are on the same filesystem (note this applies only
373 373 to the repository data, not to the checked out files). Some
374 374 filesystems, such as AFS, implement hardlinking incorrectly, but
375 375 do not report errors. In these cases, use the --pull option to
376 376 avoid hardlinking.
377 377
378 378 You can safely clone repositories and checked out files using full
379 379 hardlinks with
380 380
381 381 $ cp -al REPO REPOCLONE
382 382
383 383 which is the fastest way to clone. However, the operation is not
384 384 atomic (making sure REPO is not modified during the operation is
385 385 up to you) and you have to make sure your editor breaks hardlinks
386 386 (Emacs and most Linux Kernel tools do so).
387 387
388 388 If you use the -r option to clone up to a specific revision, no
389 389 subsequent revisions will be present in the cloned repository.
390 390 This option implies --pull, even on local repositories.
391 391
392 392 See pull for valid source format details.
393 393
394 394 It is possible to specify an ssh:// URL as the destination, but no
395 395 .hg/hgrc and working directory will be created on the remote side.
396 396 Look at the help text for the pull command for important details
397 397 about ssh:// URLs.
398 398 """
399 399 setremoteconfig(ui, opts)
400 400 hg.clone(ui, ui.expandpath(source), dest,
401 401 pull=opts['pull'],
402 402 stream=opts['uncompressed'],
403 403 rev=opts['rev'],
404 404 update=not opts['noupdate'])
405 405
406 406 def commit(ui, repo, *pats, **opts):
407 407 """commit the specified files or all outstanding changes
408 408
409 409 Commit changes to the given files into the repository.
410 410
411 411 If a list of files is omitted, all changes reported by "hg status"
412 412 will be committed.
413 413
414 414 If no commit message is specified, the editor configured in your hgrc
415 415 or in the EDITOR environment variable is started to enter a message.
416 416 """
417 417 message = logmessage(opts)
418 418
419 419 if opts['addremove']:
420 420 cmdutil.addremove(repo, pats, opts)
421 421 fns, match, anypats = cmdutil.matchpats(repo, pats, opts)
422 422 if pats:
423 423 status = repo.status(files=fns, match=match)
424 424 modified, added, removed, deleted, unknown = status[:5]
425 425 files = modified + added + removed
426 426 slist = None
427 427 for f in fns:
428 428 if f not in files:
429 429 rf = repo.wjoin(f)
430 430 if f in unknown:
431 431 raise util.Abort(_("file %s not tracked!") % rf)
432 432 try:
433 433 mode = os.lstat(rf)[stat.ST_MODE]
434 434 except OSError:
435 435 raise util.Abort(_("file %s not found!") % rf)
436 436 if stat.S_ISDIR(mode):
437 437 name = f + '/'
438 438 if slist is None:
439 439 slist = list(files)
440 440 slist.sort()
441 441 i = bisect.bisect(slist, name)
442 442 if i >= len(slist) or not slist[i].startswith(name):
443 443 raise util.Abort(_("no match under directory %s!")
444 444 % rf)
445 445 elif not stat.S_ISREG(mode):
446 446 raise util.Abort(_("can't commit %s: "
447 447 "unsupported file type!") % rf)
448 448 else:
449 449 files = []
450 450 try:
451 451 repo.commit(files, message, opts['user'], opts['date'], match,
452 452 force_editor=opts.get('force_editor'))
453 453 except ValueError, inst:
454 454 raise util.Abort(str(inst))
455 455
456 456 def docopy(ui, repo, pats, opts, wlock):
457 457 # called with the repo lock held
458 458 #
459 459 # hgsep => pathname that uses "/" to separate directories
460 460 # ossep => pathname that uses os.sep to separate directories
461 461 cwd = repo.getcwd()
462 462 errors = 0
463 463 copied = []
464 464 targets = {}
465 465
466 466 # abs: hgsep
467 467 # rel: ossep
468 468 # return: hgsep
469 469 def okaytocopy(abs, rel, exact):
470 470 reasons = {'?': _('is not managed'),
471 471 'a': _('has been marked for add'),
472 472 'r': _('has been marked for remove')}
473 473 state = repo.dirstate.state(abs)
474 474 reason = reasons.get(state)
475 475 if reason:
476 476 if state == 'a':
477 477 origsrc = repo.dirstate.copied(abs)
478 478 if origsrc is not None:
479 479 return origsrc
480 480 if exact:
481 481 ui.warn(_('%s: not copying - file %s\n') % (rel, reason))
482 482 else:
483 483 return abs
484 484
485 485 # origsrc: hgsep
486 486 # abssrc: hgsep
487 487 # relsrc: ossep
488 488 # target: ossep
489 489 def copy(origsrc, abssrc, relsrc, target, exact):
490 490 abstarget = util.canonpath(repo.root, cwd, target)
491 491 reltarget = util.pathto(cwd, abstarget)
492 492 prevsrc = targets.get(abstarget)
493 493 if prevsrc is not None:
494 494 ui.warn(_('%s: not overwriting - %s collides with %s\n') %
495 495 (reltarget, util.localpath(abssrc),
496 496 util.localpath(prevsrc)))
497 497 return
498 498 if (not opts['after'] and os.path.exists(reltarget) or
499 499 opts['after'] and repo.dirstate.state(abstarget) not in '?r'):
500 500 if not opts['force']:
501 501 ui.warn(_('%s: not overwriting - file exists\n') %
502 502 reltarget)
503 503 return
504 504 if not opts['after'] and not opts.get('dry_run'):
505 505 os.unlink(reltarget)
506 506 if opts['after']:
507 507 if not os.path.exists(reltarget):
508 508 return
509 509 else:
510 510 targetdir = os.path.dirname(reltarget) or '.'
511 511 if not os.path.isdir(targetdir) and not opts.get('dry_run'):
512 512 os.makedirs(targetdir)
513 513 try:
514 514 restore = repo.dirstate.state(abstarget) == 'r'
515 515 if restore and not opts.get('dry_run'):
516 516 repo.undelete([abstarget], wlock)
517 517 try:
518 518 if not opts.get('dry_run'):
519 519 util.copyfile(relsrc, reltarget)
520 520 restore = False
521 521 finally:
522 522 if restore:
523 523 repo.remove([abstarget], wlock)
524 524 except IOError, inst:
525 525 if inst.errno == errno.ENOENT:
526 526 ui.warn(_('%s: deleted in working copy\n') % relsrc)
527 527 else:
528 528 ui.warn(_('%s: cannot copy - %s\n') %
529 529 (relsrc, inst.strerror))
530 530 errors += 1
531 531 return
532 532 if ui.verbose or not exact:
533 533 ui.status(_('copying %s to %s\n') % (relsrc, reltarget))
534 534 targets[abstarget] = abssrc
535 535 if abstarget != origsrc and not opts.get('dry_run'):
536 536 repo.copy(origsrc, abstarget, wlock)
537 537 copied.append((abssrc, relsrc, exact))
538 538
539 539 # pat: ossep
540 540 # dest ossep
541 541 # srcs: list of (hgsep, hgsep, ossep, bool)
542 542 # return: function that takes hgsep and returns ossep
543 543 def targetpathfn(pat, dest, srcs):
544 544 if os.path.isdir(pat):
545 545 abspfx = util.canonpath(repo.root, cwd, pat)
546 546 abspfx = util.localpath(abspfx)
547 547 if destdirexists:
548 548 striplen = len(os.path.split(abspfx)[0])
549 549 else:
550 550 striplen = len(abspfx)
551 551 if striplen:
552 552 striplen += len(os.sep)
553 553 res = lambda p: os.path.join(dest, util.localpath(p)[striplen:])
554 554 elif destdirexists:
555 555 res = lambda p: os.path.join(dest,
556 556 os.path.basename(util.localpath(p)))
557 557 else:
558 558 res = lambda p: dest
559 559 return res
560 560
561 561 # pat: ossep
562 562 # dest ossep
563 563 # srcs: list of (hgsep, hgsep, ossep, bool)
564 564 # return: function that takes hgsep and returns ossep
565 565 def targetpathafterfn(pat, dest, srcs):
566 566 if util.patkind(pat, None)[0]:
567 567 # a mercurial pattern
568 568 res = lambda p: os.path.join(dest,
569 569 os.path.basename(util.localpath(p)))
570 570 else:
571 571 abspfx = util.canonpath(repo.root, cwd, pat)
572 572 if len(abspfx) < len(srcs[0][0]):
573 573 # A directory. Either the target path contains the last
574 574 # component of the source path or it does not.
575 575 def evalpath(striplen):
576 576 score = 0
577 577 for s in srcs:
578 578 t = os.path.join(dest, util.localpath(s[0])[striplen:])
579 579 if os.path.exists(t):
580 580 score += 1
581 581 return score
582 582
583 583 abspfx = util.localpath(abspfx)
584 584 striplen = len(abspfx)
585 585 if striplen:
586 586 striplen += len(os.sep)
587 587 if os.path.isdir(os.path.join(dest, os.path.split(abspfx)[1])):
588 588 score = evalpath(striplen)
589 589 striplen1 = len(os.path.split(abspfx)[0])
590 590 if striplen1:
591 591 striplen1 += len(os.sep)
592 592 if evalpath(striplen1) > score:
593 593 striplen = striplen1
594 594 res = lambda p: os.path.join(dest,
595 595 util.localpath(p)[striplen:])
596 596 else:
597 597 # a file
598 598 if destdirexists:
599 599 res = lambda p: os.path.join(dest,
600 600 os.path.basename(util.localpath(p)))
601 601 else:
602 602 res = lambda p: dest
603 603 return res
604 604
605 605
606 606 pats = list(pats)
607 607 if not pats:
608 608 raise util.Abort(_('no source or destination specified'))
609 609 if len(pats) == 1:
610 610 raise util.Abort(_('no destination specified'))
611 611 dest = pats.pop()
612 612 destdirexists = os.path.isdir(dest)
613 613 if (len(pats) > 1 or util.patkind(pats[0], None)[0]) and not destdirexists:
614 614 raise util.Abort(_('with multiple sources, destination must be an '
615 615 'existing directory'))
616 616 if opts['after']:
617 617 tfn = targetpathafterfn
618 618 else:
619 619 tfn = targetpathfn
620 620 copylist = []
621 621 for pat in pats:
622 622 srcs = []
623 623 for tag, abssrc, relsrc, exact in cmdutil.walk(repo, [pat], opts):
624 624 origsrc = okaytocopy(abssrc, relsrc, exact)
625 625 if origsrc:
626 626 srcs.append((origsrc, abssrc, relsrc, exact))
627 627 if not srcs:
628 628 continue
629 629 copylist.append((tfn(pat, dest, srcs), srcs))
630 630 if not copylist:
631 631 raise util.Abort(_('no files to copy'))
632 632
633 633 for targetpath, srcs in copylist:
634 634 for origsrc, abssrc, relsrc, exact in srcs:
635 635 copy(origsrc, abssrc, relsrc, targetpath(abssrc), exact)
636 636
637 637 if errors:
638 638 ui.warn(_('(consider using --after)\n'))
639 639 return errors, copied
640 640
641 641 def copy(ui, repo, *pats, **opts):
642 642 """mark files as copied for the next commit
643 643
644 644 Mark dest as having copies of source files. If dest is a
645 645 directory, copies are put in that directory. If dest is a file,
646 646 there can only be one source.
647 647
648 648 By default, this command copies the contents of files as they
649 649 stand in the working directory. If invoked with --after, the
650 650 operation is recorded, but no copying is performed.
651 651
652 652 This command takes effect in the next commit.
653 653 """
654 654 wlock = repo.wlock(0)
655 655 errs, copied = docopy(ui, repo, pats, opts, wlock)
656 656 return errs
657 657
658 658 def debugancestor(ui, index, rev1, rev2):
659 659 """find the ancestor revision of two revisions in a given index"""
660 660 r = revlog.revlog(util.opener(os.getcwd(), audit=False), index, "", 0)
661 661 a = r.ancestor(r.lookup(rev1), r.lookup(rev2))
662 662 ui.write("%d:%s\n" % (r.rev(a), hex(a)))
663 663
664 664 def debugcomplete(ui, cmd='', **opts):
665 665 """returns the completion list associated with the given command"""
666 666
667 667 if opts['options']:
668 668 options = []
669 669 otables = [globalopts]
670 670 if cmd:
671 671 aliases, entry = findcmd(ui, cmd)
672 672 otables.append(entry[1])
673 673 for t in otables:
674 674 for o in t:
675 675 if o[0]:
676 676 options.append('-%s' % o[0])
677 677 options.append('--%s' % o[1])
678 678 ui.write("%s\n" % "\n".join(options))
679 679 return
680 680
681 681 clist = findpossible(ui, cmd).keys()
682 682 clist.sort()
683 683 ui.write("%s\n" % "\n".join(clist))
684 684
685 685 def debugrebuildstate(ui, repo, rev=None):
686 686 """rebuild the dirstate as it would look like for the given revision"""
687 687 if not rev:
688 688 rev = repo.changelog.tip()
689 689 else:
690 690 rev = repo.lookup(rev)
691 691 change = repo.changelog.read(rev)
692 692 n = change[0]
693 693 files = repo.manifest.read(n)
694 694 wlock = repo.wlock()
695 695 repo.dirstate.rebuild(rev, files)
696 696
697 697 def debugcheckstate(ui, repo):
698 698 """validate the correctness of the current dirstate"""
699 699 parent1, parent2 = repo.dirstate.parents()
700 700 repo.dirstate.read()
701 701 dc = repo.dirstate.map
702 702 keys = dc.keys()
703 703 keys.sort()
704 704 m1n = repo.changelog.read(parent1)[0]
705 705 m2n = repo.changelog.read(parent2)[0]
706 706 m1 = repo.manifest.read(m1n)
707 707 m2 = repo.manifest.read(m2n)
708 708 errors = 0
709 709 for f in dc:
710 710 state = repo.dirstate.state(f)
711 711 if state in "nr" and f not in m1:
712 712 ui.warn(_("%s in state %s, but not in manifest1\n") % (f, state))
713 713 errors += 1
714 714 if state in "a" and f in m1:
715 715 ui.warn(_("%s in state %s, but also in manifest1\n") % (f, state))
716 716 errors += 1
717 717 if state in "m" and f not in m1 and f not in m2:
718 718 ui.warn(_("%s in state %s, but not in either manifest\n") %
719 719 (f, state))
720 720 errors += 1
721 721 for f in m1:
722 722 state = repo.dirstate.state(f)
723 723 if state not in "nrm":
724 724 ui.warn(_("%s in manifest1, but listed as state %s") % (f, state))
725 725 errors += 1
726 726 if errors:
727 727 error = _(".hg/dirstate inconsistent with current parent's manifest")
728 728 raise util.Abort(error)
729 729
730 730 def showconfig(ui, repo, *values, **opts):
731 731 """show combined config settings from all hgrc files
732 732
733 733 With no args, print names and values of all config items.
734 734
735 735 With one arg of the form section.name, print just the value of
736 736 that config item.
737 737
738 738 With multiple args, print names and values of all config items
739 739 with matching section names."""
740 740
741 741 untrusted = bool(opts.get('untrusted'))
742 742 if values:
743 743 if len([v for v in values if '.' in v]) > 1:
744 744 raise util.Abort(_('only one config item permitted'))
745 745 for section, name, value in ui.walkconfig(untrusted=untrusted):
746 746 sectname = section + '.' + name
747 747 if values:
748 748 for v in values:
749 749 if v == section:
750 750 ui.write('%s=%s\n' % (sectname, value))
751 751 elif v == sectname:
752 752 ui.write(value, '\n')
753 753 else:
754 754 ui.write('%s=%s\n' % (sectname, value))
755 755
756 756 def debugsetparents(ui, repo, rev1, rev2=None):
757 757 """manually set the parents of the current working directory
758 758
759 759 This is useful for writing repository conversion tools, but should
760 760 be used with care.
761 761 """
762 762
763 763 if not rev2:
764 764 rev2 = hex(nullid)
765 765
766 766 repo.dirstate.setparents(repo.lookup(rev1), repo.lookup(rev2))
767 767
768 768 def debugstate(ui, repo):
769 769 """show the contents of the current dirstate"""
770 770 repo.dirstate.read()
771 771 dc = repo.dirstate.map
772 772 keys = dc.keys()
773 773 keys.sort()
774 774 for file_ in keys:
775 775 ui.write("%c %3o %10d %s %s\n"
776 776 % (dc[file_][0], dc[file_][1] & 0777, dc[file_][2],
777 777 time.strftime("%x %X",
778 778 time.localtime(dc[file_][3])), file_))
779 779 for f in repo.dirstate.copies():
780 780 ui.write(_("copy: %s -> %s\n") % (repo.dirstate.copied(f), f))
781 781
782 782 def debugdata(ui, file_, rev):
783 783 """dump the contents of an data file revision"""
784 784 r = revlog.revlog(util.opener(os.getcwd(), audit=False),
785 785 file_[:-2] + ".i", file_, 0)
786 786 try:
787 787 ui.write(r.revision(r.lookup(rev)))
788 788 except KeyError:
789 789 raise util.Abort(_('invalid revision identifier %s') % rev)
790 790
791 def debugdate(ui, date):
791 def debugdate(ui, date, range=None, **opts):
792 792 """parse and display a date"""
793 d = util.parsedate(date)
793 if opts["extended"]:
794 d = util.parsedate(date, util.extendeddateformats)
795 else:
796 d = util.parsedate(date)
794 797 ui.write("internal: %s %s\n" % d)
795 798 ui.write("standard: %s\n" % util.datestr(d))
799 if range:
800 m = util.matchdate(range)
801 ui.write("match: %s\n" % m(d[0]))
796 802
797 803 def debugindex(ui, file_):
798 804 """dump the contents of an index file"""
799 805 r = revlog.revlog(util.opener(os.getcwd(), audit=False), file_, "", 0)
800 806 ui.write(" rev offset length base linkrev" +
801 807 " nodeid p1 p2\n")
802 808 for i in xrange(r.count()):
803 809 node = r.node(i)
804 810 pp = r.parents(node)
805 811 ui.write("% 6d % 9d % 7d % 6d % 7d %s %s %s\n" % (
806 812 i, r.start(i), r.length(i), r.base(i), r.linkrev(node),
807 813 short(node), short(pp[0]), short(pp[1])))
808 814
809 815 def debugindexdot(ui, file_):
810 816 """dump an index DAG as a .dot file"""
811 817 r = revlog.revlog(util.opener(os.getcwd(), audit=False), file_, "", 0)
812 818 ui.write("digraph G {\n")
813 819 for i in xrange(r.count()):
814 820 node = r.node(i)
815 821 pp = r.parents(node)
816 822 ui.write("\t%d -> %d\n" % (r.rev(pp[0]), i))
817 823 if pp[1] != nullid:
818 824 ui.write("\t%d -> %d\n" % (r.rev(pp[1]), i))
819 825 ui.write("}\n")
820 826
821 827 def debugrename(ui, repo, file1, *pats, **opts):
822 828 """dump rename information"""
823 829
824 830 ctx = repo.changectx(opts.get('rev', 'tip'))
825 831 for src, abs, rel, exact in cmdutil.walk(repo, (file1,) + pats, opts,
826 832 ctx.node()):
827 833 m = ctx.filectx(abs).renamed()
828 834 if m:
829 835 ui.write(_("%s renamed from %s:%s\n") % (rel, m[0], hex(m[1])))
830 836 else:
831 837 ui.write(_("%s not renamed\n") % rel)
832 838
833 839 def debugwalk(ui, repo, *pats, **opts):
834 840 """show how files match on given patterns"""
835 841 items = list(cmdutil.walk(repo, pats, opts))
836 842 if not items:
837 843 return
838 844 fmt = '%%s %%-%ds %%-%ds %%s' % (
839 845 max([len(abs) for (src, abs, rel, exact) in items]),
840 846 max([len(rel) for (src, abs, rel, exact) in items]))
841 847 for src, abs, rel, exact in items:
842 848 line = fmt % (src, abs, rel, exact and 'exact' or '')
843 849 ui.write("%s\n" % line.rstrip())
844 850
845 851 def diff(ui, repo, *pats, **opts):
846 852 """diff repository (or selected files)
847 853
848 854 Show differences between revisions for the specified files.
849 855
850 856 Differences between files are shown using the unified diff format.
851 857
852 858 When two revision arguments are given, then changes are shown
853 859 between those revisions. If only one revision is specified then
854 860 that revision is compared to the working directory, and, when no
855 861 revisions are specified, the working directory files are compared
856 862 to its parent.
857 863
858 864 Without the -a option, diff will avoid generating diffs of files
859 865 it detects as binary. With -a, diff will generate a diff anyway,
860 866 probably with undesirable results.
861 867 """
862 868 node1, node2 = cmdutil.revpair(repo, opts['rev'])
863 869
864 870 fns, matchfn, anypats = cmdutil.matchpats(repo, pats, opts)
865 871
866 872 patch.diff(repo, node1, node2, fns, match=matchfn,
867 873 opts=patch.diffopts(ui, opts))
868 874
869 875 def export(ui, repo, *changesets, **opts):
870 876 """dump the header and diffs for one or more changesets
871 877
872 878 Print the changeset header and diffs for one or more revisions.
873 879
874 880 The information shown in the changeset header is: author,
875 881 changeset hash, parent and commit comment.
876 882
877 883 Output may be to a file, in which case the name of the file is
878 884 given using a format string. The formatting rules are as follows:
879 885
880 886 %% literal "%" character
881 887 %H changeset hash (40 bytes of hexadecimal)
882 888 %N number of patches being generated
883 889 %R changeset revision number
884 890 %b basename of the exporting repository
885 891 %h short-form changeset hash (12 bytes of hexadecimal)
886 892 %n zero-padded sequence number, starting at 1
887 893 %r zero-padded changeset revision number
888 894
889 895 Without the -a option, export will avoid generating diffs of files
890 896 it detects as binary. With -a, export will generate a diff anyway,
891 897 probably with undesirable results.
892 898
893 899 With the --switch-parent option, the diff will be against the second
894 900 parent. It can be useful to review a merge.
895 901 """
896 902 if not changesets:
897 903 raise util.Abort(_("export requires at least one changeset"))
898 904 revs = cmdutil.revrange(repo, changesets)
899 905 if len(revs) > 1:
900 906 ui.note(_('exporting patches:\n'))
901 907 else:
902 908 ui.note(_('exporting patch:\n'))
903 909 patch.export(repo, map(repo.lookup, revs), template=opts['output'],
904 910 switch_parent=opts['switch_parent'],
905 911 opts=patch.diffopts(ui, opts))
906 912
907 913 def grep(ui, repo, pattern, *pats, **opts):
908 914 """search for a pattern in specified files and revisions
909 915
910 916 Search revisions of files for a regular expression.
911 917
912 918 This command behaves differently than Unix grep. It only accepts
913 919 Python/Perl regexps. It searches repository history, not the
914 920 working directory. It always prints the revision number in which
915 921 a match appears.
916 922
917 923 By default, grep only prints output for the first revision of a
918 924 file in which it finds a match. To get it to print every revision
919 925 that contains a change in match status ("-" for a match that
920 926 becomes a non-match, or "+" for a non-match that becomes a match),
921 927 use the --all flag.
922 928 """
923 929 reflags = 0
924 930 if opts['ignore_case']:
925 931 reflags |= re.I
926 932 regexp = re.compile(pattern, reflags)
927 933 sep, eol = ':', '\n'
928 934 if opts['print0']:
929 935 sep = eol = '\0'
930 936
931 937 fcache = {}
932 938 def getfile(fn):
933 939 if fn not in fcache:
934 940 fcache[fn] = repo.file(fn)
935 941 return fcache[fn]
936 942
937 943 def matchlines(body):
938 944 begin = 0
939 945 linenum = 0
940 946 while True:
941 947 match = regexp.search(body, begin)
942 948 if not match:
943 949 break
944 950 mstart, mend = match.span()
945 951 linenum += body.count('\n', begin, mstart) + 1
946 952 lstart = body.rfind('\n', begin, mstart) + 1 or begin
947 953 lend = body.find('\n', mend)
948 954 yield linenum, mstart - lstart, mend - lstart, body[lstart:lend]
949 955 begin = lend + 1
950 956
951 957 class linestate(object):
952 958 def __init__(self, line, linenum, colstart, colend):
953 959 self.line = line
954 960 self.linenum = linenum
955 961 self.colstart = colstart
956 962 self.colend = colend
957 963
958 964 def __eq__(self, other):
959 965 return self.line == other.line
960 966
961 967 matches = {}
962 968 copies = {}
963 969 def grepbody(fn, rev, body):
964 970 matches[rev].setdefault(fn, [])
965 971 m = matches[rev][fn]
966 972 for lnum, cstart, cend, line in matchlines(body):
967 973 s = linestate(line, lnum, cstart, cend)
968 974 m.append(s)
969 975
970 976 def difflinestates(a, b):
971 977 sm = difflib.SequenceMatcher(None, a, b)
972 978 for tag, alo, ahi, blo, bhi in sm.get_opcodes():
973 979 if tag == 'insert':
974 980 for i in xrange(blo, bhi):
975 981 yield ('+', b[i])
976 982 elif tag == 'delete':
977 983 for i in xrange(alo, ahi):
978 984 yield ('-', a[i])
979 985 elif tag == 'replace':
980 986 for i in xrange(alo, ahi):
981 987 yield ('-', a[i])
982 988 for i in xrange(blo, bhi):
983 989 yield ('+', b[i])
984 990
985 991 prev = {}
986 992 def display(fn, rev, states, prevstates):
987 993 counts = {'-': 0, '+': 0}
988 994 filerevmatches = {}
989 995 if incrementing or not opts['all']:
990 996 a, b, r = prevstates, states, rev
991 997 else:
992 998 a, b, r = states, prevstates, prev.get(fn, -1)
993 999 for change, l in difflinestates(a, b):
994 1000 cols = [fn, str(r)]
995 1001 if opts['line_number']:
996 1002 cols.append(str(l.linenum))
997 1003 if opts['all']:
998 1004 cols.append(change)
999 1005 if opts['user']:
1000 1006 cols.append(ui.shortuser(get(r)[1]))
1001 1007 if opts['files_with_matches']:
1002 1008 c = (fn, r)
1003 1009 if c in filerevmatches:
1004 1010 continue
1005 1011 filerevmatches[c] = 1
1006 1012 else:
1007 1013 cols.append(l.line)
1008 1014 ui.write(sep.join(cols), eol)
1009 1015 counts[change] += 1
1010 1016 return counts['+'], counts['-']
1011 1017
1012 1018 fstate = {}
1013 1019 skip = {}
1014 1020 get = util.cachefunc(lambda r: repo.changectx(r).changeset())
1015 1021 changeiter, matchfn = cmdutil.walkchangerevs(ui, repo, pats, get, opts)
1016 1022 count = 0
1017 1023 incrementing = False
1018 1024 follow = opts.get('follow')
1019 1025 for st, rev, fns in changeiter:
1020 1026 if st == 'window':
1021 1027 incrementing = rev
1022 1028 matches.clear()
1023 1029 elif st == 'add':
1024 1030 mf = repo.changectx(rev).manifest()
1025 1031 matches[rev] = {}
1026 1032 for fn in fns:
1027 1033 if fn in skip:
1028 1034 continue
1029 1035 fstate.setdefault(fn, {})
1030 1036 try:
1031 1037 grepbody(fn, rev, getfile(fn).read(mf[fn]))
1032 1038 if follow:
1033 1039 copied = getfile(fn).renamed(mf[fn])
1034 1040 if copied:
1035 1041 copies.setdefault(rev, {})[fn] = copied[0]
1036 1042 except KeyError:
1037 1043 pass
1038 1044 elif st == 'iter':
1039 1045 states = matches[rev].items()
1040 1046 states.sort()
1041 1047 for fn, m in states:
1042 1048 copy = copies.get(rev, {}).get(fn)
1043 1049 if fn in skip:
1044 1050 if copy:
1045 1051 skip[copy] = True
1046 1052 continue
1047 1053 if incrementing or not opts['all'] or fstate[fn]:
1048 1054 pos, neg = display(fn, rev, m, fstate[fn])
1049 1055 count += pos + neg
1050 1056 if pos and not opts['all']:
1051 1057 skip[fn] = True
1052 1058 if copy:
1053 1059 skip[copy] = True
1054 1060 fstate[fn] = m
1055 1061 if copy:
1056 1062 fstate[copy] = m
1057 1063 prev[fn] = rev
1058 1064
1059 1065 if not incrementing:
1060 1066 fstate = fstate.items()
1061 1067 fstate.sort()
1062 1068 for fn, state in fstate:
1063 1069 if fn in skip:
1064 1070 continue
1065 1071 if fn not in copies.get(prev[fn], {}):
1066 1072 display(fn, rev, {}, state)
1067 1073 return (count == 0 and 1) or 0
1068 1074
1069 1075 def heads(ui, repo, **opts):
1070 1076 """show current repository heads
1071 1077
1072 1078 Show all repository head changesets.
1073 1079
1074 1080 Repository "heads" are changesets that don't have children
1075 1081 changesets. They are where development generally takes place and
1076 1082 are the usual targets for update and merge operations.
1077 1083 """
1078 1084 if opts['rev']:
1079 1085 heads = repo.heads(repo.lookup(opts['rev']))
1080 1086 else:
1081 1087 heads = repo.heads()
1082 1088 displayer = cmdutil.show_changeset(ui, repo, opts)
1083 1089 for n in heads:
1084 1090 displayer.show(changenode=n)
1085 1091
1086 1092 def help_(ui, name=None, with_version=False):
1087 1093 """show help for a command, extension, or list of commands
1088 1094
1089 1095 With no arguments, print a list of commands and short help.
1090 1096
1091 1097 Given a command name, print help for that command.
1092 1098
1093 1099 Given an extension name, print help for that extension, and the
1094 1100 commands it provides."""
1095 1101 option_lists = []
1096 1102
1097 1103 def helpcmd(name):
1098 1104 if with_version:
1099 1105 version_(ui)
1100 1106 ui.write('\n')
1101 1107 aliases, i = findcmd(ui, name)
1102 1108 # synopsis
1103 1109 ui.write("%s\n\n" % i[2])
1104 1110
1105 1111 # description
1106 1112 doc = i[0].__doc__
1107 1113 if not doc:
1108 1114 doc = _("(No help text available)")
1109 1115 if ui.quiet:
1110 1116 doc = doc.splitlines(0)[0]
1111 1117 ui.write("%s\n" % doc.rstrip())
1112 1118
1113 1119 if not ui.quiet:
1114 1120 # aliases
1115 1121 if len(aliases) > 1:
1116 1122 ui.write(_("\naliases: %s\n") % ', '.join(aliases[1:]))
1117 1123
1118 1124 # options
1119 1125 if i[1]:
1120 1126 option_lists.append(("options", i[1]))
1121 1127
1122 1128 def helplist(select=None):
1123 1129 h = {}
1124 1130 cmds = {}
1125 1131 for c, e in table.items():
1126 1132 f = c.split("|", 1)[0]
1127 1133 if select and not select(f):
1128 1134 continue
1129 1135 if name == "shortlist" and not f.startswith("^"):
1130 1136 continue
1131 1137 f = f.lstrip("^")
1132 1138 if not ui.debugflag and f.startswith("debug"):
1133 1139 continue
1134 1140 doc = e[0].__doc__
1135 1141 if not doc:
1136 1142 doc = _("(No help text available)")
1137 1143 h[f] = doc.splitlines(0)[0].rstrip()
1138 1144 cmds[f] = c.lstrip("^")
1139 1145
1140 1146 fns = h.keys()
1141 1147 fns.sort()
1142 1148 m = max(map(len, fns))
1143 1149 for f in fns:
1144 1150 if ui.verbose:
1145 1151 commands = cmds[f].replace("|",", ")
1146 1152 ui.write(" %s:\n %s\n"%(commands, h[f]))
1147 1153 else:
1148 1154 ui.write(' %-*s %s\n' % (m, f, h[f]))
1149 1155
1150 1156 def helptopic(name):
1151 1157 v = None
1152 1158 for i in help.helptable:
1153 1159 l = i.split('|')
1154 1160 if name in l:
1155 1161 v = i
1156 1162 header = l[-1]
1157 1163 if not v:
1158 1164 raise UnknownCommand(name)
1159 1165
1160 1166 # description
1161 1167 doc = help.helptable[v]
1162 1168 if not doc:
1163 1169 doc = _("(No help text available)")
1164 1170 if callable(doc):
1165 1171 doc = doc()
1166 1172
1167 1173 ui.write("%s\n" % header)
1168 1174 ui.write("%s\n" % doc.rstrip())
1169 1175
1170 1176 def helpext(name):
1171 1177 try:
1172 1178 mod = findext(name)
1173 1179 except KeyError:
1174 1180 raise UnknownCommand(name)
1175 1181
1176 1182 doc = (mod.__doc__ or _('No help text available')).splitlines(0)
1177 1183 ui.write(_('%s extension - %s\n') % (name.split('.')[-1], doc[0]))
1178 1184 for d in doc[1:]:
1179 1185 ui.write(d, '\n')
1180 1186
1181 1187 ui.status('\n')
1182 1188 if ui.verbose:
1183 1189 ui.status(_('list of commands:\n\n'))
1184 1190 else:
1185 1191 ui.status(_('list of commands (use "hg help -v %s" '
1186 1192 'to show aliases and global options):\n\n') % name)
1187 1193
1188 1194 modcmds = dict.fromkeys([c.split('|', 1)[0] for c in mod.cmdtable])
1189 1195 helplist(modcmds.has_key)
1190 1196
1191 1197 if name and name != 'shortlist':
1192 1198 i = None
1193 1199 for f in (helpcmd, helptopic, helpext):
1194 1200 try:
1195 1201 f(name)
1196 1202 i = None
1197 1203 break
1198 1204 except UnknownCommand, inst:
1199 1205 i = inst
1200 1206 if i:
1201 1207 raise i
1202 1208
1203 1209 else:
1204 1210 # program name
1205 1211 if ui.verbose or with_version:
1206 1212 version_(ui)
1207 1213 else:
1208 1214 ui.status(_("Mercurial Distributed SCM\n"))
1209 1215 ui.status('\n')
1210 1216
1211 1217 # list of commands
1212 1218 if name == "shortlist":
1213 1219 ui.status(_('basic commands (use "hg help" '
1214 1220 'for the full list or option "-v" for details):\n\n'))
1215 1221 elif ui.verbose:
1216 1222 ui.status(_('list of commands:\n\n'))
1217 1223 else:
1218 1224 ui.status(_('list of commands (use "hg help -v" '
1219 1225 'to show aliases and global options):\n\n'))
1220 1226
1221 1227 helplist()
1222 1228
1223 1229 # global options
1224 1230 if ui.verbose:
1225 1231 option_lists.append(("global options", globalopts))
1226 1232
1227 1233 # list all option lists
1228 1234 opt_output = []
1229 1235 for title, options in option_lists:
1230 1236 opt_output.append(("\n%s:\n" % title, None))
1231 1237 for shortopt, longopt, default, desc in options:
1232 1238 if "DEPRECATED" in desc and not ui.verbose: continue
1233 1239 opt_output.append(("%2s%s" % (shortopt and "-%s" % shortopt,
1234 1240 longopt and " --%s" % longopt),
1235 1241 "%s%s" % (desc,
1236 1242 default
1237 1243 and _(" (default: %s)") % default
1238 1244 or "")))
1239 1245
1240 1246 if opt_output:
1241 1247 opts_len = max([len(line[0]) for line in opt_output if line[1]])
1242 1248 for first, second in opt_output:
1243 1249 if second:
1244 1250 ui.write(" %-*s %s\n" % (opts_len, first, second))
1245 1251 else:
1246 1252 ui.write("%s\n" % first)
1247 1253
1248 1254 def identify(ui, repo):
1249 1255 """print information about the working copy
1250 1256
1251 1257 Print a short summary of the current state of the repo.
1252 1258
1253 1259 This summary identifies the repository state using one or two parent
1254 1260 hash identifiers, followed by a "+" if there are uncommitted changes
1255 1261 in the working directory, followed by a list of tags for this revision.
1256 1262 """
1257 1263 parents = [p for p in repo.dirstate.parents() if p != nullid]
1258 1264 if not parents:
1259 1265 ui.write(_("unknown\n"))
1260 1266 return
1261 1267
1262 1268 hexfunc = ui.debugflag and hex or short
1263 1269 modified, added, removed, deleted = repo.status()[:4]
1264 1270 output = ["%s%s" %
1265 1271 ('+'.join([hexfunc(parent) for parent in parents]),
1266 1272 (modified or added or removed or deleted) and "+" or "")]
1267 1273
1268 1274 if not ui.quiet:
1269 1275
1270 1276 branch = repo.workingctx().branch()
1271 1277 if branch:
1272 1278 output.append("(%s)" % branch)
1273 1279
1274 1280 # multiple tags for a single parent separated by '/'
1275 1281 parenttags = ['/'.join(tags)
1276 1282 for tags in map(repo.nodetags, parents) if tags]
1277 1283 # tags for multiple parents separated by ' + '
1278 1284 if parenttags:
1279 1285 output.append(' + '.join(parenttags))
1280 1286
1281 1287 ui.write("%s\n" % ' '.join(output))
1282 1288
1283 1289 def import_(ui, repo, patch1, *patches, **opts):
1284 1290 """import an ordered set of patches
1285 1291
1286 1292 Import a list of patches and commit them individually.
1287 1293
1288 1294 If there are outstanding changes in the working directory, import
1289 1295 will abort unless given the -f flag.
1290 1296
1291 1297 You can import a patch straight from a mail message. Even patches
1292 1298 as attachments work (body part must be type text/plain or
1293 1299 text/x-patch to be used). From and Subject headers of email
1294 1300 message are used as default committer and commit message. All
1295 1301 text/plain body parts before first diff are added to commit
1296 1302 message.
1297 1303
1298 1304 If imported patch was generated by hg export, user and description
1299 1305 from patch override values from message headers and body. Values
1300 1306 given on command line with -m and -u override these.
1301 1307
1302 1308 To read a patch from standard input, use patch name "-".
1303 1309 """
1304 1310 patches = (patch1,) + patches
1305 1311
1306 1312 if not opts['force']:
1307 1313 bail_if_changed(repo)
1308 1314
1309 1315 d = opts["base"]
1310 1316 strip = opts["strip"]
1311 1317
1312 1318 wlock = repo.wlock()
1313 1319 lock = repo.lock()
1314 1320
1315 1321 for p in patches:
1316 1322 pf = os.path.join(d, p)
1317 1323
1318 1324 if pf == '-':
1319 1325 ui.status(_("applying patch from stdin\n"))
1320 1326 tmpname, message, user, date = patch.extract(ui, sys.stdin)
1321 1327 else:
1322 1328 ui.status(_("applying %s\n") % p)
1323 1329 tmpname, message, user, date = patch.extract(ui, file(pf))
1324 1330
1325 1331 if tmpname is None:
1326 1332 raise util.Abort(_('no diffs found'))
1327 1333
1328 1334 try:
1329 1335 if opts['message']:
1330 1336 # pickup the cmdline msg
1331 1337 message = opts['message']
1332 1338 elif message:
1333 1339 # pickup the patch msg
1334 1340 message = message.strip()
1335 1341 else:
1336 1342 # launch the editor
1337 1343 message = None
1338 1344 ui.debug(_('message:\n%s\n') % message)
1339 1345
1340 1346 files = {}
1341 1347 try:
1342 1348 fuzz = patch.patch(tmpname, ui, strip=strip, cwd=repo.root,
1343 1349 files=files)
1344 1350 finally:
1345 1351 files = patch.updatedir(ui, repo, files, wlock=wlock)
1346 1352 repo.commit(files, message, user, date, wlock=wlock, lock=lock)
1347 1353 finally:
1348 1354 os.unlink(tmpname)
1349 1355
1350 1356 def incoming(ui, repo, source="default", **opts):
1351 1357 """show new changesets found in source
1352 1358
1353 1359 Show new changesets found in the specified path/URL or the default
1354 1360 pull location. These are the changesets that would be pulled if a pull
1355 1361 was requested.
1356 1362
1357 1363 For remote repository, using --bundle avoids downloading the changesets
1358 1364 twice if the incoming is followed by a pull.
1359 1365
1360 1366 See pull for valid source format details.
1361 1367 """
1362 1368 source = ui.expandpath(source)
1363 1369 setremoteconfig(ui, opts)
1364 1370
1365 1371 other = hg.repository(ui, source)
1366 1372 incoming = repo.findincoming(other, force=opts["force"])
1367 1373 if not incoming:
1368 1374 ui.status(_("no changes found\n"))
1369 1375 return
1370 1376
1371 1377 cleanup = None
1372 1378 try:
1373 1379 fname = opts["bundle"]
1374 1380 if fname or not other.local():
1375 1381 # create a bundle (uncompressed if other repo is not local)
1376 1382 cg = other.changegroup(incoming, "incoming")
1377 1383 bundletype = other.local() and "HG10BZ" or "HG10UN"
1378 1384 fname = cleanup = changegroup.writebundle(cg, fname, bundletype)
1379 1385 # keep written bundle?
1380 1386 if opts["bundle"]:
1381 1387 cleanup = None
1382 1388 if not other.local():
1383 1389 # use the created uncompressed bundlerepo
1384 1390 other = bundlerepo.bundlerepository(ui, repo.root, fname)
1385 1391
1386 1392 revs = None
1387 1393 if opts['rev']:
1388 1394 revs = [other.lookup(rev) for rev in opts['rev']]
1389 1395 o = other.changelog.nodesbetween(incoming, revs)[0]
1390 1396 if opts['newest_first']:
1391 1397 o.reverse()
1392 1398 displayer = cmdutil.show_changeset(ui, other, opts)
1393 1399 for n in o:
1394 1400 parents = [p for p in other.changelog.parents(n) if p != nullid]
1395 1401 if opts['no_merges'] and len(parents) == 2:
1396 1402 continue
1397 1403 displayer.show(changenode=n)
1398 1404 finally:
1399 1405 if hasattr(other, 'close'):
1400 1406 other.close()
1401 1407 if cleanup:
1402 1408 os.unlink(cleanup)
1403 1409
1404 1410 def init(ui, dest=".", **opts):
1405 1411 """create a new repository in the given directory
1406 1412
1407 1413 Initialize a new repository in the given directory. If the given
1408 1414 directory does not exist, it is created.
1409 1415
1410 1416 If no directory is given, the current directory is used.
1411 1417
1412 1418 It is possible to specify an ssh:// URL as the destination.
1413 1419 Look at the help text for the pull command for important details
1414 1420 about ssh:// URLs.
1415 1421 """
1416 1422 setremoteconfig(ui, opts)
1417 1423 hg.repository(ui, dest, create=1)
1418 1424
1419 1425 def locate(ui, repo, *pats, **opts):
1420 1426 """locate files matching specific patterns
1421 1427
1422 1428 Print all files under Mercurial control whose names match the
1423 1429 given patterns.
1424 1430
1425 1431 This command searches the current directory and its
1426 1432 subdirectories. To search an entire repository, move to the root
1427 1433 of the repository.
1428 1434
1429 1435 If no patterns are given to match, this command prints all file
1430 1436 names.
1431 1437
1432 1438 If you want to feed the output of this command into the "xargs"
1433 1439 command, use the "-0" option to both this command and "xargs".
1434 1440 This will avoid the problem of "xargs" treating single filenames
1435 1441 that contain white space as multiple filenames.
1436 1442 """
1437 1443 end = opts['print0'] and '\0' or '\n'
1438 1444 rev = opts['rev']
1439 1445 if rev:
1440 1446 node = repo.lookup(rev)
1441 1447 else:
1442 1448 node = None
1443 1449
1444 1450 for src, abs, rel, exact in cmdutil.walk(repo, pats, opts, node=node,
1445 1451 head='(?:.*/|)'):
1446 1452 if not node and repo.dirstate.state(abs) == '?':
1447 1453 continue
1448 1454 if opts['fullpath']:
1449 1455 ui.write(os.path.join(repo.root, abs), end)
1450 1456 else:
1451 1457 ui.write(((pats and rel) or abs), end)
1452 1458
1453 1459 def log(ui, repo, *pats, **opts):
1454 1460 """show revision history of entire repository or files
1455 1461
1456 1462 Print the revision history of the specified files or the entire
1457 1463 project.
1458 1464
1459 1465 File history is shown without following rename or copy history of
1460 1466 files. Use -f/--follow with a file name to follow history across
1461 1467 renames and copies. --follow without a file name will only show
1462 1468 ancestors or descendants of the starting revision. --follow-first
1463 1469 only follows the first parent of merge revisions.
1464 1470
1465 1471 If no revision range is specified, the default is tip:0 unless
1466 1472 --follow is set, in which case the working directory parent is
1467 1473 used as the starting revision.
1468 1474
1469 1475 By default this command outputs: changeset id and hash, tags,
1470 1476 non-trivial parents, user, date and time, and a summary for each
1471 1477 commit. When the -v/--verbose switch is used, the list of changed
1472 1478 files and full commit message is shown.
1473 1479 """
1474 1480
1475 1481 get = util.cachefunc(lambda r: repo.changectx(r).changeset())
1476 1482 changeiter, matchfn = cmdutil.walkchangerevs(ui, repo, pats, get, opts)
1477 1483
1478 1484 if opts['limit']:
1479 1485 try:
1480 1486 limit = int(opts['limit'])
1481 1487 except ValueError:
1482 1488 raise util.Abort(_('limit must be a positive integer'))
1483 1489 if limit <= 0: raise util.Abort(_('limit must be positive'))
1484 1490 else:
1485 1491 limit = sys.maxint
1486 1492 count = 0
1487 1493
1488 1494 if opts['copies'] and opts['rev']:
1489 1495 endrev = max(cmdutil.revrange(repo, opts['rev'])) + 1
1490 1496 else:
1491 1497 endrev = repo.changelog.count()
1492 1498 rcache = {}
1493 1499 ncache = {}
1494 1500 dcache = []
1495 1501 def getrenamed(fn, rev, man):
1496 1502 '''looks up all renames for a file (up to endrev) the first
1497 1503 time the file is given. It indexes on the changerev and only
1498 1504 parses the manifest if linkrev != changerev.
1499 1505 Returns rename info for fn at changerev rev.'''
1500 1506 if fn not in rcache:
1501 1507 rcache[fn] = {}
1502 1508 ncache[fn] = {}
1503 1509 fl = repo.file(fn)
1504 1510 for i in xrange(fl.count()):
1505 1511 node = fl.node(i)
1506 1512 lr = fl.linkrev(node)
1507 1513 renamed = fl.renamed(node)
1508 1514 rcache[fn][lr] = renamed
1509 1515 if renamed:
1510 1516 ncache[fn][node] = renamed
1511 1517 if lr >= endrev:
1512 1518 break
1513 1519 if rev in rcache[fn]:
1514 1520 return rcache[fn][rev]
1515 1521 mr = repo.manifest.rev(man)
1516 1522 if repo.manifest.parentrevs(mr) != (mr - 1, nullrev):
1517 1523 return ncache[fn].get(repo.manifest.find(man, fn)[0])
1518 1524 if not dcache or dcache[0] != man:
1519 1525 dcache[:] = [man, repo.manifest.readdelta(man)]
1520 1526 if fn in dcache[1]:
1521 1527 return ncache[fn].get(dcache[1][fn])
1522 1528 return None
1523 1529
1530 df = False
1531 if opts["date"]:
1532 df = util.matchdate(opts["date"])
1533
1534
1524 1535 displayer = cmdutil.show_changeset(ui, repo, opts, buffered=True)
1525 1536 for st, rev, fns in changeiter:
1526 1537 if st == 'add':
1527 1538 changenode = repo.changelog.node(rev)
1528 1539 parents = [p for p in repo.changelog.parentrevs(rev)
1529 1540 if p != nullrev]
1530 1541 if opts['no_merges'] and len(parents) == 2:
1531 1542 continue
1532 1543 if opts['only_merges'] and len(parents) != 2:
1533 1544 continue
1534 1545
1546 if df:
1547 changes = get(rev)
1548 if not df(changes[2][0]):
1549 continue
1550
1535 1551 if opts['keyword']:
1536 1552 changes = get(rev)
1537 1553 miss = 0
1538 1554 for k in [kw.lower() for kw in opts['keyword']]:
1539 1555 if not (k in changes[1].lower() or
1540 1556 k in changes[4].lower() or
1541 1557 k in " ".join(changes[3][:20]).lower()):
1542 1558 miss = 1
1543 1559 break
1544 1560 if miss:
1545 1561 continue
1546 1562
1547 1563 copies = []
1548 1564 if opts.get('copies') and rev:
1549 1565 mf = get(rev)[0]
1550 1566 for fn in get(rev)[3]:
1551 1567 rename = getrenamed(fn, rev, mf)
1552 1568 if rename:
1553 1569 copies.append((fn, rename[0]))
1554 1570 displayer.show(rev, changenode, copies=copies)
1555 1571 elif st == 'iter':
1556 1572 if count == limit: break
1557 1573 if displayer.flush(rev):
1558 1574 count += 1
1559 1575
1560 1576 def manifest(ui, repo, rev=None):
1561 1577 """output the latest or given revision of the project manifest
1562 1578
1563 1579 Print a list of version controlled files for the given revision.
1564 1580
1565 1581 The manifest is the list of files being version controlled. If no revision
1566 1582 is given then the first parent of the working directory is used.
1567 1583
1568 1584 With -v flag, print file permissions. With --debug flag, print
1569 1585 file revision hashes.
1570 1586 """
1571 1587
1572 1588 m = repo.changectx(rev).manifest()
1573 1589 files = m.keys()
1574 1590 files.sort()
1575 1591
1576 1592 for f in files:
1577 1593 if ui.debugflag:
1578 1594 ui.write("%40s " % hex(m[f]))
1579 1595 if ui.verbose:
1580 1596 ui.write("%3s " % (m.execf(f) and "755" or "644"))
1581 1597 ui.write("%s\n" % f)
1582 1598
1583 1599 def merge(ui, repo, node=None, force=None, branch=None):
1584 1600 """Merge working directory with another revision
1585 1601
1586 1602 Merge the contents of the current working directory and the
1587 1603 requested revision. Files that changed between either parent are
1588 1604 marked as changed for the next commit and a commit must be
1589 1605 performed before any further updates are allowed.
1590 1606
1591 1607 If no revision is specified, the working directory's parent is a
1592 1608 head revision, and the repository contains exactly one other head,
1593 1609 the other head is merged with by default. Otherwise, an explicit
1594 1610 revision to merge with must be provided.
1595 1611 """
1596 1612
1597 1613 if node or branch:
1598 1614 node = _lookup(repo, node, branch)
1599 1615 else:
1600 1616 heads = repo.heads()
1601 1617 if len(heads) > 2:
1602 1618 raise util.Abort(_('repo has %d heads - '
1603 1619 'please merge with an explicit rev') %
1604 1620 len(heads))
1605 1621 if len(heads) == 1:
1606 1622 raise util.Abort(_('there is nothing to merge - '
1607 1623 'use "hg update" instead'))
1608 1624 parent = repo.dirstate.parents()[0]
1609 1625 if parent not in heads:
1610 1626 raise util.Abort(_('working dir not at a head rev - '
1611 1627 'use "hg update" or merge with an explicit rev'))
1612 1628 node = parent == heads[0] and heads[-1] or heads[0]
1613 1629 return hg.merge(repo, node, force=force)
1614 1630
1615 1631 def outgoing(ui, repo, dest=None, **opts):
1616 1632 """show changesets not found in destination
1617 1633
1618 1634 Show changesets not found in the specified destination repository or
1619 1635 the default push location. These are the changesets that would be pushed
1620 1636 if a push was requested.
1621 1637
1622 1638 See pull for valid destination format details.
1623 1639 """
1624 1640 dest = ui.expandpath(dest or 'default-push', dest or 'default')
1625 1641 setremoteconfig(ui, opts)
1626 1642 revs = None
1627 1643 if opts['rev']:
1628 1644 revs = [repo.lookup(rev) for rev in opts['rev']]
1629 1645
1630 1646 other = hg.repository(ui, dest)
1631 1647 o = repo.findoutgoing(other, force=opts['force'])
1632 1648 if not o:
1633 1649 ui.status(_("no changes found\n"))
1634 1650 return
1635 1651 o = repo.changelog.nodesbetween(o, revs)[0]
1636 1652 if opts['newest_first']:
1637 1653 o.reverse()
1638 1654 displayer = cmdutil.show_changeset(ui, repo, opts)
1639 1655 for n in o:
1640 1656 parents = [p for p in repo.changelog.parents(n) if p != nullid]
1641 1657 if opts['no_merges'] and len(parents) == 2:
1642 1658 continue
1643 1659 displayer.show(changenode=n)
1644 1660
1645 1661 def parents(ui, repo, file_=None, **opts):
1646 1662 """show the parents of the working dir or revision
1647 1663
1648 1664 Print the working directory's parent revisions.
1649 1665 """
1650 1666 rev = opts.get('rev')
1651 1667 if rev:
1652 1668 if file_:
1653 1669 ctx = repo.filectx(file_, changeid=rev)
1654 1670 else:
1655 1671 ctx = repo.changectx(rev)
1656 1672 p = [cp.node() for cp in ctx.parents()]
1657 1673 else:
1658 1674 p = repo.dirstate.parents()
1659 1675
1660 1676 displayer = cmdutil.show_changeset(ui, repo, opts)
1661 1677 for n in p:
1662 1678 if n != nullid:
1663 1679 displayer.show(changenode=n)
1664 1680
1665 1681 def paths(ui, repo, search=None):
1666 1682 """show definition of symbolic path names
1667 1683
1668 1684 Show definition of symbolic path name NAME. If no name is given, show
1669 1685 definition of available names.
1670 1686
1671 1687 Path names are defined in the [paths] section of /etc/mercurial/hgrc
1672 1688 and $HOME/.hgrc. If run inside a repository, .hg/hgrc is used, too.
1673 1689 """
1674 1690 if search:
1675 1691 for name, path in ui.configitems("paths"):
1676 1692 if name == search:
1677 1693 ui.write("%s\n" % path)
1678 1694 return
1679 1695 ui.warn(_("not found!\n"))
1680 1696 return 1
1681 1697 else:
1682 1698 for name, path in ui.configitems("paths"):
1683 1699 ui.write("%s = %s\n" % (name, path))
1684 1700
1685 1701 def postincoming(ui, repo, modheads, optupdate):
1686 1702 if modheads == 0:
1687 1703 return
1688 1704 if optupdate:
1689 1705 if modheads == 1:
1690 1706 return hg.update(repo, repo.changelog.tip()) # update
1691 1707 else:
1692 1708 ui.status(_("not updating, since new heads added\n"))
1693 1709 if modheads > 1:
1694 1710 ui.status(_("(run 'hg heads' to see heads, 'hg merge' to merge)\n"))
1695 1711 else:
1696 1712 ui.status(_("(run 'hg update' to get a working copy)\n"))
1697 1713
1698 1714 def pull(ui, repo, source="default", **opts):
1699 1715 """pull changes from the specified source
1700 1716
1701 1717 Pull changes from a remote repository to a local one.
1702 1718
1703 1719 This finds all changes from the repository at the specified path
1704 1720 or URL and adds them to the local repository. By default, this
1705 1721 does not update the copy of the project in the working directory.
1706 1722
1707 1723 Valid URLs are of the form:
1708 1724
1709 1725 local/filesystem/path (or file://local/filesystem/path)
1710 1726 http://[user@]host[:port]/[path]
1711 1727 https://[user@]host[:port]/[path]
1712 1728 ssh://[user@]host[:port]/[path]
1713 1729 static-http://host[:port]/[path]
1714 1730
1715 1731 Paths in the local filesystem can either point to Mercurial
1716 1732 repositories or to bundle files (as created by 'hg bundle' or
1717 1733 'hg incoming --bundle'). The static-http:// protocol, albeit slow,
1718 1734 allows access to a Mercurial repository where you simply use a web
1719 1735 server to publish the .hg directory as static content.
1720 1736
1721 1737 Some notes about using SSH with Mercurial:
1722 1738 - SSH requires an accessible shell account on the destination machine
1723 1739 and a copy of hg in the remote path or specified with as remotecmd.
1724 1740 - path is relative to the remote user's home directory by default.
1725 1741 Use an extra slash at the start of a path to specify an absolute path:
1726 1742 ssh://example.com//tmp/repository
1727 1743 - Mercurial doesn't use its own compression via SSH; the right thing
1728 1744 to do is to configure it in your ~/.ssh/config, e.g.:
1729 1745 Host *.mylocalnetwork.example.com
1730 1746 Compression no
1731 1747 Host *
1732 1748 Compression yes
1733 1749 Alternatively specify "ssh -C" as your ssh command in your hgrc or
1734 1750 with the --ssh command line option.
1735 1751 """
1736 1752 source = ui.expandpath(source)
1737 1753 setremoteconfig(ui, opts)
1738 1754
1739 1755 other = hg.repository(ui, source)
1740 1756 ui.status(_('pulling from %s\n') % (source))
1741 1757 revs = None
1742 1758 if opts['rev']:
1743 1759 if 'lookup' in other.capabilities:
1744 1760 revs = [other.lookup(rev) for rev in opts['rev']]
1745 1761 else:
1746 1762 error = _("Other repository doesn't support revision lookup, so a rev cannot be specified.")
1747 1763 raise util.Abort(error)
1748 1764 modheads = repo.pull(other, heads=revs, force=opts['force'])
1749 1765 return postincoming(ui, repo, modheads, opts['update'])
1750 1766
1751 1767 def push(ui, repo, dest=None, **opts):
1752 1768 """push changes to the specified destination
1753 1769
1754 1770 Push changes from the local repository to the given destination.
1755 1771
1756 1772 This is the symmetrical operation for pull. It helps to move
1757 1773 changes from the current repository to a different one. If the
1758 1774 destination is local this is identical to a pull in that directory
1759 1775 from the current one.
1760 1776
1761 1777 By default, push will refuse to run if it detects the result would
1762 1778 increase the number of remote heads. This generally indicates the
1763 1779 the client has forgotten to sync and merge before pushing.
1764 1780
1765 1781 Valid URLs are of the form:
1766 1782
1767 1783 local/filesystem/path (or file://local/filesystem/path)
1768 1784 ssh://[user@]host[:port]/[path]
1769 1785 http://[user@]host[:port]/[path]
1770 1786 https://[user@]host[:port]/[path]
1771 1787
1772 1788 Look at the help text for the pull command for important details
1773 1789 about ssh:// URLs.
1774 1790
1775 1791 Pushing to http:// and https:// URLs is only possible, if this
1776 1792 feature is explicitly enabled on the remote Mercurial server.
1777 1793 """
1778 1794 dest = ui.expandpath(dest or 'default-push', dest or 'default')
1779 1795 setremoteconfig(ui, opts)
1780 1796
1781 1797 other = hg.repository(ui, dest)
1782 1798 ui.status('pushing to %s\n' % (dest))
1783 1799 revs = None
1784 1800 if opts['rev']:
1785 1801 revs = [repo.lookup(rev) for rev in opts['rev']]
1786 1802 r = repo.push(other, opts['force'], revs=revs)
1787 1803 return r == 0
1788 1804
1789 1805 def rawcommit(ui, repo, *pats, **opts):
1790 1806 """raw commit interface (DEPRECATED)
1791 1807
1792 1808 (DEPRECATED)
1793 1809 Lowlevel commit, for use in helper scripts.
1794 1810
1795 1811 This command is not intended to be used by normal users, as it is
1796 1812 primarily useful for importing from other SCMs.
1797 1813
1798 1814 This command is now deprecated and will be removed in a future
1799 1815 release, please use debugsetparents and commit instead.
1800 1816 """
1801 1817
1802 1818 ui.warn(_("(the rawcommit command is deprecated)\n"))
1803 1819
1804 1820 message = logmessage(opts)
1805 1821
1806 1822 files, match, anypats = cmdutil.matchpats(repo, pats, opts)
1807 1823 if opts['files']:
1808 1824 files += open(opts['files']).read().splitlines()
1809 1825
1810 1826 parents = [repo.lookup(p) for p in opts['parent']]
1811 1827
1812 1828 try:
1813 1829 repo.rawcommit(files, message, opts['user'], opts['date'], *parents)
1814 1830 except ValueError, inst:
1815 1831 raise util.Abort(str(inst))
1816 1832
1817 1833 def recover(ui, repo):
1818 1834 """roll back an interrupted transaction
1819 1835
1820 1836 Recover from an interrupted commit or pull.
1821 1837
1822 1838 This command tries to fix the repository status after an interrupted
1823 1839 operation. It should only be necessary when Mercurial suggests it.
1824 1840 """
1825 1841 if repo.recover():
1826 1842 return hg.verify(repo)
1827 1843 return 1
1828 1844
1829 1845 def remove(ui, repo, *pats, **opts):
1830 1846 """remove the specified files on the next commit
1831 1847
1832 1848 Schedule the indicated files for removal from the repository.
1833 1849
1834 1850 This command schedules the files to be removed at the next commit.
1835 1851 This only removes files from the current branch, not from the
1836 1852 entire project history. If the files still exist in the working
1837 1853 directory, they will be deleted from it. If invoked with --after,
1838 1854 files that have been manually deleted are marked as removed.
1839 1855
1840 1856 Modified files and added files are not removed by default. To
1841 1857 remove them, use the -f/--force option.
1842 1858 """
1843 1859 names = []
1844 1860 if not opts['after'] and not pats:
1845 1861 raise util.Abort(_('no files specified'))
1846 1862 files, matchfn, anypats = cmdutil.matchpats(repo, pats, opts)
1847 1863 exact = dict.fromkeys(files)
1848 1864 mardu = map(dict.fromkeys, repo.status(files=files, match=matchfn))[:5]
1849 1865 modified, added, removed, deleted, unknown = mardu
1850 1866 remove, forget = [], []
1851 1867 for src, abs, rel, exact in cmdutil.walk(repo, pats, opts):
1852 1868 reason = None
1853 1869 if abs not in deleted and opts['after']:
1854 1870 reason = _('is still present')
1855 1871 elif abs in modified and not opts['force']:
1856 1872 reason = _('is modified (use -f to force removal)')
1857 1873 elif abs in added:
1858 1874 if opts['force']:
1859 1875 forget.append(abs)
1860 1876 continue
1861 1877 reason = _('has been marked for add (use -f to force removal)')
1862 1878 elif abs in unknown:
1863 1879 reason = _('is not managed')
1864 1880 elif abs in removed:
1865 1881 continue
1866 1882 if reason:
1867 1883 if exact:
1868 1884 ui.warn(_('not removing %s: file %s\n') % (rel, reason))
1869 1885 else:
1870 1886 if ui.verbose or not exact:
1871 1887 ui.status(_('removing %s\n') % rel)
1872 1888 remove.append(abs)
1873 1889 repo.forget(forget)
1874 1890 repo.remove(remove, unlink=not opts['after'])
1875 1891
1876 1892 def rename(ui, repo, *pats, **opts):
1877 1893 """rename files; equivalent of copy + remove
1878 1894
1879 1895 Mark dest as copies of sources; mark sources for deletion. If
1880 1896 dest is a directory, copies are put in that directory. If dest is
1881 1897 a file, there can only be one source.
1882 1898
1883 1899 By default, this command copies the contents of files as they
1884 1900 stand in the working directory. If invoked with --after, the
1885 1901 operation is recorded, but no copying is performed.
1886 1902
1887 1903 This command takes effect in the next commit.
1888 1904 """
1889 1905 wlock = repo.wlock(0)
1890 1906 errs, copied = docopy(ui, repo, pats, opts, wlock)
1891 1907 names = []
1892 1908 for abs, rel, exact in copied:
1893 1909 if ui.verbose or not exact:
1894 1910 ui.status(_('removing %s\n') % rel)
1895 1911 names.append(abs)
1896 1912 if not opts.get('dry_run'):
1897 1913 repo.remove(names, True, wlock)
1898 1914 return errs
1899 1915
1900 1916 def revert(ui, repo, *pats, **opts):
1901 1917 """revert files or dirs to their states as of some revision
1902 1918
1903 1919 With no revision specified, revert the named files or directories
1904 1920 to the contents they had in the parent of the working directory.
1905 1921 This restores the contents of the affected files to an unmodified
1906 1922 state. If the working directory has two parents, you must
1907 1923 explicitly specify the revision to revert to.
1908 1924
1909 1925 Modified files are saved with a .orig suffix before reverting.
1910 1926 To disable these backups, use --no-backup.
1911 1927
1912 1928 Using the -r option, revert the given files or directories to their
1913 1929 contents as of a specific revision. This can be helpful to "roll
1914 1930 back" some or all of a change that should not have been committed.
1915 1931
1916 1932 Revert modifies the working directory. It does not commit any
1917 1933 changes, or change the parent of the working directory. If you
1918 1934 revert to a revision other than the parent of the working
1919 1935 directory, the reverted files will thus appear modified
1920 1936 afterwards.
1921 1937
1922 1938 If a file has been deleted, it is recreated. If the executable
1923 1939 mode of a file was changed, it is reset.
1924 1940
1925 1941 If names are given, all files matching the names are reverted.
1926 1942
1927 1943 If no arguments are given, no files are reverted.
1928 1944 """
1929 1945
1930 1946 if not pats and not opts['all']:
1931 1947 raise util.Abort(_('no files or directories specified; '
1932 1948 'use --all to revert the whole repo'))
1933 1949
1934 1950 parent, p2 = repo.dirstate.parents()
1935 1951 if not opts['rev'] and p2 != nullid:
1936 1952 raise util.Abort(_('uncommitted merge - please provide a '
1937 1953 'specific revision'))
1938 1954 node = repo.changectx(opts['rev']).node()
1939 1955 mf = repo.manifest.read(repo.changelog.read(node)[0])
1940 1956 if node == parent:
1941 1957 pmf = mf
1942 1958 else:
1943 1959 pmf = None
1944 1960
1945 1961 wlock = repo.wlock()
1946 1962
1947 1963 # need all matching names in dirstate and manifest of target rev,
1948 1964 # so have to walk both. do not print errors if files exist in one
1949 1965 # but not other.
1950 1966
1951 1967 names = {}
1952 1968 target_only = {}
1953 1969
1954 1970 # walk dirstate.
1955 1971
1956 1972 for src, abs, rel, exact in cmdutil.walk(repo, pats, opts,
1957 1973 badmatch=mf.has_key):
1958 1974 names[abs] = (rel, exact)
1959 1975 if src == 'b':
1960 1976 target_only[abs] = True
1961 1977
1962 1978 # walk target manifest.
1963 1979
1964 1980 for src, abs, rel, exact in cmdutil.walk(repo, pats, opts, node=node,
1965 1981 badmatch=names.has_key):
1966 1982 if abs in names: continue
1967 1983 names[abs] = (rel, exact)
1968 1984 target_only[abs] = True
1969 1985
1970 1986 changes = repo.status(match=names.has_key, wlock=wlock)[:5]
1971 1987 modified, added, removed, deleted, unknown = map(dict.fromkeys, changes)
1972 1988
1973 1989 revert = ([], _('reverting %s\n'))
1974 1990 add = ([], _('adding %s\n'))
1975 1991 remove = ([], _('removing %s\n'))
1976 1992 forget = ([], _('forgetting %s\n'))
1977 1993 undelete = ([], _('undeleting %s\n'))
1978 1994 update = {}
1979 1995
1980 1996 disptable = (
1981 1997 # dispatch table:
1982 1998 # file state
1983 1999 # action if in target manifest
1984 2000 # action if not in target manifest
1985 2001 # make backup if in target manifest
1986 2002 # make backup if not in target manifest
1987 2003 (modified, revert, remove, True, True),
1988 2004 (added, revert, forget, True, False),
1989 2005 (removed, undelete, None, False, False),
1990 2006 (deleted, revert, remove, False, False),
1991 2007 (unknown, add, None, True, False),
1992 2008 (target_only, add, None, False, False),
1993 2009 )
1994 2010
1995 2011 entries = names.items()
1996 2012 entries.sort()
1997 2013
1998 2014 for abs, (rel, exact) in entries:
1999 2015 mfentry = mf.get(abs)
2000 2016 def handle(xlist, dobackup):
2001 2017 xlist[0].append(abs)
2002 2018 update[abs] = 1
2003 2019 if dobackup and not opts['no_backup'] and os.path.exists(rel):
2004 2020 bakname = "%s.orig" % rel
2005 2021 ui.note(_('saving current version of %s as %s\n') %
2006 2022 (rel, bakname))
2007 2023 if not opts.get('dry_run'):
2008 2024 util.copyfile(rel, bakname)
2009 2025 if ui.verbose or not exact:
2010 2026 ui.status(xlist[1] % rel)
2011 2027 for table, hitlist, misslist, backuphit, backupmiss in disptable:
2012 2028 if abs not in table: continue
2013 2029 # file has changed in dirstate
2014 2030 if mfentry:
2015 2031 handle(hitlist, backuphit)
2016 2032 elif misslist is not None:
2017 2033 handle(misslist, backupmiss)
2018 2034 else:
2019 2035 if exact: ui.warn(_('file not managed: %s\n') % rel)
2020 2036 break
2021 2037 else:
2022 2038 # file has not changed in dirstate
2023 2039 if node == parent:
2024 2040 if exact: ui.warn(_('no changes needed to %s\n') % rel)
2025 2041 continue
2026 2042 if pmf is None:
2027 2043 # only need parent manifest in this unlikely case,
2028 2044 # so do not read by default
2029 2045 pmf = repo.manifest.read(repo.changelog.read(parent)[0])
2030 2046 if abs in pmf:
2031 2047 if mfentry:
2032 2048 # if version of file is same in parent and target
2033 2049 # manifests, do nothing
2034 2050 if pmf[abs] != mfentry:
2035 2051 handle(revert, False)
2036 2052 else:
2037 2053 handle(remove, False)
2038 2054
2039 2055 if not opts.get('dry_run'):
2040 2056 repo.dirstate.forget(forget[0])
2041 2057 r = hg.revert(repo, node, update.has_key, wlock)
2042 2058 repo.dirstate.update(add[0], 'a')
2043 2059 repo.dirstate.update(undelete[0], 'n')
2044 2060 repo.dirstate.update(remove[0], 'r')
2045 2061 return r
2046 2062
2047 2063 def rollback(ui, repo):
2048 2064 """roll back the last transaction in this repository
2049 2065
2050 2066 Roll back the last transaction in this repository, restoring the
2051 2067 project to its state prior to the transaction.
2052 2068
2053 2069 Transactions are used to encapsulate the effects of all commands
2054 2070 that create new changesets or propagate existing changesets into a
2055 2071 repository. For example, the following commands are transactional,
2056 2072 and their effects can be rolled back:
2057 2073
2058 2074 commit
2059 2075 import
2060 2076 pull
2061 2077 push (with this repository as destination)
2062 2078 unbundle
2063 2079
2064 2080 This command should be used with care. There is only one level of
2065 2081 rollback, and there is no way to undo a rollback.
2066 2082
2067 2083 This command is not intended for use on public repositories. Once
2068 2084 changes are visible for pull by other users, rolling a transaction
2069 2085 back locally is ineffective (someone else may already have pulled
2070 2086 the changes). Furthermore, a race is possible with readers of the
2071 2087 repository; for example an in-progress pull from the repository
2072 2088 may fail if a rollback is performed.
2073 2089 """
2074 2090 repo.rollback()
2075 2091
2076 2092 def root(ui, repo):
2077 2093 """print the root (top) of the current working dir
2078 2094
2079 2095 Print the root directory of the current repository.
2080 2096 """
2081 2097 ui.write(repo.root + "\n")
2082 2098
2083 2099 def serve(ui, repo, **opts):
2084 2100 """export the repository via HTTP
2085 2101
2086 2102 Start a local HTTP repository browser and pull server.
2087 2103
2088 2104 By default, the server logs accesses to stdout and errors to
2089 2105 stderr. Use the "-A" and "-E" options to log to files.
2090 2106 """
2091 2107
2092 2108 if opts["stdio"]:
2093 2109 if repo is None:
2094 2110 raise hg.RepoError(_("There is no Mercurial repository here"
2095 2111 " (.hg not found)"))
2096 2112 s = sshserver.sshserver(ui, repo)
2097 2113 s.serve_forever()
2098 2114
2099 2115 optlist = ("name templates style address port ipv6"
2100 2116 " accesslog errorlog webdir_conf")
2101 2117 for o in optlist.split():
2102 2118 if opts[o]:
2103 2119 ui.setconfig("web", o, str(opts[o]))
2104 2120
2105 2121 if repo is None and not ui.config("web", "webdir_conf"):
2106 2122 raise hg.RepoError(_("There is no Mercurial repository here"
2107 2123 " (.hg not found)"))
2108 2124
2109 2125 if opts['daemon'] and not opts['daemon_pipefds']:
2110 2126 rfd, wfd = os.pipe()
2111 2127 args = sys.argv[:]
2112 2128 args.append('--daemon-pipefds=%d,%d' % (rfd, wfd))
2113 2129 pid = os.spawnvp(os.P_NOWAIT | getattr(os, 'P_DETACH', 0),
2114 2130 args[0], args)
2115 2131 os.close(wfd)
2116 2132 os.read(rfd, 1)
2117 2133 os._exit(0)
2118 2134
2119 2135 httpd = hgweb.server.create_server(ui, repo)
2120 2136
2121 2137 if ui.verbose:
2122 2138 if httpd.port != 80:
2123 2139 ui.status(_('listening at http://%s:%d/\n') %
2124 2140 (httpd.addr, httpd.port))
2125 2141 else:
2126 2142 ui.status(_('listening at http://%s/\n') % httpd.addr)
2127 2143
2128 2144 if opts['pid_file']:
2129 2145 fp = open(opts['pid_file'], 'w')
2130 2146 fp.write(str(os.getpid()) + '\n')
2131 2147 fp.close()
2132 2148
2133 2149 if opts['daemon_pipefds']:
2134 2150 rfd, wfd = [int(x) for x in opts['daemon_pipefds'].split(',')]
2135 2151 os.close(rfd)
2136 2152 os.write(wfd, 'y')
2137 2153 os.close(wfd)
2138 2154 sys.stdout.flush()
2139 2155 sys.stderr.flush()
2140 2156 fd = os.open(util.nulldev, os.O_RDWR)
2141 2157 if fd != 0: os.dup2(fd, 0)
2142 2158 if fd != 1: os.dup2(fd, 1)
2143 2159 if fd != 2: os.dup2(fd, 2)
2144 2160 if fd not in (0, 1, 2): os.close(fd)
2145 2161
2146 2162 httpd.serve_forever()
2147 2163
2148 2164 def status(ui, repo, *pats, **opts):
2149 2165 """show changed files in the working directory
2150 2166
2151 2167 Show status of files in the repository. If names are given, only
2152 2168 files that match are shown. Files that are clean or ignored, are
2153 2169 not listed unless -c (clean), -i (ignored) or -A is given.
2154 2170
2155 2171 If one revision is given, it is used as the base revision.
2156 2172 If two revisions are given, the difference between them is shown.
2157 2173
2158 2174 The codes used to show the status of files are:
2159 2175 M = modified
2160 2176 A = added
2161 2177 R = removed
2162 2178 C = clean
2163 2179 ! = deleted, but still tracked
2164 2180 ? = not tracked
2165 2181 I = ignored (not shown by default)
2166 2182 = the previous added file was copied from here
2167 2183 """
2168 2184
2169 2185 all = opts['all']
2170 2186 node1, node2 = cmdutil.revpair(repo, opts.get('rev'))
2171 2187
2172 2188 files, matchfn, anypats = cmdutil.matchpats(repo, pats, opts)
2173 2189 cwd = (pats and repo.getcwd()) or ''
2174 2190 modified, added, removed, deleted, unknown, ignored, clean = [
2175 2191 [util.pathto(cwd, x) for x in n]
2176 2192 for n in repo.status(node1=node1, node2=node2, files=files,
2177 2193 match=matchfn,
2178 2194 list_ignored=all or opts['ignored'],
2179 2195 list_clean=all or opts['clean'])]
2180 2196
2181 2197 changetypes = (('modified', 'M', modified),
2182 2198 ('added', 'A', added),
2183 2199 ('removed', 'R', removed),
2184 2200 ('deleted', '!', deleted),
2185 2201 ('unknown', '?', unknown),
2186 2202 ('ignored', 'I', ignored))
2187 2203
2188 2204 explicit_changetypes = changetypes + (('clean', 'C', clean),)
2189 2205
2190 2206 end = opts['print0'] and '\0' or '\n'
2191 2207
2192 2208 for opt, char, changes in ([ct for ct in explicit_changetypes
2193 2209 if all or opts[ct[0]]]
2194 2210 or changetypes):
2195 2211 if opts['no_status']:
2196 2212 format = "%%s%s" % end
2197 2213 else:
2198 2214 format = "%s %%s%s" % (char, end)
2199 2215
2200 2216 for f in changes:
2201 2217 ui.write(format % f)
2202 2218 if ((all or opts.get('copies')) and not opts.get('no_status')):
2203 2219 copied = repo.dirstate.copied(f)
2204 2220 if copied:
2205 2221 ui.write(' %s%s' % (copied, end))
2206 2222
2207 2223 def tag(ui, repo, name, rev_=None, **opts):
2208 2224 """add a tag for the current tip or a given revision
2209 2225
2210 2226 Name a particular revision using <name>.
2211 2227
2212 2228 Tags are used to name particular revisions of the repository and are
2213 2229 very useful to compare different revision, to go back to significant
2214 2230 earlier versions or to mark branch points as releases, etc.
2215 2231
2216 2232 If no revision is given, the parent of the working directory is used.
2217 2233
2218 2234 To facilitate version control, distribution, and merging of tags,
2219 2235 they are stored as a file named ".hgtags" which is managed
2220 2236 similarly to other project files and can be hand-edited if
2221 2237 necessary. The file '.hg/localtags' is used for local tags (not
2222 2238 shared among repositories).
2223 2239 """
2224 2240 if name in ['tip', '.', 'null']:
2225 2241 raise util.Abort(_("the name '%s' is reserved") % name)
2226 2242 if rev_ is not None:
2227 2243 ui.warn(_("use of 'hg tag NAME [REV]' is deprecated, "
2228 2244 "please use 'hg tag [-r REV] NAME' instead\n"))
2229 2245 if opts['rev']:
2230 2246 raise util.Abort(_("use only one form to specify the revision"))
2231 2247 if opts['rev']:
2232 2248 rev_ = opts['rev']
2233 2249 if not rev_ and repo.dirstate.parents()[1] != nullid:
2234 2250 raise util.Abort(_('uncommitted merge - please provide a '
2235 2251 'specific revision'))
2236 2252 r = repo.changectx(rev_).node()
2237 2253
2238 2254 message = opts['message']
2239 2255 if not message:
2240 2256 message = _('Added tag %s for changeset %s') % (name, short(r))
2241 2257
2242 2258 repo.tag(name, r, message, opts['local'], opts['user'], opts['date'])
2243 2259
2244 2260 def tags(ui, repo):
2245 2261 """list repository tags
2246 2262
2247 2263 List the repository tags.
2248 2264
2249 2265 This lists both regular and local tags.
2250 2266 """
2251 2267
2252 2268 l = repo.tagslist()
2253 2269 l.reverse()
2254 2270 hexfunc = ui.debugflag and hex or short
2255 2271 for t, n in l:
2256 2272 try:
2257 2273 r = "%5d:%s" % (repo.changelog.rev(n), hexfunc(n))
2258 2274 except KeyError:
2259 2275 r = " ?:?"
2260 2276 if ui.quiet:
2261 2277 ui.write("%s\n" % t)
2262 2278 else:
2263 2279 t = util.localsub(t, 30)
2264 2280 t += " " * (30 - util.locallen(t))
2265 2281 ui.write("%s %s\n" % (t, r))
2266 2282
2267 2283 def tip(ui, repo, **opts):
2268 2284 """show the tip revision
2269 2285
2270 2286 Show the tip revision.
2271 2287 """
2272 2288 cmdutil.show_changeset(ui, repo, opts).show(nullrev+repo.changelog.count())
2273 2289
2274 2290 def unbundle(ui, repo, fname, **opts):
2275 2291 """apply a changegroup file
2276 2292
2277 2293 Apply a compressed changegroup file generated by the bundle
2278 2294 command.
2279 2295 """
2280 2296 gen = changegroup.readbundle(urllib.urlopen(fname))
2281 2297 modheads = repo.addchangegroup(gen, 'unbundle', 'bundle:' + fname)
2282 2298 return postincoming(ui, repo, modheads, opts['update'])
2283 2299
2284 2300 def update(ui, repo, node=None, clean=False, branch=None):
2285 2301 """update or merge working directory
2286 2302
2287 2303 Update the working directory to the specified revision.
2288 2304
2289 2305 If there are no outstanding changes in the working directory and
2290 2306 there is a linear relationship between the current version and the
2291 2307 requested version, the result is the requested version.
2292 2308
2293 2309 To merge the working directory with another revision, use the
2294 2310 merge command.
2295 2311
2296 2312 By default, update will refuse to run if doing so would require
2297 2313 merging or discarding local changes.
2298 2314 """
2299 2315 node = _lookup(repo, node, branch)
2300 2316 if clean:
2301 2317 return hg.clean(repo, node)
2302 2318 else:
2303 2319 return hg.update(repo, node)
2304 2320
2305 2321 def _lookup(repo, node, branch=None):
2306 2322 if branch:
2307 2323 repo.ui.warn(_("the --branch option is deprecated, "
2308 2324 "please use 'hg branch' instead\n"))
2309 2325 br = repo.branchlookup(branch=branch)
2310 2326 found = []
2311 2327 for x in br:
2312 2328 if branch in br[x]:
2313 2329 found.append(x)
2314 2330 if len(found) > 1:
2315 2331 repo.ui.warn(_("Found multiple heads for %s\n") % branch)
2316 2332 for x in found:
2317 2333 cmdutil.show_changeset(ui, repo, {}).show(changenode=x)
2318 2334 raise util.Abort("")
2319 2335 if len(found) == 1:
2320 2336 node = found[0]
2321 2337 repo.ui.warn(_("Using head %s for branch %s\n")
2322 2338 % (short(node), branch))
2323 2339 else:
2324 2340 raise util.Abort(_("branch %s not found") % branch)
2325 2341 else:
2326 2342 node = node and repo.lookup(node) or repo.changelog.tip()
2327 2343 return node
2328 2344
2329 2345 def verify(ui, repo):
2330 2346 """verify the integrity of the repository
2331 2347
2332 2348 Verify the integrity of the current repository.
2333 2349
2334 2350 This will perform an extensive check of the repository's
2335 2351 integrity, validating the hashes and checksums of each entry in
2336 2352 the changelog, manifest, and tracked files, as well as the
2337 2353 integrity of their crosslinks and indices.
2338 2354 """
2339 2355 return hg.verify(repo)
2340 2356
2341 2357 def version_(ui):
2342 2358 """output version and copyright information"""
2343 2359 ui.write(_("Mercurial Distributed SCM (version %s)\n")
2344 2360 % version.get_version())
2345 2361 ui.status(_(
2346 2362 "\nCopyright (C) 2005, 2006 Matt Mackall <mpm@selenic.com>\n"
2347 2363 "This is free software; see the source for copying conditions. "
2348 2364 "There is NO\nwarranty; "
2349 2365 "not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n"
2350 2366 ))
2351 2367
2352 2368 # Command options and aliases are listed here, alphabetically
2353 2369
2354 2370 globalopts = [
2355 2371 ('R', 'repository', '',
2356 2372 _('repository root directory or symbolic path name')),
2357 2373 ('', 'cwd', '', _('change working directory')),
2358 2374 ('y', 'noninteractive', None,
2359 2375 _('do not prompt, assume \'yes\' for any required answers')),
2360 2376 ('q', 'quiet', None, _('suppress output')),
2361 2377 ('v', 'verbose', None, _('enable additional output')),
2362 2378 ('', 'config', [], _('set/override config option')),
2363 2379 ('', 'debug', None, _('enable debugging output')),
2364 2380 ('', 'debugger', None, _('start debugger')),
2365 2381 ('', 'encoding', util._encoding, _('set the charset encoding')),
2366 2382 ('', 'encodingmode', util._encodingmode, _('set the charset encoding mode')),
2367 2383 ('', 'lsprof', None, _('print improved command execution profile')),
2368 2384 ('', 'traceback', None, _('print traceback on exception')),
2369 2385 ('', 'time', None, _('time how long the command takes')),
2370 2386 ('', 'profile', None, _('print command execution profile')),
2371 2387 ('', 'version', None, _('output version information and exit')),
2372 2388 ('h', 'help', None, _('display help and exit')),
2373 2389 ]
2374 2390
2375 2391 dryrunopts = [('n', 'dry-run', None,
2376 2392 _('do not perform actions, just print output'))]
2377 2393
2378 2394 remoteopts = [
2379 2395 ('e', 'ssh', '', _('specify ssh command to use')),
2380 2396 ('', 'remotecmd', '', _('specify hg command to run on the remote side')),
2381 2397 ]
2382 2398
2383 2399 walkopts = [
2384 2400 ('I', 'include', [], _('include names matching the given patterns')),
2385 2401 ('X', 'exclude', [], _('exclude names matching the given patterns')),
2386 2402 ]
2387 2403
2388 2404 table = {
2389 2405 "^add":
2390 2406 (add,
2391 2407 walkopts + dryrunopts,
2392 2408 _('hg add [OPTION]... [FILE]...')),
2393 2409 "addremove":
2394 2410 (addremove,
2395 2411 [('s', 'similarity', '',
2396 2412 _('guess renamed files by similarity (0<=s<=100)')),
2397 2413 ] + walkopts + dryrunopts,
2398 2414 _('hg addremove [OPTION]... [FILE]...')),
2399 2415 "^annotate":
2400 2416 (annotate,
2401 2417 [('r', 'rev', '', _('annotate the specified revision')),
2402 2418 ('f', 'follow', None, _('follow file copies and renames')),
2403 2419 ('a', 'text', None, _('treat all files as text')),
2404 2420 ('u', 'user', None, _('list the author')),
2405 2421 ('d', 'date', None, _('list the date')),
2406 2422 ('n', 'number', None, _('list the revision number (default)')),
2407 2423 ('c', 'changeset', None, _('list the changeset')),
2408 2424 ] + walkopts,
2409 2425 _('hg annotate [-r REV] [-a] [-u] [-d] [-n] [-c] FILE...')),
2410 2426 "archive":
2411 2427 (archive,
2412 2428 [('', 'no-decode', None, _('do not pass files through decoders')),
2413 2429 ('p', 'prefix', '', _('directory prefix for files in archive')),
2414 2430 ('r', 'rev', '', _('revision to distribute')),
2415 2431 ('t', 'type', '', _('type of distribution to create')),
2416 2432 ] + walkopts,
2417 2433 _('hg archive [OPTION]... DEST')),
2418 2434 "backout":
2419 2435 (backout,
2420 2436 [('', 'merge', None,
2421 2437 _('merge with old dirstate parent after backout')),
2422 2438 ('m', 'message', '', _('use <text> as commit message')),
2423 2439 ('l', 'logfile', '', _('read commit message from <file>')),
2424 2440 ('d', 'date', '', _('record datecode as commit date')),
2425 2441 ('', 'parent', '', _('parent to choose when backing out merge')),
2426 2442 ('u', 'user', '', _('record user as committer')),
2427 2443 ] + walkopts,
2428 2444 _('hg backout [OPTION]... REV')),
2429 2445 "branch": (branch, [], _('hg branch [NAME]')),
2430 2446 "branches": (branches, [], _('hg branches')),
2431 2447 "bundle":
2432 2448 (bundle,
2433 2449 [('f', 'force', None,
2434 2450 _('run even when remote repository is unrelated')),
2435 2451 ('r', 'rev', [],
2436 2452 _('a changeset you would like to bundle')),
2437 2453 ('', 'base', [],
2438 2454 _('a base changeset to specify instead of a destination')),
2439 2455 ] + remoteopts,
2440 2456 _('hg bundle [--base REV]... [--rev REV]... FILE [DEST]')),
2441 2457 "cat":
2442 2458 (cat,
2443 2459 [('o', 'output', '', _('print output to file with formatted name')),
2444 2460 ('r', 'rev', '', _('print the given revision')),
2445 2461 ] + walkopts,
2446 2462 _('hg cat [OPTION]... FILE...')),
2447 2463 "^clone":
2448 2464 (clone,
2449 2465 [('U', 'noupdate', None, _('do not update the new working directory')),
2450 2466 ('r', 'rev', [],
2451 2467 _('a changeset you would like to have after cloning')),
2452 2468 ('', 'pull', None, _('use pull protocol to copy metadata')),
2453 2469 ('', 'uncompressed', None,
2454 2470 _('use uncompressed transfer (fast over LAN)')),
2455 2471 ] + remoteopts,
2456 2472 _('hg clone [OPTION]... SOURCE [DEST]')),
2457 2473 "^commit|ci":
2458 2474 (commit,
2459 2475 [('A', 'addremove', None,
2460 2476 _('mark new/missing files as added/removed before committing')),
2461 2477 ('m', 'message', '', _('use <text> as commit message')),
2462 2478 ('l', 'logfile', '', _('read the commit message from <file>')),
2463 2479 ('d', 'date', '', _('record datecode as commit date')),
2464 2480 ('u', 'user', '', _('record user as commiter')),
2465 2481 ] + walkopts,
2466 2482 _('hg commit [OPTION]... [FILE]...')),
2467 2483 "copy|cp":
2468 2484 (copy,
2469 2485 [('A', 'after', None, _('record a copy that has already occurred')),
2470 2486 ('f', 'force', None,
2471 2487 _('forcibly copy over an existing managed file')),
2472 2488 ] + walkopts + dryrunopts,
2473 2489 _('hg copy [OPTION]... [SOURCE]... DEST')),
2474 2490 "debugancestor": (debugancestor, [], _('debugancestor INDEX REV1 REV2')),
2475 2491 "debugcomplete":
2476 2492 (debugcomplete,
2477 2493 [('o', 'options', None, _('show the command options'))],
2478 2494 _('debugcomplete [-o] CMD')),
2479 2495 "debugrebuildstate":
2480 2496 (debugrebuildstate,
2481 2497 [('r', 'rev', '', _('revision to rebuild to'))],
2482 2498 _('debugrebuildstate [-r REV] [REV]')),
2483 2499 "debugcheckstate": (debugcheckstate, [], _('debugcheckstate')),
2484 2500 "debugsetparents": (debugsetparents, [], _('debugsetparents REV1 [REV2]')),
2485 2501 "debugstate": (debugstate, [], _('debugstate')),
2486 "debugdate": (debugdate, [], _('debugdata DATE')),
2502 "debugdate": (debugdate,
2503 [('e','extended', None, _('try extended date formats'))],
2504 _('debugdata [-e] DATE [RANGE]')),
2487 2505 "debugdata": (debugdata, [], _('debugdata FILE REV')),
2488 2506 "debugindex": (debugindex, [], _('debugindex FILE')),
2489 2507 "debugindexdot": (debugindexdot, [], _('debugindexdot FILE')),
2490 2508 "debugrename": (debugrename, [], _('debugrename FILE [REV]')),
2491 2509 "debugwalk":
2492 2510 (debugwalk, walkopts, _('debugwalk [OPTION]... [FILE]...')),
2493 2511 "^diff":
2494 2512 (diff,
2495 2513 [('r', 'rev', [], _('revision')),
2496 2514 ('a', 'text', None, _('treat all files as text')),
2497 2515 ('p', 'show-function', None,
2498 2516 _('show which function each change is in')),
2499 2517 ('g', 'git', None, _('use git extended diff format')),
2500 2518 ('', 'nodates', None, _("don't include dates in diff headers")),
2501 2519 ('w', 'ignore-all-space', None,
2502 2520 _('ignore white space when comparing lines')),
2503 2521 ('b', 'ignore-space-change', None,
2504 2522 _('ignore changes in the amount of white space')),
2505 2523 ('B', 'ignore-blank-lines', None,
2506 2524 _('ignore changes whose lines are all blank')),
2507 2525 ] + walkopts,
2508 2526 _('hg diff [-a] [-I] [-X] [-r REV1 [-r REV2]] [FILE]...')),
2509 2527 "^export":
2510 2528 (export,
2511 2529 [('o', 'output', '', _('print output to file with formatted name')),
2512 2530 ('a', 'text', None, _('treat all files as text')),
2513 2531 ('g', 'git', None, _('use git extended diff format')),
2514 2532 ('', 'nodates', None, _("don't include dates in diff headers")),
2515 2533 ('', 'switch-parent', None, _('diff against the second parent'))],
2516 2534 _('hg export [-a] [-o OUTFILESPEC] REV...')),
2517 2535 "grep":
2518 2536 (grep,
2519 2537 [('0', 'print0', None, _('end fields with NUL')),
2520 2538 ('', 'all', None, _('print all revisions that match')),
2521 2539 ('f', 'follow', None,
2522 2540 _('follow changeset history, or file history across copies and renames')),
2523 2541 ('i', 'ignore-case', None, _('ignore case when matching')),
2524 2542 ('l', 'files-with-matches', None,
2525 2543 _('print only filenames and revs that match')),
2526 2544 ('n', 'line-number', None, _('print matching line numbers')),
2527 2545 ('r', 'rev', [], _('search in given revision range')),
2528 2546 ('u', 'user', None, _('print user who committed change')),
2529 2547 ] + walkopts,
2530 2548 _('hg grep [OPTION]... PATTERN [FILE]...')),
2531 2549 "heads":
2532 2550 (heads,
2533 2551 [('b', 'branches', None, _('show branches (DEPRECATED)')),
2534 2552 ('', 'style', '', _('display using template map file')),
2535 2553 ('r', 'rev', '', _('show only heads which are descendants of rev')),
2536 2554 ('', 'template', '', _('display with template'))],
2537 2555 _('hg heads [-r REV]')),
2538 2556 "help": (help_, [], _('hg help [COMMAND]')),
2539 2557 "identify|id": (identify, [], _('hg identify')),
2540 2558 "import|patch":
2541 2559 (import_,
2542 2560 [('p', 'strip', 1,
2543 2561 _('directory strip option for patch. This has the same\n'
2544 2562 'meaning as the corresponding patch option')),
2545 2563 ('m', 'message', '', _('use <text> as commit message')),
2546 2564 ('b', 'base', '', _('base path (DEPRECATED)')),
2547 2565 ('f', 'force', None,
2548 2566 _('skip check for outstanding uncommitted changes'))],
2549 2567 _('hg import [-p NUM] [-m MESSAGE] [-f] PATCH...')),
2550 2568 "incoming|in": (incoming,
2551 2569 [('M', 'no-merges', None, _('do not show merges')),
2552 2570 ('f', 'force', None,
2553 2571 _('run even when remote repository is unrelated')),
2554 2572 ('', 'style', '', _('display using template map file')),
2555 2573 ('n', 'newest-first', None, _('show newest record first')),
2556 2574 ('', 'bundle', '', _('file to store the bundles into')),
2557 2575 ('p', 'patch', None, _('show patch')),
2558 2576 ('r', 'rev', [], _('a specific revision up to which you would like to pull')),
2559 2577 ('', 'template', '', _('display with template')),
2560 2578 ] + remoteopts,
2561 2579 _('hg incoming [-p] [-n] [-M] [-r REV]...'
2562 2580 ' [--bundle FILENAME] [SOURCE]')),
2563 2581 "^init":
2564 2582 (init, remoteopts, _('hg init [-e FILE] [--remotecmd FILE] [DEST]')),
2565 2583 "locate":
2566 2584 (locate,
2567 2585 [('r', 'rev', '', _('search the repository as it stood at rev')),
2568 2586 ('0', 'print0', None,
2569 2587 _('end filenames with NUL, for use with xargs')),
2570 2588 ('f', 'fullpath', None,
2571 2589 _('print complete paths from the filesystem root')),
2572 2590 ] + walkopts,
2573 2591 _('hg locate [OPTION]... [PATTERN]...')),
2574 2592 "^log|history":
2575 2593 (log,
2576 2594 [('b', 'branches', None, _('show branches (DEPRECATED)')),
2577 2595 ('f', 'follow', None,
2578 2596 _('follow changeset history, or file history across copies and renames')),
2579 2597 ('', 'follow-first', None,
2580 2598 _('only follow the first parent of merge changesets')),
2599 ('d', 'date', '', _('show revs matching date spec')),
2581 2600 ('C', 'copies', None, _('show copied files')),
2582 2601 ('k', 'keyword', [], _('search for a keyword')),
2583 2602 ('l', 'limit', '', _('limit number of changes displayed')),
2584 2603 ('r', 'rev', [], _('show the specified revision or range')),
2585 2604 ('', 'removed', None, _('include revs where files were removed')),
2586 2605 ('M', 'no-merges', None, _('do not show merges')),
2587 2606 ('', 'style', '', _('display using template map file')),
2588 2607 ('m', 'only-merges', None, _('show only merges')),
2589 2608 ('p', 'patch', None, _('show patch')),
2590 2609 ('P', 'prune', [], _('do not display revision or any of its ancestors')),
2591 2610 ('', 'template', '', _('display with template')),
2592 2611 ] + walkopts,
2593 2612 _('hg log [OPTION]... [FILE]')),
2594 2613 "manifest": (manifest, [], _('hg manifest [REV]')),
2595 2614 "merge":
2596 2615 (merge,
2597 2616 [('b', 'branch', '', _('merge with head of a specific branch (DEPRECATED)')),
2598 2617 ('f', 'force', None, _('force a merge with outstanding changes'))],
2599 2618 _('hg merge [-f] [REV]')),
2600 2619 "outgoing|out": (outgoing,
2601 2620 [('M', 'no-merges', None, _('do not show merges')),
2602 2621 ('f', 'force', None,
2603 2622 _('run even when remote repository is unrelated')),
2604 2623 ('p', 'patch', None, _('show patch')),
2605 2624 ('', 'style', '', _('display using template map file')),
2606 2625 ('r', 'rev', [], _('a specific revision you would like to push')),
2607 2626 ('n', 'newest-first', None, _('show newest record first')),
2608 2627 ('', 'template', '', _('display with template')),
2609 2628 ] + remoteopts,
2610 2629 _('hg outgoing [-M] [-p] [-n] [-r REV]... [DEST]')),
2611 2630 "^parents":
2612 2631 (parents,
2613 2632 [('b', 'branches', None, _('show branches (DEPRECATED)')),
2614 2633 ('r', 'rev', '', _('show parents from the specified rev')),
2615 2634 ('', 'style', '', _('display using template map file')),
2616 2635 ('', 'template', '', _('display with template'))],
2617 2636 _('hg parents [-r REV] [FILE]')),
2618 2637 "paths": (paths, [], _('hg paths [NAME]')),
2619 2638 "^pull":
2620 2639 (pull,
2621 2640 [('u', 'update', None,
2622 2641 _('update to new tip if changesets were pulled')),
2623 2642 ('f', 'force', None,
2624 2643 _('run even when remote repository is unrelated')),
2625 2644 ('r', 'rev', [], _('a specific revision up to which you would like to pull')),
2626 2645 ] + remoteopts,
2627 2646 _('hg pull [-u] [-r REV]... [-e FILE] [--remotecmd FILE] [SOURCE]')),
2628 2647 "^push":
2629 2648 (push,
2630 2649 [('f', 'force', None, _('force push')),
2631 2650 ('r', 'rev', [], _('a specific revision you would like to push')),
2632 2651 ] + remoteopts,
2633 2652 _('hg push [-f] [-r REV]... [-e FILE] [--remotecmd FILE] [DEST]')),
2634 2653 "debugrawcommit|rawcommit":
2635 2654 (rawcommit,
2636 2655 [('p', 'parent', [], _('parent')),
2637 2656 ('d', 'date', '', _('date code')),
2638 2657 ('u', 'user', '', _('user')),
2639 2658 ('F', 'files', '', _('file list')),
2640 2659 ('m', 'message', '', _('commit message')),
2641 2660 ('l', 'logfile', '', _('commit message file'))],
2642 2661 _('hg debugrawcommit [OPTION]... [FILE]...')),
2643 2662 "recover": (recover, [], _('hg recover')),
2644 2663 "^remove|rm":
2645 2664 (remove,
2646 2665 [('A', 'after', None, _('record remove that has already occurred')),
2647 2666 ('f', 'force', None, _('remove file even if modified')),
2648 2667 ] + walkopts,
2649 2668 _('hg remove [OPTION]... FILE...')),
2650 2669 "rename|mv":
2651 2670 (rename,
2652 2671 [('A', 'after', None, _('record a rename that has already occurred')),
2653 2672 ('f', 'force', None,
2654 2673 _('forcibly copy over an existing managed file')),
2655 2674 ] + walkopts + dryrunopts,
2656 2675 _('hg rename [OPTION]... SOURCE... DEST')),
2657 2676 "^revert":
2658 2677 (revert,
2659 2678 [('a', 'all', None, _('revert all changes when no arguments given')),
2660 2679 ('r', 'rev', '', _('revision to revert to')),
2661 2680 ('', 'no-backup', None, _('do not save backup copies of files')),
2662 2681 ] + walkopts + dryrunopts,
2663 2682 _('hg revert [-r REV] [NAME]...')),
2664 2683 "rollback": (rollback, [], _('hg rollback')),
2665 2684 "root": (root, [], _('hg root')),
2666 2685 "showconfig|debugconfig":
2667 2686 (showconfig,
2668 2687 [('u', 'untrusted', None, _('show untrusted configuration options'))],
2669 2688 _('showconfig [-u] [NAME]...')),
2670 2689 "^serve":
2671 2690 (serve,
2672 2691 [('A', 'accesslog', '', _('name of access log file to write to')),
2673 2692 ('d', 'daemon', None, _('run server in background')),
2674 2693 ('', 'daemon-pipefds', '', _('used internally by daemon mode')),
2675 2694 ('E', 'errorlog', '', _('name of error log file to write to')),
2676 2695 ('p', 'port', 0, _('port to use (default: 8000)')),
2677 2696 ('a', 'address', '', _('address to use')),
2678 2697 ('n', 'name', '',
2679 2698 _('name to show in web pages (default: working dir)')),
2680 2699 ('', 'webdir-conf', '', _('name of the webdir config file'
2681 2700 ' (serve more than one repo)')),
2682 2701 ('', 'pid-file', '', _('name of file to write process ID to')),
2683 2702 ('', 'stdio', None, _('for remote clients')),
2684 2703 ('t', 'templates', '', _('web templates to use')),
2685 2704 ('', 'style', '', _('template style to use')),
2686 2705 ('6', 'ipv6', None, _('use IPv6 in addition to IPv4'))],
2687 2706 _('hg serve [OPTION]...')),
2688 2707 "^status|st":
2689 2708 (status,
2690 2709 [('A', 'all', None, _('show status of all files')),
2691 2710 ('m', 'modified', None, _('show only modified files')),
2692 2711 ('a', 'added', None, _('show only added files')),
2693 2712 ('r', 'removed', None, _('show only removed files')),
2694 2713 ('d', 'deleted', None, _('show only deleted (but tracked) files')),
2695 2714 ('c', 'clean', None, _('show only files without changes')),
2696 2715 ('u', 'unknown', None, _('show only unknown (not tracked) files')),
2697 2716 ('i', 'ignored', None, _('show ignored files')),
2698 2717 ('n', 'no-status', None, _('hide status prefix')),
2699 2718 ('C', 'copies', None, _('show source of copied files')),
2700 2719 ('0', 'print0', None,
2701 2720 _('end filenames with NUL, for use with xargs')),
2702 2721 ('', 'rev', [], _('show difference from revision')),
2703 2722 ] + walkopts,
2704 2723 _('hg status [OPTION]... [FILE]...')),
2705 2724 "tag":
2706 2725 (tag,
2707 2726 [('l', 'local', None, _('make the tag local')),
2708 2727 ('m', 'message', '', _('message for tag commit log entry')),
2709 2728 ('d', 'date', '', _('record datecode as commit date')),
2710 2729 ('u', 'user', '', _('record user as commiter')),
2711 2730 ('r', 'rev', '', _('revision to tag'))],
2712 2731 _('hg tag [-l] [-m TEXT] [-d DATE] [-u USER] [-r REV] NAME')),
2713 2732 "tags": (tags, [], _('hg tags')),
2714 2733 "tip":
2715 2734 (tip,
2716 2735 [('b', 'branches', None, _('show branches (DEPRECATED)')),
2717 2736 ('', 'style', '', _('display using template map file')),
2718 2737 ('p', 'patch', None, _('show patch')),
2719 2738 ('', 'template', '', _('display with template'))],
2720 2739 _('hg tip [-p]')),
2721 2740 "unbundle":
2722 2741 (unbundle,
2723 2742 [('u', 'update', None,
2724 2743 _('update to new tip if changesets were unbundled'))],
2725 2744 _('hg unbundle [-u] FILE')),
2726 2745 "^update|up|checkout|co":
2727 2746 (update,
2728 2747 [('b', 'branch', '',
2729 2748 _('checkout the head of a specific branch (DEPRECATED)')),
2730 2749 ('C', 'clean', None, _('overwrite locally modified files'))],
2731 2750 _('hg update [-C] [REV]')),
2732 2751 "verify": (verify, [], _('hg verify')),
2733 2752 "version": (version_, [], _('hg version')),
2734 2753 }
2735 2754
2736 2755 norepo = ("clone init version help debugancestor debugcomplete debugdata"
2737 2756 " debugindex debugindexdot debugdate")
2738 2757 optionalrepo = ("paths serve showconfig")
2739 2758
2740 2759 def findpossible(ui, cmd):
2741 2760 """
2742 2761 Return cmd -> (aliases, command table entry)
2743 2762 for each matching command.
2744 2763 Return debug commands (or their aliases) only if no normal command matches.
2745 2764 """
2746 2765 choice = {}
2747 2766 debugchoice = {}
2748 2767 for e in table.keys():
2749 2768 aliases = e.lstrip("^").split("|")
2750 2769 found = None
2751 2770 if cmd in aliases:
2752 2771 found = cmd
2753 2772 elif not ui.config("ui", "strict"):
2754 2773 for a in aliases:
2755 2774 if a.startswith(cmd):
2756 2775 found = a
2757 2776 break
2758 2777 if found is not None:
2759 2778 if aliases[0].startswith("debug") or found.startswith("debug"):
2760 2779 debugchoice[found] = (aliases, table[e])
2761 2780 else:
2762 2781 choice[found] = (aliases, table[e])
2763 2782
2764 2783 if not choice and debugchoice:
2765 2784 choice = debugchoice
2766 2785
2767 2786 return choice
2768 2787
2769 2788 def findcmd(ui, cmd):
2770 2789 """Return (aliases, command table entry) for command string."""
2771 2790 choice = findpossible(ui, cmd)
2772 2791
2773 2792 if choice.has_key(cmd):
2774 2793 return choice[cmd]
2775 2794
2776 2795 if len(choice) > 1:
2777 2796 clist = choice.keys()
2778 2797 clist.sort()
2779 2798 raise AmbiguousCommand(cmd, clist)
2780 2799
2781 2800 if choice:
2782 2801 return choice.values()[0]
2783 2802
2784 2803 raise UnknownCommand(cmd)
2785 2804
2786 2805 def catchterm(*args):
2787 2806 raise util.SignalInterrupt
2788 2807
2789 2808 def run():
2790 2809 sys.exit(dispatch(sys.argv[1:]))
2791 2810
2792 2811 class ParseError(Exception):
2793 2812 """Exception raised on errors in parsing the command line."""
2794 2813
2795 2814 def parse(ui, args):
2796 2815 options = {}
2797 2816 cmdoptions = {}
2798 2817
2799 2818 try:
2800 2819 args = fancyopts.fancyopts(args, globalopts, options)
2801 2820 except fancyopts.getopt.GetoptError, inst:
2802 2821 raise ParseError(None, inst)
2803 2822
2804 2823 if args:
2805 2824 cmd, args = args[0], args[1:]
2806 2825 aliases, i = findcmd(ui, cmd)
2807 2826 cmd = aliases[0]
2808 2827 defaults = ui.config("defaults", cmd)
2809 2828 if defaults:
2810 2829 args = shlex.split(defaults) + args
2811 2830 c = list(i[1])
2812 2831 else:
2813 2832 cmd = None
2814 2833 c = []
2815 2834
2816 2835 # combine global options into local
2817 2836 for o in globalopts:
2818 2837 c.append((o[0], o[1], options[o[1]], o[3]))
2819 2838
2820 2839 try:
2821 2840 args = fancyopts.fancyopts(args, c, cmdoptions)
2822 2841 except fancyopts.getopt.GetoptError, inst:
2823 2842 raise ParseError(cmd, inst)
2824 2843
2825 2844 # separate global options back out
2826 2845 for o in globalopts:
2827 2846 n = o[1]
2828 2847 options[n] = cmdoptions[n]
2829 2848 del cmdoptions[n]
2830 2849
2831 2850 return (cmd, cmd and i[0] or None, args, options, cmdoptions)
2832 2851
2833 2852 external = {}
2834 2853
2835 2854 def findext(name):
2836 2855 '''return module with given extension name'''
2837 2856 try:
2838 2857 return sys.modules[external[name]]
2839 2858 except KeyError:
2840 2859 for k, v in external.iteritems():
2841 2860 if k.endswith('.' + name) or k.endswith('/' + name) or v == name:
2842 2861 return sys.modules[v]
2843 2862 raise KeyError(name)
2844 2863
2845 2864 def load_extensions(ui):
2846 2865 added = []
2847 2866 for ext_name, load_from_name in ui.extensions():
2848 2867 if ext_name in external:
2849 2868 continue
2850 2869 try:
2851 2870 if load_from_name:
2852 2871 # the module will be loaded in sys.modules
2853 2872 # choose an unique name so that it doesn't
2854 2873 # conflicts with other modules
2855 2874 module_name = "hgext_%s" % ext_name.replace('.', '_')
2856 2875 mod = imp.load_source(module_name, load_from_name)
2857 2876 else:
2858 2877 def importh(name):
2859 2878 mod = __import__(name)
2860 2879 components = name.split('.')
2861 2880 for comp in components[1:]:
2862 2881 mod = getattr(mod, comp)
2863 2882 return mod
2864 2883 try:
2865 2884 mod = importh("hgext.%s" % ext_name)
2866 2885 except ImportError:
2867 2886 mod = importh(ext_name)
2868 2887 external[ext_name] = mod.__name__
2869 2888 added.append((mod, ext_name))
2870 2889 except (util.SignalInterrupt, KeyboardInterrupt):
2871 2890 raise
2872 2891 except Exception, inst:
2873 2892 ui.warn(_("*** failed to import extension %s: %s\n") %
2874 2893 (ext_name, inst))
2875 2894 if ui.print_exc():
2876 2895 return 1
2877 2896
2878 2897 for mod, name in added:
2879 2898 uisetup = getattr(mod, 'uisetup', None)
2880 2899 if uisetup:
2881 2900 uisetup(ui)
2882 2901 cmdtable = getattr(mod, 'cmdtable', {})
2883 2902 for t in cmdtable:
2884 2903 if t in table:
2885 2904 ui.warn(_("module %s overrides %s\n") % (name, t))
2886 2905 table.update(cmdtable)
2887 2906
2888 2907 def parseconfig(config):
2889 2908 """parse the --config options from the command line"""
2890 2909 parsed = []
2891 2910 for cfg in config:
2892 2911 try:
2893 2912 name, value = cfg.split('=', 1)
2894 2913 section, name = name.split('.', 1)
2895 2914 if not section or not name:
2896 2915 raise IndexError
2897 2916 parsed.append((section, name, value))
2898 2917 except (IndexError, ValueError):
2899 2918 raise util.Abort(_('malformed --config option: %s') % cfg)
2900 2919 return parsed
2901 2920
2902 2921 def dispatch(args):
2903 2922 for name in 'SIGBREAK', 'SIGHUP', 'SIGTERM':
2904 2923 num = getattr(signal, name, None)
2905 2924 if num: signal.signal(num, catchterm)
2906 2925
2907 2926 try:
2908 2927 u = ui.ui(traceback='--traceback' in sys.argv[1:])
2909 2928 except util.Abort, inst:
2910 2929 sys.stderr.write(_("abort: %s\n") % inst)
2911 2930 return -1
2912 2931
2913 2932 load_extensions(u)
2914 2933 u.addreadhook(load_extensions)
2915 2934
2916 2935 try:
2917 2936 cmd, func, args, options, cmdoptions = parse(u, args)
2918 2937 if options["encoding"]:
2919 2938 util._encoding = options["encoding"]
2920 2939 if options["encodingmode"]:
2921 2940 util._encodingmode = options["encodingmode"]
2922 2941 if options["time"]:
2923 2942 def get_times():
2924 2943 t = os.times()
2925 2944 if t[4] == 0.0: # Windows leaves this as zero, so use time.clock()
2926 2945 t = (t[0], t[1], t[2], t[3], time.clock())
2927 2946 return t
2928 2947 s = get_times()
2929 2948 def print_time():
2930 2949 t = get_times()
2931 2950 u.warn(_("Time: real %.3f secs (user %.3f+%.3f sys %.3f+%.3f)\n") %
2932 2951 (t[4]-s[4], t[0]-s[0], t[2]-s[2], t[1]-s[1], t[3]-s[3]))
2933 2952 atexit.register(print_time)
2934 2953
2935 2954 # enter the debugger before command execution
2936 2955 if options['debugger']:
2937 2956 pdb.set_trace()
2938 2957
2939 2958 try:
2940 2959 if options['cwd']:
2941 2960 try:
2942 2961 os.chdir(options['cwd'])
2943 2962 except OSError, inst:
2944 2963 raise util.Abort('%s: %s' %
2945 2964 (options['cwd'], inst.strerror))
2946 2965
2947 2966 u.updateopts(options["verbose"], options["debug"], options["quiet"],
2948 2967 not options["noninteractive"], options["traceback"],
2949 2968 parseconfig(options["config"]))
2950 2969
2951 2970 path = u.expandpath(options["repository"]) or ""
2952 2971 repo = path and hg.repository(u, path=path) or None
2953 2972 if repo and not repo.local():
2954 2973 raise util.Abort(_("repository '%s' is not local") % path)
2955 2974
2956 2975 if options['help']:
2957 2976 return help_(u, cmd, options['version'])
2958 2977 elif options['version']:
2959 2978 return version_(u)
2960 2979 elif not cmd:
2961 2980 return help_(u, 'shortlist')
2962 2981
2963 2982 if cmd not in norepo.split():
2964 2983 try:
2965 2984 if not repo:
2966 2985 repo = hg.repository(u, path=path)
2967 2986 u = repo.ui
2968 2987 for name in external.itervalues():
2969 2988 mod = sys.modules[name]
2970 2989 if hasattr(mod, 'reposetup'):
2971 2990 mod.reposetup(u, repo)
2972 2991 hg.repo_setup_hooks.append(mod.reposetup)
2973 2992 except hg.RepoError:
2974 2993 if cmd not in optionalrepo.split():
2975 2994 raise
2976 2995 d = lambda: func(u, repo, *args, **cmdoptions)
2977 2996 else:
2978 2997 d = lambda: func(u, *args, **cmdoptions)
2979 2998
2980 2999 try:
2981 3000 if options['profile']:
2982 3001 import hotshot, hotshot.stats
2983 3002 prof = hotshot.Profile("hg.prof")
2984 3003 try:
2985 3004 try:
2986 3005 return prof.runcall(d)
2987 3006 except:
2988 3007 try:
2989 3008 u.warn(_('exception raised - generating '
2990 3009 'profile anyway\n'))
2991 3010 except:
2992 3011 pass
2993 3012 raise
2994 3013 finally:
2995 3014 prof.close()
2996 3015 stats = hotshot.stats.load("hg.prof")
2997 3016 stats.strip_dirs()
2998 3017 stats.sort_stats('time', 'calls')
2999 3018 stats.print_stats(40)
3000 3019 elif options['lsprof']:
3001 3020 try:
3002 3021 from mercurial import lsprof
3003 3022 except ImportError:
3004 3023 raise util.Abort(_(
3005 3024 'lsprof not available - install from '
3006 3025 'http://codespeak.net/svn/user/arigo/hack/misc/lsprof/'))
3007 3026 p = lsprof.Profiler()
3008 3027 p.enable(subcalls=True)
3009 3028 try:
3010 3029 return d()
3011 3030 finally:
3012 3031 p.disable()
3013 3032 stats = lsprof.Stats(p.getstats())
3014 3033 stats.sort()
3015 3034 stats.pprint(top=10, file=sys.stderr, climit=5)
3016 3035 else:
3017 3036 return d()
3018 3037 finally:
3019 3038 u.flush()
3020 3039 except:
3021 3040 # enter the debugger when we hit an exception
3022 3041 if options['debugger']:
3023 3042 pdb.post_mortem(sys.exc_info()[2])
3024 3043 u.print_exc()
3025 3044 raise
3026 3045 except ParseError, inst:
3027 3046 if inst.args[0]:
3028 3047 u.warn(_("hg %s: %s\n") % (inst.args[0], inst.args[1]))
3029 3048 help_(u, inst.args[0])
3030 3049 else:
3031 3050 u.warn(_("hg: %s\n") % inst.args[1])
3032 3051 help_(u, 'shortlist')
3033 3052 except AmbiguousCommand, inst:
3034 3053 u.warn(_("hg: command '%s' is ambiguous:\n %s\n") %
3035 3054 (inst.args[0], " ".join(inst.args[1])))
3036 3055 except UnknownCommand, inst:
3037 3056 u.warn(_("hg: unknown command '%s'\n") % inst.args[0])
3038 3057 help_(u, 'shortlist')
3039 3058 except hg.RepoError, inst:
3040 3059 u.warn(_("abort: %s!\n") % inst)
3041 3060 except lock.LockHeld, inst:
3042 3061 if inst.errno == errno.ETIMEDOUT:
3043 3062 reason = _('timed out waiting for lock held by %s') % inst.locker
3044 3063 else:
3045 3064 reason = _('lock held by %s') % inst.locker
3046 3065 u.warn(_("abort: %s: %s\n") % (inst.desc or inst.filename, reason))
3047 3066 except lock.LockUnavailable, inst:
3048 3067 u.warn(_("abort: could not lock %s: %s\n") %
3049 3068 (inst.desc or inst.filename, inst.strerror))
3050 3069 except revlog.RevlogError, inst:
3051 3070 u.warn(_("abort: %s!\n") % inst)
3052 3071 except util.SignalInterrupt:
3053 3072 u.warn(_("killed!\n"))
3054 3073 except KeyboardInterrupt:
3055 3074 try:
3056 3075 u.warn(_("interrupted!\n"))
3057 3076 except IOError, inst:
3058 3077 if inst.errno == errno.EPIPE:
3059 3078 if u.debugflag:
3060 3079 u.warn(_("\nbroken pipe\n"))
3061 3080 else:
3062 3081 raise
3063 3082 except IOError, inst:
3064 3083 if hasattr(inst, "code"):
3065 3084 u.warn(_("abort: %s\n") % inst)
3066 3085 elif hasattr(inst, "reason"):
3067 3086 u.warn(_("abort: error: %s\n") % inst.reason[1])
3068 3087 elif hasattr(inst, "args") and inst[0] == errno.EPIPE:
3069 3088 if u.debugflag:
3070 3089 u.warn(_("broken pipe\n"))
3071 3090 elif getattr(inst, "strerror", None):
3072 3091 if getattr(inst, "filename", None):
3073 3092 u.warn(_("abort: %s: %s\n") % (inst.strerror, inst.filename))
3074 3093 else:
3075 3094 u.warn(_("abort: %s\n") % inst.strerror)
3076 3095 else:
3077 3096 raise
3078 3097 except OSError, inst:
3079 3098 if getattr(inst, "filename", None):
3080 3099 u.warn(_("abort: %s: %s\n") % (inst.strerror, inst.filename))
3081 3100 else:
3082 3101 u.warn(_("abort: %s\n") % inst.strerror)
3083 3102 except util.UnexpectedOutput, inst:
3084 3103 u.warn(_("abort: %s") % inst[0])
3085 3104 if not isinstance(inst[1], basestring):
3086 3105 u.warn(" %r\n" % (inst[1],))
3087 3106 elif not inst[1]:
3088 3107 u.warn(_(" empty string\n"))
3089 3108 else:
3090 3109 u.warn("\n%r\n" % util.ellipsis(inst[1]))
3091 3110 except util.Abort, inst:
3092 3111 u.warn(_("abort: %s\n") % inst)
3093 3112 except TypeError, inst:
3094 3113 # was this an argument error?
3095 3114 tb = traceback.extract_tb(sys.exc_info()[2])
3096 3115 if len(tb) > 2: # no
3097 3116 raise
3098 3117 u.debug(inst, "\n")
3099 3118 u.warn(_("%s: invalid arguments\n") % cmd)
3100 3119 help_(u, cmd)
3101 3120 except SystemExit, inst:
3102 3121 # Commands shouldn't sys.exit directly, but give a return code.
3103 3122 # Just in case catch this and and pass exit code to caller.
3104 3123 return inst.code
3105 3124 except:
3106 3125 u.warn(_("** unknown exception encountered, details follow\n"))
3107 3126 u.warn(_("** report bug details to "
3108 3127 "http://www.selenic.com/mercurial/bts\n"))
3109 3128 u.warn(_("** or mercurial@selenic.com\n"))
3110 3129 u.warn(_("** Mercurial Distributed SCM (version %s)\n")
3111 3130 % version.get_version())
3112 3131 raise
3113 3132
3114 3133 return -1
@@ -1,1218 +1,1284
1 1 """
2 2 util.py - Mercurial utility functions and platform specfic implementations
3 3
4 4 Copyright 2005 K. Thananchayan <thananck@yahoo.com>
5 5 Copyright 2005, 2006 Matt Mackall <mpm@selenic.com>
6 6 Copyright 2006 Vadim Gelfer <vadim.gelfer@gmail.com>
7 7
8 8 This software may be used and distributed according to the terms
9 9 of the GNU General Public License, incorporated herein by reference.
10 10
11 11 This contains helper routines that are independent of the SCM core and hide
12 12 platform-specific details from the core.
13 13 """
14 14
15 15 from i18n import gettext as _
16 16 from demandload import *
17 17 demandload(globals(), "cStringIO errno getpass popen2 re shutil sys tempfile")
18 18 demandload(globals(), "os threading time calendar ConfigParser locale")
19 19
20 20 _encoding = os.environ.get("HGENCODING") or locale.getpreferredencoding()
21 21 _encodingmode = os.environ.get("HGENCODINGMODE", "strict")
22 22
23 23 def tolocal(s):
24 24 """
25 25 Convert a string from internal UTF-8 to local encoding
26 26
27 27 All internal strings should be UTF-8 but some repos before the
28 28 implementation of locale support may contain latin1 or possibly
29 29 other character sets. We attempt to decode everything strictly
30 30 using UTF-8, then Latin-1, and failing that, we use UTF-8 and
31 31 replace unknown characters.
32 32 """
33 33 for e in "utf-8 latin1".split():
34 34 try:
35 35 u = s.decode(e) # attempt strict decoding
36 36 return u.encode(_encoding, "replace")
37 37 except UnicodeDecodeError:
38 38 pass
39 39 u = s.decode("utf-8", "replace") # last ditch
40 40 return u.encode(_encoding, "replace")
41 41
42 42 def fromlocal(s):
43 43 """
44 44 Convert a string from the local character encoding to UTF-8
45 45
46 46 We attempt to decode strings using the encoding mode set by
47 47 HG_ENCODINGMODE, which defaults to 'strict'. In this mode, unknown
48 48 characters will cause an error message. Other modes include
49 49 'replace', which replaces unknown characters with a special
50 50 Unicode character, and 'ignore', which drops the character.
51 51 """
52 52 try:
53 53 return s.decode(_encoding, _encodingmode).encode("utf-8")
54 54 except UnicodeDecodeError, inst:
55 55 sub = s[max(0, inst.start-10):inst.start+10]
56 56 raise Abort("decoding near '%s': %s!\n" % (sub, inst))
57 57
58 58 def locallen(s):
59 59 """Find the length in characters of a local string"""
60 60 return len(s.decode(_encoding, "replace"))
61 61
62 62 def localsub(s, a, b=None):
63 63 try:
64 64 u = s.decode(_encoding, _encodingmode)
65 65 if b is not None:
66 66 u = u[a:b]
67 67 else:
68 68 u = u[:a]
69 69 return u.encode(_encoding, _encodingmode)
70 70 except UnicodeDecodeError, inst:
71 71 sub = s[max(0, inst.start-10), inst.start+10]
72 72 raise Abort("decoding near '%s': %s!\n" % (sub, inst))
73 73
74 74 # used by parsedate
75 75 defaultdateformats = (
76 76 '%Y-%m-%d %H:%M:%S',
77 77 '%Y-%m-%d %I:%M:%S%p',
78 78 '%Y-%m-%d %H:%M',
79 79 '%Y-%m-%d %I:%M%p',
80 80 '%Y-%m-%d',
81 81 '%m-%d',
82 82 '%m/%d',
83 83 '%m/%d/%y',
84 84 '%m/%d/%Y',
85 85 '%a %b %d %H:%M:%S %Y',
86 86 '%a %b %d %I:%M:%S%p %Y',
87 87 '%b %d %H:%M:%S %Y',
88 '%b %d %I:%M:%S%p %Y',
89 '%b %d %H:%M:%S',
88 90 '%b %d %I:%M:%S%p',
89 91 '%b %d %H:%M',
90 92 '%b %d %I:%M%p',
91 93 '%b %d %Y',
92 94 '%b %d',
93 95 '%H:%M:%S',
94 96 '%I:%M:%SP',
95 97 '%H:%M',
96 98 '%I:%M%p',
97 99 )
98 100
101 extendeddateformats = defaultdateformats + (
102 "%Y",
103 "%Y-%m",
104 "%b",
105 "%b %Y",
106 )
107
99 108 class SignalInterrupt(Exception):
100 109 """Exception raised on SIGTERM and SIGHUP."""
101 110
102 111 # like SafeConfigParser but with case-sensitive keys
103 112 class configparser(ConfigParser.SafeConfigParser):
104 113 def optionxform(self, optionstr):
105 114 return optionstr
106 115
107 116 def cachefunc(func):
108 117 '''cache the result of function calls'''
109 118 # XXX doesn't handle keywords args
110 119 cache = {}
111 120 if func.func_code.co_argcount == 1:
112 121 # we gain a small amount of time because
113 122 # we don't need to pack/unpack the list
114 123 def f(arg):
115 124 if arg not in cache:
116 125 cache[arg] = func(arg)
117 126 return cache[arg]
118 127 else:
119 128 def f(*args):
120 129 if args not in cache:
121 130 cache[args] = func(*args)
122 131 return cache[args]
123 132
124 133 return f
125 134
126 135 def pipefilter(s, cmd):
127 136 '''filter string S through command CMD, returning its output'''
128 137 (pout, pin) = popen2.popen2(cmd, -1, 'b')
129 138 def writer():
130 139 try:
131 140 pin.write(s)
132 141 pin.close()
133 142 except IOError, inst:
134 143 if inst.errno != errno.EPIPE:
135 144 raise
136 145
137 146 # we should use select instead on UNIX, but this will work on most
138 147 # systems, including Windows
139 148 w = threading.Thread(target=writer)
140 149 w.start()
141 150 f = pout.read()
142 151 pout.close()
143 152 w.join()
144 153 return f
145 154
146 155 def tempfilter(s, cmd):
147 156 '''filter string S through a pair of temporary files with CMD.
148 157 CMD is used as a template to create the real command to be run,
149 158 with the strings INFILE and OUTFILE replaced by the real names of
150 159 the temporary files generated.'''
151 160 inname, outname = None, None
152 161 try:
153 162 infd, inname = tempfile.mkstemp(prefix='hg-filter-in-')
154 163 fp = os.fdopen(infd, 'wb')
155 164 fp.write(s)
156 165 fp.close()
157 166 outfd, outname = tempfile.mkstemp(prefix='hg-filter-out-')
158 167 os.close(outfd)
159 168 cmd = cmd.replace('INFILE', inname)
160 169 cmd = cmd.replace('OUTFILE', outname)
161 170 code = os.system(cmd)
162 171 if code: raise Abort(_("command '%s' failed: %s") %
163 172 (cmd, explain_exit(code)))
164 173 return open(outname, 'rb').read()
165 174 finally:
166 175 try:
167 176 if inname: os.unlink(inname)
168 177 except: pass
169 178 try:
170 179 if outname: os.unlink(outname)
171 180 except: pass
172 181
173 182 filtertable = {
174 183 'tempfile:': tempfilter,
175 184 'pipe:': pipefilter,
176 185 }
177 186
178 187 def filter(s, cmd):
179 188 "filter a string through a command that transforms its input to its output"
180 189 for name, fn in filtertable.iteritems():
181 190 if cmd.startswith(name):
182 191 return fn(s, cmd[len(name):].lstrip())
183 192 return pipefilter(s, cmd)
184 193
185 194 def find_in_path(name, path, default=None):
186 195 '''find name in search path. path can be string (will be split
187 196 with os.pathsep), or iterable thing that returns strings. if name
188 197 found, return path to name. else return default.'''
189 198 if isinstance(path, str):
190 199 path = path.split(os.pathsep)
191 200 for p in path:
192 201 p_name = os.path.join(p, name)
193 202 if os.path.exists(p_name):
194 203 return p_name
195 204 return default
196 205
197 206 def binary(s):
198 207 """return true if a string is binary data using diff's heuristic"""
199 208 if s and '\0' in s[:4096]:
200 209 return True
201 210 return False
202 211
203 212 def unique(g):
204 213 """return the uniq elements of iterable g"""
205 214 seen = {}
206 215 l = []
207 216 for f in g:
208 217 if f not in seen:
209 218 seen[f] = 1
210 219 l.append(f)
211 220 return l
212 221
213 222 class Abort(Exception):
214 223 """Raised if a command needs to print an error and exit."""
215 224
216 225 class UnexpectedOutput(Abort):
217 226 """Raised to print an error with part of output and exit."""
218 227
219 228 def always(fn): return True
220 229 def never(fn): return False
221 230
222 231 def patkind(name, dflt_pat='glob'):
223 232 """Split a string into an optional pattern kind prefix and the
224 233 actual pattern."""
225 234 for prefix in 're', 'glob', 'path', 'relglob', 'relpath', 'relre':
226 235 if name.startswith(prefix + ':'): return name.split(':', 1)
227 236 return dflt_pat, name
228 237
229 238 def globre(pat, head='^', tail='$'):
230 239 "convert a glob pattern into a regexp"
231 240 i, n = 0, len(pat)
232 241 res = ''
233 242 group = False
234 243 def peek(): return i < n and pat[i]
235 244 while i < n:
236 245 c = pat[i]
237 246 i = i+1
238 247 if c == '*':
239 248 if peek() == '*':
240 249 i += 1
241 250 res += '.*'
242 251 else:
243 252 res += '[^/]*'
244 253 elif c == '?':
245 254 res += '.'
246 255 elif c == '[':
247 256 j = i
248 257 if j < n and pat[j] in '!]':
249 258 j += 1
250 259 while j < n and pat[j] != ']':
251 260 j += 1
252 261 if j >= n:
253 262 res += '\\['
254 263 else:
255 264 stuff = pat[i:j].replace('\\','\\\\')
256 265 i = j + 1
257 266 if stuff[0] == '!':
258 267 stuff = '^' + stuff[1:]
259 268 elif stuff[0] == '^':
260 269 stuff = '\\' + stuff
261 270 res = '%s[%s]' % (res, stuff)
262 271 elif c == '{':
263 272 group = True
264 273 res += '(?:'
265 274 elif c == '}' and group:
266 275 res += ')'
267 276 group = False
268 277 elif c == ',' and group:
269 278 res += '|'
270 279 elif c == '\\':
271 280 p = peek()
272 281 if p:
273 282 i += 1
274 283 res += re.escape(p)
275 284 else:
276 285 res += re.escape(c)
277 286 else:
278 287 res += re.escape(c)
279 288 return head + res + tail
280 289
281 290 _globchars = {'[': 1, '{': 1, '*': 1, '?': 1}
282 291
283 292 def pathto(n1, n2):
284 293 '''return the relative path from one place to another.
285 294 n1 should use os.sep to separate directories
286 295 n2 should use "/" to separate directories
287 296 returns an os.sep-separated path.
288 297 '''
289 298 if not n1: return localpath(n2)
290 299 a, b = n1.split(os.sep), n2.split('/')
291 300 a.reverse()
292 301 b.reverse()
293 302 while a and b and a[-1] == b[-1]:
294 303 a.pop()
295 304 b.pop()
296 305 b.reverse()
297 306 return os.sep.join((['..'] * len(a)) + b)
298 307
299 308 def canonpath(root, cwd, myname):
300 309 """return the canonical path of myname, given cwd and root"""
301 310 if root == os.sep:
302 311 rootsep = os.sep
303 312 elif root.endswith(os.sep):
304 313 rootsep = root
305 314 else:
306 315 rootsep = root + os.sep
307 316 name = myname
308 317 if not os.path.isabs(name):
309 318 name = os.path.join(root, cwd, name)
310 319 name = os.path.normpath(name)
311 320 if name != rootsep and name.startswith(rootsep):
312 321 name = name[len(rootsep):]
313 322 audit_path(name)
314 323 return pconvert(name)
315 324 elif name == root:
316 325 return ''
317 326 else:
318 327 # Determine whether `name' is in the hierarchy at or beneath `root',
319 328 # by iterating name=dirname(name) until that causes no change (can't
320 329 # check name == '/', because that doesn't work on windows). For each
321 330 # `name', compare dev/inode numbers. If they match, the list `rel'
322 331 # holds the reversed list of components making up the relative file
323 332 # name we want.
324 333 root_st = os.stat(root)
325 334 rel = []
326 335 while True:
327 336 try:
328 337 name_st = os.stat(name)
329 338 except OSError:
330 339 break
331 340 if samestat(name_st, root_st):
332 341 rel.reverse()
333 342 name = os.path.join(*rel)
334 343 audit_path(name)
335 344 return pconvert(name)
336 345 dirname, basename = os.path.split(name)
337 346 rel.append(basename)
338 347 if dirname == name:
339 348 break
340 349 name = dirname
341 350
342 351 raise Abort('%s not under root' % myname)
343 352
344 353 def matcher(canonroot, cwd='', names=['.'], inc=[], exc=[], head='', src=None):
345 354 return _matcher(canonroot, cwd, names, inc, exc, head, 'glob', src)
346 355
347 356 def cmdmatcher(canonroot, cwd='', names=['.'], inc=[], exc=[], head='', src=None):
348 357 if os.name == 'nt':
349 358 dflt_pat = 'glob'
350 359 else:
351 360 dflt_pat = 'relpath'
352 361 return _matcher(canonroot, cwd, names, inc, exc, head, dflt_pat, src)
353 362
354 363 def _matcher(canonroot, cwd, names, inc, exc, head, dflt_pat, src):
355 364 """build a function to match a set of file patterns
356 365
357 366 arguments:
358 367 canonroot - the canonical root of the tree you're matching against
359 368 cwd - the current working directory, if relevant
360 369 names - patterns to find
361 370 inc - patterns to include
362 371 exc - patterns to exclude
363 372 head - a regex to prepend to patterns to control whether a match is rooted
364 373
365 374 a pattern is one of:
366 375 'glob:<rooted glob>'
367 376 're:<rooted regexp>'
368 377 'path:<rooted path>'
369 378 'relglob:<relative glob>'
370 379 'relpath:<relative path>'
371 380 'relre:<relative regexp>'
372 381 '<rooted path or regexp>'
373 382
374 383 returns:
375 384 a 3-tuple containing
376 385 - list of explicit non-pattern names passed in
377 386 - a bool match(filename) function
378 387 - a bool indicating if any patterns were passed in
379 388
380 389 todo:
381 390 make head regex a rooted bool
382 391 """
383 392
384 393 def contains_glob(name):
385 394 for c in name:
386 395 if c in _globchars: return True
387 396 return False
388 397
389 398 def regex(kind, name, tail):
390 399 '''convert a pattern into a regular expression'''
391 400 if kind == 're':
392 401 return name
393 402 elif kind == 'path':
394 403 return '^' + re.escape(name) + '(?:/|$)'
395 404 elif kind == 'relglob':
396 405 return head + globre(name, '(?:|.*/)', tail)
397 406 elif kind == 'relpath':
398 407 return head + re.escape(name) + tail
399 408 elif kind == 'relre':
400 409 if name.startswith('^'):
401 410 return name
402 411 return '.*' + name
403 412 return head + globre(name, '', tail)
404 413
405 414 def matchfn(pats, tail):
406 415 """build a matching function from a set of patterns"""
407 416 if not pats:
408 417 return
409 418 matches = []
410 419 for k, p in pats:
411 420 try:
412 421 pat = '(?:%s)' % regex(k, p, tail)
413 422 matches.append(re.compile(pat).match)
414 423 except re.error:
415 424 if src: raise Abort("%s: invalid pattern (%s): %s" % (src, k, p))
416 425 else: raise Abort("invalid pattern (%s): %s" % (k, p))
417 426
418 427 def buildfn(text):
419 428 for m in matches:
420 429 r = m(text)
421 430 if r:
422 431 return r
423 432
424 433 return buildfn
425 434
426 435 def globprefix(pat):
427 436 '''return the non-glob prefix of a path, e.g. foo/* -> foo'''
428 437 root = []
429 438 for p in pat.split(os.sep):
430 439 if contains_glob(p): break
431 440 root.append(p)
432 441 return '/'.join(root)
433 442
434 443 pats = []
435 444 files = []
436 445 roots = []
437 446 for kind, name in [patkind(p, dflt_pat) for p in names]:
438 447 if kind in ('glob', 'relpath'):
439 448 name = canonpath(canonroot, cwd, name)
440 449 if name == '':
441 450 kind, name = 'glob', '**'
442 451 if kind in ('glob', 'path', 're'):
443 452 pats.append((kind, name))
444 453 if kind == 'glob':
445 454 root = globprefix(name)
446 455 if root: roots.append(root)
447 456 elif kind == 'relpath':
448 457 files.append((kind, name))
449 458 roots.append(name)
450 459
451 460 patmatch = matchfn(pats, '$') or always
452 461 filematch = matchfn(files, '(?:/|$)') or always
453 462 incmatch = always
454 463 if inc:
455 464 inckinds = [patkind(canonpath(canonroot, cwd, i)) for i in inc]
456 465 incmatch = matchfn(inckinds, '(?:/|$)')
457 466 excmatch = lambda fn: False
458 467 if exc:
459 468 exckinds = [patkind(canonpath(canonroot, cwd, x)) for x in exc]
460 469 excmatch = matchfn(exckinds, '(?:/|$)')
461 470
462 471 return (roots,
463 472 lambda fn: (incmatch(fn) and not excmatch(fn) and
464 473 (fn.endswith('/') or
465 474 (not pats and not files) or
466 475 (pats and patmatch(fn)) or
467 476 (files and filematch(fn)))),
468 477 (inc or exc or (pats and pats != [('glob', '**')])) and True)
469 478
470 479 def system(cmd, environ={}, cwd=None, onerr=None, errprefix=None):
471 480 '''enhanced shell command execution.
472 481 run with environment maybe modified, maybe in different dir.
473 482
474 483 if command fails and onerr is None, return status. if ui object,
475 484 print error message and return status, else raise onerr object as
476 485 exception.'''
477 486 def py2shell(val):
478 487 'convert python object into string that is useful to shell'
479 488 if val in (None, False):
480 489 return '0'
481 490 if val == True:
482 491 return '1'
483 492 return str(val)
484 493 oldenv = {}
485 494 for k in environ:
486 495 oldenv[k] = os.environ.get(k)
487 496 if cwd is not None:
488 497 oldcwd = os.getcwd()
489 498 try:
490 499 for k, v in environ.iteritems():
491 500 os.environ[k] = py2shell(v)
492 501 if cwd is not None and oldcwd != cwd:
493 502 os.chdir(cwd)
494 503 rc = os.system(cmd)
495 504 if rc and onerr:
496 505 errmsg = '%s %s' % (os.path.basename(cmd.split(None, 1)[0]),
497 506 explain_exit(rc)[0])
498 507 if errprefix:
499 508 errmsg = '%s: %s' % (errprefix, errmsg)
500 509 try:
501 510 onerr.warn(errmsg + '\n')
502 511 except AttributeError:
503 512 raise onerr(errmsg)
504 513 return rc
505 514 finally:
506 515 for k, v in oldenv.iteritems():
507 516 if v is None:
508 517 del os.environ[k]
509 518 else:
510 519 os.environ[k] = v
511 520 if cwd is not None and oldcwd != cwd:
512 521 os.chdir(oldcwd)
513 522
514 523 def rename(src, dst):
515 524 """forcibly rename a file"""
516 525 try:
517 526 os.rename(src, dst)
518 527 except OSError, err:
519 528 # on windows, rename to existing file is not allowed, so we
520 529 # must delete destination first. but if file is open, unlink
521 530 # schedules it for delete but does not delete it. rename
522 531 # happens immediately even for open files, so we create
523 532 # temporary file, delete it, rename destination to that name,
524 533 # then delete that. then rename is safe to do.
525 534 fd, temp = tempfile.mkstemp(dir=os.path.dirname(dst) or '.')
526 535 os.close(fd)
527 536 os.unlink(temp)
528 537 os.rename(dst, temp)
529 538 os.unlink(temp)
530 539 os.rename(src, dst)
531 540
532 541 def unlink(f):
533 542 """unlink and remove the directory if it is empty"""
534 543 os.unlink(f)
535 544 # try removing directories that might now be empty
536 545 try:
537 546 os.removedirs(os.path.dirname(f))
538 547 except OSError:
539 548 pass
540 549
541 550 def copyfile(src, dest):
542 551 "copy a file, preserving mode"
543 552 try:
544 553 shutil.copyfile(src, dest)
545 554 shutil.copymode(src, dest)
546 555 except shutil.Error, inst:
547 556 raise util.Abort(str(inst))
548 557
549 558 def copyfiles(src, dst, hardlink=None):
550 559 """Copy a directory tree using hardlinks if possible"""
551 560
552 561 if hardlink is None:
553 562 hardlink = (os.stat(src).st_dev ==
554 563 os.stat(os.path.dirname(dst)).st_dev)
555 564
556 565 if os.path.isdir(src):
557 566 os.mkdir(dst)
558 567 for name in os.listdir(src):
559 568 srcname = os.path.join(src, name)
560 569 dstname = os.path.join(dst, name)
561 570 copyfiles(srcname, dstname, hardlink)
562 571 else:
563 572 if hardlink:
564 573 try:
565 574 os_link(src, dst)
566 575 except (IOError, OSError):
567 576 hardlink = False
568 577 shutil.copy(src, dst)
569 578 else:
570 579 shutil.copy(src, dst)
571 580
572 581 def audit_path(path):
573 582 """Abort if path contains dangerous components"""
574 583 parts = os.path.normcase(path).split(os.sep)
575 584 if (os.path.splitdrive(path)[0] or parts[0] in ('.hg', '')
576 585 or os.pardir in parts):
577 586 raise Abort(_("path contains illegal component: %s\n") % path)
578 587
579 588 def _makelock_file(info, pathname):
580 589 ld = os.open(pathname, os.O_CREAT | os.O_WRONLY | os.O_EXCL)
581 590 os.write(ld, info)
582 591 os.close(ld)
583 592
584 593 def _readlock_file(pathname):
585 594 return posixfile(pathname).read()
586 595
587 596 def nlinks(pathname):
588 597 """Return number of hardlinks for the given file."""
589 598 return os.lstat(pathname).st_nlink
590 599
591 600 if hasattr(os, 'link'):
592 601 os_link = os.link
593 602 else:
594 603 def os_link(src, dst):
595 604 raise OSError(0, _("Hardlinks not supported"))
596 605
597 606 def fstat(fp):
598 607 '''stat file object that may not have fileno method.'''
599 608 try:
600 609 return os.fstat(fp.fileno())
601 610 except AttributeError:
602 611 return os.stat(fp.name)
603 612
604 613 posixfile = file
605 614
606 615 def is_win_9x():
607 616 '''return true if run on windows 95, 98 or me.'''
608 617 try:
609 618 return sys.getwindowsversion()[3] == 1
610 619 except AttributeError:
611 620 return os.name == 'nt' and 'command' in os.environ.get('comspec', '')
612 621
613 622 getuser_fallback = None
614 623
615 624 def getuser():
616 625 '''return name of current user'''
617 626 try:
618 627 return getpass.getuser()
619 628 except ImportError:
620 629 # import of pwd will fail on windows - try fallback
621 630 if getuser_fallback:
622 631 return getuser_fallback()
623 632 # raised if win32api not available
624 633 raise Abort(_('user name not available - set USERNAME '
625 634 'environment variable'))
626 635
627 636 def username(uid=None):
628 637 """Return the name of the user with the given uid.
629 638
630 639 If uid is None, return the name of the current user."""
631 640 try:
632 641 import pwd
633 642 if uid is None:
634 643 uid = os.getuid()
635 644 try:
636 645 return pwd.getpwuid(uid)[0]
637 646 except KeyError:
638 647 return str(uid)
639 648 except ImportError:
640 649 return None
641 650
642 651 def groupname(gid=None):
643 652 """Return the name of the group with the given gid.
644 653
645 654 If gid is None, return the name of the current group."""
646 655 try:
647 656 import grp
648 657 if gid is None:
649 658 gid = os.getgid()
650 659 try:
651 660 return grp.getgrgid(gid)[0]
652 661 except KeyError:
653 662 return str(gid)
654 663 except ImportError:
655 664 return None
656 665
657 666 # File system features
658 667
659 668 def checkfolding(path):
660 669 """
661 670 Check whether the given path is on a case-sensitive filesystem
662 671
663 672 Requires a path (like /foo/.hg) ending with a foldable final
664 673 directory component.
665 674 """
666 675 s1 = os.stat(path)
667 676 d, b = os.path.split(path)
668 677 p2 = os.path.join(d, b.upper())
669 678 if path == p2:
670 679 p2 = os.path.join(d, b.lower())
671 680 try:
672 681 s2 = os.stat(p2)
673 682 if s2 == s1:
674 683 return False
675 684 return True
676 685 except:
677 686 return True
678 687
679 688 # Platform specific variants
680 689 if os.name == 'nt':
681 690 demandload(globals(), "msvcrt")
682 691 nulldev = 'NUL:'
683 692
684 693 class winstdout:
685 694 '''stdout on windows misbehaves if sent through a pipe'''
686 695
687 696 def __init__(self, fp):
688 697 self.fp = fp
689 698
690 699 def __getattr__(self, key):
691 700 return getattr(self.fp, key)
692 701
693 702 def close(self):
694 703 try:
695 704 self.fp.close()
696 705 except: pass
697 706
698 707 def write(self, s):
699 708 try:
700 709 return self.fp.write(s)
701 710 except IOError, inst:
702 711 if inst.errno != 0: raise
703 712 self.close()
704 713 raise IOError(errno.EPIPE, 'Broken pipe')
705 714
706 715 sys.stdout = winstdout(sys.stdout)
707 716
708 717 def system_rcpath():
709 718 try:
710 719 return system_rcpath_win32()
711 720 except:
712 721 return [r'c:\mercurial\mercurial.ini']
713 722
714 723 def os_rcpath():
715 724 '''return default os-specific hgrc search path'''
716 725 path = system_rcpath()
717 726 path.append(user_rcpath())
718 727 userprofile = os.environ.get('USERPROFILE')
719 728 if userprofile:
720 729 path.append(os.path.join(userprofile, 'mercurial.ini'))
721 730 return path
722 731
723 732 def user_rcpath():
724 733 '''return os-specific hgrc search path to the user dir'''
725 734 return os.path.join(os.path.expanduser('~'), 'mercurial.ini')
726 735
727 736 def parse_patch_output(output_line):
728 737 """parses the output produced by patch and returns the file name"""
729 738 pf = output_line[14:]
730 739 if pf[0] == '`':
731 740 pf = pf[1:-1] # Remove the quotes
732 741 return pf
733 742
734 743 def testpid(pid):
735 744 '''return False if pid dead, True if running or not known'''
736 745 return True
737 746
738 747 def is_exec(f, last):
739 748 return last
740 749
741 750 def set_exec(f, mode):
742 751 pass
743 752
744 753 def set_binary(fd):
745 754 msvcrt.setmode(fd.fileno(), os.O_BINARY)
746 755
747 756 def pconvert(path):
748 757 return path.replace("\\", "/")
749 758
750 759 def localpath(path):
751 760 return path.replace('/', '\\')
752 761
753 762 def normpath(path):
754 763 return pconvert(os.path.normpath(path))
755 764
756 765 makelock = _makelock_file
757 766 readlock = _readlock_file
758 767
759 768 def samestat(s1, s2):
760 769 return False
761 770
762 771 def shellquote(s):
763 772 return '"%s"' % s.replace('"', '\\"')
764 773
765 774 def explain_exit(code):
766 775 return _("exited with status %d") % code, code
767 776
768 777 # if you change this stub into a real check, please try to implement the
769 778 # username and groupname functions above, too.
770 779 def isowner(fp, st=None):
771 780 return True
772 781
773 782 try:
774 783 # override functions with win32 versions if possible
775 784 from util_win32 import *
776 785 if not is_win_9x():
777 786 posixfile = posixfile_nt
778 787 except ImportError:
779 788 pass
780 789
781 790 else:
782 791 nulldev = '/dev/null'
783 792
784 793 def rcfiles(path):
785 794 rcs = [os.path.join(path, 'hgrc')]
786 795 rcdir = os.path.join(path, 'hgrc.d')
787 796 try:
788 797 rcs.extend([os.path.join(rcdir, f) for f in os.listdir(rcdir)
789 798 if f.endswith(".rc")])
790 799 except OSError:
791 800 pass
792 801 return rcs
793 802
794 803 def os_rcpath():
795 804 '''return default os-specific hgrc search path'''
796 805 path = []
797 806 # old mod_python does not set sys.argv
798 807 if len(getattr(sys, 'argv', [])) > 0:
799 808 path.extend(rcfiles(os.path.dirname(sys.argv[0]) +
800 809 '/../etc/mercurial'))
801 810 path.extend(rcfiles('/etc/mercurial'))
802 811 path.append(os.path.expanduser('~/.hgrc'))
803 812 path = [os.path.normpath(f) for f in path]
804 813 return path
805 814
806 815 def parse_patch_output(output_line):
807 816 """parses the output produced by patch and returns the file name"""
808 817 pf = output_line[14:]
809 818 if pf.startswith("'") and pf.endswith("'") and " " in pf:
810 819 pf = pf[1:-1] # Remove the quotes
811 820 return pf
812 821
813 822 def is_exec(f, last):
814 823 """check whether a file is executable"""
815 824 return (os.lstat(f).st_mode & 0100 != 0)
816 825
817 826 def set_exec(f, mode):
818 827 s = os.lstat(f).st_mode
819 828 if (s & 0100 != 0) == mode:
820 829 return
821 830 if mode:
822 831 # Turn on +x for every +r bit when making a file executable
823 832 # and obey umask.
824 833 umask = os.umask(0)
825 834 os.umask(umask)
826 835 os.chmod(f, s | (s & 0444) >> 2 & ~umask)
827 836 else:
828 837 os.chmod(f, s & 0666)
829 838
830 839 def set_binary(fd):
831 840 pass
832 841
833 842 def pconvert(path):
834 843 return path
835 844
836 845 def localpath(path):
837 846 return path
838 847
839 848 normpath = os.path.normpath
840 849 samestat = os.path.samestat
841 850
842 851 def makelock(info, pathname):
843 852 try:
844 853 os.symlink(info, pathname)
845 854 except OSError, why:
846 855 if why.errno == errno.EEXIST:
847 856 raise
848 857 else:
849 858 _makelock_file(info, pathname)
850 859
851 860 def readlock(pathname):
852 861 try:
853 862 return os.readlink(pathname)
854 863 except OSError, why:
855 864 if why.errno == errno.EINVAL:
856 865 return _readlock_file(pathname)
857 866 else:
858 867 raise
859 868
860 869 def shellquote(s):
861 870 return "'%s'" % s.replace("'", "'\\''")
862 871
863 872 def testpid(pid):
864 873 '''return False if pid dead, True if running or not sure'''
865 874 try:
866 875 os.kill(pid, 0)
867 876 return True
868 877 except OSError, inst:
869 878 return inst.errno != errno.ESRCH
870 879
871 880 def explain_exit(code):
872 881 """return a 2-tuple (desc, code) describing a process's status"""
873 882 if os.WIFEXITED(code):
874 883 val = os.WEXITSTATUS(code)
875 884 return _("exited with status %d") % val, val
876 885 elif os.WIFSIGNALED(code):
877 886 val = os.WTERMSIG(code)
878 887 return _("killed by signal %d") % val, val
879 888 elif os.WIFSTOPPED(code):
880 889 val = os.WSTOPSIG(code)
881 890 return _("stopped by signal %d") % val, val
882 891 raise ValueError(_("invalid exit code"))
883 892
884 893 def isowner(fp, st=None):
885 894 """Return True if the file object f belongs to the current user.
886 895
887 896 The return value of a util.fstat(f) may be passed as the st argument.
888 897 """
889 898 if st is None:
890 899 st = fstat(f)
891 900 return st.st_uid == os.getuid()
892 901
893 902
894 903 def opener(base, audit=True):
895 904 """
896 905 return a function that opens files relative to base
897 906
898 907 this function is used to hide the details of COW semantics and
899 908 remote file access from higher level code.
900 909 """
901 910 p = base
902 911 audit_p = audit
903 912
904 913 def mktempcopy(name):
905 914 d, fn = os.path.split(name)
906 915 fd, temp = tempfile.mkstemp(prefix='.%s-' % fn, dir=d)
907 916 os.close(fd)
908 917 ofp = posixfile(temp, "wb")
909 918 try:
910 919 try:
911 920 ifp = posixfile(name, "rb")
912 921 except IOError, inst:
913 922 if not getattr(inst, 'filename', None):
914 923 inst.filename = name
915 924 raise
916 925 for chunk in filechunkiter(ifp):
917 926 ofp.write(chunk)
918 927 ifp.close()
919 928 ofp.close()
920 929 except:
921 930 try: os.unlink(temp)
922 931 except: pass
923 932 raise
924 933 st = os.lstat(name)
925 934 os.chmod(temp, st.st_mode)
926 935 return temp
927 936
928 937 class atomictempfile(posixfile):
929 938 """the file will only be copied when rename is called"""
930 939 def __init__(self, name, mode):
931 940 self.__name = name
932 941 self.temp = mktempcopy(name)
933 942 posixfile.__init__(self, self.temp, mode)
934 943 def rename(self):
935 944 if not self.closed:
936 945 posixfile.close(self)
937 946 rename(self.temp, localpath(self.__name))
938 947 def __del__(self):
939 948 if not self.closed:
940 949 try:
941 950 os.unlink(self.temp)
942 951 except: pass
943 952 posixfile.close(self)
944 953
945 954 class atomicfile(atomictempfile):
946 955 """the file will only be copied on close"""
947 956 def __init__(self, name, mode):
948 957 atomictempfile.__init__(self, name, mode)
949 958 def close(self):
950 959 self.rename()
951 960 def __del__(self):
952 961 self.rename()
953 962
954 963 def o(path, mode="r", text=False, atomic=False, atomictemp=False):
955 964 if audit_p:
956 965 audit_path(path)
957 966 f = os.path.join(p, path)
958 967
959 968 if not text:
960 969 mode += "b" # for that other OS
961 970
962 971 if mode[0] != "r":
963 972 try:
964 973 nlink = nlinks(f)
965 974 except OSError:
966 975 d = os.path.dirname(f)
967 976 if not os.path.isdir(d):
968 977 os.makedirs(d)
969 978 else:
970 979 if atomic:
971 980 return atomicfile(f, mode)
972 981 elif atomictemp:
973 982 return atomictempfile(f, mode)
974 983 if nlink > 1:
975 984 rename(mktempcopy(f), f)
976 985 return posixfile(f, mode)
977 986
978 987 return o
979 988
980 989 class chunkbuffer(object):
981 990 """Allow arbitrary sized chunks of data to be efficiently read from an
982 991 iterator over chunks of arbitrary size."""
983 992
984 993 def __init__(self, in_iter, targetsize = 2**16):
985 994 """in_iter is the iterator that's iterating over the input chunks.
986 995 targetsize is how big a buffer to try to maintain."""
987 996 self.in_iter = iter(in_iter)
988 997 self.buf = ''
989 998 self.targetsize = int(targetsize)
990 999 if self.targetsize <= 0:
991 1000 raise ValueError(_("targetsize must be greater than 0, was %d") %
992 1001 targetsize)
993 1002 self.iterempty = False
994 1003
995 1004 def fillbuf(self):
996 1005 """Ignore target size; read every chunk from iterator until empty."""
997 1006 if not self.iterempty:
998 1007 collector = cStringIO.StringIO()
999 1008 collector.write(self.buf)
1000 1009 for ch in self.in_iter:
1001 1010 collector.write(ch)
1002 1011 self.buf = collector.getvalue()
1003 1012 self.iterempty = True
1004 1013
1005 1014 def read(self, l):
1006 1015 """Read L bytes of data from the iterator of chunks of data.
1007 1016 Returns less than L bytes if the iterator runs dry."""
1008 1017 if l > len(self.buf) and not self.iterempty:
1009 1018 # Clamp to a multiple of self.targetsize
1010 1019 targetsize = self.targetsize * ((l // self.targetsize) + 1)
1011 1020 collector = cStringIO.StringIO()
1012 1021 collector.write(self.buf)
1013 1022 collected = len(self.buf)
1014 1023 for chunk in self.in_iter:
1015 1024 collector.write(chunk)
1016 1025 collected += len(chunk)
1017 1026 if collected >= targetsize:
1018 1027 break
1019 1028 if collected < targetsize:
1020 1029 self.iterempty = True
1021 1030 self.buf = collector.getvalue()
1022 1031 s, self.buf = self.buf[:l], buffer(self.buf, l)
1023 1032 return s
1024 1033
1025 1034 def filechunkiter(f, size=65536, limit=None):
1026 1035 """Create a generator that produces the data in the file size
1027 1036 (default 65536) bytes at a time, up to optional limit (default is
1028 1037 to read all data). Chunks may be less than size bytes if the
1029 1038 chunk is the last chunk in the file, or the file is a socket or
1030 1039 some other type of file that sometimes reads less data than is
1031 1040 requested."""
1032 1041 assert size >= 0
1033 1042 assert limit is None or limit >= 0
1034 1043 while True:
1035 1044 if limit is None: nbytes = size
1036 1045 else: nbytes = min(limit, size)
1037 1046 s = nbytes and f.read(nbytes)
1038 1047 if not s: break
1039 1048 if limit: limit -= len(s)
1040 1049 yield s
1041 1050
1042 1051 def makedate():
1043 1052 lt = time.localtime()
1044 1053 if lt[8] == 1 and time.daylight:
1045 1054 tz = time.altzone
1046 1055 else:
1047 1056 tz = time.timezone
1048 1057 return time.mktime(lt), tz
1049 1058
1050 1059 def datestr(date=None, format='%a %b %d %H:%M:%S %Y', timezone=True):
1051 1060 """represent a (unixtime, offset) tuple as a localized time.
1052 1061 unixtime is seconds since the epoch, and offset is the time zone's
1053 1062 number of seconds away from UTC. if timezone is false, do not
1054 1063 append time zone to string."""
1055 1064 t, tz = date or makedate()
1056 1065 s = time.strftime(format, time.gmtime(float(t) - tz))
1057 1066 if timezone:
1058 1067 s += " %+03d%02d" % (-tz / 3600, ((-tz % 3600) / 60))
1059 1068 return s
1060 1069
1061 def strdate(string, format='%a %b %d %H:%M:%S %Y'):
1070 def strdate(string, format, defaults):
1062 1071 """parse a localized time string and return a (unixtime, offset) tuple.
1063 1072 if the string cannot be parsed, ValueError is raised."""
1064 1073 def timezone(string):
1065 1074 tz = string.split()[-1]
1066 1075 if tz[0] in "+-" and len(tz) == 5 and tz[1:].isdigit():
1067 1076 tz = int(tz)
1068 1077 offset = - 3600 * (tz / 100) - 60 * (tz % 100)
1069 1078 return offset
1070 1079 if tz == "GMT" or tz == "UTC":
1071 1080 return 0
1072 1081 return None
1073 1082
1074 1083 # NOTE: unixtime = localunixtime + offset
1075 1084 offset, date = timezone(string), string
1076 1085 if offset != None:
1077 1086 date = " ".join(string.split()[:-1])
1078 1087
1079 # add missing elements
1080 if '%y' not in format.lower():
1081 date += "@" + datestr(makedate(), "%Y", False)
1082 format += "@%Y"
1083 if '%m' not in format and '%b' not in format:
1084 date += "@" + datestr(makedate(), "%m", False)
1085 format += "@%m"
1086 if '%d' not in format:
1087 date += "@" + datestr(makedate(), "%d", False)
1088 format += "@%d"
1088 # add missing elements from defaults
1089 for part in defaults:
1090 found = [True for p in part if ("%"+p) in format]
1091 if not found:
1092 date += "@" + defaults[part]
1093 format += "@%" + part[0]
1089 1094
1090 1095 timetuple = time.strptime(date, format)
1091 1096 localunixtime = int(calendar.timegm(timetuple))
1092 1097 if offset is None:
1093 1098 # local timezone
1094 1099 unixtime = int(time.mktime(timetuple))
1095 1100 offset = unixtime - localunixtime
1096 1101 else:
1097 1102 unixtime = localunixtime + offset
1098 1103 return unixtime, offset
1099 1104
1100 def parsedate(string, formats=None):
1105 def parsedate(string, formats=None, defaults=None):
1101 1106 """parse a localized time string and return a (unixtime, offset) tuple.
1102 1107 The date may be a "unixtime offset" string or in one of the specified
1103 1108 formats."""
1104 1109 if not string:
1105 1110 return 0, 0
1106 1111 if not formats:
1107 1112 formats = defaultdateformats
1108 1113 string = string.strip()
1109 1114 try:
1110 1115 when, offset = map(int, string.split(' '))
1111 1116 except ValueError:
1117 # fill out defaults
1118 if not defaults:
1119 defaults = {}
1120 now = makedate()
1121 for part in "d mb yY HI M S".split():
1122 if part not in defaults:
1123 if part[0] in "HMS":
1124 defaults[part] = "00"
1125 elif part[0] in "dm":
1126 defaults[part] = "1"
1127 else:
1128 defaults[part] = datestr(now, "%" + part[0], False)
1129
1112 1130 for format in formats:
1113 1131 try:
1114 when, offset = strdate(string, format)
1132 when, offset = strdate(string, format, defaults)
1115 1133 except ValueError:
1116 1134 pass
1117 1135 else:
1118 1136 break
1119 1137 else:
1120 1138 raise Abort(_('invalid date: %r ') % string)
1121 1139 # validate explicit (probably user-specified) date and
1122 1140 # time zone offset. values must fit in signed 32 bits for
1123 1141 # current 32-bit linux runtimes. timezones go from UTC-12
1124 1142 # to UTC+14
1125 1143 if abs(when) > 0x7fffffff:
1126 1144 raise Abort(_('date exceeds 32 bits: %d') % when)
1127 1145 if offset < -50400 or offset > 43200:
1128 1146 raise Abort(_('impossible time zone offset: %d') % offset)
1129 1147 return when, offset
1130 1148
1149 def matchdate(date):
1150 """Return a function that matches a given date match specifier
1151
1152 Formats include:
1153
1154 '{date}' match a given date to the accuracy provided
1155
1156 '<{date}' on or before a given date
1157
1158 '>{date}' on or after a given date
1159
1160 """
1161
1162 def lower(date):
1163 return parsedate(date, extendeddateformats)[0]
1164
1165 def upper(date):
1166 d = dict(mb="12", HI="23", M="59", S="59")
1167 for days in "31 30 29".split():
1168 try:
1169 d["d"] = days
1170 return parsedate(date, extendeddateformats, d)[0]
1171 except:
1172 pass
1173 d["d"] = "28"
1174 return parsedate(date, extendeddateformats, d)[0]
1175
1176 if date[0] == "<":
1177 when = upper(date[1:])
1178 return lambda x: x <= when
1179 elif date[0] == ">":
1180 when = lower(date[1:])
1181 return lambda x: x >= when
1182 elif date[0] == "-":
1183 try:
1184 days = int(date[1:])
1185 except ValueError:
1186 raise Abort(_("invalid day spec: %s") % date[1:])
1187 when = makedate()[0] - days * 3600 * 24
1188 return lambda x: x >= when
1189 elif " to " in date:
1190 a, b = date.split(" to ")
1191 start, stop = lower(a), upper(b)
1192 return lambda x: x >= start and x <= stop
1193 else:
1194 start, stop = lower(date), upper(date)
1195 return lambda x: x >= start and x <= stop
1196
1131 1197 def shortuser(user):
1132 1198 """Return a short representation of a user name or email address."""
1133 1199 f = user.find('@')
1134 1200 if f >= 0:
1135 1201 user = user[:f]
1136 1202 f = user.find('<')
1137 1203 if f >= 0:
1138 1204 user = user[f+1:]
1139 1205 f = user.find(' ')
1140 1206 if f >= 0:
1141 1207 user = user[:f]
1142 1208 f = user.find('.')
1143 1209 if f >= 0:
1144 1210 user = user[:f]
1145 1211 return user
1146 1212
1147 1213 def ellipsis(text, maxlength=400):
1148 1214 """Trim string to at most maxlength (default: 400) characters."""
1149 1215 if len(text) <= maxlength:
1150 1216 return text
1151 1217 else:
1152 1218 return "%s..." % (text[:maxlength-3])
1153 1219
1154 1220 def walkrepos(path):
1155 1221 '''yield every hg repository under path, recursively.'''
1156 1222 def errhandler(err):
1157 1223 if err.filename == path:
1158 1224 raise err
1159 1225
1160 1226 for root, dirs, files in os.walk(path, onerror=errhandler):
1161 1227 for d in dirs:
1162 1228 if d == '.hg':
1163 1229 yield root
1164 1230 dirs[:] = []
1165 1231 break
1166 1232
1167 1233 _rcpath = None
1168 1234
1169 1235 def rcpath():
1170 1236 '''return hgrc search path. if env var HGRCPATH is set, use it.
1171 1237 for each item in path, if directory, use files ending in .rc,
1172 1238 else use item.
1173 1239 make HGRCPATH empty to only look in .hg/hgrc of current repo.
1174 1240 if no HGRCPATH, use default os-specific path.'''
1175 1241 global _rcpath
1176 1242 if _rcpath is None:
1177 1243 if 'HGRCPATH' in os.environ:
1178 1244 _rcpath = []
1179 1245 for p in os.environ['HGRCPATH'].split(os.pathsep):
1180 1246 if not p: continue
1181 1247 if os.path.isdir(p):
1182 1248 for f in os.listdir(p):
1183 1249 if f.endswith('.rc'):
1184 1250 _rcpath.append(os.path.join(p, f))
1185 1251 else:
1186 1252 _rcpath.append(p)
1187 1253 else:
1188 1254 _rcpath = os_rcpath()
1189 1255 return _rcpath
1190 1256
1191 1257 def bytecount(nbytes):
1192 1258 '''return byte count formatted as readable string, with units'''
1193 1259
1194 1260 units = (
1195 1261 (100, 1<<30, _('%.0f GB')),
1196 1262 (10, 1<<30, _('%.1f GB')),
1197 1263 (1, 1<<30, _('%.2f GB')),
1198 1264 (100, 1<<20, _('%.0f MB')),
1199 1265 (10, 1<<20, _('%.1f MB')),
1200 1266 (1, 1<<20, _('%.2f MB')),
1201 1267 (100, 1<<10, _('%.0f KB')),
1202 1268 (10, 1<<10, _('%.1f KB')),
1203 1269 (1, 1<<10, _('%.2f KB')),
1204 1270 (1, 1, _('%.0f bytes')),
1205 1271 )
1206 1272
1207 1273 for multiplier, divisor, format in units:
1208 1274 if nbytes >= divisor * multiplier:
1209 1275 return format % (nbytes / float(divisor))
1210 1276 return units[-1][2] % nbytes
1211 1277
1212 1278 def drop_scheme(scheme, path):
1213 1279 sc = scheme + ':'
1214 1280 if path.startswith(sc):
1215 1281 path = path[len(sc):]
1216 1282 if path.startswith('//'):
1217 1283 path = path[2:]
1218 1284 return path
General Comments 0
You need to be logged in to leave comments. Login now