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