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