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