##// END OF EJS Templates
tidy up mtime patch of andrea, get back half of performance loss....
Vadim Gelfer -
r2461:ff9ee834 default
parent child Browse files
Show More
@@ -1,3511 +1,3509 b''
1 1 # commands.py - command processing for mercurial
2 2 #
3 3 # Copyright 2005 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 demandload(globals(), "os re sys signal shutil imp urllib pdb stat")
11 demandload(globals(), "os re sys signal shutil imp urllib pdb")
12 12 demandload(globals(), "fancyopts ui hg util lock revlog templater bundlerepo")
13 13 demandload(globals(), "fnmatch mdiff random signal tempfile time")
14 14 demandload(globals(), "traceback errno socket version struct atexit sets bz2")
15 15 demandload(globals(), "archival changegroup")
16 16 demandload(globals(), "hgweb.server sshserver")
17 17
18 18 class UnknownCommand(Exception):
19 19 """Exception raised if command is not in the command table."""
20 20 class AmbiguousCommand(Exception):
21 21 """Exception raised if command shortcut matches more than one command."""
22 22
23 23 def bail_if_changed(repo):
24 24 modified, added, removed, deleted, unknown = repo.changes()
25 25 if modified or added or removed or deleted:
26 26 raise util.Abort(_("outstanding uncommitted changes"))
27 27
28 28 def filterfiles(filters, files):
29 29 l = [x for x in files if x in filters]
30 30
31 31 for t in filters:
32 32 if t and t[-1] != "/":
33 33 t += "/"
34 34 l += [x for x in files if x.startswith(t)]
35 35 return l
36 36
37 37 def relpath(repo, args):
38 38 cwd = repo.getcwd()
39 39 if cwd:
40 40 return [util.normpath(os.path.join(cwd, x)) for x in args]
41 41 return args
42 42
43 43 def matchpats(repo, pats=[], opts={}, head=''):
44 44 cwd = repo.getcwd()
45 45 if not pats and cwd:
46 46 opts['include'] = [os.path.join(cwd, i) for i in opts['include']]
47 47 opts['exclude'] = [os.path.join(cwd, x) for x in opts['exclude']]
48 48 cwd = ''
49 49 return util.cmdmatcher(repo.root, cwd, pats or ['.'], opts.get('include'),
50 50 opts.get('exclude'), head)
51 51
52 52 def makewalk(repo, pats, opts, node=None, head='', badmatch=None):
53 53 files, matchfn, anypats = matchpats(repo, pats, opts, head)
54 54 exact = dict(zip(files, files))
55 55 def walk():
56 56 for src, fn in repo.walk(node=node, files=files, match=matchfn,
57 57 badmatch=badmatch):
58 58 yield src, fn, util.pathto(repo.getcwd(), fn), fn in exact
59 59 return files, matchfn, walk()
60 60
61 61 def walk(repo, pats, opts, node=None, head='', badmatch=None):
62 62 files, matchfn, results = makewalk(repo, pats, opts, node, head, badmatch)
63 63 for r in results:
64 64 yield r
65 65
66 66 def walkchangerevs(ui, repo, pats, opts):
67 67 '''Iterate over files and the revs they changed in.
68 68
69 69 Callers most commonly need to iterate backwards over the history
70 70 it is interested in. Doing so has awful (quadratic-looking)
71 71 performance, so we use iterators in a "windowed" way.
72 72
73 73 We walk a window of revisions in the desired order. Within the
74 74 window, we first walk forwards to gather data, then in the desired
75 75 order (usually backwards) to display it.
76 76
77 77 This function returns an (iterator, getchange, matchfn) tuple. The
78 78 getchange function returns the changelog entry for a numeric
79 79 revision. The iterator yields 3-tuples. They will be of one of
80 80 the following forms:
81 81
82 82 "window", incrementing, lastrev: stepping through a window,
83 83 positive if walking forwards through revs, last rev in the
84 84 sequence iterated over - use to reset state for the current window
85 85
86 86 "add", rev, fns: out-of-order traversal of the given file names
87 87 fns, which changed during revision rev - use to gather data for
88 88 possible display
89 89
90 90 "iter", rev, None: in-order traversal of the revs earlier iterated
91 91 over with "add" - use to display data'''
92 92
93 93 def increasing_windows(start, end, windowsize=8, sizelimit=512):
94 94 if start < end:
95 95 while start < end:
96 96 yield start, min(windowsize, end-start)
97 97 start += windowsize
98 98 if windowsize < sizelimit:
99 99 windowsize *= 2
100 100 else:
101 101 while start > end:
102 102 yield start, min(windowsize, start-end-1)
103 103 start -= windowsize
104 104 if windowsize < sizelimit:
105 105 windowsize *= 2
106 106
107 107
108 108 files, matchfn, anypats = matchpats(repo, pats, opts)
109 109
110 110 if repo.changelog.count() == 0:
111 111 return [], False, matchfn
112 112
113 113 revs = map(int, revrange(ui, repo, opts['rev'] or ['tip:0']))
114 114 wanted = {}
115 115 slowpath = anypats
116 116 fncache = {}
117 117
118 118 chcache = {}
119 119 def getchange(rev):
120 120 ch = chcache.get(rev)
121 121 if ch is None:
122 122 chcache[rev] = ch = repo.changelog.read(repo.lookup(str(rev)))
123 123 return ch
124 124
125 125 if not slowpath and not files:
126 126 # No files, no patterns. Display all revs.
127 127 wanted = dict(zip(revs, revs))
128 128 if not slowpath:
129 129 # Only files, no patterns. Check the history of each file.
130 130 def filerevgen(filelog):
131 131 for i, window in increasing_windows(filelog.count()-1, -1):
132 132 revs = []
133 133 for j in xrange(i - window, i + 1):
134 134 revs.append(filelog.linkrev(filelog.node(j)))
135 135 revs.reverse()
136 136 for rev in revs:
137 137 yield rev
138 138
139 139 minrev, maxrev = min(revs), max(revs)
140 140 for file_ in files:
141 141 filelog = repo.file(file_)
142 142 # A zero count may be a directory or deleted file, so
143 143 # try to find matching entries on the slow path.
144 144 if filelog.count() == 0:
145 145 slowpath = True
146 146 break
147 147 for rev in filerevgen(filelog):
148 148 if rev <= maxrev:
149 149 if rev < minrev:
150 150 break
151 151 fncache.setdefault(rev, [])
152 152 fncache[rev].append(file_)
153 153 wanted[rev] = 1
154 154 if slowpath:
155 155 # The slow path checks files modified in every changeset.
156 156 def changerevgen():
157 157 for i, window in increasing_windows(repo.changelog.count()-1, -1):
158 158 for j in xrange(i - window, i + 1):
159 159 yield j, getchange(j)[3]
160 160
161 161 for rev, changefiles in changerevgen():
162 162 matches = filter(matchfn, changefiles)
163 163 if matches:
164 164 fncache[rev] = matches
165 165 wanted[rev] = 1
166 166
167 167 def iterate():
168 168 for i, window in increasing_windows(0, len(revs)):
169 169 yield 'window', revs[0] < revs[-1], revs[-1]
170 170 nrevs = [rev for rev in revs[i:i+window]
171 171 if rev in wanted]
172 172 srevs = list(nrevs)
173 173 srevs.sort()
174 174 for rev in srevs:
175 175 fns = fncache.get(rev) or filter(matchfn, getchange(rev)[3])
176 176 yield 'add', rev, fns
177 177 for rev in nrevs:
178 178 yield 'iter', rev, None
179 179 return iterate(), getchange, matchfn
180 180
181 181 revrangesep = ':'
182 182
183 183 def revfix(repo, val, defval):
184 184 '''turn user-level id of changeset into rev number.
185 185 user-level id can be tag, changeset, rev number, or negative rev
186 186 number relative to number of revs (-1 is tip, etc).'''
187 187 if not val:
188 188 return defval
189 189 try:
190 190 num = int(val)
191 191 if str(num) != val:
192 192 raise ValueError
193 193 if num < 0:
194 194 num += repo.changelog.count()
195 195 if num < 0:
196 196 num = 0
197 197 elif num >= repo.changelog.count():
198 198 raise ValueError
199 199 except ValueError:
200 200 try:
201 201 num = repo.changelog.rev(repo.lookup(val))
202 202 except KeyError:
203 203 raise util.Abort(_('invalid revision identifier %s'), val)
204 204 return num
205 205
206 206 def revpair(ui, repo, revs):
207 207 '''return pair of nodes, given list of revisions. second item can
208 208 be None, meaning use working dir.'''
209 209 if not revs:
210 210 return repo.dirstate.parents()[0], None
211 211 end = None
212 212 if len(revs) == 1:
213 213 start = revs[0]
214 214 if revrangesep in start:
215 215 start, end = start.split(revrangesep, 1)
216 216 start = revfix(repo, start, 0)
217 217 end = revfix(repo, end, repo.changelog.count() - 1)
218 218 else:
219 219 start = revfix(repo, start, None)
220 220 elif len(revs) == 2:
221 221 if revrangesep in revs[0] or revrangesep in revs[1]:
222 222 raise util.Abort(_('too many revisions specified'))
223 223 start = revfix(repo, revs[0], None)
224 224 end = revfix(repo, revs[1], None)
225 225 else:
226 226 raise util.Abort(_('too many revisions specified'))
227 227 if end is not None: end = repo.lookup(str(end))
228 228 return repo.lookup(str(start)), end
229 229
230 230 def revrange(ui, repo, revs):
231 231 """Yield revision as strings from a list of revision specifications."""
232 232 seen = {}
233 233 for spec in revs:
234 234 if spec.find(revrangesep) >= 0:
235 235 start, end = spec.split(revrangesep, 1)
236 236 start = revfix(repo, start, 0)
237 237 end = revfix(repo, end, repo.changelog.count() - 1)
238 238 step = start > end and -1 or 1
239 239 for rev in xrange(start, end+step, step):
240 240 if rev in seen:
241 241 continue
242 242 seen[rev] = 1
243 243 yield str(rev)
244 244 else:
245 245 rev = revfix(repo, spec, None)
246 246 if rev in seen:
247 247 continue
248 248 seen[rev] = 1
249 249 yield str(rev)
250 250
251 251 def make_filename(repo, r, pat, node=None,
252 252 total=None, seqno=None, revwidth=None, pathname=None):
253 253 node_expander = {
254 254 'H': lambda: hex(node),
255 255 'R': lambda: str(r.rev(node)),
256 256 'h': lambda: short(node),
257 257 }
258 258 expander = {
259 259 '%': lambda: '%',
260 260 'b': lambda: os.path.basename(repo.root),
261 261 }
262 262
263 263 try:
264 264 if node:
265 265 expander.update(node_expander)
266 266 if node and revwidth is not None:
267 267 expander['r'] = lambda: str(r.rev(node)).zfill(revwidth)
268 268 if total is not None:
269 269 expander['N'] = lambda: str(total)
270 270 if seqno is not None:
271 271 expander['n'] = lambda: str(seqno)
272 272 if total is not None and seqno is not None:
273 273 expander['n'] = lambda:str(seqno).zfill(len(str(total)))
274 274 if pathname is not None:
275 275 expander['s'] = lambda: os.path.basename(pathname)
276 276 expander['d'] = lambda: os.path.dirname(pathname) or '.'
277 277 expander['p'] = lambda: pathname
278 278
279 279 newname = []
280 280 patlen = len(pat)
281 281 i = 0
282 282 while i < patlen:
283 283 c = pat[i]
284 284 if c == '%':
285 285 i += 1
286 286 c = pat[i]
287 287 c = expander[c]()
288 288 newname.append(c)
289 289 i += 1
290 290 return ''.join(newname)
291 291 except KeyError, inst:
292 292 raise util.Abort(_("invalid format spec '%%%s' in output file name"),
293 293 inst.args[0])
294 294
295 295 def make_file(repo, r, pat, node=None,
296 296 total=None, seqno=None, revwidth=None, mode='wb', pathname=None):
297 297 if not pat or pat == '-':
298 298 return 'w' in mode and sys.stdout or sys.stdin
299 299 if hasattr(pat, 'write') and 'w' in mode:
300 300 return pat
301 301 if hasattr(pat, 'read') and 'r' in mode:
302 302 return pat
303 303 return open(make_filename(repo, r, pat, node, total, seqno, revwidth,
304 304 pathname),
305 305 mode)
306 306
307 307 def write_bundle(cg, filename=None, compress=True):
308 308 """Write a bundle file and return its filename.
309 309
310 310 Existing files will not be overwritten.
311 311 If no filename is specified, a temporary file is created.
312 312 bz2 compression can be turned off.
313 313 The bundle file will be deleted in case of errors.
314 314 """
315 315 class nocompress(object):
316 316 def compress(self, x):
317 317 return x
318 318 def flush(self):
319 319 return ""
320 320
321 321 fh = None
322 322 cleanup = None
323 323 try:
324 324 if filename:
325 325 if os.path.exists(filename):
326 326 raise util.Abort(_("file '%s' already exists"), filename)
327 327 fh = open(filename, "wb")
328 328 else:
329 329 fd, filename = tempfile.mkstemp(prefix="hg-bundle-", suffix=".hg")
330 330 fh = os.fdopen(fd, "wb")
331 331 cleanup = filename
332 332
333 333 if compress:
334 334 fh.write("HG10")
335 335 z = bz2.BZ2Compressor(9)
336 336 else:
337 337 fh.write("HG10UN")
338 338 z = nocompress()
339 339 # parse the changegroup data, otherwise we will block
340 340 # in case of sshrepo because we don't know the end of the stream
341 341
342 342 # an empty chunkiter is the end of the changegroup
343 343 empty = False
344 344 while not empty:
345 345 empty = True
346 346 for chunk in changegroup.chunkiter(cg):
347 347 empty = False
348 348 fh.write(z.compress(changegroup.genchunk(chunk)))
349 349 fh.write(z.compress(changegroup.closechunk()))
350 350 fh.write(z.flush())
351 351 cleanup = None
352 352 return filename
353 353 finally:
354 354 if fh is not None:
355 355 fh.close()
356 356 if cleanup is not None:
357 357 os.unlink(cleanup)
358 358
359 359 def dodiff(fp, ui, repo, node1, node2, files=None, match=util.always,
360 360 changes=None, text=False, opts={}):
361 361 if not node1:
362 362 node1 = repo.dirstate.parents()[0]
363 363 # reading the data for node1 early allows it to play nicely
364 364 # with repo.changes and the revlog cache.
365 365 change = repo.changelog.read(node1)
366 366 mmap = repo.manifest.read(change[0])
367 367 date1 = util.datestr(change[2])
368 368
369 369 if not changes:
370 370 changes = repo.changes(node1, node2, files, match=match)
371 371 modified, added, removed, deleted, unknown = changes
372 372 if files:
373 373 modified, added, removed = map(lambda x: filterfiles(files, x),
374 374 (modified, added, removed))
375 375
376 376 if not modified and not added and not removed:
377 377 return
378 378
379 379 if node2:
380 380 change = repo.changelog.read(node2)
381 381 mmap2 = repo.manifest.read(change[0])
382 382 _date2 = util.datestr(change[2])
383 383 def date2(f):
384 384 return _date2
385 385 def read(f):
386 386 return repo.file(f).read(mmap2[f])
387 387 else:
388 tz = util.makedate()[1]
388 389 _date2 = util.datestr()
389 _tz = util.makedate()[1]
390 390 def date2(f):
391 391 try:
392 _f = repo.wopener(f)
392 return util.datestr((os.lstat(repo.wjoin(f)).st_mtime, tz))
393 393 except IOError, err:
394 if err[0] != errno.ENOENT:
395 raise
394 if err.errno != errno.ENOENT: raise
396 395 return _date2
397 return util.datestr((os.fstat(_f.fileno())[stat.ST_MTIME], _tz))
398 396 def read(f):
399 397 return repo.wread(f)
400 398
401 399 if ui.quiet:
402 400 r = None
403 401 else:
404 402 hexfunc = ui.verbose and hex or short
405 403 r = [hexfunc(node) for node in [node1, node2] if node]
406 404
407 405 diffopts = ui.diffopts()
408 406 showfunc = opts.get('show_function') or diffopts['showfunc']
409 407 ignorews = opts.get('ignore_all_space') or diffopts['ignorews']
410 408 for f in modified:
411 409 to = None
412 410 if f in mmap:
413 411 to = repo.file(f).read(mmap[f])
414 412 tn = read(f)
415 413 fp.write(mdiff.unidiff(to, date1, tn, date2(f), f, r, text=text,
416 414 showfunc=showfunc, ignorews=ignorews))
417 415 for f in added:
418 416 to = None
419 417 tn = read(f)
420 418 fp.write(mdiff.unidiff(to, date1, tn, date2(f), f, r, text=text,
421 419 showfunc=showfunc, ignorews=ignorews))
422 420 for f in removed:
423 421 to = repo.file(f).read(mmap[f])
424 422 tn = None
425 423 fp.write(mdiff.unidiff(to, date1, tn, date2(f), f, r, text=text,
426 424 showfunc=showfunc, ignorews=ignorews))
427 425
428 426 def trimuser(ui, name, rev, revcache):
429 427 """trim the name of the user who committed a change"""
430 428 user = revcache.get(rev)
431 429 if user is None:
432 430 user = revcache[rev] = ui.shortuser(name)
433 431 return user
434 432
435 433 class changeset_printer(object):
436 434 '''show changeset information when templating not requested.'''
437 435
438 436 def __init__(self, ui, repo):
439 437 self.ui = ui
440 438 self.repo = repo
441 439
442 440 def show(self, rev=0, changenode=None, brinfo=None):
443 441 '''show a single changeset or file revision'''
444 442 log = self.repo.changelog
445 443 if changenode is None:
446 444 changenode = log.node(rev)
447 445 elif not rev:
448 446 rev = log.rev(changenode)
449 447
450 448 if self.ui.quiet:
451 449 self.ui.write("%d:%s\n" % (rev, short(changenode)))
452 450 return
453 451
454 452 changes = log.read(changenode)
455 453 date = util.datestr(changes[2])
456 454
457 455 parents = [(log.rev(p), self.ui.verbose and hex(p) or short(p))
458 456 for p in log.parents(changenode)
459 457 if self.ui.debugflag or p != nullid]
460 458 if (not self.ui.debugflag and len(parents) == 1 and
461 459 parents[0][0] == rev-1):
462 460 parents = []
463 461
464 462 if self.ui.verbose:
465 463 self.ui.write(_("changeset: %d:%s\n") % (rev, hex(changenode)))
466 464 else:
467 465 self.ui.write(_("changeset: %d:%s\n") % (rev, short(changenode)))
468 466
469 467 for tag in self.repo.nodetags(changenode):
470 468 self.ui.status(_("tag: %s\n") % tag)
471 469 for parent in parents:
472 470 self.ui.write(_("parent: %d:%s\n") % parent)
473 471
474 472 if brinfo and changenode in brinfo:
475 473 br = brinfo[changenode]
476 474 self.ui.write(_("branch: %s\n") % " ".join(br))
477 475
478 476 self.ui.debug(_("manifest: %d:%s\n") %
479 477 (self.repo.manifest.rev(changes[0]), hex(changes[0])))
480 478 self.ui.status(_("user: %s\n") % changes[1])
481 479 self.ui.status(_("date: %s\n") % date)
482 480
483 481 if self.ui.debugflag:
484 482 files = self.repo.changes(log.parents(changenode)[0], changenode)
485 483 for key, value in zip([_("files:"), _("files+:"), _("files-:")],
486 484 files):
487 485 if value:
488 486 self.ui.note("%-12s %s\n" % (key, " ".join(value)))
489 487 else:
490 488 self.ui.note(_("files: %s\n") % " ".join(changes[3]))
491 489
492 490 description = changes[4].strip()
493 491 if description:
494 492 if self.ui.verbose:
495 493 self.ui.status(_("description:\n"))
496 494 self.ui.status(description)
497 495 self.ui.status("\n\n")
498 496 else:
499 497 self.ui.status(_("summary: %s\n") %
500 498 description.splitlines()[0])
501 499 self.ui.status("\n")
502 500
503 501 def show_changeset(ui, repo, opts):
504 502 '''show one changeset. uses template or regular display. caller
505 503 can pass in 'style' and 'template' options in opts.'''
506 504
507 505 tmpl = opts.get('template')
508 506 if tmpl:
509 507 tmpl = templater.parsestring(tmpl, quoted=False)
510 508 else:
511 509 tmpl = ui.config('ui', 'logtemplate')
512 510 if tmpl: tmpl = templater.parsestring(tmpl)
513 511 mapfile = opts.get('style') or ui.config('ui', 'style')
514 512 if tmpl or mapfile:
515 513 if mapfile:
516 514 if not os.path.isfile(mapfile):
517 515 mapname = templater.templatepath('map-cmdline.' + mapfile)
518 516 if not mapname: mapname = templater.templatepath(mapfile)
519 517 if mapname: mapfile = mapname
520 518 try:
521 519 t = templater.changeset_templater(ui, repo, mapfile)
522 520 except SyntaxError, inst:
523 521 raise util.Abort(inst.args[0])
524 522 if tmpl: t.use_template(tmpl)
525 523 return t
526 524 return changeset_printer(ui, repo)
527 525
528 526 def show_version(ui):
529 527 """output version and copyright information"""
530 528 ui.write(_("Mercurial Distributed SCM (version %s)\n")
531 529 % version.get_version())
532 530 ui.status(_(
533 531 "\nCopyright (C) 2005 Matt Mackall <mpm@selenic.com>\n"
534 532 "This is free software; see the source for copying conditions. "
535 533 "There is NO\nwarranty; "
536 534 "not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n"
537 535 ))
538 536
539 537 def help_(ui, cmd=None, with_version=False):
540 538 """show help for a given command or all commands"""
541 539 option_lists = []
542 540 if cmd and cmd != 'shortlist':
543 541 if with_version:
544 542 show_version(ui)
545 543 ui.write('\n')
546 544 aliases, i = find(cmd)
547 545 # synopsis
548 546 ui.write("%s\n\n" % i[2])
549 547
550 548 # description
551 549 doc = i[0].__doc__
552 550 if not doc:
553 551 doc = _("(No help text available)")
554 552 if ui.quiet:
555 553 doc = doc.splitlines(0)[0]
556 554 ui.write("%s\n" % doc.rstrip())
557 555
558 556 if not ui.quiet:
559 557 # aliases
560 558 if len(aliases) > 1:
561 559 ui.write(_("\naliases: %s\n") % ', '.join(aliases[1:]))
562 560
563 561 # options
564 562 if i[1]:
565 563 option_lists.append(("options", i[1]))
566 564
567 565 else:
568 566 # program name
569 567 if ui.verbose or with_version:
570 568 show_version(ui)
571 569 else:
572 570 ui.status(_("Mercurial Distributed SCM\n"))
573 571 ui.status('\n')
574 572
575 573 # list of commands
576 574 if cmd == "shortlist":
577 575 ui.status(_('basic commands (use "hg help" '
578 576 'for the full list or option "-v" for details):\n\n'))
579 577 elif ui.verbose:
580 578 ui.status(_('list of commands:\n\n'))
581 579 else:
582 580 ui.status(_('list of commands (use "hg help -v" '
583 581 'to show aliases and global options):\n\n'))
584 582
585 583 h = {}
586 584 cmds = {}
587 585 for c, e in table.items():
588 586 f = c.split("|")[0]
589 587 if cmd == "shortlist" and not f.startswith("^"):
590 588 continue
591 589 f = f.lstrip("^")
592 590 if not ui.debugflag and f.startswith("debug"):
593 591 continue
594 592 doc = e[0].__doc__
595 593 if not doc:
596 594 doc = _("(No help text available)")
597 595 h[f] = doc.splitlines(0)[0].rstrip()
598 596 cmds[f] = c.lstrip("^")
599 597
600 598 fns = h.keys()
601 599 fns.sort()
602 600 m = max(map(len, fns))
603 601 for f in fns:
604 602 if ui.verbose:
605 603 commands = cmds[f].replace("|",", ")
606 604 ui.write(" %s:\n %s\n"%(commands, h[f]))
607 605 else:
608 606 ui.write(' %-*s %s\n' % (m, f, h[f]))
609 607
610 608 # global options
611 609 if ui.verbose:
612 610 option_lists.append(("global options", globalopts))
613 611
614 612 # list all option lists
615 613 opt_output = []
616 614 for title, options in option_lists:
617 615 opt_output.append(("\n%s:\n" % title, None))
618 616 for shortopt, longopt, default, desc in options:
619 617 opt_output.append(("%2s%s" % (shortopt and "-%s" % shortopt,
620 618 longopt and " --%s" % longopt),
621 619 "%s%s" % (desc,
622 620 default
623 621 and _(" (default: %s)") % default
624 622 or "")))
625 623
626 624 if opt_output:
627 625 opts_len = max([len(line[0]) for line in opt_output if line[1]])
628 626 for first, second in opt_output:
629 627 if second:
630 628 ui.write(" %-*s %s\n" % (opts_len, first, second))
631 629 else:
632 630 ui.write("%s\n" % first)
633 631
634 632 # Commands start here, listed alphabetically
635 633
636 634 def add(ui, repo, *pats, **opts):
637 635 """add the specified files on the next commit
638 636
639 637 Schedule files to be version controlled and added to the repository.
640 638
641 639 The files will be added to the repository at the next commit.
642 640
643 641 If no names are given, add all files in the repository.
644 642 """
645 643
646 644 names = []
647 645 for src, abs, rel, exact in walk(repo, pats, opts):
648 646 if exact:
649 647 if ui.verbose:
650 648 ui.status(_('adding %s\n') % rel)
651 649 names.append(abs)
652 650 elif repo.dirstate.state(abs) == '?':
653 651 ui.status(_('adding %s\n') % rel)
654 652 names.append(abs)
655 653 if not opts['dry_run']:
656 654 repo.add(names)
657 655
658 656 def addremove(ui, repo, *pats, **opts):
659 657 """add all new files, delete all missing files (DEPRECATED)
660 658
661 659 (DEPRECATED)
662 660 Add all new files and remove all missing files from the repository.
663 661
664 662 New files are ignored if they match any of the patterns in .hgignore. As
665 663 with add, these changes take effect at the next commit.
666 664
667 665 This command is now deprecated and will be removed in a future
668 666 release. Please use add and remove --after instead.
669 667 """
670 668 ui.warn(_('(the addremove command is deprecated; use add and remove '
671 669 '--after instead)\n'))
672 670 return addremove_lock(ui, repo, pats, opts)
673 671
674 672 def addremove_lock(ui, repo, pats, opts, wlock=None):
675 673 add, remove = [], []
676 674 for src, abs, rel, exact in walk(repo, pats, opts):
677 675 if src == 'f' and repo.dirstate.state(abs) == '?':
678 676 add.append(abs)
679 677 if ui.verbose or not exact:
680 678 ui.status(_('adding %s\n') % ((pats and rel) or abs))
681 679 if repo.dirstate.state(abs) != 'r' and not os.path.exists(rel):
682 680 remove.append(abs)
683 681 if ui.verbose or not exact:
684 682 ui.status(_('removing %s\n') % ((pats and rel) or abs))
685 683 if not opts.get('dry_run'):
686 684 repo.add(add, wlock=wlock)
687 685 repo.remove(remove, wlock=wlock)
688 686
689 687 def annotate(ui, repo, *pats, **opts):
690 688 """show changeset information per file line
691 689
692 690 List changes in files, showing the revision id responsible for each line
693 691
694 692 This command is useful to discover who did a change or when a change took
695 693 place.
696 694
697 695 Without the -a option, annotate will avoid processing files it
698 696 detects as binary. With -a, annotate will generate an annotation
699 697 anyway, probably with undesirable results.
700 698 """
701 699 def getnode(rev):
702 700 return short(repo.changelog.node(rev))
703 701
704 702 ucache = {}
705 703 def getname(rev):
706 704 cl = repo.changelog.read(repo.changelog.node(rev))
707 705 return trimuser(ui, cl[1], rev, ucache)
708 706
709 707 dcache = {}
710 708 def getdate(rev):
711 709 datestr = dcache.get(rev)
712 710 if datestr is None:
713 711 cl = repo.changelog.read(repo.changelog.node(rev))
714 712 datestr = dcache[rev] = util.datestr(cl[2])
715 713 return datestr
716 714
717 715 if not pats:
718 716 raise util.Abort(_('at least one file name or pattern required'))
719 717
720 718 opmap = [['user', getname], ['number', str], ['changeset', getnode],
721 719 ['date', getdate]]
722 720 if not opts['user'] and not opts['changeset'] and not opts['date']:
723 721 opts['number'] = 1
724 722
725 723 if opts['rev']:
726 724 node = repo.changelog.lookup(opts['rev'])
727 725 else:
728 726 node = repo.dirstate.parents()[0]
729 727 change = repo.changelog.read(node)
730 728 mmap = repo.manifest.read(change[0])
731 729
732 730 for src, abs, rel, exact in walk(repo, pats, opts, node=node):
733 731 f = repo.file(abs)
734 732 if not opts['text'] and util.binary(f.read(mmap[abs])):
735 733 ui.write(_("%s: binary file\n") % ((pats and rel) or abs))
736 734 continue
737 735
738 736 lines = f.annotate(mmap[abs])
739 737 pieces = []
740 738
741 739 for o, f in opmap:
742 740 if opts[o]:
743 741 l = [f(n) for n, dummy in lines]
744 742 if l:
745 743 m = max(map(len, l))
746 744 pieces.append(["%*s" % (m, x) for x in l])
747 745
748 746 if pieces:
749 747 for p, l in zip(zip(*pieces), lines):
750 748 ui.write("%s: %s" % (" ".join(p), l[1]))
751 749
752 750 def archive(ui, repo, dest, **opts):
753 751 '''create unversioned archive of a repository revision
754 752
755 753 By default, the revision used is the parent of the working
756 754 directory; use "-r" to specify a different revision.
757 755
758 756 To specify the type of archive to create, use "-t". Valid
759 757 types are:
760 758
761 759 "files" (default): a directory full of files
762 760 "tar": tar archive, uncompressed
763 761 "tbz2": tar archive, compressed using bzip2
764 762 "tgz": tar archive, compressed using gzip
765 763 "uzip": zip archive, uncompressed
766 764 "zip": zip archive, compressed using deflate
767 765
768 766 The exact name of the destination archive or directory is given
769 767 using a format string; see "hg help export" for details.
770 768
771 769 Each member added to an archive file has a directory prefix
772 770 prepended. Use "-p" to specify a format string for the prefix.
773 771 The default is the basename of the archive, with suffixes removed.
774 772 '''
775 773
776 774 if opts['rev']:
777 775 node = repo.lookup(opts['rev'])
778 776 else:
779 777 node, p2 = repo.dirstate.parents()
780 778 if p2 != nullid:
781 779 raise util.Abort(_('uncommitted merge - please provide a '
782 780 'specific revision'))
783 781
784 782 dest = make_filename(repo, repo.changelog, dest, node)
785 783 prefix = make_filename(repo, repo.changelog, opts['prefix'], node)
786 784 if os.path.realpath(dest) == repo.root:
787 785 raise util.Abort(_('repository root cannot be destination'))
788 786 dummy, matchfn, dummy = matchpats(repo, [], opts)
789 787 archival.archive(repo, dest, node, opts.get('type') or 'files',
790 788 not opts['no_decode'], matchfn, prefix)
791 789
792 790 def backout(ui, repo, rev, **opts):
793 791 '''reverse effect of earlier changeset
794 792
795 793 Commit the backed out changes as a new changeset. The new
796 794 changeset is a child of the backed out changeset.
797 795
798 796 If you back out a changeset other than the tip, a new head is
799 797 created. This head is the parent of the working directory. If
800 798 you back out an old changeset, your working directory will appear
801 799 old after the backout. You should merge the backout changeset
802 800 with another head.
803 801
804 802 The --merge option remembers the parent of the working directory
805 803 before starting the backout, then merges the new head with that
806 804 changeset afterwards. This saves you from doing the merge by
807 805 hand. The result of this merge is not committed, as for a normal
808 806 merge.'''
809 807
810 808 bail_if_changed(repo)
811 809 op1, op2 = repo.dirstate.parents()
812 810 if op2 != nullid:
813 811 raise util.Abort(_('outstanding uncommitted merge'))
814 812 node = repo.lookup(rev)
815 813 parent, p2 = repo.changelog.parents(node)
816 814 if parent == nullid:
817 815 raise util.Abort(_('cannot back out a change with no parents'))
818 816 if p2 != nullid:
819 817 raise util.Abort(_('cannot back out a merge'))
820 818 repo.update(node, force=True, show_stats=False)
821 819 revert_opts = opts.copy()
822 820 revert_opts['rev'] = hex(parent)
823 821 revert(ui, repo, **revert_opts)
824 822 commit_opts = opts.copy()
825 823 commit_opts['addremove'] = False
826 824 if not commit_opts['message'] and not commit_opts['logfile']:
827 825 commit_opts['message'] = _("Backed out changeset %s") % (hex(node))
828 826 commit_opts['force_editor'] = True
829 827 commit(ui, repo, **commit_opts)
830 828 def nice(node):
831 829 return '%d:%s' % (repo.changelog.rev(node), short(node))
832 830 ui.status(_('changeset %s backs out changeset %s\n') %
833 831 (nice(repo.changelog.tip()), nice(node)))
834 832 if opts['merge'] and op1 != node:
835 833 ui.status(_('merging with changeset %s\n') % nice(op1))
836 834 doupdate(ui, repo, hex(op1), **opts)
837 835
838 836 def bundle(ui, repo, fname, dest="default-push", **opts):
839 837 """create a changegroup file
840 838
841 839 Generate a compressed changegroup file collecting all changesets
842 840 not found in the other repository.
843 841
844 842 This file can then be transferred using conventional means and
845 843 applied to another repository with the unbundle command. This is
846 844 useful when native push and pull are not available or when
847 845 exporting an entire repository is undesirable. The standard file
848 846 extension is ".hg".
849 847
850 848 Unlike import/export, this exactly preserves all changeset
851 849 contents including permissions, rename data, and revision history.
852 850 """
853 851 dest = ui.expandpath(dest)
854 852 other = hg.repository(ui, dest)
855 853 o = repo.findoutgoing(other, force=opts['force'])
856 854 cg = repo.changegroup(o, 'bundle')
857 855 write_bundle(cg, fname)
858 856
859 857 def cat(ui, repo, file1, *pats, **opts):
860 858 """output the latest or given revisions of files
861 859
862 860 Print the specified files as they were at the given revision.
863 861 If no revision is given then the tip is used.
864 862
865 863 Output may be to a file, in which case the name of the file is
866 864 given using a format string. The formatting rules are the same as
867 865 for the export command, with the following additions:
868 866
869 867 %s basename of file being printed
870 868 %d dirname of file being printed, or '.' if in repo root
871 869 %p root-relative path name of file being printed
872 870 """
873 871 mf = {}
874 872 rev = opts['rev']
875 873 if rev:
876 874 node = repo.lookup(rev)
877 875 else:
878 876 node = repo.changelog.tip()
879 877 change = repo.changelog.read(node)
880 878 mf = repo.manifest.read(change[0])
881 879 for src, abs, rel, exact in walk(repo, (file1,) + pats, opts, node):
882 880 r = repo.file(abs)
883 881 n = mf[abs]
884 882 fp = make_file(repo, r, opts['output'], node=n, pathname=abs)
885 883 fp.write(r.read(n))
886 884
887 885 def clone(ui, source, dest=None, **opts):
888 886 """make a copy of an existing repository
889 887
890 888 Create a copy of an existing repository in a new directory.
891 889
892 890 If no destination directory name is specified, it defaults to the
893 891 basename of the source.
894 892
895 893 The location of the source is added to the new repository's
896 894 .hg/hgrc file, as the default to be used for future pulls.
897 895
898 896 For efficiency, hardlinks are used for cloning whenever the source
899 897 and destination are on the same filesystem. Some filesystems,
900 898 such as AFS, implement hardlinking incorrectly, but do not report
901 899 errors. In these cases, use the --pull option to avoid
902 900 hardlinking.
903 901
904 902 See pull for valid source format details.
905 903 """
906 904 if dest is None:
907 905 dest = os.path.basename(os.path.normpath(source))
908 906
909 907 if os.path.exists(dest):
910 908 raise util.Abort(_("destination '%s' already exists"), dest)
911 909
912 910 dest = os.path.realpath(dest)
913 911
914 912 class Dircleanup(object):
915 913 def __init__(self, dir_):
916 914 self.rmtree = shutil.rmtree
917 915 self.dir_ = dir_
918 916 os.mkdir(dir_)
919 917 def close(self):
920 918 self.dir_ = None
921 919 def __del__(self):
922 920 if self.dir_:
923 921 self.rmtree(self.dir_, True)
924 922
925 923 if opts['ssh']:
926 924 ui.setconfig("ui", "ssh", opts['ssh'])
927 925 if opts['remotecmd']:
928 926 ui.setconfig("ui", "remotecmd", opts['remotecmd'])
929 927
930 928 source = ui.expandpath(source)
931 929
932 930 d = Dircleanup(dest)
933 931 abspath = source
934 932 other = hg.repository(ui, source)
935 933
936 934 copy = False
937 935 if other.dev() != -1:
938 936 abspath = os.path.abspath(source)
939 937 if not opts['pull'] and not opts['rev']:
940 938 copy = True
941 939
942 940 if copy:
943 941 try:
944 942 # we use a lock here because if we race with commit, we
945 943 # can end up with extra data in the cloned revlogs that's
946 944 # not pointed to by changesets, thus causing verify to
947 945 # fail
948 946 l1 = other.lock()
949 947 except lock.LockException:
950 948 copy = False
951 949
952 950 if copy:
953 951 # we lock here to avoid premature writing to the target
954 952 os.mkdir(os.path.join(dest, ".hg"))
955 953 l2 = lock.lock(os.path.join(dest, ".hg", "lock"))
956 954
957 955 files = "data 00manifest.d 00manifest.i 00changelog.d 00changelog.i"
958 956 for f in files.split():
959 957 src = os.path.join(source, ".hg", f)
960 958 dst = os.path.join(dest, ".hg", f)
961 959 try:
962 960 util.copyfiles(src, dst)
963 961 except OSError, inst:
964 962 if inst.errno != errno.ENOENT:
965 963 raise
966 964
967 965 repo = hg.repository(ui, dest)
968 966
969 967 else:
970 968 revs = None
971 969 if opts['rev']:
972 970 if not other.local():
973 971 error = _("clone -r not supported yet for remote repositories.")
974 972 raise util.Abort(error)
975 973 else:
976 974 revs = [other.lookup(rev) for rev in opts['rev']]
977 975 repo = hg.repository(ui, dest, create=1)
978 976 repo.pull(other, heads = revs)
979 977
980 978 f = repo.opener("hgrc", "w", text=True)
981 979 f.write("[paths]\n")
982 980 f.write("default = %s\n" % abspath)
983 981 f.close()
984 982
985 983 if not opts['noupdate']:
986 984 doupdate(repo.ui, repo)
987 985
988 986 d.close()
989 987
990 988 def commit(ui, repo, *pats, **opts):
991 989 """commit the specified files or all outstanding changes
992 990
993 991 Commit changes to the given files into the repository.
994 992
995 993 If a list of files is omitted, all changes reported by "hg status"
996 994 will be committed.
997 995
998 996 If no commit message is specified, the editor configured in your hgrc
999 997 or in the EDITOR environment variable is started to enter a message.
1000 998 """
1001 999 message = opts['message']
1002 1000 logfile = opts['logfile']
1003 1001
1004 1002 if message and logfile:
1005 1003 raise util.Abort(_('options --message and --logfile are mutually '
1006 1004 'exclusive'))
1007 1005 if not message and logfile:
1008 1006 try:
1009 1007 if logfile == '-':
1010 1008 message = sys.stdin.read()
1011 1009 else:
1012 1010 message = open(logfile).read()
1013 1011 except IOError, inst:
1014 1012 raise util.Abort(_("can't read commit message '%s': %s") %
1015 1013 (logfile, inst.strerror))
1016 1014
1017 1015 if opts['addremove']:
1018 1016 addremove_lock(ui, repo, pats, opts)
1019 1017 fns, match, anypats = matchpats(repo, pats, opts)
1020 1018 if pats:
1021 1019 modified, added, removed, deleted, unknown = (
1022 1020 repo.changes(files=fns, match=match))
1023 1021 files = modified + added + removed
1024 1022 else:
1025 1023 files = []
1026 1024 try:
1027 1025 repo.commit(files, message, opts['user'], opts['date'], match,
1028 1026 force_editor=opts.get('force_editor'))
1029 1027 except ValueError, inst:
1030 1028 raise util.Abort(str(inst))
1031 1029
1032 1030 def docopy(ui, repo, pats, opts, wlock):
1033 1031 # called with the repo lock held
1034 1032 cwd = repo.getcwd()
1035 1033 errors = 0
1036 1034 copied = []
1037 1035 targets = {}
1038 1036
1039 1037 def okaytocopy(abs, rel, exact):
1040 1038 reasons = {'?': _('is not managed'),
1041 1039 'a': _('has been marked for add'),
1042 1040 'r': _('has been marked for remove')}
1043 1041 state = repo.dirstate.state(abs)
1044 1042 reason = reasons.get(state)
1045 1043 if reason:
1046 1044 if state == 'a':
1047 1045 origsrc = repo.dirstate.copied(abs)
1048 1046 if origsrc is not None:
1049 1047 return origsrc
1050 1048 if exact:
1051 1049 ui.warn(_('%s: not copying - file %s\n') % (rel, reason))
1052 1050 else:
1053 1051 return abs
1054 1052
1055 1053 def copy(origsrc, abssrc, relsrc, target, exact):
1056 1054 abstarget = util.canonpath(repo.root, cwd, target)
1057 1055 reltarget = util.pathto(cwd, abstarget)
1058 1056 prevsrc = targets.get(abstarget)
1059 1057 if prevsrc is not None:
1060 1058 ui.warn(_('%s: not overwriting - %s collides with %s\n') %
1061 1059 (reltarget, abssrc, prevsrc))
1062 1060 return
1063 1061 if (not opts['after'] and os.path.exists(reltarget) or
1064 1062 opts['after'] and repo.dirstate.state(abstarget) not in '?r'):
1065 1063 if not opts['force']:
1066 1064 ui.warn(_('%s: not overwriting - file exists\n') %
1067 1065 reltarget)
1068 1066 return
1069 1067 if not opts['after'] and not opts['dry_run']:
1070 1068 os.unlink(reltarget)
1071 1069 if opts['after']:
1072 1070 if not os.path.exists(reltarget):
1073 1071 return
1074 1072 else:
1075 1073 targetdir = os.path.dirname(reltarget) or '.'
1076 1074 if not os.path.isdir(targetdir) and not opts['dry_run']:
1077 1075 os.makedirs(targetdir)
1078 1076 try:
1079 1077 restore = repo.dirstate.state(abstarget) == 'r'
1080 1078 if restore and not opts['dry_run']:
1081 1079 repo.undelete([abstarget], wlock)
1082 1080 try:
1083 1081 if not opts['dry_run']:
1084 1082 shutil.copyfile(relsrc, reltarget)
1085 1083 shutil.copymode(relsrc, reltarget)
1086 1084 restore = False
1087 1085 finally:
1088 1086 if restore:
1089 1087 repo.remove([abstarget], wlock)
1090 1088 except shutil.Error, inst:
1091 1089 raise util.Abort(str(inst))
1092 1090 except IOError, inst:
1093 1091 if inst.errno == errno.ENOENT:
1094 1092 ui.warn(_('%s: deleted in working copy\n') % relsrc)
1095 1093 else:
1096 1094 ui.warn(_('%s: cannot copy - %s\n') %
1097 1095 (relsrc, inst.strerror))
1098 1096 errors += 1
1099 1097 return
1100 1098 if ui.verbose or not exact:
1101 1099 ui.status(_('copying %s to %s\n') % (relsrc, reltarget))
1102 1100 targets[abstarget] = abssrc
1103 1101 if abstarget != origsrc and not opts['dry_run']:
1104 1102 repo.copy(origsrc, abstarget, wlock)
1105 1103 copied.append((abssrc, relsrc, exact))
1106 1104
1107 1105 def targetpathfn(pat, dest, srcs):
1108 1106 if os.path.isdir(pat):
1109 1107 abspfx = util.canonpath(repo.root, cwd, pat)
1110 1108 if destdirexists:
1111 1109 striplen = len(os.path.split(abspfx)[0])
1112 1110 else:
1113 1111 striplen = len(abspfx)
1114 1112 if striplen:
1115 1113 striplen += len(os.sep)
1116 1114 res = lambda p: os.path.join(dest, p[striplen:])
1117 1115 elif destdirexists:
1118 1116 res = lambda p: os.path.join(dest, os.path.basename(p))
1119 1117 else:
1120 1118 res = lambda p: dest
1121 1119 return res
1122 1120
1123 1121 def targetpathafterfn(pat, dest, srcs):
1124 1122 if util.patkind(pat, None)[0]:
1125 1123 # a mercurial pattern
1126 1124 res = lambda p: os.path.join(dest, os.path.basename(p))
1127 1125 else:
1128 1126 abspfx = util.canonpath(repo.root, cwd, pat)
1129 1127 if len(abspfx) < len(srcs[0][0]):
1130 1128 # A directory. Either the target path contains the last
1131 1129 # component of the source path or it does not.
1132 1130 def evalpath(striplen):
1133 1131 score = 0
1134 1132 for s in srcs:
1135 1133 t = os.path.join(dest, s[0][striplen:])
1136 1134 if os.path.exists(t):
1137 1135 score += 1
1138 1136 return score
1139 1137
1140 1138 striplen = len(abspfx)
1141 1139 if striplen:
1142 1140 striplen += len(os.sep)
1143 1141 if os.path.isdir(os.path.join(dest, os.path.split(abspfx)[1])):
1144 1142 score = evalpath(striplen)
1145 1143 striplen1 = len(os.path.split(abspfx)[0])
1146 1144 if striplen1:
1147 1145 striplen1 += len(os.sep)
1148 1146 if evalpath(striplen1) > score:
1149 1147 striplen = striplen1
1150 1148 res = lambda p: os.path.join(dest, p[striplen:])
1151 1149 else:
1152 1150 # a file
1153 1151 if destdirexists:
1154 1152 res = lambda p: os.path.join(dest, os.path.basename(p))
1155 1153 else:
1156 1154 res = lambda p: dest
1157 1155 return res
1158 1156
1159 1157
1160 1158 pats = list(pats)
1161 1159 if not pats:
1162 1160 raise util.Abort(_('no source or destination specified'))
1163 1161 if len(pats) == 1:
1164 1162 raise util.Abort(_('no destination specified'))
1165 1163 dest = pats.pop()
1166 1164 destdirexists = os.path.isdir(dest)
1167 1165 if (len(pats) > 1 or util.patkind(pats[0], None)[0]) and not destdirexists:
1168 1166 raise util.Abort(_('with multiple sources, destination must be an '
1169 1167 'existing directory'))
1170 1168 if opts['after']:
1171 1169 tfn = targetpathafterfn
1172 1170 else:
1173 1171 tfn = targetpathfn
1174 1172 copylist = []
1175 1173 for pat in pats:
1176 1174 srcs = []
1177 1175 for tag, abssrc, relsrc, exact in walk(repo, [pat], opts):
1178 1176 origsrc = okaytocopy(abssrc, relsrc, exact)
1179 1177 if origsrc:
1180 1178 srcs.append((origsrc, abssrc, relsrc, exact))
1181 1179 if not srcs:
1182 1180 continue
1183 1181 copylist.append((tfn(pat, dest, srcs), srcs))
1184 1182 if not copylist:
1185 1183 raise util.Abort(_('no files to copy'))
1186 1184
1187 1185 for targetpath, srcs in copylist:
1188 1186 for origsrc, abssrc, relsrc, exact in srcs:
1189 1187 copy(origsrc, abssrc, relsrc, targetpath(abssrc), exact)
1190 1188
1191 1189 if errors:
1192 1190 ui.warn(_('(consider using --after)\n'))
1193 1191 return errors, copied
1194 1192
1195 1193 def copy(ui, repo, *pats, **opts):
1196 1194 """mark files as copied for the next commit
1197 1195
1198 1196 Mark dest as having copies of source files. If dest is a
1199 1197 directory, copies are put in that directory. If dest is a file,
1200 1198 there can only be one source.
1201 1199
1202 1200 By default, this command copies the contents of files as they
1203 1201 stand in the working directory. If invoked with --after, the
1204 1202 operation is recorded, but no copying is performed.
1205 1203
1206 1204 This command takes effect in the next commit.
1207 1205
1208 1206 NOTE: This command should be treated as experimental. While it
1209 1207 should properly record copied files, this information is not yet
1210 1208 fully used by merge, nor fully reported by log.
1211 1209 """
1212 1210 wlock = repo.wlock(0)
1213 1211 errs, copied = docopy(ui, repo, pats, opts, wlock)
1214 1212 return errs
1215 1213
1216 1214 def debugancestor(ui, index, rev1, rev2):
1217 1215 """find the ancestor revision of two revisions in a given index"""
1218 1216 r = revlog.revlog(util.opener(os.getcwd(), audit=False), index, "", 0)
1219 1217 a = r.ancestor(r.lookup(rev1), r.lookup(rev2))
1220 1218 ui.write("%d:%s\n" % (r.rev(a), hex(a)))
1221 1219
1222 1220 def debugcomplete(ui, cmd='', **opts):
1223 1221 """returns the completion list associated with the given command"""
1224 1222
1225 1223 if opts['options']:
1226 1224 options = []
1227 1225 otables = [globalopts]
1228 1226 if cmd:
1229 1227 aliases, entry = find(cmd)
1230 1228 otables.append(entry[1])
1231 1229 for t in otables:
1232 1230 for o in t:
1233 1231 if o[0]:
1234 1232 options.append('-%s' % o[0])
1235 1233 options.append('--%s' % o[1])
1236 1234 ui.write("%s\n" % "\n".join(options))
1237 1235 return
1238 1236
1239 1237 clist = findpossible(cmd).keys()
1240 1238 clist.sort()
1241 1239 ui.write("%s\n" % "\n".join(clist))
1242 1240
1243 1241 def debugrebuildstate(ui, repo, rev=None):
1244 1242 """rebuild the dirstate as it would look like for the given revision"""
1245 1243 if not rev:
1246 1244 rev = repo.changelog.tip()
1247 1245 else:
1248 1246 rev = repo.lookup(rev)
1249 1247 change = repo.changelog.read(rev)
1250 1248 n = change[0]
1251 1249 files = repo.manifest.readflags(n)
1252 1250 wlock = repo.wlock()
1253 1251 repo.dirstate.rebuild(rev, files.iteritems())
1254 1252
1255 1253 def debugcheckstate(ui, repo):
1256 1254 """validate the correctness of the current dirstate"""
1257 1255 parent1, parent2 = repo.dirstate.parents()
1258 1256 repo.dirstate.read()
1259 1257 dc = repo.dirstate.map
1260 1258 keys = dc.keys()
1261 1259 keys.sort()
1262 1260 m1n = repo.changelog.read(parent1)[0]
1263 1261 m2n = repo.changelog.read(parent2)[0]
1264 1262 m1 = repo.manifest.read(m1n)
1265 1263 m2 = repo.manifest.read(m2n)
1266 1264 errors = 0
1267 1265 for f in dc:
1268 1266 state = repo.dirstate.state(f)
1269 1267 if state in "nr" and f not in m1:
1270 1268 ui.warn(_("%s in state %s, but not in manifest1\n") % (f, state))
1271 1269 errors += 1
1272 1270 if state in "a" and f in m1:
1273 1271 ui.warn(_("%s in state %s, but also in manifest1\n") % (f, state))
1274 1272 errors += 1
1275 1273 if state in "m" and f not in m1 and f not in m2:
1276 1274 ui.warn(_("%s in state %s, but not in either manifest\n") %
1277 1275 (f, state))
1278 1276 errors += 1
1279 1277 for f in m1:
1280 1278 state = repo.dirstate.state(f)
1281 1279 if state not in "nrm":
1282 1280 ui.warn(_("%s in manifest1, but listed as state %s") % (f, state))
1283 1281 errors += 1
1284 1282 if errors:
1285 1283 error = _(".hg/dirstate inconsistent with current parent's manifest")
1286 1284 raise util.Abort(error)
1287 1285
1288 1286 def debugconfig(ui, repo, *values):
1289 1287 """show combined config settings from all hgrc files
1290 1288
1291 1289 With no args, print names and values of all config items.
1292 1290
1293 1291 With one arg of the form section.name, print just the value of
1294 1292 that config item.
1295 1293
1296 1294 With multiple args, print names and values of all config items
1297 1295 with matching section names."""
1298 1296
1299 1297 if values:
1300 1298 if len([v for v in values if '.' in v]) > 1:
1301 1299 raise util.Abort(_('only one config item permitted'))
1302 1300 for section, name, value in ui.walkconfig():
1303 1301 sectname = section + '.' + name
1304 1302 if values:
1305 1303 for v in values:
1306 1304 if v == section:
1307 1305 ui.write('%s=%s\n' % (sectname, value))
1308 1306 elif v == sectname:
1309 1307 ui.write(value, '\n')
1310 1308 else:
1311 1309 ui.write('%s=%s\n' % (sectname, value))
1312 1310
1313 1311 def debugsetparents(ui, repo, rev1, rev2=None):
1314 1312 """manually set the parents of the current working directory
1315 1313
1316 1314 This is useful for writing repository conversion tools, but should
1317 1315 be used with care.
1318 1316 """
1319 1317
1320 1318 if not rev2:
1321 1319 rev2 = hex(nullid)
1322 1320
1323 1321 repo.dirstate.setparents(repo.lookup(rev1), repo.lookup(rev2))
1324 1322
1325 1323 def debugstate(ui, repo):
1326 1324 """show the contents of the current dirstate"""
1327 1325 repo.dirstate.read()
1328 1326 dc = repo.dirstate.map
1329 1327 keys = dc.keys()
1330 1328 keys.sort()
1331 1329 for file_ in keys:
1332 1330 ui.write("%c %3o %10d %s %s\n"
1333 1331 % (dc[file_][0], dc[file_][1] & 0777, dc[file_][2],
1334 1332 time.strftime("%x %X",
1335 1333 time.localtime(dc[file_][3])), file_))
1336 1334 for f in repo.dirstate.copies:
1337 1335 ui.write(_("copy: %s -> %s\n") % (repo.dirstate.copies[f], f))
1338 1336
1339 1337 def debugdata(ui, file_, rev):
1340 1338 """dump the contents of an data file revision"""
1341 1339 r = revlog.revlog(util.opener(os.getcwd(), audit=False),
1342 1340 file_[:-2] + ".i", file_, 0)
1343 1341 try:
1344 1342 ui.write(r.revision(r.lookup(rev)))
1345 1343 except KeyError:
1346 1344 raise util.Abort(_('invalid revision identifier %s'), rev)
1347 1345
1348 1346 def debugindex(ui, file_):
1349 1347 """dump the contents of an index file"""
1350 1348 r = revlog.revlog(util.opener(os.getcwd(), audit=False), file_, "", 0)
1351 1349 ui.write(" rev offset length base linkrev" +
1352 1350 " nodeid p1 p2\n")
1353 1351 for i in range(r.count()):
1354 1352 node = r.node(i)
1355 1353 pp = r.parents(node)
1356 1354 ui.write("% 6d % 9d % 7d % 6d % 7d %s %s %s\n" % (
1357 1355 i, r.start(i), r.length(i), r.base(i), r.linkrev(node),
1358 1356 short(node), short(pp[0]), short(pp[1])))
1359 1357
1360 1358 def debugindexdot(ui, file_):
1361 1359 """dump an index DAG as a .dot file"""
1362 1360 r = revlog.revlog(util.opener(os.getcwd(), audit=False), file_, "", 0)
1363 1361 ui.write("digraph G {\n")
1364 1362 for i in range(r.count()):
1365 1363 node = r.node(i)
1366 1364 pp = r.parents(node)
1367 1365 ui.write("\t%d -> %d\n" % (r.rev(pp[0]), i))
1368 1366 if pp[1] != nullid:
1369 1367 ui.write("\t%d -> %d\n" % (r.rev(pp[1]), i))
1370 1368 ui.write("}\n")
1371 1369
1372 1370 def debugrename(ui, repo, file, rev=None):
1373 1371 """dump rename information"""
1374 1372 r = repo.file(relpath(repo, [file])[0])
1375 1373 if rev:
1376 1374 try:
1377 1375 # assume all revision numbers are for changesets
1378 1376 n = repo.lookup(rev)
1379 1377 change = repo.changelog.read(n)
1380 1378 m = repo.manifest.read(change[0])
1381 1379 n = m[relpath(repo, [file])[0]]
1382 1380 except (hg.RepoError, KeyError):
1383 1381 n = r.lookup(rev)
1384 1382 else:
1385 1383 n = r.tip()
1386 1384 m = r.renamed(n)
1387 1385 if m:
1388 1386 ui.write(_("renamed from %s:%s\n") % (m[0], hex(m[1])))
1389 1387 else:
1390 1388 ui.write(_("not renamed\n"))
1391 1389
1392 1390 def debugwalk(ui, repo, *pats, **opts):
1393 1391 """show how files match on given patterns"""
1394 1392 items = list(walk(repo, pats, opts))
1395 1393 if not items:
1396 1394 return
1397 1395 fmt = '%%s %%-%ds %%-%ds %%s' % (
1398 1396 max([len(abs) for (src, abs, rel, exact) in items]),
1399 1397 max([len(rel) for (src, abs, rel, exact) in items]))
1400 1398 for src, abs, rel, exact in items:
1401 1399 line = fmt % (src, abs, rel, exact and 'exact' or '')
1402 1400 ui.write("%s\n" % line.rstrip())
1403 1401
1404 1402 def diff(ui, repo, *pats, **opts):
1405 1403 """diff repository (or selected files)
1406 1404
1407 1405 Show differences between revisions for the specified files.
1408 1406
1409 1407 Differences between files are shown using the unified diff format.
1410 1408
1411 1409 When two revision arguments are given, then changes are shown
1412 1410 between those revisions. If only one revision is specified then
1413 1411 that revision is compared to the working directory, and, when no
1414 1412 revisions are specified, the working directory files are compared
1415 1413 to its parent.
1416 1414
1417 1415 Without the -a option, diff will avoid generating diffs of files
1418 1416 it detects as binary. With -a, diff will generate a diff anyway,
1419 1417 probably with undesirable results.
1420 1418 """
1421 1419 node1, node2 = revpair(ui, repo, opts['rev'])
1422 1420
1423 1421 fns, matchfn, anypats = matchpats(repo, pats, opts)
1424 1422
1425 1423 dodiff(sys.stdout, ui, repo, node1, node2, fns, match=matchfn,
1426 1424 text=opts['text'], opts=opts)
1427 1425
1428 1426 def doexport(ui, repo, changeset, seqno, total, revwidth, opts):
1429 1427 node = repo.lookup(changeset)
1430 1428 parents = [p for p in repo.changelog.parents(node) if p != nullid]
1431 1429 if opts['switch_parent']:
1432 1430 parents.reverse()
1433 1431 prev = (parents and parents[0]) or nullid
1434 1432 change = repo.changelog.read(node)
1435 1433
1436 1434 fp = make_file(repo, repo.changelog, opts['output'],
1437 1435 node=node, total=total, seqno=seqno,
1438 1436 revwidth=revwidth)
1439 1437 if fp != sys.stdout:
1440 1438 ui.note("%s\n" % fp.name)
1441 1439
1442 1440 fp.write("# HG changeset patch\n")
1443 1441 fp.write("# User %s\n" % change[1])
1444 1442 fp.write("# Date %d %d\n" % change[2])
1445 1443 fp.write("# Node ID %s\n" % hex(node))
1446 1444 fp.write("# Parent %s\n" % hex(prev))
1447 1445 if len(parents) > 1:
1448 1446 fp.write("# Parent %s\n" % hex(parents[1]))
1449 1447 fp.write(change[4].rstrip())
1450 1448 fp.write("\n\n")
1451 1449
1452 1450 dodiff(fp, ui, repo, prev, node, text=opts['text'])
1453 1451 if fp != sys.stdout:
1454 1452 fp.close()
1455 1453
1456 1454 def export(ui, repo, *changesets, **opts):
1457 1455 """dump the header and diffs for one or more changesets
1458 1456
1459 1457 Print the changeset header and diffs for one or more revisions.
1460 1458
1461 1459 The information shown in the changeset header is: author,
1462 1460 changeset hash, parent and commit comment.
1463 1461
1464 1462 Output may be to a file, in which case the name of the file is
1465 1463 given using a format string. The formatting rules are as follows:
1466 1464
1467 1465 %% literal "%" character
1468 1466 %H changeset hash (40 bytes of hexadecimal)
1469 1467 %N number of patches being generated
1470 1468 %R changeset revision number
1471 1469 %b basename of the exporting repository
1472 1470 %h short-form changeset hash (12 bytes of hexadecimal)
1473 1471 %n zero-padded sequence number, starting at 1
1474 1472 %r zero-padded changeset revision number
1475 1473
1476 1474 Without the -a option, export will avoid generating diffs of files
1477 1475 it detects as binary. With -a, export will generate a diff anyway,
1478 1476 probably with undesirable results.
1479 1477
1480 1478 With the --switch-parent option, the diff will be against the second
1481 1479 parent. It can be useful to review a merge.
1482 1480 """
1483 1481 if not changesets:
1484 1482 raise util.Abort(_("export requires at least one changeset"))
1485 1483 seqno = 0
1486 1484 revs = list(revrange(ui, repo, changesets))
1487 1485 total = len(revs)
1488 1486 revwidth = max(map(len, revs))
1489 1487 msg = len(revs) > 1 and _("Exporting patches:\n") or _("Exporting patch:\n")
1490 1488 ui.note(msg)
1491 1489 for cset in revs:
1492 1490 seqno += 1
1493 1491 doexport(ui, repo, cset, seqno, total, revwidth, opts)
1494 1492
1495 1493 def forget(ui, repo, *pats, **opts):
1496 1494 """don't add the specified files on the next commit (DEPRECATED)
1497 1495
1498 1496 (DEPRECATED)
1499 1497 Undo an 'hg add' scheduled for the next commit.
1500 1498
1501 1499 This command is now deprecated and will be removed in a future
1502 1500 release. Please use revert instead.
1503 1501 """
1504 1502 ui.warn(_("(the forget command is deprecated; use revert instead)\n"))
1505 1503 forget = []
1506 1504 for src, abs, rel, exact in walk(repo, pats, opts):
1507 1505 if repo.dirstate.state(abs) == 'a':
1508 1506 forget.append(abs)
1509 1507 if ui.verbose or not exact:
1510 1508 ui.status(_('forgetting %s\n') % ((pats and rel) or abs))
1511 1509 repo.forget(forget)
1512 1510
1513 1511 def grep(ui, repo, pattern, *pats, **opts):
1514 1512 """search for a pattern in specified files and revisions
1515 1513
1516 1514 Search revisions of files for a regular expression.
1517 1515
1518 1516 This command behaves differently than Unix grep. It only accepts
1519 1517 Python/Perl regexps. It searches repository history, not the
1520 1518 working directory. It always prints the revision number in which
1521 1519 a match appears.
1522 1520
1523 1521 By default, grep only prints output for the first revision of a
1524 1522 file in which it finds a match. To get it to print every revision
1525 1523 that contains a change in match status ("-" for a match that
1526 1524 becomes a non-match, or "+" for a non-match that becomes a match),
1527 1525 use the --all flag.
1528 1526 """
1529 1527 reflags = 0
1530 1528 if opts['ignore_case']:
1531 1529 reflags |= re.I
1532 1530 regexp = re.compile(pattern, reflags)
1533 1531 sep, eol = ':', '\n'
1534 1532 if opts['print0']:
1535 1533 sep = eol = '\0'
1536 1534
1537 1535 fcache = {}
1538 1536 def getfile(fn):
1539 1537 if fn not in fcache:
1540 1538 fcache[fn] = repo.file(fn)
1541 1539 return fcache[fn]
1542 1540
1543 1541 def matchlines(body):
1544 1542 begin = 0
1545 1543 linenum = 0
1546 1544 while True:
1547 1545 match = regexp.search(body, begin)
1548 1546 if not match:
1549 1547 break
1550 1548 mstart, mend = match.span()
1551 1549 linenum += body.count('\n', begin, mstart) + 1
1552 1550 lstart = body.rfind('\n', begin, mstart) + 1 or begin
1553 1551 lend = body.find('\n', mend)
1554 1552 yield linenum, mstart - lstart, mend - lstart, body[lstart:lend]
1555 1553 begin = lend + 1
1556 1554
1557 1555 class linestate(object):
1558 1556 def __init__(self, line, linenum, colstart, colend):
1559 1557 self.line = line
1560 1558 self.linenum = linenum
1561 1559 self.colstart = colstart
1562 1560 self.colend = colend
1563 1561 def __eq__(self, other):
1564 1562 return self.line == other.line
1565 1563 def __hash__(self):
1566 1564 return hash(self.line)
1567 1565
1568 1566 matches = {}
1569 1567 def grepbody(fn, rev, body):
1570 1568 matches[rev].setdefault(fn, {})
1571 1569 m = matches[rev][fn]
1572 1570 for lnum, cstart, cend, line in matchlines(body):
1573 1571 s = linestate(line, lnum, cstart, cend)
1574 1572 m[s] = s
1575 1573
1576 1574 # FIXME: prev isn't used, why ?
1577 1575 prev = {}
1578 1576 ucache = {}
1579 1577 def display(fn, rev, states, prevstates):
1580 1578 diff = list(sets.Set(states).symmetric_difference(sets.Set(prevstates)))
1581 1579 diff.sort(lambda x, y: cmp(x.linenum, y.linenum))
1582 1580 counts = {'-': 0, '+': 0}
1583 1581 filerevmatches = {}
1584 1582 for l in diff:
1585 1583 if incrementing or not opts['all']:
1586 1584 change = ((l in prevstates) and '-') or '+'
1587 1585 r = rev
1588 1586 else:
1589 1587 change = ((l in states) and '-') or '+'
1590 1588 r = prev[fn]
1591 1589 cols = [fn, str(rev)]
1592 1590 if opts['line_number']:
1593 1591 cols.append(str(l.linenum))
1594 1592 if opts['all']:
1595 1593 cols.append(change)
1596 1594 if opts['user']:
1597 1595 cols.append(trimuser(ui, getchange(rev)[1], rev,
1598 1596 ucache))
1599 1597 if opts['files_with_matches']:
1600 1598 c = (fn, rev)
1601 1599 if c in filerevmatches:
1602 1600 continue
1603 1601 filerevmatches[c] = 1
1604 1602 else:
1605 1603 cols.append(l.line)
1606 1604 ui.write(sep.join(cols), eol)
1607 1605 counts[change] += 1
1608 1606 return counts['+'], counts['-']
1609 1607
1610 1608 fstate = {}
1611 1609 skip = {}
1612 1610 changeiter, getchange, matchfn = walkchangerevs(ui, repo, pats, opts)
1613 1611 count = 0
1614 1612 incrementing = False
1615 1613 for st, rev, fns in changeiter:
1616 1614 if st == 'window':
1617 1615 incrementing = rev
1618 1616 matches.clear()
1619 1617 elif st == 'add':
1620 1618 change = repo.changelog.read(repo.lookup(str(rev)))
1621 1619 mf = repo.manifest.read(change[0])
1622 1620 matches[rev] = {}
1623 1621 for fn in fns:
1624 1622 if fn in skip:
1625 1623 continue
1626 1624 fstate.setdefault(fn, {})
1627 1625 try:
1628 1626 grepbody(fn, rev, getfile(fn).read(mf[fn]))
1629 1627 except KeyError:
1630 1628 pass
1631 1629 elif st == 'iter':
1632 1630 states = matches[rev].items()
1633 1631 states.sort()
1634 1632 for fn, m in states:
1635 1633 if fn in skip:
1636 1634 continue
1637 1635 if incrementing or not opts['all'] or fstate[fn]:
1638 1636 pos, neg = display(fn, rev, m, fstate[fn])
1639 1637 count += pos + neg
1640 1638 if pos and not opts['all']:
1641 1639 skip[fn] = True
1642 1640 fstate[fn] = m
1643 1641 prev[fn] = rev
1644 1642
1645 1643 if not incrementing:
1646 1644 fstate = fstate.items()
1647 1645 fstate.sort()
1648 1646 for fn, state in fstate:
1649 1647 if fn in skip:
1650 1648 continue
1651 1649 display(fn, rev, {}, state)
1652 1650 return (count == 0 and 1) or 0
1653 1651
1654 1652 def heads(ui, repo, **opts):
1655 1653 """show current repository heads
1656 1654
1657 1655 Show all repository head changesets.
1658 1656
1659 1657 Repository "heads" are changesets that don't have children
1660 1658 changesets. They are where development generally takes place and
1661 1659 are the usual targets for update and merge operations.
1662 1660 """
1663 1661 if opts['rev']:
1664 1662 heads = repo.heads(repo.lookup(opts['rev']))
1665 1663 else:
1666 1664 heads = repo.heads()
1667 1665 br = None
1668 1666 if opts['branches']:
1669 1667 br = repo.branchlookup(heads)
1670 1668 displayer = show_changeset(ui, repo, opts)
1671 1669 for n in heads:
1672 1670 displayer.show(changenode=n, brinfo=br)
1673 1671
1674 1672 def identify(ui, repo):
1675 1673 """print information about the working copy
1676 1674
1677 1675 Print a short summary of the current state of the repo.
1678 1676
1679 1677 This summary identifies the repository state using one or two parent
1680 1678 hash identifiers, followed by a "+" if there are uncommitted changes
1681 1679 in the working directory, followed by a list of tags for this revision.
1682 1680 """
1683 1681 parents = [p for p in repo.dirstate.parents() if p != nullid]
1684 1682 if not parents:
1685 1683 ui.write(_("unknown\n"))
1686 1684 return
1687 1685
1688 1686 hexfunc = ui.verbose and hex or short
1689 1687 modified, added, removed, deleted, unknown = repo.changes()
1690 1688 output = ["%s%s" %
1691 1689 ('+'.join([hexfunc(parent) for parent in parents]),
1692 1690 (modified or added or removed or deleted) and "+" or "")]
1693 1691
1694 1692 if not ui.quiet:
1695 1693 # multiple tags for a single parent separated by '/'
1696 1694 parenttags = ['/'.join(tags)
1697 1695 for tags in map(repo.nodetags, parents) if tags]
1698 1696 # tags for multiple parents separated by ' + '
1699 1697 if parenttags:
1700 1698 output.append(' + '.join(parenttags))
1701 1699
1702 1700 ui.write("%s\n" % ' '.join(output))
1703 1701
1704 1702 def import_(ui, repo, patch1, *patches, **opts):
1705 1703 """import an ordered set of patches
1706 1704
1707 1705 Import a list of patches and commit them individually.
1708 1706
1709 1707 If there are outstanding changes in the working directory, import
1710 1708 will abort unless given the -f flag.
1711 1709
1712 1710 If a patch looks like a mail message (its first line starts with
1713 1711 "From " or looks like an RFC822 header), it will not be applied
1714 1712 unless the -f option is used. The importer neither parses nor
1715 1713 discards mail headers, so use -f only to override the "mailness"
1716 1714 safety check, not to import a real mail message.
1717 1715
1718 1716 To read a patch from standard input, use patch name "-".
1719 1717 """
1720 1718 patches = (patch1,) + patches
1721 1719
1722 1720 if not opts['force']:
1723 1721 bail_if_changed(repo)
1724 1722
1725 1723 d = opts["base"]
1726 1724 strip = opts["strip"]
1727 1725
1728 1726 mailre = re.compile(r'(?:From |[\w-]+:)')
1729 1727
1730 1728 # attempt to detect the start of a patch
1731 1729 # (this heuristic is borrowed from quilt)
1732 1730 diffre = re.compile(r'(?:Index:[ \t]|diff[ \t]|RCS file: |' +
1733 1731 'retrieving revision [0-9]+(\.[0-9]+)*$|' +
1734 1732 '(---|\*\*\*)[ \t])')
1735 1733
1736 1734 for patch in patches:
1737 1735 pf = os.path.join(d, patch)
1738 1736
1739 1737 message = []
1740 1738 user = None
1741 1739 date = None
1742 1740 hgpatch = False
1743 1741 if pf == '-':
1744 1742 f = sys.stdin
1745 1743 fd, tmpname = tempfile.mkstemp(prefix='hg-patch-')
1746 1744 pf = tmpname
1747 1745 tmpfp = os.fdopen(fd, 'w')
1748 1746 ui.status(_("applying patch from stdin\n"))
1749 1747 else:
1750 1748 f = open(pf)
1751 1749 tmpfp, tmpname = None, None
1752 1750 ui.status(_("applying %s\n") % patch)
1753 1751 try:
1754 1752 while True:
1755 1753 line = f.readline()
1756 1754 if not line: break
1757 1755 if tmpfp: tmpfp.write(line)
1758 1756 line = line.rstrip()
1759 1757 if (not message and not hgpatch and
1760 1758 mailre.match(line) and not opts['force']):
1761 1759 if len(line) > 35:
1762 1760 line = line[:32] + '...'
1763 1761 raise util.Abort(_('first line looks like a '
1764 1762 'mail header: ') + line)
1765 1763 if diffre.match(line):
1766 1764 if tmpfp:
1767 1765 for chunk in util.filechunkiter(f):
1768 1766 tmpfp.write(chunk)
1769 1767 break
1770 1768 elif hgpatch:
1771 1769 # parse values when importing the result of an hg export
1772 1770 if line.startswith("# User "):
1773 1771 user = line[7:]
1774 1772 ui.debug(_('User: %s\n') % user)
1775 1773 elif line.startswith("# Date "):
1776 1774 date = line[7:]
1777 1775 elif not line.startswith("# ") and line:
1778 1776 message.append(line)
1779 1777 hgpatch = False
1780 1778 elif line == '# HG changeset patch':
1781 1779 hgpatch = True
1782 1780 message = [] # We may have collected garbage
1783 1781 elif message or line:
1784 1782 message.append(line)
1785 1783
1786 1784 if opts['message']:
1787 1785 # pickup the cmdline msg
1788 1786 message = opts['message']
1789 1787 elif message:
1790 1788 # pickup the patch msg
1791 1789 message = '\n'.join(message).rstrip()
1792 1790 else:
1793 1791 # launch the editor
1794 1792 message = None
1795 1793 ui.debug(_('message:\n%s\n') % message)
1796 1794
1797 1795 if tmpfp: tmpfp.close()
1798 1796 files = util.patch(strip, pf, ui)
1799 1797
1800 1798 if len(files) > 0:
1801 1799 addremove_lock(ui, repo, files, {})
1802 1800 repo.commit(files, message, user, date)
1803 1801 finally:
1804 1802 if tmpname: os.unlink(tmpname)
1805 1803
1806 1804 def incoming(ui, repo, source="default", **opts):
1807 1805 """show new changesets found in source
1808 1806
1809 1807 Show new changesets found in the specified path/URL or the default
1810 1808 pull location. These are the changesets that would be pulled if a pull
1811 1809 was requested.
1812 1810
1813 1811 For remote repository, using --bundle avoids downloading the changesets
1814 1812 twice if the incoming is followed by a pull.
1815 1813
1816 1814 See pull for valid source format details.
1817 1815 """
1818 1816 source = ui.expandpath(source)
1819 1817 if opts['ssh']:
1820 1818 ui.setconfig("ui", "ssh", opts['ssh'])
1821 1819 if opts['remotecmd']:
1822 1820 ui.setconfig("ui", "remotecmd", opts['remotecmd'])
1823 1821
1824 1822 other = hg.repository(ui, source)
1825 1823 incoming = repo.findincoming(other, force=opts["force"])
1826 1824 if not incoming:
1827 1825 ui.status(_("no changes found\n"))
1828 1826 return
1829 1827
1830 1828 cleanup = None
1831 1829 try:
1832 1830 fname = opts["bundle"]
1833 1831 if fname or not other.local():
1834 1832 # create a bundle (uncompressed if other repo is not local)
1835 1833 cg = other.changegroup(incoming, "incoming")
1836 1834 fname = cleanup = write_bundle(cg, fname, compress=other.local())
1837 1835 # keep written bundle?
1838 1836 if opts["bundle"]:
1839 1837 cleanup = None
1840 1838 if not other.local():
1841 1839 # use the created uncompressed bundlerepo
1842 1840 other = bundlerepo.bundlerepository(ui, repo.root, fname)
1843 1841
1844 1842 o = other.changelog.nodesbetween(incoming)[0]
1845 1843 if opts['newest_first']:
1846 1844 o.reverse()
1847 1845 displayer = show_changeset(ui, other, opts)
1848 1846 for n in o:
1849 1847 parents = [p for p in other.changelog.parents(n) if p != nullid]
1850 1848 if opts['no_merges'] and len(parents) == 2:
1851 1849 continue
1852 1850 displayer.show(changenode=n)
1853 1851 if opts['patch']:
1854 1852 prev = (parents and parents[0]) or nullid
1855 1853 dodiff(ui, ui, other, prev, n)
1856 1854 ui.write("\n")
1857 1855 finally:
1858 1856 if hasattr(other, 'close'):
1859 1857 other.close()
1860 1858 if cleanup:
1861 1859 os.unlink(cleanup)
1862 1860
1863 1861 def init(ui, dest="."):
1864 1862 """create a new repository in the given directory
1865 1863
1866 1864 Initialize a new repository in the given directory. If the given
1867 1865 directory does not exist, it is created.
1868 1866
1869 1867 If no directory is given, the current directory is used.
1870 1868 """
1871 1869 if not os.path.exists(dest):
1872 1870 os.mkdir(dest)
1873 1871 hg.repository(ui, dest, create=1)
1874 1872
1875 1873 def locate(ui, repo, *pats, **opts):
1876 1874 """locate files matching specific patterns
1877 1875
1878 1876 Print all files under Mercurial control whose names match the
1879 1877 given patterns.
1880 1878
1881 1879 This command searches the current directory and its
1882 1880 subdirectories. To search an entire repository, move to the root
1883 1881 of the repository.
1884 1882
1885 1883 If no patterns are given to match, this command prints all file
1886 1884 names.
1887 1885
1888 1886 If you want to feed the output of this command into the "xargs"
1889 1887 command, use the "-0" option to both this command and "xargs".
1890 1888 This will avoid the problem of "xargs" treating single filenames
1891 1889 that contain white space as multiple filenames.
1892 1890 """
1893 1891 end = opts['print0'] and '\0' or '\n'
1894 1892 rev = opts['rev']
1895 1893 if rev:
1896 1894 node = repo.lookup(rev)
1897 1895 else:
1898 1896 node = None
1899 1897
1900 1898 for src, abs, rel, exact in walk(repo, pats, opts, node=node,
1901 1899 head='(?:.*/|)'):
1902 1900 if not node and repo.dirstate.state(abs) == '?':
1903 1901 continue
1904 1902 if opts['fullpath']:
1905 1903 ui.write(os.path.join(repo.root, abs), end)
1906 1904 else:
1907 1905 ui.write(((pats and rel) or abs), end)
1908 1906
1909 1907 def log(ui, repo, *pats, **opts):
1910 1908 """show revision history of entire repository or files
1911 1909
1912 1910 Print the revision history of the specified files or the entire project.
1913 1911
1914 1912 By default this command outputs: changeset id and hash, tags,
1915 1913 non-trivial parents, user, date and time, and a summary for each
1916 1914 commit. When the -v/--verbose switch is used, the list of changed
1917 1915 files and full commit message is shown.
1918 1916 """
1919 1917 class dui(object):
1920 1918 # Implement and delegate some ui protocol. Save hunks of
1921 1919 # output for later display in the desired order.
1922 1920 def __init__(self, ui):
1923 1921 self.ui = ui
1924 1922 self.hunk = {}
1925 1923 self.header = {}
1926 1924 def bump(self, rev):
1927 1925 self.rev = rev
1928 1926 self.hunk[rev] = []
1929 1927 self.header[rev] = []
1930 1928 def note(self, *args):
1931 1929 if self.verbose:
1932 1930 self.write(*args)
1933 1931 def status(self, *args):
1934 1932 if not self.quiet:
1935 1933 self.write(*args)
1936 1934 def write(self, *args):
1937 1935 self.hunk[self.rev].append(args)
1938 1936 def write_header(self, *args):
1939 1937 self.header[self.rev].append(args)
1940 1938 def debug(self, *args):
1941 1939 if self.debugflag:
1942 1940 self.write(*args)
1943 1941 def __getattr__(self, key):
1944 1942 return getattr(self.ui, key)
1945 1943
1946 1944 changeiter, getchange, matchfn = walkchangerevs(ui, repo, pats, opts)
1947 1945
1948 1946 if opts['limit']:
1949 1947 try:
1950 1948 limit = int(opts['limit'])
1951 1949 except ValueError:
1952 1950 raise util.Abort(_('limit must be a positive integer'))
1953 1951 if limit <= 0: raise util.Abort(_('limit must be positive'))
1954 1952 else:
1955 1953 limit = sys.maxint
1956 1954 count = 0
1957 1955
1958 1956 displayer = show_changeset(ui, repo, opts)
1959 1957 for st, rev, fns in changeiter:
1960 1958 if st == 'window':
1961 1959 du = dui(ui)
1962 1960 displayer.ui = du
1963 1961 elif st == 'add':
1964 1962 du.bump(rev)
1965 1963 changenode = repo.changelog.node(rev)
1966 1964 parents = [p for p in repo.changelog.parents(changenode)
1967 1965 if p != nullid]
1968 1966 if opts['no_merges'] and len(parents) == 2:
1969 1967 continue
1970 1968 if opts['only_merges'] and len(parents) != 2:
1971 1969 continue
1972 1970
1973 1971 if opts['keyword']:
1974 1972 changes = getchange(rev)
1975 1973 miss = 0
1976 1974 for k in [kw.lower() for kw in opts['keyword']]:
1977 1975 if not (k in changes[1].lower() or
1978 1976 k in changes[4].lower() or
1979 1977 k in " ".join(changes[3][:20]).lower()):
1980 1978 miss = 1
1981 1979 break
1982 1980 if miss:
1983 1981 continue
1984 1982
1985 1983 br = None
1986 1984 if opts['branches']:
1987 1985 br = repo.branchlookup([repo.changelog.node(rev)])
1988 1986
1989 1987 displayer.show(rev, brinfo=br)
1990 1988 if opts['patch']:
1991 1989 prev = (parents and parents[0]) or nullid
1992 1990 dodiff(du, du, repo, prev, changenode, match=matchfn)
1993 1991 du.write("\n\n")
1994 1992 elif st == 'iter':
1995 1993 if count == limit: break
1996 1994 if du.header[rev]:
1997 1995 for args in du.header[rev]:
1998 1996 ui.write_header(*args)
1999 1997 if du.hunk[rev]:
2000 1998 count += 1
2001 1999 for args in du.hunk[rev]:
2002 2000 ui.write(*args)
2003 2001
2004 2002 def manifest(ui, repo, rev=None):
2005 2003 """output the latest or given revision of the project manifest
2006 2004
2007 2005 Print a list of version controlled files for the given revision.
2008 2006
2009 2007 The manifest is the list of files being version controlled. If no revision
2010 2008 is given then the tip is used.
2011 2009 """
2012 2010 if rev:
2013 2011 try:
2014 2012 # assume all revision numbers are for changesets
2015 2013 n = repo.lookup(rev)
2016 2014 change = repo.changelog.read(n)
2017 2015 n = change[0]
2018 2016 except hg.RepoError:
2019 2017 n = repo.manifest.lookup(rev)
2020 2018 else:
2021 2019 n = repo.manifest.tip()
2022 2020 m = repo.manifest.read(n)
2023 2021 mf = repo.manifest.readflags(n)
2024 2022 files = m.keys()
2025 2023 files.sort()
2026 2024
2027 2025 for f in files:
2028 2026 ui.write("%40s %3s %s\n" % (hex(m[f]), mf[f] and "755" or "644", f))
2029 2027
2030 2028 def merge(ui, repo, node=None, **opts):
2031 2029 """Merge working directory with another revision
2032 2030
2033 2031 Merge the contents of the current working directory and the
2034 2032 requested revision. Files that changed between either parent are
2035 2033 marked as changed for the next commit and a commit must be
2036 2034 performed before any further updates are allowed.
2037 2035 """
2038 2036 return doupdate(ui, repo, node=node, merge=True, **opts)
2039 2037
2040 2038 def outgoing(ui, repo, dest="default-push", **opts):
2041 2039 """show changesets not found in destination
2042 2040
2043 2041 Show changesets not found in the specified destination repository or
2044 2042 the default push location. These are the changesets that would be pushed
2045 2043 if a push was requested.
2046 2044
2047 2045 See pull for valid destination format details.
2048 2046 """
2049 2047 dest = ui.expandpath(dest)
2050 2048 if opts['ssh']:
2051 2049 ui.setconfig("ui", "ssh", opts['ssh'])
2052 2050 if opts['remotecmd']:
2053 2051 ui.setconfig("ui", "remotecmd", opts['remotecmd'])
2054 2052
2055 2053 other = hg.repository(ui, dest)
2056 2054 o = repo.findoutgoing(other, force=opts['force'])
2057 2055 if not o:
2058 2056 ui.status(_("no changes found\n"))
2059 2057 return
2060 2058 o = repo.changelog.nodesbetween(o)[0]
2061 2059 if opts['newest_first']:
2062 2060 o.reverse()
2063 2061 displayer = show_changeset(ui, repo, opts)
2064 2062 for n in o:
2065 2063 parents = [p for p in repo.changelog.parents(n) if p != nullid]
2066 2064 if opts['no_merges'] and len(parents) == 2:
2067 2065 continue
2068 2066 displayer.show(changenode=n)
2069 2067 if opts['patch']:
2070 2068 prev = (parents and parents[0]) or nullid
2071 2069 dodiff(ui, ui, repo, prev, n)
2072 2070 ui.write("\n")
2073 2071
2074 2072 def parents(ui, repo, rev=None, branches=None, **opts):
2075 2073 """show the parents of the working dir or revision
2076 2074
2077 2075 Print the working directory's parent revisions.
2078 2076 """
2079 2077 if rev:
2080 2078 p = repo.changelog.parents(repo.lookup(rev))
2081 2079 else:
2082 2080 p = repo.dirstate.parents()
2083 2081
2084 2082 br = None
2085 2083 if branches is not None:
2086 2084 br = repo.branchlookup(p)
2087 2085 displayer = show_changeset(ui, repo, opts)
2088 2086 for n in p:
2089 2087 if n != nullid:
2090 2088 displayer.show(changenode=n, brinfo=br)
2091 2089
2092 2090 def paths(ui, repo, search=None):
2093 2091 """show definition of symbolic path names
2094 2092
2095 2093 Show definition of symbolic path name NAME. If no name is given, show
2096 2094 definition of available names.
2097 2095
2098 2096 Path names are defined in the [paths] section of /etc/mercurial/hgrc
2099 2097 and $HOME/.hgrc. If run inside a repository, .hg/hgrc is used, too.
2100 2098 """
2101 2099 if search:
2102 2100 for name, path in ui.configitems("paths"):
2103 2101 if name == search:
2104 2102 ui.write("%s\n" % path)
2105 2103 return
2106 2104 ui.warn(_("not found!\n"))
2107 2105 return 1
2108 2106 else:
2109 2107 for name, path in ui.configitems("paths"):
2110 2108 ui.write("%s = %s\n" % (name, path))
2111 2109
2112 2110 def postincoming(ui, repo, modheads, optupdate):
2113 2111 if modheads == 0:
2114 2112 return
2115 2113 if optupdate:
2116 2114 if modheads == 1:
2117 2115 return doupdate(ui, repo)
2118 2116 else:
2119 2117 ui.status(_("not updating, since new heads added\n"))
2120 2118 if modheads > 1:
2121 2119 ui.status(_("(run 'hg heads' to see heads, 'hg merge' to merge)\n"))
2122 2120 else:
2123 2121 ui.status(_("(run 'hg update' to get a working copy)\n"))
2124 2122
2125 2123 def pull(ui, repo, source="default", **opts):
2126 2124 """pull changes from the specified source
2127 2125
2128 2126 Pull changes from a remote repository to a local one.
2129 2127
2130 2128 This finds all changes from the repository at the specified path
2131 2129 or URL and adds them to the local repository. By default, this
2132 2130 does not update the copy of the project in the working directory.
2133 2131
2134 2132 Valid URLs are of the form:
2135 2133
2136 2134 local/filesystem/path
2137 2135 http://[user@]host[:port][/path]
2138 2136 https://[user@]host[:port][/path]
2139 2137 ssh://[user@]host[:port][/path]
2140 2138
2141 2139 Some notes about using SSH with Mercurial:
2142 2140 - SSH requires an accessible shell account on the destination machine
2143 2141 and a copy of hg in the remote path or specified with as remotecmd.
2144 2142 - /path is relative to the remote user's home directory by default.
2145 2143 Use two slashes at the start of a path to specify an absolute path.
2146 2144 - Mercurial doesn't use its own compression via SSH; the right thing
2147 2145 to do is to configure it in your ~/.ssh/ssh_config, e.g.:
2148 2146 Host *.mylocalnetwork.example.com
2149 2147 Compression off
2150 2148 Host *
2151 2149 Compression on
2152 2150 Alternatively specify "ssh -C" as your ssh command in your hgrc or
2153 2151 with the --ssh command line option.
2154 2152 """
2155 2153 source = ui.expandpath(source)
2156 2154 ui.status(_('pulling from %s\n') % (source))
2157 2155
2158 2156 if opts['ssh']:
2159 2157 ui.setconfig("ui", "ssh", opts['ssh'])
2160 2158 if opts['remotecmd']:
2161 2159 ui.setconfig("ui", "remotecmd", opts['remotecmd'])
2162 2160
2163 2161 other = hg.repository(ui, source)
2164 2162 revs = None
2165 2163 if opts['rev'] and not other.local():
2166 2164 raise util.Abort(_("pull -r doesn't work for remote repositories yet"))
2167 2165 elif opts['rev']:
2168 2166 revs = [other.lookup(rev) for rev in opts['rev']]
2169 2167 modheads = repo.pull(other, heads=revs, force=opts['force'])
2170 2168 return postincoming(ui, repo, modheads, opts['update'])
2171 2169
2172 2170 def push(ui, repo, dest="default-push", **opts):
2173 2171 """push changes to the specified destination
2174 2172
2175 2173 Push changes from the local repository to the given destination.
2176 2174
2177 2175 This is the symmetrical operation for pull. It helps to move
2178 2176 changes from the current repository to a different one. If the
2179 2177 destination is local this is identical to a pull in that directory
2180 2178 from the current one.
2181 2179
2182 2180 By default, push will refuse to run if it detects the result would
2183 2181 increase the number of remote heads. This generally indicates the
2184 2182 the client has forgotten to sync and merge before pushing.
2185 2183
2186 2184 Valid URLs are of the form:
2187 2185
2188 2186 local/filesystem/path
2189 2187 ssh://[user@]host[:port][/path]
2190 2188
2191 2189 Look at the help text for the pull command for important details
2192 2190 about ssh:// URLs.
2193 2191 """
2194 2192 dest = ui.expandpath(dest)
2195 2193 ui.status('pushing to %s\n' % (dest))
2196 2194
2197 2195 if opts['ssh']:
2198 2196 ui.setconfig("ui", "ssh", opts['ssh'])
2199 2197 if opts['remotecmd']:
2200 2198 ui.setconfig("ui", "remotecmd", opts['remotecmd'])
2201 2199
2202 2200 other = hg.repository(ui, dest)
2203 2201 revs = None
2204 2202 if opts['rev']:
2205 2203 revs = [repo.lookup(rev) for rev in opts['rev']]
2206 2204 r = repo.push(other, opts['force'], revs=revs)
2207 2205 return r == 0
2208 2206
2209 2207 def rawcommit(ui, repo, *flist, **rc):
2210 2208 """raw commit interface (DEPRECATED)
2211 2209
2212 2210 (DEPRECATED)
2213 2211 Lowlevel commit, for use in helper scripts.
2214 2212
2215 2213 This command is not intended to be used by normal users, as it is
2216 2214 primarily useful for importing from other SCMs.
2217 2215
2218 2216 This command is now deprecated and will be removed in a future
2219 2217 release, please use debugsetparents and commit instead.
2220 2218 """
2221 2219
2222 2220 ui.warn(_("(the rawcommit command is deprecated)\n"))
2223 2221
2224 2222 message = rc['message']
2225 2223 if not message and rc['logfile']:
2226 2224 try:
2227 2225 message = open(rc['logfile']).read()
2228 2226 except IOError:
2229 2227 pass
2230 2228 if not message and not rc['logfile']:
2231 2229 raise util.Abort(_("missing commit message"))
2232 2230
2233 2231 files = relpath(repo, list(flist))
2234 2232 if rc['files']:
2235 2233 files += open(rc['files']).read().splitlines()
2236 2234
2237 2235 rc['parent'] = map(repo.lookup, rc['parent'])
2238 2236
2239 2237 try:
2240 2238 repo.rawcommit(files, message, rc['user'], rc['date'], *rc['parent'])
2241 2239 except ValueError, inst:
2242 2240 raise util.Abort(str(inst))
2243 2241
2244 2242 def recover(ui, repo):
2245 2243 """roll back an interrupted transaction
2246 2244
2247 2245 Recover from an interrupted commit or pull.
2248 2246
2249 2247 This command tries to fix the repository status after an interrupted
2250 2248 operation. It should only be necessary when Mercurial suggests it.
2251 2249 """
2252 2250 if repo.recover():
2253 2251 return repo.verify()
2254 2252 return 1
2255 2253
2256 2254 def remove(ui, repo, *pats, **opts):
2257 2255 """remove the specified files on the next commit
2258 2256
2259 2257 Schedule the indicated files for removal from the repository.
2260 2258
2261 2259 This command schedules the files to be removed at the next commit.
2262 2260 This only removes files from the current branch, not from the
2263 2261 entire project history. If the files still exist in the working
2264 2262 directory, they will be deleted from it. If invoked with --after,
2265 2263 files that have been manually deleted are marked as removed.
2266 2264
2267 2265 Modified files and added files are not removed by default. To
2268 2266 remove them, use the -f/--force option.
2269 2267 """
2270 2268 names = []
2271 2269 if not opts['after'] and not pats:
2272 2270 raise util.Abort(_('no files specified'))
2273 2271 files, matchfn, anypats = matchpats(repo, pats, opts)
2274 2272 exact = dict.fromkeys(files)
2275 2273 mardu = map(dict.fromkeys, repo.changes(files=files, match=matchfn))
2276 2274 modified, added, removed, deleted, unknown = mardu
2277 2275 remove, forget = [], []
2278 2276 for src, abs, rel, exact in walk(repo, pats, opts):
2279 2277 reason = None
2280 2278 if abs not in deleted and opts['after']:
2281 2279 reason = _('is still present')
2282 2280 elif abs in modified and not opts['force']:
2283 2281 reason = _('is modified (use -f to force removal)')
2284 2282 elif abs in added:
2285 2283 if opts['force']:
2286 2284 forget.append(abs)
2287 2285 continue
2288 2286 reason = _('has been marked for add (use -f to force removal)')
2289 2287 elif abs in unknown:
2290 2288 reason = _('is not managed')
2291 2289 elif abs in removed:
2292 2290 continue
2293 2291 if reason:
2294 2292 if exact:
2295 2293 ui.warn(_('not removing %s: file %s\n') % (rel, reason))
2296 2294 else:
2297 2295 if ui.verbose or not exact:
2298 2296 ui.status(_('removing %s\n') % rel)
2299 2297 remove.append(abs)
2300 2298 repo.forget(forget)
2301 2299 repo.remove(remove, unlink=not opts['after'])
2302 2300
2303 2301 def rename(ui, repo, *pats, **opts):
2304 2302 """rename files; equivalent of copy + remove
2305 2303
2306 2304 Mark dest as copies of sources; mark sources for deletion. If
2307 2305 dest is a directory, copies are put in that directory. If dest is
2308 2306 a file, there can only be one source.
2309 2307
2310 2308 By default, this command copies the contents of files as they
2311 2309 stand in the working directory. If invoked with --after, the
2312 2310 operation is recorded, but no copying is performed.
2313 2311
2314 2312 This command takes effect in the next commit.
2315 2313
2316 2314 NOTE: This command should be treated as experimental. While it
2317 2315 should properly record rename files, this information is not yet
2318 2316 fully used by merge, nor fully reported by log.
2319 2317 """
2320 2318 wlock = repo.wlock(0)
2321 2319 errs, copied = docopy(ui, repo, pats, opts, wlock)
2322 2320 names = []
2323 2321 for abs, rel, exact in copied:
2324 2322 if ui.verbose or not exact:
2325 2323 ui.status(_('removing %s\n') % rel)
2326 2324 names.append(abs)
2327 2325 if not opts['dry_run']:
2328 2326 repo.remove(names, True, wlock)
2329 2327 return errs
2330 2328
2331 2329 def revert(ui, repo, *pats, **opts):
2332 2330 """revert files or dirs to their states as of some revision
2333 2331
2334 2332 With no revision specified, revert the named files or directories
2335 2333 to the contents they had in the parent of the working directory.
2336 2334 This restores the contents of the affected files to an unmodified
2337 2335 state. If the working directory has two parents, you must
2338 2336 explicitly specify the revision to revert to.
2339 2337
2340 2338 Modified files are saved with a .orig suffix before reverting.
2341 2339 To disable these backups, use --no-backup.
2342 2340
2343 2341 Using the -r option, revert the given files or directories to
2344 2342 their contents as of a specific revision. This can be helpful to"roll
2345 2343 back" some or all of a change that should not have been committed.
2346 2344
2347 2345 Revert modifies the working directory. It does not commit any
2348 2346 changes, or change the parent of the working directory. If you
2349 2347 revert to a revision other than the parent of the working
2350 2348 directory, the reverted files will thus appear modified
2351 2349 afterwards.
2352 2350
2353 2351 If a file has been deleted, it is recreated. If the executable
2354 2352 mode of a file was changed, it is reset.
2355 2353
2356 2354 If names are given, all files matching the names are reverted.
2357 2355
2358 2356 If no arguments are given, all files in the repository are reverted.
2359 2357 """
2360 2358 parent, p2 = repo.dirstate.parents()
2361 2359 if opts['rev']:
2362 2360 node = repo.lookup(opts['rev'])
2363 2361 elif p2 != nullid:
2364 2362 raise util.Abort(_('working dir has two parents; '
2365 2363 'you must specify the revision to revert to'))
2366 2364 else:
2367 2365 node = parent
2368 2366 mf = repo.manifest.read(repo.changelog.read(node)[0])
2369 2367 if node == parent:
2370 2368 pmf = mf
2371 2369 else:
2372 2370 pmf = None
2373 2371
2374 2372 wlock = repo.wlock()
2375 2373
2376 2374 # need all matching names in dirstate and manifest of target rev,
2377 2375 # so have to walk both. do not print errors if files exist in one
2378 2376 # but not other.
2379 2377
2380 2378 names = {}
2381 2379 target_only = {}
2382 2380
2383 2381 # walk dirstate.
2384 2382
2385 2383 for src, abs, rel, exact in walk(repo, pats, opts, badmatch=mf.has_key):
2386 2384 names[abs] = (rel, exact)
2387 2385 if src == 'b':
2388 2386 target_only[abs] = True
2389 2387
2390 2388 # walk target manifest.
2391 2389
2392 2390 for src, abs, rel, exact in walk(repo, pats, opts, node=node,
2393 2391 badmatch=names.has_key):
2394 2392 if abs in names: continue
2395 2393 names[abs] = (rel, exact)
2396 2394 target_only[abs] = True
2397 2395
2398 2396 changes = repo.changes(match=names.has_key, wlock=wlock)
2399 2397 modified, added, removed, deleted, unknown = map(dict.fromkeys, changes)
2400 2398
2401 2399 revert = ([], _('reverting %s\n'))
2402 2400 add = ([], _('adding %s\n'))
2403 2401 remove = ([], _('removing %s\n'))
2404 2402 forget = ([], _('forgetting %s\n'))
2405 2403 undelete = ([], _('undeleting %s\n'))
2406 2404 update = {}
2407 2405
2408 2406 disptable = (
2409 2407 # dispatch table:
2410 2408 # file state
2411 2409 # action if in target manifest
2412 2410 # action if not in target manifest
2413 2411 # make backup if in target manifest
2414 2412 # make backup if not in target manifest
2415 2413 (modified, revert, remove, True, True),
2416 2414 (added, revert, forget, True, False),
2417 2415 (removed, undelete, None, False, False),
2418 2416 (deleted, revert, remove, False, False),
2419 2417 (unknown, add, None, True, False),
2420 2418 (target_only, add, None, False, False),
2421 2419 )
2422 2420
2423 2421 entries = names.items()
2424 2422 entries.sort()
2425 2423
2426 2424 for abs, (rel, exact) in entries:
2427 2425 mfentry = mf.get(abs)
2428 2426 def handle(xlist, dobackup):
2429 2427 xlist[0].append(abs)
2430 2428 update[abs] = 1
2431 2429 if dobackup and not opts['no_backup'] and os.path.exists(rel):
2432 2430 bakname = "%s.orig" % rel
2433 2431 ui.note(_('saving current version of %s as %s\n') %
2434 2432 (rel, bakname))
2435 2433 if not opts.get('dry_run'):
2436 2434 shutil.copyfile(rel, bakname)
2437 2435 shutil.copymode(rel, bakname)
2438 2436 if ui.verbose or not exact:
2439 2437 ui.status(xlist[1] % rel)
2440 2438 for table, hitlist, misslist, backuphit, backupmiss in disptable:
2441 2439 if abs not in table: continue
2442 2440 # file has changed in dirstate
2443 2441 if mfentry:
2444 2442 handle(hitlist, backuphit)
2445 2443 elif misslist is not None:
2446 2444 handle(misslist, backupmiss)
2447 2445 else:
2448 2446 if exact: ui.warn(_('file not managed: %s\n' % rel))
2449 2447 break
2450 2448 else:
2451 2449 # file has not changed in dirstate
2452 2450 if node == parent:
2453 2451 if exact: ui.warn(_('no changes needed to %s\n' % rel))
2454 2452 continue
2455 2453 if pmf is None:
2456 2454 # only need parent manifest in this unlikely case,
2457 2455 # so do not read by default
2458 2456 pmf = repo.manifest.read(repo.changelog.read(parent)[0])
2459 2457 if abs in pmf:
2460 2458 if mfentry:
2461 2459 # if version of file is same in parent and target
2462 2460 # manifests, do nothing
2463 2461 if pmf[abs] != mfentry:
2464 2462 handle(revert, False)
2465 2463 else:
2466 2464 handle(remove, False)
2467 2465
2468 2466 if not opts.get('dry_run'):
2469 2467 repo.dirstate.forget(forget[0])
2470 2468 r = repo.update(node, False, True, update.has_key, False, wlock=wlock,
2471 2469 show_stats=False)
2472 2470 repo.dirstate.update(add[0], 'a')
2473 2471 repo.dirstate.update(undelete[0], 'n')
2474 2472 repo.dirstate.update(remove[0], 'r')
2475 2473 return r
2476 2474
2477 2475 def rollback(ui, repo):
2478 2476 """roll back the last transaction in this repository
2479 2477
2480 2478 Roll back the last transaction in this repository, restoring the
2481 2479 project to its state prior to the transaction.
2482 2480
2483 2481 Transactions are used to encapsulate the effects of all commands
2484 2482 that create new changesets or propagate existing changesets into a
2485 2483 repository. For example, the following commands are transactional,
2486 2484 and their effects can be rolled back:
2487 2485
2488 2486 commit
2489 2487 import
2490 2488 pull
2491 2489 push (with this repository as destination)
2492 2490 unbundle
2493 2491
2494 2492 This command should be used with care. There is only one level of
2495 2493 rollback, and there is no way to undo a rollback.
2496 2494
2497 2495 This command is not intended for use on public repositories. Once
2498 2496 changes are visible for pull by other users, rolling a transaction
2499 2497 back locally is ineffective (someone else may already have pulled
2500 2498 the changes). Furthermore, a race is possible with readers of the
2501 2499 repository; for example an in-progress pull from the repository
2502 2500 may fail if a rollback is performed.
2503 2501 """
2504 2502 repo.rollback()
2505 2503
2506 2504 def root(ui, repo):
2507 2505 """print the root (top) of the current working dir
2508 2506
2509 2507 Print the root directory of the current repository.
2510 2508 """
2511 2509 ui.write(repo.root + "\n")
2512 2510
2513 2511 def serve(ui, repo, **opts):
2514 2512 """export the repository via HTTP
2515 2513
2516 2514 Start a local HTTP repository browser and pull server.
2517 2515
2518 2516 By default, the server logs accesses to stdout and errors to
2519 2517 stderr. Use the "-A" and "-E" options to log to files.
2520 2518 """
2521 2519
2522 2520 if opts["stdio"]:
2523 2521 if repo is None:
2524 2522 raise hg.RepoError(_('no repo found'))
2525 2523 s = sshserver.sshserver(ui, repo)
2526 2524 s.serve_forever()
2527 2525
2528 2526 optlist = ("name templates style address port ipv6"
2529 2527 " accesslog errorlog webdir_conf")
2530 2528 for o in optlist.split():
2531 2529 if opts[o]:
2532 2530 ui.setconfig("web", o, opts[o])
2533 2531
2534 2532 if repo is None and not ui.config("web", "webdir_conf"):
2535 2533 raise hg.RepoError(_('no repo found'))
2536 2534
2537 2535 if opts['daemon'] and not opts['daemon_pipefds']:
2538 2536 rfd, wfd = os.pipe()
2539 2537 args = sys.argv[:]
2540 2538 args.append('--daemon-pipefds=%d,%d' % (rfd, wfd))
2541 2539 pid = os.spawnvp(os.P_NOWAIT | getattr(os, 'P_DETACH', 0),
2542 2540 args[0], args)
2543 2541 os.close(wfd)
2544 2542 os.read(rfd, 1)
2545 2543 os._exit(0)
2546 2544
2547 2545 try:
2548 2546 httpd = hgweb.server.create_server(ui, repo)
2549 2547 except socket.error, inst:
2550 2548 raise util.Abort(_('cannot start server: ') + inst.args[1])
2551 2549
2552 2550 if ui.verbose:
2553 2551 addr, port = httpd.socket.getsockname()
2554 2552 if addr == '0.0.0.0':
2555 2553 addr = socket.gethostname()
2556 2554 else:
2557 2555 try:
2558 2556 addr = socket.gethostbyaddr(addr)[0]
2559 2557 except socket.error:
2560 2558 pass
2561 2559 if port != 80:
2562 2560 ui.status(_('listening at http://%s:%d/\n') % (addr, port))
2563 2561 else:
2564 2562 ui.status(_('listening at http://%s/\n') % addr)
2565 2563
2566 2564 if opts['pid_file']:
2567 2565 fp = open(opts['pid_file'], 'w')
2568 2566 fp.write(str(os.getpid()))
2569 2567 fp.close()
2570 2568
2571 2569 if opts['daemon_pipefds']:
2572 2570 rfd, wfd = [int(x) for x in opts['daemon_pipefds'].split(',')]
2573 2571 os.close(rfd)
2574 2572 os.write(wfd, 'y')
2575 2573 os.close(wfd)
2576 2574 sys.stdout.flush()
2577 2575 sys.stderr.flush()
2578 2576 fd = os.open(util.nulldev, os.O_RDWR)
2579 2577 if fd != 0: os.dup2(fd, 0)
2580 2578 if fd != 1: os.dup2(fd, 1)
2581 2579 if fd != 2: os.dup2(fd, 2)
2582 2580 if fd not in (0, 1, 2): os.close(fd)
2583 2581
2584 2582 httpd.serve_forever()
2585 2583
2586 2584 def status(ui, repo, *pats, **opts):
2587 2585 """show changed files in the working directory
2588 2586
2589 2587 Show changed files in the repository. If names are
2590 2588 given, only files that match are shown.
2591 2589
2592 2590 The codes used to show the status of files are:
2593 2591 M = modified
2594 2592 A = added
2595 2593 R = removed
2596 2594 ! = deleted, but still tracked
2597 2595 ? = not tracked
2598 2596 I = ignored (not shown by default)
2599 2597 """
2600 2598
2601 2599 show_ignored = opts['ignored'] and True or False
2602 2600 files, matchfn, anypats = matchpats(repo, pats, opts)
2603 2601 cwd = (pats and repo.getcwd()) or ''
2604 2602 modified, added, removed, deleted, unknown, ignored = [
2605 2603 [util.pathto(cwd, x) for x in n]
2606 2604 for n in repo.changes(files=files, match=matchfn,
2607 2605 show_ignored=show_ignored)]
2608 2606
2609 2607 changetypes = [('modified', 'M', modified),
2610 2608 ('added', 'A', added),
2611 2609 ('removed', 'R', removed),
2612 2610 ('deleted', '!', deleted),
2613 2611 ('unknown', '?', unknown),
2614 2612 ('ignored', 'I', ignored)]
2615 2613
2616 2614 end = opts['print0'] and '\0' or '\n'
2617 2615
2618 2616 for opt, char, changes in ([ct for ct in changetypes if opts[ct[0]]]
2619 2617 or changetypes):
2620 2618 if opts['no_status']:
2621 2619 format = "%%s%s" % end
2622 2620 else:
2623 2621 format = "%s %%s%s" % (char, end)
2624 2622
2625 2623 for f in changes:
2626 2624 ui.write(format % f)
2627 2625
2628 2626 def tag(ui, repo, name, rev_=None, **opts):
2629 2627 """add a tag for the current tip or a given revision
2630 2628
2631 2629 Name a particular revision using <name>.
2632 2630
2633 2631 Tags are used to name particular revisions of the repository and are
2634 2632 very useful to compare different revision, to go back to significant
2635 2633 earlier versions or to mark branch points as releases, etc.
2636 2634
2637 2635 If no revision is given, the tip is used.
2638 2636
2639 2637 To facilitate version control, distribution, and merging of tags,
2640 2638 they are stored as a file named ".hgtags" which is managed
2641 2639 similarly to other project files and can be hand-edited if
2642 2640 necessary. The file '.hg/localtags' is used for local tags (not
2643 2641 shared among repositories).
2644 2642 """
2645 2643 if name == "tip":
2646 2644 raise util.Abort(_("the name 'tip' is reserved"))
2647 2645 if rev_ is not None:
2648 2646 ui.warn(_("use of 'hg tag NAME [REV]' is deprecated, "
2649 2647 "please use 'hg tag [-r REV] NAME' instead\n"))
2650 2648 if opts['rev']:
2651 2649 raise util.Abort(_("use only one form to specify the revision"))
2652 2650 if opts['rev']:
2653 2651 rev_ = opts['rev']
2654 2652 if rev_:
2655 2653 r = hex(repo.lookup(rev_))
2656 2654 else:
2657 2655 r = hex(repo.changelog.tip())
2658 2656
2659 2657 disallowed = (revrangesep, '\r', '\n')
2660 2658 for c in disallowed:
2661 2659 if name.find(c) >= 0:
2662 2660 raise util.Abort(_("%s cannot be used in a tag name") % repr(c))
2663 2661
2664 2662 repo.hook('pretag', throw=True, node=r, tag=name,
2665 2663 local=int(not not opts['local']))
2666 2664
2667 2665 if opts['local']:
2668 2666 repo.opener("localtags", "a").write("%s %s\n" % (r, name))
2669 2667 repo.hook('tag', node=r, tag=name, local=1)
2670 2668 return
2671 2669
2672 2670 for x in repo.changes():
2673 2671 if ".hgtags" in x:
2674 2672 raise util.Abort(_("working copy of .hgtags is changed "
2675 2673 "(please commit .hgtags manually)"))
2676 2674
2677 2675 repo.wfile(".hgtags", "ab").write("%s %s\n" % (r, name))
2678 2676 if repo.dirstate.state(".hgtags") == '?':
2679 2677 repo.add([".hgtags"])
2680 2678
2681 2679 message = (opts['message'] or
2682 2680 _("Added tag %s for changeset %s") % (name, r))
2683 2681 try:
2684 2682 repo.commit([".hgtags"], message, opts['user'], opts['date'])
2685 2683 repo.hook('tag', node=r, tag=name, local=0)
2686 2684 except ValueError, inst:
2687 2685 raise util.Abort(str(inst))
2688 2686
2689 2687 def tags(ui, repo):
2690 2688 """list repository tags
2691 2689
2692 2690 List the repository tags.
2693 2691
2694 2692 This lists both regular and local tags.
2695 2693 """
2696 2694
2697 2695 l = repo.tagslist()
2698 2696 l.reverse()
2699 2697 for t, n in l:
2700 2698 try:
2701 2699 r = "%5d:%s" % (repo.changelog.rev(n), hex(n))
2702 2700 except KeyError:
2703 2701 r = " ?:?"
2704 2702 if ui.quiet:
2705 2703 ui.write("%s\n" % t)
2706 2704 else:
2707 2705 ui.write("%-30s %s\n" % (t, r))
2708 2706
2709 2707 def tip(ui, repo, **opts):
2710 2708 """show the tip revision
2711 2709
2712 2710 Show the tip revision.
2713 2711 """
2714 2712 n = repo.changelog.tip()
2715 2713 br = None
2716 2714 if opts['branches']:
2717 2715 br = repo.branchlookup([n])
2718 2716 show_changeset(ui, repo, opts).show(changenode=n, brinfo=br)
2719 2717 if opts['patch']:
2720 2718 dodiff(ui, ui, repo, repo.changelog.parents(n)[0], n)
2721 2719
2722 2720 def unbundle(ui, repo, fname, **opts):
2723 2721 """apply a changegroup file
2724 2722
2725 2723 Apply a compressed changegroup file generated by the bundle
2726 2724 command.
2727 2725 """
2728 2726 f = urllib.urlopen(fname)
2729 2727
2730 2728 header = f.read(6)
2731 2729 if not header.startswith("HG"):
2732 2730 raise util.Abort(_("%s: not a Mercurial bundle file") % fname)
2733 2731 elif not header.startswith("HG10"):
2734 2732 raise util.Abort(_("%s: unknown bundle version") % fname)
2735 2733 elif header == "HG10BZ":
2736 2734 def generator(f):
2737 2735 zd = bz2.BZ2Decompressor()
2738 2736 zd.decompress("BZ")
2739 2737 for chunk in f:
2740 2738 yield zd.decompress(chunk)
2741 2739 elif header == "HG10UN":
2742 2740 def generator(f):
2743 2741 for chunk in f:
2744 2742 yield chunk
2745 2743 else:
2746 2744 raise util.Abort(_("%s: unknown bundle compression type")
2747 2745 % fname)
2748 2746 gen = generator(util.filechunkiter(f, 4096))
2749 2747 modheads = repo.addchangegroup(util.chunkbuffer(gen), 'unbundle')
2750 2748 return postincoming(ui, repo, modheads, opts['update'])
2751 2749
2752 2750 def undo(ui, repo):
2753 2751 """undo the last commit or pull (DEPRECATED)
2754 2752
2755 2753 (DEPRECATED)
2756 2754 This command is now deprecated and will be removed in a future
2757 2755 release. Please use the rollback command instead. For usage
2758 2756 instructions, see the rollback command.
2759 2757 """
2760 2758 ui.warn(_('(the undo command is deprecated; use rollback instead)\n'))
2761 2759 repo.rollback()
2762 2760
2763 2761 def update(ui, repo, node=None, merge=False, clean=False, force=None,
2764 2762 branch=None, **opts):
2765 2763 """update or merge working directory
2766 2764
2767 2765 Update the working directory to the specified revision.
2768 2766
2769 2767 If there are no outstanding changes in the working directory and
2770 2768 there is a linear relationship between the current version and the
2771 2769 requested version, the result is the requested version.
2772 2770
2773 2771 To merge the working directory with another revision, use the
2774 2772 merge command.
2775 2773
2776 2774 By default, update will refuse to run if doing so would require
2777 2775 merging or discarding local changes.
2778 2776 """
2779 2777 if merge:
2780 2778 ui.warn(_('(the -m/--merge option is deprecated; '
2781 2779 'use the merge command instead)\n'))
2782 2780 return doupdate(ui, repo, node, merge, clean, force, branch, **opts)
2783 2781
2784 2782 def doupdate(ui, repo, node=None, merge=False, clean=False, force=None,
2785 2783 branch=None, **opts):
2786 2784 if branch:
2787 2785 br = repo.branchlookup(branch=branch)
2788 2786 found = []
2789 2787 for x in br:
2790 2788 if branch in br[x]:
2791 2789 found.append(x)
2792 2790 if len(found) > 1:
2793 2791 ui.warn(_("Found multiple heads for %s\n") % branch)
2794 2792 for x in found:
2795 2793 show_changeset(ui, repo, opts).show(changenode=x, brinfo=br)
2796 2794 return 1
2797 2795 if len(found) == 1:
2798 2796 node = found[0]
2799 2797 ui.warn(_("Using head %s for branch %s\n") % (short(node), branch))
2800 2798 else:
2801 2799 ui.warn(_("branch %s not found\n") % (branch))
2802 2800 return 1
2803 2801 else:
2804 2802 node = node and repo.lookup(node) or repo.changelog.tip()
2805 2803 return repo.update(node, allow=merge, force=clean, forcemerge=force)
2806 2804
2807 2805 def verify(ui, repo):
2808 2806 """verify the integrity of the repository
2809 2807
2810 2808 Verify the integrity of the current repository.
2811 2809
2812 2810 This will perform an extensive check of the repository's
2813 2811 integrity, validating the hashes and checksums of each entry in
2814 2812 the changelog, manifest, and tracked files, as well as the
2815 2813 integrity of their crosslinks and indices.
2816 2814 """
2817 2815 return repo.verify()
2818 2816
2819 2817 # Command options and aliases are listed here, alphabetically
2820 2818
2821 2819 table = {
2822 2820 "^add":
2823 2821 (add,
2824 2822 [('I', 'include', [], _('include names matching the given patterns')),
2825 2823 ('X', 'exclude', [], _('exclude names matching the given patterns')),
2826 2824 ('n', 'dry-run', None, _('do not perform actions, just print output'))],
2827 2825 _('hg add [OPTION]... [FILE]...')),
2828 2826 "debugaddremove|addremove":
2829 2827 (addremove,
2830 2828 [('I', 'include', [], _('include names matching the given patterns')),
2831 2829 ('X', 'exclude', [], _('exclude names matching the given patterns')),
2832 2830 ('n', 'dry-run', None, _('do not perform actions, just print output'))],
2833 2831 _('hg addremove [OPTION]... [FILE]...')),
2834 2832 "^annotate":
2835 2833 (annotate,
2836 2834 [('r', 'rev', '', _('annotate the specified revision')),
2837 2835 ('a', 'text', None, _('treat all files as text')),
2838 2836 ('u', 'user', None, _('list the author')),
2839 2837 ('d', 'date', None, _('list the date')),
2840 2838 ('n', 'number', None, _('list the revision number (default)')),
2841 2839 ('c', 'changeset', None, _('list the changeset')),
2842 2840 ('I', 'include', [], _('include names matching the given patterns')),
2843 2841 ('X', 'exclude', [], _('exclude names matching the given patterns'))],
2844 2842 _('hg annotate [-r REV] [-a] [-u] [-d] [-n] [-c] FILE...')),
2845 2843 "archive":
2846 2844 (archive,
2847 2845 [('', 'no-decode', None, _('do not pass files through decoders')),
2848 2846 ('p', 'prefix', '', _('directory prefix for files in archive')),
2849 2847 ('r', 'rev', '', _('revision to distribute')),
2850 2848 ('t', 'type', '', _('type of distribution to create')),
2851 2849 ('I', 'include', [], _('include names matching the given patterns')),
2852 2850 ('X', 'exclude', [], _('exclude names matching the given patterns'))],
2853 2851 _('hg archive [OPTION]... DEST')),
2854 2852 "backout":
2855 2853 (backout,
2856 2854 [('', 'merge', None,
2857 2855 _('merge with old dirstate parent after backout')),
2858 2856 ('m', 'message', '', _('use <text> as commit message')),
2859 2857 ('l', 'logfile', '', _('read commit message from <file>')),
2860 2858 ('d', 'date', '', _('record datecode as commit date')),
2861 2859 ('u', 'user', '', _('record user as committer')),
2862 2860 ('I', 'include', [], _('include names matching the given patterns')),
2863 2861 ('X', 'exclude', [], _('exclude names matching the given patterns'))],
2864 2862 _('hg backout [OPTION]... REV')),
2865 2863 "bundle":
2866 2864 (bundle,
2867 2865 [('f', 'force', None,
2868 2866 _('run even when remote repository is unrelated'))],
2869 2867 _('hg bundle FILE DEST')),
2870 2868 "cat":
2871 2869 (cat,
2872 2870 [('o', 'output', '', _('print output to file with formatted name')),
2873 2871 ('r', 'rev', '', _('print the given revision')),
2874 2872 ('I', 'include', [], _('include names matching the given patterns')),
2875 2873 ('X', 'exclude', [], _('exclude names matching the given patterns'))],
2876 2874 _('hg cat [OPTION]... FILE...')),
2877 2875 "^clone":
2878 2876 (clone,
2879 2877 [('U', 'noupdate', None, _('do not update the new working directory')),
2880 2878 ('r', 'rev', [],
2881 2879 _('a changeset you would like to have after cloning')),
2882 2880 ('', 'pull', None, _('use pull protocol to copy metadata')),
2883 2881 ('e', 'ssh', '', _('specify ssh command to use')),
2884 2882 ('', 'remotecmd', '',
2885 2883 _('specify hg command to run on the remote side'))],
2886 2884 _('hg clone [OPTION]... SOURCE [DEST]')),
2887 2885 "^commit|ci":
2888 2886 (commit,
2889 2887 [('A', 'addremove', None,
2890 2888 _('mark new/missing files as added/removed before committing')),
2891 2889 ('m', 'message', '', _('use <text> as commit message')),
2892 2890 ('l', 'logfile', '', _('read the commit message from <file>')),
2893 2891 ('d', 'date', '', _('record datecode as commit date')),
2894 2892 ('u', 'user', '', _('record user as commiter')),
2895 2893 ('I', 'include', [], _('include names matching the given patterns')),
2896 2894 ('X', 'exclude', [], _('exclude names matching the given patterns'))],
2897 2895 _('hg commit [OPTION]... [FILE]...')),
2898 2896 "copy|cp":
2899 2897 (copy,
2900 2898 [('A', 'after', None, _('record a copy that has already occurred')),
2901 2899 ('f', 'force', None,
2902 2900 _('forcibly copy over an existing managed file')),
2903 2901 ('I', 'include', [], _('include names matching the given patterns')),
2904 2902 ('X', 'exclude', [], _('exclude names matching the given patterns')),
2905 2903 ('n', 'dry-run', None, _('do not perform actions, just print output'))],
2906 2904 _('hg copy [OPTION]... [SOURCE]... DEST')),
2907 2905 "debugancestor": (debugancestor, [], _('debugancestor INDEX REV1 REV2')),
2908 2906 "debugcomplete":
2909 2907 (debugcomplete,
2910 2908 [('o', 'options', None, _('show the command options'))],
2911 2909 _('debugcomplete [-o] CMD')),
2912 2910 "debugrebuildstate":
2913 2911 (debugrebuildstate,
2914 2912 [('r', 'rev', '', _('revision to rebuild to'))],
2915 2913 _('debugrebuildstate [-r REV] [REV]')),
2916 2914 "debugcheckstate": (debugcheckstate, [], _('debugcheckstate')),
2917 2915 "debugconfig": (debugconfig, [], _('debugconfig [NAME]...')),
2918 2916 "debugsetparents": (debugsetparents, [], _('debugsetparents REV1 [REV2]')),
2919 2917 "debugstate": (debugstate, [], _('debugstate')),
2920 2918 "debugdata": (debugdata, [], _('debugdata FILE REV')),
2921 2919 "debugindex": (debugindex, [], _('debugindex FILE')),
2922 2920 "debugindexdot": (debugindexdot, [], _('debugindexdot FILE')),
2923 2921 "debugrename": (debugrename, [], _('debugrename FILE [REV]')),
2924 2922 "debugwalk":
2925 2923 (debugwalk,
2926 2924 [('I', 'include', [], _('include names matching the given patterns')),
2927 2925 ('X', 'exclude', [], _('exclude names matching the given patterns'))],
2928 2926 _('debugwalk [OPTION]... [FILE]...')),
2929 2927 "^diff":
2930 2928 (diff,
2931 2929 [('r', 'rev', [], _('revision')),
2932 2930 ('a', 'text', None, _('treat all files as text')),
2933 2931 ('p', 'show-function', None,
2934 2932 _('show which function each change is in')),
2935 2933 ('w', 'ignore-all-space', None,
2936 2934 _('ignore white space when comparing lines')),
2937 2935 ('I', 'include', [], _('include names matching the given patterns')),
2938 2936 ('X', 'exclude', [], _('exclude names matching the given patterns'))],
2939 2937 _('hg diff [-a] [-I] [-X] [-r REV1 [-r REV2]] [FILE]...')),
2940 2938 "^export":
2941 2939 (export,
2942 2940 [('o', 'output', '', _('print output to file with formatted name')),
2943 2941 ('a', 'text', None, _('treat all files as text')),
2944 2942 ('', 'switch-parent', None, _('diff against the second parent'))],
2945 2943 _('hg export [-a] [-o OUTFILESPEC] REV...')),
2946 2944 "debugforget|forget":
2947 2945 (forget,
2948 2946 [('I', 'include', [], _('include names matching the given patterns')),
2949 2947 ('X', 'exclude', [], _('exclude names matching the given patterns'))],
2950 2948 _('hg forget [OPTION]... FILE...')),
2951 2949 "grep":
2952 2950 (grep,
2953 2951 [('0', 'print0', None, _('end fields with NUL')),
2954 2952 ('', 'all', None, _('print all revisions that match')),
2955 2953 ('i', 'ignore-case', None, _('ignore case when matching')),
2956 2954 ('l', 'files-with-matches', None,
2957 2955 _('print only filenames and revs that match')),
2958 2956 ('n', 'line-number', None, _('print matching line numbers')),
2959 2957 ('r', 'rev', [], _('search in given revision range')),
2960 2958 ('u', 'user', None, _('print user who committed change')),
2961 2959 ('I', 'include', [], _('include names matching the given patterns')),
2962 2960 ('X', 'exclude', [], _('exclude names matching the given patterns'))],
2963 2961 _('hg grep [OPTION]... PATTERN [FILE]...')),
2964 2962 "heads":
2965 2963 (heads,
2966 2964 [('b', 'branches', None, _('show branches')),
2967 2965 ('', 'style', '', _('display using template map file')),
2968 2966 ('r', 'rev', '', _('show only heads which are descendants of rev')),
2969 2967 ('', 'template', '', _('display with template'))],
2970 2968 _('hg heads [-b] [-r <rev>]')),
2971 2969 "help": (help_, [], _('hg help [COMMAND]')),
2972 2970 "identify|id": (identify, [], _('hg identify')),
2973 2971 "import|patch":
2974 2972 (import_,
2975 2973 [('p', 'strip', 1,
2976 2974 _('directory strip option for patch. This has the same\n'
2977 2975 'meaning as the corresponding patch option')),
2978 2976 ('m', 'message', '', _('use <text> as commit message')),
2979 2977 ('b', 'base', '', _('base path')),
2980 2978 ('f', 'force', None,
2981 2979 _('skip check for outstanding uncommitted changes'))],
2982 2980 _('hg import [-p NUM] [-b BASE] [-m MESSAGE] [-f] PATCH...')),
2983 2981 "incoming|in": (incoming,
2984 2982 [('M', 'no-merges', None, _('do not show merges')),
2985 2983 ('f', 'force', None,
2986 2984 _('run even when remote repository is unrelated')),
2987 2985 ('', 'style', '', _('display using template map file')),
2988 2986 ('n', 'newest-first', None, _('show newest record first')),
2989 2987 ('', 'bundle', '', _('file to store the bundles into')),
2990 2988 ('p', 'patch', None, _('show patch')),
2991 2989 ('', 'template', '', _('display with template')),
2992 2990 ('e', 'ssh', '', _('specify ssh command to use')),
2993 2991 ('', 'remotecmd', '',
2994 2992 _('specify hg command to run on the remote side'))],
2995 2993 _('hg incoming [-p] [-n] [-M] [--bundle FILENAME] [SOURCE]')),
2996 2994 "^init": (init, [], _('hg init [DEST]')),
2997 2995 "locate":
2998 2996 (locate,
2999 2997 [('r', 'rev', '', _('search the repository as it stood at rev')),
3000 2998 ('0', 'print0', None,
3001 2999 _('end filenames with NUL, for use with xargs')),
3002 3000 ('f', 'fullpath', None,
3003 3001 _('print complete paths from the filesystem root')),
3004 3002 ('I', 'include', [], _('include names matching the given patterns')),
3005 3003 ('X', 'exclude', [], _('exclude names matching the given patterns'))],
3006 3004 _('hg locate [OPTION]... [PATTERN]...')),
3007 3005 "^log|history":
3008 3006 (log,
3009 3007 [('b', 'branches', None, _('show branches')),
3010 3008 ('k', 'keyword', [], _('search for a keyword')),
3011 3009 ('l', 'limit', '', _('limit number of changes displayed')),
3012 3010 ('r', 'rev', [], _('show the specified revision or range')),
3013 3011 ('M', 'no-merges', None, _('do not show merges')),
3014 3012 ('', 'style', '', _('display using template map file')),
3015 3013 ('m', 'only-merges', None, _('show only merges')),
3016 3014 ('p', 'patch', None, _('show patch')),
3017 3015 ('', 'template', '', _('display with template')),
3018 3016 ('I', 'include', [], _('include names matching the given patterns')),
3019 3017 ('X', 'exclude', [], _('exclude names matching the given patterns'))],
3020 3018 _('hg log [OPTION]... [FILE]')),
3021 3019 "manifest": (manifest, [], _('hg manifest [REV]')),
3022 3020 "merge":
3023 3021 (merge,
3024 3022 [('b', 'branch', '', _('merge with head of a specific branch')),
3025 3023 ('f', 'force', None, _('force a merge with outstanding changes'))],
3026 3024 _('hg merge [-b TAG] [-f] [REV]')),
3027 3025 "outgoing|out": (outgoing,
3028 3026 [('M', 'no-merges', None, _('do not show merges')),
3029 3027 ('f', 'force', None,
3030 3028 _('run even when remote repository is unrelated')),
3031 3029 ('p', 'patch', None, _('show patch')),
3032 3030 ('', 'style', '', _('display using template map file')),
3033 3031 ('n', 'newest-first', None, _('show newest record first')),
3034 3032 ('', 'template', '', _('display with template')),
3035 3033 ('e', 'ssh', '', _('specify ssh command to use')),
3036 3034 ('', 'remotecmd', '',
3037 3035 _('specify hg command to run on the remote side'))],
3038 3036 _('hg outgoing [-M] [-p] [-n] [DEST]')),
3039 3037 "^parents":
3040 3038 (parents,
3041 3039 [('b', 'branches', None, _('show branches')),
3042 3040 ('', 'style', '', _('display using template map file')),
3043 3041 ('', 'template', '', _('display with template'))],
3044 3042 _('hg parents [-b] [REV]')),
3045 3043 "paths": (paths, [], _('hg paths [NAME]')),
3046 3044 "^pull":
3047 3045 (pull,
3048 3046 [('u', 'update', None,
3049 3047 _('update the working directory to tip after pull')),
3050 3048 ('e', 'ssh', '', _('specify ssh command to use')),
3051 3049 ('f', 'force', None,
3052 3050 _('run even when remote repository is unrelated')),
3053 3051 ('r', 'rev', [], _('a specific revision you would like to pull')),
3054 3052 ('', 'remotecmd', '',
3055 3053 _('specify hg command to run on the remote side'))],
3056 3054 _('hg pull [-u] [-e FILE] [-r REV]... [--remotecmd FILE] [SOURCE]')),
3057 3055 "^push":
3058 3056 (push,
3059 3057 [('f', 'force', None, _('force push')),
3060 3058 ('e', 'ssh', '', _('specify ssh command to use')),
3061 3059 ('r', 'rev', [], _('a specific revision you would like to push')),
3062 3060 ('', 'remotecmd', '',
3063 3061 _('specify hg command to run on the remote side'))],
3064 3062 _('hg push [-f] [-e FILE] [-r REV]... [--remotecmd FILE] [DEST]')),
3065 3063 "debugrawcommit|rawcommit":
3066 3064 (rawcommit,
3067 3065 [('p', 'parent', [], _('parent')),
3068 3066 ('d', 'date', '', _('date code')),
3069 3067 ('u', 'user', '', _('user')),
3070 3068 ('F', 'files', '', _('file list')),
3071 3069 ('m', 'message', '', _('commit message')),
3072 3070 ('l', 'logfile', '', _('commit message file'))],
3073 3071 _('hg debugrawcommit [OPTION]... [FILE]...')),
3074 3072 "recover": (recover, [], _('hg recover')),
3075 3073 "^remove|rm":
3076 3074 (remove,
3077 3075 [('A', 'after', None, _('record remove that has already occurred')),
3078 3076 ('f', 'force', None, _('remove file even if modified')),
3079 3077 ('I', 'include', [], _('include names matching the given patterns')),
3080 3078 ('X', 'exclude', [], _('exclude names matching the given patterns'))],
3081 3079 _('hg remove [OPTION]... FILE...')),
3082 3080 "rename|mv":
3083 3081 (rename,
3084 3082 [('A', 'after', None, _('record a rename that has already occurred')),
3085 3083 ('f', 'force', None,
3086 3084 _('forcibly copy over an existing managed file')),
3087 3085 ('I', 'include', [], _('include names matching the given patterns')),
3088 3086 ('X', 'exclude', [], _('exclude names matching the given patterns')),
3089 3087 ('n', 'dry-run', None, _('do not perform actions, just print output'))],
3090 3088 _('hg rename [OPTION]... SOURCE... DEST')),
3091 3089 "^revert":
3092 3090 (revert,
3093 3091 [('r', 'rev', '', _('revision to revert to')),
3094 3092 ('', 'no-backup', None, _('do not save backup copies of files')),
3095 3093 ('I', 'include', [], _('include names matching given patterns')),
3096 3094 ('X', 'exclude', [], _('exclude names matching given patterns')),
3097 3095 ('n', 'dry-run', None, _('do not perform actions, just print output'))],
3098 3096 _('hg revert [-r REV] [NAME]...')),
3099 3097 "rollback": (rollback, [], _('hg rollback')),
3100 3098 "root": (root, [], _('hg root')),
3101 3099 "^serve":
3102 3100 (serve,
3103 3101 [('A', 'accesslog', '', _('name of access log file to write to')),
3104 3102 ('d', 'daemon', None, _('run server in background')),
3105 3103 ('', 'daemon-pipefds', '', _('used internally by daemon mode')),
3106 3104 ('E', 'errorlog', '', _('name of error log file to write to')),
3107 3105 ('p', 'port', 0, _('port to use (default: 8000)')),
3108 3106 ('a', 'address', '', _('address to use')),
3109 3107 ('n', 'name', '',
3110 3108 _('name to show in web pages (default: working dir)')),
3111 3109 ('', 'webdir-conf', '', _('name of the webdir config file'
3112 3110 ' (serve more than one repo)')),
3113 3111 ('', 'pid-file', '', _('name of file to write process ID to')),
3114 3112 ('', 'stdio', None, _('for remote clients')),
3115 3113 ('t', 'templates', '', _('web templates to use')),
3116 3114 ('', 'style', '', _('template style to use')),
3117 3115 ('6', 'ipv6', None, _('use IPv6 in addition to IPv4'))],
3118 3116 _('hg serve [OPTION]...')),
3119 3117 "^status|st":
3120 3118 (status,
3121 3119 [('m', 'modified', None, _('show only modified files')),
3122 3120 ('a', 'added', None, _('show only added files')),
3123 3121 ('r', 'removed', None, _('show only removed files')),
3124 3122 ('d', 'deleted', None, _('show only deleted (but tracked) files')),
3125 3123 ('u', 'unknown', None, _('show only unknown (not tracked) files')),
3126 3124 ('i', 'ignored', None, _('show ignored files')),
3127 3125 ('n', 'no-status', None, _('hide status prefix')),
3128 3126 ('0', 'print0', None,
3129 3127 _('end filenames with NUL, for use with xargs')),
3130 3128 ('I', 'include', [], _('include names matching the given patterns')),
3131 3129 ('X', 'exclude', [], _('exclude names matching the given patterns'))],
3132 3130 _('hg status [OPTION]... [FILE]...')),
3133 3131 "tag":
3134 3132 (tag,
3135 3133 [('l', 'local', None, _('make the tag local')),
3136 3134 ('m', 'message', '', _('message for tag commit log entry')),
3137 3135 ('d', 'date', '', _('record datecode as commit date')),
3138 3136 ('u', 'user', '', _('record user as commiter')),
3139 3137 ('r', 'rev', '', _('revision to tag'))],
3140 3138 _('hg tag [-l] [-m TEXT] [-d DATE] [-u USER] [-r REV] NAME')),
3141 3139 "tags": (tags, [], _('hg tags')),
3142 3140 "tip":
3143 3141 (tip,
3144 3142 [('b', 'branches', None, _('show branches')),
3145 3143 ('', 'style', '', _('display using template map file')),
3146 3144 ('p', 'patch', None, _('show patch')),
3147 3145 ('', 'template', '', _('display with template'))],
3148 3146 _('hg tip [-b] [-p]')),
3149 3147 "unbundle":
3150 3148 (unbundle,
3151 3149 [('u', 'update', None,
3152 3150 _('update the working directory to tip after unbundle'))],
3153 3151 _('hg unbundle [-u] FILE')),
3154 3152 "debugundo|undo": (undo, [], _('hg undo')),
3155 3153 "^update|up|checkout|co":
3156 3154 (update,
3157 3155 [('b', 'branch', '', _('checkout the head of a specific branch')),
3158 3156 ('m', 'merge', None, _('allow merging of branches (DEPRECATED)')),
3159 3157 ('C', 'clean', None, _('overwrite locally modified files')),
3160 3158 ('f', 'force', None, _('force a merge with outstanding changes'))],
3161 3159 _('hg update [-b TAG] [-m] [-C] [-f] [REV]')),
3162 3160 "verify": (verify, [], _('hg verify')),
3163 3161 "version": (show_version, [], _('hg version')),
3164 3162 }
3165 3163
3166 3164 globalopts = [
3167 3165 ('R', 'repository', '',
3168 3166 _('repository root directory or symbolic path name')),
3169 3167 ('', 'cwd', '', _('change working directory')),
3170 3168 ('y', 'noninteractive', None,
3171 3169 _('do not prompt, assume \'yes\' for any required answers')),
3172 3170 ('q', 'quiet', None, _('suppress output')),
3173 3171 ('v', 'verbose', None, _('enable additional output')),
3174 3172 ('', 'config', [], _('set/override config option')),
3175 3173 ('', 'debug', None, _('enable debugging output')),
3176 3174 ('', 'debugger', None, _('start debugger')),
3177 3175 ('', 'lsprof', None, _('print improved command execution profile')),
3178 3176 ('', 'traceback', None, _('print traceback on exception')),
3179 3177 ('', 'time', None, _('time how long the command takes')),
3180 3178 ('', 'profile', None, _('print command execution profile')),
3181 3179 ('', 'version', None, _('output version information and exit')),
3182 3180 ('h', 'help', None, _('display help and exit')),
3183 3181 ]
3184 3182
3185 3183 norepo = ("clone init version help debugancestor debugcomplete debugdata"
3186 3184 " debugindex debugindexdot")
3187 3185 optionalrepo = ("paths serve debugconfig")
3188 3186
3189 3187 def findpossible(cmd):
3190 3188 """
3191 3189 Return cmd -> (aliases, command table entry)
3192 3190 for each matching command.
3193 3191 Return debug commands (or their aliases) only if no normal command matches.
3194 3192 """
3195 3193 choice = {}
3196 3194 debugchoice = {}
3197 3195 for e in table.keys():
3198 3196 aliases = e.lstrip("^").split("|")
3199 3197 found = None
3200 3198 if cmd in aliases:
3201 3199 found = cmd
3202 3200 else:
3203 3201 for a in aliases:
3204 3202 if a.startswith(cmd):
3205 3203 found = a
3206 3204 break
3207 3205 if found is not None:
3208 3206 if aliases[0].startswith("debug"):
3209 3207 debugchoice[found] = (aliases, table[e])
3210 3208 else:
3211 3209 choice[found] = (aliases, table[e])
3212 3210
3213 3211 if not choice and debugchoice:
3214 3212 choice = debugchoice
3215 3213
3216 3214 return choice
3217 3215
3218 3216 def find(cmd):
3219 3217 """Return (aliases, command table entry) for command string."""
3220 3218 choice = findpossible(cmd)
3221 3219
3222 3220 if choice.has_key(cmd):
3223 3221 return choice[cmd]
3224 3222
3225 3223 if len(choice) > 1:
3226 3224 clist = choice.keys()
3227 3225 clist.sort()
3228 3226 raise AmbiguousCommand(cmd, clist)
3229 3227
3230 3228 if choice:
3231 3229 return choice.values()[0]
3232 3230
3233 3231 raise UnknownCommand(cmd)
3234 3232
3235 3233 def catchterm(*args):
3236 3234 raise util.SignalInterrupt
3237 3235
3238 3236 def run():
3239 3237 sys.exit(dispatch(sys.argv[1:]))
3240 3238
3241 3239 class ParseError(Exception):
3242 3240 """Exception raised on errors in parsing the command line."""
3243 3241
3244 3242 def parse(ui, args):
3245 3243 options = {}
3246 3244 cmdoptions = {}
3247 3245
3248 3246 try:
3249 3247 args = fancyopts.fancyopts(args, globalopts, options)
3250 3248 except fancyopts.getopt.GetoptError, inst:
3251 3249 raise ParseError(None, inst)
3252 3250
3253 3251 if args:
3254 3252 cmd, args = args[0], args[1:]
3255 3253 aliases, i = find(cmd)
3256 3254 cmd = aliases[0]
3257 3255 defaults = ui.config("defaults", cmd)
3258 3256 if defaults:
3259 3257 args = defaults.split() + args
3260 3258 c = list(i[1])
3261 3259 else:
3262 3260 cmd = None
3263 3261 c = []
3264 3262
3265 3263 # combine global options into local
3266 3264 for o in globalopts:
3267 3265 c.append((o[0], o[1], options[o[1]], o[3]))
3268 3266
3269 3267 try:
3270 3268 args = fancyopts.fancyopts(args, c, cmdoptions)
3271 3269 except fancyopts.getopt.GetoptError, inst:
3272 3270 raise ParseError(cmd, inst)
3273 3271
3274 3272 # separate global options back out
3275 3273 for o in globalopts:
3276 3274 n = o[1]
3277 3275 options[n] = cmdoptions[n]
3278 3276 del cmdoptions[n]
3279 3277
3280 3278 return (cmd, cmd and i[0] or None, args, options, cmdoptions)
3281 3279
3282 3280 def dispatch(args):
3283 3281 for name in 'SIGBREAK', 'SIGHUP', 'SIGTERM':
3284 3282 num = getattr(signal, name, None)
3285 3283 if num: signal.signal(num, catchterm)
3286 3284
3287 3285 try:
3288 3286 u = ui.ui(traceback='--traceback' in sys.argv[1:])
3289 3287 except util.Abort, inst:
3290 3288 sys.stderr.write(_("abort: %s\n") % inst)
3291 3289 return -1
3292 3290
3293 3291 external = []
3294 3292 for x in u.extensions():
3295 3293 try:
3296 3294 if x[1]:
3297 3295 # the module will be loaded in sys.modules
3298 3296 # choose an unique name so that it doesn't
3299 3297 # conflicts with other modules
3300 3298 module_name = "hgext_%s" % x[0].replace('.', '_')
3301 3299 mod = imp.load_source(module_name, x[1])
3302 3300 else:
3303 3301 def importh(name):
3304 3302 mod = __import__(name)
3305 3303 components = name.split('.')
3306 3304 for comp in components[1:]:
3307 3305 mod = getattr(mod, comp)
3308 3306 return mod
3309 3307 try:
3310 3308 mod = importh("hgext." + x[0])
3311 3309 except ImportError:
3312 3310 mod = importh(x[0])
3313 3311 external.append(mod)
3314 3312 except (util.SignalInterrupt, KeyboardInterrupt):
3315 3313 raise
3316 3314 except Exception, inst:
3317 3315 u.warn(_("*** failed to import extension %s: %s\n") % (x[0], inst))
3318 3316 if u.print_exc():
3319 3317 return 1
3320 3318
3321 3319 for x in external:
3322 3320 uisetup = getattr(x, 'uisetup', None)
3323 3321 if uisetup:
3324 3322 uisetup(u)
3325 3323 cmdtable = getattr(x, 'cmdtable', {})
3326 3324 for t in cmdtable:
3327 3325 if t in table:
3328 3326 u.warn(_("module %s overrides %s\n") % (x.__name__, t))
3329 3327 table.update(cmdtable)
3330 3328
3331 3329 try:
3332 3330 cmd, func, args, options, cmdoptions = parse(u, args)
3333 3331 if options["time"]:
3334 3332 def get_times():
3335 3333 t = os.times()
3336 3334 if t[4] == 0.0: # Windows leaves this as zero, so use time.clock()
3337 3335 t = (t[0], t[1], t[2], t[3], time.clock())
3338 3336 return t
3339 3337 s = get_times()
3340 3338 def print_time():
3341 3339 t = get_times()
3342 3340 u.warn(_("Time: real %.3f secs (user %.3f+%.3f sys %.3f+%.3f)\n") %
3343 3341 (t[4]-s[4], t[0]-s[0], t[2]-s[2], t[1]-s[1], t[3]-s[3]))
3344 3342 atexit.register(print_time)
3345 3343
3346 3344 u.updateopts(options["verbose"], options["debug"], options["quiet"],
3347 3345 not options["noninteractive"], options["traceback"],
3348 3346 options["config"])
3349 3347
3350 3348 # enter the debugger before command execution
3351 3349 if options['debugger']:
3352 3350 pdb.set_trace()
3353 3351
3354 3352 try:
3355 3353 if options['cwd']:
3356 3354 try:
3357 3355 os.chdir(options['cwd'])
3358 3356 except OSError, inst:
3359 3357 raise util.Abort('%s: %s' %
3360 3358 (options['cwd'], inst.strerror))
3361 3359
3362 3360 path = u.expandpath(options["repository"]) or ""
3363 3361 repo = path and hg.repository(u, path=path) or None
3364 3362
3365 3363 if options['help']:
3366 3364 return help_(u, cmd, options['version'])
3367 3365 elif options['version']:
3368 3366 return show_version(u)
3369 3367 elif not cmd:
3370 3368 return help_(u, 'shortlist')
3371 3369
3372 3370 if cmd not in norepo.split():
3373 3371 try:
3374 3372 if not repo:
3375 3373 repo = hg.repository(u, path=path)
3376 3374 u = repo.ui
3377 3375 for x in external:
3378 3376 if hasattr(x, 'reposetup'):
3379 3377 x.reposetup(u, repo)
3380 3378 except hg.RepoError:
3381 3379 if cmd not in optionalrepo.split():
3382 3380 raise
3383 3381 d = lambda: func(u, repo, *args, **cmdoptions)
3384 3382 else:
3385 3383 d = lambda: func(u, *args, **cmdoptions)
3386 3384
3387 3385 try:
3388 3386 if options['profile']:
3389 3387 import hotshot, hotshot.stats
3390 3388 prof = hotshot.Profile("hg.prof")
3391 3389 try:
3392 3390 try:
3393 3391 return prof.runcall(d)
3394 3392 except:
3395 3393 try:
3396 3394 u.warn(_('exception raised - generating '
3397 3395 'profile anyway\n'))
3398 3396 except:
3399 3397 pass
3400 3398 raise
3401 3399 finally:
3402 3400 prof.close()
3403 3401 stats = hotshot.stats.load("hg.prof")
3404 3402 stats.strip_dirs()
3405 3403 stats.sort_stats('time', 'calls')
3406 3404 stats.print_stats(40)
3407 3405 elif options['lsprof']:
3408 3406 try:
3409 3407 from mercurial import lsprof
3410 3408 except ImportError:
3411 3409 raise util.Abort(_(
3412 3410 'lsprof not available - install from '
3413 3411 'http://codespeak.net/svn/user/arigo/hack/misc/lsprof/'))
3414 3412 p = lsprof.Profiler()
3415 3413 p.enable(subcalls=True)
3416 3414 try:
3417 3415 return d()
3418 3416 finally:
3419 3417 p.disable()
3420 3418 stats = lsprof.Stats(p.getstats())
3421 3419 stats.sort()
3422 3420 stats.pprint(top=10, file=sys.stderr, climit=5)
3423 3421 else:
3424 3422 return d()
3425 3423 finally:
3426 3424 u.flush()
3427 3425 except:
3428 3426 # enter the debugger when we hit an exception
3429 3427 if options['debugger']:
3430 3428 pdb.post_mortem(sys.exc_info()[2])
3431 3429 u.print_exc()
3432 3430 raise
3433 3431 except ParseError, inst:
3434 3432 if inst.args[0]:
3435 3433 u.warn(_("hg %s: %s\n") % (inst.args[0], inst.args[1]))
3436 3434 help_(u, inst.args[0])
3437 3435 else:
3438 3436 u.warn(_("hg: %s\n") % inst.args[1])
3439 3437 help_(u, 'shortlist')
3440 3438 except AmbiguousCommand, inst:
3441 3439 u.warn(_("hg: command '%s' is ambiguous:\n %s\n") %
3442 3440 (inst.args[0], " ".join(inst.args[1])))
3443 3441 except UnknownCommand, inst:
3444 3442 u.warn(_("hg: unknown command '%s'\n") % inst.args[0])
3445 3443 help_(u, 'shortlist')
3446 3444 except hg.RepoError, inst:
3447 3445 u.warn(_("abort: %s!\n") % inst)
3448 3446 except lock.LockHeld, inst:
3449 3447 if inst.errno == errno.ETIMEDOUT:
3450 3448 reason = _('timed out waiting for lock held by %s') % inst.locker
3451 3449 else:
3452 3450 reason = _('lock held by %s') % inst.locker
3453 3451 u.warn(_("abort: %s: %s\n") % (inst.desc or inst.filename, reason))
3454 3452 except lock.LockUnavailable, inst:
3455 3453 u.warn(_("abort: could not lock %s: %s\n") %
3456 3454 (inst.desc or inst.filename, inst.strerror))
3457 3455 except revlog.RevlogError, inst:
3458 3456 u.warn(_("abort: "), inst, "!\n")
3459 3457 except util.SignalInterrupt:
3460 3458 u.warn(_("killed!\n"))
3461 3459 except KeyboardInterrupt:
3462 3460 try:
3463 3461 u.warn(_("interrupted!\n"))
3464 3462 except IOError, inst:
3465 3463 if inst.errno == errno.EPIPE:
3466 3464 if u.debugflag:
3467 3465 u.warn(_("\nbroken pipe\n"))
3468 3466 else:
3469 3467 raise
3470 3468 except IOError, inst:
3471 3469 if hasattr(inst, "code"):
3472 3470 u.warn(_("abort: %s\n") % inst)
3473 3471 elif hasattr(inst, "reason"):
3474 3472 u.warn(_("abort: error: %s\n") % inst.reason[1])
3475 3473 elif hasattr(inst, "args") and inst[0] == errno.EPIPE:
3476 3474 if u.debugflag:
3477 3475 u.warn(_("broken pipe\n"))
3478 3476 elif getattr(inst, "strerror", None):
3479 3477 if getattr(inst, "filename", None):
3480 3478 u.warn(_("abort: %s - %s\n") % (inst.strerror, inst.filename))
3481 3479 else:
3482 3480 u.warn(_("abort: %s\n") % inst.strerror)
3483 3481 else:
3484 3482 raise
3485 3483 except OSError, inst:
3486 3484 if hasattr(inst, "filename"):
3487 3485 u.warn(_("abort: %s: %s\n") % (inst.strerror, inst.filename))
3488 3486 else:
3489 3487 u.warn(_("abort: %s\n") % inst.strerror)
3490 3488 except util.Abort, inst:
3491 3489 u.warn(_('abort: '), inst.args[0] % inst.args[1:], '\n')
3492 3490 except TypeError, inst:
3493 3491 # was this an argument error?
3494 3492 tb = traceback.extract_tb(sys.exc_info()[2])
3495 3493 if len(tb) > 2: # no
3496 3494 raise
3497 3495 u.debug(inst, "\n")
3498 3496 u.warn(_("%s: invalid arguments\n") % cmd)
3499 3497 help_(u, cmd)
3500 3498 except SystemExit, inst:
3501 3499 # Commands shouldn't sys.exit directly, but give a return code.
3502 3500 # Just in case catch this and and pass exit code to caller.
3503 3501 return inst.code
3504 3502 except:
3505 3503 u.warn(_("** unknown exception encountered, details follow\n"))
3506 3504 u.warn(_("** report bug details to mercurial@selenic.com\n"))
3507 3505 u.warn(_("** Mercurial Distributed SCM (version %s)\n")
3508 3506 % version.get_version())
3509 3507 raise
3510 3508
3511 3509 return -1
General Comments 0
You need to be logged in to leave comments. Login now