##// END OF EJS Templates
Deal with non-integer date offsets generated by some tools
mpm@selenic.com -
r973:a51991eb default
parent child Browse files
Show More
@@ -1,1618 +1,1623 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 demandload(globals(), "os re sys signal shutil")
10 10 demandload(globals(), "fancyopts ui hg util lock")
11 11 demandload(globals(), "fnmatch hgweb mdiff random signal time traceback")
12 12 demandload(globals(), "errno socket version struct atexit")
13 13
14 14 class UnknownCommand(Exception):
15 15 """Exception raised if command is not in the command table."""
16 16
17 17 def filterfiles(filters, files):
18 18 l = [x for x in files if x in filters]
19 19
20 20 for t in filters:
21 21 if t and t[-1] != "/":
22 22 t += "/"
23 23 l += [x for x in files if x.startswith(t)]
24 24 return l
25 25
26 26 def relfilter(repo, files):
27 27 cwd = repo.getcwd()
28 28 if cwd:
29 29 return filterfiles([util.pconvert(cwd)], files)
30 30 return files
31 31
32 32 def relpath(repo, args):
33 33 cwd = repo.getcwd()
34 34 if cwd:
35 35 return [util.normpath(os.path.join(cwd, x)) for x in args]
36 36 return args
37 37
38 38 def matchpats(repo, cwd, pats = [], opts = {}, head = ''):
39 39 return util.matcher(repo, cwd, pats or ['.'], opts.get('include'),
40 40 opts.get('exclude'), head)
41 41
42 42 def makewalk(repo, pats, opts, head = ''):
43 43 cwd = repo.getcwd()
44 44 files, matchfn = matchpats(repo, cwd, pats, opts, head)
45 45 exact = dict(zip(files, files))
46 46 def walk():
47 47 for src, fn in repo.walk(files = files, match = matchfn):
48 48 yield src, fn, util.pathto(cwd, fn), fn in exact
49 49 return files, matchfn, walk()
50 50
51 51 def walk(repo, pats, opts, head = ''):
52 52 files, matchfn, results = makewalk(repo, pats, opts, head)
53 53 for r in results: yield r
54 54
55 55 revrangesep = ':'
56 56
57 57 def revrange(ui, repo, revs, revlog=None):
58 58 if revlog is None:
59 59 revlog = repo.changelog
60 60 revcount = revlog.count()
61 61 def fix(val, defval):
62 62 if not val:
63 63 return defval
64 64 try:
65 65 num = int(val)
66 66 if str(num) != val:
67 67 raise ValueError
68 68 if num < 0:
69 69 num += revcount
70 70 if not (0 <= num < revcount):
71 71 raise ValueError
72 72 except ValueError:
73 73 try:
74 74 num = repo.changelog.rev(repo.lookup(val))
75 75 except KeyError:
76 76 try:
77 77 num = revlog.rev(revlog.lookup(val))
78 78 except KeyError:
79 79 raise util.Abort('invalid revision identifier %s', val)
80 80 return num
81 81 for spec in revs:
82 82 if spec.find(revrangesep) >= 0:
83 83 start, end = spec.split(revrangesep, 1)
84 84 start = fix(start, 0)
85 85 end = fix(end, revcount - 1)
86 86 if end > start:
87 87 end += 1
88 88 step = 1
89 89 else:
90 90 end -= 1
91 91 step = -1
92 92 for rev in xrange(start, end, step):
93 93 yield str(rev)
94 94 else:
95 95 yield spec
96 96
97 97 def make_filename(repo, r, pat, node=None,
98 98 total=None, seqno=None, revwidth=None):
99 99 node_expander = {
100 100 'H': lambda: hg.hex(node),
101 101 'R': lambda: str(r.rev(node)),
102 102 'h': lambda: hg.short(node),
103 103 }
104 104 expander = {
105 105 '%': lambda: '%',
106 106 'b': lambda: os.path.basename(repo.root),
107 107 }
108 108
109 109 try:
110 110 if node:
111 111 expander.update(node_expander)
112 112 if node and revwidth is not None:
113 113 expander['r'] = lambda: str(r.rev(node)).zfill(revwidth)
114 114 if total is not None:
115 115 expander['N'] = lambda: str(total)
116 116 if seqno is not None:
117 117 expander['n'] = lambda: str(seqno)
118 118 if total is not None and seqno is not None:
119 119 expander['n'] = lambda:str(seqno).zfill(len(str(total)))
120 120
121 121 newname = []
122 122 patlen = len(pat)
123 123 i = 0
124 124 while i < patlen:
125 125 c = pat[i]
126 126 if c == '%':
127 127 i += 1
128 128 c = pat[i]
129 129 c = expander[c]()
130 130 newname.append(c)
131 131 i += 1
132 132 return ''.join(newname)
133 133 except KeyError, inst:
134 134 raise util.Abort("invalid format spec '%%%s' in output file name",
135 135 inst.args[0])
136 136
137 137 def make_file(repo, r, pat, node=None,
138 138 total=None, seqno=None, revwidth=None, mode='wb'):
139 139 if not pat or pat == '-':
140 140 if 'w' in mode: return sys.stdout
141 141 else: return sys.stdin
142 142 if hasattr(pat, 'write') and 'w' in mode:
143 143 return pat
144 144 if hasattr(pat, 'read') and 'r' in mode:
145 145 return pat
146 146 return open(make_filename(repo, r, pat, node, total, seqno, revwidth),
147 147 mode)
148 148
149 149 def dodiff(fp, ui, repo, files=None, node1=None, node2=None, match=util.always, changes=None):
150 150 def date(c):
151 151 return time.asctime(time.gmtime(float(c[2].split(' ')[0])))
152 152
153 153 if not changes:
154 154 (c, a, d, u) = repo.changes(node1, node2, files, match = match)
155 155 else:
156 156 (c, a, d, u) = changes
157 157 if files:
158 158 c, a, d = map(lambda x: filterfiles(files, x), (c, a, d))
159 159
160 160 if not c and not a and not d:
161 161 return
162 162
163 163 if node2:
164 164 change = repo.changelog.read(node2)
165 165 mmap2 = repo.manifest.read(change[0])
166 166 date2 = date(change)
167 167 def read(f):
168 168 return repo.file(f).read(mmap2[f])
169 169 else:
170 170 date2 = time.asctime()
171 171 if not node1:
172 172 node1 = repo.dirstate.parents()[0]
173 173 def read(f):
174 174 return repo.wfile(f).read()
175 175
176 176 if ui.quiet:
177 177 r = None
178 178 else:
179 179 hexfunc = ui.verbose and hg.hex or hg.short
180 180 r = [hexfunc(node) for node in [node1, node2] if node]
181 181
182 182 change = repo.changelog.read(node1)
183 183 mmap = repo.manifest.read(change[0])
184 184 date1 = date(change)
185 185
186 186 for f in c:
187 187 to = None
188 188 if f in mmap:
189 189 to = repo.file(f).read(mmap[f])
190 190 tn = read(f)
191 191 fp.write(mdiff.unidiff(to, date1, tn, date2, f, r))
192 192 for f in a:
193 193 to = None
194 194 tn = read(f)
195 195 fp.write(mdiff.unidiff(to, date1, tn, date2, f, r))
196 196 for f in d:
197 197 to = repo.file(f).read(mmap[f])
198 198 tn = None
199 199 fp.write(mdiff.unidiff(to, date1, tn, date2, f, r))
200 200
201 201 def show_changeset(ui, repo, rev=0, changenode=None, filelog=None, brinfo=None):
202 202 """show a single changeset or file revision"""
203 203 changelog = repo.changelog
204 204 if filelog:
205 205 log = filelog
206 206 filerev = rev
207 207 node = filenode = filelog.node(filerev)
208 208 changerev = filelog.linkrev(filenode)
209 209 changenode = changenode or changelog.node(changerev)
210 210 else:
211 211 log = changelog
212 212 changerev = rev
213 213 if changenode is None:
214 214 changenode = changelog.node(changerev)
215 215 elif not changerev:
216 216 rev = changerev = changelog.rev(changenode)
217 217 node = changenode
218 218
219 219 if ui.quiet:
220 220 ui.write("%d:%s\n" % (rev, hg.short(node)))
221 221 return
222 222
223 223 changes = changelog.read(changenode)
224 224
225 225 t, tz = changes[2].split(' ')
226 # a conversion tool was sticking non-integer offsets into repos
227 try:
228 tz = int(tz)
229 except ValueError:
230 tz = 0
226 231 date = time.asctime(time.localtime(float(t))) + " %+05d" % (int(tz)/-36)
227 232
228 233 parents = [(log.rev(p), ui.verbose and hg.hex(p) or hg.short(p))
229 234 for p in log.parents(node)
230 235 if ui.debugflag or p != hg.nullid]
231 236 if not ui.debugflag and len(parents) == 1 and parents[0][0] == rev-1:
232 237 parents = []
233 238
234 239 if ui.verbose:
235 240 ui.write("changeset: %d:%s\n" % (changerev, hg.hex(changenode)))
236 241 else:
237 242 ui.write("changeset: %d:%s\n" % (changerev, hg.short(changenode)))
238 243
239 244 for tag in repo.nodetags(changenode):
240 245 ui.status("tag: %s\n" % tag)
241 246 for parent in parents:
242 247 ui.write("parent: %d:%s\n" % parent)
243 248 if filelog:
244 249 ui.debug("file rev: %d:%s\n" % (filerev, hg.hex(filenode)))
245 250
246 251 if brinfo and changenode in brinfo:
247 252 br = brinfo[changenode]
248 253 ui.write("branch: %s\n" % " ".join(br))
249 254
250 255 ui.debug("manifest: %d:%s\n" % (repo.manifest.rev(changes[0]),
251 256 hg.hex(changes[0])))
252 257 ui.status("user: %s\n" % changes[1])
253 258 ui.status("date: %s\n" % date)
254 259
255 260 if ui.debugflag:
256 261 files = repo.changes(changelog.parents(changenode)[0], changenode)
257 262 for key, value in zip(["files:", "files+:", "files-:"], files):
258 263 if value:
259 264 ui.note("%-12s %s\n" % (key, " ".join(value)))
260 265 else:
261 266 ui.note("files: %s\n" % " ".join(changes[3]))
262 267
263 268 description = changes[4].strip()
264 269 if description:
265 270 if ui.verbose:
266 271 ui.status("description:\n")
267 272 ui.status(description)
268 273 ui.status("\n\n")
269 274 else:
270 275 ui.status("summary: %s\n" % description.splitlines()[0])
271 276 ui.status("\n")
272 277
273 278 def show_version(ui):
274 279 """output version and copyright information"""
275 280 ui.write("Mercurial Distributed SCM (version %s)\n"
276 281 % version.get_version())
277 282 ui.status(
278 283 "\nCopyright (C) 2005 Matt Mackall <mpm@selenic.com>\n"
279 284 "This is free software; see the source for copying conditions. "
280 285 "There is NO\nwarranty; "
281 286 "not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n"
282 287 )
283 288
284 289 def help_(ui, cmd=None):
285 290 """show help for a given command or all commands"""
286 291 if cmd and cmd != 'shortlist':
287 292 key, i = find(cmd)
288 293 # synopsis
289 294 ui.write("%s\n\n" % i[2])
290 295
291 296 # description
292 297 doc = i[0].__doc__
293 298 if ui.quiet:
294 299 doc = doc.splitlines(0)[0]
295 300 ui.write("%s\n" % doc.rstrip())
296 301
297 302 # aliases
298 303 if not ui.quiet:
299 304 aliases = ', '.join(key.split('|')[1:])
300 305 if aliases:
301 306 ui.write("\naliases: %s\n" % aliases)
302 307
303 308 # options
304 309 if not ui.quiet and i[1]:
305 310 ui.write("\noptions:\n\n")
306 311 for s, l, d, c in i[1]:
307 312 opt = ' '
308 313 if s:
309 314 opt = opt + '-' + s + ' '
310 315 if l:
311 316 opt = opt + '--' + l + ' '
312 317 if d:
313 318 opt = opt + '(' + str(d) + ')'
314 319 ui.write(opt, "\n")
315 320 if c:
316 321 ui.write(' %s\n' % c)
317 322
318 323 else:
319 324 # program name
320 325 if ui.verbose:
321 326 show_version(ui)
322 327 else:
323 328 ui.status("Mercurial Distributed SCM\n")
324 329 ui.status('\n')
325 330
326 331 # list of commands
327 332 if cmd == "shortlist":
328 333 ui.status('basic commands (use "hg help" '
329 334 'for the full list or option "-v" for details):\n\n')
330 335 elif ui.verbose:
331 336 ui.status('list of commands:\n\n')
332 337 else:
333 338 ui.status('list of commands (use "hg help -v" '
334 339 'to show aliases and global options):\n\n')
335 340
336 341 h = {}
337 342 cmds = {}
338 343 for c, e in table.items():
339 344 f = c.split("|")[0]
340 345 if cmd == "shortlist" and not f.startswith("^"):
341 346 continue
342 347 f = f.lstrip("^")
343 348 if not ui.debugflag and f.startswith("debug"):
344 349 continue
345 350 d = ""
346 351 if e[0].__doc__:
347 352 d = e[0].__doc__.splitlines(0)[0].rstrip()
348 353 h[f] = d
349 354 cmds[f]=c.lstrip("^")
350 355
351 356 fns = h.keys()
352 357 fns.sort()
353 358 m = max(map(len, fns))
354 359 for f in fns:
355 360 if ui.verbose:
356 361 commands = cmds[f].replace("|",", ")
357 362 ui.write(" %s:\n %s\n"%(commands,h[f]))
358 363 else:
359 364 ui.write(' %-*s %s\n' % (m, f, h[f]))
360 365
361 366 # global options
362 367 if ui.verbose:
363 368 ui.write("\nglobal options:\n\n")
364 369 for s, l, d, c in globalopts:
365 370 opt = ' '
366 371 if s:
367 372 opt = opt + '-' + s + ' '
368 373 if l:
369 374 opt = opt + '--' + l + ' '
370 375 if d:
371 376 opt = opt + '(' + str(d) + ')'
372 377 ui.write(opt, "\n")
373 378 if c:
374 379 ui.write(' %s\n' % c)
375 380
376 381 # Commands start here, listed alphabetically
377 382
378 383 def add(ui, repo, *pats, **opts):
379 384 '''add the specified files on the next commit'''
380 385 names = []
381 386 for src, abs, rel, exact in walk(repo, pats, opts):
382 387 if exact:
383 388 names.append(abs)
384 389 elif repo.dirstate.state(abs) == '?':
385 390 ui.status('adding %s\n' % rel)
386 391 names.append(abs)
387 392 repo.add(names)
388 393
389 394 def addremove(ui, repo, *pats, **opts):
390 395 """add all new files, delete all missing files"""
391 396 add, remove = [], []
392 397 for src, abs, rel, exact in walk(repo, pats, opts):
393 398 if src == 'f' and repo.dirstate.state(abs) == '?':
394 399 add.append(abs)
395 400 if not exact: ui.status('adding ', rel, '\n')
396 401 if repo.dirstate.state(abs) != 'r' and not os.path.exists(rel):
397 402 remove.append(abs)
398 403 if not exact: ui.status('removing ', rel, '\n')
399 404 repo.add(add)
400 405 repo.remove(remove)
401 406
402 407 def annotate(ui, repo, *pats, **opts):
403 408 """show changeset information per file line"""
404 409 def getnode(rev):
405 410 return hg.short(repo.changelog.node(rev))
406 411
407 412 def getname(rev):
408 413 try:
409 414 return bcache[rev]
410 415 except KeyError:
411 416 cl = repo.changelog.read(repo.changelog.node(rev))
412 417 name = cl[1]
413 418 f = name.find('@')
414 419 if f >= 0:
415 420 name = name[:f]
416 421 f = name.find('<')
417 422 if f >= 0:
418 423 name = name[f+1:]
419 424 bcache[rev] = name
420 425 return name
421 426
422 427 if not pats:
423 428 raise util.Abort('at least one file name or pattern required')
424 429
425 430 bcache = {}
426 431 opmap = [['user', getname], ['number', str], ['changeset', getnode]]
427 432 if not opts['user'] and not opts['changeset']:
428 433 opts['number'] = 1
429 434
430 435 if opts['rev']:
431 436 node = repo.changelog.lookup(opts['rev'])
432 437 else:
433 438 node = repo.dirstate.parents()[0]
434 439 change = repo.changelog.read(node)
435 440 mmap = repo.manifest.read(change[0])
436 441 for src, abs, rel, exact in walk(repo, pats, opts):
437 442 if abs not in mmap:
438 443 ui.warn("warning: %s is not in the repository!\n" % rel)
439 444 continue
440 445
441 446 lines = repo.file(abs).annotate(mmap[abs])
442 447 pieces = []
443 448
444 449 for o, f in opmap:
445 450 if opts[o]:
446 451 l = [f(n) for n, dummy in lines]
447 452 if l:
448 453 m = max(map(len, l))
449 454 pieces.append(["%*s" % (m, x) for x in l])
450 455
451 456 if pieces:
452 457 for p, l in zip(zip(*pieces), lines):
453 458 ui.write("%s: %s" % (" ".join(p), l[1]))
454 459
455 460 def cat(ui, repo, file1, rev=None, **opts):
456 461 """output the latest or given revision of a file"""
457 462 r = repo.file(relpath(repo, [file1])[0])
458 463 if rev:
459 464 try:
460 465 # assume all revision numbers are for changesets
461 466 n = repo.lookup(rev)
462 467 change = repo.changelog.read(n)
463 468 m = repo.manifest.read(change[0])
464 469 n = m[relpath(repo, [file1])[0]]
465 470 except hg.RepoError, KeyError:
466 471 n = r.lookup(rev)
467 472 else:
468 473 n = r.tip()
469 474 fp = make_file(repo, r, opts['output'], node=n)
470 475 fp.write(r.read(n))
471 476
472 477 def clone(ui, source, dest=None, **opts):
473 478 """make a copy of an existing repository"""
474 479 if dest is None:
475 480 dest = os.path.basename(os.path.normpath(source))
476 481
477 482 if os.path.exists(dest):
478 483 ui.warn("abort: destination '%s' already exists\n" % dest)
479 484 return 1
480 485
481 486 dest = os.path.realpath(dest)
482 487
483 488 class Dircleanup:
484 489 def __init__(self, dir_):
485 490 self.rmtree = shutil.rmtree
486 491 self.dir_ = dir_
487 492 os.mkdir(dir_)
488 493 def close(self):
489 494 self.dir_ = None
490 495 def __del__(self):
491 496 if self.dir_:
492 497 self.rmtree(self.dir_, True)
493 498
494 499 if opts['ssh']:
495 500 ui.setconfig("ui", "ssh", opts['ssh'])
496 501 if opts['remotecmd']:
497 502 ui.setconfig("ui", "remotecmd", opts['remotecmd'])
498 503
499 504 d = Dircleanup(dest)
500 505 source = ui.expandpath(source)
501 506 abspath = source
502 507 other = hg.repository(ui, source)
503 508
504 509 if other.dev() != -1:
505 510 abspath = os.path.abspath(source)
506 511 copyfile = (os.stat(dest).st_dev == other.dev()
507 512 and getattr(os, 'link', None) or shutil.copy2)
508 513 if copyfile is not shutil.copy2:
509 514 ui.note("cloning by hardlink\n")
510 515 # we use a lock here because because we're not nicely ordered
511 516 l = lock.lock(os.path.join(source, ".hg", "lock"))
512 517
513 518 util.copytree(os.path.join(source, ".hg"), os.path.join(dest, ".hg"),
514 519 copyfile)
515 520 try:
516 521 os.unlink(os.path.join(dest, ".hg", "dirstate"))
517 522 except OSError:
518 523 pass
519 524
520 525 repo = hg.repository(ui, dest)
521 526
522 527 else:
523 528 repo = hg.repository(ui, dest, create=1)
524 529 repo.pull(other)
525 530
526 531 f = repo.opener("hgrc", "w")
527 532 f.write("[paths]\n")
528 533 f.write("default = %s\n" % abspath)
529 534
530 535 if not opts['noupdate']:
531 536 update(ui, repo)
532 537
533 538 d.close()
534 539
535 540 def commit(ui, repo, *pats, **opts):
536 541 """commit the specified files or all outstanding changes"""
537 542 if opts['text']:
538 543 ui.warn("Warning: -t and --text is deprecated,"
539 544 " please use -m or --message instead.\n")
540 545 message = opts['message'] or opts['text']
541 546 logfile = opts['logfile']
542 547 if not message and logfile:
543 548 try:
544 549 if logfile == '-':
545 550 message = sys.stdin.read()
546 551 else:
547 552 message = open(logfile).read()
548 553 except IOError, why:
549 554 ui.warn("Can't read commit message %s: %s\n" % (logfile, why))
550 555
551 556 if opts['addremove']:
552 557 addremove(ui, repo, *pats, **opts)
553 558 cwd = repo.getcwd()
554 559 if not pats and cwd:
555 560 opts['include'] = [os.path.join(cwd, i) for i in opts['include']]
556 561 opts['exclude'] = [os.path.join(cwd, x) for x in opts['exclude']]
557 562 fns, match = matchpats(repo, (pats and repo.getcwd()) or '', pats, opts)
558 563 if pats:
559 564 c, a, d, u = repo.changes(files = fns, match = match)
560 565 files = c + a + [fn for fn in d if repo.dirstate.state(fn) == 'r']
561 566 else:
562 567 files = []
563 568 repo.commit(files, message, opts['user'], opts['date'], match)
564 569
565 570 def copy(ui, repo, source, dest):
566 571 """mark a file as copied or renamed for the next commit"""
567 572 return repo.copy(*relpath(repo, (source, dest)))
568 573
569 574 def debugcheckstate(ui, repo):
570 575 """validate the correctness of the current dirstate"""
571 576 parent1, parent2 = repo.dirstate.parents()
572 577 repo.dirstate.read()
573 578 dc = repo.dirstate.map
574 579 keys = dc.keys()
575 580 keys.sort()
576 581 m1n = repo.changelog.read(parent1)[0]
577 582 m2n = repo.changelog.read(parent2)[0]
578 583 m1 = repo.manifest.read(m1n)
579 584 m2 = repo.manifest.read(m2n)
580 585 errors = 0
581 586 for f in dc:
582 587 state = repo.dirstate.state(f)
583 588 if state in "nr" and f not in m1:
584 589 ui.warn("%s in state %s, but not in manifest1\n" % (f, state))
585 590 errors += 1
586 591 if state in "a" and f in m1:
587 592 ui.warn("%s in state %s, but also in manifest1\n" % (f, state))
588 593 errors += 1
589 594 if state in "m" and f not in m1 and f not in m2:
590 595 ui.warn("%s in state %s, but not in either manifest\n" %
591 596 (f, state))
592 597 errors += 1
593 598 for f in m1:
594 599 state = repo.dirstate.state(f)
595 600 if state not in "nrm":
596 601 ui.warn("%s in manifest1, but listed as state %s" % (f, state))
597 602 errors += 1
598 603 if errors:
599 604 raise util.Abort(".hg/dirstate inconsistent with current parent's manifest")
600 605
601 606 def debugstate(ui, repo):
602 607 """show the contents of the current dirstate"""
603 608 repo.dirstate.read()
604 609 dc = repo.dirstate.map
605 610 keys = dc.keys()
606 611 keys.sort()
607 612 for file_ in keys:
608 613 ui.write("%c %3o %10d %s %s\n"
609 614 % (dc[file_][0], dc[file_][1] & 0777, dc[file_][2],
610 615 time.strftime("%x %X",
611 616 time.localtime(dc[file_][3])), file_))
612 617
613 618 def debugindex(ui, file_):
614 619 """dump the contents of an index file"""
615 620 r = hg.revlog(hg.opener(""), file_, "")
616 621 ui.write(" rev offset length base linkrev" +
617 622 " p1 p2 nodeid\n")
618 623 for i in range(r.count()):
619 624 e = r.index[i]
620 625 ui.write("% 6d % 9d % 7d % 6d % 7d %s.. %s.. %s..\n" % (
621 626 i, e[0], e[1], e[2], e[3],
622 627 hg.hex(e[4][:5]), hg.hex(e[5][:5]), hg.hex(e[6][:5])))
623 628
624 629 def debugindexdot(ui, file_):
625 630 """dump an index DAG as a .dot file"""
626 631 r = hg.revlog(hg.opener(""), file_, "")
627 632 ui.write("digraph G {\n")
628 633 for i in range(r.count()):
629 634 e = r.index[i]
630 635 ui.write("\t%d -> %d\n" % (r.rev(e[4]), i))
631 636 if e[5] != hg.nullid:
632 637 ui.write("\t%d -> %d\n" % (r.rev(e[5]), i))
633 638 ui.write("}\n")
634 639
635 640 def debugwalk(ui, repo, *pats, **opts):
636 641 items = list(walk(repo, pats, opts))
637 642 if not items: return
638 643 fmt = '%%s %%-%ds %%-%ds %%s' % (
639 644 max([len(abs) for (src, abs, rel, exact) in items]),
640 645 max([len(rel) for (src, abs, rel, exact) in items]))
641 646 exactly = {True: 'exact', False: ''}
642 647 for src, abs, rel, exact in items:
643 648 print fmt % (src, abs, rel, exactly[exact])
644 649
645 650 def diff(ui, repo, *pats, **opts):
646 651 """diff working directory (or selected files)"""
647 652 revs = []
648 653 if opts['rev']:
649 654 revs = map(lambda x: repo.lookup(x), opts['rev'])
650 655
651 656 if len(revs) > 2:
652 657 raise util.Abort("too many revisions to diff")
653 658
654 659 files = []
655 660 match = util.always
656 661 if pats:
657 662 roots, match, results = makewalk(repo, pats, opts)
658 663 for src, abs, rel, exact in results:
659 664 files.append(abs)
660 665 dodiff(sys.stdout, ui, repo, files, *revs, **{'match': match})
661 666
662 667 def doexport(ui, repo, changeset, seqno, total, revwidth, opts):
663 668 node = repo.lookup(changeset)
664 669 prev, other = repo.changelog.parents(node)
665 670 change = repo.changelog.read(node)
666 671
667 672 fp = make_file(repo, repo.changelog, opts['output'],
668 673 node=node, total=total, seqno=seqno,
669 674 revwidth=revwidth)
670 675 if fp != sys.stdout:
671 676 ui.note("%s\n" % fp.name)
672 677
673 678 fp.write("# HG changeset patch\n")
674 679 fp.write("# User %s\n" % change[1])
675 680 fp.write("# Node ID %s\n" % hg.hex(node))
676 681 fp.write("# Parent %s\n" % hg.hex(prev))
677 682 if other != hg.nullid:
678 683 fp.write("# Parent %s\n" % hg.hex(other))
679 684 fp.write(change[4].rstrip())
680 685 fp.write("\n\n")
681 686
682 687 dodiff(fp, ui, repo, None, prev, node)
683 688 if fp != sys.stdout: fp.close()
684 689
685 690 def export(ui, repo, *changesets, **opts):
686 691 """dump the header and diffs for one or more changesets"""
687 692 if not changesets:
688 693 raise util.Abort("export requires at least one changeset")
689 694 seqno = 0
690 695 revs = list(revrange(ui, repo, changesets))
691 696 total = len(revs)
692 697 revwidth = max(len(revs[0]), len(revs[-1]))
693 698 ui.note(len(revs) > 1 and "Exporting patches:\n" or "Exporting patch:\n")
694 699 for cset in revs:
695 700 seqno += 1
696 701 doexport(ui, repo, cset, seqno, total, revwidth, opts)
697 702
698 703 def forget(ui, repo, *pats, **opts):
699 704 """don't add the specified files on the next commit"""
700 705 forget = []
701 706 for src, abs, rel, exact in walk(repo, pats, opts):
702 707 if repo.dirstate.state(abs) == 'a':
703 708 forget.append(abs)
704 709 if not exact: ui.status('forgetting ', rel, '\n')
705 710 repo.forget(forget)
706 711
707 712 def heads(ui, repo, **opts):
708 713 """show current repository heads"""
709 714 heads = repo.changelog.heads()
710 715 br = None
711 716 if opts['branches']:
712 717 br = repo.branchlookup(heads)
713 718 for n in repo.changelog.heads():
714 719 show_changeset(ui, repo, changenode=n, brinfo=br)
715 720
716 721 def identify(ui, repo):
717 722 """print information about the working copy"""
718 723 parents = [p for p in repo.dirstate.parents() if p != hg.nullid]
719 724 if not parents:
720 725 ui.write("unknown\n")
721 726 return
722 727
723 728 hexfunc = ui.verbose and hg.hex or hg.short
724 729 (c, a, d, u) = repo.changes()
725 730 output = ["%s%s" % ('+'.join([hexfunc(parent) for parent in parents]),
726 731 (c or a or d) and "+" or "")]
727 732
728 733 if not ui.quiet:
729 734 # multiple tags for a single parent separated by '/'
730 735 parenttags = ['/'.join(tags)
731 736 for tags in map(repo.nodetags, parents) if tags]
732 737 # tags for multiple parents separated by ' + '
733 738 if parenttags:
734 739 output.append(' + '.join(parenttags))
735 740
736 741 ui.write("%s\n" % ' '.join(output))
737 742
738 743 def import_(ui, repo, patch1, *patches, **opts):
739 744 """import an ordered set of patches"""
740 745 patches = (patch1,) + patches
741 746
742 747 if not opts['force']:
743 748 (c, a, d, u) = repo.changes()
744 749 if c or a or d:
745 750 ui.warn("abort: outstanding uncommitted changes!\n")
746 751 return 1
747 752
748 753 d = opts["base"]
749 754 strip = opts["strip"]
750 755
751 756 for patch in patches:
752 757 ui.status("applying %s\n" % patch)
753 758 pf = os.path.join(d, patch)
754 759
755 760 message = []
756 761 user = None
757 762 hgpatch = False
758 763 for line in file(pf):
759 764 line = line.rstrip()
760 765 if line.startswith("--- ") or line.startswith("diff -r"):
761 766 break
762 767 elif hgpatch:
763 768 # parse values when importing the result of an hg export
764 769 if line.startswith("# User "):
765 770 user = line[7:]
766 771 ui.debug('User: %s\n' % user)
767 772 elif not line.startswith("# ") and line:
768 773 message.append(line)
769 774 hgpatch = False
770 775 elif line == '# HG changeset patch':
771 776 hgpatch = True
772 777 message = [] # We may have collected garbage
773 778 else:
774 779 message.append(line)
775 780
776 781 # make sure message isn't empty
777 782 if not message:
778 783 message = "imported patch %s\n" % patch
779 784 else:
780 785 message = "%s\n" % '\n'.join(message)
781 786 ui.debug('message:\n%s\n' % message)
782 787
783 788 f = os.popen("patch -p%d < '%s'" % (strip, pf))
784 789 files = []
785 790 for l in f.read().splitlines():
786 791 l.rstrip('\r\n');
787 792 ui.status("%s\n" % l)
788 793 if l.startswith('patching file '):
789 794 pf = l[14:]
790 795 if pf not in files:
791 796 files.append(pf)
792 797 patcherr = f.close()
793 798 if patcherr:
794 799 raise util.Abort("patch failed")
795 800
796 801 if len(files) > 0:
797 802 addremove(ui, repo, *files)
798 803 repo.commit(files, message, user)
799 804
800 805 def incoming(ui, repo, source="default"):
801 806 """show new changesets found in source"""
802 807 source = ui.expandpath(source)
803 808 other = hg.repository(ui, source)
804 809 if not other.local():
805 810 ui.warn("abort: incoming doesn't work for remote"
806 811 + " repositories yet, sorry!\n")
807 812 return 1
808 813 o = repo.findincoming(other)
809 814 if not o:
810 815 return
811 816 o = other.newer(o)
812 817 o.reverse()
813 818 for n in o:
814 819 show_changeset(ui, other, changenode=n)
815 820
816 821 def init(ui, dest="."):
817 822 """create a new repository in the given directory"""
818 823 if not os.path.exists(dest):
819 824 os.mkdir(dest)
820 825 hg.repository(ui, dest, create=1)
821 826
822 827 def locate(ui, repo, *pats, **opts):
823 828 """locate files matching specific patterns"""
824 829 end = '\n'
825 830 if opts['print0']: end = '\0'
826 831
827 832 for src, abs, rel, exact in walk(repo, pats, opts, '(?:.*/|)'):
828 833 if repo.dirstate.state(abs) == '?': continue
829 834 if opts['fullpath']:
830 835 ui.write(os.path.join(repo.root, abs), end)
831 836 else:
832 837 ui.write(rel, end)
833 838
834 839 def log(ui, repo, f=None, **opts):
835 840 """show the revision history of the repository or a single file"""
836 841 if f:
837 842 files = relpath(repo, [f])
838 843 filelog = repo.file(files[0])
839 844 log = filelog
840 845 lookup = filelog.lookup
841 846 else:
842 847 files = None
843 848 filelog = None
844 849 log = repo.changelog
845 850 lookup = repo.lookup
846 851 revlist = []
847 852 revs = [log.rev(lookup(rev)) for rev in opts['rev']]
848 853 while revs:
849 854 if len(revs) == 1:
850 855 revlist.append(revs.pop(0))
851 856 else:
852 857 a = revs.pop(0)
853 858 b = revs.pop(0)
854 859 off = a > b and -1 or 1
855 860 revlist.extend(range(a, b + off, off))
856 861
857 862 for i in revlist or range(log.count() - 1, -1, -1):
858 863 show_changeset(ui, repo, filelog=filelog, rev=i)
859 864 if opts['patch']:
860 865 if filelog:
861 866 filenode = filelog.node(i)
862 867 i = filelog.linkrev(filenode)
863 868 changenode = repo.changelog.node(i)
864 869 prev, other = repo.changelog.parents(changenode)
865 870 dodiff(sys.stdout, ui, repo, files, prev, changenode)
866 871 ui.write("\n\n")
867 872
868 873 def manifest(ui, repo, rev=None):
869 874 """output the latest or given revision of the project manifest"""
870 875 if rev:
871 876 try:
872 877 # assume all revision numbers are for changesets
873 878 n = repo.lookup(rev)
874 879 change = repo.changelog.read(n)
875 880 n = change[0]
876 881 except hg.RepoError:
877 882 n = repo.manifest.lookup(rev)
878 883 else:
879 884 n = repo.manifest.tip()
880 885 m = repo.manifest.read(n)
881 886 mf = repo.manifest.readflags(n)
882 887 files = m.keys()
883 888 files.sort()
884 889
885 890 for f in files:
886 891 ui.write("%40s %3s %s\n" % (hg.hex(m[f]), mf[f] and "755" or "644", f))
887 892
888 893 def outgoing(ui, repo, dest="default-push"):
889 894 """show changesets not found in destination"""
890 895 dest = ui.expandpath(dest)
891 896 other = hg.repository(ui, dest)
892 897 o = repo.findoutgoing(other)
893 898 o = repo.newer(o)
894 899 o.reverse()
895 900 for n in o:
896 901 show_changeset(ui, repo, changenode=n)
897 902
898 903 def parents(ui, repo, rev=None):
899 904 """show the parents of the working dir or revision"""
900 905 if rev:
901 906 p = repo.changelog.parents(repo.lookup(rev))
902 907 else:
903 908 p = repo.dirstate.parents()
904 909
905 910 for n in p:
906 911 if n != hg.nullid:
907 912 show_changeset(ui, repo, changenode=n)
908 913
909 914 def paths(ui, search = None):
910 915 """show definition of symbolic path names"""
911 916 try:
912 917 repo = hg.repository(ui=ui)
913 918 except:
914 919 pass
915 920
916 921 if search:
917 922 for name, path in ui.configitems("paths"):
918 923 if name == search:
919 924 ui.write("%s\n" % path)
920 925 return
921 926 ui.warn("not found!\n")
922 927 return 1
923 928 else:
924 929 for name, path in ui.configitems("paths"):
925 930 ui.write("%s = %s\n" % (name, path))
926 931
927 932 def pull(ui, repo, source="default", **opts):
928 933 """pull changes from the specified source"""
929 934 source = ui.expandpath(source)
930 935 ui.status('pulling from %s\n' % (source))
931 936
932 937 if opts['ssh']:
933 938 ui.setconfig("ui", "ssh", opts['ssh'])
934 939 if opts['remotecmd']:
935 940 ui.setconfig("ui", "remotecmd", opts['remotecmd'])
936 941
937 942 other = hg.repository(ui, source)
938 943 r = repo.pull(other)
939 944 if not r:
940 945 if opts['update']:
941 946 return update(ui, repo)
942 947 else:
943 948 ui.status("(run 'hg update' to get a working copy)\n")
944 949
945 950 return r
946 951
947 952 def push(ui, repo, dest="default-push", force=False, ssh=None, remotecmd=None):
948 953 """push changes to the specified destination"""
949 954 dest = ui.expandpath(dest)
950 955 ui.status('pushing to %s\n' % (dest))
951 956
952 957 if ssh:
953 958 ui.setconfig("ui", "ssh", ssh)
954 959 if remotecmd:
955 960 ui.setconfig("ui", "remotecmd", remotecmd)
956 961
957 962 other = hg.repository(ui, dest)
958 963 r = repo.push(other, force)
959 964 return r
960 965
961 966 def rawcommit(ui, repo, *flist, **rc):
962 967 "raw commit interface"
963 968 if rc['text']:
964 969 ui.warn("Warning: -t and --text is deprecated,"
965 970 " please use -m or --message instead.\n")
966 971 message = rc['message'] or rc['text']
967 972 if not message and rc['logfile']:
968 973 try:
969 974 message = open(rc['logfile']).read()
970 975 except IOError:
971 976 pass
972 977 if not message and not rc['logfile']:
973 978 ui.warn("abort: missing commit message\n")
974 979 return 1
975 980
976 981 files = relpath(repo, list(flist))
977 982 if rc['files']:
978 983 files += open(rc['files']).read().splitlines()
979 984
980 985 rc['parent'] = map(repo.lookup, rc['parent'])
981 986
982 987 repo.rawcommit(files, message, rc['user'], rc['date'], *rc['parent'])
983 988
984 989 def recover(ui, repo):
985 990 """roll back an interrupted transaction"""
986 991 repo.recover()
987 992
988 993 def remove(ui, repo, file1, *files):
989 994 """remove the specified files on the next commit"""
990 995 repo.remove(relpath(repo, (file1,) + files))
991 996
992 997 def revert(ui, repo, *names, **opts):
993 998 """revert modified files or dirs back to their unmodified states"""
994 999 node = opts['rev'] and repo.lookup(opts['rev']) or \
995 1000 repo.dirstate.parents()[0]
996 1001 root = os.path.realpath(repo.root)
997 1002
998 1003 def trimpath(p):
999 1004 p = os.path.realpath(p)
1000 1005 if p.startswith(root):
1001 1006 rest = p[len(root):]
1002 1007 if not rest:
1003 1008 return rest
1004 1009 if p.startswith(os.sep):
1005 1010 return rest[1:]
1006 1011 return p
1007 1012
1008 1013 relnames = map(trimpath, names or [os.getcwd()])
1009 1014 chosen = {}
1010 1015
1011 1016 def choose(name):
1012 1017 def body(name):
1013 1018 for r in relnames:
1014 1019 if not name.startswith(r):
1015 1020 continue
1016 1021 rest = name[len(r):]
1017 1022 if not rest:
1018 1023 return r, True
1019 1024 depth = rest.count(os.sep)
1020 1025 if not r:
1021 1026 if depth == 0 or not opts['nonrecursive']:
1022 1027 return r, True
1023 1028 elif rest[0] == os.sep:
1024 1029 if depth == 1 or not opts['nonrecursive']:
1025 1030 return r, True
1026 1031 return None, False
1027 1032 relname, ret = body(name)
1028 1033 if ret:
1029 1034 chosen[relname] = 1
1030 1035 return ret
1031 1036
1032 1037 r = repo.update(node, False, True, choose, False)
1033 1038 for n in relnames:
1034 1039 if n not in chosen:
1035 1040 ui.warn('error: no matches for %s\n' % n)
1036 1041 r = 1
1037 1042 sys.stdout.flush()
1038 1043 return r
1039 1044
1040 1045 def root(ui, repo):
1041 1046 """print the root (top) of the current working dir"""
1042 1047 ui.write(repo.root + "\n")
1043 1048
1044 1049 def serve(ui, repo, **opts):
1045 1050 """export the repository via HTTP"""
1046 1051
1047 1052 if opts["stdio"]:
1048 1053 fin, fout = sys.stdin, sys.stdout
1049 1054 sys.stdout = sys.stderr
1050 1055
1051 1056 def getarg():
1052 1057 argline = fin.readline()[:-1]
1053 1058 arg, l = argline.split()
1054 1059 val = fin.read(int(l))
1055 1060 return arg, val
1056 1061 def respond(v):
1057 1062 fout.write("%d\n" % len(v))
1058 1063 fout.write(v)
1059 1064 fout.flush()
1060 1065
1061 1066 lock = None
1062 1067
1063 1068 while 1:
1064 1069 cmd = fin.readline()[:-1]
1065 1070 if cmd == '':
1066 1071 return
1067 1072 if cmd == "heads":
1068 1073 h = repo.heads()
1069 1074 respond(" ".join(map(hg.hex, h)) + "\n")
1070 1075 if cmd == "lock":
1071 1076 lock = repo.lock()
1072 1077 respond("")
1073 1078 if cmd == "unlock":
1074 1079 if lock:
1075 1080 lock.release()
1076 1081 lock = None
1077 1082 respond("")
1078 1083 elif cmd == "branches":
1079 1084 arg, nodes = getarg()
1080 1085 nodes = map(hg.bin, nodes.split(" "))
1081 1086 r = []
1082 1087 for b in repo.branches(nodes):
1083 1088 r.append(" ".join(map(hg.hex, b)) + "\n")
1084 1089 respond("".join(r))
1085 1090 elif cmd == "between":
1086 1091 arg, pairs = getarg()
1087 1092 pairs = [map(hg.bin, p.split("-")) for p in pairs.split(" ")]
1088 1093 r = []
1089 1094 for b in repo.between(pairs):
1090 1095 r.append(" ".join(map(hg.hex, b)) + "\n")
1091 1096 respond("".join(r))
1092 1097 elif cmd == "changegroup":
1093 1098 nodes = []
1094 1099 arg, roots = getarg()
1095 1100 nodes = map(hg.bin, roots.split(" "))
1096 1101
1097 1102 cg = repo.changegroup(nodes)
1098 1103 while 1:
1099 1104 d = cg.read(4096)
1100 1105 if not d:
1101 1106 break
1102 1107 fout.write(d)
1103 1108
1104 1109 fout.flush()
1105 1110
1106 1111 elif cmd == "addchangegroup":
1107 1112 if not lock:
1108 1113 respond("not locked")
1109 1114 continue
1110 1115 respond("")
1111 1116
1112 1117 r = repo.addchangegroup(fin)
1113 1118 respond("")
1114 1119
1115 1120 httpd = hgweb.create_server(repo.root, opts["name"], opts["templates"],
1116 1121 opts["address"], opts["port"], opts["ipv6"],
1117 1122 opts['accesslog'], opts['errorlog'])
1118 1123 if ui.verbose:
1119 1124 addr, port = httpd.socket.getsockname()
1120 1125 if addr == '0.0.0.0':
1121 1126 addr = socket.gethostname()
1122 1127 else:
1123 1128 try:
1124 1129 addr = socket.gethostbyaddr(addr)[0]
1125 1130 except socket.error:
1126 1131 pass
1127 1132 if port != 80:
1128 1133 ui.status('listening at http://%s:%d/\n' % (addr, port))
1129 1134 else:
1130 1135 ui.status('listening at http://%s/\n' % addr)
1131 1136 httpd.serve_forever()
1132 1137
1133 1138 def status(ui, repo, *pats, **opts):
1134 1139 '''show changed files in the working directory
1135 1140
1136 1141 M = modified
1137 1142 A = added
1138 1143 R = removed
1139 1144 ? = not tracked
1140 1145 '''
1141 1146
1142 1147 cwd = repo.getcwd()
1143 1148 files, matchfn = matchpats(repo, cwd, pats, opts)
1144 1149 (c, a, d, u) = [[util.pathto(cwd, x) for x in n]
1145 1150 for n in repo.changes(files=files, match=matchfn)]
1146 1151
1147 1152 changetypes = [('modified', 'M', c),
1148 1153 ('added', 'A', a),
1149 1154 ('removed', 'R', d),
1150 1155 ('unknown', '?', u)]
1151 1156
1152 1157 for opt, char, changes in ([ct for ct in changetypes if opts[ct[0]]]
1153 1158 or changetypes):
1154 1159 for f in changes:
1155 1160 ui.write("%s %s\n" % (char, f))
1156 1161
1157 1162 def tag(ui, repo, name, rev=None, **opts):
1158 1163 """add a tag for the current tip or a given revision"""
1159 1164 if opts['text']:
1160 1165 ui.warn("Warning: -t and --text is deprecated,"
1161 1166 " please use -m or --message instead.\n")
1162 1167 if name == "tip":
1163 1168 ui.warn("abort: 'tip' is a reserved name!\n")
1164 1169 return -1
1165 1170 if rev:
1166 1171 r = hg.hex(repo.lookup(rev))
1167 1172 else:
1168 1173 r = hg.hex(repo.changelog.tip())
1169 1174
1170 1175 if name.find(revrangesep) >= 0:
1171 1176 ui.warn("abort: '%s' cannot be used in a tag name\n" % revrangesep)
1172 1177 return -1
1173 1178
1174 1179 if opts['local']:
1175 1180 repo.opener("localtags", "a").write("%s %s\n" % (r, name))
1176 1181 return
1177 1182
1178 1183 (c, a, d, u) = repo.changes()
1179 1184 for x in (c, a, d, u):
1180 1185 if ".hgtags" in x:
1181 1186 ui.warn("abort: working copy of .hgtags is changed!\n")
1182 1187 ui.status("(please commit .hgtags manually)\n")
1183 1188 return -1
1184 1189
1185 1190 repo.wfile(".hgtags", "ab").write("%s %s\n" % (r, name))
1186 1191 if repo.dirstate.state(".hgtags") == '?':
1187 1192 repo.add([".hgtags"])
1188 1193
1189 1194 message = (opts['message'] or opts['text'] or
1190 1195 "Added tag %s for changeset %s" % (name, r))
1191 1196 repo.commit([".hgtags"], message, opts['user'], opts['date'])
1192 1197
1193 1198 def tags(ui, repo):
1194 1199 """list repository tags"""
1195 1200
1196 1201 l = repo.tagslist()
1197 1202 l.reverse()
1198 1203 for t, n in l:
1199 1204 try:
1200 1205 r = "%5d:%s" % (repo.changelog.rev(n), hg.hex(n))
1201 1206 except KeyError:
1202 1207 r = " ?:?"
1203 1208 ui.write("%-30s %s\n" % (t, r))
1204 1209
1205 1210 def tip(ui, repo):
1206 1211 """show the tip revision"""
1207 1212 n = repo.changelog.tip()
1208 1213 show_changeset(ui, repo, changenode=n)
1209 1214
1210 1215 def undo(ui, repo):
1211 1216 """undo the last commit or pull
1212 1217
1213 1218 Roll back the last pull or commit transaction on the
1214 1219 repository, restoring the project to its earlier state.
1215 1220
1216 1221 This command should be used with care. There is only one level of
1217 1222 undo and there is no redo.
1218 1223
1219 1224 This command is not intended for use on public repositories. Once
1220 1225 a change is visible for pull by other users, undoing it locally is
1221 1226 ineffective.
1222 1227 """
1223 1228 repo.undo()
1224 1229
1225 1230 def update(ui, repo, node=None, merge=False, clean=False, branch=None):
1226 1231 '''update or merge working directory
1227 1232
1228 1233 If there are no outstanding changes in the working directory and
1229 1234 there is a linear relationship between the current version and the
1230 1235 requested version, the result is the requested version.
1231 1236
1232 1237 Otherwise the result is a merge between the contents of the
1233 1238 current working directory and the requested version. Files that
1234 1239 changed between either parent are marked as changed for the next
1235 1240 commit and a commit must be performed before any further updates
1236 1241 are allowed.
1237 1242 '''
1238 1243 if branch:
1239 1244 br = repo.branchlookup(branch=branch)
1240 1245 found = []
1241 1246 for x in br:
1242 1247 if branch in br[x]:
1243 1248 found.append(x)
1244 1249 if len(found) > 1:
1245 1250 ui.warn("Found multiple heads for %s\n" % branch)
1246 1251 for x in found:
1247 1252 show_changeset(ui, repo, changenode=x, brinfo=br)
1248 1253 return 1
1249 1254 if len(found) == 1:
1250 1255 node = found[0]
1251 1256 ui.warn("Using head %s for branch %s\n" % (hg.short(node), branch))
1252 1257 else:
1253 1258 ui.warn("branch %s not found\n" % (branch))
1254 1259 return 1
1255 1260 else:
1256 1261 node = node and repo.lookup(node) or repo.changelog.tip()
1257 1262 return repo.update(node, allow=merge, force=clean)
1258 1263
1259 1264 def verify(ui, repo):
1260 1265 """verify the integrity of the repository"""
1261 1266 return repo.verify()
1262 1267
1263 1268 # Command options and aliases are listed here, alphabetically
1264 1269
1265 1270 table = {
1266 1271 "^add":
1267 1272 (add,
1268 1273 [('I', 'include', [], 'include path in search'),
1269 1274 ('X', 'exclude', [], 'exclude path from search')],
1270 1275 "hg add [OPTION]... [FILE]..."),
1271 1276 "addremove":
1272 1277 (addremove,
1273 1278 [('I', 'include', [], 'include path in search'),
1274 1279 ('X', 'exclude', [], 'exclude path from search')],
1275 1280 "hg addremove [OPTION]... [FILE]..."),
1276 1281 "^annotate":
1277 1282 (annotate,
1278 1283 [('r', 'rev', '', 'revision'),
1279 1284 ('u', 'user', None, 'show user'),
1280 1285 ('n', 'number', None, 'show revision number'),
1281 1286 ('c', 'changeset', None, 'show changeset'),
1282 1287 ('I', 'include', [], 'include path in search'),
1283 1288 ('X', 'exclude', [], 'exclude path from search')],
1284 1289 'hg annotate [OPTION]... FILE...'),
1285 1290 "cat":
1286 1291 (cat,
1287 1292 [('o', 'output', "", 'output to file')],
1288 1293 'hg cat [-o OUTFILE] FILE [REV]'),
1289 1294 "^clone":
1290 1295 (clone,
1291 1296 [('U', 'noupdate', None, 'skip update after cloning'),
1292 1297 ('e', 'ssh', "", 'ssh command'),
1293 1298 ('', 'remotecmd', "", 'remote hg command')],
1294 1299 'hg clone [OPTIONS] SOURCE [DEST]'),
1295 1300 "^commit|ci":
1296 1301 (commit,
1297 1302 [('A', 'addremove', None, 'run add/remove during commit'),
1298 1303 ('I', 'include', [], 'include path in search'),
1299 1304 ('X', 'exclude', [], 'exclude path from search'),
1300 1305 ('m', 'message', "", 'commit message'),
1301 1306 ('t', 'text', "", 'commit message (deprecated: use -m)'),
1302 1307 ('l', 'logfile', "", 'commit message file'),
1303 1308 ('d', 'date', "", 'date code'),
1304 1309 ('u', 'user', "", 'user')],
1305 1310 'hg commit [OPTION]... [FILE]...'),
1306 1311 "copy": (copy, [], 'hg copy SOURCE DEST'),
1307 1312 "debugcheckstate": (debugcheckstate, [], 'debugcheckstate'),
1308 1313 "debugstate": (debugstate, [], 'debugstate'),
1309 1314 "debugindex": (debugindex, [], 'debugindex FILE'),
1310 1315 "debugindexdot": (debugindexdot, [], 'debugindexdot FILE'),
1311 1316 "debugwalk":
1312 1317 (debugwalk,
1313 1318 [('I', 'include', [], 'include path in search'),
1314 1319 ('X', 'exclude', [], 'exclude path from search')],
1315 1320 'debugwalk [OPTION]... [FILE]...'),
1316 1321 "^diff":
1317 1322 (diff,
1318 1323 [('r', 'rev', [], 'revision'),
1319 1324 ('I', 'include', [], 'include path in search'),
1320 1325 ('X', 'exclude', [], 'exclude path from search')],
1321 1326 'hg diff [-I] [-X] [-r REV1 [-r REV2]] [FILE]...'),
1322 1327 "^export":
1323 1328 (export,
1324 1329 [('o', 'output', "", 'output to file')],
1325 1330 "hg export [-o OUTFILE] REV..."),
1326 1331 "forget":
1327 1332 (forget,
1328 1333 [('I', 'include', [], 'include path in search'),
1329 1334 ('X', 'exclude', [], 'exclude path from search')],
1330 1335 "hg forget [OPTION]... FILE..."),
1331 1336 "heads":
1332 1337 (heads,
1333 1338 [('b', 'branches', None, 'find branch info')],
1334 1339 'hg [-b] heads'),
1335 1340 "help": (help_, [], 'hg help [COMMAND]'),
1336 1341 "identify|id": (identify, [], 'hg identify'),
1337 1342 "import|patch":
1338 1343 (import_,
1339 1344 [('p', 'strip', 1, 'path strip'),
1340 1345 ('f', 'force', None, 'skip check for outstanding changes'),
1341 1346 ('b', 'base', "", 'base path')],
1342 1347 "hg import [-p NUM] [-b BASE] PATCH..."),
1343 1348 "incoming|in": (incoming, [], 'hg incoming [SOURCE]'),
1344 1349 "^init": (init, [], 'hg init [DEST]'),
1345 1350 "locate":
1346 1351 (locate,
1347 1352 [('r', 'rev', '', 'revision'),
1348 1353 ('0', 'print0', None, 'end records with NUL'),
1349 1354 ('f', 'fullpath', None, 'print complete paths'),
1350 1355 ('I', 'include', [], 'include path in search'),
1351 1356 ('X', 'exclude', [], 'exclude path from search')],
1352 1357 'hg locate [OPTION]... [PATTERN]...'),
1353 1358 "^log|history":
1354 1359 (log,
1355 1360 [('r', 'rev', [], 'revision'),
1356 1361 ('p', 'patch', None, 'show patch')],
1357 1362 'hg log [-r REV1 [-r REV2]] [-p] [FILE]'),
1358 1363 "manifest": (manifest, [], 'hg manifest [REV]'),
1359 1364 "outgoing|out": (outgoing, [], 'hg outgoing [DEST]'),
1360 1365 "parents": (parents, [], 'hg parents [REV]'),
1361 1366 "paths": (paths, [], 'hg paths [NAME]'),
1362 1367 "^pull":
1363 1368 (pull,
1364 1369 [('u', 'update', None, 'update working directory'),
1365 1370 ('e', 'ssh', "", 'ssh command'),
1366 1371 ('', 'remotecmd', "", 'remote hg command')],
1367 1372 'hg pull [OPTIONS] [SOURCE]'),
1368 1373 "^push":
1369 1374 (push,
1370 1375 [('f', 'force', None, 'force push'),
1371 1376 ('e', 'ssh', "", 'ssh command'),
1372 1377 ('', 'remotecmd', "", 'remote hg command')],
1373 1378 'hg push [-f] [DEST]'),
1374 1379 "rawcommit":
1375 1380 (rawcommit,
1376 1381 [('p', 'parent', [], 'parent'),
1377 1382 ('d', 'date', "", 'date code'),
1378 1383 ('u', 'user', "", 'user'),
1379 1384 ('F', 'files', "", 'file list'),
1380 1385 ('m', 'message', "", 'commit message'),
1381 1386 ('t', 'text', "", 'commit message (deprecated: use -m)'),
1382 1387 ('l', 'logfile', "", 'commit message file')],
1383 1388 'hg rawcommit [OPTION]... [FILE]...'),
1384 1389 "recover": (recover, [], "hg recover"),
1385 1390 "^remove|rm": (remove, [], "hg remove FILE..."),
1386 1391 "^revert":
1387 1392 (revert,
1388 1393 [("n", "nonrecursive", None, "don't recurse into subdirs"),
1389 1394 ("r", "rev", "", "revision")],
1390 1395 "hg revert [-n] [-r REV] [NAME]..."),
1391 1396 "root": (root, [], "hg root"),
1392 1397 "^serve":
1393 1398 (serve,
1394 1399 [('A', 'accesslog', '', 'access log file'),
1395 1400 ('E', 'errorlog', '', 'error log file'),
1396 1401 ('p', 'port', 0, 'listen port'),
1397 1402 ('a', 'address', '', 'interface address'),
1398 1403 ('n', 'name', "", 'repository name'),
1399 1404 ('', 'stdio', None, 'for remote clients'),
1400 1405 ('t', 'templates', "", 'template map'),
1401 1406 ('6', 'ipv6', None, 'use IPv6 in addition to IPv4')],
1402 1407 "hg serve [OPTION]..."),
1403 1408 "^status":
1404 1409 (status,
1405 1410 [('m', 'modified', None, 'show only modified files'),
1406 1411 ('a', 'added', None, 'show only added files'),
1407 1412 ('r', 'removed', None, 'show only removed files'),
1408 1413 ('u', 'unknown', None, 'show only unknown (not tracked) files'),
1409 1414 ('I', 'include', [], 'include path in search'),
1410 1415 ('X', 'exclude', [], 'exclude path from search')],
1411 1416 "hg status [OPTION]... [FILE]..."),
1412 1417 "tag":
1413 1418 (tag,
1414 1419 [('l', 'local', None, 'make the tag local'),
1415 1420 ('m', 'message', "", 'commit message'),
1416 1421 ('t', 'text', "", 'commit message (deprecated: use -m)'),
1417 1422 ('d', 'date', "", 'date code'),
1418 1423 ('u', 'user', "", 'user')],
1419 1424 'hg tag [OPTION]... NAME [REV]'),
1420 1425 "tags": (tags, [], 'hg tags'),
1421 1426 "tip": (tip, [], 'hg tip'),
1422 1427 "undo": (undo, [], 'hg undo'),
1423 1428 "^update|up|checkout|co":
1424 1429 (update,
1425 1430 [('b', 'branch', "", 'checkout the head of a specific branch'),
1426 1431 ('m', 'merge', None, 'allow merging of conflicts'),
1427 1432 ('C', 'clean', None, 'overwrite locally modified files')],
1428 1433 'hg update [-b TAG] [-m] [-C] [REV]'),
1429 1434 "verify": (verify, [], 'hg verify'),
1430 1435 "version": (show_version, [], 'hg version'),
1431 1436 }
1432 1437
1433 1438 globalopts = [('v', 'verbose', None, 'verbose mode'),
1434 1439 ('', 'debug', None, 'debug mode'),
1435 1440 ('q', 'quiet', None, 'quiet mode'),
1436 1441 ('', 'profile', None, 'profile'),
1437 1442 ('', 'cwd', '', 'change working directory'),
1438 1443 ('R', 'repository', "", 'repository root directory'),
1439 1444 ('', 'traceback', None, 'print traceback on exception'),
1440 1445 ('y', 'noninteractive', None, 'run non-interactively'),
1441 1446 ('', 'version', None, 'output version information and exit'),
1442 1447 ('', 'time', None, 'time how long the command takes'),
1443 1448 ]
1444 1449
1445 1450 norepo = "clone init version help debugindex debugindexdot paths"
1446 1451
1447 1452 def find(cmd):
1448 1453 for e in table.keys():
1449 1454 if re.match("(%s)$" % e, cmd):
1450 1455 return e, table[e]
1451 1456
1452 1457 raise UnknownCommand(cmd)
1453 1458
1454 1459 class SignalInterrupt(Exception):
1455 1460 """Exception raised on SIGTERM and SIGHUP."""
1456 1461
1457 1462 def catchterm(*args):
1458 1463 raise SignalInterrupt
1459 1464
1460 1465 def run():
1461 1466 sys.exit(dispatch(sys.argv[1:]))
1462 1467
1463 1468 class ParseError(Exception):
1464 1469 """Exception raised on errors in parsing the command line."""
1465 1470
1466 1471 def parse(args):
1467 1472 options = {}
1468 1473 cmdoptions = {}
1469 1474
1470 1475 try:
1471 1476 args = fancyopts.fancyopts(args, globalopts, options)
1472 1477 except fancyopts.getopt.GetoptError, inst:
1473 1478 raise ParseError(None, inst)
1474 1479
1475 1480 if options["version"]:
1476 1481 return ("version", show_version, [], options, cmdoptions)
1477 1482 elif not args:
1478 1483 return ("help", help_, ["shortlist"], options, cmdoptions)
1479 1484 else:
1480 1485 cmd, args = args[0], args[1:]
1481 1486
1482 1487 i = find(cmd)[1]
1483 1488
1484 1489 # combine global options into local
1485 1490 c = list(i[1])
1486 1491 for o in globalopts:
1487 1492 c.append((o[0], o[1], options[o[1]], o[3]))
1488 1493
1489 1494 try:
1490 1495 args = fancyopts.fancyopts(args, c, cmdoptions)
1491 1496 except fancyopts.getopt.GetoptError, inst:
1492 1497 raise ParseError(cmd, inst)
1493 1498
1494 1499 # separate global options back out
1495 1500 for o in globalopts:
1496 1501 n = o[1]
1497 1502 options[n] = cmdoptions[n]
1498 1503 del cmdoptions[n]
1499 1504
1500 1505 return (cmd, i[0], args, options, cmdoptions)
1501 1506
1502 1507 def dispatch(args):
1503 1508 signal.signal(signal.SIGTERM, catchterm)
1504 1509 try:
1505 1510 signal.signal(signal.SIGHUP, catchterm)
1506 1511 except AttributeError:
1507 1512 pass
1508 1513
1509 1514 try:
1510 1515 cmd, func, args, options, cmdoptions = parse(args)
1511 1516 except ParseError, inst:
1512 1517 u = ui.ui()
1513 1518 if inst.args[0]:
1514 1519 u.warn("hg %s: %s\n" % (inst.args[0], inst.args[1]))
1515 1520 help_(u, inst.args[0])
1516 1521 else:
1517 1522 u.warn("hg: %s\n" % inst.args[1])
1518 1523 help_(u, 'shortlist')
1519 1524 sys.exit(-1)
1520 1525 except UnknownCommand, inst:
1521 1526 u = ui.ui()
1522 1527 u.warn("hg: unknown command '%s'\n" % inst.args[0])
1523 1528 help_(u, 'shortlist')
1524 1529 sys.exit(1)
1525 1530
1526 1531 if options['cwd']:
1527 1532 try:
1528 1533 os.chdir(options['cwd'])
1529 1534 except OSError, inst:
1530 1535 u = ui.ui()
1531 1536 u.warn('abort: %s: %s\n' % (options['cwd'], inst.strerror))
1532 1537 sys.exit(1)
1533 1538
1534 1539 if options["time"]:
1535 1540 def get_times():
1536 1541 t = os.times()
1537 1542 if t[4] == 0.0: # Windows leaves this as zero, so use time.clock()
1538 1543 t = (t[0], t[1], t[2], t[3], time.clock())
1539 1544 return t
1540 1545 s = get_times()
1541 1546 def print_time():
1542 1547 t = get_times()
1543 1548 u = ui.ui()
1544 1549 u.warn("Time: real %.3f secs (user %.3f+%.3f sys %.3f+%.3f)\n" %
1545 1550 (t[4]-s[4], t[0]-s[0], t[2]-s[2], t[1]-s[1], t[3]-s[3]))
1546 1551 atexit.register(print_time)
1547 1552
1548 1553 u = ui.ui(options["verbose"], options["debug"], options["quiet"],
1549 1554 not options["noninteractive"])
1550 1555
1551 1556 try:
1552 1557 try:
1553 1558 if cmd not in norepo.split():
1554 1559 path = options["repository"] or ""
1555 1560 repo = hg.repository(ui=u, path=path)
1556 1561 d = lambda: func(u, repo, *args, **cmdoptions)
1557 1562 else:
1558 1563 d = lambda: func(u, *args, **cmdoptions)
1559 1564
1560 1565 if options['profile']:
1561 1566 import hotshot, hotshot.stats
1562 1567 prof = hotshot.Profile("hg.prof")
1563 1568 r = prof.runcall(d)
1564 1569 prof.close()
1565 1570 stats = hotshot.stats.load("hg.prof")
1566 1571 stats.strip_dirs()
1567 1572 stats.sort_stats('time', 'calls')
1568 1573 stats.print_stats(40)
1569 1574 return r
1570 1575 else:
1571 1576 return d()
1572 1577 except:
1573 1578 if options['traceback']:
1574 1579 traceback.print_exc()
1575 1580 raise
1576 1581 except hg.RepoError, inst:
1577 1582 u.warn("abort: ", inst, "!\n")
1578 1583 except SignalInterrupt:
1579 1584 u.warn("killed!\n")
1580 1585 except KeyboardInterrupt:
1581 1586 try:
1582 1587 u.warn("interrupted!\n")
1583 1588 except IOError, inst:
1584 1589 if inst.errno == errno.EPIPE:
1585 1590 if u.debugflag:
1586 1591 u.warn("\nbroken pipe\n")
1587 1592 else:
1588 1593 raise
1589 1594 except IOError, inst:
1590 1595 if hasattr(inst, "code"):
1591 1596 u.warn("abort: %s\n" % inst)
1592 1597 elif hasattr(inst, "reason"):
1593 1598 u.warn("abort: error: %s\n" % inst.reason[1])
1594 1599 elif hasattr(inst, "args") and inst[0] == errno.EPIPE:
1595 1600 if u.debugflag: u.warn("broken pipe\n")
1596 1601 else:
1597 1602 raise
1598 1603 except OSError, inst:
1599 1604 if hasattr(inst, "filename"):
1600 1605 u.warn("abort: %s: %s\n" % (inst.strerror, inst.filename))
1601 1606 else:
1602 1607 u.warn("abort: %s\n" % inst.strerror)
1603 1608 except util.Abort, inst:
1604 1609 u.warn('abort: ', inst.args[0] % inst.args[1:], '\n')
1605 1610 sys.exit(1)
1606 1611 except TypeError, inst:
1607 1612 # was this an argument error?
1608 1613 tb = traceback.extract_tb(sys.exc_info()[2])
1609 1614 if len(tb) > 2: # no
1610 1615 raise
1611 1616 u.debug(inst, "\n")
1612 1617 u.warn("%s: invalid arguments\n" % cmd)
1613 1618 help_(u, cmd)
1614 1619 except UnknownCommand, inst:
1615 1620 u.warn("hg: unknown command '%s'\n" % inst.args[0])
1616 1621 help_(u, 'shortlist')
1617 1622
1618 1623 sys.exit(-1)
General Comments 0
You need to be logged in to leave comments. Login now