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