##// END OF EJS Templates
Made the abort message for IOError nicer when the filename is known.
Eric Hopper -
r1354:8cf364c6 default
parent child Browse files
Show More
@@ -1,2211 +1,2214 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 571
572 572 def cat(ui, repo, file1, *pats, **opts):
573 573 """output the latest or given revisions of files"""
574 574 mf = {}
575 575 if opts['rev']:
576 576 change = repo.changelog.read(repo.lookup(opts['rev']))
577 577 mf = repo.manifest.read(change[0])
578 578 for src, abs, rel, exact in walk(repo, (file1,) + pats, opts):
579 579 r = repo.file(abs)
580 580 if opts['rev']:
581 581 try:
582 582 n = mf[abs]
583 583 except (hg.RepoError, KeyError):
584 584 try:
585 585 n = r.lookup(rev)
586 586 except KeyError, inst:
587 587 raise util.Abort('cannot find file %s in rev %s', rel, rev)
588 588 else:
589 589 n = r.tip()
590 590 fp = make_file(repo, r, opts['output'], node=n, pathname=abs)
591 591 fp.write(r.read(n))
592 592
593 593 def clone(ui, source, dest=None, **opts):
594 594 """make a copy of an existing repository"""
595 595 if dest is None:
596 596 dest = os.path.basename(os.path.normpath(source))
597 597
598 598 if os.path.exists(dest):
599 599 raise util.Abort("destination '%s' already exists", dest)
600 600
601 601 dest = os.path.realpath(dest)
602 602
603 603 class Dircleanup:
604 604 def __init__(self, dir_):
605 605 self.rmtree = shutil.rmtree
606 606 self.dir_ = dir_
607 607 os.mkdir(dir_)
608 608 def close(self):
609 609 self.dir_ = None
610 610 def __del__(self):
611 611 if self.dir_:
612 612 self.rmtree(self.dir_, True)
613 613
614 614 if opts['ssh']:
615 615 ui.setconfig("ui", "ssh", opts['ssh'])
616 616 if opts['remotecmd']:
617 617 ui.setconfig("ui", "remotecmd", opts['remotecmd'])
618 618
619 619 if not os.path.exists(source):
620 620 source = ui.expandpath(source)
621 621
622 622 d = Dircleanup(dest)
623 623 abspath = source
624 624 other = hg.repository(ui, source)
625 625
626 626 copy = False
627 627 if other.dev() != -1:
628 628 abspath = os.path.abspath(source)
629 629 if not opts['pull']:
630 630 copy = True
631 631
632 632 if copy:
633 633 try:
634 634 # we use a lock here because if we race with commit, we
635 635 # can end up with extra data in the cloned revlogs that's
636 636 # not pointed to by changesets, thus causing verify to
637 637 # fail
638 638 l1 = lock.lock(os.path.join(source, ".hg", "lock"))
639 639 except OSError:
640 640 copy = False
641 641
642 642 if copy:
643 643 # we lock here to avoid premature writing to the target
644 644 os.mkdir(os.path.join(dest, ".hg"))
645 645 l2 = lock.lock(os.path.join(dest, ".hg", "lock"))
646 646
647 647 files = "data 00manifest.d 00manifest.i 00changelog.d 00changelog.i"
648 648 for f in files.split():
649 649 src = os.path.join(source, ".hg", f)
650 650 dst = os.path.join(dest, ".hg", f)
651 651 util.copyfiles(src, dst)
652 652
653 653 repo = hg.repository(ui, dest)
654 654
655 655 else:
656 656 repo = hg.repository(ui, dest, create=1)
657 657 repo.pull(other)
658 658
659 659 f = repo.opener("hgrc", "w", text=True)
660 660 f.write("[paths]\n")
661 661 f.write("default = %s\n" % abspath)
662 662
663 663 if not opts['noupdate']:
664 664 update(ui, repo)
665 665
666 666 d.close()
667 667
668 668 def commit(ui, repo, *pats, **opts):
669 669 """commit the specified files or all outstanding changes"""
670 670 if opts['text']:
671 671 ui.warn("Warning: -t and --text is deprecated,"
672 672 " please use -m or --message instead.\n")
673 673 message = opts['message'] or opts['text']
674 674 logfile = opts['logfile']
675 675
676 676 if message and logfile:
677 677 raise util.Abort('options --message and --logfile are mutually '
678 678 'exclusive')
679 679 if not message and logfile:
680 680 try:
681 681 if logfile == '-':
682 682 message = sys.stdin.read()
683 683 else:
684 684 message = open(logfile).read()
685 685 except IOError, inst:
686 686 raise util.Abort("can't read commit message '%s': %s" %
687 687 (logfile, inst.strerror))
688 688
689 689 if opts['addremove']:
690 690 addremove(ui, repo, *pats, **opts)
691 691 cwd = repo.getcwd()
692 692 if not pats and cwd:
693 693 opts['include'] = [os.path.join(cwd, i) for i in opts['include']]
694 694 opts['exclude'] = [os.path.join(cwd, x) for x in opts['exclude']]
695 695 fns, match, anypats = matchpats(repo, (pats and repo.getcwd()) or '',
696 696 pats, opts)
697 697 if pats:
698 698 c, a, d, u = repo.changes(files=fns, match=match)
699 699 files = c + a + [fn for fn in d if repo.dirstate.state(fn) == 'r']
700 700 else:
701 701 files = []
702 702 try:
703 703 repo.commit(files, message, opts['user'], opts['date'], match)
704 704 except ValueError, inst:
705 705 raise util.Abort(str(inst))
706 706
707 707 def docopy(ui, repo, pats, opts):
708 708 if not pats:
709 709 raise util.Abort('no source or destination specified')
710 710 elif len(pats) == 1:
711 711 raise util.Abort('no destination specified')
712 712 pats = list(pats)
713 713 dest = pats.pop()
714 714 sources = []
715 715
716 716 def okaytocopy(abs, rel, exact):
717 717 reasons = {'?': 'is not managed',
718 718 'a': 'has been marked for add'}
719 719 reason = reasons.get(repo.dirstate.state(abs))
720 720 if reason:
721 721 if exact: ui.warn('%s: not copying - file %s\n' % (rel, reason))
722 722 else:
723 723 return True
724 724
725 725 for src, abs, rel, exact in walk(repo, pats, opts):
726 726 if okaytocopy(abs, rel, exact):
727 727 sources.append((abs, rel, exact))
728 728 if not sources:
729 729 raise util.Abort('no files to copy')
730 730
731 731 cwd = repo.getcwd()
732 732 absdest = util.canonpath(repo.root, cwd, dest)
733 733 reldest = util.pathto(cwd, absdest)
734 734 if os.path.exists(reldest):
735 735 destisfile = not os.path.isdir(reldest)
736 736 else:
737 737 destisfile = len(sources) == 1 or repo.dirstate.state(absdest) != '?'
738 738
739 739 if destisfile:
740 740 if opts['parents']:
741 741 raise util.Abort('with --parents, destination must be a directory')
742 742 elif len(sources) > 1:
743 743 raise util.Abort('with multiple sources, destination must be a '
744 744 'directory')
745 745 errs, copied = 0, []
746 746 for abs, rel, exact in sources:
747 747 if opts['parents']:
748 748 mydest = os.path.join(dest, rel)
749 749 elif destisfile:
750 750 mydest = reldest
751 751 else:
752 752 mydest = os.path.join(dest, os.path.basename(rel))
753 753 myabsdest = util.canonpath(repo.root, cwd, mydest)
754 754 myreldest = util.pathto(cwd, myabsdest)
755 755 if not opts['force'] and repo.dirstate.state(myabsdest) not in 'a?':
756 756 ui.warn('%s: not overwriting - file already managed\n' % myreldest)
757 757 continue
758 758 mydestdir = os.path.dirname(myreldest) or '.'
759 759 if not opts['after']:
760 760 try:
761 761 if opts['parents']: os.makedirs(mydestdir)
762 762 elif not destisfile: os.mkdir(mydestdir)
763 763 except OSError, inst:
764 764 if inst.errno != errno.EEXIST: raise
765 765 if ui.verbose or not exact:
766 766 ui.status('copying %s to %s\n' % (rel, myreldest))
767 767 if not opts['after']:
768 768 try:
769 769 shutil.copyfile(rel, myreldest)
770 770 n = repo.manifest.tip()
771 771 mf = repo.manifest.readflags(n)
772 772 util.set_exec(myreldest, util.is_exec(rel, mf[abs]))
773 773 except shutil.Error, inst:
774 774 raise util.Abort(str(inst))
775 775 except IOError, inst:
776 776 if inst.errno == errno.ENOENT:
777 777 ui.warn('%s: deleted in working copy\n' % rel)
778 778 else:
779 779 ui.warn('%s: cannot copy - %s\n' % (rel, inst.strerror))
780 780 errs += 1
781 781 continue
782 782 repo.copy(abs, myabsdest)
783 783 copied.append((abs, rel, exact))
784 784 if errs:
785 785 ui.warn('(consider using --after)\n')
786 786 return errs, copied
787 787
788 788 def copy(ui, repo, *pats, **opts):
789 789 """mark files as copied for the next commit"""
790 790 errs, copied = docopy(ui, repo, pats, opts)
791 791 return errs
792 792
793 793 def debugancestor(ui, index, rev1, rev2):
794 794 """find the ancestor revision of two revisions in a given index"""
795 795 r = revlog.revlog(file, index, "")
796 796 a = r.ancestor(r.lookup(rev1), r.lookup(rev2))
797 797 ui.write("%d:%s\n" % (r.rev(a), hex(a)))
798 798
799 799 def debugcheckstate(ui, repo):
800 800 """validate the correctness of the current dirstate"""
801 801 parent1, parent2 = repo.dirstate.parents()
802 802 repo.dirstate.read()
803 803 dc = repo.dirstate.map
804 804 keys = dc.keys()
805 805 keys.sort()
806 806 m1n = repo.changelog.read(parent1)[0]
807 807 m2n = repo.changelog.read(parent2)[0]
808 808 m1 = repo.manifest.read(m1n)
809 809 m2 = repo.manifest.read(m2n)
810 810 errors = 0
811 811 for f in dc:
812 812 state = repo.dirstate.state(f)
813 813 if state in "nr" and f not in m1:
814 814 ui.warn("%s in state %s, but not in manifest1\n" % (f, state))
815 815 errors += 1
816 816 if state in "a" and f in m1:
817 817 ui.warn("%s in state %s, but also in manifest1\n" % (f, state))
818 818 errors += 1
819 819 if state in "m" and f not in m1 and f not in m2:
820 820 ui.warn("%s in state %s, but not in either manifest\n" %
821 821 (f, state))
822 822 errors += 1
823 823 for f in m1:
824 824 state = repo.dirstate.state(f)
825 825 if state not in "nrm":
826 826 ui.warn("%s in manifest1, but listed as state %s" % (f, state))
827 827 errors += 1
828 828 if errors:
829 829 raise util.Abort(".hg/dirstate inconsistent with current parent's manifest")
830 830
831 831 def debugconfig(ui):
832 832 """show combined config settings from all hgrc files"""
833 833 try:
834 834 repo = hg.repository(ui)
835 835 except hg.RepoError:
836 836 pass
837 837 for section, name, value in ui.walkconfig():
838 838 ui.write('%s.%s=%s\n' % (section, name, value))
839 839
840 840 def debugstate(ui, repo):
841 841 """show the contents of the current dirstate"""
842 842 repo.dirstate.read()
843 843 dc = repo.dirstate.map
844 844 keys = dc.keys()
845 845 keys.sort()
846 846 for file_ in keys:
847 847 ui.write("%c %3o %10d %s %s\n"
848 848 % (dc[file_][0], dc[file_][1] & 0777, dc[file_][2],
849 849 time.strftime("%x %X",
850 850 time.localtime(dc[file_][3])), file_))
851 851 for f in repo.dirstate.copies:
852 852 ui.write("copy: %s -> %s\n" % (repo.dirstate.copies[f], f))
853 853
854 854 def debugdata(ui, file_, rev):
855 855 """dump the contents of an data file revision"""
856 856 r = revlog.revlog(file, file_[:-2] + ".i", file_)
857 857 try:
858 858 ui.write(r.revision(r.lookup(rev)))
859 859 except KeyError:
860 860 raise util.Abort('invalid revision identifier %s', rev)
861 861
862 862 def debugindex(ui, file_):
863 863 """dump the contents of an index file"""
864 864 r = revlog.revlog(file, file_, "")
865 865 ui.write(" rev offset length base linkrev" +
866 866 " nodeid p1 p2\n")
867 867 for i in range(r.count()):
868 868 e = r.index[i]
869 869 ui.write("% 6d % 9d % 7d % 6d % 7d %s %s %s\n" % (
870 870 i, e[0], e[1], e[2], e[3],
871 871 short(e[6]), short(e[4]), short(e[5])))
872 872
873 873 def debugindexdot(ui, file_):
874 874 """dump an index DAG as a .dot file"""
875 875 r = revlog.revlog(file, file_, "")
876 876 ui.write("digraph G {\n")
877 877 for i in range(r.count()):
878 878 e = r.index[i]
879 879 ui.write("\t%d -> %d\n" % (r.rev(e[4]), i))
880 880 if e[5] != nullid:
881 881 ui.write("\t%d -> %d\n" % (r.rev(e[5]), i))
882 882 ui.write("}\n")
883 883
884 884 def debugrename(ui, repo, file, rev=None):
885 885 """dump rename information"""
886 886 r = repo.file(relpath(repo, [file])[0])
887 887 if rev:
888 888 try:
889 889 # assume all revision numbers are for changesets
890 890 n = repo.lookup(rev)
891 891 change = repo.changelog.read(n)
892 892 m = repo.manifest.read(change[0])
893 893 n = m[relpath(repo, [file])[0]]
894 894 except hg.RepoError, KeyError:
895 895 n = r.lookup(rev)
896 896 else:
897 897 n = r.tip()
898 898 m = r.renamed(n)
899 899 if m:
900 900 ui.write("renamed from %s:%s\n" % (m[0], hex(m[1])))
901 901 else:
902 902 ui.write("not renamed\n")
903 903
904 904 def debugwalk(ui, repo, *pats, **opts):
905 905 """show how files match on given patterns"""
906 906 items = list(walk(repo, pats, opts))
907 907 if not items:
908 908 return
909 909 fmt = '%%s %%-%ds %%-%ds %%s' % (
910 910 max([len(abs) for (src, abs, rel, exact) in items]),
911 911 max([len(rel) for (src, abs, rel, exact) in items]))
912 912 for src, abs, rel, exact in items:
913 913 line = fmt % (src, abs, rel, exact and 'exact' or '')
914 914 ui.write("%s\n" % line.rstrip())
915 915
916 916 def diff(ui, repo, *pats, **opts):
917 917 """diff working directory (or selected files)"""
918 918 node1, node2 = None, None
919 919 revs = [repo.lookup(x) for x in opts['rev']]
920 920
921 921 if len(revs) > 0:
922 922 node1 = revs[0]
923 923 if len(revs) > 1:
924 924 node2 = revs[1]
925 925 if len(revs) > 2:
926 926 raise util.Abort("too many revisions to diff")
927 927
928 928 fns, matchfn, anypats = matchpats(repo, repo.getcwd(), pats, opts)
929 929
930 930 dodiff(sys.stdout, ui, repo, node1, node2, fns, match=matchfn,
931 931 text=opts['text'])
932 932
933 933 def doexport(ui, repo, changeset, seqno, total, revwidth, opts):
934 934 node = repo.lookup(changeset)
935 935 prev, other = repo.changelog.parents(node)
936 936 change = repo.changelog.read(node)
937 937
938 938 fp = make_file(repo, repo.changelog, opts['output'],
939 939 node=node, total=total, seqno=seqno,
940 940 revwidth=revwidth)
941 941 if fp != sys.stdout:
942 942 ui.note("%s\n" % fp.name)
943 943
944 944 fp.write("# HG changeset patch\n")
945 945 fp.write("# User %s\n" % change[1])
946 946 fp.write("# Node ID %s\n" % hex(node))
947 947 fp.write("# Parent %s\n" % hex(prev))
948 948 if other != nullid:
949 949 fp.write("# Parent %s\n" % hex(other))
950 950 fp.write(change[4].rstrip())
951 951 fp.write("\n\n")
952 952
953 953 dodiff(fp, ui, repo, prev, node, text=opts['text'])
954 954 if fp != sys.stdout:
955 955 fp.close()
956 956
957 957 def export(ui, repo, *changesets, **opts):
958 958 """dump the header and diffs for one or more changesets"""
959 959 if not changesets:
960 960 raise util.Abort("export requires at least one changeset")
961 961 seqno = 0
962 962 revs = list(revrange(ui, repo, changesets))
963 963 total = len(revs)
964 964 revwidth = max(map(len, revs))
965 965 ui.note(len(revs) > 1 and "Exporting patches:\n" or "Exporting patch:\n")
966 966 for cset in revs:
967 967 seqno += 1
968 968 doexport(ui, repo, cset, seqno, total, revwidth, opts)
969 969
970 970 def forget(ui, repo, *pats, **opts):
971 971 """don't add the specified files on the next commit"""
972 972 forget = []
973 973 for src, abs, rel, exact in walk(repo, pats, opts):
974 974 if repo.dirstate.state(abs) == 'a':
975 975 forget.append(abs)
976 976 if ui.verbose or not exact:
977 977 ui.status('forgetting ', rel, '\n')
978 978 repo.forget(forget)
979 979
980 980 def grep(ui, repo, pattern, *pats, **opts):
981 981 """search for a pattern in specified files and revisions"""
982 982 reflags = 0
983 983 if opts['ignore_case']:
984 984 reflags |= re.I
985 985 regexp = re.compile(pattern, reflags)
986 986 sep, eol = ':', '\n'
987 987 if opts['print0']:
988 988 sep = eol = '\0'
989 989
990 990 fcache = {}
991 991 def getfile(fn):
992 992 if fn not in fcache:
993 993 fcache[fn] = repo.file(fn)
994 994 return fcache[fn]
995 995
996 996 def matchlines(body):
997 997 begin = 0
998 998 linenum = 0
999 999 while True:
1000 1000 match = regexp.search(body, begin)
1001 1001 if not match:
1002 1002 break
1003 1003 mstart, mend = match.span()
1004 1004 linenum += body.count('\n', begin, mstart) + 1
1005 1005 lstart = body.rfind('\n', begin, mstart) + 1 or begin
1006 1006 lend = body.find('\n', mend)
1007 1007 yield linenum, mstart - lstart, mend - lstart, body[lstart:lend]
1008 1008 begin = lend + 1
1009 1009
1010 1010 class linestate:
1011 1011 def __init__(self, line, linenum, colstart, colend):
1012 1012 self.line = line
1013 1013 self.linenum = linenum
1014 1014 self.colstart = colstart
1015 1015 self.colend = colend
1016 1016 def __eq__(self, other):
1017 1017 return self.line == other.line
1018 1018 def __hash__(self):
1019 1019 return hash(self.line)
1020 1020
1021 1021 matches = {}
1022 1022 def grepbody(fn, rev, body):
1023 1023 matches[rev].setdefault(fn, {})
1024 1024 m = matches[rev][fn]
1025 1025 for lnum, cstart, cend, line in matchlines(body):
1026 1026 s = linestate(line, lnum, cstart, cend)
1027 1027 m[s] = s
1028 1028
1029 1029 prev = {}
1030 1030 ucache = {}
1031 1031 def display(fn, rev, states, prevstates):
1032 1032 diff = list(sets.Set(states).symmetric_difference(sets.Set(prevstates)))
1033 1033 diff.sort(lambda x, y: cmp(x.linenum, y.linenum))
1034 1034 counts = {'-': 0, '+': 0}
1035 1035 filerevmatches = {}
1036 1036 for l in diff:
1037 1037 if incrementing or not opts['all']:
1038 1038 change = ((l in prevstates) and '-') or '+'
1039 1039 r = rev
1040 1040 else:
1041 1041 change = ((l in states) and '-') or '+'
1042 1042 r = prev[fn]
1043 1043 cols = [fn, str(rev)]
1044 1044 if opts['line_number']: cols.append(str(l.linenum))
1045 1045 if opts['all']: cols.append(change)
1046 1046 if opts['user']: cols.append(trimuser(ui, getchange(rev)[1], rev,
1047 1047 ucache))
1048 1048 if opts['files_with_matches']:
1049 1049 c = (fn, rev)
1050 1050 if c in filerevmatches: continue
1051 1051 filerevmatches[c] = 1
1052 1052 else:
1053 1053 cols.append(l.line)
1054 1054 ui.write(sep.join(cols), eol)
1055 1055 counts[change] += 1
1056 1056 return counts['+'], counts['-']
1057 1057
1058 1058 fstate = {}
1059 1059 skip = {}
1060 1060 changeiter, getchange = walkchangerevs(ui, repo, repo.getcwd(), pats, opts)
1061 1061 count = 0
1062 1062 incrementing = False
1063 1063 for st, rev, fns in changeiter:
1064 1064 if st == 'window':
1065 1065 incrementing = rev
1066 1066 matches.clear()
1067 1067 elif st == 'add':
1068 1068 change = repo.changelog.read(repo.lookup(str(rev)))
1069 1069 mf = repo.manifest.read(change[0])
1070 1070 matches[rev] = {}
1071 1071 for fn in fns:
1072 1072 if fn in skip: continue
1073 1073 fstate.setdefault(fn, {})
1074 1074 try:
1075 1075 grepbody(fn, rev, getfile(fn).read(mf[fn]))
1076 1076 except KeyError:
1077 1077 pass
1078 1078 elif st == 'iter':
1079 1079 states = matches[rev].items()
1080 1080 states.sort()
1081 1081 for fn, m in states:
1082 1082 if fn in skip: continue
1083 1083 if incrementing or not opts['all'] or fstate[fn]:
1084 1084 pos, neg = display(fn, rev, m, fstate[fn])
1085 1085 count += pos + neg
1086 1086 if pos and not opts['all']:
1087 1087 skip[fn] = True
1088 1088 fstate[fn] = m
1089 1089 prev[fn] = rev
1090 1090
1091 1091 if not incrementing:
1092 1092 fstate = fstate.items()
1093 1093 fstate.sort()
1094 1094 for fn, state in fstate:
1095 1095 if fn in skip: continue
1096 1096 display(fn, rev, {}, state)
1097 1097 return (count == 0 and 1) or 0
1098 1098
1099 1099 def heads(ui, repo, **opts):
1100 1100 """show current repository heads"""
1101 1101 heads = repo.changelog.heads()
1102 1102 br = None
1103 1103 if opts['branches']:
1104 1104 br = repo.branchlookup(heads)
1105 1105 for n in repo.changelog.heads():
1106 1106 show_changeset(ui, repo, changenode=n, brinfo=br)
1107 1107
1108 1108 def identify(ui, repo):
1109 1109 """print information about the working copy"""
1110 1110 parents = [p for p in repo.dirstate.parents() if p != nullid]
1111 1111 if not parents:
1112 1112 ui.write("unknown\n")
1113 1113 return
1114 1114
1115 1115 hexfunc = ui.verbose and hex or short
1116 1116 (c, a, d, u) = repo.changes()
1117 1117 output = ["%s%s" % ('+'.join([hexfunc(parent) for parent in parents]),
1118 1118 (c or a or d) and "+" or "")]
1119 1119
1120 1120 if not ui.quiet:
1121 1121 # multiple tags for a single parent separated by '/'
1122 1122 parenttags = ['/'.join(tags)
1123 1123 for tags in map(repo.nodetags, parents) if tags]
1124 1124 # tags for multiple parents separated by ' + '
1125 1125 if parenttags:
1126 1126 output.append(' + '.join(parenttags))
1127 1127
1128 1128 ui.write("%s\n" % ' '.join(output))
1129 1129
1130 1130 def import_(ui, repo, patch1, *patches, **opts):
1131 1131 """import an ordered set of patches"""
1132 1132 patches = (patch1,) + patches
1133 1133
1134 1134 if not opts['force']:
1135 1135 (c, a, d, u) = repo.changes()
1136 1136 if c or a or d:
1137 1137 raise util.Abort("outstanding uncommitted changes")
1138 1138
1139 1139 d = opts["base"]
1140 1140 strip = opts["strip"]
1141 1141
1142 1142 mailre = re.compile(r'(?:From |[\w-]+:)')
1143 1143 diffre = re.compile(r'(?:diff -|--- .*\s+\w+ \w+ +\d+ \d+:\d+:\d+ \d+)')
1144 1144
1145 1145 for patch in patches:
1146 1146 ui.status("applying %s\n" % patch)
1147 1147 pf = os.path.join(d, patch)
1148 1148
1149 1149 message = []
1150 1150 user = None
1151 1151 hgpatch = False
1152 1152 for line in file(pf):
1153 1153 line = line.rstrip()
1154 1154 if (not message and not hgpatch and
1155 1155 mailre.match(line) and not opts['force']):
1156 1156 if len(line) > 35: line = line[:32] + '...'
1157 1157 raise util.Abort('first line looks like a '
1158 1158 'mail header: ' + line)
1159 1159 if diffre.match(line):
1160 1160 break
1161 1161 elif hgpatch:
1162 1162 # parse values when importing the result of an hg export
1163 1163 if line.startswith("# User "):
1164 1164 user = line[7:]
1165 1165 ui.debug('User: %s\n' % user)
1166 1166 elif not line.startswith("# ") and line:
1167 1167 message.append(line)
1168 1168 hgpatch = False
1169 1169 elif line == '# HG changeset patch':
1170 1170 hgpatch = True
1171 1171 message = [] # We may have collected garbage
1172 1172 else:
1173 1173 message.append(line)
1174 1174
1175 1175 # make sure message isn't empty
1176 1176 if not message:
1177 1177 message = "imported patch %s\n" % patch
1178 1178 else:
1179 1179 message = "%s\n" % '\n'.join(message)
1180 1180 ui.debug('message:\n%s\n' % message)
1181 1181
1182 1182 files = util.patch(strip, pf, ui)
1183 1183
1184 1184 if len(files) > 0:
1185 1185 addremove(ui, repo, *files)
1186 1186 repo.commit(files, message, user)
1187 1187
1188 1188 def incoming(ui, repo, source="default", **opts):
1189 1189 """show new changesets found in source"""
1190 1190 source = ui.expandpath(source)
1191 1191 other = hg.repository(ui, source)
1192 1192 if not other.local():
1193 1193 raise util.Abort("incoming doesn't work for remote repositories yet")
1194 1194 o = repo.findincoming(other)
1195 1195 if not o:
1196 1196 return
1197 1197 o = other.newer(o)
1198 1198 for n in o:
1199 1199 show_changeset(ui, other, changenode=n)
1200 1200 if opts['patch']:
1201 1201 prev = other.changelog.parents(n)[0]
1202 1202 dodiff(ui, ui, other, prev, n)
1203 1203 ui.write("\n")
1204 1204
1205 1205 def init(ui, dest="."):
1206 1206 """create a new repository in the given directory"""
1207 1207 if not os.path.exists(dest):
1208 1208 os.mkdir(dest)
1209 1209 hg.repository(ui, dest, create=1)
1210 1210
1211 1211 def locate(ui, repo, *pats, **opts):
1212 1212 """locate files matching specific patterns"""
1213 1213 end = opts['print0'] and '\0' or '\n'
1214 1214
1215 1215 for src, abs, rel, exact in walk(repo, pats, opts, '(?:.*/|)'):
1216 1216 if repo.dirstate.state(abs) == '?':
1217 1217 continue
1218 1218 if opts['fullpath']:
1219 1219 ui.write(os.path.join(repo.root, abs), end)
1220 1220 else:
1221 1221 ui.write(rel, end)
1222 1222
1223 1223 def log(ui, repo, *pats, **opts):
1224 1224 """show revision history of entire repository or files"""
1225 1225 class dui:
1226 1226 # Implement and delegate some ui protocol. Save hunks of
1227 1227 # output for later display in the desired order.
1228 1228 def __init__(self, ui):
1229 1229 self.ui = ui
1230 1230 self.hunk = {}
1231 1231 def bump(self, rev):
1232 1232 self.rev = rev
1233 1233 self.hunk[rev] = []
1234 1234 def note(self, *args):
1235 1235 if self.verbose:
1236 1236 self.write(*args)
1237 1237 def status(self, *args):
1238 1238 if not self.quiet:
1239 1239 self.write(*args)
1240 1240 def write(self, *args):
1241 1241 self.hunk[self.rev].append(args)
1242 1242 def __getattr__(self, key):
1243 1243 return getattr(self.ui, key)
1244 1244 cwd = repo.getcwd()
1245 1245 if not pats and cwd:
1246 1246 opts['include'] = [os.path.join(cwd, i) for i in opts['include']]
1247 1247 opts['exclude'] = [os.path.join(cwd, x) for x in opts['exclude']]
1248 1248 changeiter, getchange = walkchangerevs(ui, repo, (pats and cwd) or '',
1249 1249 pats, opts)
1250 1250 for st, rev, fns in changeiter:
1251 1251 if st == 'window':
1252 1252 du = dui(ui)
1253 1253 elif st == 'add':
1254 1254 du.bump(rev)
1255 1255 br = None
1256 1256 if opts['branch']:
1257 1257 br = repo.branchlookup([repo.changelog.node(rev)])
1258 1258 show_changeset(du, repo, rev, brinfo=br)
1259 1259 if opts['patch']:
1260 1260 changenode = repo.changelog.node(rev)
1261 1261 prev, other = repo.changelog.parents(changenode)
1262 1262 dodiff(du, du, repo, prev, changenode, fns)
1263 1263 du.write("\n\n")
1264 1264 elif st == 'iter':
1265 1265 for args in du.hunk[rev]:
1266 1266 ui.write(*args)
1267 1267
1268 1268 def manifest(ui, repo, rev=None):
1269 1269 """output the latest or given revision of the project manifest"""
1270 1270 if rev:
1271 1271 try:
1272 1272 # assume all revision numbers are for changesets
1273 1273 n = repo.lookup(rev)
1274 1274 change = repo.changelog.read(n)
1275 1275 n = change[0]
1276 1276 except hg.RepoError:
1277 1277 n = repo.manifest.lookup(rev)
1278 1278 else:
1279 1279 n = repo.manifest.tip()
1280 1280 m = repo.manifest.read(n)
1281 1281 mf = repo.manifest.readflags(n)
1282 1282 files = m.keys()
1283 1283 files.sort()
1284 1284
1285 1285 for f in files:
1286 1286 ui.write("%40s %3s %s\n" % (hex(m[f]), mf[f] and "755" or "644", f))
1287 1287
1288 1288 def outgoing(ui, repo, dest="default-push", **opts):
1289 1289 """show changesets not found in destination"""
1290 1290 dest = ui.expandpath(dest)
1291 1291 other = hg.repository(ui, dest)
1292 1292 o = repo.findoutgoing(other)
1293 1293 o = repo.newer(o)
1294 1294 for n in o:
1295 1295 show_changeset(ui, repo, changenode=n)
1296 1296 if opts['patch']:
1297 1297 prev = repo.changelog.parents(n)[0]
1298 1298 dodiff(ui, ui, repo, prev, n)
1299 1299 ui.write("\n")
1300 1300
1301 1301 def parents(ui, repo, rev=None):
1302 1302 """show the parents of the working dir or revision"""
1303 1303 if rev:
1304 1304 p = repo.changelog.parents(repo.lookup(rev))
1305 1305 else:
1306 1306 p = repo.dirstate.parents()
1307 1307
1308 1308 for n in p:
1309 1309 if n != nullid:
1310 1310 show_changeset(ui, repo, changenode=n)
1311 1311
1312 1312 def paths(ui, search=None):
1313 1313 """show definition of symbolic path names"""
1314 1314 try:
1315 1315 repo = hg.repository(ui=ui)
1316 1316 except hg.RepoError:
1317 1317 pass
1318 1318
1319 1319 if search:
1320 1320 for name, path in ui.configitems("paths"):
1321 1321 if name == search:
1322 1322 ui.write("%s\n" % path)
1323 1323 return
1324 1324 ui.warn("not found!\n")
1325 1325 return 1
1326 1326 else:
1327 1327 for name, path in ui.configitems("paths"):
1328 1328 ui.write("%s = %s\n" % (name, path))
1329 1329
1330 1330 def pull(ui, repo, source="default", **opts):
1331 1331 """pull changes from the specified source"""
1332 1332 source = ui.expandpath(source)
1333 1333 ui.status('pulling from %s\n' % (source))
1334 1334
1335 1335 if opts['ssh']:
1336 1336 ui.setconfig("ui", "ssh", opts['ssh'])
1337 1337 if opts['remotecmd']:
1338 1338 ui.setconfig("ui", "remotecmd", opts['remotecmd'])
1339 1339
1340 1340 other = hg.repository(ui, source)
1341 1341 r = repo.pull(other)
1342 1342 if not r:
1343 1343 if opts['update']:
1344 1344 return update(ui, repo)
1345 1345 else:
1346 1346 ui.status("(run 'hg update' to get a working copy)\n")
1347 1347
1348 1348 return r
1349 1349
1350 1350 def push(ui, repo, dest="default-push", force=False, ssh=None, remotecmd=None):
1351 1351 """push changes to the specified destination"""
1352 1352 dest = ui.expandpath(dest)
1353 1353 ui.status('pushing to %s\n' % (dest))
1354 1354
1355 1355 if ssh:
1356 1356 ui.setconfig("ui", "ssh", ssh)
1357 1357 if remotecmd:
1358 1358 ui.setconfig("ui", "remotecmd", remotecmd)
1359 1359
1360 1360 other = hg.repository(ui, dest)
1361 1361 r = repo.push(other, force)
1362 1362 return r
1363 1363
1364 1364 def rawcommit(ui, repo, *flist, **rc):
1365 1365 "raw commit interface"
1366 1366 if rc['text']:
1367 1367 ui.warn("Warning: -t and --text is deprecated,"
1368 1368 " please use -m or --message instead.\n")
1369 1369 message = rc['message'] or rc['text']
1370 1370 if not message and rc['logfile']:
1371 1371 try:
1372 1372 message = open(rc['logfile']).read()
1373 1373 except IOError:
1374 1374 pass
1375 1375 if not message and not rc['logfile']:
1376 1376 raise util.Abort("missing commit message")
1377 1377
1378 1378 files = relpath(repo, list(flist))
1379 1379 if rc['files']:
1380 1380 files += open(rc['files']).read().splitlines()
1381 1381
1382 1382 rc['parent'] = map(repo.lookup, rc['parent'])
1383 1383
1384 1384 try:
1385 1385 repo.rawcommit(files, message, rc['user'], rc['date'], *rc['parent'])
1386 1386 except ValueError, inst:
1387 1387 raise util.Abort(str(inst))
1388 1388
1389 1389 def recover(ui, repo):
1390 1390 """roll back an interrupted transaction"""
1391 1391 repo.recover()
1392 1392
1393 1393 def remove(ui, repo, pat, *pats, **opts):
1394 1394 """remove the specified files on the next commit"""
1395 1395 names = []
1396 1396 def okaytoremove(abs, rel, exact):
1397 1397 c, a, d, u = repo.changes(files = [abs])
1398 1398 reason = None
1399 1399 if c: reason = 'is modified'
1400 1400 elif a: reason = 'has been marked for add'
1401 1401 elif u: reason = 'is not managed'
1402 1402 if reason:
1403 1403 if exact: ui.warn('not removing %s: file %s\n' % (rel, reason))
1404 1404 else:
1405 1405 return True
1406 1406 for src, abs, rel, exact in walk(repo, (pat,) + pats, opts):
1407 1407 if okaytoremove(abs, rel, exact):
1408 1408 if ui.verbose or not exact: ui.status('removing %s\n' % rel)
1409 1409 names.append(abs)
1410 1410 for name in names:
1411 1411 try:
1412 1412 os.unlink(name)
1413 1413 except OSError, inst:
1414 1414 if inst.errno != errno.ENOENT: raise
1415 1415 repo.remove(names)
1416 1416
1417 1417 def rename(ui, repo, *pats, **opts):
1418 1418 """rename files; equivalent of copy + remove"""
1419 1419 errs, copied = docopy(ui, repo, pats, opts)
1420 1420 names = []
1421 1421 for abs, rel, exact in copied:
1422 1422 if ui.verbose or not exact: ui.status('removing %s\n' % rel)
1423 1423 try:
1424 1424 os.unlink(rel)
1425 1425 except OSError, inst:
1426 1426 if inst.errno != errno.ENOENT: raise
1427 1427 names.append(abs)
1428 1428 repo.remove(names)
1429 1429 return errs
1430 1430
1431 1431 def revert(ui, repo, *names, **opts):
1432 1432 """revert modified files or dirs back to their unmodified states"""
1433 1433 node = opts['rev'] and repo.lookup(opts['rev']) or \
1434 1434 repo.dirstate.parents()[0]
1435 1435 root = os.path.realpath(repo.root)
1436 1436
1437 1437 def trimpath(p):
1438 1438 p = os.path.realpath(p)
1439 1439 if p.startswith(root):
1440 1440 rest = p[len(root):]
1441 1441 if not rest:
1442 1442 return rest
1443 1443 if p.startswith(os.sep):
1444 1444 return rest[1:]
1445 1445 return p
1446 1446
1447 1447 relnames = map(trimpath, names or [os.getcwd()])
1448 1448 chosen = {}
1449 1449
1450 1450 def choose(name):
1451 1451 def body(name):
1452 1452 for r in relnames:
1453 1453 if not name.startswith(r):
1454 1454 continue
1455 1455 rest = name[len(r):]
1456 1456 if not rest:
1457 1457 return r, True
1458 1458 depth = rest.count(os.sep)
1459 1459 if not r:
1460 1460 if depth == 0 or not opts['nonrecursive']:
1461 1461 return r, True
1462 1462 elif rest[0] == os.sep:
1463 1463 if depth == 1 or not opts['nonrecursive']:
1464 1464 return r, True
1465 1465 return None, False
1466 1466 relname, ret = body(name)
1467 1467 if ret:
1468 1468 chosen[relname] = 1
1469 1469 return ret
1470 1470
1471 1471 r = repo.update(node, False, True, choose, False)
1472 1472 for n in relnames:
1473 1473 if n not in chosen:
1474 1474 ui.warn('error: no matches for %s\n' % n)
1475 1475 r = 1
1476 1476 sys.stdout.flush()
1477 1477 return r
1478 1478
1479 1479 def root(ui, repo):
1480 1480 """print the root (top) of the current working dir"""
1481 1481 ui.write(repo.root + "\n")
1482 1482
1483 1483 def serve(ui, repo, **opts):
1484 1484 """export the repository via HTTP"""
1485 1485
1486 1486 if opts["stdio"]:
1487 1487 fin, fout = sys.stdin, sys.stdout
1488 1488 sys.stdout = sys.stderr
1489 1489
1490 1490 def getarg():
1491 1491 argline = fin.readline()[:-1]
1492 1492 arg, l = argline.split()
1493 1493 val = fin.read(int(l))
1494 1494 return arg, val
1495 1495 def respond(v):
1496 1496 fout.write("%d\n" % len(v))
1497 1497 fout.write(v)
1498 1498 fout.flush()
1499 1499
1500 1500 lock = None
1501 1501
1502 1502 while 1:
1503 1503 cmd = fin.readline()[:-1]
1504 1504 if cmd == '':
1505 1505 return
1506 1506 if cmd == "heads":
1507 1507 h = repo.heads()
1508 1508 respond(" ".join(map(hex, h)) + "\n")
1509 1509 if cmd == "lock":
1510 1510 lock = repo.lock()
1511 1511 respond("")
1512 1512 if cmd == "unlock":
1513 1513 if lock:
1514 1514 lock.release()
1515 1515 lock = None
1516 1516 respond("")
1517 1517 elif cmd == "branches":
1518 1518 arg, nodes = getarg()
1519 1519 nodes = map(bin, nodes.split(" "))
1520 1520 r = []
1521 1521 for b in repo.branches(nodes):
1522 1522 r.append(" ".join(map(hex, b)) + "\n")
1523 1523 respond("".join(r))
1524 1524 elif cmd == "between":
1525 1525 arg, pairs = getarg()
1526 1526 pairs = [map(bin, p.split("-")) for p in pairs.split(" ")]
1527 1527 r = []
1528 1528 for b in repo.between(pairs):
1529 1529 r.append(" ".join(map(hex, b)) + "\n")
1530 1530 respond("".join(r))
1531 1531 elif cmd == "changegroup":
1532 1532 nodes = []
1533 1533 arg, roots = getarg()
1534 1534 nodes = map(bin, roots.split(" "))
1535 1535
1536 1536 cg = repo.changegroup(nodes)
1537 1537 while 1:
1538 1538 d = cg.read(4096)
1539 1539 if not d:
1540 1540 break
1541 1541 fout.write(d)
1542 1542
1543 1543 fout.flush()
1544 1544
1545 1545 elif cmd == "addchangegroup":
1546 1546 if not lock:
1547 1547 respond("not locked")
1548 1548 continue
1549 1549 respond("")
1550 1550
1551 1551 r = repo.addchangegroup(fin)
1552 1552 respond("")
1553 1553
1554 1554 optlist = "name templates style address port ipv6 accesslog errorlog"
1555 1555 for o in optlist.split():
1556 1556 if opts[o]:
1557 1557 ui.setconfig("web", o, opts[o])
1558 1558
1559 1559 try:
1560 1560 httpd = hgweb.create_server(repo)
1561 1561 except socket.error, inst:
1562 1562 raise util.Abort('cannot start server: ' + inst.args[1])
1563 1563
1564 1564 if ui.verbose:
1565 1565 addr, port = httpd.socket.getsockname()
1566 1566 if addr == '0.0.0.0':
1567 1567 addr = socket.gethostname()
1568 1568 else:
1569 1569 try:
1570 1570 addr = socket.gethostbyaddr(addr)[0]
1571 1571 except socket.error:
1572 1572 pass
1573 1573 if port != 80:
1574 1574 ui.status('listening at http://%s:%d/\n' % (addr, port))
1575 1575 else:
1576 1576 ui.status('listening at http://%s/\n' % addr)
1577 1577 httpd.serve_forever()
1578 1578
1579 1579 def status(ui, repo, *pats, **opts):
1580 1580 '''show changed files in the working directory
1581 1581
1582 1582 M = modified
1583 1583 A = added
1584 1584 R = removed
1585 1585 ? = not tracked
1586 1586 '''
1587 1587
1588 1588 cwd = repo.getcwd()
1589 1589 files, matchfn, anypats = matchpats(repo, cwd, pats, opts)
1590 1590 (c, a, d, u) = [[util.pathto(cwd, x) for x in n]
1591 1591 for n in repo.changes(files=files, match=matchfn)]
1592 1592
1593 1593 changetypes = [('modified', 'M', c),
1594 1594 ('added', 'A', a),
1595 1595 ('removed', 'R', d),
1596 1596 ('unknown', '?', u)]
1597 1597
1598 1598 end = opts['print0'] and '\0' or '\n'
1599 1599
1600 1600 for opt, char, changes in ([ct for ct in changetypes if opts[ct[0]]]
1601 1601 or changetypes):
1602 1602 if opts['no_status']:
1603 1603 format = "%%s%s" % end
1604 1604 else:
1605 1605 format = "%s %%s%s" % (char, end);
1606 1606
1607 1607 for f in changes:
1608 1608 ui.write(format % f)
1609 1609
1610 1610 def tag(ui, repo, name, rev=None, **opts):
1611 1611 """add a tag for the current tip or a given revision"""
1612 1612 if opts['text']:
1613 1613 ui.warn("Warning: -t and --text is deprecated,"
1614 1614 " please use -m or --message instead.\n")
1615 1615 if name == "tip":
1616 1616 raise util.Abort("the name 'tip' is reserved")
1617 1617 if rev:
1618 1618 r = hex(repo.lookup(rev))
1619 1619 else:
1620 1620 r = hex(repo.changelog.tip())
1621 1621
1622 1622 if name.find(revrangesep) >= 0:
1623 1623 raise util.Abort("'%s' cannot be used in a tag name" % revrangesep)
1624 1624
1625 1625 if opts['local']:
1626 1626 repo.opener("localtags", "a").write("%s %s\n" % (r, name))
1627 1627 return
1628 1628
1629 1629 (c, a, d, u) = repo.changes()
1630 1630 for x in (c, a, d, u):
1631 1631 if ".hgtags" in x:
1632 1632 raise util.Abort("working copy of .hgtags is changed "
1633 1633 "(please commit .hgtags manually)")
1634 1634
1635 1635 repo.wfile(".hgtags", "ab").write("%s %s\n" % (r, name))
1636 1636 if repo.dirstate.state(".hgtags") == '?':
1637 1637 repo.add([".hgtags"])
1638 1638
1639 1639 message = (opts['message'] or opts['text'] or
1640 1640 "Added tag %s for changeset %s" % (name, r))
1641 1641 try:
1642 1642 repo.commit([".hgtags"], message, opts['user'], opts['date'])
1643 1643 except ValueError, inst:
1644 1644 raise util.Abort(str(inst))
1645 1645
1646 1646 def tags(ui, repo):
1647 1647 """list repository tags"""
1648 1648
1649 1649 l = repo.tagslist()
1650 1650 l.reverse()
1651 1651 for t, n in l:
1652 1652 try:
1653 1653 r = "%5d:%s" % (repo.changelog.rev(n), hex(n))
1654 1654 except KeyError:
1655 1655 r = " ?:?"
1656 1656 ui.write("%-30s %s\n" % (t, r))
1657 1657
1658 1658 def tip(ui, repo):
1659 1659 """show the tip revision"""
1660 1660 n = repo.changelog.tip()
1661 1661 show_changeset(ui, repo, changenode=n)
1662 1662
1663 1663 def unbundle(ui, repo, fname):
1664 1664 """apply a changegroup file"""
1665 1665 f = urllib.urlopen(fname)
1666 1666
1667 1667 if f.read(4) != "HG10":
1668 1668 raise util.Abort("%s: not a Mercurial bundle file" % fname)
1669 1669
1670 1670 class bzread:
1671 1671 def __init__(self, f):
1672 1672 self.zd = bz2.BZ2Decompressor()
1673 1673 self.f = f
1674 1674 self.buf = ""
1675 1675 def read(self, l):
1676 1676 while l > len(self.buf):
1677 1677 r = self.f.read(4096)
1678 1678 if r:
1679 1679 self.buf += self.zd.decompress(r)
1680 1680 else:
1681 1681 break
1682 1682 d, self.buf = self.buf[:l], self.buf[l:]
1683 1683 return d
1684 1684
1685 1685 repo.addchangegroup(bzread(f))
1686 1686
1687 1687 def undo(ui, repo):
1688 1688 """undo the last commit or pull
1689 1689
1690 1690 Roll back the last pull or commit transaction on the
1691 1691 repository, restoring the project to its earlier state.
1692 1692
1693 1693 This command should be used with care. There is only one level of
1694 1694 undo and there is no redo.
1695 1695
1696 1696 This command is not intended for use on public repositories. Once
1697 1697 a change is visible for pull by other users, undoing it locally is
1698 1698 ineffective.
1699 1699 """
1700 1700 repo.undo()
1701 1701
1702 1702 def update(ui, repo, node=None, merge=False, clean=False, branch=None):
1703 1703 '''update or merge working directory
1704 1704
1705 1705 If there are no outstanding changes in the working directory and
1706 1706 there is a linear relationship between the current version and the
1707 1707 requested version, the result is the requested version.
1708 1708
1709 1709 Otherwise the result is a merge between the contents of the
1710 1710 current working directory and the requested version. Files that
1711 1711 changed between either parent are marked as changed for the next
1712 1712 commit and a commit must be performed before any further updates
1713 1713 are allowed.
1714 1714 '''
1715 1715 if branch:
1716 1716 br = repo.branchlookup(branch=branch)
1717 1717 found = []
1718 1718 for x in br:
1719 1719 if branch in br[x]:
1720 1720 found.append(x)
1721 1721 if len(found) > 1:
1722 1722 ui.warn("Found multiple heads for %s\n" % branch)
1723 1723 for x in found:
1724 1724 show_changeset(ui, repo, changenode=x, brinfo=br)
1725 1725 return 1
1726 1726 if len(found) == 1:
1727 1727 node = found[0]
1728 1728 ui.warn("Using head %s for branch %s\n" % (short(node), branch))
1729 1729 else:
1730 1730 ui.warn("branch %s not found\n" % (branch))
1731 1731 return 1
1732 1732 else:
1733 1733 node = node and repo.lookup(node) or repo.changelog.tip()
1734 1734 return repo.update(node, allow=merge, force=clean)
1735 1735
1736 1736 def verify(ui, repo):
1737 1737 """verify the integrity of the repository"""
1738 1738 return repo.verify()
1739 1739
1740 1740 # Command options and aliases are listed here, alphabetically
1741 1741
1742 1742 table = {
1743 1743 "^add":
1744 1744 (add,
1745 1745 [('I', 'include', [], 'include path in search'),
1746 1746 ('X', 'exclude', [], 'exclude path from search')],
1747 1747 "hg add [OPTION]... [FILE]..."),
1748 1748 "addremove":
1749 1749 (addremove,
1750 1750 [('I', 'include', [], 'include path in search'),
1751 1751 ('X', 'exclude', [], 'exclude path from search')],
1752 1752 "hg addremove [OPTION]... [FILE]..."),
1753 1753 "^annotate":
1754 1754 (annotate,
1755 1755 [('r', 'rev', '', 'revision'),
1756 1756 ('a', 'text', None, 'treat all files as text'),
1757 1757 ('u', 'user', None, 'show user'),
1758 1758 ('n', 'number', None, 'show revision number'),
1759 1759 ('c', 'changeset', None, 'show changeset'),
1760 1760 ('I', 'include', [], 'include path in search'),
1761 1761 ('X', 'exclude', [], 'exclude path from search')],
1762 1762 'hg annotate [OPTION]... FILE...'),
1763 1763 "bundle":
1764 1764 (bundle,
1765 1765 [],
1766 1766 'hg bundle FILE DEST'),
1767 1767 "cat":
1768 1768 (cat,
1769 1769 [('I', 'include', [], 'include path in search'),
1770 1770 ('X', 'exclude', [], 'exclude path from search'),
1771 1771 ('o', 'output', "", 'output to file'),
1772 1772 ('r', 'rev', '', 'revision')],
1773 1773 'hg cat [OPTION]... FILE...'),
1774 1774 "^clone":
1775 1775 (clone,
1776 1776 [('U', 'noupdate', None, 'skip update after cloning'),
1777 1777 ('e', 'ssh', "", 'ssh command'),
1778 1778 ('', 'pull', None, 'use pull protocol to copy metadata'),
1779 1779 ('', 'remotecmd', "", 'remote hg command')],
1780 1780 'hg clone [OPTION]... SOURCE [DEST]'),
1781 1781 "^commit|ci":
1782 1782 (commit,
1783 1783 [('A', 'addremove', None, 'run add/remove during commit'),
1784 1784 ('I', 'include', [], 'include path in search'),
1785 1785 ('X', 'exclude', [], 'exclude path from search'),
1786 1786 ('m', 'message', "", 'commit message'),
1787 1787 ('t', 'text', "", 'commit message (deprecated: use -m)'),
1788 1788 ('l', 'logfile', "", 'commit message file'),
1789 1789 ('d', 'date', "", 'date code'),
1790 1790 ('u', 'user', "", 'user')],
1791 1791 'hg commit [OPTION]... [FILE]...'),
1792 1792 "copy|cp": (copy,
1793 1793 [('I', 'include', [], 'include path in search'),
1794 1794 ('X', 'exclude', [], 'exclude path from search'),
1795 1795 ('A', 'after', None, 'record a copy after it has happened'),
1796 1796 ('f', 'force', None, 'replace destination if it exists'),
1797 1797 ('p', 'parents', None, 'append source path to dest')],
1798 1798 'hg copy [OPTION]... [SOURCE]... DEST'),
1799 1799 "debugancestor": (debugancestor, [], 'debugancestor INDEX REV1 REV2'),
1800 1800 "debugcheckstate": (debugcheckstate, [], 'debugcheckstate'),
1801 1801 "debugconfig": (debugconfig, [], 'debugconfig'),
1802 1802 "debugstate": (debugstate, [], 'debugstate'),
1803 1803 "debugdata": (debugdata, [], 'debugdata FILE REV'),
1804 1804 "debugindex": (debugindex, [], 'debugindex FILE'),
1805 1805 "debugindexdot": (debugindexdot, [], 'debugindexdot FILE'),
1806 1806 "debugrename": (debugrename, [], 'debugrename FILE [REV]'),
1807 1807 "debugwalk":
1808 1808 (debugwalk,
1809 1809 [('I', 'include', [], 'include path in search'),
1810 1810 ('X', 'exclude', [], 'exclude path from search')],
1811 1811 'debugwalk [OPTION]... [FILE]...'),
1812 1812 "^diff":
1813 1813 (diff,
1814 1814 [('r', 'rev', [], 'revision'),
1815 1815 ('a', 'text', None, 'treat all files as text'),
1816 1816 ('I', 'include', [], 'include path in search'),
1817 1817 ('X', 'exclude', [], 'exclude path from search')],
1818 1818 'hg diff [-a] [-I] [-X] [-r REV1 [-r REV2]] [FILE]...'),
1819 1819 "^export":
1820 1820 (export,
1821 1821 [('o', 'output', "", 'output to file'),
1822 1822 ('a', 'text', None, 'treat all files as text')],
1823 1823 "hg export [-a] [-o OUTFILE] REV..."),
1824 1824 "forget":
1825 1825 (forget,
1826 1826 [('I', 'include', [], 'include path in search'),
1827 1827 ('X', 'exclude', [], 'exclude path from search')],
1828 1828 "hg forget [OPTION]... FILE..."),
1829 1829 "grep":
1830 1830 (grep,
1831 1831 [('0', 'print0', None, 'end fields with NUL'),
1832 1832 ('I', 'include', [], 'include path in search'),
1833 1833 ('X', 'exclude', [], 'include path in search'),
1834 1834 ('', 'all', None, 'print all revisions with matches'),
1835 1835 ('i', 'ignore-case', None, 'ignore case when matching'),
1836 1836 ('l', 'files-with-matches', None, 'print names of files and revs with matches'),
1837 1837 ('n', 'line-number', None, 'print line numbers'),
1838 1838 ('r', 'rev', [], 'search in revision rev'),
1839 1839 ('u', 'user', None, 'print user who made change')],
1840 1840 "hg grep [OPTION]... PATTERN [FILE]..."),
1841 1841 "heads":
1842 1842 (heads,
1843 1843 [('b', 'branches', None, 'find branch info')],
1844 1844 'hg heads [-b]'),
1845 1845 "help": (help_, [], 'hg help [COMMAND]'),
1846 1846 "identify|id": (identify, [], 'hg identify'),
1847 1847 "import|patch":
1848 1848 (import_,
1849 1849 [('p', 'strip', 1, 'path strip'),
1850 1850 ('f', 'force', None, 'skip check for outstanding changes'),
1851 1851 ('b', 'base', "", 'base path')],
1852 1852 "hg import [-f] [-p NUM] [-b BASE] PATCH..."),
1853 1853 "incoming|in": (incoming,
1854 1854 [('p', 'patch', None, 'show patch')],
1855 1855 'hg incoming [-p] [SOURCE]'),
1856 1856 "^init": (init, [], 'hg init [DEST]'),
1857 1857 "locate":
1858 1858 (locate,
1859 1859 [('r', 'rev', '', 'revision'),
1860 1860 ('0', 'print0', None, 'end filenames with NUL'),
1861 1861 ('f', 'fullpath', None, 'print complete paths'),
1862 1862 ('I', 'include', [], 'include path in search'),
1863 1863 ('X', 'exclude', [], 'exclude path from search')],
1864 1864 'hg locate [OPTION]... [PATTERN]...'),
1865 1865 "^log|history":
1866 1866 (log,
1867 1867 [('I', 'include', [], 'include path in search'),
1868 1868 ('X', 'exclude', [], 'exclude path from search'),
1869 1869 ('b', 'branch', None, 'show branches'),
1870 1870 ('r', 'rev', [], 'revision'),
1871 1871 ('p', 'patch', None, 'show patch')],
1872 1872 'hg log [-I] [-X] [-r REV]... [-p] [FILE]'),
1873 1873 "manifest": (manifest, [], 'hg manifest [REV]'),
1874 1874 "outgoing|out": (outgoing,
1875 1875 [('p', 'patch', None, 'show patch')],
1876 1876 'hg outgoing [-p] [DEST]'),
1877 1877 "parents": (parents, [], 'hg parents [REV]'),
1878 1878 "paths": (paths, [], 'hg paths [NAME]'),
1879 1879 "^pull":
1880 1880 (pull,
1881 1881 [('u', 'update', None, 'update working directory'),
1882 1882 ('e', 'ssh', "", 'ssh command'),
1883 1883 ('', 'remotecmd', "", 'remote hg command')],
1884 1884 'hg pull [-u] [-e FILE] [--remotecmd FILE] [SOURCE]'),
1885 1885 "^push":
1886 1886 (push,
1887 1887 [('f', 'force', None, 'force push'),
1888 1888 ('e', 'ssh', "", 'ssh command'),
1889 1889 ('', 'remotecmd', "", 'remote hg command')],
1890 1890 'hg push [-f] [-e FILE] [--remotecmd FILE] [DEST]'),
1891 1891 "rawcommit":
1892 1892 (rawcommit,
1893 1893 [('p', 'parent', [], 'parent'),
1894 1894 ('d', 'date', "", 'date code'),
1895 1895 ('u', 'user', "", 'user'),
1896 1896 ('F', 'files', "", 'file list'),
1897 1897 ('m', 'message', "", 'commit message'),
1898 1898 ('t', 'text', "", 'commit message (deprecated: use -m)'),
1899 1899 ('l', 'logfile', "", 'commit message file')],
1900 1900 'hg rawcommit [OPTION]... [FILE]...'),
1901 1901 "recover": (recover, [], "hg recover"),
1902 1902 "^remove|rm": (remove,
1903 1903 [('I', 'include', [], 'include path in search'),
1904 1904 ('X', 'exclude', [], 'exclude path from search')],
1905 1905 "hg remove [OPTION]... FILE..."),
1906 1906 "rename|mv": (rename,
1907 1907 [('I', 'include', [], 'include path in search'),
1908 1908 ('X', 'exclude', [], 'exclude path from search'),
1909 1909 ('A', 'after', None, 'record a copy after it has happened'),
1910 1910 ('f', 'force', None, 'replace destination if it exists'),
1911 1911 ('p', 'parents', None, 'append source path to dest')],
1912 1912 'hg rename [OPTION]... [SOURCE]... DEST'),
1913 1913 "^revert":
1914 1914 (revert,
1915 1915 [("n", "nonrecursive", None, "don't recurse into subdirs"),
1916 1916 ("r", "rev", "", "revision")],
1917 1917 "hg revert [-n] [-r REV] [NAME]..."),
1918 1918 "root": (root, [], "hg root"),
1919 1919 "^serve":
1920 1920 (serve,
1921 1921 [('A', 'accesslog', '', 'access log file'),
1922 1922 ('E', 'errorlog', '', 'error log file'),
1923 1923 ('p', 'port', 0, 'listen port'),
1924 1924 ('a', 'address', '', 'interface address'),
1925 1925 ('n', 'name', "", 'repository name'),
1926 1926 ('', 'stdio', None, 'for remote clients'),
1927 1927 ('t', 'templates', "", 'template directory'),
1928 1928 ('', 'style', "", 'template style'),
1929 1929 ('6', 'ipv6', None, 'use IPv6 in addition to IPv4')],
1930 1930 "hg serve [OPTION]..."),
1931 1931 "^status":
1932 1932 (status,
1933 1933 [('m', 'modified', None, 'show only modified files'),
1934 1934 ('a', 'added', None, 'show only added files'),
1935 1935 ('r', 'removed', None, 'show only removed files'),
1936 1936 ('u', 'unknown', None, 'show only unknown (not tracked) files'),
1937 1937 ('n', 'no-status', None, 'hide status prefix'),
1938 1938 ('0', 'print0', None, 'end filenames with NUL'),
1939 1939 ('I', 'include', [], 'include path in search'),
1940 1940 ('X', 'exclude', [], 'exclude path from search')],
1941 1941 "hg status [OPTION]... [FILE]..."),
1942 1942 "tag":
1943 1943 (tag,
1944 1944 [('l', 'local', None, 'make the tag local'),
1945 1945 ('m', 'message', "", 'commit message'),
1946 1946 ('t', 'text', "", 'commit message (deprecated: use -m)'),
1947 1947 ('d', 'date', "", 'date code'),
1948 1948 ('u', 'user', "", 'user')],
1949 1949 'hg tag [OPTION]... NAME [REV]'),
1950 1950 "tags": (tags, [], 'hg tags'),
1951 1951 "tip": (tip, [], 'hg tip'),
1952 1952 "unbundle":
1953 1953 (unbundle,
1954 1954 [],
1955 1955 'hg unbundle FILE'),
1956 1956 "undo": (undo, [], 'hg undo'),
1957 1957 "^update|up|checkout|co":
1958 1958 (update,
1959 1959 [('b', 'branch', "", 'checkout the head of a specific branch'),
1960 1960 ('m', 'merge', None, 'allow merging of conflicts'),
1961 1961 ('C', 'clean', None, 'overwrite locally modified files')],
1962 1962 'hg update [-b TAG] [-m] [-C] [REV]'),
1963 1963 "verify": (verify, [], 'hg verify'),
1964 1964 "version": (show_version, [], 'hg version'),
1965 1965 }
1966 1966
1967 1967 globalopts = [
1968 1968 ('R', 'repository', "", 'repository root directory'),
1969 1969 ('', 'cwd', '', 'change working directory'),
1970 1970 ('y', 'noninteractive', None, 'run non-interactively'),
1971 1971 ('q', 'quiet', None, 'quiet mode'),
1972 1972 ('v', 'verbose', None, 'verbose mode'),
1973 1973 ('', 'debug', None, 'debug mode'),
1974 1974 ('', 'debugger', None, 'start debugger'),
1975 1975 ('', 'traceback', None, 'print traceback on exception'),
1976 1976 ('', 'time', None, 'time how long the command takes'),
1977 1977 ('', 'profile', None, 'profile'),
1978 1978 ('', 'version', None, 'output version information and exit'),
1979 1979 ('h', 'help', None, 'display help and exit'),
1980 1980 ]
1981 1981
1982 1982 norepo = ("clone init version help debugancestor debugconfig debugdata"
1983 1983 " debugindex debugindexdot paths")
1984 1984
1985 1985 def find(cmd):
1986 1986 for e in table.keys():
1987 1987 if re.match("(%s)$" % e, cmd):
1988 1988 return e, table[e]
1989 1989
1990 1990 raise UnknownCommand(cmd)
1991 1991
1992 1992 class SignalInterrupt(Exception):
1993 1993 """Exception raised on SIGTERM and SIGHUP."""
1994 1994
1995 1995 def catchterm(*args):
1996 1996 raise SignalInterrupt
1997 1997
1998 1998 def run():
1999 1999 sys.exit(dispatch(sys.argv[1:]))
2000 2000
2001 2001 class ParseError(Exception):
2002 2002 """Exception raised on errors in parsing the command line."""
2003 2003
2004 2004 def parse(args):
2005 2005 options = {}
2006 2006 cmdoptions = {}
2007 2007
2008 2008 try:
2009 2009 args = fancyopts.fancyopts(args, globalopts, options)
2010 2010 except fancyopts.getopt.GetoptError, inst:
2011 2011 raise ParseError(None, inst)
2012 2012
2013 2013 if args:
2014 2014 cmd, args = args[0], args[1:]
2015 2015 i = find(cmd)[1]
2016 2016 c = list(i[1])
2017 2017 else:
2018 2018 cmd = None
2019 2019 c = []
2020 2020
2021 2021 # combine global options into local
2022 2022 for o in globalopts:
2023 2023 c.append((o[0], o[1], options[o[1]], o[3]))
2024 2024
2025 2025 try:
2026 2026 args = fancyopts.fancyopts(args, c, cmdoptions)
2027 2027 except fancyopts.getopt.GetoptError, inst:
2028 2028 raise ParseError(cmd, inst)
2029 2029
2030 2030 # separate global options back out
2031 2031 for o in globalopts:
2032 2032 n = o[1]
2033 2033 options[n] = cmdoptions[n]
2034 2034 del cmdoptions[n]
2035 2035
2036 2036 return (cmd, cmd and i[0] or None, args, options, cmdoptions)
2037 2037
2038 2038 def dispatch(args):
2039 2039 signal.signal(signal.SIGTERM, catchterm)
2040 2040 try:
2041 2041 signal.signal(signal.SIGHUP, catchterm)
2042 2042 except AttributeError:
2043 2043 pass
2044 2044
2045 2045 u = ui.ui()
2046 2046 external = []
2047 2047 for x in u.extensions():
2048 2048 if x[1]:
2049 2049 try:
2050 2050 mod = imp.load_source(x[0], x[1])
2051 2051 except:
2052 2052 u.warn("*** failed to import extension %s\n" % x[1])
2053 2053 continue
2054 2054 else:
2055 2055 def importh(name):
2056 2056 mod = __import__(name)
2057 2057 components = name.split('.')
2058 2058 for comp in components[1:]:
2059 2059 mod = getattr(mod, comp)
2060 2060 return mod
2061 2061 try:
2062 2062 mod = importh(x[0])
2063 2063 except:
2064 2064 u.warn("failed to import extension %s\n" % x[0])
2065 2065 continue
2066 2066
2067 2067 external.append(mod)
2068 2068 for x in external:
2069 2069 cmdtable = getattr(x, 'cmdtable', {})
2070 2070 for t in cmdtable:
2071 2071 if t in table:
2072 2072 u.warn("module %s overrides %s\n" % (x.__name__, t))
2073 2073 table.update(cmdtable)
2074 2074
2075 2075 try:
2076 2076 cmd, func, args, options, cmdoptions = parse(args)
2077 2077 except ParseError, inst:
2078 2078 if inst.args[0]:
2079 2079 u.warn("hg %s: %s\n" % (inst.args[0], inst.args[1]))
2080 2080 help_(u, inst.args[0])
2081 2081 else:
2082 2082 u.warn("hg: %s\n" % inst.args[1])
2083 2083 help_(u, 'shortlist')
2084 2084 sys.exit(-1)
2085 2085 except UnknownCommand, inst:
2086 2086 u.warn("hg: unknown command '%s'\n" % inst.args[0])
2087 2087 help_(u, 'shortlist')
2088 2088 sys.exit(1)
2089 2089
2090 2090 if options["time"]:
2091 2091 def get_times():
2092 2092 t = os.times()
2093 2093 if t[4] == 0.0: # Windows leaves this as zero, so use time.clock()
2094 2094 t = (t[0], t[1], t[2], t[3], time.clock())
2095 2095 return t
2096 2096 s = get_times()
2097 2097 def print_time():
2098 2098 t = get_times()
2099 2099 u.warn("Time: real %.3f secs (user %.3f+%.3f sys %.3f+%.3f)\n" %
2100 2100 (t[4]-s[4], t[0]-s[0], t[2]-s[2], t[1]-s[1], t[3]-s[3]))
2101 2101 atexit.register(print_time)
2102 2102
2103 2103 u.updateopts(options["verbose"], options["debug"], options["quiet"],
2104 2104 not options["noninteractive"])
2105 2105
2106 2106 # enter the debugger before command execution
2107 2107 if options['debugger']:
2108 2108 pdb.set_trace()
2109 2109
2110 2110 try:
2111 2111 try:
2112 2112 if options['help']:
2113 2113 help_(u, cmd, options['version'])
2114 2114 sys.exit(0)
2115 2115 elif options['version']:
2116 2116 show_version(u)
2117 2117 sys.exit(0)
2118 2118 elif not cmd:
2119 2119 help_(u, 'shortlist')
2120 2120 sys.exit(0)
2121 2121
2122 2122 if options['cwd']:
2123 2123 try:
2124 2124 os.chdir(options['cwd'])
2125 2125 except OSError, inst:
2126 2126 raise util.Abort('%s: %s' %
2127 2127 (options['cwd'], inst.strerror))
2128 2128
2129 2129 if cmd not in norepo.split():
2130 2130 path = options["repository"] or ""
2131 2131 repo = hg.repository(ui=u, path=path)
2132 2132 for x in external:
2133 2133 if hasattr(x, 'reposetup'): x.reposetup(u, repo)
2134 2134 d = lambda: func(u, repo, *args, **cmdoptions)
2135 2135 else:
2136 2136 d = lambda: func(u, *args, **cmdoptions)
2137 2137
2138 2138 if options['profile']:
2139 2139 import hotshot, hotshot.stats
2140 2140 prof = hotshot.Profile("hg.prof")
2141 2141 r = prof.runcall(d)
2142 2142 prof.close()
2143 2143 stats = hotshot.stats.load("hg.prof")
2144 2144 stats.strip_dirs()
2145 2145 stats.sort_stats('time', 'calls')
2146 2146 stats.print_stats(40)
2147 2147 return r
2148 2148 else:
2149 2149 return d()
2150 2150 except:
2151 2151 # enter the debugger when we hit an exception
2152 2152 if options['debugger']:
2153 2153 pdb.post_mortem(sys.exc_info()[2])
2154 2154 if options['traceback']:
2155 2155 traceback.print_exc()
2156 2156 raise
2157 2157 except hg.RepoError, inst:
2158 2158 u.warn("abort: ", inst, "!\n")
2159 2159 except revlog.RevlogError, inst:
2160 2160 u.warn("abort: ", inst, "!\n")
2161 2161 except SignalInterrupt:
2162 2162 u.warn("killed!\n")
2163 2163 except KeyboardInterrupt:
2164 2164 try:
2165 2165 u.warn("interrupted!\n")
2166 2166 except IOError, inst:
2167 2167 if inst.errno == errno.EPIPE:
2168 2168 if u.debugflag:
2169 2169 u.warn("\nbroken pipe\n")
2170 2170 else:
2171 2171 raise
2172 2172 except IOError, inst:
2173 2173 if hasattr(inst, "code"):
2174 2174 u.warn("abort: %s\n" % inst)
2175 2175 elif hasattr(inst, "reason"):
2176 2176 u.warn("abort: error: %s\n" % inst.reason[1])
2177 2177 elif hasattr(inst, "args") and inst[0] == errno.EPIPE:
2178 2178 if u.debugflag:
2179 2179 u.warn("broken pipe\n")
2180 2180 elif getattr(inst, "strerror", None):
2181 if getattr(inst, "filename", None):
2182 u.warn("abort: %s - %s\n" % (inst.strerror, inst.filename))
2183 else:
2181 2184 u.warn("abort: %s\n" % inst.strerror)
2182 2185 else:
2183 2186 raise
2184 2187 except OSError, inst:
2185 2188 if hasattr(inst, "filename"):
2186 2189 u.warn("abort: %s: %s\n" % (inst.strerror, inst.filename))
2187 2190 else:
2188 2191 u.warn("abort: %s\n" % inst.strerror)
2189 2192 except util.Abort, inst:
2190 2193 u.warn('abort: ', inst.args[0] % inst.args[1:], '\n')
2191 2194 sys.exit(1)
2192 2195 except TypeError, inst:
2193 2196 # was this an argument error?
2194 2197 tb = traceback.extract_tb(sys.exc_info()[2])
2195 2198 if len(tb) > 2: # no
2196 2199 raise
2197 2200 u.debug(inst, "\n")
2198 2201 u.warn("%s: invalid arguments\n" % cmd)
2199 2202 help_(u, cmd)
2200 2203 except UnknownCommand, inst:
2201 2204 u.warn("hg: unknown command '%s'\n" % inst.args[0])
2202 2205 help_(u, 'shortlist')
2203 2206 except SystemExit:
2204 2207 # don't catch this in the catch-all below
2205 2208 raise
2206 2209 except:
2207 2210 u.warn("** unknown exception encountered, details follow\n")
2208 2211 u.warn("** report bug details to mercurial@selenic.com\n")
2209 2212 raise
2210 2213
2211 2214 sys.exit(-1)
General Comments 0
You need to be logged in to leave comments. Login now