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