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