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