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