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