##// END OF EJS Templates
filectx.annotate: return filectx for each line instead of rev
Brendan Cully -
r3146:e69a0cbe default
parent child Browse files
Show More
@@ -1,3419 +1,3404 b''
1 1 # commands.py - command processing for mercurial
2 2 #
3 3 # Copyright 2005, 2006 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 from i18n import gettext as _
11 11 demandload(globals(), "os re sys signal shutil imp urllib pdb shlex")
12 12 demandload(globals(), "fancyopts ui hg util lock revlog templater bundlerepo")
13 13 demandload(globals(), "fnmatch difflib patch random signal tempfile time")
14 14 demandload(globals(), "traceback errno socket version struct atexit sets bz2")
15 15 demandload(globals(), "archival cStringIO changegroup")
16 16 demandload(globals(), "cmdutil hgweb.server sshserver")
17 17
18 18 class UnknownCommand(Exception):
19 19 """Exception raised if command is not in the command table."""
20 20 class AmbiguousCommand(Exception):
21 21 """Exception raised if command shortcut matches more than one command."""
22 22
23 23 def bail_if_changed(repo):
24 24 modified, added, removed, deleted = repo.status()[:4]
25 25 if modified or added or removed or deleted:
26 26 raise util.Abort(_("outstanding uncommitted changes"))
27 27
28 28 def relpath(repo, args):
29 29 cwd = repo.getcwd()
30 30 if cwd:
31 31 return [util.normpath(os.path.join(cwd, x)) for x in args]
32 32 return args
33 33
34 34 def logmessage(opts):
35 35 """ get the log message according to -m and -l option """
36 36 message = opts['message']
37 37 logfile = opts['logfile']
38 38
39 39 if message and logfile:
40 40 raise util.Abort(_('options --message and --logfile are mutually '
41 41 'exclusive'))
42 42 if not message and logfile:
43 43 try:
44 44 if logfile == '-':
45 45 message = sys.stdin.read()
46 46 else:
47 47 message = open(logfile).read()
48 48 except IOError, inst:
49 49 raise util.Abort(_("can't read commit message '%s': %s") %
50 50 (logfile, inst.strerror))
51 51 return message
52 52
53 53 def walkchangerevs(ui, repo, pats, opts):
54 54 '''Iterate over files and the revs they changed in.
55 55
56 56 Callers most commonly need to iterate backwards over the history
57 57 it is interested in. Doing so has awful (quadratic-looking)
58 58 performance, so we use iterators in a "windowed" way.
59 59
60 60 We walk a window of revisions in the desired order. Within the
61 61 window, we first walk forwards to gather data, then in the desired
62 62 order (usually backwards) to display it.
63 63
64 64 This function returns an (iterator, getchange, matchfn) tuple. The
65 65 getchange function returns the changelog entry for a numeric
66 66 revision. The iterator yields 3-tuples. They will be of one of
67 67 the following forms:
68 68
69 69 "window", incrementing, lastrev: stepping through a window,
70 70 positive if walking forwards through revs, last rev in the
71 71 sequence iterated over - use to reset state for the current window
72 72
73 73 "add", rev, fns: out-of-order traversal of the given file names
74 74 fns, which changed during revision rev - use to gather data for
75 75 possible display
76 76
77 77 "iter", rev, None: in-order traversal of the revs earlier iterated
78 78 over with "add" - use to display data'''
79 79
80 80 def increasing_windows(start, end, windowsize=8, sizelimit=512):
81 81 if start < end:
82 82 while start < end:
83 83 yield start, min(windowsize, end-start)
84 84 start += windowsize
85 85 if windowsize < sizelimit:
86 86 windowsize *= 2
87 87 else:
88 88 while start > end:
89 89 yield start, min(windowsize, start-end-1)
90 90 start -= windowsize
91 91 if windowsize < sizelimit:
92 92 windowsize *= 2
93 93
94 94
95 95 files, matchfn, anypats = cmdutil.matchpats(repo, pats, opts)
96 96 follow = opts.get('follow') or opts.get('follow_first')
97 97
98 98 if repo.changelog.count() == 0:
99 99 return [], False, matchfn
100 100
101 101 if follow:
102 102 defrange = '%s:0' % repo.changectx().rev()
103 103 else:
104 104 defrange = 'tip:0'
105 105 revs = map(int, cmdutil.revrange(ui, repo, opts['rev'] or [defrange]))
106 106 wanted = {}
107 107 slowpath = anypats
108 108 fncache = {}
109 109
110 110 chcache = {}
111 111 def getchange(rev):
112 112 ch = chcache.get(rev)
113 113 if ch is None:
114 114 chcache[rev] = ch = repo.changelog.read(repo.lookup(str(rev)))
115 115 return ch
116 116
117 117 if not slowpath and not files:
118 118 # No files, no patterns. Display all revs.
119 119 wanted = dict(zip(revs, revs))
120 120 copies = []
121 121 if not slowpath:
122 122 # Only files, no patterns. Check the history of each file.
123 123 def filerevgen(filelog, node):
124 124 cl_count = repo.changelog.count()
125 125 if node is None:
126 126 last = filelog.count() - 1
127 127 else:
128 128 last = filelog.rev(node)
129 129 for i, window in increasing_windows(last, -1):
130 130 revs = []
131 131 for j in xrange(i - window, i + 1):
132 132 n = filelog.node(j)
133 133 revs.append((filelog.linkrev(n),
134 134 follow and filelog.renamed(n)))
135 135 revs.reverse()
136 136 for rev in revs:
137 137 # only yield rev for which we have the changelog, it can
138 138 # happen while doing "hg log" during a pull or commit
139 139 if rev[0] < cl_count:
140 140 yield rev
141 141 def iterfiles():
142 142 for filename in files:
143 143 yield filename, None
144 144 for filename_node in copies:
145 145 yield filename_node
146 146 minrev, maxrev = min(revs), max(revs)
147 147 for file_, node in iterfiles():
148 148 filelog = repo.file(file_)
149 149 # A zero count may be a directory or deleted file, so
150 150 # try to find matching entries on the slow path.
151 151 if filelog.count() == 0:
152 152 slowpath = True
153 153 break
154 154 for rev, copied in filerevgen(filelog, node):
155 155 if rev <= maxrev:
156 156 if rev < minrev:
157 157 break
158 158 fncache.setdefault(rev, [])
159 159 fncache[rev].append(file_)
160 160 wanted[rev] = 1
161 161 if follow and copied:
162 162 copies.append(copied)
163 163 if slowpath:
164 164 if follow:
165 165 raise util.Abort(_('can only follow copies/renames for explicit '
166 166 'file names'))
167 167
168 168 # The slow path checks files modified in every changeset.
169 169 def changerevgen():
170 170 for i, window in increasing_windows(repo.changelog.count()-1, -1):
171 171 for j in xrange(i - window, i + 1):
172 172 yield j, getchange(j)[3]
173 173
174 174 for rev, changefiles in changerevgen():
175 175 matches = filter(matchfn, changefiles)
176 176 if matches:
177 177 fncache[rev] = matches
178 178 wanted[rev] = 1
179 179
180 180 class followfilter:
181 181 def __init__(self, onlyfirst=False):
182 182 self.startrev = -1
183 183 self.roots = []
184 184 self.onlyfirst = onlyfirst
185 185
186 186 def match(self, rev):
187 187 def realparents(rev):
188 188 if self.onlyfirst:
189 189 return repo.changelog.parentrevs(rev)[0:1]
190 190 else:
191 191 return filter(lambda x: x != -1, repo.changelog.parentrevs(rev))
192 192
193 193 if self.startrev == -1:
194 194 self.startrev = rev
195 195 return True
196 196
197 197 if rev > self.startrev:
198 198 # forward: all descendants
199 199 if not self.roots:
200 200 self.roots.append(self.startrev)
201 201 for parent in realparents(rev):
202 202 if parent in self.roots:
203 203 self.roots.append(rev)
204 204 return True
205 205 else:
206 206 # backwards: all parents
207 207 if not self.roots:
208 208 self.roots.extend(realparents(self.startrev))
209 209 if rev in self.roots:
210 210 self.roots.remove(rev)
211 211 self.roots.extend(realparents(rev))
212 212 return True
213 213
214 214 return False
215 215
216 216 # it might be worthwhile to do this in the iterator if the rev range
217 217 # is descending and the prune args are all within that range
218 218 for rev in opts.get('prune', ()):
219 219 rev = repo.changelog.rev(repo.lookup(rev))
220 220 ff = followfilter()
221 221 stop = min(revs[0], revs[-1])
222 222 for x in range(rev, stop-1, -1):
223 223 if ff.match(x) and wanted.has_key(x):
224 224 del wanted[x]
225 225
226 226 def iterate():
227 227 if follow and not files:
228 228 ff = followfilter(onlyfirst=opts.get('follow_first'))
229 229 def want(rev):
230 230 if ff.match(rev) and rev in wanted:
231 231 return True
232 232 return False
233 233 else:
234 234 def want(rev):
235 235 return rev in wanted
236 236
237 237 for i, window in increasing_windows(0, len(revs)):
238 238 yield 'window', revs[0] < revs[-1], revs[-1]
239 239 nrevs = [rev for rev in revs[i:i+window] if want(rev)]
240 240 srevs = list(nrevs)
241 241 srevs.sort()
242 242 for rev in srevs:
243 243 fns = fncache.get(rev) or filter(matchfn, getchange(rev)[3])
244 244 yield 'add', rev, fns
245 245 for rev in nrevs:
246 246 yield 'iter', rev, None
247 247 return iterate(), getchange, matchfn
248 248
249 249 def write_bundle(cg, filename=None, compress=True):
250 250 """Write a bundle file and return its filename.
251 251
252 252 Existing files will not be overwritten.
253 253 If no filename is specified, a temporary file is created.
254 254 bz2 compression can be turned off.
255 255 The bundle file will be deleted in case of errors.
256 256 """
257 257 class nocompress(object):
258 258 def compress(self, x):
259 259 return x
260 260 def flush(self):
261 261 return ""
262 262
263 263 fh = None
264 264 cleanup = None
265 265 try:
266 266 if filename:
267 267 if os.path.exists(filename):
268 268 raise util.Abort(_("file '%s' already exists") % filename)
269 269 fh = open(filename, "wb")
270 270 else:
271 271 fd, filename = tempfile.mkstemp(prefix="hg-bundle-", suffix=".hg")
272 272 fh = os.fdopen(fd, "wb")
273 273 cleanup = filename
274 274
275 275 if compress:
276 276 fh.write("HG10")
277 277 z = bz2.BZ2Compressor(9)
278 278 else:
279 279 fh.write("HG10UN")
280 280 z = nocompress()
281 281 # parse the changegroup data, otherwise we will block
282 282 # in case of sshrepo because we don't know the end of the stream
283 283
284 284 # an empty chunkiter is the end of the changegroup
285 285 empty = False
286 286 while not empty:
287 287 empty = True
288 288 for chunk in changegroup.chunkiter(cg):
289 289 empty = False
290 290 fh.write(z.compress(changegroup.genchunk(chunk)))
291 291 fh.write(z.compress(changegroup.closechunk()))
292 292 fh.write(z.flush())
293 293 cleanup = None
294 294 return filename
295 295 finally:
296 296 if fh is not None:
297 297 fh.close()
298 298 if cleanup is not None:
299 299 os.unlink(cleanup)
300 300
301 301 def trimuser(ui, name, rev, revcache):
302 302 """trim the name of the user who committed a change"""
303 303 user = revcache.get(rev)
304 304 if user is None:
305 305 user = revcache[rev] = ui.shortuser(name)
306 306 return user
307 307
308 308 class changeset_printer(object):
309 309 '''show changeset information when templating not requested.'''
310 310
311 311 def __init__(self, ui, repo):
312 312 self.ui = ui
313 313 self.repo = repo
314 314
315 315 def show(self, rev=0, changenode=None, brinfo=None):
316 316 '''show a single changeset or file revision'''
317 317 log = self.repo.changelog
318 318 if changenode is None:
319 319 changenode = log.node(rev)
320 320 elif not rev:
321 321 rev = log.rev(changenode)
322 322
323 323 if self.ui.quiet:
324 324 self.ui.write("%d:%s\n" % (rev, short(changenode)))
325 325 return
326 326
327 327 changes = log.read(changenode)
328 328 date = util.datestr(changes[2])
329 329
330 330 hexfunc = self.ui.debugflag and hex or short
331 331
332 332 parents = [(log.rev(p), hexfunc(p)) for p in log.parents(changenode)
333 333 if self.ui.debugflag or p != nullid]
334 334 if (not self.ui.debugflag and len(parents) == 1 and
335 335 parents[0][0] == rev-1):
336 336 parents = []
337 337
338 338 self.ui.write(_("changeset: %d:%s\n") % (rev, hexfunc(changenode)))
339 339
340 340 for tag in self.repo.nodetags(changenode):
341 341 self.ui.status(_("tag: %s\n") % tag)
342 342 for parent in parents:
343 343 self.ui.write(_("parent: %d:%s\n") % parent)
344 344
345 345 if brinfo and changenode in brinfo:
346 346 br = brinfo[changenode]
347 347 self.ui.write(_("branch: %s\n") % " ".join(br))
348 348
349 349 self.ui.debug(_("manifest: %d:%s\n") %
350 350 (self.repo.manifest.rev(changes[0]), hex(changes[0])))
351 351 self.ui.status(_("user: %s\n") % changes[1])
352 352 self.ui.status(_("date: %s\n") % date)
353 353
354 354 if self.ui.debugflag:
355 355 files = self.repo.status(log.parents(changenode)[0], changenode)[:3]
356 356 for key, value in zip([_("files:"), _("files+:"), _("files-:")],
357 357 files):
358 358 if value:
359 359 self.ui.note("%-12s %s\n" % (key, " ".join(value)))
360 360 else:
361 361 self.ui.note(_("files: %s\n") % " ".join(changes[3]))
362 362
363 363 description = changes[4].strip()
364 364 if description:
365 365 if self.ui.verbose:
366 366 self.ui.status(_("description:\n"))
367 367 self.ui.status(description)
368 368 self.ui.status("\n\n")
369 369 else:
370 370 self.ui.status(_("summary: %s\n") %
371 371 description.splitlines()[0])
372 372 self.ui.status("\n")
373 373
374 374 def show_changeset(ui, repo, opts):
375 375 '''show one changeset. uses template or regular display. caller
376 376 can pass in 'style' and 'template' options in opts.'''
377 377
378 378 tmpl = opts.get('template')
379 379 if tmpl:
380 380 tmpl = templater.parsestring(tmpl, quoted=False)
381 381 else:
382 382 tmpl = ui.config('ui', 'logtemplate')
383 383 if tmpl: tmpl = templater.parsestring(tmpl)
384 384 mapfile = opts.get('style') or ui.config('ui', 'style')
385 385 if tmpl or mapfile:
386 386 if mapfile:
387 387 if not os.path.isfile(mapfile):
388 388 mapname = templater.templatepath('map-cmdline.' + mapfile)
389 389 if not mapname: mapname = templater.templatepath(mapfile)
390 390 if mapname: mapfile = mapname
391 391 try:
392 392 t = templater.changeset_templater(ui, repo, mapfile)
393 393 except SyntaxError, inst:
394 394 raise util.Abort(inst.args[0])
395 395 if tmpl: t.use_template(tmpl)
396 396 return t
397 397 return changeset_printer(ui, repo)
398 398
399 399 def setremoteconfig(ui, opts):
400 400 "copy remote options to ui tree"
401 401 if opts.get('ssh'):
402 402 ui.setconfig("ui", "ssh", opts['ssh'])
403 403 if opts.get('remotecmd'):
404 404 ui.setconfig("ui", "remotecmd", opts['remotecmd'])
405 405
406 406 def show_version(ui):
407 407 """output version and copyright information"""
408 408 ui.write(_("Mercurial Distributed SCM (version %s)\n")
409 409 % version.get_version())
410 410 ui.status(_(
411 411 "\nCopyright (C) 2005, 2006 Matt Mackall <mpm@selenic.com>\n"
412 412 "This is free software; see the source for copying conditions. "
413 413 "There is NO\nwarranty; "
414 414 "not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n"
415 415 ))
416 416
417 417 def help_(ui, name=None, with_version=False):
418 418 """show help for a command, extension, or list of commands
419 419
420 420 With no arguments, print a list of commands and short help.
421 421
422 422 Given a command name, print help for that command.
423 423
424 424 Given an extension name, print help for that extension, and the
425 425 commands it provides."""
426 426 option_lists = []
427 427
428 428 def helpcmd(name):
429 429 if with_version:
430 430 show_version(ui)
431 431 ui.write('\n')
432 432 aliases, i = findcmd(ui, name)
433 433 # synopsis
434 434 ui.write("%s\n\n" % i[2])
435 435
436 436 # description
437 437 doc = i[0].__doc__
438 438 if not doc:
439 439 doc = _("(No help text available)")
440 440 if ui.quiet:
441 441 doc = doc.splitlines(0)[0]
442 442 ui.write("%s\n" % doc.rstrip())
443 443
444 444 if not ui.quiet:
445 445 # aliases
446 446 if len(aliases) > 1:
447 447 ui.write(_("\naliases: %s\n") % ', '.join(aliases[1:]))
448 448
449 449 # options
450 450 if i[1]:
451 451 option_lists.append(("options", i[1]))
452 452
453 453 def helplist(select=None):
454 454 h = {}
455 455 cmds = {}
456 456 for c, e in table.items():
457 457 f = c.split("|", 1)[0]
458 458 if select and not select(f):
459 459 continue
460 460 if name == "shortlist" and not f.startswith("^"):
461 461 continue
462 462 f = f.lstrip("^")
463 463 if not ui.debugflag and f.startswith("debug"):
464 464 continue
465 465 doc = e[0].__doc__
466 466 if not doc:
467 467 doc = _("(No help text available)")
468 468 h[f] = doc.splitlines(0)[0].rstrip()
469 469 cmds[f] = c.lstrip("^")
470 470
471 471 fns = h.keys()
472 472 fns.sort()
473 473 m = max(map(len, fns))
474 474 for f in fns:
475 475 if ui.verbose:
476 476 commands = cmds[f].replace("|",", ")
477 477 ui.write(" %s:\n %s\n"%(commands, h[f]))
478 478 else:
479 479 ui.write(' %-*s %s\n' % (m, f, h[f]))
480 480
481 481 def helpext(name):
482 482 try:
483 483 mod = findext(name)
484 484 except KeyError:
485 485 raise UnknownCommand(name)
486 486
487 487 doc = (mod.__doc__ or _('No help text available')).splitlines(0)
488 488 ui.write(_('%s extension - %s\n') % (name.split('.')[-1], doc[0]))
489 489 for d in doc[1:]:
490 490 ui.write(d, '\n')
491 491
492 492 ui.status('\n')
493 493 if ui.verbose:
494 494 ui.status(_('list of commands:\n\n'))
495 495 else:
496 496 ui.status(_('list of commands (use "hg help -v %s" '
497 497 'to show aliases and global options):\n\n') % name)
498 498
499 499 modcmds = dict.fromkeys([c.split('|', 1)[0] for c in mod.cmdtable])
500 500 helplist(modcmds.has_key)
501 501
502 502 if name and name != 'shortlist':
503 503 try:
504 504 helpcmd(name)
505 505 except UnknownCommand:
506 506 helpext(name)
507 507
508 508 else:
509 509 # program name
510 510 if ui.verbose or with_version:
511 511 show_version(ui)
512 512 else:
513 513 ui.status(_("Mercurial Distributed SCM\n"))
514 514 ui.status('\n')
515 515
516 516 # list of commands
517 517 if name == "shortlist":
518 518 ui.status(_('basic commands (use "hg help" '
519 519 'for the full list or option "-v" for details):\n\n'))
520 520 elif ui.verbose:
521 521 ui.status(_('list of commands:\n\n'))
522 522 else:
523 523 ui.status(_('list of commands (use "hg help -v" '
524 524 'to show aliases and global options):\n\n'))
525 525
526 526 helplist()
527 527
528 528 # global options
529 529 if ui.verbose:
530 530 option_lists.append(("global options", globalopts))
531 531
532 532 # list all option lists
533 533 opt_output = []
534 534 for title, options in option_lists:
535 535 opt_output.append(("\n%s:\n" % title, None))
536 536 for shortopt, longopt, default, desc in options:
537 537 opt_output.append(("%2s%s" % (shortopt and "-%s" % shortopt,
538 538 longopt and " --%s" % longopt),
539 539 "%s%s" % (desc,
540 540 default
541 541 and _(" (default: %s)") % default
542 542 or "")))
543 543
544 544 if opt_output:
545 545 opts_len = max([len(line[0]) for line in opt_output if line[1]])
546 546 for first, second in opt_output:
547 547 if second:
548 548 ui.write(" %-*s %s\n" % (opts_len, first, second))
549 549 else:
550 550 ui.write("%s\n" % first)
551 551
552 552 # Commands start here, listed alphabetically
553 553
554 554 def add(ui, repo, *pats, **opts):
555 555 """add the specified files on the next commit
556 556
557 557 Schedule files to be version controlled and added to the repository.
558 558
559 559 The files will be added to the repository at the next commit.
560 560
561 561 If no names are given, add all files in the repository.
562 562 """
563 563
564 564 names = []
565 565 for src, abs, rel, exact in cmdutil.walk(repo, pats, opts):
566 566 if exact:
567 567 if ui.verbose:
568 568 ui.status(_('adding %s\n') % rel)
569 569 names.append(abs)
570 570 elif repo.dirstate.state(abs) == '?':
571 571 ui.status(_('adding %s\n') % rel)
572 572 names.append(abs)
573 573 if not opts.get('dry_run'):
574 574 repo.add(names)
575 575
576 576 def addremove(ui, repo, *pats, **opts):
577 577 """add all new files, delete all missing files (DEPRECATED)
578 578
579 579 Add all new files and remove all missing files from the repository.
580 580
581 581 New files are ignored if they match any of the patterns in .hgignore. As
582 582 with add, these changes take effect at the next commit.
583 583
584 584 Use the -s option to detect renamed files. With a parameter > 0,
585 585 this compares every removed file with every added file and records
586 586 those similar enough as renames. This option takes a percentage
587 587 between 0 (disabled) and 100 (files must be identical) as its
588 588 parameter. Detecting renamed files this way can be expensive.
589 589 """
590 590 sim = float(opts.get('similarity') or 0)
591 591 if sim < 0 or sim > 100:
592 592 raise util.Abort(_('similarity must be between 0 and 100'))
593 593 return cmdutil.addremove(repo, pats, opts, similarity=sim/100.)
594 594
595 595 def annotate(ui, repo, *pats, **opts):
596 596 """show changeset information per file line
597 597
598 598 List changes in files, showing the revision id responsible for each line
599 599
600 600 This command is useful to discover who did a change or when a change took
601 601 place.
602 602
603 603 Without the -a option, annotate will avoid processing files it
604 604 detects as binary. With -a, annotate will generate an annotation
605 605 anyway, probably with undesirable results.
606 606 """
607 def getnode(rev):
608 return short(repo.changelog.node(rev))
609
610 ucache = {}
611 def getname(rev):
612 try:
613 return ucache[rev]
614 except:
615 u = trimuser(ui, repo.changectx(rev).user(), rev, ucache)
616 ucache[rev] = u
617 return u
618
619 dcache = {}
620 def getdate(rev):
621 datestr = dcache.get(rev)
622 if datestr is None:
623 datestr = dcache[rev] = util.datestr(repo.changectx(rev).date())
624 return datestr
607 getdate = util.cachefunc(lambda x: util.datestr(x.date()))
625 608
626 609 if not pats:
627 610 raise util.Abort(_('at least one file name or pattern required'))
628 611
629 opmap = [['user', getname], ['number', str], ['changeset', getnode],
612 opmap = [['user', lambda x: ui.shortuser(x.user())],
613 ['number', lambda x: str(x.rev())],
614 ['changeset', lambda x: short(x.node())],
630 615 ['date', getdate]]
631 616 if not opts['user'] and not opts['changeset'] and not opts['date']:
632 617 opts['number'] = 1
633 618
634 619 ctx = repo.changectx(opts['rev'])
635 620
636 621 for src, abs, rel, exact in cmdutil.walk(repo, pats, opts,
637 622 node=ctx.node()):
638 623 fctx = ctx.filectx(abs)
639 624 if not opts['text'] and util.binary(fctx.data()):
640 625 ui.write(_("%s: binary file\n") % ((pats and rel) or abs))
641 626 continue
642 627
643 628 lines = fctx.annotate()
644 629 pieces = []
645 630
646 631 for o, f in opmap:
647 632 if opts[o]:
648 633 l = [f(n) for n, dummy in lines]
649 634 if l:
650 635 m = max(map(len, l))
651 636 pieces.append(["%*s" % (m, x) for x in l])
652 637
653 638 if pieces:
654 639 for p, l in zip(zip(*pieces), lines):
655 640 ui.write("%s: %s" % (" ".join(p), l[1]))
656 641
657 642 def archive(ui, repo, dest, **opts):
658 643 '''create unversioned archive of a repository revision
659 644
660 645 By default, the revision used is the parent of the working
661 646 directory; use "-r" to specify a different revision.
662 647
663 648 To specify the type of archive to create, use "-t". Valid
664 649 types are:
665 650
666 651 "files" (default): a directory full of files
667 652 "tar": tar archive, uncompressed
668 653 "tbz2": tar archive, compressed using bzip2
669 654 "tgz": tar archive, compressed using gzip
670 655 "uzip": zip archive, uncompressed
671 656 "zip": zip archive, compressed using deflate
672 657
673 658 The exact name of the destination archive or directory is given
674 659 using a format string; see "hg help export" for details.
675 660
676 661 Each member added to an archive file has a directory prefix
677 662 prepended. Use "-p" to specify a format string for the prefix.
678 663 The default is the basename of the archive, with suffixes removed.
679 664 '''
680 665
681 666 node = repo.changectx(opts['rev']).node()
682 667 dest = cmdutil.make_filename(repo, dest, node)
683 668 if os.path.realpath(dest) == repo.root:
684 669 raise util.Abort(_('repository root cannot be destination'))
685 670 dummy, matchfn, dummy = cmdutil.matchpats(repo, [], opts)
686 671 kind = opts.get('type') or 'files'
687 672 prefix = opts['prefix']
688 673 if dest == '-':
689 674 if kind == 'files':
690 675 raise util.Abort(_('cannot archive plain files to stdout'))
691 676 dest = sys.stdout
692 677 if not prefix: prefix = os.path.basename(repo.root) + '-%h'
693 678 prefix = cmdutil.make_filename(repo, prefix, node)
694 679 archival.archive(repo, dest, node, kind, not opts['no_decode'],
695 680 matchfn, prefix)
696 681
697 682 def backout(ui, repo, rev, **opts):
698 683 '''reverse effect of earlier changeset
699 684
700 685 Commit the backed out changes as a new changeset. The new
701 686 changeset is a child of the backed out changeset.
702 687
703 688 If you back out a changeset other than the tip, a new head is
704 689 created. This head is the parent of the working directory. If
705 690 you back out an old changeset, your working directory will appear
706 691 old after the backout. You should merge the backout changeset
707 692 with another head.
708 693
709 694 The --merge option remembers the parent of the working directory
710 695 before starting the backout, then merges the new head with that
711 696 changeset afterwards. This saves you from doing the merge by
712 697 hand. The result of this merge is not committed, as for a normal
713 698 merge.'''
714 699
715 700 bail_if_changed(repo)
716 701 op1, op2 = repo.dirstate.parents()
717 702 if op2 != nullid:
718 703 raise util.Abort(_('outstanding uncommitted merge'))
719 704 node = repo.lookup(rev)
720 705 p1, p2 = repo.changelog.parents(node)
721 706 if p1 == nullid:
722 707 raise util.Abort(_('cannot back out a change with no parents'))
723 708 if p2 != nullid:
724 709 if not opts['parent']:
725 710 raise util.Abort(_('cannot back out a merge changeset without '
726 711 '--parent'))
727 712 p = repo.lookup(opts['parent'])
728 713 if p not in (p1, p2):
729 714 raise util.Abort(_('%s is not a parent of %s' %
730 715 (short(p), short(node))))
731 716 parent = p
732 717 else:
733 718 if opts['parent']:
734 719 raise util.Abort(_('cannot use --parent on non-merge changeset'))
735 720 parent = p1
736 721 hg.clean(repo, node, show_stats=False)
737 722 revert_opts = opts.copy()
738 723 revert_opts['all'] = True
739 724 revert_opts['rev'] = hex(parent)
740 725 revert(ui, repo, **revert_opts)
741 726 commit_opts = opts.copy()
742 727 commit_opts['addremove'] = False
743 728 if not commit_opts['message'] and not commit_opts['logfile']:
744 729 commit_opts['message'] = _("Backed out changeset %s") % (hex(node))
745 730 commit_opts['force_editor'] = True
746 731 commit(ui, repo, **commit_opts)
747 732 def nice(node):
748 733 return '%d:%s' % (repo.changelog.rev(node), short(node))
749 734 ui.status(_('changeset %s backs out changeset %s\n') %
750 735 (nice(repo.changelog.tip()), nice(node)))
751 736 if op1 != node:
752 737 if opts['merge']:
753 738 ui.status(_('merging with changeset %s\n') % nice(op1))
754 739 n = _lookup(repo, hex(op1))
755 740 hg.merge(repo, n)
756 741 else:
757 742 ui.status(_('the backout changeset is a new head - '
758 743 'do not forget to merge\n'))
759 744 ui.status(_('(use "backout --merge" '
760 745 'if you want to auto-merge)\n'))
761 746
762 747 def bundle(ui, repo, fname, dest=None, **opts):
763 748 """create a changegroup file
764 749
765 750 Generate a compressed changegroup file collecting all changesets
766 751 not found in the other repository.
767 752
768 753 This file can then be transferred using conventional means and
769 754 applied to another repository with the unbundle command. This is
770 755 useful when native push and pull are not available or when
771 756 exporting an entire repository is undesirable. The standard file
772 757 extension is ".hg".
773 758
774 759 Unlike import/export, this exactly preserves all changeset
775 760 contents including permissions, rename data, and revision history.
776 761 """
777 762 dest = ui.expandpath(dest or 'default-push', dest or 'default')
778 763 other = hg.repository(ui, dest)
779 764 o = repo.findoutgoing(other, force=opts['force'])
780 765 cg = repo.changegroup(o, 'bundle')
781 766 write_bundle(cg, fname)
782 767
783 768 def cat(ui, repo, file1, *pats, **opts):
784 769 """output the latest or given revisions of files
785 770
786 771 Print the specified files as they were at the given revision.
787 772 If no revision is given then working dir parent is used, or tip
788 773 if no revision is checked out.
789 774
790 775 Output may be to a file, in which case the name of the file is
791 776 given using a format string. The formatting rules are the same as
792 777 for the export command, with the following additions:
793 778
794 779 %s basename of file being printed
795 780 %d dirname of file being printed, or '.' if in repo root
796 781 %p root-relative path name of file being printed
797 782 """
798 783 ctx = repo.changectx(opts['rev'])
799 784 for src, abs, rel, exact in cmdutil.walk(repo, (file1,) + pats, opts,
800 785 ctx.node()):
801 786 fp = cmdutil.make_file(repo, opts['output'], ctx.node(), pathname=abs)
802 787 fp.write(ctx.filectx(abs).data())
803 788
804 789 def clone(ui, source, dest=None, **opts):
805 790 """make a copy of an existing repository
806 791
807 792 Create a copy of an existing repository in a new directory.
808 793
809 794 If no destination directory name is specified, it defaults to the
810 795 basename of the source.
811 796
812 797 The location of the source is added to the new repository's
813 798 .hg/hgrc file, as the default to be used for future pulls.
814 799
815 800 For efficiency, hardlinks are used for cloning whenever the source
816 801 and destination are on the same filesystem (note this applies only
817 802 to the repository data, not to the checked out files). Some
818 803 filesystems, such as AFS, implement hardlinking incorrectly, but
819 804 do not report errors. In these cases, use the --pull option to
820 805 avoid hardlinking.
821 806
822 807 You can safely clone repositories and checked out files using full
823 808 hardlinks with
824 809
825 810 $ cp -al REPO REPOCLONE
826 811
827 812 which is the fastest way to clone. However, the operation is not
828 813 atomic (making sure REPO is not modified during the operation is
829 814 up to you) and you have to make sure your editor breaks hardlinks
830 815 (Emacs and most Linux Kernel tools do so).
831 816
832 817 If you use the -r option to clone up to a specific revision, no
833 818 subsequent revisions will be present in the cloned repository.
834 819 This option implies --pull, even on local repositories.
835 820
836 821 See pull for valid source format details.
837 822
838 823 It is possible to specify an ssh:// URL as the destination, but no
839 824 .hg/hgrc will be created on the remote side. Look at the help text
840 825 for the pull command for important details about ssh:// URLs.
841 826 """
842 827 setremoteconfig(ui, opts)
843 828 hg.clone(ui, ui.expandpath(source), dest,
844 829 pull=opts['pull'],
845 830 stream=opts['uncompressed'],
846 831 rev=opts['rev'],
847 832 update=not opts['noupdate'])
848 833
849 834 def commit(ui, repo, *pats, **opts):
850 835 """commit the specified files or all outstanding changes
851 836
852 837 Commit changes to the given files into the repository.
853 838
854 839 If a list of files is omitted, all changes reported by "hg status"
855 840 will be committed.
856 841
857 842 If no commit message is specified, the editor configured in your hgrc
858 843 or in the EDITOR environment variable is started to enter a message.
859 844 """
860 845 message = logmessage(opts)
861 846
862 847 if opts['addremove']:
863 848 cmdutil.addremove(repo, pats, opts)
864 849 fns, match, anypats = cmdutil.matchpats(repo, pats, opts)
865 850 if pats:
866 851 modified, added, removed = repo.status(files=fns, match=match)[:3]
867 852 files = modified + added + removed
868 853 else:
869 854 files = []
870 855 try:
871 856 repo.commit(files, message, opts['user'], opts['date'], match,
872 857 force_editor=opts.get('force_editor'))
873 858 except ValueError, inst:
874 859 raise util.Abort(str(inst))
875 860
876 861 def docopy(ui, repo, pats, opts, wlock):
877 862 # called with the repo lock held
878 863 cwd = repo.getcwd()
879 864 errors = 0
880 865 copied = []
881 866 targets = {}
882 867
883 868 def okaytocopy(abs, rel, exact):
884 869 reasons = {'?': _('is not managed'),
885 870 'a': _('has been marked for add'),
886 871 'r': _('has been marked for remove')}
887 872 state = repo.dirstate.state(abs)
888 873 reason = reasons.get(state)
889 874 if reason:
890 875 if state == 'a':
891 876 origsrc = repo.dirstate.copied(abs)
892 877 if origsrc is not None:
893 878 return origsrc
894 879 if exact:
895 880 ui.warn(_('%s: not copying - file %s\n') % (rel, reason))
896 881 else:
897 882 return abs
898 883
899 884 def copy(origsrc, abssrc, relsrc, target, exact):
900 885 abstarget = util.canonpath(repo.root, cwd, target)
901 886 reltarget = util.pathto(cwd, abstarget)
902 887 prevsrc = targets.get(abstarget)
903 888 if prevsrc is not None:
904 889 ui.warn(_('%s: not overwriting - %s collides with %s\n') %
905 890 (reltarget, abssrc, prevsrc))
906 891 return
907 892 if (not opts['after'] and os.path.exists(reltarget) or
908 893 opts['after'] and repo.dirstate.state(abstarget) not in '?r'):
909 894 if not opts['force']:
910 895 ui.warn(_('%s: not overwriting - file exists\n') %
911 896 reltarget)
912 897 return
913 898 if not opts['after'] and not opts.get('dry_run'):
914 899 os.unlink(reltarget)
915 900 if opts['after']:
916 901 if not os.path.exists(reltarget):
917 902 return
918 903 else:
919 904 targetdir = os.path.dirname(reltarget) or '.'
920 905 if not os.path.isdir(targetdir) and not opts.get('dry_run'):
921 906 os.makedirs(targetdir)
922 907 try:
923 908 restore = repo.dirstate.state(abstarget) == 'r'
924 909 if restore and not opts.get('dry_run'):
925 910 repo.undelete([abstarget], wlock)
926 911 try:
927 912 if not opts.get('dry_run'):
928 913 shutil.copyfile(relsrc, reltarget)
929 914 shutil.copymode(relsrc, reltarget)
930 915 restore = False
931 916 finally:
932 917 if restore:
933 918 repo.remove([abstarget], wlock)
934 919 except shutil.Error, inst:
935 920 raise util.Abort(str(inst))
936 921 except IOError, inst:
937 922 if inst.errno == errno.ENOENT:
938 923 ui.warn(_('%s: deleted in working copy\n') % relsrc)
939 924 else:
940 925 ui.warn(_('%s: cannot copy - %s\n') %
941 926 (relsrc, inst.strerror))
942 927 errors += 1
943 928 return
944 929 if ui.verbose or not exact:
945 930 ui.status(_('copying %s to %s\n') % (relsrc, reltarget))
946 931 targets[abstarget] = abssrc
947 932 if abstarget != origsrc and not opts.get('dry_run'):
948 933 repo.copy(origsrc, abstarget, wlock)
949 934 copied.append((abssrc, relsrc, exact))
950 935
951 936 def targetpathfn(pat, dest, srcs):
952 937 if os.path.isdir(pat):
953 938 abspfx = util.canonpath(repo.root, cwd, pat)
954 939 if destdirexists:
955 940 striplen = len(os.path.split(abspfx)[0])
956 941 else:
957 942 striplen = len(abspfx)
958 943 if striplen:
959 944 striplen += len(os.sep)
960 945 res = lambda p: os.path.join(dest, p[striplen:])
961 946 elif destdirexists:
962 947 res = lambda p: os.path.join(dest, os.path.basename(p))
963 948 else:
964 949 res = lambda p: dest
965 950 return res
966 951
967 952 def targetpathafterfn(pat, dest, srcs):
968 953 if util.patkind(pat, None)[0]:
969 954 # a mercurial pattern
970 955 res = lambda p: os.path.join(dest, os.path.basename(p))
971 956 else:
972 957 abspfx = util.canonpath(repo.root, cwd, pat)
973 958 if len(abspfx) < len(srcs[0][0]):
974 959 # A directory. Either the target path contains the last
975 960 # component of the source path or it does not.
976 961 def evalpath(striplen):
977 962 score = 0
978 963 for s in srcs:
979 964 t = os.path.join(dest, s[0][striplen:])
980 965 if os.path.exists(t):
981 966 score += 1
982 967 return score
983 968
984 969 striplen = len(abspfx)
985 970 if striplen:
986 971 striplen += len(os.sep)
987 972 if os.path.isdir(os.path.join(dest, os.path.split(abspfx)[1])):
988 973 score = evalpath(striplen)
989 974 striplen1 = len(os.path.split(abspfx)[0])
990 975 if striplen1:
991 976 striplen1 += len(os.sep)
992 977 if evalpath(striplen1) > score:
993 978 striplen = striplen1
994 979 res = lambda p: os.path.join(dest, p[striplen:])
995 980 else:
996 981 # a file
997 982 if destdirexists:
998 983 res = lambda p: os.path.join(dest, os.path.basename(p))
999 984 else:
1000 985 res = lambda p: dest
1001 986 return res
1002 987
1003 988
1004 989 pats = list(pats)
1005 990 if not pats:
1006 991 raise util.Abort(_('no source or destination specified'))
1007 992 if len(pats) == 1:
1008 993 raise util.Abort(_('no destination specified'))
1009 994 dest = pats.pop()
1010 995 destdirexists = os.path.isdir(dest)
1011 996 if (len(pats) > 1 or util.patkind(pats[0], None)[0]) and not destdirexists:
1012 997 raise util.Abort(_('with multiple sources, destination must be an '
1013 998 'existing directory'))
1014 999 if opts['after']:
1015 1000 tfn = targetpathafterfn
1016 1001 else:
1017 1002 tfn = targetpathfn
1018 1003 copylist = []
1019 1004 for pat in pats:
1020 1005 srcs = []
1021 1006 for tag, abssrc, relsrc, exact in cmdutil.walk(repo, [pat], opts):
1022 1007 origsrc = okaytocopy(abssrc, relsrc, exact)
1023 1008 if origsrc:
1024 1009 srcs.append((origsrc, abssrc, relsrc, exact))
1025 1010 if not srcs:
1026 1011 continue
1027 1012 copylist.append((tfn(pat, dest, srcs), srcs))
1028 1013 if not copylist:
1029 1014 raise util.Abort(_('no files to copy'))
1030 1015
1031 1016 for targetpath, srcs in copylist:
1032 1017 for origsrc, abssrc, relsrc, exact in srcs:
1033 1018 copy(origsrc, abssrc, relsrc, targetpath(abssrc), exact)
1034 1019
1035 1020 if errors:
1036 1021 ui.warn(_('(consider using --after)\n'))
1037 1022 return errors, copied
1038 1023
1039 1024 def copy(ui, repo, *pats, **opts):
1040 1025 """mark files as copied for the next commit
1041 1026
1042 1027 Mark dest as having copies of source files. If dest is a
1043 1028 directory, copies are put in that directory. If dest is a file,
1044 1029 there can only be one source.
1045 1030
1046 1031 By default, this command copies the contents of files as they
1047 1032 stand in the working directory. If invoked with --after, the
1048 1033 operation is recorded, but no copying is performed.
1049 1034
1050 1035 This command takes effect in the next commit.
1051 1036
1052 1037 NOTE: This command should be treated as experimental. While it
1053 1038 should properly record copied files, this information is not yet
1054 1039 fully used by merge, nor fully reported by log.
1055 1040 """
1056 1041 wlock = repo.wlock(0)
1057 1042 errs, copied = docopy(ui, repo, pats, opts, wlock)
1058 1043 return errs
1059 1044
1060 1045 def debugancestor(ui, index, rev1, rev2):
1061 1046 """find the ancestor revision of two revisions in a given index"""
1062 1047 r = revlog.revlog(util.opener(os.getcwd(), audit=False), index, "", 0)
1063 1048 a = r.ancestor(r.lookup(rev1), r.lookup(rev2))
1064 1049 ui.write("%d:%s\n" % (r.rev(a), hex(a)))
1065 1050
1066 1051 def debugcomplete(ui, cmd='', **opts):
1067 1052 """returns the completion list associated with the given command"""
1068 1053
1069 1054 if opts['options']:
1070 1055 options = []
1071 1056 otables = [globalopts]
1072 1057 if cmd:
1073 1058 aliases, entry = findcmd(ui, cmd)
1074 1059 otables.append(entry[1])
1075 1060 for t in otables:
1076 1061 for o in t:
1077 1062 if o[0]:
1078 1063 options.append('-%s' % o[0])
1079 1064 options.append('--%s' % o[1])
1080 1065 ui.write("%s\n" % "\n".join(options))
1081 1066 return
1082 1067
1083 1068 clist = findpossible(ui, cmd).keys()
1084 1069 clist.sort()
1085 1070 ui.write("%s\n" % "\n".join(clist))
1086 1071
1087 1072 def debugrebuildstate(ui, repo, rev=None):
1088 1073 """rebuild the dirstate as it would look like for the given revision"""
1089 1074 if not rev:
1090 1075 rev = repo.changelog.tip()
1091 1076 else:
1092 1077 rev = repo.lookup(rev)
1093 1078 change = repo.changelog.read(rev)
1094 1079 n = change[0]
1095 1080 files = repo.manifest.read(n)
1096 1081 wlock = repo.wlock()
1097 1082 repo.dirstate.rebuild(rev, files)
1098 1083
1099 1084 def debugcheckstate(ui, repo):
1100 1085 """validate the correctness of the current dirstate"""
1101 1086 parent1, parent2 = repo.dirstate.parents()
1102 1087 repo.dirstate.read()
1103 1088 dc = repo.dirstate.map
1104 1089 keys = dc.keys()
1105 1090 keys.sort()
1106 1091 m1n = repo.changelog.read(parent1)[0]
1107 1092 m2n = repo.changelog.read(parent2)[0]
1108 1093 m1 = repo.manifest.read(m1n)
1109 1094 m2 = repo.manifest.read(m2n)
1110 1095 errors = 0
1111 1096 for f in dc:
1112 1097 state = repo.dirstate.state(f)
1113 1098 if state in "nr" and f not in m1:
1114 1099 ui.warn(_("%s in state %s, but not in manifest1\n") % (f, state))
1115 1100 errors += 1
1116 1101 if state in "a" and f in m1:
1117 1102 ui.warn(_("%s in state %s, but also in manifest1\n") % (f, state))
1118 1103 errors += 1
1119 1104 if state in "m" and f not in m1 and f not in m2:
1120 1105 ui.warn(_("%s in state %s, but not in either manifest\n") %
1121 1106 (f, state))
1122 1107 errors += 1
1123 1108 for f in m1:
1124 1109 state = repo.dirstate.state(f)
1125 1110 if state not in "nrm":
1126 1111 ui.warn(_("%s in manifest1, but listed as state %s") % (f, state))
1127 1112 errors += 1
1128 1113 if errors:
1129 1114 error = _(".hg/dirstate inconsistent with current parent's manifest")
1130 1115 raise util.Abort(error)
1131 1116
1132 1117 def debugconfig(ui, repo, *values):
1133 1118 """show combined config settings from all hgrc files
1134 1119
1135 1120 With no args, print names and values of all config items.
1136 1121
1137 1122 With one arg of the form section.name, print just the value of
1138 1123 that config item.
1139 1124
1140 1125 With multiple args, print names and values of all config items
1141 1126 with matching section names."""
1142 1127
1143 1128 if values:
1144 1129 if len([v for v in values if '.' in v]) > 1:
1145 1130 raise util.Abort(_('only one config item permitted'))
1146 1131 for section, name, value in ui.walkconfig():
1147 1132 sectname = section + '.' + name
1148 1133 if values:
1149 1134 for v in values:
1150 1135 if v == section:
1151 1136 ui.write('%s=%s\n' % (sectname, value))
1152 1137 elif v == sectname:
1153 1138 ui.write(value, '\n')
1154 1139 else:
1155 1140 ui.write('%s=%s\n' % (sectname, value))
1156 1141
1157 1142 def debugsetparents(ui, repo, rev1, rev2=None):
1158 1143 """manually set the parents of the current working directory
1159 1144
1160 1145 This is useful for writing repository conversion tools, but should
1161 1146 be used with care.
1162 1147 """
1163 1148
1164 1149 if not rev2:
1165 1150 rev2 = hex(nullid)
1166 1151
1167 1152 repo.dirstate.setparents(repo.lookup(rev1), repo.lookup(rev2))
1168 1153
1169 1154 def debugstate(ui, repo):
1170 1155 """show the contents of the current dirstate"""
1171 1156 repo.dirstate.read()
1172 1157 dc = repo.dirstate.map
1173 1158 keys = dc.keys()
1174 1159 keys.sort()
1175 1160 for file_ in keys:
1176 1161 ui.write("%c %3o %10d %s %s\n"
1177 1162 % (dc[file_][0], dc[file_][1] & 0777, dc[file_][2],
1178 1163 time.strftime("%x %X",
1179 1164 time.localtime(dc[file_][3])), file_))
1180 1165 for f in repo.dirstate.copies:
1181 1166 ui.write(_("copy: %s -> %s\n") % (repo.dirstate.copies[f], f))
1182 1167
1183 1168 def debugdata(ui, file_, rev):
1184 1169 """dump the contents of an data file revision"""
1185 1170 r = revlog.revlog(util.opener(os.getcwd(), audit=False),
1186 1171 file_[:-2] + ".i", file_, 0)
1187 1172 try:
1188 1173 ui.write(r.revision(r.lookup(rev)))
1189 1174 except KeyError:
1190 1175 raise util.Abort(_('invalid revision identifier %s') % rev)
1191 1176
1192 1177 def debugindex(ui, file_):
1193 1178 """dump the contents of an index file"""
1194 1179 r = revlog.revlog(util.opener(os.getcwd(), audit=False), file_, "", 0)
1195 1180 ui.write(" rev offset length base linkrev" +
1196 1181 " nodeid p1 p2\n")
1197 1182 for i in range(r.count()):
1198 1183 node = r.node(i)
1199 1184 pp = r.parents(node)
1200 1185 ui.write("% 6d % 9d % 7d % 6d % 7d %s %s %s\n" % (
1201 1186 i, r.start(i), r.length(i), r.base(i), r.linkrev(node),
1202 1187 short(node), short(pp[0]), short(pp[1])))
1203 1188
1204 1189 def debugindexdot(ui, file_):
1205 1190 """dump an index DAG as a .dot file"""
1206 1191 r = revlog.revlog(util.opener(os.getcwd(), audit=False), file_, "", 0)
1207 1192 ui.write("digraph G {\n")
1208 1193 for i in range(r.count()):
1209 1194 node = r.node(i)
1210 1195 pp = r.parents(node)
1211 1196 ui.write("\t%d -> %d\n" % (r.rev(pp[0]), i))
1212 1197 if pp[1] != nullid:
1213 1198 ui.write("\t%d -> %d\n" % (r.rev(pp[1]), i))
1214 1199 ui.write("}\n")
1215 1200
1216 1201 def debugrename(ui, repo, file, rev=None):
1217 1202 """dump rename information"""
1218 1203 r = repo.file(relpath(repo, [file])[0])
1219 1204 if rev:
1220 1205 try:
1221 1206 # assume all revision numbers are for changesets
1222 1207 n = repo.lookup(rev)
1223 1208 change = repo.changelog.read(n)
1224 1209 m = repo.manifest.read(change[0])
1225 1210 n = m[relpath(repo, [file])[0]]
1226 1211 except (hg.RepoError, KeyError):
1227 1212 n = r.lookup(rev)
1228 1213 else:
1229 1214 n = r.tip()
1230 1215 m = r.renamed(n)
1231 1216 if m:
1232 1217 ui.write(_("renamed from %s:%s\n") % (m[0], hex(m[1])))
1233 1218 else:
1234 1219 ui.write(_("not renamed\n"))
1235 1220
1236 1221 def debugwalk(ui, repo, *pats, **opts):
1237 1222 """show how files match on given patterns"""
1238 1223 items = list(cmdutil.walk(repo, pats, opts))
1239 1224 if not items:
1240 1225 return
1241 1226 fmt = '%%s %%-%ds %%-%ds %%s' % (
1242 1227 max([len(abs) for (src, abs, rel, exact) in items]),
1243 1228 max([len(rel) for (src, abs, rel, exact) in items]))
1244 1229 for src, abs, rel, exact in items:
1245 1230 line = fmt % (src, abs, rel, exact and 'exact' or '')
1246 1231 ui.write("%s\n" % line.rstrip())
1247 1232
1248 1233 def diff(ui, repo, *pats, **opts):
1249 1234 """diff repository (or selected files)
1250 1235
1251 1236 Show differences between revisions for the specified files.
1252 1237
1253 1238 Differences between files are shown using the unified diff format.
1254 1239
1255 1240 When two revision arguments are given, then changes are shown
1256 1241 between those revisions. If only one revision is specified then
1257 1242 that revision is compared to the working directory, and, when no
1258 1243 revisions are specified, the working directory files are compared
1259 1244 to its parent.
1260 1245
1261 1246 Without the -a option, diff will avoid generating diffs of files
1262 1247 it detects as binary. With -a, diff will generate a diff anyway,
1263 1248 probably with undesirable results.
1264 1249 """
1265 1250 node1, node2 = cmdutil.revpair(ui, repo, opts['rev'])
1266 1251
1267 1252 fns, matchfn, anypats = cmdutil.matchpats(repo, pats, opts)
1268 1253
1269 1254 patch.diff(repo, node1, node2, fns, match=matchfn,
1270 1255 opts=patch.diffopts(ui, opts))
1271 1256
1272 1257 def export(ui, repo, *changesets, **opts):
1273 1258 """dump the header and diffs for one or more changesets
1274 1259
1275 1260 Print the changeset header and diffs for one or more revisions.
1276 1261
1277 1262 The information shown in the changeset header is: author,
1278 1263 changeset hash, parent and commit comment.
1279 1264
1280 1265 Output may be to a file, in which case the name of the file is
1281 1266 given using a format string. The formatting rules are as follows:
1282 1267
1283 1268 %% literal "%" character
1284 1269 %H changeset hash (40 bytes of hexadecimal)
1285 1270 %N number of patches being generated
1286 1271 %R changeset revision number
1287 1272 %b basename of the exporting repository
1288 1273 %h short-form changeset hash (12 bytes of hexadecimal)
1289 1274 %n zero-padded sequence number, starting at 1
1290 1275 %r zero-padded changeset revision number
1291 1276
1292 1277 Without the -a option, export will avoid generating diffs of files
1293 1278 it detects as binary. With -a, export will generate a diff anyway,
1294 1279 probably with undesirable results.
1295 1280
1296 1281 With the --switch-parent option, the diff will be against the second
1297 1282 parent. It can be useful to review a merge.
1298 1283 """
1299 1284 if not changesets:
1300 1285 raise util.Abort(_("export requires at least one changeset"))
1301 1286 revs = list(cmdutil.revrange(ui, repo, changesets))
1302 1287 if len(revs) > 1:
1303 1288 ui.note(_('exporting patches:\n'))
1304 1289 else:
1305 1290 ui.note(_('exporting patch:\n'))
1306 1291 patch.export(repo, map(repo.lookup, revs), template=opts['output'],
1307 1292 switch_parent=opts['switch_parent'],
1308 1293 opts=patch.diffopts(ui, opts))
1309 1294
1310 1295 def forget(ui, repo, *pats, **opts):
1311 1296 """don't add the specified files on the next commit (DEPRECATED)
1312 1297
1313 1298 (DEPRECATED)
1314 1299 Undo an 'hg add' scheduled for the next commit.
1315 1300
1316 1301 This command is now deprecated and will be removed in a future
1317 1302 release. Please use revert instead.
1318 1303 """
1319 1304 ui.warn(_("(the forget command is deprecated; use revert instead)\n"))
1320 1305 forget = []
1321 1306 for src, abs, rel, exact in cmdutil.walk(repo, pats, opts):
1322 1307 if repo.dirstate.state(abs) == 'a':
1323 1308 forget.append(abs)
1324 1309 if ui.verbose or not exact:
1325 1310 ui.status(_('forgetting %s\n') % ((pats and rel) or abs))
1326 1311 repo.forget(forget)
1327 1312
1328 1313 def grep(ui, repo, pattern, *pats, **opts):
1329 1314 """search for a pattern in specified files and revisions
1330 1315
1331 1316 Search revisions of files for a regular expression.
1332 1317
1333 1318 This command behaves differently than Unix grep. It only accepts
1334 1319 Python/Perl regexps. It searches repository history, not the
1335 1320 working directory. It always prints the revision number in which
1336 1321 a match appears.
1337 1322
1338 1323 By default, grep only prints output for the first revision of a
1339 1324 file in which it finds a match. To get it to print every revision
1340 1325 that contains a change in match status ("-" for a match that
1341 1326 becomes a non-match, or "+" for a non-match that becomes a match),
1342 1327 use the --all flag.
1343 1328 """
1344 1329 reflags = 0
1345 1330 if opts['ignore_case']:
1346 1331 reflags |= re.I
1347 1332 regexp = re.compile(pattern, reflags)
1348 1333 sep, eol = ':', '\n'
1349 1334 if opts['print0']:
1350 1335 sep = eol = '\0'
1351 1336
1352 1337 fcache = {}
1353 1338 def getfile(fn):
1354 1339 if fn not in fcache:
1355 1340 fcache[fn] = repo.file(fn)
1356 1341 return fcache[fn]
1357 1342
1358 1343 def matchlines(body):
1359 1344 begin = 0
1360 1345 linenum = 0
1361 1346 while True:
1362 1347 match = regexp.search(body, begin)
1363 1348 if not match:
1364 1349 break
1365 1350 mstart, mend = match.span()
1366 1351 linenum += body.count('\n', begin, mstart) + 1
1367 1352 lstart = body.rfind('\n', begin, mstart) + 1 or begin
1368 1353 lend = body.find('\n', mend)
1369 1354 yield linenum, mstart - lstart, mend - lstart, body[lstart:lend]
1370 1355 begin = lend + 1
1371 1356
1372 1357 class linestate(object):
1373 1358 def __init__(self, line, linenum, colstart, colend):
1374 1359 self.line = line
1375 1360 self.linenum = linenum
1376 1361 self.colstart = colstart
1377 1362 self.colend = colend
1378 1363
1379 1364 def __eq__(self, other):
1380 1365 return self.line == other.line
1381 1366
1382 1367 matches = {}
1383 1368 copies = {}
1384 1369 def grepbody(fn, rev, body):
1385 1370 matches[rev].setdefault(fn, [])
1386 1371 m = matches[rev][fn]
1387 1372 for lnum, cstart, cend, line in matchlines(body):
1388 1373 s = linestate(line, lnum, cstart, cend)
1389 1374 m.append(s)
1390 1375
1391 1376 def difflinestates(a, b):
1392 1377 sm = difflib.SequenceMatcher(None, a, b)
1393 1378 for tag, alo, ahi, blo, bhi in sm.get_opcodes():
1394 1379 if tag == 'insert':
1395 1380 for i in range(blo, bhi):
1396 1381 yield ('+', b[i])
1397 1382 elif tag == 'delete':
1398 1383 for i in range(alo, ahi):
1399 1384 yield ('-', a[i])
1400 1385 elif tag == 'replace':
1401 1386 for i in range(alo, ahi):
1402 1387 yield ('-', a[i])
1403 1388 for i in range(blo, bhi):
1404 1389 yield ('+', b[i])
1405 1390
1406 1391 prev = {}
1407 1392 ucache = {}
1408 1393 def display(fn, rev, states, prevstates):
1409 1394 counts = {'-': 0, '+': 0}
1410 1395 filerevmatches = {}
1411 1396 if incrementing or not opts['all']:
1412 1397 a, b = prevstates, states
1413 1398 else:
1414 1399 a, b = states, prevstates
1415 1400 for change, l in difflinestates(a, b):
1416 1401 if incrementing or not opts['all']:
1417 1402 r = rev
1418 1403 else:
1419 1404 r = prev[fn]
1420 1405 cols = [fn, str(r)]
1421 1406 if opts['line_number']:
1422 1407 cols.append(str(l.linenum))
1423 1408 if opts['all']:
1424 1409 cols.append(change)
1425 1410 if opts['user']:
1426 1411 cols.append(trimuser(ui, getchange(r)[1], rev,
1427 1412 ucache))
1428 1413 if opts['files_with_matches']:
1429 1414 c = (fn, rev)
1430 1415 if c in filerevmatches:
1431 1416 continue
1432 1417 filerevmatches[c] = 1
1433 1418 else:
1434 1419 cols.append(l.line)
1435 1420 ui.write(sep.join(cols), eol)
1436 1421 counts[change] += 1
1437 1422 return counts['+'], counts['-']
1438 1423
1439 1424 fstate = {}
1440 1425 skip = {}
1441 1426 changeiter, getchange, matchfn = walkchangerevs(ui, repo, pats, opts)
1442 1427 count = 0
1443 1428 incrementing = False
1444 1429 follow = opts.get('follow')
1445 1430 for st, rev, fns in changeiter:
1446 1431 if st == 'window':
1447 1432 incrementing = rev
1448 1433 matches.clear()
1449 1434 elif st == 'add':
1450 1435 change = repo.changelog.read(repo.lookup(str(rev)))
1451 1436 mf = repo.manifest.read(change[0])
1452 1437 matches[rev] = {}
1453 1438 for fn in fns:
1454 1439 if fn in skip:
1455 1440 continue
1456 1441 fstate.setdefault(fn, {})
1457 1442 try:
1458 1443 grepbody(fn, rev, getfile(fn).read(mf[fn]))
1459 1444 if follow:
1460 1445 copied = getfile(fn).renamed(mf[fn])
1461 1446 if copied:
1462 1447 copies.setdefault(rev, {})[fn] = copied[0]
1463 1448 except KeyError:
1464 1449 pass
1465 1450 elif st == 'iter':
1466 1451 states = matches[rev].items()
1467 1452 states.sort()
1468 1453 for fn, m in states:
1469 1454 copy = copies.get(rev, {}).get(fn)
1470 1455 if fn in skip:
1471 1456 if copy:
1472 1457 skip[copy] = True
1473 1458 continue
1474 1459 if incrementing or not opts['all'] or fstate[fn]:
1475 1460 pos, neg = display(fn, rev, m, fstate[fn])
1476 1461 count += pos + neg
1477 1462 if pos and not opts['all']:
1478 1463 skip[fn] = True
1479 1464 if copy:
1480 1465 skip[copy] = True
1481 1466 fstate[fn] = m
1482 1467 if copy:
1483 1468 fstate[copy] = m
1484 1469 prev[fn] = rev
1485 1470
1486 1471 if not incrementing:
1487 1472 fstate = fstate.items()
1488 1473 fstate.sort()
1489 1474 for fn, state in fstate:
1490 1475 if fn in skip:
1491 1476 continue
1492 1477 if fn not in copies.get(prev[fn], {}):
1493 1478 display(fn, rev, {}, state)
1494 1479 return (count == 0 and 1) or 0
1495 1480
1496 1481 def heads(ui, repo, **opts):
1497 1482 """show current repository heads
1498 1483
1499 1484 Show all repository head changesets.
1500 1485
1501 1486 Repository "heads" are changesets that don't have children
1502 1487 changesets. They are where development generally takes place and
1503 1488 are the usual targets for update and merge operations.
1504 1489 """
1505 1490 if opts['rev']:
1506 1491 heads = repo.heads(repo.lookup(opts['rev']))
1507 1492 else:
1508 1493 heads = repo.heads()
1509 1494 br = None
1510 1495 if opts['branches']:
1511 1496 br = repo.branchlookup(heads)
1512 1497 displayer = show_changeset(ui, repo, opts)
1513 1498 for n in heads:
1514 1499 displayer.show(changenode=n, brinfo=br)
1515 1500
1516 1501 def identify(ui, repo):
1517 1502 """print information about the working copy
1518 1503
1519 1504 Print a short summary of the current state of the repo.
1520 1505
1521 1506 This summary identifies the repository state using one or two parent
1522 1507 hash identifiers, followed by a "+" if there are uncommitted changes
1523 1508 in the working directory, followed by a list of tags for this revision.
1524 1509 """
1525 1510 parents = [p for p in repo.dirstate.parents() if p != nullid]
1526 1511 if not parents:
1527 1512 ui.write(_("unknown\n"))
1528 1513 return
1529 1514
1530 1515 hexfunc = ui.debugflag and hex or short
1531 1516 modified, added, removed, deleted = repo.status()[:4]
1532 1517 output = ["%s%s" %
1533 1518 ('+'.join([hexfunc(parent) for parent in parents]),
1534 1519 (modified or added or removed or deleted) and "+" or "")]
1535 1520
1536 1521 if not ui.quiet:
1537 1522 # multiple tags for a single parent separated by '/'
1538 1523 parenttags = ['/'.join(tags)
1539 1524 for tags in map(repo.nodetags, parents) if tags]
1540 1525 # tags for multiple parents separated by ' + '
1541 1526 if parenttags:
1542 1527 output.append(' + '.join(parenttags))
1543 1528
1544 1529 ui.write("%s\n" % ' '.join(output))
1545 1530
1546 1531 def import_(ui, repo, patch1, *patches, **opts):
1547 1532 """import an ordered set of patches
1548 1533
1549 1534 Import a list of patches and commit them individually.
1550 1535
1551 1536 If there are outstanding changes in the working directory, import
1552 1537 will abort unless given the -f flag.
1553 1538
1554 1539 You can import a patch straight from a mail message. Even patches
1555 1540 as attachments work (body part must be type text/plain or
1556 1541 text/x-patch to be used). From and Subject headers of email
1557 1542 message are used as default committer and commit message. All
1558 1543 text/plain body parts before first diff are added to commit
1559 1544 message.
1560 1545
1561 1546 If imported patch was generated by hg export, user and description
1562 1547 from patch override values from message headers and body. Values
1563 1548 given on command line with -m and -u override these.
1564 1549
1565 1550 To read a patch from standard input, use patch name "-".
1566 1551 """
1567 1552 patches = (patch1,) + patches
1568 1553
1569 1554 if not opts['force']:
1570 1555 bail_if_changed(repo)
1571 1556
1572 1557 d = opts["base"]
1573 1558 strip = opts["strip"]
1574 1559
1575 1560 wlock = repo.wlock()
1576 1561 lock = repo.lock()
1577 1562
1578 1563 for p in patches:
1579 1564 pf = os.path.join(d, p)
1580 1565
1581 1566 if pf == '-':
1582 1567 ui.status(_("applying patch from stdin\n"))
1583 1568 tmpname, message, user, date = patch.extract(ui, sys.stdin)
1584 1569 else:
1585 1570 ui.status(_("applying %s\n") % p)
1586 1571 tmpname, message, user, date = patch.extract(ui, file(pf))
1587 1572
1588 1573 if tmpname is None:
1589 1574 raise util.Abort(_('no diffs found'))
1590 1575
1591 1576 try:
1592 1577 if opts['message']:
1593 1578 # pickup the cmdline msg
1594 1579 message = opts['message']
1595 1580 elif message:
1596 1581 # pickup the patch msg
1597 1582 message = message.strip()
1598 1583 else:
1599 1584 # launch the editor
1600 1585 message = None
1601 1586 ui.debug(_('message:\n%s\n') % message)
1602 1587
1603 1588 files, fuzz = patch.patch(tmpname, ui, strip=strip, cwd=repo.root)
1604 1589 files = patch.updatedir(ui, repo, files, wlock=wlock)
1605 1590 repo.commit(files, message, user, date, wlock=wlock, lock=lock)
1606 1591 finally:
1607 1592 os.unlink(tmpname)
1608 1593
1609 1594 def incoming(ui, repo, source="default", **opts):
1610 1595 """show new changesets found in source
1611 1596
1612 1597 Show new changesets found in the specified path/URL or the default
1613 1598 pull location. These are the changesets that would be pulled if a pull
1614 1599 was requested.
1615 1600
1616 1601 For remote repository, using --bundle avoids downloading the changesets
1617 1602 twice if the incoming is followed by a pull.
1618 1603
1619 1604 See pull for valid source format details.
1620 1605 """
1621 1606 source = ui.expandpath(source)
1622 1607 setremoteconfig(ui, opts)
1623 1608
1624 1609 other = hg.repository(ui, source)
1625 1610 incoming = repo.findincoming(other, force=opts["force"])
1626 1611 if not incoming:
1627 1612 ui.status(_("no changes found\n"))
1628 1613 return
1629 1614
1630 1615 cleanup = None
1631 1616 try:
1632 1617 fname = opts["bundle"]
1633 1618 if fname or not other.local():
1634 1619 # create a bundle (uncompressed if other repo is not local)
1635 1620 cg = other.changegroup(incoming, "incoming")
1636 1621 fname = cleanup = write_bundle(cg, fname, compress=other.local())
1637 1622 # keep written bundle?
1638 1623 if opts["bundle"]:
1639 1624 cleanup = None
1640 1625 if not other.local():
1641 1626 # use the created uncompressed bundlerepo
1642 1627 other = bundlerepo.bundlerepository(ui, repo.root, fname)
1643 1628
1644 1629 revs = None
1645 1630 if opts['rev']:
1646 1631 revs = [other.lookup(rev) for rev in opts['rev']]
1647 1632 o = other.changelog.nodesbetween(incoming, revs)[0]
1648 1633 if opts['newest_first']:
1649 1634 o.reverse()
1650 1635 displayer = show_changeset(ui, other, opts)
1651 1636 for n in o:
1652 1637 parents = [p for p in other.changelog.parents(n) if p != nullid]
1653 1638 if opts['no_merges'] and len(parents) == 2:
1654 1639 continue
1655 1640 displayer.show(changenode=n)
1656 1641 if opts['patch']:
1657 1642 prev = (parents and parents[0]) or nullid
1658 1643 patch.diff(other, prev, n, fp=repo.ui)
1659 1644 ui.write("\n")
1660 1645 finally:
1661 1646 if hasattr(other, 'close'):
1662 1647 other.close()
1663 1648 if cleanup:
1664 1649 os.unlink(cleanup)
1665 1650
1666 1651 def init(ui, dest=".", **opts):
1667 1652 """create a new repository in the given directory
1668 1653
1669 1654 Initialize a new repository in the given directory. If the given
1670 1655 directory does not exist, it is created.
1671 1656
1672 1657 If no directory is given, the current directory is used.
1673 1658
1674 1659 It is possible to specify an ssh:// URL as the destination.
1675 1660 Look at the help text for the pull command for important details
1676 1661 about ssh:// URLs.
1677 1662 """
1678 1663 setremoteconfig(ui, opts)
1679 1664 hg.repository(ui, dest, create=1)
1680 1665
1681 1666 def locate(ui, repo, *pats, **opts):
1682 1667 """locate files matching specific patterns
1683 1668
1684 1669 Print all files under Mercurial control whose names match the
1685 1670 given patterns.
1686 1671
1687 1672 This command searches the current directory and its
1688 1673 subdirectories. To search an entire repository, move to the root
1689 1674 of the repository.
1690 1675
1691 1676 If no patterns are given to match, this command prints all file
1692 1677 names.
1693 1678
1694 1679 If you want to feed the output of this command into the "xargs"
1695 1680 command, use the "-0" option to both this command and "xargs".
1696 1681 This will avoid the problem of "xargs" treating single filenames
1697 1682 that contain white space as multiple filenames.
1698 1683 """
1699 1684 end = opts['print0'] and '\0' or '\n'
1700 1685 rev = opts['rev']
1701 1686 if rev:
1702 1687 node = repo.lookup(rev)
1703 1688 else:
1704 1689 node = None
1705 1690
1706 1691 for src, abs, rel, exact in cmdutil.walk(repo, pats, opts, node=node,
1707 1692 head='(?:.*/|)'):
1708 1693 if not node and repo.dirstate.state(abs) == '?':
1709 1694 continue
1710 1695 if opts['fullpath']:
1711 1696 ui.write(os.path.join(repo.root, abs), end)
1712 1697 else:
1713 1698 ui.write(((pats and rel) or abs), end)
1714 1699
1715 1700 def log(ui, repo, *pats, **opts):
1716 1701 """show revision history of entire repository or files
1717 1702
1718 1703 Print the revision history of the specified files or the entire
1719 1704 project.
1720 1705
1721 1706 File history is shown without following rename or copy history of
1722 1707 files. Use -f/--follow with a file name to follow history across
1723 1708 renames and copies. --follow without a file name will only show
1724 1709 ancestors or descendants of the starting revision. --follow-first
1725 1710 only follows the first parent of merge revisions.
1726 1711
1727 1712 If no revision range is specified, the default is tip:0 unless
1728 1713 --follow is set, in which case the working directory parent is
1729 1714 used as the starting revision.
1730 1715
1731 1716 By default this command outputs: changeset id and hash, tags,
1732 1717 non-trivial parents, user, date and time, and a summary for each
1733 1718 commit. When the -v/--verbose switch is used, the list of changed
1734 1719 files and full commit message is shown.
1735 1720 """
1736 1721 class dui(object):
1737 1722 # Implement and delegate some ui protocol. Save hunks of
1738 1723 # output for later display in the desired order.
1739 1724 def __init__(self, ui):
1740 1725 self.ui = ui
1741 1726 self.hunk = {}
1742 1727 self.header = {}
1743 1728 def bump(self, rev):
1744 1729 self.rev = rev
1745 1730 self.hunk[rev] = []
1746 1731 self.header[rev] = []
1747 1732 def note(self, *args):
1748 1733 if self.verbose:
1749 1734 self.write(*args)
1750 1735 def status(self, *args):
1751 1736 if not self.quiet:
1752 1737 self.write(*args)
1753 1738 def write(self, *args):
1754 1739 self.hunk[self.rev].append(args)
1755 1740 def write_header(self, *args):
1756 1741 self.header[self.rev].append(args)
1757 1742 def debug(self, *args):
1758 1743 if self.debugflag:
1759 1744 self.write(*args)
1760 1745 def __getattr__(self, key):
1761 1746 return getattr(self.ui, key)
1762 1747
1763 1748 changeiter, getchange, matchfn = walkchangerevs(ui, repo, pats, opts)
1764 1749
1765 1750 if opts['limit']:
1766 1751 try:
1767 1752 limit = int(opts['limit'])
1768 1753 except ValueError:
1769 1754 raise util.Abort(_('limit must be a positive integer'))
1770 1755 if limit <= 0: raise util.Abort(_('limit must be positive'))
1771 1756 else:
1772 1757 limit = sys.maxint
1773 1758 count = 0
1774 1759
1775 1760 displayer = show_changeset(ui, repo, opts)
1776 1761 for st, rev, fns in changeiter:
1777 1762 if st == 'window':
1778 1763 du = dui(ui)
1779 1764 displayer.ui = du
1780 1765 elif st == 'add':
1781 1766 du.bump(rev)
1782 1767 changenode = repo.changelog.node(rev)
1783 1768 parents = [p for p in repo.changelog.parents(changenode)
1784 1769 if p != nullid]
1785 1770 if opts['no_merges'] and len(parents) == 2:
1786 1771 continue
1787 1772 if opts['only_merges'] and len(parents) != 2:
1788 1773 continue
1789 1774
1790 1775 if opts['keyword']:
1791 1776 changes = getchange(rev)
1792 1777 miss = 0
1793 1778 for k in [kw.lower() for kw in opts['keyword']]:
1794 1779 if not (k in changes[1].lower() or
1795 1780 k in changes[4].lower() or
1796 1781 k in " ".join(changes[3][:20]).lower()):
1797 1782 miss = 1
1798 1783 break
1799 1784 if miss:
1800 1785 continue
1801 1786
1802 1787 br = None
1803 1788 if opts['branches']:
1804 1789 br = repo.branchlookup([repo.changelog.node(rev)])
1805 1790
1806 1791 displayer.show(rev, brinfo=br)
1807 1792 if opts['patch']:
1808 1793 prev = (parents and parents[0]) or nullid
1809 1794 patch.diff(repo, prev, changenode, match=matchfn, fp=du)
1810 1795 du.write("\n\n")
1811 1796 elif st == 'iter':
1812 1797 if count == limit: break
1813 1798 if du.header[rev]:
1814 1799 for args in du.header[rev]:
1815 1800 ui.write_header(*args)
1816 1801 if du.hunk[rev]:
1817 1802 count += 1
1818 1803 for args in du.hunk[rev]:
1819 1804 ui.write(*args)
1820 1805
1821 1806 def manifest(ui, repo, rev=None):
1822 1807 """output the latest or given revision of the project manifest
1823 1808
1824 1809 Print a list of version controlled files for the given revision.
1825 1810
1826 1811 The manifest is the list of files being version controlled. If no revision
1827 1812 is given then the tip is used.
1828 1813 """
1829 1814 if rev:
1830 1815 try:
1831 1816 # assume all revision numbers are for changesets
1832 1817 n = repo.lookup(rev)
1833 1818 change = repo.changelog.read(n)
1834 1819 n = change[0]
1835 1820 except hg.RepoError:
1836 1821 n = repo.manifest.lookup(rev)
1837 1822 else:
1838 1823 n = repo.manifest.tip()
1839 1824 m = repo.manifest.read(n)
1840 1825 files = m.keys()
1841 1826 files.sort()
1842 1827
1843 1828 for f in files:
1844 1829 ui.write("%40s %3s %s\n" % (hex(m[f]),
1845 1830 m.execf(f) and "755" or "644", f))
1846 1831
1847 1832 def merge(ui, repo, node=None, force=None, branch=None):
1848 1833 """Merge working directory with another revision
1849 1834
1850 1835 Merge the contents of the current working directory and the
1851 1836 requested revision. Files that changed between either parent are
1852 1837 marked as changed for the next commit and a commit must be
1853 1838 performed before any further updates are allowed.
1854 1839
1855 1840 If no revision is specified, the working directory's parent is a
1856 1841 head revision, and the repository contains exactly one other head,
1857 1842 the other head is merged with by default. Otherwise, an explicit
1858 1843 revision to merge with must be provided.
1859 1844 """
1860 1845
1861 1846 if node or branch:
1862 1847 node = _lookup(repo, node, branch)
1863 1848 else:
1864 1849 heads = repo.heads()
1865 1850 if len(heads) > 2:
1866 1851 raise util.Abort(_('repo has %d heads - '
1867 1852 'please merge with an explicit rev') %
1868 1853 len(heads))
1869 1854 if len(heads) == 1:
1870 1855 raise util.Abort(_('there is nothing to merge - '
1871 1856 'use "hg update" instead'))
1872 1857 parent = repo.dirstate.parents()[0]
1873 1858 if parent not in heads:
1874 1859 raise util.Abort(_('working dir not at a head rev - '
1875 1860 'use "hg update" or merge with an explicit rev'))
1876 1861 node = parent == heads[0] and heads[-1] or heads[0]
1877 1862 return hg.merge(repo, node, force=force)
1878 1863
1879 1864 def outgoing(ui, repo, dest=None, **opts):
1880 1865 """show changesets not found in destination
1881 1866
1882 1867 Show changesets not found in the specified destination repository or
1883 1868 the default push location. These are the changesets that would be pushed
1884 1869 if a push was requested.
1885 1870
1886 1871 See pull for valid destination format details.
1887 1872 """
1888 1873 dest = ui.expandpath(dest or 'default-push', dest or 'default')
1889 1874 setremoteconfig(ui, opts)
1890 1875 revs = None
1891 1876 if opts['rev']:
1892 1877 revs = [repo.lookup(rev) for rev in opts['rev']]
1893 1878
1894 1879 other = hg.repository(ui, dest)
1895 1880 o = repo.findoutgoing(other, force=opts['force'])
1896 1881 if not o:
1897 1882 ui.status(_("no changes found\n"))
1898 1883 return
1899 1884 o = repo.changelog.nodesbetween(o, revs)[0]
1900 1885 if opts['newest_first']:
1901 1886 o.reverse()
1902 1887 displayer = show_changeset(ui, repo, opts)
1903 1888 for n in o:
1904 1889 parents = [p for p in repo.changelog.parents(n) if p != nullid]
1905 1890 if opts['no_merges'] and len(parents) == 2:
1906 1891 continue
1907 1892 displayer.show(changenode=n)
1908 1893 if opts['patch']:
1909 1894 prev = (parents and parents[0]) or nullid
1910 1895 patch.diff(repo, prev, n)
1911 1896 ui.write("\n")
1912 1897
1913 1898 def parents(ui, repo, file_=None, rev=None, branches=None, **opts):
1914 1899 """show the parents of the working dir or revision
1915 1900
1916 1901 Print the working directory's parent revisions.
1917 1902 """
1918 1903 # legacy
1919 1904 if file_ and not rev:
1920 1905 try:
1921 1906 rev = repo.lookup(file_)
1922 1907 file_ = None
1923 1908 except hg.RepoError:
1924 1909 pass
1925 1910 else:
1926 1911 ui.warn(_("'hg parent REV' is deprecated, "
1927 1912 "please use 'hg parents -r REV instead\n"))
1928 1913
1929 1914 if rev:
1930 1915 if file_:
1931 1916 ctx = repo.filectx(file_, changeid=rev)
1932 1917 else:
1933 1918 ctx = repo.changectx(rev)
1934 1919 p = [cp.node() for cp in ctx.parents()]
1935 1920 else:
1936 1921 p = repo.dirstate.parents()
1937 1922
1938 1923 br = None
1939 1924 if branches is not None:
1940 1925 br = repo.branchlookup(p)
1941 1926 displayer = show_changeset(ui, repo, opts)
1942 1927 for n in p:
1943 1928 if n != nullid:
1944 1929 displayer.show(changenode=n, brinfo=br)
1945 1930
1946 1931 def paths(ui, repo, search=None):
1947 1932 """show definition of symbolic path names
1948 1933
1949 1934 Show definition of symbolic path name NAME. If no name is given, show
1950 1935 definition of available names.
1951 1936
1952 1937 Path names are defined in the [paths] section of /etc/mercurial/hgrc
1953 1938 and $HOME/.hgrc. If run inside a repository, .hg/hgrc is used, too.
1954 1939 """
1955 1940 if search:
1956 1941 for name, path in ui.configitems("paths"):
1957 1942 if name == search:
1958 1943 ui.write("%s\n" % path)
1959 1944 return
1960 1945 ui.warn(_("not found!\n"))
1961 1946 return 1
1962 1947 else:
1963 1948 for name, path in ui.configitems("paths"):
1964 1949 ui.write("%s = %s\n" % (name, path))
1965 1950
1966 1951 def postincoming(ui, repo, modheads, optupdate):
1967 1952 if modheads == 0:
1968 1953 return
1969 1954 if optupdate:
1970 1955 if modheads == 1:
1971 1956 return hg.update(repo, repo.changelog.tip()) # update
1972 1957 else:
1973 1958 ui.status(_("not updating, since new heads added\n"))
1974 1959 if modheads > 1:
1975 1960 ui.status(_("(run 'hg heads' to see heads, 'hg merge' to merge)\n"))
1976 1961 else:
1977 1962 ui.status(_("(run 'hg update' to get a working copy)\n"))
1978 1963
1979 1964 def pull(ui, repo, source="default", **opts):
1980 1965 """pull changes from the specified source
1981 1966
1982 1967 Pull changes from a remote repository to a local one.
1983 1968
1984 1969 This finds all changes from the repository at the specified path
1985 1970 or URL and adds them to the local repository. By default, this
1986 1971 does not update the copy of the project in the working directory.
1987 1972
1988 1973 Valid URLs are of the form:
1989 1974
1990 1975 local/filesystem/path
1991 1976 http://[user@]host[:port]/[path]
1992 1977 https://[user@]host[:port]/[path]
1993 1978 ssh://[user@]host[:port]/[path]
1994 1979
1995 1980 Some notes about using SSH with Mercurial:
1996 1981 - SSH requires an accessible shell account on the destination machine
1997 1982 and a copy of hg in the remote path or specified with as remotecmd.
1998 1983 - path is relative to the remote user's home directory by default.
1999 1984 Use an extra slash at the start of a path to specify an absolute path:
2000 1985 ssh://example.com//tmp/repository
2001 1986 - Mercurial doesn't use its own compression via SSH; the right thing
2002 1987 to do is to configure it in your ~/.ssh/ssh_config, e.g.:
2003 1988 Host *.mylocalnetwork.example.com
2004 1989 Compression off
2005 1990 Host *
2006 1991 Compression on
2007 1992 Alternatively specify "ssh -C" as your ssh command in your hgrc or
2008 1993 with the --ssh command line option.
2009 1994 """
2010 1995 source = ui.expandpath(source)
2011 1996 setremoteconfig(ui, opts)
2012 1997
2013 1998 other = hg.repository(ui, source)
2014 1999 ui.status(_('pulling from %s\n') % (source))
2015 2000 revs = None
2016 2001 if opts['rev'] and not other.local():
2017 2002 raise util.Abort(_("pull -r doesn't work for remote repositories yet"))
2018 2003 elif opts['rev']:
2019 2004 revs = [other.lookup(rev) for rev in opts['rev']]
2020 2005 modheads = repo.pull(other, heads=revs, force=opts['force'])
2021 2006 return postincoming(ui, repo, modheads, opts['update'])
2022 2007
2023 2008 def push(ui, repo, dest=None, **opts):
2024 2009 """push changes to the specified destination
2025 2010
2026 2011 Push changes from the local repository to the given destination.
2027 2012
2028 2013 This is the symmetrical operation for pull. It helps to move
2029 2014 changes from the current repository to a different one. If the
2030 2015 destination is local this is identical to a pull in that directory
2031 2016 from the current one.
2032 2017
2033 2018 By default, push will refuse to run if it detects the result would
2034 2019 increase the number of remote heads. This generally indicates the
2035 2020 the client has forgotten to sync and merge before pushing.
2036 2021
2037 2022 Valid URLs are of the form:
2038 2023
2039 2024 local/filesystem/path
2040 2025 ssh://[user@]host[:port]/[path]
2041 2026
2042 2027 Look at the help text for the pull command for important details
2043 2028 about ssh:// URLs.
2044 2029
2045 2030 Pushing to http:// and https:// URLs is possible, too, if this
2046 2031 feature is enabled on the remote Mercurial server.
2047 2032 """
2048 2033 dest = ui.expandpath(dest or 'default-push', dest or 'default')
2049 2034 setremoteconfig(ui, opts)
2050 2035
2051 2036 other = hg.repository(ui, dest)
2052 2037 ui.status('pushing to %s\n' % (dest))
2053 2038 revs = None
2054 2039 if opts['rev']:
2055 2040 revs = [repo.lookup(rev) for rev in opts['rev']]
2056 2041 r = repo.push(other, opts['force'], revs=revs)
2057 2042 return r == 0
2058 2043
2059 2044 def rawcommit(ui, repo, *flist, **rc):
2060 2045 """raw commit interface (DEPRECATED)
2061 2046
2062 2047 (DEPRECATED)
2063 2048 Lowlevel commit, for use in helper scripts.
2064 2049
2065 2050 This command is not intended to be used by normal users, as it is
2066 2051 primarily useful for importing from other SCMs.
2067 2052
2068 2053 This command is now deprecated and will be removed in a future
2069 2054 release, please use debugsetparents and commit instead.
2070 2055 """
2071 2056
2072 2057 ui.warn(_("(the rawcommit command is deprecated)\n"))
2073 2058
2074 2059 message = rc['message']
2075 2060 if not message and rc['logfile']:
2076 2061 try:
2077 2062 message = open(rc['logfile']).read()
2078 2063 except IOError:
2079 2064 pass
2080 2065 if not message and not rc['logfile']:
2081 2066 raise util.Abort(_("missing commit message"))
2082 2067
2083 2068 files = relpath(repo, list(flist))
2084 2069 if rc['files']:
2085 2070 files += open(rc['files']).read().splitlines()
2086 2071
2087 2072 rc['parent'] = map(repo.lookup, rc['parent'])
2088 2073
2089 2074 try:
2090 2075 repo.rawcommit(files, message, rc['user'], rc['date'], *rc['parent'])
2091 2076 except ValueError, inst:
2092 2077 raise util.Abort(str(inst))
2093 2078
2094 2079 def recover(ui, repo):
2095 2080 """roll back an interrupted transaction
2096 2081
2097 2082 Recover from an interrupted commit or pull.
2098 2083
2099 2084 This command tries to fix the repository status after an interrupted
2100 2085 operation. It should only be necessary when Mercurial suggests it.
2101 2086 """
2102 2087 if repo.recover():
2103 2088 return hg.verify(repo)
2104 2089 return 1
2105 2090
2106 2091 def remove(ui, repo, *pats, **opts):
2107 2092 """remove the specified files on the next commit
2108 2093
2109 2094 Schedule the indicated files for removal from the repository.
2110 2095
2111 2096 This command schedules the files to be removed at the next commit.
2112 2097 This only removes files from the current branch, not from the
2113 2098 entire project history. If the files still exist in the working
2114 2099 directory, they will be deleted from it. If invoked with --after,
2115 2100 files that have been manually deleted are marked as removed.
2116 2101
2117 2102 Modified files and added files are not removed by default. To
2118 2103 remove them, use the -f/--force option.
2119 2104 """
2120 2105 names = []
2121 2106 if not opts['after'] and not pats:
2122 2107 raise util.Abort(_('no files specified'))
2123 2108 files, matchfn, anypats = cmdutil.matchpats(repo, pats, opts)
2124 2109 exact = dict.fromkeys(files)
2125 2110 mardu = map(dict.fromkeys, repo.status(files=files, match=matchfn))[:5]
2126 2111 modified, added, removed, deleted, unknown = mardu
2127 2112 remove, forget = [], []
2128 2113 for src, abs, rel, exact in cmdutil.walk(repo, pats, opts):
2129 2114 reason = None
2130 2115 if abs not in deleted and opts['after']:
2131 2116 reason = _('is still present')
2132 2117 elif abs in modified and not opts['force']:
2133 2118 reason = _('is modified (use -f to force removal)')
2134 2119 elif abs in added:
2135 2120 if opts['force']:
2136 2121 forget.append(abs)
2137 2122 continue
2138 2123 reason = _('has been marked for add (use -f to force removal)')
2139 2124 elif abs in unknown:
2140 2125 reason = _('is not managed')
2141 2126 elif abs in removed:
2142 2127 continue
2143 2128 if reason:
2144 2129 if exact:
2145 2130 ui.warn(_('not removing %s: file %s\n') % (rel, reason))
2146 2131 else:
2147 2132 if ui.verbose or not exact:
2148 2133 ui.status(_('removing %s\n') % rel)
2149 2134 remove.append(abs)
2150 2135 repo.forget(forget)
2151 2136 repo.remove(remove, unlink=not opts['after'])
2152 2137
2153 2138 def rename(ui, repo, *pats, **opts):
2154 2139 """rename files; equivalent of copy + remove
2155 2140
2156 2141 Mark dest as copies of sources; mark sources for deletion. If
2157 2142 dest is a directory, copies are put in that directory. If dest is
2158 2143 a file, there can only be one source.
2159 2144
2160 2145 By default, this command copies the contents of files as they
2161 2146 stand in the working directory. If invoked with --after, the
2162 2147 operation is recorded, but no copying is performed.
2163 2148
2164 2149 This command takes effect in the next commit.
2165 2150
2166 2151 NOTE: This command should be treated as experimental. While it
2167 2152 should properly record rename files, this information is not yet
2168 2153 fully used by merge, nor fully reported by log.
2169 2154 """
2170 2155 wlock = repo.wlock(0)
2171 2156 errs, copied = docopy(ui, repo, pats, opts, wlock)
2172 2157 names = []
2173 2158 for abs, rel, exact in copied:
2174 2159 if ui.verbose or not exact:
2175 2160 ui.status(_('removing %s\n') % rel)
2176 2161 names.append(abs)
2177 2162 if not opts.get('dry_run'):
2178 2163 repo.remove(names, True, wlock)
2179 2164 return errs
2180 2165
2181 2166 def revert(ui, repo, *pats, **opts):
2182 2167 """revert files or dirs to their states as of some revision
2183 2168
2184 2169 With no revision specified, revert the named files or directories
2185 2170 to the contents they had in the parent of the working directory.
2186 2171 This restores the contents of the affected files to an unmodified
2187 2172 state. If the working directory has two parents, you must
2188 2173 explicitly specify the revision to revert to.
2189 2174
2190 2175 Modified files are saved with a .orig suffix before reverting.
2191 2176 To disable these backups, use --no-backup.
2192 2177
2193 2178 Using the -r option, revert the given files or directories to their
2194 2179 contents as of a specific revision. This can be helpful to "roll
2195 2180 back" some or all of a change that should not have been committed.
2196 2181
2197 2182 Revert modifies the working directory. It does not commit any
2198 2183 changes, or change the parent of the working directory. If you
2199 2184 revert to a revision other than the parent of the working
2200 2185 directory, the reverted files will thus appear modified
2201 2186 afterwards.
2202 2187
2203 2188 If a file has been deleted, it is recreated. If the executable
2204 2189 mode of a file was changed, it is reset.
2205 2190
2206 2191 If names are given, all files matching the names are reverted.
2207 2192
2208 2193 If no arguments are given, no files are reverted.
2209 2194 """
2210 2195
2211 2196 if not pats and not opts['all']:
2212 2197 raise util.Abort(_('no files or directories specified; '
2213 2198 'use --all to revert the whole repo'))
2214 2199
2215 2200 parent, p2 = repo.dirstate.parents()
2216 2201 if not opts['rev'] and p2 != nullid:
2217 2202 raise util.Abort(_('uncommitted merge - please provide a '
2218 2203 'specific revision'))
2219 2204 node = repo.changectx(opts['rev']).node()
2220 2205 mf = repo.manifest.read(repo.changelog.read(node)[0])
2221 2206 if node == parent:
2222 2207 pmf = mf
2223 2208 else:
2224 2209 pmf = None
2225 2210
2226 2211 wlock = repo.wlock()
2227 2212
2228 2213 # need all matching names in dirstate and manifest of target rev,
2229 2214 # so have to walk both. do not print errors if files exist in one
2230 2215 # but not other.
2231 2216
2232 2217 names = {}
2233 2218 target_only = {}
2234 2219
2235 2220 # walk dirstate.
2236 2221
2237 2222 for src, abs, rel, exact in cmdutil.walk(repo, pats, opts,
2238 2223 badmatch=mf.has_key):
2239 2224 names[abs] = (rel, exact)
2240 2225 if src == 'b':
2241 2226 target_only[abs] = True
2242 2227
2243 2228 # walk target manifest.
2244 2229
2245 2230 for src, abs, rel, exact in cmdutil.walk(repo, pats, opts, node=node,
2246 2231 badmatch=names.has_key):
2247 2232 if abs in names: continue
2248 2233 names[abs] = (rel, exact)
2249 2234 target_only[abs] = True
2250 2235
2251 2236 changes = repo.status(match=names.has_key, wlock=wlock)[:5]
2252 2237 modified, added, removed, deleted, unknown = map(dict.fromkeys, changes)
2253 2238
2254 2239 revert = ([], _('reverting %s\n'))
2255 2240 add = ([], _('adding %s\n'))
2256 2241 remove = ([], _('removing %s\n'))
2257 2242 forget = ([], _('forgetting %s\n'))
2258 2243 undelete = ([], _('undeleting %s\n'))
2259 2244 update = {}
2260 2245
2261 2246 disptable = (
2262 2247 # dispatch table:
2263 2248 # file state
2264 2249 # action if in target manifest
2265 2250 # action if not in target manifest
2266 2251 # make backup if in target manifest
2267 2252 # make backup if not in target manifest
2268 2253 (modified, revert, remove, True, True),
2269 2254 (added, revert, forget, True, False),
2270 2255 (removed, undelete, None, False, False),
2271 2256 (deleted, revert, remove, False, False),
2272 2257 (unknown, add, None, True, False),
2273 2258 (target_only, add, None, False, False),
2274 2259 )
2275 2260
2276 2261 entries = names.items()
2277 2262 entries.sort()
2278 2263
2279 2264 for abs, (rel, exact) in entries:
2280 2265 mfentry = mf.get(abs)
2281 2266 def handle(xlist, dobackup):
2282 2267 xlist[0].append(abs)
2283 2268 update[abs] = 1
2284 2269 if dobackup and not opts['no_backup'] and os.path.exists(rel):
2285 2270 bakname = "%s.orig" % rel
2286 2271 ui.note(_('saving current version of %s as %s\n') %
2287 2272 (rel, bakname))
2288 2273 if not opts.get('dry_run'):
2289 2274 shutil.copyfile(rel, bakname)
2290 2275 shutil.copymode(rel, bakname)
2291 2276 if ui.verbose or not exact:
2292 2277 ui.status(xlist[1] % rel)
2293 2278 for table, hitlist, misslist, backuphit, backupmiss in disptable:
2294 2279 if abs not in table: continue
2295 2280 # file has changed in dirstate
2296 2281 if mfentry:
2297 2282 handle(hitlist, backuphit)
2298 2283 elif misslist is not None:
2299 2284 handle(misslist, backupmiss)
2300 2285 else:
2301 2286 if exact: ui.warn(_('file not managed: %s\n' % rel))
2302 2287 break
2303 2288 else:
2304 2289 # file has not changed in dirstate
2305 2290 if node == parent:
2306 2291 if exact: ui.warn(_('no changes needed to %s\n' % rel))
2307 2292 continue
2308 2293 if pmf is None:
2309 2294 # only need parent manifest in this unlikely case,
2310 2295 # so do not read by default
2311 2296 pmf = repo.manifest.read(repo.changelog.read(parent)[0])
2312 2297 if abs in pmf:
2313 2298 if mfentry:
2314 2299 # if version of file is same in parent and target
2315 2300 # manifests, do nothing
2316 2301 if pmf[abs] != mfentry:
2317 2302 handle(revert, False)
2318 2303 else:
2319 2304 handle(remove, False)
2320 2305
2321 2306 if not opts.get('dry_run'):
2322 2307 repo.dirstate.forget(forget[0])
2323 2308 r = hg.revert(repo, node, update.has_key, wlock)
2324 2309 repo.dirstate.update(add[0], 'a')
2325 2310 repo.dirstate.update(undelete[0], 'n')
2326 2311 repo.dirstate.update(remove[0], 'r')
2327 2312 return r
2328 2313
2329 2314 def rollback(ui, repo):
2330 2315 """roll back the last transaction in this repository
2331 2316
2332 2317 Roll back the last transaction in this repository, restoring the
2333 2318 project to its state prior to the transaction.
2334 2319
2335 2320 Transactions are used to encapsulate the effects of all commands
2336 2321 that create new changesets or propagate existing changesets into a
2337 2322 repository. For example, the following commands are transactional,
2338 2323 and their effects can be rolled back:
2339 2324
2340 2325 commit
2341 2326 import
2342 2327 pull
2343 2328 push (with this repository as destination)
2344 2329 unbundle
2345 2330
2346 2331 This command should be used with care. There is only one level of
2347 2332 rollback, and there is no way to undo a rollback.
2348 2333
2349 2334 This command is not intended for use on public repositories. Once
2350 2335 changes are visible for pull by other users, rolling a transaction
2351 2336 back locally is ineffective (someone else may already have pulled
2352 2337 the changes). Furthermore, a race is possible with readers of the
2353 2338 repository; for example an in-progress pull from the repository
2354 2339 may fail if a rollback is performed.
2355 2340 """
2356 2341 repo.rollback()
2357 2342
2358 2343 def root(ui, repo):
2359 2344 """print the root (top) of the current working dir
2360 2345
2361 2346 Print the root directory of the current repository.
2362 2347 """
2363 2348 ui.write(repo.root + "\n")
2364 2349
2365 2350 def serve(ui, repo, **opts):
2366 2351 """export the repository via HTTP
2367 2352
2368 2353 Start a local HTTP repository browser and pull server.
2369 2354
2370 2355 By default, the server logs accesses to stdout and errors to
2371 2356 stderr. Use the "-A" and "-E" options to log to files.
2372 2357 """
2373 2358
2374 2359 if opts["stdio"]:
2375 2360 if repo is None:
2376 2361 raise hg.RepoError(_("There is no Mercurial repository here"
2377 2362 " (.hg not found)"))
2378 2363 s = sshserver.sshserver(ui, repo)
2379 2364 s.serve_forever()
2380 2365
2381 2366 optlist = ("name templates style address port ipv6"
2382 2367 " accesslog errorlog webdir_conf")
2383 2368 for o in optlist.split():
2384 2369 if opts[o]:
2385 2370 ui.setconfig("web", o, opts[o])
2386 2371
2387 2372 if repo is None and not ui.config("web", "webdir_conf"):
2388 2373 raise hg.RepoError(_("There is no Mercurial repository here"
2389 2374 " (.hg not found)"))
2390 2375
2391 2376 if opts['daemon'] and not opts['daemon_pipefds']:
2392 2377 rfd, wfd = os.pipe()
2393 2378 args = sys.argv[:]
2394 2379 args.append('--daemon-pipefds=%d,%d' % (rfd, wfd))
2395 2380 pid = os.spawnvp(os.P_NOWAIT | getattr(os, 'P_DETACH', 0),
2396 2381 args[0], args)
2397 2382 os.close(wfd)
2398 2383 os.read(rfd, 1)
2399 2384 os._exit(0)
2400 2385
2401 2386 try:
2402 2387 httpd = hgweb.server.create_server(ui, repo)
2403 2388 except socket.error, inst:
2404 2389 raise util.Abort(_('cannot start server: %s') % inst.args[1])
2405 2390
2406 2391 if ui.verbose:
2407 2392 addr, port = httpd.socket.getsockname()
2408 2393 if addr == '0.0.0.0':
2409 2394 addr = socket.gethostname()
2410 2395 else:
2411 2396 try:
2412 2397 addr = socket.gethostbyaddr(addr)[0]
2413 2398 except socket.error:
2414 2399 pass
2415 2400 if port != 80:
2416 2401 ui.status(_('listening at http://%s:%d/\n') % (addr, port))
2417 2402 else:
2418 2403 ui.status(_('listening at http://%s/\n') % addr)
2419 2404
2420 2405 if opts['pid_file']:
2421 2406 fp = open(opts['pid_file'], 'w')
2422 2407 fp.write(str(os.getpid()) + '\n')
2423 2408 fp.close()
2424 2409
2425 2410 if opts['daemon_pipefds']:
2426 2411 rfd, wfd = [int(x) for x in opts['daemon_pipefds'].split(',')]
2427 2412 os.close(rfd)
2428 2413 os.write(wfd, 'y')
2429 2414 os.close(wfd)
2430 2415 sys.stdout.flush()
2431 2416 sys.stderr.flush()
2432 2417 fd = os.open(util.nulldev, os.O_RDWR)
2433 2418 if fd != 0: os.dup2(fd, 0)
2434 2419 if fd != 1: os.dup2(fd, 1)
2435 2420 if fd != 2: os.dup2(fd, 2)
2436 2421 if fd not in (0, 1, 2): os.close(fd)
2437 2422
2438 2423 httpd.serve_forever()
2439 2424
2440 2425 def status(ui, repo, *pats, **opts):
2441 2426 """show changed files in the working directory
2442 2427
2443 2428 Show status of files in the repository. If names are given, only
2444 2429 files that match are shown. Files that are clean or ignored, are
2445 2430 not listed unless -c (clean), -i (ignored) or -A is given.
2446 2431
2447 2432 The codes used to show the status of files are:
2448 2433 M = modified
2449 2434 A = added
2450 2435 R = removed
2451 2436 C = clean
2452 2437 ! = deleted, but still tracked
2453 2438 ? = not tracked
2454 2439 I = ignored (not shown by default)
2455 2440 = the previous added file was copied from here
2456 2441 """
2457 2442
2458 2443 all = opts['all']
2459 2444
2460 2445 files, matchfn, anypats = cmdutil.matchpats(repo, pats, opts)
2461 2446 cwd = (pats and repo.getcwd()) or ''
2462 2447 modified, added, removed, deleted, unknown, ignored, clean = [
2463 2448 [util.pathto(cwd, x) for x in n]
2464 2449 for n in repo.status(files=files, match=matchfn,
2465 2450 list_ignored=all or opts['ignored'],
2466 2451 list_clean=all or opts['clean'])]
2467 2452
2468 2453 changetypes = (('modified', 'M', modified),
2469 2454 ('added', 'A', added),
2470 2455 ('removed', 'R', removed),
2471 2456 ('deleted', '!', deleted),
2472 2457 ('unknown', '?', unknown),
2473 2458 ('ignored', 'I', ignored))
2474 2459
2475 2460 explicit_changetypes = changetypes + (('clean', 'C', clean),)
2476 2461
2477 2462 end = opts['print0'] and '\0' or '\n'
2478 2463
2479 2464 for opt, char, changes in ([ct for ct in explicit_changetypes
2480 2465 if all or opts[ct[0]]]
2481 2466 or changetypes):
2482 2467 if opts['no_status']:
2483 2468 format = "%%s%s" % end
2484 2469 else:
2485 2470 format = "%s %%s%s" % (char, end)
2486 2471
2487 2472 for f in changes:
2488 2473 ui.write(format % f)
2489 2474 if ((all or opts.get('copies')) and not opts.get('no_status')
2490 2475 and opt == 'added' and repo.dirstate.copies.has_key(f)):
2491 2476 ui.write(' %s%s' % (repo.dirstate.copies[f], end))
2492 2477
2493 2478 def tag(ui, repo, name, rev_=None, **opts):
2494 2479 """add a tag for the current tip or a given revision
2495 2480
2496 2481 Name a particular revision using <name>.
2497 2482
2498 2483 Tags are used to name particular revisions of the repository and are
2499 2484 very useful to compare different revision, to go back to significant
2500 2485 earlier versions or to mark branch points as releases, etc.
2501 2486
2502 2487 If no revision is given, the parent of the working directory is used.
2503 2488
2504 2489 To facilitate version control, distribution, and merging of tags,
2505 2490 they are stored as a file named ".hgtags" which is managed
2506 2491 similarly to other project files and can be hand-edited if
2507 2492 necessary. The file '.hg/localtags' is used for local tags (not
2508 2493 shared among repositories).
2509 2494 """
2510 2495 if name in ['tip', '.']:
2511 2496 raise util.Abort(_("the name '%s' is reserved") % name)
2512 2497 if rev_ is not None:
2513 2498 ui.warn(_("use of 'hg tag NAME [REV]' is deprecated, "
2514 2499 "please use 'hg tag [-r REV] NAME' instead\n"))
2515 2500 if opts['rev']:
2516 2501 raise util.Abort(_("use only one form to specify the revision"))
2517 2502 if opts['rev']:
2518 2503 rev_ = opts['rev']
2519 2504 if not rev_ and repo.dirstate.parents()[1] != nullid:
2520 2505 raise util.Abort(_('uncommitted merge - please provide a '
2521 2506 'specific revision'))
2522 2507 r = repo.changectx(rev_).node()
2523 2508
2524 2509 message = opts['message']
2525 2510 if not message:
2526 2511 message = _('Added tag %s for changeset %s') % (name, short(r))
2527 2512
2528 2513 repo.tag(name, r, message, opts['local'], opts['user'], opts['date'])
2529 2514
2530 2515 def tags(ui, repo):
2531 2516 """list repository tags
2532 2517
2533 2518 List the repository tags.
2534 2519
2535 2520 This lists both regular and local tags.
2536 2521 """
2537 2522
2538 2523 l = repo.tagslist()
2539 2524 l.reverse()
2540 2525 hexfunc = ui.debugflag and hex or short
2541 2526 for t, n in l:
2542 2527 try:
2543 2528 r = "%5d:%s" % (repo.changelog.rev(n), hexfunc(n))
2544 2529 except KeyError:
2545 2530 r = " ?:?"
2546 2531 if ui.quiet:
2547 2532 ui.write("%s\n" % t)
2548 2533 else:
2549 2534 ui.write("%-30s %s\n" % (t, r))
2550 2535
2551 2536 def tip(ui, repo, **opts):
2552 2537 """show the tip revision
2553 2538
2554 2539 Show the tip revision.
2555 2540 """
2556 2541 n = repo.changelog.tip()
2557 2542 br = None
2558 2543 if opts['branches']:
2559 2544 br = repo.branchlookup([n])
2560 2545 show_changeset(ui, repo, opts).show(changenode=n, brinfo=br)
2561 2546 if opts['patch']:
2562 2547 patch.diff(repo, repo.changelog.parents(n)[0], n)
2563 2548
2564 2549 def unbundle(ui, repo, fname, **opts):
2565 2550 """apply a changegroup file
2566 2551
2567 2552 Apply a compressed changegroup file generated by the bundle
2568 2553 command.
2569 2554 """
2570 2555 f = urllib.urlopen(fname)
2571 2556
2572 2557 header = f.read(6)
2573 2558 if not header.startswith("HG"):
2574 2559 raise util.Abort(_("%s: not a Mercurial bundle file") % fname)
2575 2560 elif not header.startswith("HG10"):
2576 2561 raise util.Abort(_("%s: unknown bundle version") % fname)
2577 2562 elif header == "HG10BZ":
2578 2563 def generator(f):
2579 2564 zd = bz2.BZ2Decompressor()
2580 2565 zd.decompress("BZ")
2581 2566 for chunk in f:
2582 2567 yield zd.decompress(chunk)
2583 2568 elif header == "HG10UN":
2584 2569 def generator(f):
2585 2570 for chunk in f:
2586 2571 yield chunk
2587 2572 else:
2588 2573 raise util.Abort(_("%s: unknown bundle compression type")
2589 2574 % fname)
2590 2575 gen = generator(util.filechunkiter(f, 4096))
2591 2576 modheads = repo.addchangegroup(util.chunkbuffer(gen), 'unbundle',
2592 2577 'bundle:' + fname)
2593 2578 return postincoming(ui, repo, modheads, opts['update'])
2594 2579
2595 2580 def undo(ui, repo):
2596 2581 """undo the last commit or pull (DEPRECATED)
2597 2582
2598 2583 (DEPRECATED)
2599 2584 This command is now deprecated and will be removed in a future
2600 2585 release. Please use the rollback command instead. For usage
2601 2586 instructions, see the rollback command.
2602 2587 """
2603 2588 ui.warn(_('(the undo command is deprecated; use rollback instead)\n'))
2604 2589 repo.rollback()
2605 2590
2606 2591 def update(ui, repo, node=None, merge=False, clean=False, force=None,
2607 2592 branch=None):
2608 2593 """update or merge working directory
2609 2594
2610 2595 Update the working directory to the specified revision.
2611 2596
2612 2597 If there are no outstanding changes in the working directory and
2613 2598 there is a linear relationship between the current version and the
2614 2599 requested version, the result is the requested version.
2615 2600
2616 2601 To merge the working directory with another revision, use the
2617 2602 merge command.
2618 2603
2619 2604 By default, update will refuse to run if doing so would require
2620 2605 merging or discarding local changes.
2621 2606 """
2622 2607 node = _lookup(repo, node, branch)
2623 2608 if merge:
2624 2609 ui.warn(_('(the -m/--merge option is deprecated; '
2625 2610 'use the merge command instead)\n'))
2626 2611 return hg.merge(repo, node, force=force)
2627 2612 elif clean:
2628 2613 return hg.clean(repo, node)
2629 2614 else:
2630 2615 return hg.update(repo, node)
2631 2616
2632 2617 def _lookup(repo, node, branch=None):
2633 2618 if branch:
2634 2619 br = repo.branchlookup(branch=branch)
2635 2620 found = []
2636 2621 for x in br:
2637 2622 if branch in br[x]:
2638 2623 found.append(x)
2639 2624 if len(found) > 1:
2640 2625 repo.ui.warn(_("Found multiple heads for %s\n") % branch)
2641 2626 for x in found:
2642 2627 show_changeset(ui, repo, {}).show(changenode=x, brinfo=br)
2643 2628 raise util.Abort("")
2644 2629 if len(found) == 1:
2645 2630 node = found[0]
2646 2631 repo.ui.warn(_("Using head %s for branch %s\n")
2647 2632 % (short(node), branch))
2648 2633 else:
2649 2634 raise util.Abort(_("branch %s not found") % branch)
2650 2635 else:
2651 2636 node = node and repo.lookup(node) or repo.changelog.tip()
2652 2637 return node
2653 2638
2654 2639 def verify(ui, repo):
2655 2640 """verify the integrity of the repository
2656 2641
2657 2642 Verify the integrity of the current repository.
2658 2643
2659 2644 This will perform an extensive check of the repository's
2660 2645 integrity, validating the hashes and checksums of each entry in
2661 2646 the changelog, manifest, and tracked files, as well as the
2662 2647 integrity of their crosslinks and indices.
2663 2648 """
2664 2649 return hg.verify(repo)
2665 2650
2666 2651 # Command options and aliases are listed here, alphabetically
2667 2652
2668 2653 table = {
2669 2654 "^add":
2670 2655 (add,
2671 2656 [('I', 'include', [], _('include names matching the given patterns')),
2672 2657 ('X', 'exclude', [], _('exclude names matching the given patterns')),
2673 2658 ('n', 'dry-run', None, _('do not perform actions, just print output'))],
2674 2659 _('hg add [OPTION]... [FILE]...')),
2675 2660 "addremove":
2676 2661 (addremove,
2677 2662 [('I', 'include', [], _('include names matching the given patterns')),
2678 2663 ('X', 'exclude', [], _('exclude names matching the given patterns')),
2679 2664 ('n', 'dry-run', None,
2680 2665 _('do not perform actions, just print output')),
2681 2666 ('s', 'similarity', '',
2682 2667 _('guess renamed files by similarity (0<=s<=1)'))],
2683 2668 _('hg addremove [OPTION]... [FILE]...')),
2684 2669 "^annotate":
2685 2670 (annotate,
2686 2671 [('r', 'rev', '', _('annotate the specified revision')),
2687 2672 ('a', 'text', None, _('treat all files as text')),
2688 2673 ('u', 'user', None, _('list the author')),
2689 2674 ('d', 'date', None, _('list the date')),
2690 2675 ('n', 'number', None, _('list the revision number (default)')),
2691 2676 ('c', 'changeset', None, _('list the changeset')),
2692 2677 ('I', 'include', [], _('include names matching the given patterns')),
2693 2678 ('X', 'exclude', [], _('exclude names matching the given patterns'))],
2694 2679 _('hg annotate [-r REV] [-a] [-u] [-d] [-n] [-c] FILE...')),
2695 2680 "archive":
2696 2681 (archive,
2697 2682 [('', 'no-decode', None, _('do not pass files through decoders')),
2698 2683 ('p', 'prefix', '', _('directory prefix for files in archive')),
2699 2684 ('r', 'rev', '', _('revision to distribute')),
2700 2685 ('t', 'type', '', _('type of distribution to create')),
2701 2686 ('I', 'include', [], _('include names matching the given patterns')),
2702 2687 ('X', 'exclude', [], _('exclude names matching the given patterns'))],
2703 2688 _('hg archive [OPTION]... DEST')),
2704 2689 "backout":
2705 2690 (backout,
2706 2691 [('', 'merge', None,
2707 2692 _('merge with old dirstate parent after backout')),
2708 2693 ('m', 'message', '', _('use <text> as commit message')),
2709 2694 ('l', 'logfile', '', _('read commit message from <file>')),
2710 2695 ('d', 'date', '', _('record datecode as commit date')),
2711 2696 ('', 'parent', '', _('parent to choose when backing out merge')),
2712 2697 ('u', 'user', '', _('record user as committer')),
2713 2698 ('I', 'include', [], _('include names matching the given patterns')),
2714 2699 ('X', 'exclude', [], _('exclude names matching the given patterns'))],
2715 2700 _('hg backout [OPTION]... REV')),
2716 2701 "bundle":
2717 2702 (bundle,
2718 2703 [('f', 'force', None,
2719 2704 _('run even when remote repository is unrelated'))],
2720 2705 _('hg bundle FILE DEST')),
2721 2706 "cat":
2722 2707 (cat,
2723 2708 [('o', 'output', '', _('print output to file with formatted name')),
2724 2709 ('r', 'rev', '', _('print the given revision')),
2725 2710 ('I', 'include', [], _('include names matching the given patterns')),
2726 2711 ('X', 'exclude', [], _('exclude names matching the given patterns'))],
2727 2712 _('hg cat [OPTION]... FILE...')),
2728 2713 "^clone":
2729 2714 (clone,
2730 2715 [('U', 'noupdate', None, _('do not update the new working directory')),
2731 2716 ('r', 'rev', [],
2732 2717 _('a changeset you would like to have after cloning')),
2733 2718 ('', 'pull', None, _('use pull protocol to copy metadata')),
2734 2719 ('', 'uncompressed', None,
2735 2720 _('use uncompressed transfer (fast over LAN)')),
2736 2721 ('e', 'ssh', '', _('specify ssh command to use')),
2737 2722 ('', 'remotecmd', '',
2738 2723 _('specify hg command to run on the remote side'))],
2739 2724 _('hg clone [OPTION]... SOURCE [DEST]')),
2740 2725 "^commit|ci":
2741 2726 (commit,
2742 2727 [('A', 'addremove', None,
2743 2728 _('mark new/missing files as added/removed before committing')),
2744 2729 ('m', 'message', '', _('use <text> as commit message')),
2745 2730 ('l', 'logfile', '', _('read the commit message from <file>')),
2746 2731 ('d', 'date', '', _('record datecode as commit date')),
2747 2732 ('u', 'user', '', _('record user as commiter')),
2748 2733 ('I', 'include', [], _('include names matching the given patterns')),
2749 2734 ('X', 'exclude', [], _('exclude names matching the given patterns'))],
2750 2735 _('hg commit [OPTION]... [FILE]...')),
2751 2736 "copy|cp":
2752 2737 (copy,
2753 2738 [('A', 'after', None, _('record a copy that has already occurred')),
2754 2739 ('f', 'force', None,
2755 2740 _('forcibly copy over an existing managed file')),
2756 2741 ('I', 'include', [], _('include names matching the given patterns')),
2757 2742 ('X', 'exclude', [], _('exclude names matching the given patterns')),
2758 2743 ('n', 'dry-run', None, _('do not perform actions, just print output'))],
2759 2744 _('hg copy [OPTION]... [SOURCE]... DEST')),
2760 2745 "debugancestor": (debugancestor, [], _('debugancestor INDEX REV1 REV2')),
2761 2746 "debugcomplete":
2762 2747 (debugcomplete,
2763 2748 [('o', 'options', None, _('show the command options'))],
2764 2749 _('debugcomplete [-o] CMD')),
2765 2750 "debugrebuildstate":
2766 2751 (debugrebuildstate,
2767 2752 [('r', 'rev', '', _('revision to rebuild to'))],
2768 2753 _('debugrebuildstate [-r REV] [REV]')),
2769 2754 "debugcheckstate": (debugcheckstate, [], _('debugcheckstate')),
2770 2755 "debugconfig": (debugconfig, [], _('debugconfig [NAME]...')),
2771 2756 "debugsetparents": (debugsetparents, [], _('debugsetparents REV1 [REV2]')),
2772 2757 "debugstate": (debugstate, [], _('debugstate')),
2773 2758 "debugdata": (debugdata, [], _('debugdata FILE REV')),
2774 2759 "debugindex": (debugindex, [], _('debugindex FILE')),
2775 2760 "debugindexdot": (debugindexdot, [], _('debugindexdot FILE')),
2776 2761 "debugrename": (debugrename, [], _('debugrename FILE [REV]')),
2777 2762 "debugwalk":
2778 2763 (debugwalk,
2779 2764 [('I', 'include', [], _('include names matching the given patterns')),
2780 2765 ('X', 'exclude', [], _('exclude names matching the given patterns'))],
2781 2766 _('debugwalk [OPTION]... [FILE]...')),
2782 2767 "^diff":
2783 2768 (diff,
2784 2769 [('r', 'rev', [], _('revision')),
2785 2770 ('a', 'text', None, _('treat all files as text')),
2786 2771 ('p', 'show-function', None,
2787 2772 _('show which function each change is in')),
2788 2773 ('g', 'git', None, _('use git extended diff format')),
2789 2774 ('w', 'ignore-all-space', None,
2790 2775 _('ignore white space when comparing lines')),
2791 2776 ('b', 'ignore-space-change', None,
2792 2777 _('ignore changes in the amount of white space')),
2793 2778 ('B', 'ignore-blank-lines', None,
2794 2779 _('ignore changes whose lines are all blank')),
2795 2780 ('I', 'include', [], _('include names matching the given patterns')),
2796 2781 ('X', 'exclude', [], _('exclude names matching the given patterns'))],
2797 2782 _('hg diff [-a] [-I] [-X] [-r REV1 [-r REV2]] [FILE]...')),
2798 2783 "^export":
2799 2784 (export,
2800 2785 [('o', 'output', '', _('print output to file with formatted name')),
2801 2786 ('a', 'text', None, _('treat all files as text')),
2802 2787 ('g', 'git', None, _('use git extended diff format')),
2803 2788 ('', 'switch-parent', None, _('diff against the second parent'))],
2804 2789 _('hg export [-a] [-o OUTFILESPEC] REV...')),
2805 2790 "debugforget|forget":
2806 2791 (forget,
2807 2792 [('I', 'include', [], _('include names matching the given patterns')),
2808 2793 ('X', 'exclude', [], _('exclude names matching the given patterns'))],
2809 2794 _('hg forget [OPTION]... FILE...')),
2810 2795 "grep":
2811 2796 (grep,
2812 2797 [('0', 'print0', None, _('end fields with NUL')),
2813 2798 ('', 'all', None, _('print all revisions that match')),
2814 2799 ('f', 'follow', None,
2815 2800 _('follow changeset history, or file history across copies and renames')),
2816 2801 ('i', 'ignore-case', None, _('ignore case when matching')),
2817 2802 ('l', 'files-with-matches', None,
2818 2803 _('print only filenames and revs that match')),
2819 2804 ('n', 'line-number', None, _('print matching line numbers')),
2820 2805 ('r', 'rev', [], _('search in given revision range')),
2821 2806 ('u', 'user', None, _('print user who committed change')),
2822 2807 ('I', 'include', [], _('include names matching the given patterns')),
2823 2808 ('X', 'exclude', [], _('exclude names matching the given patterns'))],
2824 2809 _('hg grep [OPTION]... PATTERN [FILE]...')),
2825 2810 "heads":
2826 2811 (heads,
2827 2812 [('b', 'branches', None, _('show branches')),
2828 2813 ('', 'style', '', _('display using template map file')),
2829 2814 ('r', 'rev', '', _('show only heads which are descendants of rev')),
2830 2815 ('', 'template', '', _('display with template'))],
2831 2816 _('hg heads [-b] [-r <rev>]')),
2832 2817 "help": (help_, [], _('hg help [COMMAND]')),
2833 2818 "identify|id": (identify, [], _('hg identify')),
2834 2819 "import|patch":
2835 2820 (import_,
2836 2821 [('p', 'strip', 1,
2837 2822 _('directory strip option for patch. This has the same\n'
2838 2823 'meaning as the corresponding patch option')),
2839 2824 ('m', 'message', '', _('use <text> as commit message')),
2840 2825 ('b', 'base', '', _('base path')),
2841 2826 ('f', 'force', None,
2842 2827 _('skip check for outstanding uncommitted changes'))],
2843 2828 _('hg import [-p NUM] [-b BASE] [-m MESSAGE] [-f] PATCH...')),
2844 2829 "incoming|in": (incoming,
2845 2830 [('M', 'no-merges', None, _('do not show merges')),
2846 2831 ('f', 'force', None,
2847 2832 _('run even when remote repository is unrelated')),
2848 2833 ('', 'style', '', _('display using template map file')),
2849 2834 ('n', 'newest-first', None, _('show newest record first')),
2850 2835 ('', 'bundle', '', _('file to store the bundles into')),
2851 2836 ('p', 'patch', None, _('show patch')),
2852 2837 ('r', 'rev', [], _('a specific revision up to which you would like to pull')),
2853 2838 ('', 'template', '', _('display with template')),
2854 2839 ('e', 'ssh', '', _('specify ssh command to use')),
2855 2840 ('', 'remotecmd', '',
2856 2841 _('specify hg command to run on the remote side'))],
2857 2842 _('hg incoming [-p] [-n] [-M] [-r REV]...'
2858 2843 ' [--bundle FILENAME] [SOURCE]')),
2859 2844 "^init":
2860 2845 (init,
2861 2846 [('e', 'ssh', '', _('specify ssh command to use')),
2862 2847 ('', 'remotecmd', '',
2863 2848 _('specify hg command to run on the remote side'))],
2864 2849 _('hg init [-e FILE] [--remotecmd FILE] [DEST]')),
2865 2850 "locate":
2866 2851 (locate,
2867 2852 [('r', 'rev', '', _('search the repository as it stood at rev')),
2868 2853 ('0', 'print0', None,
2869 2854 _('end filenames with NUL, for use with xargs')),
2870 2855 ('f', 'fullpath', None,
2871 2856 _('print complete paths from the filesystem root')),
2872 2857 ('I', 'include', [], _('include names matching the given patterns')),
2873 2858 ('X', 'exclude', [], _('exclude names matching the given patterns'))],
2874 2859 _('hg locate [OPTION]... [PATTERN]...')),
2875 2860 "^log|history":
2876 2861 (log,
2877 2862 [('b', 'branches', None, _('show branches')),
2878 2863 ('f', 'follow', None,
2879 2864 _('follow changeset history, or file history across copies and renames')),
2880 2865 ('', 'follow-first', None,
2881 2866 _('only follow the first parent of merge changesets')),
2882 2867 ('k', 'keyword', [], _('search for a keyword')),
2883 2868 ('l', 'limit', '', _('limit number of changes displayed')),
2884 2869 ('r', 'rev', [], _('show the specified revision or range')),
2885 2870 ('M', 'no-merges', None, _('do not show merges')),
2886 2871 ('', 'style', '', _('display using template map file')),
2887 2872 ('m', 'only-merges', None, _('show only merges')),
2888 2873 ('p', 'patch', None, _('show patch')),
2889 2874 ('P', 'prune', [], _('do not display revision or any of its ancestors')),
2890 2875 ('', 'template', '', _('display with template')),
2891 2876 ('I', 'include', [], _('include names matching the given patterns')),
2892 2877 ('X', 'exclude', [], _('exclude names matching the given patterns'))],
2893 2878 _('hg log [OPTION]... [FILE]')),
2894 2879 "manifest": (manifest, [], _('hg manifest [REV]')),
2895 2880 "merge":
2896 2881 (merge,
2897 2882 [('b', 'branch', '', _('merge with head of a specific branch')),
2898 2883 ('f', 'force', None, _('force a merge with outstanding changes'))],
2899 2884 _('hg merge [-b TAG] [-f] [REV]')),
2900 2885 "outgoing|out": (outgoing,
2901 2886 [('M', 'no-merges', None, _('do not show merges')),
2902 2887 ('f', 'force', None,
2903 2888 _('run even when remote repository is unrelated')),
2904 2889 ('p', 'patch', None, _('show patch')),
2905 2890 ('', 'style', '', _('display using template map file')),
2906 2891 ('r', 'rev', [], _('a specific revision you would like to push')),
2907 2892 ('n', 'newest-first', None, _('show newest record first')),
2908 2893 ('', 'template', '', _('display with template')),
2909 2894 ('e', 'ssh', '', _('specify ssh command to use')),
2910 2895 ('', 'remotecmd', '',
2911 2896 _('specify hg command to run on the remote side'))],
2912 2897 _('hg outgoing [-M] [-p] [-n] [-r REV]... [DEST]')),
2913 2898 "^parents":
2914 2899 (parents,
2915 2900 [('b', 'branches', None, _('show branches')),
2916 2901 ('r', 'rev', '', _('show parents from the specified rev')),
2917 2902 ('', 'style', '', _('display using template map file')),
2918 2903 ('', 'template', '', _('display with template'))],
2919 2904 _('hg parents [-b] [-r REV] [FILE]')),
2920 2905 "paths": (paths, [], _('hg paths [NAME]')),
2921 2906 "^pull":
2922 2907 (pull,
2923 2908 [('u', 'update', None,
2924 2909 _('update the working directory to tip after pull')),
2925 2910 ('e', 'ssh', '', _('specify ssh command to use')),
2926 2911 ('f', 'force', None,
2927 2912 _('run even when remote repository is unrelated')),
2928 2913 ('r', 'rev', [], _('a specific revision up to which you would like to pull')),
2929 2914 ('', 'remotecmd', '',
2930 2915 _('specify hg command to run on the remote side'))],
2931 2916 _('hg pull [-u] [-r REV]... [-e FILE] [--remotecmd FILE] [SOURCE]')),
2932 2917 "^push":
2933 2918 (push,
2934 2919 [('f', 'force', None, _('force push')),
2935 2920 ('e', 'ssh', '', _('specify ssh command to use')),
2936 2921 ('r', 'rev', [], _('a specific revision you would like to push')),
2937 2922 ('', 'remotecmd', '',
2938 2923 _('specify hg command to run on the remote side'))],
2939 2924 _('hg push [-f] [-r REV]... [-e FILE] [--remotecmd FILE] [DEST]')),
2940 2925 "debugrawcommit|rawcommit":
2941 2926 (rawcommit,
2942 2927 [('p', 'parent', [], _('parent')),
2943 2928 ('d', 'date', '', _('date code')),
2944 2929 ('u', 'user', '', _('user')),
2945 2930 ('F', 'files', '', _('file list')),
2946 2931 ('m', 'message', '', _('commit message')),
2947 2932 ('l', 'logfile', '', _('commit message file'))],
2948 2933 _('hg debugrawcommit [OPTION]... [FILE]...')),
2949 2934 "recover": (recover, [], _('hg recover')),
2950 2935 "^remove|rm":
2951 2936 (remove,
2952 2937 [('A', 'after', None, _('record remove that has already occurred')),
2953 2938 ('f', 'force', None, _('remove file even if modified')),
2954 2939 ('I', 'include', [], _('include names matching the given patterns')),
2955 2940 ('X', 'exclude', [], _('exclude names matching the given patterns'))],
2956 2941 _('hg remove [OPTION]... FILE...')),
2957 2942 "rename|mv":
2958 2943 (rename,
2959 2944 [('A', 'after', None, _('record a rename that has already occurred')),
2960 2945 ('f', 'force', None,
2961 2946 _('forcibly copy over an existing managed file')),
2962 2947 ('I', 'include', [], _('include names matching the given patterns')),
2963 2948 ('X', 'exclude', [], _('exclude names matching the given patterns')),
2964 2949 ('n', 'dry-run', None, _('do not perform actions, just print output'))],
2965 2950 _('hg rename [OPTION]... SOURCE... DEST')),
2966 2951 "^revert":
2967 2952 (revert,
2968 2953 [('a', 'all', None, _('revert all changes when no arguments given')),
2969 2954 ('r', 'rev', '', _('revision to revert to')),
2970 2955 ('', 'no-backup', None, _('do not save backup copies of files')),
2971 2956 ('I', 'include', [], _('include names matching given patterns')),
2972 2957 ('X', 'exclude', [], _('exclude names matching given patterns')),
2973 2958 ('n', 'dry-run', None, _('do not perform actions, just print output'))],
2974 2959 _('hg revert [-r REV] [NAME]...')),
2975 2960 "rollback": (rollback, [], _('hg rollback')),
2976 2961 "root": (root, [], _('hg root')),
2977 2962 "^serve":
2978 2963 (serve,
2979 2964 [('A', 'accesslog', '', _('name of access log file to write to')),
2980 2965 ('d', 'daemon', None, _('run server in background')),
2981 2966 ('', 'daemon-pipefds', '', _('used internally by daemon mode')),
2982 2967 ('E', 'errorlog', '', _('name of error log file to write to')),
2983 2968 ('p', 'port', 0, _('port to use (default: 8000)')),
2984 2969 ('a', 'address', '', _('address to use')),
2985 2970 ('n', 'name', '',
2986 2971 _('name to show in web pages (default: working dir)')),
2987 2972 ('', 'webdir-conf', '', _('name of the webdir config file'
2988 2973 ' (serve more than one repo)')),
2989 2974 ('', 'pid-file', '', _('name of file to write process ID to')),
2990 2975 ('', 'stdio', None, _('for remote clients')),
2991 2976 ('t', 'templates', '', _('web templates to use')),
2992 2977 ('', 'style', '', _('template style to use')),
2993 2978 ('6', 'ipv6', None, _('use IPv6 in addition to IPv4'))],
2994 2979 _('hg serve [OPTION]...')),
2995 2980 "^status|st":
2996 2981 (status,
2997 2982 [('A', 'all', None, _('show status of all files')),
2998 2983 ('m', 'modified', None, _('show only modified files')),
2999 2984 ('a', 'added', None, _('show only added files')),
3000 2985 ('r', 'removed', None, _('show only removed files')),
3001 2986 ('d', 'deleted', None, _('show only deleted (but tracked) files')),
3002 2987 ('c', 'clean', None, _('show only files without changes')),
3003 2988 ('u', 'unknown', None, _('show only unknown (not tracked) files')),
3004 2989 ('i', 'ignored', None, _('show ignored files')),
3005 2990 ('n', 'no-status', None, _('hide status prefix')),
3006 2991 ('C', 'copies', None, _('show source of copied files')),
3007 2992 ('0', 'print0', None,
3008 2993 _('end filenames with NUL, for use with xargs')),
3009 2994 ('I', 'include', [], _('include names matching the given patterns')),
3010 2995 ('X', 'exclude', [], _('exclude names matching the given patterns'))],
3011 2996 _('hg status [OPTION]... [FILE]...')),
3012 2997 "tag":
3013 2998 (tag,
3014 2999 [('l', 'local', None, _('make the tag local')),
3015 3000 ('m', 'message', '', _('message for tag commit log entry')),
3016 3001 ('d', 'date', '', _('record datecode as commit date')),
3017 3002 ('u', 'user', '', _('record user as commiter')),
3018 3003 ('r', 'rev', '', _('revision to tag'))],
3019 3004 _('hg tag [-l] [-m TEXT] [-d DATE] [-u USER] [-r REV] NAME')),
3020 3005 "tags": (tags, [], _('hg tags')),
3021 3006 "tip":
3022 3007 (tip,
3023 3008 [('b', 'branches', None, _('show branches')),
3024 3009 ('', 'style', '', _('display using template map file')),
3025 3010 ('p', 'patch', None, _('show patch')),
3026 3011 ('', 'template', '', _('display with template'))],
3027 3012 _('hg tip [-b] [-p]')),
3028 3013 "unbundle":
3029 3014 (unbundle,
3030 3015 [('u', 'update', None,
3031 3016 _('update the working directory to tip after unbundle'))],
3032 3017 _('hg unbundle [-u] FILE')),
3033 3018 "debugundo|undo": (undo, [], _('hg undo')),
3034 3019 "^update|up|checkout|co":
3035 3020 (update,
3036 3021 [('b', 'branch', '', _('checkout the head of a specific branch')),
3037 3022 ('m', 'merge', None, _('allow merging of branches (DEPRECATED)')),
3038 3023 ('C', 'clean', None, _('overwrite locally modified files')),
3039 3024 ('f', 'force', None, _('force a merge with outstanding changes'))],
3040 3025 _('hg update [-b TAG] [-m] [-C] [-f] [REV]')),
3041 3026 "verify": (verify, [], _('hg verify')),
3042 3027 "version": (show_version, [], _('hg version')),
3043 3028 }
3044 3029
3045 3030 globalopts = [
3046 3031 ('R', 'repository', '',
3047 3032 _('repository root directory or symbolic path name')),
3048 3033 ('', 'cwd', '', _('change working directory')),
3049 3034 ('y', 'noninteractive', None,
3050 3035 _('do not prompt, assume \'yes\' for any required answers')),
3051 3036 ('q', 'quiet', None, _('suppress output')),
3052 3037 ('v', 'verbose', None, _('enable additional output')),
3053 3038 ('', 'config', [], _('set/override config option')),
3054 3039 ('', 'debug', None, _('enable debugging output')),
3055 3040 ('', 'debugger', None, _('start debugger')),
3056 3041 ('', 'lsprof', None, _('print improved command execution profile')),
3057 3042 ('', 'traceback', None, _('print traceback on exception')),
3058 3043 ('', 'time', None, _('time how long the command takes')),
3059 3044 ('', 'profile', None, _('print command execution profile')),
3060 3045 ('', 'version', None, _('output version information and exit')),
3061 3046 ('h', 'help', None, _('display help and exit')),
3062 3047 ]
3063 3048
3064 3049 norepo = ("clone init version help debugancestor debugcomplete debugdata"
3065 3050 " debugindex debugindexdot")
3066 3051 optionalrepo = ("paths serve debugconfig")
3067 3052
3068 3053 def findpossible(ui, cmd):
3069 3054 """
3070 3055 Return cmd -> (aliases, command table entry)
3071 3056 for each matching command.
3072 3057 Return debug commands (or their aliases) only if no normal command matches.
3073 3058 """
3074 3059 choice = {}
3075 3060 debugchoice = {}
3076 3061 for e in table.keys():
3077 3062 aliases = e.lstrip("^").split("|")
3078 3063 found = None
3079 3064 if cmd in aliases:
3080 3065 found = cmd
3081 3066 elif not ui.config("ui", "strict"):
3082 3067 for a in aliases:
3083 3068 if a.startswith(cmd):
3084 3069 found = a
3085 3070 break
3086 3071 if found is not None:
3087 3072 if aliases[0].startswith("debug"):
3088 3073 debugchoice[found] = (aliases, table[e])
3089 3074 else:
3090 3075 choice[found] = (aliases, table[e])
3091 3076
3092 3077 if not choice and debugchoice:
3093 3078 choice = debugchoice
3094 3079
3095 3080 return choice
3096 3081
3097 3082 def findcmd(ui, cmd):
3098 3083 """Return (aliases, command table entry) for command string."""
3099 3084 choice = findpossible(ui, cmd)
3100 3085
3101 3086 if choice.has_key(cmd):
3102 3087 return choice[cmd]
3103 3088
3104 3089 if len(choice) > 1:
3105 3090 clist = choice.keys()
3106 3091 clist.sort()
3107 3092 raise AmbiguousCommand(cmd, clist)
3108 3093
3109 3094 if choice:
3110 3095 return choice.values()[0]
3111 3096
3112 3097 raise UnknownCommand(cmd)
3113 3098
3114 3099 def catchterm(*args):
3115 3100 raise util.SignalInterrupt
3116 3101
3117 3102 def run():
3118 3103 sys.exit(dispatch(sys.argv[1:]))
3119 3104
3120 3105 class ParseError(Exception):
3121 3106 """Exception raised on errors in parsing the command line."""
3122 3107
3123 3108 def parse(ui, args):
3124 3109 options = {}
3125 3110 cmdoptions = {}
3126 3111
3127 3112 try:
3128 3113 args = fancyopts.fancyopts(args, globalopts, options)
3129 3114 except fancyopts.getopt.GetoptError, inst:
3130 3115 raise ParseError(None, inst)
3131 3116
3132 3117 if args:
3133 3118 cmd, args = args[0], args[1:]
3134 3119 aliases, i = findcmd(ui, cmd)
3135 3120 cmd = aliases[0]
3136 3121 defaults = ui.config("defaults", cmd)
3137 3122 if defaults:
3138 3123 args = shlex.split(defaults) + args
3139 3124 c = list(i[1])
3140 3125 else:
3141 3126 cmd = None
3142 3127 c = []
3143 3128
3144 3129 # combine global options into local
3145 3130 for o in globalopts:
3146 3131 c.append((o[0], o[1], options[o[1]], o[3]))
3147 3132
3148 3133 try:
3149 3134 args = fancyopts.fancyopts(args, c, cmdoptions)
3150 3135 except fancyopts.getopt.GetoptError, inst:
3151 3136 raise ParseError(cmd, inst)
3152 3137
3153 3138 # separate global options back out
3154 3139 for o in globalopts:
3155 3140 n = o[1]
3156 3141 options[n] = cmdoptions[n]
3157 3142 del cmdoptions[n]
3158 3143
3159 3144 return (cmd, cmd and i[0] or None, args, options, cmdoptions)
3160 3145
3161 3146 external = {}
3162 3147
3163 3148 def findext(name):
3164 3149 '''return module with given extension name'''
3165 3150 try:
3166 3151 return sys.modules[external[name]]
3167 3152 except KeyError:
3168 3153 for k, v in external.iteritems():
3169 3154 if k.endswith('.' + name) or k.endswith('/' + name) or v == name:
3170 3155 return sys.modules[v]
3171 3156 raise KeyError(name)
3172 3157
3173 3158 def load_extensions(ui):
3174 3159 added = []
3175 3160 for ext_name, load_from_name in ui.extensions():
3176 3161 if ext_name in external:
3177 3162 continue
3178 3163 try:
3179 3164 if load_from_name:
3180 3165 # the module will be loaded in sys.modules
3181 3166 # choose an unique name so that it doesn't
3182 3167 # conflicts with other modules
3183 3168 module_name = "hgext_%s" % ext_name.replace('.', '_')
3184 3169 mod = imp.load_source(module_name, load_from_name)
3185 3170 else:
3186 3171 def importh(name):
3187 3172 mod = __import__(name)
3188 3173 components = name.split('.')
3189 3174 for comp in components[1:]:
3190 3175 mod = getattr(mod, comp)
3191 3176 return mod
3192 3177 try:
3193 3178 mod = importh("hgext.%s" % ext_name)
3194 3179 except ImportError:
3195 3180 mod = importh(ext_name)
3196 3181 external[ext_name] = mod.__name__
3197 3182 added.append((mod, ext_name))
3198 3183 except (util.SignalInterrupt, KeyboardInterrupt):
3199 3184 raise
3200 3185 except Exception, inst:
3201 3186 ui.warn(_("*** failed to import extension %s: %s\n") %
3202 3187 (ext_name, inst))
3203 3188 if ui.print_exc():
3204 3189 return 1
3205 3190
3206 3191 for mod, name in added:
3207 3192 uisetup = getattr(mod, 'uisetup', None)
3208 3193 if uisetup:
3209 3194 uisetup(ui)
3210 3195 cmdtable = getattr(mod, 'cmdtable', {})
3211 3196 for t in cmdtable:
3212 3197 if t in table:
3213 3198 ui.warn(_("module %s overrides %s\n") % (name, t))
3214 3199 table.update(cmdtable)
3215 3200
3216 3201 def dispatch(args):
3217 3202 for name in 'SIGBREAK', 'SIGHUP', 'SIGTERM':
3218 3203 num = getattr(signal, name, None)
3219 3204 if num: signal.signal(num, catchterm)
3220 3205
3221 3206 try:
3222 3207 u = ui.ui(traceback='--traceback' in sys.argv[1:])
3223 3208 except util.Abort, inst:
3224 3209 sys.stderr.write(_("abort: %s\n") % inst)
3225 3210 return -1
3226 3211
3227 3212 load_extensions(u)
3228 3213 u.addreadhook(load_extensions)
3229 3214
3230 3215 try:
3231 3216 cmd, func, args, options, cmdoptions = parse(u, args)
3232 3217 if options["time"]:
3233 3218 def get_times():
3234 3219 t = os.times()
3235 3220 if t[4] == 0.0: # Windows leaves this as zero, so use time.clock()
3236 3221 t = (t[0], t[1], t[2], t[3], time.clock())
3237 3222 return t
3238 3223 s = get_times()
3239 3224 def print_time():
3240 3225 t = get_times()
3241 3226 u.warn(_("Time: real %.3f secs (user %.3f+%.3f sys %.3f+%.3f)\n") %
3242 3227 (t[4]-s[4], t[0]-s[0], t[2]-s[2], t[1]-s[1], t[3]-s[3]))
3243 3228 atexit.register(print_time)
3244 3229
3245 3230 u.updateopts(options["verbose"], options["debug"], options["quiet"],
3246 3231 not options["noninteractive"], options["traceback"],
3247 3232 options["config"])
3248 3233
3249 3234 # enter the debugger before command execution
3250 3235 if options['debugger']:
3251 3236 pdb.set_trace()
3252 3237
3253 3238 try:
3254 3239 if options['cwd']:
3255 3240 try:
3256 3241 os.chdir(options['cwd'])
3257 3242 except OSError, inst:
3258 3243 raise util.Abort('%s: %s' %
3259 3244 (options['cwd'], inst.strerror))
3260 3245
3261 3246 path = u.expandpath(options["repository"]) or ""
3262 3247 repo = path and hg.repository(u, path=path) or None
3263 3248
3264 3249 if options['help']:
3265 3250 return help_(u, cmd, options['version'])
3266 3251 elif options['version']:
3267 3252 return show_version(u)
3268 3253 elif not cmd:
3269 3254 return help_(u, 'shortlist')
3270 3255
3271 3256 if cmd not in norepo.split():
3272 3257 try:
3273 3258 if not repo:
3274 3259 repo = hg.repository(u, path=path)
3275 3260 u = repo.ui
3276 3261 for name in external.itervalues():
3277 3262 mod = sys.modules[name]
3278 3263 if hasattr(mod, 'reposetup'):
3279 3264 mod.reposetup(u, repo)
3280 3265 hg.repo_setup_hooks.append(mod.reposetup)
3281 3266 except hg.RepoError:
3282 3267 if cmd not in optionalrepo.split():
3283 3268 raise
3284 3269 d = lambda: func(u, repo, *args, **cmdoptions)
3285 3270 else:
3286 3271 d = lambda: func(u, *args, **cmdoptions)
3287 3272
3288 3273 # reupdate the options, repo/.hg/hgrc may have changed them
3289 3274 u.updateopts(options["verbose"], options["debug"], options["quiet"],
3290 3275 not options["noninteractive"], options["traceback"],
3291 3276 options["config"])
3292 3277
3293 3278 try:
3294 3279 if options['profile']:
3295 3280 import hotshot, hotshot.stats
3296 3281 prof = hotshot.Profile("hg.prof")
3297 3282 try:
3298 3283 try:
3299 3284 return prof.runcall(d)
3300 3285 except:
3301 3286 try:
3302 3287 u.warn(_('exception raised - generating '
3303 3288 'profile anyway\n'))
3304 3289 except:
3305 3290 pass
3306 3291 raise
3307 3292 finally:
3308 3293 prof.close()
3309 3294 stats = hotshot.stats.load("hg.prof")
3310 3295 stats.strip_dirs()
3311 3296 stats.sort_stats('time', 'calls')
3312 3297 stats.print_stats(40)
3313 3298 elif options['lsprof']:
3314 3299 try:
3315 3300 from mercurial import lsprof
3316 3301 except ImportError:
3317 3302 raise util.Abort(_(
3318 3303 'lsprof not available - install from '
3319 3304 'http://codespeak.net/svn/user/arigo/hack/misc/lsprof/'))
3320 3305 p = lsprof.Profiler()
3321 3306 p.enable(subcalls=True)
3322 3307 try:
3323 3308 return d()
3324 3309 finally:
3325 3310 p.disable()
3326 3311 stats = lsprof.Stats(p.getstats())
3327 3312 stats.sort()
3328 3313 stats.pprint(top=10, file=sys.stderr, climit=5)
3329 3314 else:
3330 3315 return d()
3331 3316 finally:
3332 3317 u.flush()
3333 3318 except:
3334 3319 # enter the debugger when we hit an exception
3335 3320 if options['debugger']:
3336 3321 pdb.post_mortem(sys.exc_info()[2])
3337 3322 u.print_exc()
3338 3323 raise
3339 3324 except ParseError, inst:
3340 3325 if inst.args[0]:
3341 3326 u.warn(_("hg %s: %s\n") % (inst.args[0], inst.args[1]))
3342 3327 help_(u, inst.args[0])
3343 3328 else:
3344 3329 u.warn(_("hg: %s\n") % inst.args[1])
3345 3330 help_(u, 'shortlist')
3346 3331 except AmbiguousCommand, inst:
3347 3332 u.warn(_("hg: command '%s' is ambiguous:\n %s\n") %
3348 3333 (inst.args[0], " ".join(inst.args[1])))
3349 3334 except UnknownCommand, inst:
3350 3335 u.warn(_("hg: unknown command '%s'\n") % inst.args[0])
3351 3336 help_(u, 'shortlist')
3352 3337 except hg.RepoError, inst:
3353 3338 u.warn(_("abort: %s!\n") % inst)
3354 3339 except lock.LockHeld, inst:
3355 3340 if inst.errno == errno.ETIMEDOUT:
3356 3341 reason = _('timed out waiting for lock held by %s') % inst.locker
3357 3342 else:
3358 3343 reason = _('lock held by %s') % inst.locker
3359 3344 u.warn(_("abort: %s: %s\n") % (inst.desc or inst.filename, reason))
3360 3345 except lock.LockUnavailable, inst:
3361 3346 u.warn(_("abort: could not lock %s: %s\n") %
3362 3347 (inst.desc or inst.filename, inst.strerror))
3363 3348 except revlog.RevlogError, inst:
3364 3349 u.warn(_("abort: %s!\n") % inst)
3365 3350 except util.SignalInterrupt:
3366 3351 u.warn(_("killed!\n"))
3367 3352 except KeyboardInterrupt:
3368 3353 try:
3369 3354 u.warn(_("interrupted!\n"))
3370 3355 except IOError, inst:
3371 3356 if inst.errno == errno.EPIPE:
3372 3357 if u.debugflag:
3373 3358 u.warn(_("\nbroken pipe\n"))
3374 3359 else:
3375 3360 raise
3376 3361 except IOError, inst:
3377 3362 if hasattr(inst, "code"):
3378 3363 u.warn(_("abort: %s\n") % inst)
3379 3364 elif hasattr(inst, "reason"):
3380 3365 u.warn(_("abort: error: %s\n") % inst.reason[1])
3381 3366 elif hasattr(inst, "args") and inst[0] == errno.EPIPE:
3382 3367 if u.debugflag:
3383 3368 u.warn(_("broken pipe\n"))
3384 3369 elif getattr(inst, "strerror", None):
3385 3370 if getattr(inst, "filename", None):
3386 3371 u.warn(_("abort: %s: %s\n") % (inst.strerror, inst.filename))
3387 3372 else:
3388 3373 u.warn(_("abort: %s\n") % inst.strerror)
3389 3374 else:
3390 3375 raise
3391 3376 except OSError, inst:
3392 3377 if getattr(inst, "filename", None):
3393 3378 u.warn(_("abort: %s: %s\n") % (inst.strerror, inst.filename))
3394 3379 else:
3395 3380 u.warn(_("abort: %s\n") % inst.strerror)
3396 3381 except util.Abort, inst:
3397 3382 u.warn(_("abort: %s\n") % inst)
3398 3383 except TypeError, inst:
3399 3384 # was this an argument error?
3400 3385 tb = traceback.extract_tb(sys.exc_info()[2])
3401 3386 if len(tb) > 2: # no
3402 3387 raise
3403 3388 u.debug(inst, "\n")
3404 3389 u.warn(_("%s: invalid arguments\n") % cmd)
3405 3390 help_(u, cmd)
3406 3391 except SystemExit, inst:
3407 3392 # Commands shouldn't sys.exit directly, but give a return code.
3408 3393 # Just in case catch this and and pass exit code to caller.
3409 3394 return inst.code
3410 3395 except:
3411 3396 u.warn(_("** unknown exception encountered, details follow\n"))
3412 3397 u.warn(_("** report bug details to "
3413 3398 "http://www.selenic.com/mercurial/bts\n"))
3414 3399 u.warn(_("** or mercurial@selenic.com\n"))
3415 3400 u.warn(_("** Mercurial Distributed SCM (version %s)\n")
3416 3401 % version.get_version())
3417 3402 raise
3418 3403
3419 3404 return -1
@@ -1,187 +1,192 b''
1 1 # context.py - changeset and file context objects for mercurial
2 2 #
3 3 # Copyright 2006 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 node import *
9 9 from demandload import demandload
10 demandload(globals(), "ancestor")
10 demandload(globals(), "ancestor util")
11 11
12 12 class changectx(object):
13 13 """A changecontext object makes access to data related to a particular
14 14 changeset convenient."""
15 15 def __init__(self, repo, changeid=None):
16 16 """changeid is a revision number, node, or tag"""
17 17 self._repo = repo
18 18
19 19 if not changeid and changeid != 0:
20 20 p1, p2 = self._repo.dirstate.parents()
21 21 self._rev = self._repo.changelog.rev(p1)
22 22 if self._rev == -1:
23 23 changeid = 'tip'
24 24 else:
25 25 self._node = p1
26 26 return
27 27
28 28 self._node = self._repo.lookup(changeid)
29 29 self._rev = self._repo.changelog.rev(self._node)
30 30
31 31 def changeset(self):
32 32 try:
33 33 return self._changeset
34 34 except AttributeError:
35 35 self._changeset = self._repo.changelog.read(self.node())
36 36 return self._changeset
37 37
38 38 def manifest(self):
39 39 try:
40 40 return self._manifest
41 41 except AttributeError:
42 42 self._manifest = self._repo.manifest.read(self.changeset()[0])
43 43 return self._manifest
44 44
45 45 def rev(self): return self._rev
46 46 def node(self): return self._node
47 47 def user(self): return self.changeset()[1]
48 48 def date(self): return self.changeset()[2]
49 49 def files(self): return self.changeset()[3]
50 50 def description(self): return self.changeset()[4]
51 51
52 52 def parents(self):
53 53 """return contexts for each parent changeset"""
54 54 p = self._repo.changelog.parents(self._node)
55 55 return [ changectx(self._repo, x) for x in p ]
56 56
57 57 def children(self):
58 58 """return contexts for each child changeset"""
59 59 c = self._repo.changelog.children(self._node)
60 60 return [ changectx(self._repo, x) for x in c ]
61 61
62 62 def filenode(self, path):
63 63 node, flag = self._repo.manifest.find(self.changeset()[0], path)
64 64 return node
65 65
66 66 def filectx(self, path, fileid=None):
67 67 """get a file context from this changeset"""
68 68 if fileid is None:
69 69 fileid = self.filenode(path)
70 70 return filectx(self._repo, path, fileid=fileid)
71 71
72 72 def filectxs(self):
73 73 """generate a file context for each file in this changeset's
74 74 manifest"""
75 75 mf = self.manifest()
76 76 m = mf.keys()
77 77 m.sort()
78 78 for f in m:
79 79 yield self.filectx(f, fileid=mf[f])
80 80
81 81 def ancestor(self, c2):
82 82 """
83 83 return the ancestor context of self and c2
84 84 """
85 85 n = self._repo.changelog.ancestor(self._node, c2._node)
86 86 return changectx(self._repo, n)
87 87
88 88 class filectx(object):
89 89 """A filecontext object makes access to data related to a particular
90 90 filerevision convenient."""
91 91 def __init__(self, repo, path, changeid=None, fileid=None, filelog=None):
92 92 """changeid can be a changeset revision, node, or tag.
93 93 fileid can be a file revision or node."""
94 94 self._repo = repo
95 95 self._path = path
96 96
97 97 assert changeid is not None or fileid is not None
98 98
99 99 if filelog:
100 100 self._filelog = filelog
101 101 else:
102 102 self._filelog = self._repo.file(self._path)
103 103
104 104 if fileid is None:
105 105 self._changeid = changeid
106 106 else:
107 107 self._filenode = self._filelog.lookup(fileid)
108 108 self._changeid = self._filelog.linkrev(self._filenode)
109 109
110 110 def __getattr__(self, name):
111 111 if name == '_changectx':
112 112 self._changectx = changectx(self._repo, self._changeid)
113 113 return self._changectx
114 114 elif name == '_filenode':
115 115 self._filenode = self._changectx.filenode(self._path)
116 116 return self._filenode
117 117 elif name == '_filerev':
118 118 self._filerev = self._filelog.rev(self._filenode)
119 119 return self._filerev
120 120 else:
121 121 raise AttributeError, name
122 122
123 123 def filerev(self): return self._filerev
124 124 def filenode(self): return self._filenode
125 125 def filelog(self): return self._filelog
126 126
127 127 def rev(self): return self._changectx.rev()
128 128 def node(self): return self._changectx.node()
129 129 def user(self): return self._changectx.user()
130 130 def date(self): return self._changectx.date()
131 131 def files(self): return self._changectx.files()
132 132 def description(self): return self._changectx.description()
133 133 def manifest(self): return self._changectx.manifest()
134 134
135 135 def data(self): return self._filelog.read(self._filenode)
136 136 def renamed(self): return self._filelog.renamed(self._filenode)
137 137 def path(self): return self._path
138 138
139 139 def parents(self):
140 140 p = self._path
141 141 fl = self._filelog
142 142 pl = [ (p, n, fl) for n in self._filelog.parents(self._filenode) ]
143 143
144 144 r = self.renamed()
145 145 if r:
146 146 pl[0] = (r[0], r[1], None)
147 147
148 148 return [ filectx(self._repo, p, fileid=n, filelog=l)
149 149 for p,n,l in pl if n != nullid ]
150 150
151 151 def children(self):
152 152 # hard for renames
153 153 c = self._filelog.children(self._filenode)
154 154 return [ filectx(self._repo, self._path, fileid=x,
155 155 filelog=self._filelog) for x in c ]
156 156
157 157 def annotate(self):
158 return self._filelog.annotate(self._filenode)
158 getctx = util.cachefunc(lambda x: filectx(self._repo, self._path,
159 changeid=x,
160 filelog=self._filelog))
161 hist = self._filelog.annotate(self._filenode)
162
163 return [(getctx(rev), line) for rev, line in hist]
159 164
160 165 def ancestor(self, fc2):
161 166 """
162 167 find the common ancestor file context, if any, of self, and fc2
163 168 """
164 169
165 170 acache = {}
166 171 flcache = {self._path:self._filelog, fc2._path:fc2._filelog}
167 172 def parents(vertex):
168 173 if vertex in acache:
169 174 return acache[vertex]
170 175 f, n = vertex
171 176 if f not in flcache:
172 177 flcache[f] = self._repo.file(f)
173 178 fl = flcache[f]
174 179 pl = [ (f,p) for p in fl.parents(n) if p != nullid ]
175 180 re = fl.renamed(n)
176 181 if re:
177 182 pl.append(re)
178 183 acache[vertex]=pl
179 184 return pl
180 185
181 186 a, b = (self._path, self._filenode), (fc2._path, fc2._filenode)
182 187 v = ancestor.ancestor(a, b, parents)
183 188 if v:
184 189 f,n = v
185 190 return filectx(self._repo, f, fileid=n, filelog=flcache[f])
186 191
187 192 return None
General Comments 0
You need to be logged in to leave comments. Login now