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