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