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