diff -r 5ff1fc726848 -r c6bca38c1cbf eggs/mercurial-1.7.3-py2.6-linux-x86_64.egg/mercurial/commands.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/eggs/mercurial-1.7.3-py2.6-linux-x86_64.egg/mercurial/commands.py Sat Jan 08 11:20:57 2011 +0530 @@ -0,0 +1,4530 @@ +# commands.py - command processing for mercurial +# +# Copyright 2005-2007 Matt Mackall +# +# This software may be used and distributed according to the terms of the +# GNU General Public License version 2 or any later version. + +from node import hex, nullid, nullrev, short +from lock import release +from i18n import _, gettext +import os, re, sys, difflib, time, tempfile +import hg, util, revlog, extensions, copies, error +import patch, help, mdiff, url, encoding, templatekw, discovery +import archival, changegroup, cmdutil, sshserver, hbisect, hgweb, hgweb.server +import merge as mergemod +import minirst, revset +import dagparser + +# Commands start here, listed alphabetically + +def add(ui, repo, *pats, **opts): + """add the specified files on the next commit + + Schedule files to be version controlled and added to the + repository. + + The files will be added to the repository at the next commit. To + undo an add before that, see :hg:`forget`. + + If no names are given, add all files to the repository. + + .. container:: verbose + + An example showing how new (unknown) files are added + automatically by :hg:`add`:: + + $ ls + foo.c + $ hg status + ? foo.c + $ hg add + adding foo.c + $ hg status + A foo.c + + Returns 0 if all files are successfully added. + """ + + m = cmdutil.match(repo, pats, opts) + rejected = cmdutil.add(ui, repo, m, opts.get('dry_run'), + opts.get('subrepos'), prefix="") + return rejected and 1 or 0 + +def addremove(ui, repo, *pats, **opts): + """add all new files, delete all missing files + + Add all new files and remove all missing files from the + repository. + + New files are ignored if they match any of the patterns in + .hgignore. As with add, these changes take effect at the next + commit. + + Use the -s/--similarity option to detect renamed files. With a + parameter greater than 0, this compares every removed file with + every added file and records those similar enough as renames. This + option takes a percentage between 0 (disabled) and 100 (files must + be identical) as its parameter. Detecting renamed files this way + can be expensive. After using this option, :hg:`status -C` can be + used to check which files were identified as moved or renamed. + + Returns 0 if all files are successfully added. + """ + try: + sim = float(opts.get('similarity') or 100) + except ValueError: + raise util.Abort(_('similarity must be a number')) + if sim < 0 or sim > 100: + raise util.Abort(_('similarity must be between 0 and 100')) + return cmdutil.addremove(repo, pats, opts, similarity=sim / 100.0) + +def annotate(ui, repo, *pats, **opts): + """show changeset information by line for each file + + List changes in files, showing the revision id responsible for + each line + + This command is useful for discovering when a change was made and + by whom. + + Without the -a/--text option, annotate will avoid processing files + it detects as binary. With -a, annotate will annotate the file + anyway, although the results will probably be neither useful + nor desirable. + + Returns 0 on success. + """ + if opts.get('follow'): + # --follow is deprecated and now just an alias for -f/--file + # to mimic the behavior of Mercurial before version 1.5 + opts['file'] = 1 + + datefunc = ui.quiet and util.shortdate or util.datestr + getdate = util.cachefunc(lambda x: datefunc(x[0].date())) + + if not pats: + raise util.Abort(_('at least one filename or pattern is required')) + + opmap = [('user', lambda x: ui.shortuser(x[0].user())), + ('number', lambda x: str(x[0].rev())), + ('changeset', lambda x: short(x[0].node())), + ('date', getdate), + ('file', lambda x: x[0].path()), + ] + + if (not opts.get('user') and not opts.get('changeset') + and not opts.get('date') and not opts.get('file')): + opts['number'] = 1 + + linenumber = opts.get('line_number') is not None + if linenumber and (not opts.get('changeset')) and (not opts.get('number')): + raise util.Abort(_('at least one of -n/-c is required for -l')) + + funcmap = [func for op, func in opmap if opts.get(op)] + if linenumber: + lastfunc = funcmap[-1] + funcmap[-1] = lambda x: "%s:%s" % (lastfunc(x), x[1]) + + ctx = repo[opts.get('rev')] + m = cmdutil.match(repo, pats, opts) + follow = not opts.get('no_follow') + for abs in ctx.walk(m): + fctx = ctx[abs] + if not opts.get('text') and util.binary(fctx.data()): + ui.write(_("%s: binary file\n") % ((pats and m.rel(abs)) or abs)) + continue + + lines = fctx.annotate(follow=follow, linenumber=linenumber) + pieces = [] + + for f in funcmap: + l = [f(n) for n, dummy in lines] + if l: + sized = [(x, encoding.colwidth(x)) for x in l] + ml = max([w for x, w in sized]) + pieces.append(["%s%s" % (' ' * (ml - w), x) for x, w in sized]) + + if pieces: + for p, l in zip(zip(*pieces), lines): + ui.write("%s: %s" % (" ".join(p), l[1])) + +def archive(ui, repo, dest, **opts): + '''create an unversioned archive of a repository revision + + By default, the revision used is the parent of the working + directory; use -r/--rev to specify a different revision. + + The archive type is automatically detected based on file + extension (or override using -t/--type). + + Valid types are: + + :``files``: a directory full of files (default) + :``tar``: tar archive, uncompressed + :``tbz2``: tar archive, compressed using bzip2 + :``tgz``: tar archive, compressed using gzip + :``uzip``: zip archive, uncompressed + :``zip``: zip archive, compressed using deflate + + The exact name of the destination archive or directory is given + using a format string; see :hg:`help export` for details. + + Each member added to an archive file has a directory prefix + prepended. Use -p/--prefix to specify a format string for the + prefix. The default is the basename of the archive, with suffixes + removed. + + Returns 0 on success. + ''' + + ctx = repo[opts.get('rev')] + if not ctx: + raise util.Abort(_('no working directory: please specify a revision')) + node = ctx.node() + dest = cmdutil.make_filename(repo, dest, node) + if os.path.realpath(dest) == repo.root: + raise util.Abort(_('repository root cannot be destination')) + + kind = opts.get('type') or archival.guesskind(dest) or 'files' + prefix = opts.get('prefix') + + if dest == '-': + if kind == 'files': + raise util.Abort(_('cannot archive plain files to stdout')) + dest = sys.stdout + if not prefix: + prefix = os.path.basename(repo.root) + '-%h' + + prefix = cmdutil.make_filename(repo, prefix, node) + matchfn = cmdutil.match(repo, [], opts) + archival.archive(repo, dest, node, kind, not opts.get('no_decode'), + matchfn, prefix, subrepos=opts.get('subrepos')) + +def backout(ui, repo, node=None, rev=None, **opts): + '''reverse effect of earlier changeset + + The backout command merges the reverse effect of the reverted + changeset into the working directory. + + With the --merge option, it first commits the reverted changes + as a new changeset. This new changeset is a child of the reverted + changeset. + The --merge option remembers the parent of the working directory + before starting the backout, then merges the new head with that + changeset afterwards. + This will result in an explicit merge in the history. + + If you backout a changeset other than the original parent of the + working directory, the result of this merge is not committed, + as with a normal merge. Otherwise, no merge is needed and the + commit is automatic. + + Note that the default behavior (without --merge) has changed in + version 1.7. To restore the previous default behavior, use + :hg:`backout --merge` and then :hg:`update --clean .` to get rid of + the ongoing merge. + + See :hg:`help dates` for a list of formats valid for -d/--date. + + Returns 0 on success. + ''' + if rev and node: + raise util.Abort(_("please specify just one revision")) + + if not rev: + rev = node + + if not rev: + raise util.Abort(_("please specify a revision to backout")) + + date = opts.get('date') + if date: + opts['date'] = util.parsedate(date) + + cmdutil.bail_if_changed(repo) + node = repo.lookup(rev) + + op1, op2 = repo.dirstate.parents() + a = repo.changelog.ancestor(op1, node) + if a != node: + raise util.Abort(_('cannot backout change on a different branch')) + + p1, p2 = repo.changelog.parents(node) + if p1 == nullid: + raise util.Abort(_('cannot backout a change with no parents')) + if p2 != nullid: + if not opts.get('parent'): + raise util.Abort(_('cannot backout a merge changeset without ' + '--parent')) + p = repo.lookup(opts['parent']) + if p not in (p1, p2): + raise util.Abort(_('%s is not a parent of %s') % + (short(p), short(node))) + parent = p + else: + if opts.get('parent'): + raise util.Abort(_('cannot use --parent on non-merge changeset')) + parent = p1 + + # the backout should appear on the same branch + branch = repo.dirstate.branch() + hg.clean(repo, node, show_stats=False) + repo.dirstate.setbranch(branch) + revert_opts = opts.copy() + revert_opts['date'] = None + revert_opts['all'] = True + revert_opts['rev'] = hex(parent) + revert_opts['no_backup'] = None + revert(ui, repo, **revert_opts) + if not opts.get('merge') and op1 != node: + try: + ui.setconfig('ui', 'forcemerge', opts.get('tool', '')) + return hg.update(repo, op1) + finally: + ui.setconfig('ui', 'forcemerge', '') + + commit_opts = opts.copy() + commit_opts['addremove'] = False + if not commit_opts['message'] and not commit_opts['logfile']: + # we don't translate commit messages + commit_opts['message'] = "Backed out changeset %s" % short(node) + commit_opts['force_editor'] = True + commit(ui, repo, **commit_opts) + def nice(node): + return '%d:%s' % (repo.changelog.rev(node), short(node)) + ui.status(_('changeset %s backs out changeset %s\n') % + (nice(repo.changelog.tip()), nice(node))) + if opts.get('merge') and op1 != node: + hg.clean(repo, op1, show_stats=False) + ui.status(_('merging with changeset %s\n') + % nice(repo.changelog.tip())) + try: + ui.setconfig('ui', 'forcemerge', opts.get('tool', '')) + return hg.merge(repo, hex(repo.changelog.tip())) + finally: + ui.setconfig('ui', 'forcemerge', '') + return 0 + +def bisect(ui, repo, rev=None, extra=None, command=None, + reset=None, good=None, bad=None, skip=None, noupdate=None): + """subdivision search of changesets + + This command helps to find changesets which introduce problems. To + use, mark the earliest changeset you know exhibits the problem as + bad, then mark the latest changeset which is free from the problem + as good. Bisect will update your working directory to a revision + for testing (unless the -U/--noupdate option is specified). Once + you have performed tests, mark the working directory as good or + bad, and bisect will either update to another candidate changeset + or announce that it has found the bad revision. + + As a shortcut, you can also use the revision argument to mark a + revision as good or bad without checking it out first. + + If you supply a command, it will be used for automatic bisection. + Its exit status will be used to mark revisions as good or bad: + status 0 means good, 125 means to skip the revision, 127 + (command not found) will abort the bisection, and any other + non-zero exit status means the revision is bad. + + Returns 0 on success. + """ + def print_result(nodes, good): + displayer = cmdutil.show_changeset(ui, repo, {}) + if len(nodes) == 1: + # narrowed it down to a single revision + if good: + ui.write(_("The first good revision is:\n")) + else: + ui.write(_("The first bad revision is:\n")) + displayer.show(repo[nodes[0]]) + parents = repo[nodes[0]].parents() + if len(parents) > 1: + side = good and state['bad'] or state['good'] + num = len(set(i.node() for i in parents) & set(side)) + if num == 1: + common = parents[0].ancestor(parents[1]) + ui.write(_('Not all ancestors of this changeset have been' + ' checked.\nTo check the other ancestors, start' + ' from the common ancestor, %s.\n' % common)) + else: + # multiple possible revisions + if good: + ui.write(_("Due to skipped revisions, the first " + "good revision could be any of:\n")) + else: + ui.write(_("Due to skipped revisions, the first " + "bad revision could be any of:\n")) + for n in nodes: + displayer.show(repo[n]) + displayer.close() + + def check_state(state, interactive=True): + if not state['good'] or not state['bad']: + if (good or bad or skip or reset) and interactive: + return + if not state['good']: + raise util.Abort(_('cannot bisect (no known good revisions)')) + else: + raise util.Abort(_('cannot bisect (no known bad revisions)')) + return True + + # backward compatibility + if rev in "good bad reset init".split(): + ui.warn(_("(use of 'hg bisect ' is deprecated)\n")) + cmd, rev, extra = rev, extra, None + if cmd == "good": + good = True + elif cmd == "bad": + bad = True + else: + reset = True + elif extra or good + bad + skip + reset + bool(command) > 1: + raise util.Abort(_('incompatible arguments')) + + if reset: + p = repo.join("bisect.state") + if os.path.exists(p): + os.unlink(p) + return + + state = hbisect.load_state(repo) + + if command: + changesets = 1 + try: + while changesets: + # update state + status = util.system(command) + if status == 125: + transition = "skip" + elif status == 0: + transition = "good" + # status < 0 means process was killed + elif status == 127: + raise util.Abort(_("failed to execute %s") % command) + elif status < 0: + raise util.Abort(_("%s killed") % command) + else: + transition = "bad" + ctx = repo[rev or '.'] + state[transition].append(ctx.node()) + ui.status(_('Changeset %d:%s: %s\n') % (ctx, ctx, transition)) + check_state(state, interactive=False) + # bisect + nodes, changesets, good = hbisect.bisect(repo.changelog, state) + # update to next check + cmdutil.bail_if_changed(repo) + hg.clean(repo, nodes[0], show_stats=False) + finally: + hbisect.save_state(repo, state) + print_result(nodes, good) + return + + # update state + + if rev: + nodes = [repo.lookup(i) for i in cmdutil.revrange(repo, [rev])] + else: + nodes = [repo.lookup('.')] + + if good or bad or skip: + if good: + state['good'] += nodes + elif bad: + state['bad'] += nodes + elif skip: + state['skip'] += nodes + hbisect.save_state(repo, state) + + if not check_state(state): + return + + # actually bisect + nodes, changesets, good = hbisect.bisect(repo.changelog, state) + if changesets == 0: + print_result(nodes, good) + else: + assert len(nodes) == 1 # only a single node can be tested next + node = nodes[0] + # compute the approximate number of remaining tests + tests, size = 0, 2 + while size <= changesets: + tests, size = tests + 1, size * 2 + rev = repo.changelog.rev(node) + ui.write(_("Testing changeset %d:%s " + "(%d changesets remaining, ~%d tests)\n") + % (rev, short(node), changesets, tests)) + if not noupdate: + cmdutil.bail_if_changed(repo) + return hg.clean(repo, node) + +def branch(ui, repo, label=None, **opts): + """set or show the current branch name + + With no argument, show the current branch name. With one argument, + set the working directory branch name (the branch will not exist + in the repository until the next commit). Standard practice + recommends that primary development take place on the 'default' + branch. + + Unless -f/--force is specified, branch will not let you set a + branch name that already exists, even if it's inactive. + + Use -C/--clean to reset the working directory branch to that of + the parent of the working directory, negating a previous branch + change. + + Use the command :hg:`update` to switch to an existing branch. Use + :hg:`commit --close-branch` to mark this branch as closed. + + Returns 0 on success. + """ + + if opts.get('clean'): + label = repo[None].parents()[0].branch() + repo.dirstate.setbranch(label) + ui.status(_('reset working directory to branch %s\n') % label) + elif label: + utflabel = encoding.fromlocal(label) + if not opts.get('force') and utflabel in repo.branchtags(): + if label not in [p.branch() for p in repo.parents()]: + raise util.Abort(_('a branch of the same name already exists' + " (use 'hg update' to switch to it)")) + repo.dirstate.setbranch(utflabel) + ui.status(_('marked working directory as branch %s\n') % label) + else: + ui.write("%s\n" % encoding.tolocal(repo.dirstate.branch())) + +def branches(ui, repo, active=False, closed=False): + """list repository named branches + + List the repository's named branches, indicating which ones are + inactive. If -c/--closed is specified, also list branches which have + been marked closed (see :hg:`commit --close-branch`). + + If -a/--active is specified, only show active branches. A branch + is considered active if it contains repository heads. + + Use the command :hg:`update` to switch to an existing branch. + + Returns 0. + """ + + hexfunc = ui.debugflag and hex or short + activebranches = [repo[n].branch() for n in repo.heads()] + def testactive(tag, node): + realhead = tag in activebranches + open = node in repo.branchheads(tag, closed=False) + return realhead and open + branches = sorted([(testactive(tag, node), repo.changelog.rev(node), tag) + for tag, node in repo.branchtags().items()], + reverse=True) + + for isactive, node, tag in branches: + if (not active) or isactive: + encodedtag = encoding.tolocal(tag) + if ui.quiet: + ui.write("%s\n" % encodedtag) + else: + hn = repo.lookup(node) + if isactive: + label = 'branches.active' + notice = '' + elif hn not in repo.branchheads(tag, closed=False): + if not closed: + continue + label = 'branches.closed' + notice = _(' (closed)') + else: + label = 'branches.inactive' + notice = _(' (inactive)') + if tag == repo.dirstate.branch(): + label = 'branches.current' + rev = str(node).rjust(31 - encoding.colwidth(encodedtag)) + rev = ui.label('%s:%s' % (rev, hexfunc(hn)), 'log.changeset') + encodedtag = ui.label(encodedtag, label) + ui.write("%s %s%s\n" % (encodedtag, rev, notice)) + +def bundle(ui, repo, fname, dest=None, **opts): + """create a changegroup file + + Generate a compressed changegroup file collecting changesets not + known to be in another repository. + + If you omit the destination repository, then hg assumes the + destination will have all the nodes you specify with --base + parameters. To create a bundle containing all changesets, use + -a/--all (or --base null). + + You can change compression method with the -t/--type option. + The available compression methods are: none, bzip2, and + gzip (by default, bundles are compressed using bzip2). + + The bundle file can then be transferred using conventional means + and applied to another repository with the unbundle or pull + command. This is useful when direct push and pull are not + available or when exporting an entire repository is undesirable. + + Applying bundles preserves all changeset contents including + permissions, copy/rename information, and revision history. + + Returns 0 on success, 1 if no changes found. + """ + revs = opts.get('rev') or None + if opts.get('all'): + base = ['null'] + else: + base = opts.get('base') + if base: + if dest: + raise util.Abort(_("--base is incompatible with specifying " + "a destination")) + base = [repo.lookup(rev) for rev in base] + # create the right base + # XXX: nodesbetween / changegroup* should be "fixed" instead + o = [] + has = set((nullid,)) + for n in base: + has.update(repo.changelog.reachable(n)) + if revs: + revs = [repo.lookup(rev) for rev in revs] + visit = revs[:] + has.difference_update(visit) + else: + visit = repo.changelog.heads() + seen = {} + while visit: + n = visit.pop(0) + parents = [p for p in repo.changelog.parents(n) if p not in has] + if len(parents) == 0: + if n not in has: + o.append(n) + else: + for p in parents: + if p not in seen: + seen[p] = 1 + visit.append(p) + else: + dest = ui.expandpath(dest or 'default-push', dest or 'default') + dest, branches = hg.parseurl(dest, opts.get('branch')) + other = hg.repository(hg.remoteui(repo, opts), dest) + revs, checkout = hg.addbranchrevs(repo, other, branches, revs) + if revs: + revs = [repo.lookup(rev) for rev in revs] + o = discovery.findoutgoing(repo, other, force=opts.get('force')) + + if not o: + ui.status(_("no changes found\n")) + return 1 + + if revs: + cg = repo.changegroupsubset(o, revs, 'bundle') + else: + cg = repo.changegroup(o, 'bundle') + + bundletype = opts.get('type', 'bzip2').lower() + btypes = {'none': 'HG10UN', 'bzip2': 'HG10BZ', 'gzip': 'HG10GZ'} + bundletype = btypes.get(bundletype) + if bundletype not in changegroup.bundletypes: + raise util.Abort(_('unknown bundle type specified with --type')) + + changegroup.writebundle(cg, fname, bundletype) + +def cat(ui, repo, file1, *pats, **opts): + """output the current or given revision of files + + Print the specified files as they were at the given revision. If + no revision is given, the parent of the working directory is used, + or tip if no revision is checked out. + + Output may be to a file, in which case the name of the file is + given using a format string. The formatting rules are the same as + for the export command, with the following additions: + + :``%s``: basename of file being printed + :``%d``: dirname of file being printed, or '.' if in repository root + :``%p``: root-relative path name of file being printed + + Returns 0 on success. + """ + ctx = cmdutil.revsingle(repo, opts.get('rev')) + err = 1 + m = cmdutil.match(repo, (file1,) + pats, opts) + for abs in ctx.walk(m): + fp = cmdutil.make_file(repo, opts.get('output'), ctx.node(), pathname=abs) + data = ctx[abs].data() + if opts.get('decode'): + data = repo.wwritedata(abs, data) + fp.write(data) + err = 0 + return err + +def clone(ui, source, dest=None, **opts): + """make a copy of an existing repository + + Create a copy of an existing repository in a new directory. + + If no destination directory name is specified, it defaults to the + basename of the source. + + The location of the source is added to the new repository's + .hg/hgrc file, as the default to be used for future pulls. + + See :hg:`help urls` for valid source format details. + + It is possible to specify an ``ssh://`` URL as the destination, but no + .hg/hgrc and working directory will be created on the remote side. + Please see :hg:`help urls` for important details about ``ssh://`` URLs. + + A set of changesets (tags, or branch names) to pull may be specified + by listing each changeset (tag, or branch name) with -r/--rev. + If -r/--rev is used, the cloned repository will contain only a subset + of the changesets of the source repository. Only the set of changesets + defined by all -r/--rev options (including all their ancestors) + will be pulled into the destination repository. + No subsequent changesets (including subsequent tags) will be present + in the destination. + + Using -r/--rev (or 'clone src#rev dest') implies --pull, even for + local source repositories. + + For efficiency, hardlinks are used for cloning whenever the source + and destination are on the same filesystem (note this applies only + to the repository data, not to the working directory). Some + filesystems, such as AFS, implement hardlinking incorrectly, but + do not report errors. In these cases, use the --pull option to + avoid hardlinking. + + In some cases, you can clone repositories and the working directory + using full hardlinks with :: + + $ cp -al REPO REPOCLONE + + This is the fastest way to clone, but it is not always safe. The + operation is not atomic (making sure REPO is not modified during + the operation is up to you) and you have to make sure your editor + breaks hardlinks (Emacs and most Linux Kernel tools do so). Also, + this is not compatible with certain extensions that place their + metadata under the .hg directory, such as mq. + + Mercurial will update the working directory to the first applicable + revision from this list: + + a) null if -U or the source repository has no changesets + b) if -u . and the source repository is local, the first parent of + the source repository's working directory + c) the changeset specified with -u (if a branch name, this means the + latest head of that branch) + d) the changeset specified with -r + e) the tipmost head specified with -b + f) the tipmost head specified with the url#branch source syntax + g) the tipmost head of the default branch + h) tip + + Returns 0 on success. + """ + if opts.get('noupdate') and opts.get('updaterev'): + raise util.Abort(_("cannot specify both --noupdate and --updaterev")) + + r = hg.clone(hg.remoteui(ui, opts), source, dest, + pull=opts.get('pull'), + stream=opts.get('uncompressed'), + rev=opts.get('rev'), + update=opts.get('updaterev') or not opts.get('noupdate'), + branch=opts.get('branch')) + + return r is None + +def commit(ui, repo, *pats, **opts): + """commit the specified files or all outstanding changes + + Commit changes to the given files into the repository. Unlike a + centralized RCS, this operation is a local operation. See + :hg:`push` for a way to actively distribute your changes. + + If a list of files is omitted, all changes reported by :hg:`status` + will be committed. + + If you are committing the result of a merge, do not provide any + filenames or -I/-X filters. + + If no commit message is specified, Mercurial starts your + configured editor where you can enter a message. In case your + commit fails, you will find a backup of your message in + ``.hg/last-message.txt``. + + See :hg:`help dates` for a list of formats valid for -d/--date. + + Returns 0 on success, 1 if nothing changed. + """ + extra = {} + if opts.get('close_branch'): + if repo['.'].node() not in repo.branchheads(): + # The topo heads set is included in the branch heads set of the + # current branch, so it's sufficient to test branchheads + raise util.Abort(_('can only close branch heads')) + extra['close'] = 1 + e = cmdutil.commiteditor + if opts.get('force_editor'): + e = cmdutil.commitforceeditor + + def commitfunc(ui, repo, message, match, opts): + return repo.commit(message, opts.get('user'), opts.get('date'), match, + editor=e, extra=extra) + + branch = repo[None].branch() + bheads = repo.branchheads(branch) + + node = cmdutil.commit(ui, repo, commitfunc, pats, opts) + if not node: + ui.status(_("nothing changed\n")) + return 1 + + ctx = repo[node] + parents = ctx.parents() + + if bheads and not [x for x in parents + if x.node() in bheads and x.branch() == branch]: + ui.status(_('created new head\n')) + # The message is not printed for initial roots. For the other + # changesets, it is printed in the following situations: + # + # Par column: for the 2 parents with ... + # N: null or no parent + # B: parent is on another named branch + # C: parent is a regular non head changeset + # H: parent was a branch head of the current branch + # Msg column: whether we print "created new head" message + # In the following, it is assumed that there already exists some + # initial branch heads of the current branch, otherwise nothing is + # printed anyway. + # + # Par Msg Comment + # NN y additional topo root + # + # BN y additional branch root + # CN y additional topo head + # HN n usual case + # + # BB y weird additional branch root + # CB y branch merge + # HB n merge with named branch + # + # CC y additional head from merge + # CH n merge with a head + # + # HH n head merge: head count decreases + + if not opts.get('close_branch'): + for r in parents: + if r.extra().get('close') and r.branch() == branch: + ui.status(_('reopening closed branch head %d\n') % r) + + if ui.debugflag: + ui.write(_('committed changeset %d:%s\n') % (int(ctx), ctx.hex())) + elif ui.verbose: + ui.write(_('committed changeset %d:%s\n') % (int(ctx), ctx)) + +def copy(ui, repo, *pats, **opts): + """mark files as copied for the next commit + + Mark dest as having copies of source files. If dest is a + directory, copies are put in that directory. If dest is a file, + the source must be a single file. + + By default, this command copies the contents of files as they + exist in the working directory. If invoked with -A/--after, the + operation is recorded, but no copying is performed. + + This command takes effect with the next commit. To undo a copy + before that, see :hg:`revert`. + + Returns 0 on success, 1 if errors are encountered. + """ + wlock = repo.wlock(False) + try: + return cmdutil.copy(ui, repo, pats, opts) + finally: + wlock.release() + +def debugancestor(ui, repo, *args): + """find the ancestor revision of two revisions in a given index""" + if len(args) == 3: + index, rev1, rev2 = args + r = revlog.revlog(util.opener(os.getcwd(), audit=False), index) + lookup = r.lookup + elif len(args) == 2: + if not repo: + raise util.Abort(_("there is no Mercurial repository here " + "(.hg not found)")) + rev1, rev2 = args + r = repo.changelog + lookup = repo.lookup + else: + raise util.Abort(_('either two or three arguments required')) + a = r.ancestor(lookup(rev1), lookup(rev2)) + ui.write("%d:%s\n" % (r.rev(a), hex(a))) + +def debugbuilddag(ui, repo, text, + mergeable_file=False, + appended_file=False, + overwritten_file=False, + new_file=False): + """builds a repo with a given dag from scratch in the current empty repo + + Elements: + + - "+n" is a linear run of n nodes based on the current default parent + - "." is a single node based on the current default parent + - "$" resets the default parent to null (implied at the start); + otherwise the default parent is always the last node created + - " 0: + raise util.Abort(_('repository is not empty')) + + if overwritten_file or appended_file: + # we don't want to fail in merges during buildup + os.environ['HGMERGE'] = 'internal:local' + + def writefile(fname, text, fmode="wb"): + f = open(fname, fmode) + try: + f.write(text) + finally: + f.close() + + if mergeable_file: + linesperrev = 2 + # determine number of revs in DAG + n = 0 + for type, data in dagparser.parsedag(text): + if type == 'n': + n += 1 + # make a file with k lines per rev + writefile("mf", "\n".join(str(i) for i in xrange(0, n * linesperrev)) + + "\n") + + at = -1 + atbranch = 'default' + for type, data in dagparser.parsedag(text): + if type == 'n': + ui.status('node %s\n' % str(data)) + id, ps = data + p1 = ps[0] + if p1 != at: + update(ui, repo, node=str(p1), clean=True) + at = p1 + if repo.dirstate.branch() != atbranch: + branch(ui, repo, atbranch, force=True) + if len(ps) > 1: + p2 = ps[1] + merge(ui, repo, node=p2) + + if mergeable_file: + f = open("mf", "rb+") + try: + lines = f.read().split("\n") + lines[id * linesperrev] += " r%i" % id + f.seek(0) + f.write("\n".join(lines)) + finally: + f.close() + + if appended_file: + writefile("af", "r%i\n" % id, "ab") + + if overwritten_file: + writefile("of", "r%i\n" % id) + + if new_file: + writefile("nf%i" % id, "r%i\n" % id) + + commit(ui, repo, addremove=True, message="r%i" % id, date=(id, 0)) + at = id + elif type == 'l': + id, name = data + ui.status('tag %s\n' % name) + tag(ui, repo, name, local=True) + elif type == 'a': + ui.status('branch %s\n' % data) + atbranch = data + elif type in 'cC': + r = util.system(data, cwd=repo.root) + if r: + desc, r = util.explain_exit(r) + raise util.Abort(_('%s command %s') % (data, desc)) + +def debugcommands(ui, cmd='', *args): + """list all available commands and options""" + for cmd, vals in sorted(table.iteritems()): + cmd = cmd.split('|')[0].strip('^') + opts = ', '.join([i[1] for i in vals[1]]) + ui.write('%s: %s\n' % (cmd, opts)) + +def debugcomplete(ui, cmd='', **opts): + """returns the completion list associated with the given command""" + + if opts.get('options'): + options = [] + otables = [globalopts] + if cmd: + aliases, entry = cmdutil.findcmd(cmd, table, False) + otables.append(entry[1]) + for t in otables: + for o in t: + if "(DEPRECATED)" in o[3]: + continue + if o[0]: + options.append('-%s' % o[0]) + options.append('--%s' % o[1]) + ui.write("%s\n" % "\n".join(options)) + return + + cmdlist = cmdutil.findpossible(cmd, table) + if ui.verbose: + cmdlist = [' '.join(c[0]) for c in cmdlist.values()] + ui.write("%s\n" % "\n".join(sorted(cmdlist))) + +def debugfsinfo(ui, path = "."): + """show information detected about current filesystem""" + open('.debugfsinfo', 'w').write('') + ui.write('exec: %s\n' % (util.checkexec(path) and 'yes' or 'no')) + ui.write('symlink: %s\n' % (util.checklink(path) and 'yes' or 'no')) + ui.write('case-sensitive: %s\n' % (util.checkcase('.debugfsinfo') + and 'yes' or 'no')) + os.unlink('.debugfsinfo') + +def debugrebuildstate(ui, repo, rev="tip"): + """rebuild the dirstate as it would look like for the given revision""" + ctx = repo[rev] + wlock = repo.wlock() + try: + repo.dirstate.rebuild(ctx.node(), ctx.manifest()) + finally: + wlock.release() + +def debugcheckstate(ui, repo): + """validate the correctness of the current dirstate""" + parent1, parent2 = repo.dirstate.parents() + m1 = repo[parent1].manifest() + m2 = repo[parent2].manifest() + errors = 0 + for f in repo.dirstate: + state = repo.dirstate[f] + if state in "nr" and f not in m1: + ui.warn(_("%s in state %s, but not in manifest1\n") % (f, state)) + errors += 1 + if state in "a" and f in m1: + ui.warn(_("%s in state %s, but also in manifest1\n") % (f, state)) + errors += 1 + if state in "m" and f not in m1 and f not in m2: + ui.warn(_("%s in state %s, but not in either manifest\n") % + (f, state)) + errors += 1 + for f in m1: + state = repo.dirstate[f] + if state not in "nrm": + ui.warn(_("%s in manifest1, but listed as state %s") % (f, state)) + errors += 1 + if errors: + error = _(".hg/dirstate inconsistent with current parent's manifest") + raise util.Abort(error) + +def showconfig(ui, repo, *values, **opts): + """show combined config settings from all hgrc files + + With no arguments, print names and values of all config items. + + With one argument of the form section.name, print just the value + of that config item. + + With multiple arguments, print names and values of all config + items with matching section names. + + With --debug, the source (filename and line number) is printed + for each config item. + + Returns 0 on success. + """ + + for f in util.rcpath(): + ui.debug(_('read config from: %s\n') % f) + untrusted = bool(opts.get('untrusted')) + if values: + sections = [v for v in values if '.' not in v] + items = [v for v in values if '.' in v] + if len(items) > 1 or items and sections: + raise util.Abort(_('only one config item permitted')) + for section, name, value in ui.walkconfig(untrusted=untrusted): + sectname = section + '.' + name + if values: + for v in values: + if v == section: + ui.debug('%s: ' % + ui.configsource(section, name, untrusted)) + ui.write('%s=%s\n' % (sectname, value)) + elif v == sectname: + ui.debug('%s: ' % + ui.configsource(section, name, untrusted)) + ui.write(value, '\n') + else: + ui.debug('%s: ' % + ui.configsource(section, name, untrusted)) + ui.write('%s=%s\n' % (sectname, value)) + +def debugpushkey(ui, repopath, namespace, *keyinfo): + '''access the pushkey key/value protocol + + With two args, list the keys in the given namespace. + + With five args, set a key to new if it currently is set to old. + Reports success or failure. + ''' + + target = hg.repository(ui, repopath) + if keyinfo: + key, old, new = keyinfo + r = target.pushkey(namespace, key, old, new) + ui.status(str(r) + '\n') + return not(r) + else: + for k, v in target.listkeys(namespace).iteritems(): + ui.write("%s\t%s\n" % (k.encode('string-escape'), + v.encode('string-escape'))) + +def debugrevspec(ui, repo, expr): + '''parse and apply a revision specification''' + if ui.verbose: + tree = revset.parse(expr) + ui.note(tree, "\n") + func = revset.match(expr) + for c in func(repo, range(len(repo))): + ui.write("%s\n" % c) + +def debugsetparents(ui, repo, rev1, rev2=None): + """manually set the parents of the current working directory + + This is useful for writing repository conversion tools, but should + be used with care. + + Returns 0 on success. + """ + + if not rev2: + rev2 = hex(nullid) + + wlock = repo.wlock() + try: + repo.dirstate.setparents(repo.lookup(rev1), repo.lookup(rev2)) + finally: + wlock.release() + +def debugstate(ui, repo, nodates=None): + """show the contents of the current dirstate""" + timestr = "" + showdate = not nodates + for file_, ent in sorted(repo.dirstate._map.iteritems()): + if showdate: + if ent[3] == -1: + # Pad or slice to locale representation + locale_len = len(time.strftime("%Y-%m-%d %H:%M:%S ", + time.localtime(0))) + timestr = 'unset' + timestr = (timestr[:locale_len] + + ' ' * (locale_len - len(timestr))) + else: + timestr = time.strftime("%Y-%m-%d %H:%M:%S ", + time.localtime(ent[3])) + if ent[1] & 020000: + mode = 'lnk' + else: + mode = '%3o' % (ent[1] & 0777) + ui.write("%c %s %10d %s%s\n" % (ent[0], mode, ent[2], timestr, file_)) + for f in repo.dirstate.copies(): + ui.write(_("copy: %s -> %s\n") % (repo.dirstate.copied(f), f)) + +def debugsub(ui, repo, rev=None): + if rev == '': + rev = None + for k, v in sorted(repo[rev].substate.items()): + ui.write('path %s\n' % k) + ui.write(' source %s\n' % v[0]) + ui.write(' revision %s\n' % v[1]) + +def debugdag(ui, repo, file_=None, *revs, **opts): + """format the changelog or an index DAG as a concise textual description + + If you pass a revlog index, the revlog's DAG is emitted. If you list + revision numbers, they get labelled in the output as rN. + + Otherwise, the changelog DAG of the current repo is emitted. + """ + spaces = opts.get('spaces') + dots = opts.get('dots') + if file_: + rlog = revlog.revlog(util.opener(os.getcwd(), audit=False), file_) + revs = set((int(r) for r in revs)) + def events(): + for r in rlog: + yield 'n', (r, list(set(p for p in rlog.parentrevs(r) if p != -1))) + if r in revs: + yield 'l', (r, "r%i" % r) + elif repo: + cl = repo.changelog + tags = opts.get('tags') + branches = opts.get('branches') + if tags: + labels = {} + for l, n in repo.tags().items(): + labels.setdefault(cl.rev(n), []).append(l) + def events(): + b = "default" + for r in cl: + if branches: + newb = cl.read(cl.node(r))[5]['branch'] + if newb != b: + yield 'a', newb + b = newb + yield 'n', (r, list(set(p for p in cl.parentrevs(r) if p != -1))) + if tags: + ls = labels.get(r) + if ls: + for l in ls: + yield 'l', (r, l) + else: + raise util.Abort(_('need repo for changelog dag')) + + for line in dagparser.dagtextlines(events(), + addspaces=spaces, + wraplabels=True, + wrapannotations=True, + wrapnonlinear=dots, + usedots=dots, + maxlinewidth=70): + ui.write(line) + ui.write("\n") + +def debugdata(ui, repo, file_, rev): + """dump the contents of a data file revision""" + r = None + if repo: + filelog = repo.file(file_) + if len(filelog): + r = filelog + if not r: + r = revlog.revlog(util.opener(os.getcwd(), audit=False), file_[:-2] + ".i") + try: + ui.write(r.revision(r.lookup(rev))) + except KeyError: + raise util.Abort(_('invalid revision identifier %s') % rev) + +def debugdate(ui, date, range=None, **opts): + """parse and display a date""" + if opts["extended"]: + d = util.parsedate(date, util.extendeddateformats) + else: + d = util.parsedate(date) + ui.write("internal: %s %s\n" % d) + ui.write("standard: %s\n" % util.datestr(d)) + if range: + m = util.matchdate(range) + ui.write("match: %s\n" % m(d[0])) + +def debugindex(ui, repo, file_, **opts): + """dump the contents of an index file""" + r = None + if repo: + filelog = repo.file(file_) + if len(filelog): + r = filelog + + format = opts.get('format', 0) + if format not in (0, 1): + raise util.Abort("unknown format %d" % format) + + if not r: + r = revlog.revlog(util.opener(os.getcwd(), audit=False), file_) + + if format == 0: + ui.write(" rev offset length base linkrev" + " nodeid p1 p2\n") + elif format == 1: + ui.write(" rev flag offset length" + " size base link p1 p2 nodeid\n") + + for i in r: + node = r.node(i) + if format == 0: + try: + pp = r.parents(node) + except: + pp = [nullid, nullid] + ui.write("% 6d % 9d % 7d % 6d % 7d %s %s %s\n" % ( + i, r.start(i), r.length(i), r.base(i), r.linkrev(i), + short(node), short(pp[0]), short(pp[1]))) + elif format == 1: + pr = r.parentrevs(i) + ui.write("% 6d %04x % 8d % 8d % 8d % 6d % 6d % 6d % 6d %s\n" % ( + i, r.flags(i), r.start(i), r.length(i), r.rawsize(i), + r.base(i), r.linkrev(i), pr[0], pr[1], short(node))) + +def debugindexdot(ui, repo, file_): + """dump an index DAG as a graphviz dot file""" + r = None + if repo: + filelog = repo.file(file_) + if len(filelog): + r = filelog + if not r: + r = revlog.revlog(util.opener(os.getcwd(), audit=False), file_) + ui.write("digraph G {\n") + for i in r: + node = r.node(i) + pp = r.parents(node) + ui.write("\t%d -> %d\n" % (r.rev(pp[0]), i)) + if pp[1] != nullid: + ui.write("\t%d -> %d\n" % (r.rev(pp[1]), i)) + ui.write("}\n") + +def debuginstall(ui): + '''test Mercurial installation + + Returns 0 on success. + ''' + + def writetemp(contents): + (fd, name) = tempfile.mkstemp(prefix="hg-debuginstall-") + f = os.fdopen(fd, "wb") + f.write(contents) + f.close() + return name + + problems = 0 + + # encoding + ui.status(_("Checking encoding (%s)...\n") % encoding.encoding) + try: + encoding.fromlocal("test") + except util.Abort, inst: + ui.write(" %s\n" % inst) + ui.write(_(" (check that your locale is properly set)\n")) + problems += 1 + + # compiled modules + ui.status(_("Checking installed modules (%s)...\n") + % os.path.dirname(__file__)) + try: + import bdiff, mpatch, base85, osutil + except Exception, inst: + ui.write(" %s\n" % inst) + ui.write(_(" One or more extensions could not be found")) + ui.write(_(" (check that you compiled the extensions)\n")) + problems += 1 + + # templates + ui.status(_("Checking templates...\n")) + try: + import templater + templater.templater(templater.templatepath("map-cmdline.default")) + except Exception, inst: + ui.write(" %s\n" % inst) + ui.write(_(" (templates seem to have been installed incorrectly)\n")) + problems += 1 + + # patch + ui.status(_("Checking patch...\n")) + patchproblems = 0 + a = "1\n2\n3\n4\n" + b = "1\n2\n3\ninsert\n4\n" + fa = writetemp(a) + d = mdiff.unidiff(a, None, b, None, os.path.basename(fa), + os.path.basename(fa)) + fd = writetemp(d) + + files = {} + try: + patch.patch(fd, ui, cwd=os.path.dirname(fa), files=files) + except util.Abort, e: + ui.write(_(" patch call failed:\n")) + ui.write(" " + str(e) + "\n") + patchproblems += 1 + else: + if list(files) != [os.path.basename(fa)]: + ui.write(_(" unexpected patch output!\n")) + patchproblems += 1 + a = open(fa).read() + if a != b: + ui.write(_(" patch test failed!\n")) + patchproblems += 1 + + if patchproblems: + if ui.config('ui', 'patch'): + ui.write(_(" (Current patch tool may be incompatible with patch," + " or misconfigured. Please check your configuration" + " file)\n")) + else: + ui.write(_(" Internal patcher failure, please report this error" + " to http://mercurial.selenic.com/wiki/BugTracker\n")) + problems += patchproblems + + os.unlink(fa) + os.unlink(fd) + + # editor + ui.status(_("Checking commit editor...\n")) + editor = ui.geteditor() + cmdpath = util.find_exe(editor) or util.find_exe(editor.split()[0]) + if not cmdpath: + if editor == 'vi': + ui.write(_(" No commit editor set and can't find vi in PATH\n")) + ui.write(_(" (specify a commit editor in your configuration" + " file)\n")) + else: + ui.write(_(" Can't find editor '%s' in PATH\n") % editor) + ui.write(_(" (specify a commit editor in your configuration" + " file)\n")) + problems += 1 + + # check username + ui.status(_("Checking username...\n")) + try: + ui.username() + except util.Abort, e: + ui.write(" %s\n" % e) + ui.write(_(" (specify a username in your configuration file)\n")) + problems += 1 + + if not problems: + ui.status(_("No problems detected\n")) + else: + ui.write(_("%s problems detected," + " please check your install!\n") % problems) + + return problems + +def debugrename(ui, repo, file1, *pats, **opts): + """dump rename information""" + + ctx = repo[opts.get('rev')] + m = cmdutil.match(repo, (file1,) + pats, opts) + for abs in ctx.walk(m): + fctx = ctx[abs] + o = fctx.filelog().renamed(fctx.filenode()) + rel = m.rel(abs) + if o: + ui.write(_("%s renamed from %s:%s\n") % (rel, o[0], hex(o[1]))) + else: + ui.write(_("%s not renamed\n") % rel) + +def debugwalk(ui, repo, *pats, **opts): + """show how files match on given patterns""" + m = cmdutil.match(repo, pats, opts) + items = list(repo.walk(m)) + if not items: + return + fmt = 'f %%-%ds %%-%ds %%s' % ( + max([len(abs) for abs in items]), + max([len(m.rel(abs)) for abs in items])) + for abs in items: + line = fmt % (abs, m.rel(abs), m.exact(abs) and 'exact' or '') + ui.write("%s\n" % line.rstrip()) + +def diff(ui, repo, *pats, **opts): + """diff repository (or selected files) + + Show differences between revisions for the specified files. + + Differences between files are shown using the unified diff format. + + .. note:: + diff may generate unexpected results for merges, as it will + default to comparing against the working directory's first + parent changeset if no revisions are specified. + + When two revision arguments are given, then changes are shown + between those revisions. If only one revision is specified then + that revision is compared to the working directory, and, when no + revisions are specified, the working directory files are compared + to its parent. + + Alternatively you can specify -c/--change with a revision to see + the changes in that changeset relative to its first parent. + + Without the -a/--text option, diff will avoid generating diffs of + files it detects as binary. With -a, diff will generate a diff + anyway, probably with undesirable results. + + Use the -g/--git option to generate diffs in the git extended diff + format. For more information, read :hg:`help diffs`. + + Returns 0 on success. + """ + + revs = opts.get('rev') + change = opts.get('change') + stat = opts.get('stat') + reverse = opts.get('reverse') + + if revs and change: + msg = _('cannot specify --rev and --change at the same time') + raise util.Abort(msg) + elif change: + node2 = repo.lookup(change) + node1 = repo[node2].parents()[0].node() + else: + node1, node2 = cmdutil.revpair(repo, revs) + + if reverse: + node1, node2 = node2, node1 + + diffopts = patch.diffopts(ui, opts) + m = cmdutil.match(repo, pats, opts) + cmdutil.diffordiffstat(ui, repo, diffopts, node1, node2, m, stat=stat, + listsubrepos=opts.get('subrepos')) + +def export(ui, repo, *changesets, **opts): + """dump the header and diffs for one or more changesets + + Print the changeset header and diffs for one or more revisions. + + The information shown in the changeset header is: author, date, + branch name (if non-default), changeset hash, parent(s) and commit + comment. + + .. note:: + export may generate unexpected diff output for merge + changesets, as it will compare the merge changeset against its + first parent only. + + Output may be to a file, in which case the name of the file is + given using a format string. The formatting rules are as follows: + + :``%%``: literal "%" character + :``%H``: changeset hash (40 hexadecimal digits) + :``%N``: number of patches being generated + :``%R``: changeset revision number + :``%b``: basename of the exporting repository + :``%h``: short-form changeset hash (12 hexadecimal digits) + :``%n``: zero-padded sequence number, starting at 1 + :``%r``: zero-padded changeset revision number + + Without the -a/--text option, export will avoid generating diffs + of files it detects as binary. With -a, export will generate a + diff anyway, probably with undesirable results. + + Use the -g/--git option to generate diffs in the git extended diff + format. See :hg:`help diffs` for more information. + + With the --switch-parent option, the diff will be against the + second parent. It can be useful to review a merge. + + Returns 0 on success. + """ + changesets += tuple(opts.get('rev', [])) + if not changesets: + raise util.Abort(_("export requires at least one changeset")) + revs = cmdutil.revrange(repo, changesets) + if len(revs) > 1: + ui.note(_('exporting patches:\n')) + else: + ui.note(_('exporting patch:\n')) + cmdutil.export(repo, revs, template=opts.get('output'), + switch_parent=opts.get('switch_parent'), + opts=patch.diffopts(ui, opts)) + +def forget(ui, repo, *pats, **opts): + """forget the specified files on the next commit + + Mark the specified files so they will no longer be tracked + after the next commit. + + This only removes files from the current branch, not from the + entire project history, and it does not delete them from the + working directory. + + To undo a forget before the next commit, see :hg:`add`. + + Returns 0 on success. + """ + + if not pats: + raise util.Abort(_('no files specified')) + + m = cmdutil.match(repo, pats, opts) + s = repo.status(match=m, clean=True) + forget = sorted(s[0] + s[1] + s[3] + s[6]) + errs = 0 + + for f in m.files(): + if f not in repo.dirstate and not os.path.isdir(m.rel(f)): + ui.warn(_('not removing %s: file is already untracked\n') + % m.rel(f)) + errs = 1 + + for f in forget: + if ui.verbose or not m.exact(f): + ui.status(_('removing %s\n') % m.rel(f)) + + repo[None].remove(forget, unlink=False) + return errs + +def grep(ui, repo, pattern, *pats, **opts): + """search for a pattern in specified files and revisions + + Search revisions of files for a regular expression. + + This command behaves differently than Unix grep. It only accepts + Python/Perl regexps. It searches repository history, not the + working directory. It always prints the revision number in which a + match appears. + + By default, grep only prints output for the first revision of a + file in which it finds a match. To get it to print every revision + that contains a change in match status ("-" for a match that + becomes a non-match, or "+" for a non-match that becomes a match), + use the --all flag. + + Returns 0 if a match is found, 1 otherwise. + """ + reflags = 0 + if opts.get('ignore_case'): + reflags |= re.I + try: + regexp = re.compile(pattern, reflags) + except re.error, inst: + ui.warn(_("grep: invalid match pattern: %s\n") % inst) + return 1 + sep, eol = ':', '\n' + if opts.get('print0'): + sep = eol = '\0' + + getfile = util.lrucachefunc(repo.file) + + def matchlines(body): + begin = 0 + linenum = 0 + while True: + match = regexp.search(body, begin) + if not match: + break + mstart, mend = match.span() + linenum += body.count('\n', begin, mstart) + 1 + lstart = body.rfind('\n', begin, mstart) + 1 or begin + begin = body.find('\n', mend) + 1 or len(body) + lend = begin - 1 + yield linenum, mstart - lstart, mend - lstart, body[lstart:lend] + + class linestate(object): + def __init__(self, line, linenum, colstart, colend): + self.line = line + self.linenum = linenum + self.colstart = colstart + self.colend = colend + + def __hash__(self): + return hash((self.linenum, self.line)) + + def __eq__(self, other): + return self.line == other.line + + matches = {} + copies = {} + def grepbody(fn, rev, body): + matches[rev].setdefault(fn, []) + m = matches[rev][fn] + for lnum, cstart, cend, line in matchlines(body): + s = linestate(line, lnum, cstart, cend) + m.append(s) + + def difflinestates(a, b): + sm = difflib.SequenceMatcher(None, a, b) + for tag, alo, ahi, blo, bhi in sm.get_opcodes(): + if tag == 'insert': + for i in xrange(blo, bhi): + yield ('+', b[i]) + elif tag == 'delete': + for i in xrange(alo, ahi): + yield ('-', a[i]) + elif tag == 'replace': + for i in xrange(alo, ahi): + yield ('-', a[i]) + for i in xrange(blo, bhi): + yield ('+', b[i]) + + def display(fn, ctx, pstates, states): + rev = ctx.rev() + datefunc = ui.quiet and util.shortdate or util.datestr + found = False + filerevmatches = {} + if opts.get('all'): + iter = difflinestates(pstates, states) + else: + iter = [('', l) for l in states] + for change, l in iter: + cols = [fn, str(rev)] + before, match, after = None, None, None + if opts.get('line_number'): + cols.append(str(l.linenum)) + if opts.get('all'): + cols.append(change) + if opts.get('user'): + cols.append(ui.shortuser(ctx.user())) + if opts.get('date'): + cols.append(datefunc(ctx.date())) + if opts.get('files_with_matches'): + c = (fn, rev) + if c in filerevmatches: + continue + filerevmatches[c] = 1 + else: + before = l.line[:l.colstart] + match = l.line[l.colstart:l.colend] + after = l.line[l.colend:] + ui.write(sep.join(cols)) + if before is not None: + ui.write(sep + before) + ui.write(match, label='grep.match') + ui.write(after) + ui.write(eol) + found = True + return found + + skip = {} + revfiles = {} + matchfn = cmdutil.match(repo, pats, opts) + found = False + follow = opts.get('follow') + + def prep(ctx, fns): + rev = ctx.rev() + pctx = ctx.parents()[0] + parent = pctx.rev() + matches.setdefault(rev, {}) + matches.setdefault(parent, {}) + files = revfiles.setdefault(rev, []) + for fn in fns: + flog = getfile(fn) + try: + fnode = ctx.filenode(fn) + except error.LookupError: + continue + + copied = flog.renamed(fnode) + copy = follow and copied and copied[0] + if copy: + copies.setdefault(rev, {})[fn] = copy + if fn in skip: + if copy: + skip[copy] = True + continue + files.append(fn) + + if fn not in matches[rev]: + grepbody(fn, rev, flog.read(fnode)) + + pfn = copy or fn + if pfn not in matches[parent]: + try: + fnode = pctx.filenode(pfn) + grepbody(pfn, parent, flog.read(fnode)) + except error.LookupError: + pass + + for ctx in cmdutil.walkchangerevs(repo, matchfn, opts, prep): + rev = ctx.rev() + parent = ctx.parents()[0].rev() + for fn in sorted(revfiles.get(rev, [])): + states = matches[rev][fn] + copy = copies.get(rev, {}).get(fn) + if fn in skip: + if copy: + skip[copy] = True + continue + pstates = matches.get(parent, {}).get(copy or fn, []) + if pstates or states: + r = display(fn, ctx, pstates, states) + found = found or r + if r and not opts.get('all'): + skip[fn] = True + if copy: + skip[copy] = True + del matches[rev] + del revfiles[rev] + + return not found + +def heads(ui, repo, *branchrevs, **opts): + """show current repository heads or show branch heads + + With no arguments, show all repository branch heads. + + Repository "heads" are changesets with no child changesets. They are + where development generally takes place and are the usual targets + for update and merge operations. Branch heads are changesets that have + no child changeset on the same branch. + + If one or more REVs are given, only branch heads on the branches + associated with the specified changesets are shown. + + If -c/--closed is specified, also show branch heads marked closed + (see :hg:`commit --close-branch`). + + If STARTREV is specified, only those heads that are descendants of + STARTREV will be displayed. + + If -t/--topo is specified, named branch mechanics will be ignored and only + changesets without children will be shown. + + Returns 0 if matching heads are found, 1 if not. + """ + + if opts.get('rev'): + start = repo.lookup(opts['rev']) + else: + start = None + + if opts.get('topo'): + heads = [repo[h] for h in repo.heads(start)] + else: + heads = [] + for b, ls in repo.branchmap().iteritems(): + if start is None: + heads += [repo[h] for h in ls] + continue + startrev = repo.changelog.rev(start) + descendants = set(repo.changelog.descendants(startrev)) + descendants.add(startrev) + rev = repo.changelog.rev + heads += [repo[h] for h in ls if rev(h) in descendants] + + if branchrevs: + decode, encode = encoding.fromlocal, encoding.tolocal + branches = set(repo[decode(br)].branch() for br in branchrevs) + heads = [h for h in heads if h.branch() in branches] + + if not opts.get('closed'): + heads = [h for h in heads if not h.extra().get('close')] + + if opts.get('active') and branchrevs: + dagheads = repo.heads(start) + heads = [h for h in heads if h.node() in dagheads] + + if branchrevs: + haveheads = set(h.branch() for h in heads) + if branches - haveheads: + headless = ', '.join(encode(b) for b in branches - haveheads) + msg = _('no open branch heads found on branches %s') + if opts.get('rev'): + msg += _(' (started at %s)' % opts['rev']) + ui.warn((msg + '\n') % headless) + + if not heads: + return 1 + + heads = sorted(heads, key=lambda x: -x.rev()) + displayer = cmdutil.show_changeset(ui, repo, opts) + for ctx in heads: + displayer.show(ctx) + displayer.close() + +def help_(ui, name=None, with_version=False, unknowncmd=False): + """show help for a given topic or a help overview + + With no arguments, print a list of commands with short help messages. + + Given a topic, extension, or command name, print help for that + topic. + + Returns 0 if successful. + """ + option_lists = [] + textwidth = ui.termwidth() - 2 + + def addglobalopts(aliases): + if ui.verbose: + option_lists.append((_("global options:"), globalopts)) + if name == 'shortlist': + option_lists.append((_('use "hg help" for the full list ' + 'of commands'), ())) + else: + if name == 'shortlist': + msg = _('use "hg help" for the full list of commands ' + 'or "hg -v" for details') + elif aliases: + msg = _('use "hg -v help%s" to show aliases and ' + 'global options') % (name and " " + name or "") + else: + msg = _('use "hg -v help %s" to show global options') % name + option_lists.append((msg, ())) + + def helpcmd(name): + if with_version: + version_(ui) + ui.write('\n') + + try: + aliases, entry = cmdutil.findcmd(name, table, strict=unknowncmd) + except error.AmbiguousCommand, inst: + # py3k fix: except vars can't be used outside the scope of the + # except block, nor can be used inside a lambda. python issue4617 + prefix = inst.args[0] + select = lambda c: c.lstrip('^').startswith(prefix) + helplist(_('list of commands:\n\n'), select) + return + + # check if it's an invalid alias and display its error if it is + if getattr(entry[0], 'badalias', False): + if not unknowncmd: + entry[0](ui) + return + + # synopsis + if len(entry) > 2: + if entry[2].startswith('hg'): + ui.write("%s\n" % entry[2]) + else: + ui.write('hg %s %s\n' % (aliases[0], entry[2])) + else: + ui.write('hg %s\n' % aliases[0]) + + # aliases + if not ui.quiet and len(aliases) > 1: + ui.write(_("\naliases: %s\n") % ', '.join(aliases[1:])) + + # description + doc = gettext(entry[0].__doc__) + if not doc: + doc = _("(no help text available)") + if hasattr(entry[0], 'definition'): # aliased command + if entry[0].definition.startswith('!'): # shell alias + doc = _('shell alias for::\n\n %s') % entry[0].definition[1:] + else: + doc = _('alias for: hg %s\n\n%s') % (entry[0].definition, doc) + if ui.quiet: + doc = doc.splitlines()[0] + keep = ui.verbose and ['verbose'] or [] + formatted, pruned = minirst.format(doc, textwidth, keep=keep) + ui.write("\n%s\n" % formatted) + if pruned: + ui.write(_('\nuse "hg -v help %s" to show verbose help\n') % name) + + if not ui.quiet: + # options + if entry[1]: + option_lists.append((_("options:\n"), entry[1])) + + addglobalopts(False) + + def helplist(header, select=None): + h = {} + cmds = {} + for c, e in table.iteritems(): + f = c.split("|", 1)[0] + if select and not select(f): + continue + if (not select and name != 'shortlist' and + e[0].__module__ != __name__): + continue + if name == "shortlist" and not f.startswith("^"): + continue + f = f.lstrip("^") + if not ui.debugflag and f.startswith("debug"): + continue + doc = e[0].__doc__ + if doc and 'DEPRECATED' in doc and not ui.verbose: + continue + doc = gettext(doc) + if not doc: + doc = _("(no help text available)") + h[f] = doc.splitlines()[0].rstrip() + cmds[f] = c.lstrip("^") + + if not h: + ui.status(_('no commands defined\n')) + return + + ui.status(header) + fns = sorted(h) + m = max(map(len, fns)) + for f in fns: + if ui.verbose: + commands = cmds[f].replace("|",", ") + ui.write(" %s:\n %s\n"%(commands, h[f])) + else: + ui.write('%s\n' % (util.wrap(h[f], textwidth, + initindent=' %-*s ' % (m, f), + hangindent=' ' * (m + 4)))) + + if not ui.quiet: + addglobalopts(True) + + def helptopic(name): + for names, header, doc in help.helptable: + if name in names: + break + else: + raise error.UnknownCommand(name) + + # description + if not doc: + doc = _("(no help text available)") + if hasattr(doc, '__call__'): + doc = doc() + + ui.write("%s\n\n" % header) + ui.write("%s\n" % minirst.format(doc, textwidth, indent=4)) + + def helpext(name): + try: + mod = extensions.find(name) + doc = gettext(mod.__doc__) or _('no help text available') + except KeyError: + mod = None + doc = extensions.disabledext(name) + if not doc: + raise error.UnknownCommand(name) + + if '\n' not in doc: + head, tail = doc, "" + else: + head, tail = doc.split('\n', 1) + ui.write(_('%s extension - %s\n\n') % (name.split('.')[-1], head)) + if tail: + ui.write(minirst.format(tail, textwidth)) + ui.status('\n\n') + + if mod: + try: + ct = mod.cmdtable + except AttributeError: + ct = {} + modcmds = set([c.split('|', 1)[0] for c in ct]) + helplist(_('list of commands:\n\n'), modcmds.__contains__) + else: + ui.write(_('use "hg help extensions" for information on enabling ' + 'extensions\n')) + + def helpextcmd(name): + cmd, ext, mod = extensions.disabledcmd(name, ui.config('ui', 'strict')) + doc = gettext(mod.__doc__).splitlines()[0] + + msg = help.listexts(_("'%s' is provided by the following " + "extension:") % cmd, {ext: doc}, len(ext), + indent=4) + ui.write(minirst.format(msg, textwidth)) + ui.write('\n\n') + ui.write(_('use "hg help extensions" for information on enabling ' + 'extensions\n')) + + help.addtopichook('revsets', revset.makedoc) + + if name and name != 'shortlist': + i = None + if unknowncmd: + queries = (helpextcmd,) + else: + queries = (helptopic, helpcmd, helpext, helpextcmd) + for f in queries: + try: + f(name) + i = None + break + except error.UnknownCommand, inst: + i = inst + if i: + raise i + + else: + # program name + if ui.verbose or with_version: + version_(ui) + else: + ui.status(_("Mercurial Distributed SCM\n")) + ui.status('\n') + + # list of commands + if name == "shortlist": + header = _('basic commands:\n\n') + else: + header = _('list of commands:\n\n') + + helplist(header) + if name != 'shortlist': + exts, maxlength = extensions.enabled() + text = help.listexts(_('enabled extensions:'), exts, maxlength) + if text: + ui.write("\n%s\n" % minirst.format(text, textwidth)) + + # list all option lists + opt_output = [] + multioccur = False + for title, options in option_lists: + opt_output.append(("\n%s" % title, None)) + for option in options: + if len(option) == 5: + shortopt, longopt, default, desc, optlabel = option + else: + shortopt, longopt, default, desc = option + optlabel = _("VALUE") # default label + + if _("DEPRECATED") in desc and not ui.verbose: + continue + if isinstance(default, list): + numqualifier = " %s [+]" % optlabel + multioccur = True + elif (default is not None) and not isinstance(default, bool): + numqualifier = " %s" % optlabel + else: + numqualifier = "" + opt_output.append(("%2s%s" % + (shortopt and "-%s" % shortopt, + longopt and " --%s%s" % + (longopt, numqualifier)), + "%s%s" % (desc, + default + and _(" (default: %s)") % default + or ""))) + if multioccur: + msg = _("\n[+] marked option can be specified multiple times") + if ui.verbose and name != 'shortlist': + opt_output.append((msg, None)) + else: + opt_output.insert(-1, (msg, None)) + + if not name: + ui.write(_("\nadditional help topics:\n\n")) + topics = [] + for names, header, doc in help.helptable: + topics.append((sorted(names, key=len, reverse=True)[0], header)) + topics_len = max([len(s[0]) for s in topics]) + for t, desc in topics: + ui.write(" %-*s %s\n" % (topics_len, t, desc)) + + if opt_output: + colwidth = encoding.colwidth + # normalize: (opt or message, desc or None, width of opt) + entries = [desc and (opt, desc, colwidth(opt)) or (opt, None, 0) + for opt, desc in opt_output] + hanging = max([e[2] for e in entries]) + for opt, desc, width in entries: + if desc: + initindent = ' %s%s ' % (opt, ' ' * (hanging - width)) + hangindent = ' ' * (hanging + 3) + ui.write('%s\n' % (util.wrap(desc, textwidth, + initindent=initindent, + hangindent=hangindent))) + else: + ui.write("%s\n" % opt) + +def identify(ui, repo, source=None, + rev=None, num=None, id=None, branch=None, tags=None): + """identify the working copy or specified revision + + With no revision, print a summary of the current state of the + repository. + + Specifying a path to a repository root or Mercurial bundle will + cause lookup to operate on that repository/bundle. + + This summary identifies the repository state using one or two + parent hash identifiers, followed by a "+" if there are + uncommitted changes in the working directory, a list of tags for + this revision and a branch name for non-default branches. + + Returns 0 if successful. + """ + + if not repo and not source: + raise util.Abort(_("there is no Mercurial repository here " + "(.hg not found)")) + + hexfunc = ui.debugflag and hex or short + default = not (num or id or branch or tags) + output = [] + + revs = [] + if source: + source, branches = hg.parseurl(ui.expandpath(source)) + repo = hg.repository(ui, source) + revs, checkout = hg.addbranchrevs(repo, repo, branches, None) + + if not repo.local(): + if not rev and revs: + rev = revs[0] + if not rev: + rev = "tip" + if num or branch or tags: + raise util.Abort( + "can't query remote revision number, branch, or tags") + output = [hexfunc(repo.lookup(rev))] + elif not rev: + ctx = repo[None] + parents = ctx.parents() + changed = False + if default or id or num: + changed = util.any(repo.status()) + if default or id: + output = ["%s%s" % ('+'.join([hexfunc(p.node()) for p in parents]), + (changed) and "+" or "")] + if num: + output.append("%s%s" % ('+'.join([str(p.rev()) for p in parents]), + (changed) and "+" or "")) + else: + ctx = repo[rev] + if default or id: + output = [hexfunc(ctx.node())] + if num: + output.append(str(ctx.rev())) + + if repo.local() and default and not ui.quiet: + b = encoding.tolocal(ctx.branch()) + if b != 'default': + output.append("(%s)" % b) + + # multiple tags for a single parent separated by '/' + t = "/".join(ctx.tags()) + if t: + output.append(t) + + if branch: + output.append(encoding.tolocal(ctx.branch())) + + if tags: + output.extend(ctx.tags()) + + ui.write("%s\n" % ' '.join(output)) + +def import_(ui, repo, patch1, *patches, **opts): + """import an ordered set of patches + + Import a list of patches and commit them individually (unless + --no-commit is specified). + + If there are outstanding changes in the working directory, import + will abort unless given the -f/--force flag. + + You can import a patch straight from a mail message. Even patches + as attachments work (to use the body part, it must have type + text/plain or text/x-patch). From and Subject headers of email + message are used as default committer and commit message. All + text/plain body parts before first diff are added to commit + message. + + If the imported patch was generated by :hg:`export`, user and + description from patch override values from message headers and + body. Values given on command line with -m/--message and -u/--user + override these. + + If --exact is specified, import will set the working directory to + the parent of each patch before applying it, and will abort if the + resulting changeset has a different ID than the one recorded in + the patch. This may happen due to character set problems or other + deficiencies in the text patch format. + + With -s/--similarity, hg will attempt to discover renames and + copies in the patch in the same way as 'addremove'. + + To read a patch from standard input, use "-" as the patch name. If + a URL is specified, the patch will be downloaded from it. + See :hg:`help dates` for a list of formats valid for -d/--date. + + Returns 0 on success. + """ + patches = (patch1,) + patches + + date = opts.get('date') + if date: + opts['date'] = util.parsedate(date) + + try: + sim = float(opts.get('similarity') or 0) + except ValueError: + raise util.Abort(_('similarity must be a number')) + if sim < 0 or sim > 100: + raise util.Abort(_('similarity must be between 0 and 100')) + + if opts.get('exact') or not opts.get('force'): + cmdutil.bail_if_changed(repo) + + d = opts["base"] + strip = opts["strip"] + wlock = lock = None + + def tryone(ui, hunk): + tmpname, message, user, date, branch, nodeid, p1, p2 = \ + patch.extract(ui, hunk) + + if not tmpname: + return None + commitid = _('to working directory') + + try: + cmdline_message = cmdutil.logmessage(opts) + if cmdline_message: + # pickup the cmdline msg + message = cmdline_message + elif message: + # pickup the patch msg + message = message.strip() + else: + # launch the editor + message = None + ui.debug('message:\n%s\n' % message) + + wp = repo.parents() + if opts.get('exact'): + if not nodeid or not p1: + raise util.Abort(_('not a Mercurial patch')) + p1 = repo.lookup(p1) + p2 = repo.lookup(p2 or hex(nullid)) + + if p1 != wp[0].node(): + hg.clean(repo, p1) + repo.dirstate.setparents(p1, p2) + elif p2: + try: + p1 = repo.lookup(p1) + p2 = repo.lookup(p2) + if p1 == wp[0].node(): + repo.dirstate.setparents(p1, p2) + except error.RepoError: + pass + if opts.get('exact') or opts.get('import_branch'): + repo.dirstate.setbranch(branch or 'default') + + files = {} + try: + patch.patch(tmpname, ui, strip=strip, cwd=repo.root, + files=files, eolmode=None) + finally: + files = cmdutil.updatedir(ui, repo, files, + similarity=sim / 100.0) + if not opts.get('no_commit'): + if opts.get('exact'): + m = None + else: + m = cmdutil.matchfiles(repo, files or []) + n = repo.commit(message, opts.get('user') or user, + opts.get('date') or date, match=m, + editor=cmdutil.commiteditor) + if opts.get('exact'): + if hex(n) != nodeid: + repo.rollback() + raise util.Abort(_('patch is damaged' + ' or loses information')) + # Force a dirstate write so that the next transaction + # backups an up-do-date file. + repo.dirstate.write() + if n: + commitid = short(n) + + return commitid + finally: + os.unlink(tmpname) + + try: + wlock = repo.wlock() + lock = repo.lock() + lastcommit = None + for p in patches: + pf = os.path.join(d, p) + + if pf == '-': + ui.status(_("applying patch from stdin\n")) + pf = sys.stdin + else: + ui.status(_("applying %s\n") % p) + pf = url.open(ui, pf) + + haspatch = False + for hunk in patch.split(pf): + commitid = tryone(ui, hunk) + if commitid: + haspatch = True + if lastcommit: + ui.status(_('applied %s\n') % lastcommit) + lastcommit = commitid + + if not haspatch: + raise util.Abort(_('no diffs found')) + + finally: + release(lock, wlock) + +def incoming(ui, repo, source="default", **opts): + """show new changesets found in source + + Show new changesets found in the specified path/URL or the default + pull location. These are the changesets that would have been pulled + if a pull at the time you issued this command. + + For remote repository, using --bundle avoids downloading the + changesets twice if the incoming is followed by a pull. + + See pull for valid source format details. + + Returns 0 if there are incoming changes, 1 otherwise. + """ + if opts.get('bundle') and opts.get('subrepos'): + raise util.Abort(_('cannot combine --bundle and --subrepos')) + + ret = hg.incoming(ui, repo, source, opts) + return ret + +def init(ui, dest=".", **opts): + """create a new repository in the given directory + + Initialize a new repository in the given directory. If the given + directory does not exist, it will be created. + + If no directory is given, the current directory is used. + + It is possible to specify an ``ssh://`` URL as the destination. + See :hg:`help urls` for more information. + + Returns 0 on success. + """ + hg.repository(hg.remoteui(ui, opts), ui.expandpath(dest), create=1) + +def locate(ui, repo, *pats, **opts): + """locate files matching specific patterns + + Print files under Mercurial control in the working directory whose + names match the given patterns. + + By default, this command searches all directories in the working + directory. To search just the current directory and its + subdirectories, use "--include .". + + If no patterns are given to match, this command prints the names + of all files under Mercurial control in the working directory. + + If you want to feed the output of this command into the "xargs" + command, use the -0 option to both this command and "xargs". This + will avoid the problem of "xargs" treating single filenames that + contain whitespace as multiple filenames. + + Returns 0 if a match is found, 1 otherwise. + """ + end = opts.get('print0') and '\0' or '\n' + rev = opts.get('rev') or None + + ret = 1 + m = cmdutil.match(repo, pats, opts, default='relglob') + m.bad = lambda x, y: False + for abs in repo[rev].walk(m): + if not rev and abs not in repo.dirstate: + continue + if opts.get('fullpath'): + ui.write(repo.wjoin(abs), end) + else: + ui.write(((pats and m.rel(abs)) or abs), end) + ret = 0 + + return ret + +def log(ui, repo, *pats, **opts): + """show revision history of entire repository or files + + Print the revision history of the specified files or the entire + project. + + File history is shown without following rename or copy history of + files. Use -f/--follow with a filename to follow history across + renames and copies. --follow without a filename will only show + ancestors or descendants of the starting revision. --follow-first + only follows the first parent of merge revisions. + + If no revision range is specified, the default is ``tip:0`` unless + --follow is set, in which case the working directory parent is + used as the starting revision. You can specify a revision set for + log, see :hg:`help revsets` for more information. + + See :hg:`help dates` for a list of formats valid for -d/--date. + + By default this command prints revision number and changeset id, + tags, non-trivial parents, user, date and time, and a summary for + each commit. When the -v/--verbose switch is used, the list of + changed files and full commit message are shown. + + .. note:: + log -p/--patch may generate unexpected diff output for merge + changesets, as it will only compare the merge changeset against + its first parent. Also, only files different from BOTH parents + will appear in files:. + + Returns 0 on success. + """ + + matchfn = cmdutil.match(repo, pats, opts) + limit = cmdutil.loglimit(opts) + count = 0 + + endrev = None + if opts.get('copies') and opts.get('rev'): + endrev = max(cmdutil.revrange(repo, opts.get('rev'))) + 1 + + df = False + if opts["date"]: + df = util.matchdate(opts["date"]) + + branches = opts.get('branch', []) + opts.get('only_branch', []) + opts['branch'] = [repo.lookupbranch(b) for b in branches] + + displayer = cmdutil.show_changeset(ui, repo, opts, True) + def prep(ctx, fns): + rev = ctx.rev() + parents = [p for p in repo.changelog.parentrevs(rev) + if p != nullrev] + if opts.get('no_merges') and len(parents) == 2: + return + if opts.get('only_merges') and len(parents) != 2: + return + if opts.get('branch') and ctx.branch() not in opts['branch']: + return + if df and not df(ctx.date()[0]): + return + if opts['user'] and not [k for k in opts['user'] + if k.lower() in ctx.user().lower()]: + return + if opts.get('keyword'): + for k in [kw.lower() for kw in opts['keyword']]: + if (k in ctx.user().lower() or + k in ctx.description().lower() or + k in " ".join(ctx.files()).lower()): + break + else: + return + + copies = None + if opts.get('copies') and rev: + copies = [] + getrenamed = templatekw.getrenamedfn(repo, endrev=endrev) + for fn in ctx.files(): + rename = getrenamed(fn, rev) + if rename: + copies.append((fn, rename[0])) + + revmatchfn = None + if opts.get('patch') or opts.get('stat'): + if opts.get('follow') or opts.get('follow_first'): + # note: this might be wrong when following through merges + revmatchfn = cmdutil.match(repo, fns, default='path') + else: + revmatchfn = matchfn + + displayer.show(ctx, copies=copies, matchfn=revmatchfn) + + for ctx in cmdutil.walkchangerevs(repo, matchfn, opts, prep): + if count == limit: + break + if displayer.flush(ctx.rev()): + count += 1 + displayer.close() + +def manifest(ui, repo, node=None, rev=None): + """output the current or given revision of the project manifest + + Print a list of version controlled files for the given revision. + If no revision is given, the first parent of the working directory + is used, or the null revision if no revision is checked out. + + With -v, print file permissions, symlink and executable bits. + With --debug, print file revision hashes. + + Returns 0 on success. + """ + + if rev and node: + raise util.Abort(_("please specify just one revision")) + + if not node: + node = rev + + decor = {'l':'644 @ ', 'x':'755 * ', '':'644 '} + ctx = repo[node] + for f in ctx: + if ui.debugflag: + ui.write("%40s " % hex(ctx.manifest()[f])) + if ui.verbose: + ui.write(decor[ctx.flags(f)]) + ui.write("%s\n" % f) + +def merge(ui, repo, node=None, **opts): + """merge working directory with another revision + + The current working directory is updated with all changes made in + the requested revision since the last common predecessor revision. + + Files that changed between either parent are marked as changed for + the next commit and a commit must be performed before any further + updates to the repository are allowed. The next commit will have + two parents. + + ``--tool`` can be used to specify the merge tool used for file + merges. It overrides the HGMERGE environment variable and your + configuration files. + + If no revision is specified, the working directory's parent is a + head revision, and the current branch contains exactly one other + head, the other head is merged with by default. Otherwise, an + explicit revision with which to merge with must be provided. + + :hg:`resolve` must be used to resolve unresolved files. + + To undo an uncommitted merge, use :hg:`update --clean .` which + will check out a clean copy of the original merge parent, losing + all changes. + + Returns 0 on success, 1 if there are unresolved files. + """ + + if opts.get('rev') and node: + raise util.Abort(_("please specify just one revision")) + if not node: + node = opts.get('rev') + + if not node: + branch = repo.changectx(None).branch() + bheads = repo.branchheads(branch) + if len(bheads) > 2: + raise util.Abort(_( + 'branch \'%s\' has %d heads - ' + 'please merge with an explicit rev\n' + '(run \'hg heads .\' to see heads)') + % (branch, len(bheads))) + + parent = repo.dirstate.parents()[0] + if len(bheads) == 1: + if len(repo.heads()) > 1: + raise util.Abort(_( + 'branch \'%s\' has one head - ' + 'please merge with an explicit rev\n' + '(run \'hg heads\' to see all heads)') + % branch) + msg = _('there is nothing to merge') + if parent != repo.lookup(repo[None].branch()): + msg = _('%s - use "hg update" instead') % msg + raise util.Abort(msg) + + if parent not in bheads: + raise util.Abort(_('working dir not at a head rev - ' + 'use "hg update" or merge with an explicit rev')) + node = parent == bheads[0] and bheads[-1] or bheads[0] + + if opts.get('preview'): + # find nodes that are ancestors of p2 but not of p1 + p1 = repo.lookup('.') + p2 = repo.lookup(node) + nodes = repo.changelog.findmissing(common=[p1], heads=[p2]) + + displayer = cmdutil.show_changeset(ui, repo, opts) + for node in nodes: + displayer.show(repo[node]) + displayer.close() + return 0 + + try: + # ui.forcemerge is an internal variable, do not document + ui.setconfig('ui', 'forcemerge', opts.get('tool', '')) + return hg.merge(repo, node, force=opts.get('force')) + finally: + ui.setconfig('ui', 'forcemerge', '') + +def outgoing(ui, repo, dest=None, **opts): + """show changesets not found in the destination + + Show changesets not found in the specified destination repository + or the default push location. These are the changesets that would + be pushed if a push was requested. + + See pull for details of valid destination formats. + + Returns 0 if there are outgoing changes, 1 otherwise. + """ + ret = hg.outgoing(ui, repo, dest, opts) + return ret + +def parents(ui, repo, file_=None, **opts): + """show the parents of the working directory or revision + + Print the working directory's parent revisions. If a revision is + given via -r/--rev, the parent of that revision will be printed. + If a file argument is given, the revision in which the file was + last changed (before the working directory revision or the + argument to --rev if given) is printed. + + Returns 0 on success. + """ + rev = opts.get('rev') + if rev: + ctx = repo[rev] + else: + ctx = repo[None] + + if file_: + m = cmdutil.match(repo, (file_,), opts) + if m.anypats() or len(m.files()) != 1: + raise util.Abort(_('can only specify an explicit filename')) + file_ = m.files()[0] + filenodes = [] + for cp in ctx.parents(): + if not cp: + continue + try: + filenodes.append(cp.filenode(file_)) + except error.LookupError: + pass + if not filenodes: + raise util.Abort(_("'%s' not found in manifest!") % file_) + fl = repo.file(file_) + p = [repo.lookup(fl.linkrev(fl.rev(fn))) for fn in filenodes] + else: + p = [cp.node() for cp in ctx.parents()] + + displayer = cmdutil.show_changeset(ui, repo, opts) + for n in p: + if n != nullid: + displayer.show(repo[n]) + displayer.close() + +def paths(ui, repo, search=None): + """show aliases for remote repositories + + Show definition of symbolic path name NAME. If no name is given, + show definition of all available names. + + Path names are defined in the [paths] section of your + configuration file and in ``/etc/mercurial/hgrc``. If run inside a + repository, ``.hg/hgrc`` is used, too. + + The path names ``default`` and ``default-push`` have a special + meaning. When performing a push or pull operation, they are used + as fallbacks if no location is specified on the command-line. + When ``default-push`` is set, it will be used for push and + ``default`` will be used for pull; otherwise ``default`` is used + as the fallback for both. When cloning a repository, the clone + source is written as ``default`` in ``.hg/hgrc``. Note that + ``default`` and ``default-push`` apply to all inbound (e.g. + :hg:`incoming`) and outbound (e.g. :hg:`outgoing`, :hg:`email` and + :hg:`bundle`) operations. + + See :hg:`help urls` for more information. + + Returns 0 on success. + """ + if search: + for name, path in ui.configitems("paths"): + if name == search: + ui.write("%s\n" % url.hidepassword(path)) + return + ui.warn(_("not found!\n")) + return 1 + else: + for name, path in ui.configitems("paths"): + ui.write("%s = %s\n" % (name, url.hidepassword(path))) + +def postincoming(ui, repo, modheads, optupdate, checkout): + if modheads == 0: + return + if optupdate: + if (modheads <= 1 or len(repo.branchheads()) == 1) or checkout: + return hg.update(repo, checkout) + else: + ui.status(_("not updating, since new heads added\n")) + if modheads > 1: + ui.status(_("(run 'hg heads' to see heads, 'hg merge' to merge)\n")) + else: + ui.status(_("(run 'hg update' to get a working copy)\n")) + +def pull(ui, repo, source="default", **opts): + """pull changes from the specified source + + Pull changes from a remote repository to a local one. + + This finds all changes from the repository at the specified path + or URL and adds them to a local repository (the current one unless + -R is specified). By default, this does not update the copy of the + project in the working directory. + + Use :hg:`incoming` if you want to see what would have been added + by a pull at the time you issued this command. If you then decide + to add those changes to the repository, you should use :hg:`pull + -r X` where ``X`` is the last changeset listed by :hg:`incoming`. + + If SOURCE is omitted, the 'default' path will be used. + See :hg:`help urls` for more information. + + Returns 0 on success, 1 if an update had unresolved files. + """ + source, branches = hg.parseurl(ui.expandpath(source), opts.get('branch')) + other = hg.repository(hg.remoteui(repo, opts), source) + ui.status(_('pulling from %s\n') % url.hidepassword(source)) + revs, checkout = hg.addbranchrevs(repo, other, branches, opts.get('rev')) + if revs: + try: + revs = [other.lookup(rev) for rev in revs] + except error.CapabilityError: + err = _("other repository doesn't support revision lookup, " + "so a rev cannot be specified.") + raise util.Abort(err) + + modheads = repo.pull(other, heads=revs, force=opts.get('force')) + if checkout: + checkout = str(repo.changelog.rev(other.lookup(checkout))) + repo._subtoppath = source + try: + return postincoming(ui, repo, modheads, opts.get('update'), checkout) + finally: + del repo._subtoppath + +def push(ui, repo, dest=None, **opts): + """push changes to the specified destination + + Push changesets from the local repository to the specified + destination. + + This operation is symmetrical to pull: it is identical to a pull + in the destination repository from the current one. + + By default, push will not allow creation of new heads at the + destination, since multiple heads would make it unclear which head + to use. In this situation, it is recommended to pull and merge + before pushing. + + Use --new-branch if you want to allow push to create a new named + branch that is not present at the destination. This allows you to + only create a new branch without forcing other changes. + + Use -f/--force to override the default behavior and push all + changesets on all branches. + + If -r/--rev is used, the specified revision and all its ancestors + will be pushed to the remote repository. + + Please see :hg:`help urls` for important details about ``ssh://`` + URLs. If DESTINATION is omitted, a default path will be used. + + Returns 0 if push was successful, 1 if nothing to push. + """ + dest = ui.expandpath(dest or 'default-push', dest or 'default') + dest, branches = hg.parseurl(dest, opts.get('branch')) + revs, checkout = hg.addbranchrevs(repo, repo, branches, opts.get('rev')) + other = hg.repository(hg.remoteui(repo, opts), dest) + ui.status(_('pushing to %s\n') % url.hidepassword(dest)) + if revs: + revs = [repo.lookup(rev) for rev in revs] + + repo._subtoppath = dest + try: + # push subrepos depth-first for coherent ordering + c = repo[''] + subs = c.substate # only repos that are committed + for s in sorted(subs): + if not c.sub(s).push(opts.get('force')): + return False + finally: + del repo._subtoppath + r = repo.push(other, opts.get('force'), revs=revs, + newbranch=opts.get('new_branch')) + return r == 0 + +def recover(ui, repo): + """roll back an interrupted transaction + + Recover from an interrupted commit or pull. + + This command tries to fix the repository status after an + interrupted operation. It should only be necessary when Mercurial + suggests it. + + Returns 0 if successful, 1 if nothing to recover or verify fails. + """ + if repo.recover(): + return hg.verify(repo) + return 1 + +def remove(ui, repo, *pats, **opts): + """remove the specified files on the next commit + + Schedule the indicated files for removal from the repository. + + This only removes files from the current branch, not from the + entire project history. -A/--after can be used to remove only + files that have already been deleted, -f/--force can be used to + force deletion, and -Af can be used to remove files from the next + revision without deleting them from the working directory. + + The following table details the behavior of remove for different + file states (columns) and option combinations (rows). The file + states are Added [A], Clean [C], Modified [M] and Missing [!] (as + reported by :hg:`status`). The actions are Warn, Remove (from + branch) and Delete (from disk):: + + A C M ! + none W RD W R + -f R RD RD R + -A W W W R + -Af R R R R + + This command schedules the files to be removed at the next commit. + To undo a remove before that, see :hg:`revert`. + + Returns 0 on success, 1 if any warnings encountered. + """ + + ret = 0 + after, force = opts.get('after'), opts.get('force') + if not pats and not after: + raise util.Abort(_('no files specified')) + + m = cmdutil.match(repo, pats, opts) + s = repo.status(match=m, clean=True) + modified, added, deleted, clean = s[0], s[1], s[3], s[6] + + for f in m.files(): + if f not in repo.dirstate and not os.path.isdir(m.rel(f)): + ui.warn(_('not removing %s: file is untracked\n') % m.rel(f)) + ret = 1 + + if force: + remove, forget = modified + deleted + clean, added + elif after: + remove, forget = deleted, [] + for f in modified + added + clean: + ui.warn(_('not removing %s: file still exists (use -f' + ' to force removal)\n') % m.rel(f)) + ret = 1 + else: + remove, forget = deleted + clean, [] + for f in modified: + ui.warn(_('not removing %s: file is modified (use -f' + ' to force removal)\n') % m.rel(f)) + ret = 1 + for f in added: + ui.warn(_('not removing %s: file has been marked for add (use -f' + ' to force removal)\n') % m.rel(f)) + ret = 1 + + for f in sorted(remove + forget): + if ui.verbose or not m.exact(f): + ui.status(_('removing %s\n') % m.rel(f)) + + repo[None].forget(forget) + repo[None].remove(remove, unlink=not after) + return ret + +def rename(ui, repo, *pats, **opts): + """rename files; equivalent of copy + remove + + Mark dest as copies of sources; mark sources for deletion. If dest + is a directory, copies are put in that directory. If dest is a + file, there can only be one source. + + By default, this command copies the contents of files as they + exist in the working directory. If invoked with -A/--after, the + operation is recorded, but no copying is performed. + + This command takes effect at the next commit. To undo a rename + before that, see :hg:`revert`. + + Returns 0 on success, 1 if errors are encountered. + """ + wlock = repo.wlock(False) + try: + return cmdutil.copy(ui, repo, pats, opts, rename=True) + finally: + wlock.release() + +def resolve(ui, repo, *pats, **opts): + """redo merges or set/view the merge status of files + + Merges with unresolved conflicts are often the result of + non-interactive merging using the ``internal:merge`` configuration + setting, or a command-line merge tool like ``diff3``. The resolve + command is used to manage the files involved in a merge, after + :hg:`merge` has been run, and before :hg:`commit` is run (i.e. the + working directory must have two parents). + + The resolve command can be used in the following ways: + + - :hg:`resolve [--tool TOOL] FILE...`: attempt to re-merge the specified + files, discarding any previous merge attempts. Re-merging is not + performed for files already marked as resolved. Use ``--all/-a`` + to selects all unresolved files. ``--tool`` can be used to specify + the merge tool used for the given files. It overrides the HGMERGE + environment variable and your configuration files. + + - :hg:`resolve -m [FILE]`: mark a file as having been resolved + (e.g. after having manually fixed-up the files). The default is + to mark all unresolved files. + + - :hg:`resolve -u [FILE]...`: mark a file as unresolved. The + default is to mark all resolved files. + + - :hg:`resolve -l`: list files which had or still have conflicts. + In the printed list, ``U`` = unresolved and ``R`` = resolved. + + Note that Mercurial will not let you commit files with unresolved + merge conflicts. You must use :hg:`resolve -m ...` before you can + commit after a conflicting merge. + + Returns 0 on success, 1 if any files fail a resolve attempt. + """ + + all, mark, unmark, show, nostatus = \ + [opts.get(o) for o in 'all mark unmark list no_status'.split()] + + if (show and (mark or unmark)) or (mark and unmark): + raise util.Abort(_("too many options specified")) + if pats and all: + raise util.Abort(_("can't specify --all and patterns")) + if not (all or pats or show or mark or unmark): + raise util.Abort(_('no files or directories specified; ' + 'use --all to remerge all files')) + + ms = mergemod.mergestate(repo) + m = cmdutil.match(repo, pats, opts) + ret = 0 + + for f in ms: + if m(f): + if show: + if nostatus: + ui.write("%s\n" % f) + else: + ui.write("%s %s\n" % (ms[f].upper(), f), + label='resolve.' + + {'u': 'unresolved', 'r': 'resolved'}[ms[f]]) + elif mark: + ms.mark(f, "r") + elif unmark: + ms.mark(f, "u") + else: + wctx = repo[None] + mctx = wctx.parents()[-1] + + # backup pre-resolve (merge uses .orig for its own purposes) + a = repo.wjoin(f) + util.copyfile(a, a + ".resolve") + + try: + # resolve file + ui.setconfig('ui', 'forcemerge', opts.get('tool', '')) + if ms.resolve(f, wctx, mctx): + ret = 1 + finally: + ui.setconfig('ui', 'forcemerge', '') + + # replace filemerge's .orig file with our resolve file + util.rename(a + ".resolve", a + ".orig") + + ms.commit() + return ret + +def revert(ui, repo, *pats, **opts): + """restore individual files or directories to an earlier state + + .. note:: + This command is most likely not what you are looking for. + Revert will partially overwrite content in the working + directory without changing the working directory parents. Use + :hg:`update -r rev` to check out earlier revisions, or + :hg:`update --clean .` to undo a merge which has added another + parent. + + With no revision specified, revert the named files or directories + to the contents they had in the parent of the working directory. + This restores the contents of the affected files to an unmodified + state and unschedules adds, removes, copies, and renames. If the + working directory has two parents, you must explicitly specify a + revision. + + Using the -r/--rev option, revert the given files or directories + to their contents as of a specific revision. This can be helpful + to "roll back" some or all of an earlier change. See :hg:`help + dates` for a list of formats valid for -d/--date. + + Revert modifies the working directory. It does not commit any + changes, or change the parent of the working directory. If you + revert to a revision other than the parent of the working + directory, the reverted files will thus appear modified + afterwards. + + If a file has been deleted, it is restored. If the executable mode + of a file was changed, it is reset. + + If names are given, all files matching the names are reverted. + If no arguments are given, no files are reverted. + + Modified files are saved with a .orig suffix before reverting. + To disable these backups, use --no-backup. + + Returns 0 on success. + """ + + if opts.get("date"): + if opts.get("rev"): + raise util.Abort(_("you can't specify a revision and a date")) + opts["rev"] = cmdutil.finddate(ui, repo, opts["date"]) + + if not pats and not opts.get('all'): + raise util.Abort(_('no files or directories specified; ' + 'use --all to revert the whole repo')) + + parent, p2 = repo.dirstate.parents() + if not opts.get('rev') and p2 != nullid: + raise util.Abort(_('uncommitted merge - please provide a ' + 'specific revision')) + ctx = repo[opts.get('rev')] + node = ctx.node() + mf = ctx.manifest() + if node == parent: + pmf = mf + else: + pmf = None + + # need all matching names in dirstate and manifest of target rev, + # so have to walk both. do not print errors if files exist in one + # but not other. + + names = {} + + wlock = repo.wlock() + try: + # walk dirstate. + + m = cmdutil.match(repo, pats, opts) + m.bad = lambda x, y: False + for abs in repo.walk(m): + names[abs] = m.rel(abs), m.exact(abs) + + # walk target manifest. + + def badfn(path, msg): + if path in names: + return + path_ = path + '/' + for f in names: + if f.startswith(path_): + return + ui.warn("%s: %s\n" % (m.rel(path), msg)) + + m = cmdutil.match(repo, pats, opts) + m.bad = badfn + for abs in repo[node].walk(m): + if abs not in names: + names[abs] = m.rel(abs), m.exact(abs) + + m = cmdutil.matchfiles(repo, names) + changes = repo.status(match=m)[:4] + modified, added, removed, deleted = map(set, changes) + + # if f is a rename, also revert the source + cwd = repo.getcwd() + for f in added: + src = repo.dirstate.copied(f) + if src and src not in names and repo.dirstate[src] == 'r': + removed.add(src) + names[src] = (repo.pathto(src, cwd), True) + + def removeforget(abs): + if repo.dirstate[abs] == 'a': + return _('forgetting %s\n') + return _('removing %s\n') + + revert = ([], _('reverting %s\n')) + add = ([], _('adding %s\n')) + remove = ([], removeforget) + undelete = ([], _('undeleting %s\n')) + + disptable = ( + # dispatch table: + # file state + # action if in target manifest + # action if not in target manifest + # make backup if in target manifest + # make backup if not in target manifest + (modified, revert, remove, True, True), + (added, revert, remove, True, False), + (removed, undelete, None, False, False), + (deleted, revert, remove, False, False), + ) + + for abs, (rel, exact) in sorted(names.items()): + mfentry = mf.get(abs) + target = repo.wjoin(abs) + def handle(xlist, dobackup): + xlist[0].append(abs) + if (dobackup and not opts.get('no_backup') and + os.path.lexists(target)): + bakname = "%s.orig" % rel + ui.note(_('saving current version of %s as %s\n') % + (rel, bakname)) + if not opts.get('dry_run'): + util.rename(target, bakname) + if ui.verbose or not exact: + msg = xlist[1] + if not isinstance(msg, basestring): + msg = msg(abs) + ui.status(msg % rel) + for table, hitlist, misslist, backuphit, backupmiss in disptable: + if abs not in table: + continue + # file has changed in dirstate + if mfentry: + handle(hitlist, backuphit) + elif misslist is not None: + handle(misslist, backupmiss) + break + else: + if abs not in repo.dirstate: + if mfentry: + handle(add, True) + elif exact: + ui.warn(_('file not managed: %s\n') % rel) + continue + # file has not changed in dirstate + if node == parent: + if exact: + ui.warn(_('no changes needed to %s\n') % rel) + continue + if pmf is None: + # only need parent manifest in this unlikely case, + # so do not read by default + pmf = repo[parent].manifest() + if abs in pmf: + if mfentry: + # if version of file is same in parent and target + # manifests, do nothing + if (pmf[abs] != mfentry or + pmf.flags(abs) != mf.flags(abs)): + handle(revert, False) + else: + handle(remove, False) + + if not opts.get('dry_run'): + def checkout(f): + fc = ctx[f] + repo.wwrite(f, fc.data(), fc.flags()) + + audit_path = util.path_auditor(repo.root) + for f in remove[0]: + if repo.dirstate[f] == 'a': + repo.dirstate.forget(f) + continue + audit_path(f) + try: + util.unlink(repo.wjoin(f)) + except OSError: + pass + repo.dirstate.remove(f) + + normal = None + if node == parent: + # We're reverting to our parent. If possible, we'd like status + # to report the file as clean. We have to use normallookup for + # merges to avoid losing information about merged/dirty files. + if p2 != nullid: + normal = repo.dirstate.normallookup + else: + normal = repo.dirstate.normal + for f in revert[0]: + checkout(f) + if normal: + normal(f) + + for f in add[0]: + checkout(f) + repo.dirstate.add(f) + + normal = repo.dirstate.normallookup + if node == parent and p2 == nullid: + normal = repo.dirstate.normal + for f in undelete[0]: + checkout(f) + normal(f) + + finally: + wlock.release() + +def rollback(ui, repo, **opts): + """roll back the last transaction (dangerous) + + This command should be used with care. There is only one level of + rollback, and there is no way to undo a rollback. It will also + restore the dirstate at the time of the last transaction, losing + any dirstate changes since that time. This command does not alter + the working directory. + + Transactions are used to encapsulate the effects of all commands + that create new changesets or propagate existing changesets into a + repository. For example, the following commands are transactional, + and their effects can be rolled back: + + - commit + - import + - pull + - push (with this repository as the destination) + - unbundle + + This command is not intended for use on public repositories. Once + changes are visible for pull by other users, rolling a transaction + back locally is ineffective (someone else may already have pulled + the changes). Furthermore, a race is possible with readers of the + repository; for example an in-progress pull from the repository + may fail if a rollback is performed. + + Returns 0 on success, 1 if no rollback data is available. + """ + return repo.rollback(opts.get('dry_run')) + +def root(ui, repo): + """print the root (top) of the current working directory + + Print the root directory of the current repository. + + Returns 0 on success. + """ + ui.write(repo.root + "\n") + +def serve(ui, repo, **opts): + """start stand-alone webserver + + Start a local HTTP repository browser and pull server. You can use + this for ad-hoc sharing and browsing of repositories. It is + recommended to use a real web server to serve a repository for + longer periods of time. + + Please note that the server does not implement access control. + This means that, by default, anybody can read from the server and + nobody can write to it by default. Set the ``web.allow_push`` + option to ``*`` to allow everybody to push to the server. You + should use a real web server if you need to authenticate users. + + By default, the server logs accesses to stdout and errors to + stderr. Use the -A/--accesslog and -E/--errorlog options to log to + files. + + To have the server choose a free port number to listen on, specify + a port number of 0; in this case, the server will print the port + number it uses. + + Returns 0 on success. + """ + + if opts["stdio"]: + if repo is None: + raise error.RepoError(_("There is no Mercurial repository here" + " (.hg not found)")) + s = sshserver.sshserver(ui, repo) + s.serve_forever() + + # this way we can check if something was given in the command-line + if opts.get('port'): + opts['port'] = util.getport(opts.get('port')) + + baseui = repo and repo.baseui or ui + optlist = ("name templates style address port prefix ipv6" + " accesslog errorlog certificate encoding") + for o in optlist.split(): + val = opts.get(o, '') + if val in (None, ''): # should check against default options instead + continue + baseui.setconfig("web", o, val) + if repo and repo.ui != baseui: + repo.ui.setconfig("web", o, val) + + o = opts.get('web_conf') or opts.get('webdir_conf') + if not o: + if not repo: + raise error.RepoError(_("There is no Mercurial repository" + " here (.hg not found)")) + o = repo.root + + app = hgweb.hgweb(o, baseui=ui) + + class service(object): + def init(self): + util.set_signal_handler() + self.httpd = hgweb.server.create_server(ui, app) + + if opts['port'] and not ui.verbose: + return + + if self.httpd.prefix: + prefix = self.httpd.prefix.strip('/') + '/' + else: + prefix = '' + + port = ':%d' % self.httpd.port + if port == ':80': + port = '' + + bindaddr = self.httpd.addr + if bindaddr == '0.0.0.0': + bindaddr = '*' + elif ':' in bindaddr: # IPv6 + bindaddr = '[%s]' % bindaddr + + fqaddr = self.httpd.fqaddr + if ':' in fqaddr: + fqaddr = '[%s]' % fqaddr + if opts['port']: + write = ui.status + else: + write = ui.write + write(_('listening at http://%s%s/%s (bound to %s:%d)\n') % + (fqaddr, port, prefix, bindaddr, self.httpd.port)) + + def run(self): + self.httpd.serve_forever() + + service = service() + + cmdutil.service(opts, initfn=service.init, runfn=service.run) + +def status(ui, repo, *pats, **opts): + """show changed files in the working directory + + Show status of files in the repository. If names are given, only + files that match are shown. Files that are clean or ignored or + the source of a copy/move operation, are not listed unless + -c/--clean, -i/--ignored, -C/--copies or -A/--all are given. + Unless options described with "show only ..." are given, the + options -mardu are used. + + Option -q/--quiet hides untracked (unknown and ignored) files + unless explicitly requested with -u/--unknown or -i/--ignored. + + .. note:: + status may appear to disagree with diff if permissions have + changed or a merge has occurred. The standard diff format does + not report permission changes and diff only reports changes + relative to one merge parent. + + If one revision is given, it is used as the base revision. + If two revisions are given, the differences between them are + shown. The --change option can also be used as a shortcut to list + the changed files of a revision from its first parent. + + The codes used to show the status of files are:: + + M = modified + A = added + R = removed + C = clean + ! = missing (deleted by non-hg command, but still tracked) + ? = not tracked + I = ignored + = origin of the previous file listed as A (added) + + Returns 0 on success. + """ + + revs = opts.get('rev') + change = opts.get('change') + + if revs and change: + msg = _('cannot specify --rev and --change at the same time') + raise util.Abort(msg) + elif change: + node2 = repo.lookup(change) + node1 = repo[node2].parents()[0].node() + else: + node1, node2 = cmdutil.revpair(repo, revs) + + cwd = (pats and repo.getcwd()) or '' + end = opts.get('print0') and '\0' or '\n' + copy = {} + states = 'modified added removed deleted unknown ignored clean'.split() + show = [k for k in states if opts.get(k)] + if opts.get('all'): + show += ui.quiet and (states[:4] + ['clean']) or states + if not show: + show = ui.quiet and states[:4] or states[:5] + + stat = repo.status(node1, node2, cmdutil.match(repo, pats, opts), + 'ignored' in show, 'clean' in show, 'unknown' in show, + opts.get('subrepos')) + changestates = zip(states, 'MAR!?IC', stat) + + if (opts.get('all') or opts.get('copies')) and not opts.get('no_status'): + ctxn = repo[nullid] + ctx1 = repo[node1] + ctx2 = repo[node2] + added = stat[1] + if node2 is None: + added = stat[0] + stat[1] # merged? + + for k, v in copies.copies(repo, ctx1, ctx2, ctxn)[0].iteritems(): + if k in added: + copy[k] = v + elif v in added: + copy[v] = k + + for state, char, files in changestates: + if state in show: + format = "%s %%s%s" % (char, end) + if opts.get('no_status'): + format = "%%s%s" % end + + for f in files: + ui.write(format % repo.pathto(f, cwd), + label='status.' + state) + if f in copy: + ui.write(' %s%s' % (repo.pathto(copy[f], cwd), end), + label='status.copied') + +def summary(ui, repo, **opts): + """summarize working directory state + + This generates a brief summary of the working directory state, + including parents, branch, commit status, and available updates. + + With the --remote option, this will check the default paths for + incoming and outgoing changes. This can be time-consuming. + + Returns 0 on success. + """ + + ctx = repo[None] + parents = ctx.parents() + pnode = parents[0].node() + + for p in parents: + # label with log.changeset (instead of log.parent) since this + # shows a working directory parent *changeset*: + ui.write(_('parent: %d:%s ') % (p.rev(), str(p)), + label='log.changeset') + ui.write(' '.join(p.tags()), label='log.tag') + if p.rev() == -1: + if not len(repo): + ui.write(_(' (empty repository)')) + else: + ui.write(_(' (no revision checked out)')) + ui.write('\n') + if p.description(): + ui.status(' ' + p.description().splitlines()[0].strip() + '\n', + label='log.summary') + + branch = ctx.branch() + bheads = repo.branchheads(branch) + m = _('branch: %s\n') % branch + if branch != 'default': + ui.write(m, label='log.branch') + else: + ui.status(m, label='log.branch') + + st = list(repo.status(unknown=True))[:6] + + c = repo.dirstate.copies() + copied, renamed = [], [] + for d, s in c.iteritems(): + if s in st[2]: + st[2].remove(s) + renamed.append(d) + else: + copied.append(d) + if d in st[1]: + st[1].remove(d) + st.insert(3, renamed) + st.insert(4, copied) + + ms = mergemod.mergestate(repo) + st.append([f for f in ms if ms[f] == 'u']) + + subs = [s for s in ctx.substate if ctx.sub(s).dirty()] + st.append(subs) + + labels = [ui.label(_('%d modified'), 'status.modified'), + ui.label(_('%d added'), 'status.added'), + ui.label(_('%d removed'), 'status.removed'), + ui.label(_('%d renamed'), 'status.copied'), + ui.label(_('%d copied'), 'status.copied'), + ui.label(_('%d deleted'), 'status.deleted'), + ui.label(_('%d unknown'), 'status.unknown'), + ui.label(_('%d ignored'), 'status.ignored'), + ui.label(_('%d unresolved'), 'resolve.unresolved'), + ui.label(_('%d subrepos'), 'status.modified')] + t = [] + for s, l in zip(st, labels): + if s: + t.append(l % len(s)) + + t = ', '.join(t) + cleanworkdir = False + + if len(parents) > 1: + t += _(' (merge)') + elif branch != parents[0].branch(): + t += _(' (new branch)') + elif (parents[0].extra().get('close') and + pnode in repo.branchheads(branch, closed=True)): + t += _(' (head closed)') + elif not (st[0] or st[1] or st[2] or st[3] or st[4] or st[9]): + t += _(' (clean)') + cleanworkdir = True + elif pnode not in bheads: + t += _(' (new branch head)') + + if cleanworkdir: + ui.status(_('commit: %s\n') % t.strip()) + else: + ui.write(_('commit: %s\n') % t.strip()) + + # all ancestors of branch heads - all ancestors of parent = new csets + new = [0] * len(repo) + cl = repo.changelog + for a in [cl.rev(n) for n in bheads]: + new[a] = 1 + for a in cl.ancestors(*[cl.rev(n) for n in bheads]): + new[a] = 1 + for a in [p.rev() for p in parents]: + if a >= 0: + new[a] = 0 + for a in cl.ancestors(*[p.rev() for p in parents]): + new[a] = 0 + new = sum(new) + + if new == 0: + ui.status(_('update: (current)\n')) + elif pnode not in bheads: + ui.write(_('update: %d new changesets (update)\n') % new) + else: + ui.write(_('update: %d new changesets, %d branch heads (merge)\n') % + (new, len(bheads))) + + if opts.get('remote'): + t = [] + source, branches = hg.parseurl(ui.expandpath('default')) + other = hg.repository(hg.remoteui(repo, {}), source) + revs, checkout = hg.addbranchrevs(repo, other, branches, opts.get('rev')) + ui.debug('comparing with %s\n' % url.hidepassword(source)) + repo.ui.pushbuffer() + common, incoming, rheads = discovery.findcommonincoming(repo, other) + repo.ui.popbuffer() + if incoming: + t.append(_('1 or more incoming')) + + dest, branches = hg.parseurl(ui.expandpath('default-push', 'default')) + revs, checkout = hg.addbranchrevs(repo, repo, branches, None) + other = hg.repository(hg.remoteui(repo, {}), dest) + ui.debug('comparing with %s\n' % url.hidepassword(dest)) + repo.ui.pushbuffer() + o = discovery.findoutgoing(repo, other) + repo.ui.popbuffer() + o = repo.changelog.nodesbetween(o, None)[0] + if o: + t.append(_('%d outgoing') % len(o)) + + if t: + ui.write(_('remote: %s\n') % (', '.join(t))) + else: + ui.status(_('remote: (synced)\n')) + +def tag(ui, repo, name1, *names, **opts): + """add one or more tags for the current or given revision + + Name a particular revision using . + + Tags are used to name particular revisions of the repository and are + very useful to compare different revisions, to go back to significant + earlier versions or to mark branch points as releases, etc. Changing + an existing tag is normally disallowed; use -f/--force to override. + + If no revision is given, the parent of the working directory is + used, or tip if no revision is checked out. + + To facilitate version control, distribution, and merging of tags, + they are stored as a file named ".hgtags" which is managed similarly + to other project files and can be hand-edited if necessary. This + also means that tagging creates a new commit. The file + ".hg/localtags" is used for local tags (not shared among + repositories). + + Tag commits are usually made at the head of a branch. If the parent + of the working directory is not a branch head, :hg:`tag` aborts; use + -f/--force to force the tag commit to be based on a non-head + changeset. + + See :hg:`help dates` for a list of formats valid for -d/--date. + + Since tag names have priority over branch names during revision + lookup, using an existing branch name as a tag name is discouraged. + + Returns 0 on success. + """ + + rev_ = "." + names = [t.strip() for t in (name1,) + names] + if len(names) != len(set(names)): + raise util.Abort(_('tag names must be unique')) + for n in names: + if n in ['tip', '.', 'null']: + raise util.Abort(_('the name \'%s\' is reserved') % n) + if not n: + raise util.Abort(_('tag names cannot consist entirely of whitespace')) + if opts.get('rev') and opts.get('remove'): + raise util.Abort(_("--rev and --remove are incompatible")) + if opts.get('rev'): + rev_ = opts['rev'] + message = opts.get('message') + if opts.get('remove'): + expectedtype = opts.get('local') and 'local' or 'global' + for n in names: + if not repo.tagtype(n): + raise util.Abort(_('tag \'%s\' does not exist') % n) + if repo.tagtype(n) != expectedtype: + if expectedtype == 'global': + raise util.Abort(_('tag \'%s\' is not a global tag') % n) + else: + raise util.Abort(_('tag \'%s\' is not a local tag') % n) + rev_ = nullid + if not message: + # we don't translate commit messages + message = 'Removed tag %s' % ', '.join(names) + elif not opts.get('force'): + for n in names: + if n in repo.tags(): + raise util.Abort(_('tag \'%s\' already exists ' + '(use -f to force)') % n) + if not opts.get('local'): + p1, p2 = repo.dirstate.parents() + if p2 != nullid: + raise util.Abort(_('uncommitted merge')) + bheads = repo.branchheads() + if not opts.get('force') and bheads and p1 not in bheads: + raise util.Abort(_('not at a branch head (use -f to force)')) + r = repo[rev_].node() + + if not message: + # we don't translate commit messages + message = ('Added tag %s for changeset %s' % + (', '.join(names), short(r))) + + date = opts.get('date') + if date: + date = util.parsedate(date) + + if opts.get('edit'): + message = ui.edit(message, ui.username()) + + repo.tag(names, r, message, opts.get('local'), opts.get('user'), date) + +def tags(ui, repo): + """list repository tags + + This lists both regular and local tags. When the -v/--verbose + switch is used, a third column "local" is printed for local tags. + + Returns 0 on success. + """ + + hexfunc = ui.debugflag and hex or short + tagtype = "" + + for t, n in reversed(repo.tagslist()): + if ui.quiet: + ui.write("%s\n" % t) + continue + + try: + hn = hexfunc(n) + r = "%5d:%s" % (repo.changelog.rev(n), hn) + except error.LookupError: + r = " ?:%s" % hn + else: + spaces = " " * (30 - encoding.colwidth(t)) + if ui.verbose: + if repo.tagtype(t) == 'local': + tagtype = " local" + else: + tagtype = "" + ui.write("%s%s %s%s\n" % (t, spaces, r, tagtype)) + +def tip(ui, repo, **opts): + """show the tip revision + + The tip revision (usually just called the tip) is the changeset + most recently added to the repository (and therefore the most + recently changed head). + + If you have just made a commit, that commit will be the tip. If + you have just pulled changes from another repository, the tip of + that repository becomes the current tip. The "tip" tag is special + and cannot be renamed or assigned to a different changeset. + + Returns 0 on success. + """ + displayer = cmdutil.show_changeset(ui, repo, opts) + displayer.show(repo[len(repo) - 1]) + displayer.close() + +def unbundle(ui, repo, fname1, *fnames, **opts): + """apply one or more changegroup files + + Apply one or more compressed changegroup files generated by the + bundle command. + + Returns 0 on success, 1 if an update has unresolved files. + """ + fnames = (fname1,) + fnames + + lock = repo.lock() + try: + for fname in fnames: + f = url.open(ui, fname) + gen = changegroup.readbundle(f, fname) + modheads = repo.addchangegroup(gen, 'unbundle', 'bundle:' + fname, + lock=lock) + finally: + lock.release() + + return postincoming(ui, repo, modheads, opts.get('update'), None) + +def update(ui, repo, node=None, rev=None, clean=False, date=None, check=False): + """update working directory (or switch revisions) + + Update the repository's working directory to the specified + changeset. If no changeset is specified, update to the tip of the + current named branch. + + If the changeset is not a descendant of the working directory's + parent, the update is aborted. With the -c/--check option, the + working directory is checked for uncommitted changes; if none are + found, the working directory is updated to the specified + changeset. + + The following rules apply when the working directory contains + uncommitted changes: + + 1. If neither -c/--check nor -C/--clean is specified, and if + the requested changeset is an ancestor or descendant of + the working directory's parent, the uncommitted changes + are merged into the requested changeset and the merged + result is left uncommitted. If the requested changeset is + not an ancestor or descendant (that is, it is on another + branch), the update is aborted and the uncommitted changes + are preserved. + + 2. With the -c/--check option, the update is aborted and the + uncommitted changes are preserved. + + 3. With the -C/--clean option, uncommitted changes are discarded and + the working directory is updated to the requested changeset. + + Use null as the changeset to remove the working directory (like + :hg:`clone -U`). + + If you want to update just one file to an older changeset, use + :hg:`revert`. + + See :hg:`help dates` for a list of formats valid for -d/--date. + + Returns 0 on success, 1 if there are unresolved files. + """ + if rev and node: + raise util.Abort(_("please specify just one revision")) + + if not rev: + rev = node + + rev = cmdutil.revsingle(repo, rev, rev).rev() + + if check and clean: + raise util.Abort(_("cannot specify both -c/--check and -C/--clean")) + + if check: + # we could use dirty() but we can ignore merge and branch trivia + c = repo[None] + if c.modified() or c.added() or c.removed(): + raise util.Abort(_("uncommitted local changes")) + + if date: + if rev: + raise util.Abort(_("you can't specify a revision and a date")) + rev = cmdutil.finddate(ui, repo, date) + + if clean or check: + return hg.clean(repo, rev) + else: + return hg.update(repo, rev) + +def verify(ui, repo): + """verify the integrity of the repository + + Verify the integrity of the current repository. + + This will perform an extensive check of the repository's + integrity, validating the hashes and checksums of each entry in + the changelog, manifest, and tracked files, as well as the + integrity of their crosslinks and indices. + + Returns 0 on success, 1 if errors are encountered. + """ + return hg.verify(repo) + +def version_(ui): + """output version and copyright information""" + ui.write(_("Mercurial Distributed SCM (version %s)\n") + % util.version()) + ui.status(_( + "(see http://mercurial.selenic.com for more information)\n" + "\nCopyright (C) 2005-2010 Matt Mackall and others\n" + "This is free software; see the source for copying conditions. " + "There is NO\nwarranty; " + "not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n" + )) + +# Command options and aliases are listed here, alphabetically + +globalopts = [ + ('R', 'repository', '', + _('repository root directory or name of overlay bundle file'), + _('REPO')), + ('', 'cwd', '', + _('change working directory'), _('DIR')), + ('y', 'noninteractive', None, + _('do not prompt, assume \'yes\' for any required answers')), + ('q', 'quiet', None, _('suppress output')), + ('v', 'verbose', None, _('enable additional output')), + ('', 'config', [], + _('set/override config option (use \'section.name=value\')'), + _('CONFIG')), + ('', 'debug', None, _('enable debugging output')), + ('', 'debugger', None, _('start debugger')), + ('', 'encoding', encoding.encoding, _('set the charset encoding'), + _('ENCODE')), + ('', 'encodingmode', encoding.encodingmode, + _('set the charset encoding mode'), _('MODE')), + ('', 'traceback', None, _('always print a traceback on exception')), + ('', 'time', None, _('time how long the command takes')), + ('', 'profile', None, _('print command execution profile')), + ('', 'version', None, _('output version information and exit')), + ('h', 'help', None, _('display help and exit')), +] + +dryrunopts = [('n', 'dry-run', None, + _('do not perform actions, just print output'))] + +remoteopts = [ + ('e', 'ssh', '', + _('specify ssh command to use'), _('CMD')), + ('', 'remotecmd', '', + _('specify hg command to run on the remote side'), _('CMD')), +] + +walkopts = [ + ('I', 'include', [], + _('include names matching the given patterns'), _('PATTERN')), + ('X', 'exclude', [], + _('exclude names matching the given patterns'), _('PATTERN')), +] + +commitopts = [ + ('m', 'message', '', + _('use text as commit message'), _('TEXT')), + ('l', 'logfile', '', + _('read commit message from file'), _('FILE')), +] + +commitopts2 = [ + ('d', 'date', '', + _('record datecode as commit date'), _('DATE')), + ('u', 'user', '', + _('record the specified user as committer'), _('USER')), +] + +templateopts = [ + ('', 'style', '', + _('display using template map file'), _('STYLE')), + ('', 'template', '', + _('display with template'), _('TEMPLATE')), +] + +logopts = [ + ('p', 'patch', None, _('show patch')), + ('g', 'git', None, _('use git extended diff format')), + ('l', 'limit', '', + _('limit number of changes displayed'), _('NUM')), + ('M', 'no-merges', None, _('do not show merges')), + ('', 'stat', None, _('output diffstat-style summary of changes')), +] + templateopts + +diffopts = [ + ('a', 'text', None, _('treat all files as text')), + ('g', 'git', None, _('use git extended diff format')), + ('', 'nodates', None, _('omit dates from diff headers')) +] + +diffopts2 = [ + ('p', 'show-function', None, _('show which function each change is in')), + ('', 'reverse', None, _('produce a diff that undoes the changes')), + ('w', 'ignore-all-space', None, + _('ignore white space when comparing lines')), + ('b', 'ignore-space-change', None, + _('ignore changes in the amount of white space')), + ('B', 'ignore-blank-lines', None, + _('ignore changes whose lines are all blank')), + ('U', 'unified', '', + _('number of lines of context to show'), _('NUM')), + ('', 'stat', None, _('output diffstat-style summary of changes')), +] + +similarityopts = [ + ('s', 'similarity', '', + _('guess renamed files by similarity (0<=s<=100)'), _('SIMILARITY')) +] + +subrepoopts = [ + ('S', 'subrepos', None, + _('recurse into subrepositories')) +] + +table = { + "^add": (add, walkopts + subrepoopts + dryrunopts, + _('[OPTION]... [FILE]...')), + "addremove": + (addremove, similarityopts + walkopts + dryrunopts, + _('[OPTION]... [FILE]...')), + "^annotate|blame": + (annotate, + [('r', 'rev', '', + _('annotate the specified revision'), _('REV')), + ('', 'follow', None, + _('follow copies/renames and list the filename (DEPRECATED)')), + ('', 'no-follow', None, _("don't follow copies and renames")), + ('a', 'text', None, _('treat all files as text')), + ('u', 'user', None, _('list the author (long with -v)')), + ('f', 'file', None, _('list the filename')), + ('d', 'date', None, _('list the date (short with -q)')), + ('n', 'number', None, _('list the revision number (default)')), + ('c', 'changeset', None, _('list the changeset')), + ('l', 'line-number', None, + _('show line number at the first appearance')) + ] + walkopts, + _('[-r REV] [-f] [-a] [-u] [-d] [-n] [-c] [-l] FILE...')), + "archive": + (archive, + [('', 'no-decode', None, _('do not pass files through decoders')), + ('p', 'prefix', '', + _('directory prefix for files in archive'), _('PREFIX')), + ('r', 'rev', '', + _('revision to distribute'), _('REV')), + ('t', 'type', '', + _('type of distribution to create'), _('TYPE')), + ] + subrepoopts + walkopts, + _('[OPTION]... DEST')), + "backout": + (backout, + [('', 'merge', None, + _('merge with old dirstate parent after backout')), + ('', 'parent', '', + _('parent to choose when backing out merge'), _('REV')), + ('t', 'tool', '', + _('specify merge tool')), + ('r', 'rev', '', + _('revision to backout'), _('REV')), + ] + walkopts + commitopts + commitopts2, + _('[OPTION]... [-r] REV')), + "bisect": + (bisect, + [('r', 'reset', False, _('reset bisect state')), + ('g', 'good', False, _('mark changeset good')), + ('b', 'bad', False, _('mark changeset bad')), + ('s', 'skip', False, _('skip testing changeset')), + ('c', 'command', '', + _('use command to check changeset state'), _('CMD')), + ('U', 'noupdate', False, _('do not update to target'))], + _("[-gbsr] [-U] [-c CMD] [REV]")), + "branch": + (branch, + [('f', 'force', None, + _('set branch name even if it shadows an existing branch')), + ('C', 'clean', None, _('reset branch name to parent branch name'))], + _('[-fC] [NAME]')), + "branches": + (branches, + [('a', 'active', False, + _('show only branches that have unmerged heads')), + ('c', 'closed', False, + _('show normal and closed branches'))], + _('[-ac]')), + "bundle": + (bundle, + [('f', 'force', None, + _('run even when the destination is unrelated')), + ('r', 'rev', [], + _('a changeset intended to be added to the destination'), + _('REV')), + ('b', 'branch', [], + _('a specific branch you would like to bundle'), + _('BRANCH')), + ('', 'base', [], + _('a base changeset assumed to be available at the destination'), + _('REV')), + ('a', 'all', None, _('bundle all changesets in the repository')), + ('t', 'type', 'bzip2', + _('bundle compression type to use'), _('TYPE')), + ] + remoteopts, + _('[-f] [-t TYPE] [-a] [-r REV]... [--base REV]... FILE [DEST]')), + "cat": + (cat, + [('o', 'output', '', + _('print output to file with formatted name'), _('FORMAT')), + ('r', 'rev', '', + _('print the given revision'), _('REV')), + ('', 'decode', None, _('apply any matching decode filter')), + ] + walkopts, + _('[OPTION]... FILE...')), + "^clone": + (clone, + [('U', 'noupdate', None, + _('the clone will include an empty working copy (only a repository)')), + ('u', 'updaterev', '', + _('revision, tag or branch to check out'), _('REV')), + ('r', 'rev', [], + _('include the specified changeset'), _('REV')), + ('b', 'branch', [], + _('clone only the specified branch'), _('BRANCH')), + ('', 'pull', None, _('use pull protocol to copy metadata')), + ('', 'uncompressed', None, + _('use uncompressed transfer (fast over LAN)')), + ] + remoteopts, + _('[OPTION]... SOURCE [DEST]')), + "^commit|ci": + (commit, + [('A', 'addremove', None, + _('mark new/missing files as added/removed before committing')), + ('', 'close-branch', None, + _('mark a branch as closed, hiding it from the branch list')), + ] + walkopts + commitopts + commitopts2, + _('[OPTION]... [FILE]...')), + "copy|cp": + (copy, + [('A', 'after', None, _('record a copy that has already occurred')), + ('f', 'force', None, + _('forcibly copy over an existing managed file')), + ] + walkopts + dryrunopts, + _('[OPTION]... [SOURCE]... DEST')), + "debugancestor": (debugancestor, [], _('[INDEX] REV1 REV2')), + "debugbuilddag": + (debugbuilddag, + [('m', 'mergeable-file', None, _('add single file mergeable changes')), + ('a', 'appended-file', None, _('add single file all revs append to')), + ('o', 'overwritten-file', None, _('add single file all revs overwrite')), + ('n', 'new-file', None, _('add new file at each rev')), + ], + _('[OPTION]... TEXT')), + "debugcheckstate": (debugcheckstate, [], ''), + "debugcommands": (debugcommands, [], _('[COMMAND]')), + "debugcomplete": + (debugcomplete, + [('o', 'options', None, _('show the command options'))], + _('[-o] CMD')), + "debugdag": + (debugdag, + [('t', 'tags', None, _('use tags as labels')), + ('b', 'branches', None, _('annotate with branch names')), + ('', 'dots', None, _('use dots for runs')), + ('s', 'spaces', None, _('separate elements by spaces')), + ], + _('[OPTION]... [FILE [REV]...]')), + "debugdate": + (debugdate, + [('e', 'extended', None, _('try extended date formats'))], + _('[-e] DATE [RANGE]')), + "debugdata": (debugdata, [], _('FILE REV')), + "debugfsinfo": (debugfsinfo, [], _('[PATH]')), + "debugindex": (debugindex, + [('f', 'format', 0, _('revlog format'), _('FORMAT'))], + _('FILE')), + "debugindexdot": (debugindexdot, [], _('FILE')), + "debuginstall": (debuginstall, [], ''), + "debugpushkey": (debugpushkey, [], _('REPO NAMESPACE [KEY OLD NEW]')), + "debugrebuildstate": + (debugrebuildstate, + [('r', 'rev', '', + _('revision to rebuild to'), _('REV'))], + _('[-r REV] [REV]')), + "debugrename": + (debugrename, + [('r', 'rev', '', + _('revision to debug'), _('REV'))], + _('[-r REV] FILE')), + "debugrevspec": + (debugrevspec, [], ('REVSPEC')), + "debugsetparents": + (debugsetparents, [], _('REV1 [REV2]')), + "debugstate": + (debugstate, + [('', 'nodates', None, _('do not display the saved mtime'))], + _('[OPTION]...')), + "debugsub": + (debugsub, + [('r', 'rev', '', + _('revision to check'), _('REV'))], + _('[-r REV] [REV]')), + "debugwalk": (debugwalk, walkopts, _('[OPTION]... [FILE]...')), + "^diff": + (diff, + [('r', 'rev', [], + _('revision'), _('REV')), + ('c', 'change', '', + _('change made by revision'), _('REV')) + ] + diffopts + diffopts2 + walkopts + subrepoopts, + _('[OPTION]... ([-c REV] | [-r REV1 [-r REV2]]) [FILE]...')), + "^export": + (export, + [('o', 'output', '', + _('print output to file with formatted name'), _('FORMAT')), + ('', 'switch-parent', None, _('diff against the second parent')), + ('r', 'rev', [], + _('revisions to export'), _('REV')), + ] + diffopts, + _('[OPTION]... [-o OUTFILESPEC] REV...')), + "^forget": + (forget, + [] + walkopts, + _('[OPTION]... FILE...')), + "grep": + (grep, + [('0', 'print0', None, _('end fields with NUL')), + ('', 'all', None, _('print all revisions that match')), + ('f', 'follow', None, + _('follow changeset history,' + ' or file history across copies and renames')), + ('i', 'ignore-case', None, _('ignore case when matching')), + ('l', 'files-with-matches', None, + _('print only filenames and revisions that match')), + ('n', 'line-number', None, _('print matching line numbers')), + ('r', 'rev', [], + _('only search files changed within revision range'), _('REV')), + ('u', 'user', None, _('list the author (long with -v)')), + ('d', 'date', None, _('list the date (short with -q)')), + ] + walkopts, + _('[OPTION]... PATTERN [FILE]...')), + "heads": + (heads, + [('r', 'rev', '', + _('show only heads which are descendants of STARTREV'), + _('STARTREV')), + ('t', 'topo', False, _('show topological heads only')), + ('a', 'active', False, + _('show active branchheads only (DEPRECATED)')), + ('c', 'closed', False, + _('show normal and closed branch heads')), + ] + templateopts, + _('[-ac] [-r STARTREV] [REV]...')), + "help": (help_, [], _('[TOPIC]')), + "identify|id": + (identify, + [('r', 'rev', '', + _('identify the specified revision'), _('REV')), + ('n', 'num', None, _('show local revision number')), + ('i', 'id', None, _('show global revision id')), + ('b', 'branch', None, _('show branch')), + ('t', 'tags', None, _('show tags'))], + _('[-nibt] [-r REV] [SOURCE]')), + "import|patch": + (import_, + [('p', 'strip', 1, + _('directory strip option for patch. This has the same ' + 'meaning as the corresponding patch option'), + _('NUM')), + ('b', 'base', '', + _('base path'), _('PATH')), + ('f', 'force', None, + _('skip check for outstanding uncommitted changes')), + ('', 'no-commit', None, + _("don't commit, just update the working directory")), + ('', 'exact', None, + _('apply patch to the nodes from which it was generated')), + ('', 'import-branch', None, + _('use any branch information in patch (implied by --exact)'))] + + commitopts + commitopts2 + similarityopts, + _('[OPTION]... PATCH...')), + "incoming|in": + (incoming, + [('f', 'force', None, + _('run even if remote repository is unrelated')), + ('n', 'newest-first', None, _('show newest record first')), + ('', 'bundle', '', + _('file to store the bundles into'), _('FILE')), + ('r', 'rev', [], + _('a remote changeset intended to be added'), _('REV')), + ('b', 'branch', [], + _('a specific branch you would like to pull'), _('BRANCH')), + ] + logopts + remoteopts + subrepoopts, + _('[-p] [-n] [-M] [-f] [-r REV]...' + ' [--bundle FILENAME] [SOURCE]')), + "^init": + (init, + remoteopts, + _('[-e CMD] [--remotecmd CMD] [DEST]')), + "locate": + (locate, + [('r', 'rev', '', + _('search the repository as it is in REV'), _('REV')), + ('0', 'print0', None, + _('end filenames with NUL, for use with xargs')), + ('f', 'fullpath', None, + _('print complete paths from the filesystem root')), + ] + walkopts, + _('[OPTION]... [PATTERN]...')), + "^log|history": + (log, + [('f', 'follow', None, + _('follow changeset history,' + ' or file history across copies and renames')), + ('', 'follow-first', None, + _('only follow the first parent of merge changesets')), + ('d', 'date', '', + _('show revisions matching date spec'), _('DATE')), + ('C', 'copies', None, _('show copied files')), + ('k', 'keyword', [], + _('do case-insensitive search for a given text'), _('TEXT')), + ('r', 'rev', [], + _('show the specified revision or range'), _('REV')), + ('', 'removed', None, _('include revisions where files were removed')), + ('m', 'only-merges', None, _('show only merges')), + ('u', 'user', [], + _('revisions committed by user'), _('USER')), + ('', 'only-branch', [], + _('show only changesets within the given named branch (DEPRECATED)'), + _('BRANCH')), + ('b', 'branch', [], + _('show changesets within the given named branch'), _('BRANCH')), + ('P', 'prune', [], + _('do not display revision or any of its ancestors'), _('REV')), + ] + logopts + walkopts, + _('[OPTION]... [FILE]')), + "manifest": + (manifest, + [('r', 'rev', '', + _('revision to display'), _('REV'))], + _('[-r REV]')), + "^merge": + (merge, + [('f', 'force', None, _('force a merge with outstanding changes')), + ('t', 'tool', '', _('specify merge tool')), + ('r', 'rev', '', + _('revision to merge'), _('REV')), + ('P', 'preview', None, + _('review revisions to merge (no merge is performed)'))], + _('[-P] [-f] [[-r] REV]')), + "outgoing|out": + (outgoing, + [('f', 'force', None, + _('run even when the destination is unrelated')), + ('r', 'rev', [], + _('a changeset intended to be included in the destination'), + _('REV')), + ('n', 'newest-first', None, _('show newest record first')), + ('b', 'branch', [], + _('a specific branch you would like to push'), _('BRANCH')), + ] + logopts + remoteopts + subrepoopts, + _('[-M] [-p] [-n] [-f] [-r REV]... [DEST]')), + "parents": + (parents, + [('r', 'rev', '', + _('show parents of the specified revision'), _('REV')), + ] + templateopts, + _('[-r REV] [FILE]')), + "paths": (paths, [], _('[NAME]')), + "^pull": + (pull, + [('u', 'update', None, + _('update to new branch head if changesets were pulled')), + ('f', 'force', None, + _('run even when remote repository is unrelated')), + ('r', 'rev', [], + _('a remote changeset intended to be added'), _('REV')), + ('b', 'branch', [], + _('a specific branch you would like to pull'), _('BRANCH')), + ] + remoteopts, + _('[-u] [-f] [-r REV]... [-e CMD] [--remotecmd CMD] [SOURCE]')), + "^push": + (push, + [('f', 'force', None, _('force push')), + ('r', 'rev', [], + _('a changeset intended to be included in the destination'), + _('REV')), + ('b', 'branch', [], + _('a specific branch you would like to push'), _('BRANCH')), + ('', 'new-branch', False, _('allow pushing a new branch')), + ] + remoteopts, + _('[-f] [-r REV]... [-e CMD] [--remotecmd CMD] [DEST]')), + "recover": (recover, []), + "^remove|rm": + (remove, + [('A', 'after', None, _('record delete for missing files')), + ('f', 'force', None, + _('remove (and delete) file even if added or modified')), + ] + walkopts, + _('[OPTION]... FILE...')), + "rename|move|mv": + (rename, + [('A', 'after', None, _('record a rename that has already occurred')), + ('f', 'force', None, + _('forcibly copy over an existing managed file')), + ] + walkopts + dryrunopts, + _('[OPTION]... SOURCE... DEST')), + "resolve": + (resolve, + [('a', 'all', None, _('select all unresolved files')), + ('l', 'list', None, _('list state of files needing merge')), + ('m', 'mark', None, _('mark files as resolved')), + ('u', 'unmark', None, _('mark files as unresolved')), + ('t', 'tool', '', _('specify merge tool')), + ('n', 'no-status', None, _('hide status prefix'))] + + walkopts, + _('[OPTION]... [FILE]...')), + "revert": + (revert, + [('a', 'all', None, _('revert all changes when no arguments given')), + ('d', 'date', '', + _('tipmost revision matching date'), _('DATE')), + ('r', 'rev', '', + _('revert to the specified revision'), _('REV')), + ('', 'no-backup', None, _('do not save backup copies of files')), + ] + walkopts + dryrunopts, + _('[OPTION]... [-r REV] [NAME]...')), + "rollback": (rollback, dryrunopts), + "root": (root, []), + "^serve": + (serve, + [('A', 'accesslog', '', + _('name of access log file to write to'), _('FILE')), + ('d', 'daemon', None, _('run server in background')), + ('', 'daemon-pipefds', '', + _('used internally by daemon mode'), _('NUM')), + ('E', 'errorlog', '', + _('name of error log file to write to'), _('FILE')), + # use string type, then we can check if something was passed + ('p', 'port', '', + _('port to listen on (default: 8000)'), _('PORT')), + ('a', 'address', '', + _('address to listen on (default: all interfaces)'), _('ADDR')), + ('', 'prefix', '', + _('prefix path to serve from (default: server root)'), _('PREFIX')), + ('n', 'name', '', + _('name to show in web pages (default: working directory)'), + _('NAME')), + ('', 'web-conf', '', + _('name of the hgweb config file (see "hg help hgweb")'), + _('FILE')), + ('', 'webdir-conf', '', + _('name of the hgweb config file (DEPRECATED)'), _('FILE')), + ('', 'pid-file', '', + _('name of file to write process ID to'), _('FILE')), + ('', 'stdio', None, _('for remote clients')), + ('t', 'templates', '', + _('web templates to use'), _('TEMPLATE')), + ('', 'style', '', + _('template style to use'), _('STYLE')), + ('6', 'ipv6', None, _('use IPv6 in addition to IPv4')), + ('', 'certificate', '', + _('SSL certificate file'), _('FILE'))], + _('[OPTION]...')), + "showconfig|debugconfig": + (showconfig, + [('u', 'untrusted', None, _('show untrusted configuration options'))], + _('[-u] [NAME]...')), + "^summary|sum": + (summary, + [('', 'remote', None, _('check for push and pull'))], '[--remote]'), + "^status|st": + (status, + [('A', 'all', None, _('show status of all files')), + ('m', 'modified', None, _('show only modified files')), + ('a', 'added', None, _('show only added files')), + ('r', 'removed', None, _('show only removed files')), + ('d', 'deleted', None, _('show only deleted (but tracked) files')), + ('c', 'clean', None, _('show only files without changes')), + ('u', 'unknown', None, _('show only unknown (not tracked) files')), + ('i', 'ignored', None, _('show only ignored files')), + ('n', 'no-status', None, _('hide status prefix')), + ('C', 'copies', None, _('show source of copied files')), + ('0', 'print0', None, + _('end filenames with NUL, for use with xargs')), + ('', 'rev', [], + _('show difference from revision'), _('REV')), + ('', 'change', '', + _('list the changed files of a revision'), _('REV')), + ] + walkopts + subrepoopts, + _('[OPTION]... [FILE]...')), + "tag": + (tag, + [('f', 'force', None, _('force tag')), + ('l', 'local', None, _('make the tag local')), + ('r', 'rev', '', + _('revision to tag'), _('REV')), + ('', 'remove', None, _('remove a tag')), + # -l/--local is already there, commitopts cannot be used + ('e', 'edit', None, _('edit commit message')), + ('m', 'message', '', + _('use as commit message'), _('TEXT')), + ] + commitopts2, + _('[-f] [-l] [-m TEXT] [-d DATE] [-u USER] [-r REV] NAME...')), + "tags": (tags, [], ''), + "tip": + (tip, + [('p', 'patch', None, _('show patch')), + ('g', 'git', None, _('use git extended diff format')), + ] + templateopts, + _('[-p] [-g]')), + "unbundle": + (unbundle, + [('u', 'update', None, + _('update to new branch head if changesets were unbundled'))], + _('[-u] FILE...')), + "^update|up|checkout|co": + (update, + [('C', 'clean', None, _('discard uncommitted changes (no backup)')), + ('c', 'check', None, + _('update across branches if no uncommitted changes')), + ('d', 'date', '', + _('tipmost revision matching date'), _('DATE')), + ('r', 'rev', '', + _('revision'), _('REV'))], + _('[-c] [-C] [-d DATE] [[-r] REV]')), + "verify": (verify, []), + "version": (version_, []), +} + +norepo = ("clone init version help debugcommands debugcomplete" + " debugdate debuginstall debugfsinfo debugpushkey") +optionalrepo = ("identify paths serve showconfig debugancestor debugdag" + " debugdata debugindex debugindexdot")