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