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