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