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