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