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