Mercurial > public > mercurial-scm > hg-stable
diff mercurial/commands.py @ 6042:2da5b19a6460
Merge with crew
author | Bryan O'Sullivan <bos@serpentine.com> |
---|---|
date | Wed, 06 Feb 2008 19:57:52 -0800 |
parents | 1d0bfa4c75c0 5af5f0f9d724 |
children | cfb4a51da7d5 |
line wrap: on
line diff
--- a/mercurial/commands.py Thu Jul 26 07:56:27 2007 -0400 +++ b/mercurial/commands.py Wed Feb 06 19:57:52 2008 -0800 @@ -5,14 +5,13 @@ # This software may be used and distributed according to the terms # of the GNU General Public License, incorporated herein by reference. -import demandimport; demandimport.enable() from node import * from i18n import _ -import bisect, os, re, sys, urllib, stat -import ui, hg, util, revlog, bundlerepo, extensions +import os, re, sys, urllib +import hg, util, revlog, bundlerepo, extensions import difflib, patch, time, help, mdiff, tempfile import errno, version, socket -import archival, changegroup, cmdutil, hgweb.server, sshserver +import archival, changegroup, cmdutil, hgweb.server, sshserver, hbisect # Commands start here, listed alphabetically @@ -27,17 +26,23 @@ If no names are given, add all files in the repository. """ + rejected = None + exacts = {} names = [] - for src, abs, rel, exact in cmdutil.walk(repo, pats, opts): + for src, abs, rel, exact in cmdutil.walk(repo, pats, opts, + badmatch=util.always): if exact: if ui.verbose: ui.status(_('adding %s\n') % rel) names.append(abs) + exacts[abs] = 1 elif abs not in repo.dirstate: ui.status(_('adding %s\n') % rel) names.append(abs) if not opts.get('dry_run'): - repo.add(names) + rejected = repo.add(names) + rejected = [p for p in rejected if p in exacts] + return rejected and 1 or 0 def addremove(ui, repo, *pats, **opts): """add all new files, delete all missing files @@ -191,10 +196,13 @@ raise util.Abort(_("please specify a revision to backout")) cmdutil.bail_if_changed(repo) + node = repo.lookup(rev) + op1, op2 = repo.dirstate.parents() - if op2 != nullid: - raise util.Abort(_('outstanding uncommitted merge')) - node = repo.lookup(rev) + a = repo.changelog.ancestor(op1, node) + if a != node: + raise util.Abort(_('cannot back out change on a different branch')) + p1, p2 = repo.changelog.parents(node) if p1 == nullid: raise util.Abort(_('cannot back out a change with no parents')) @@ -211,6 +219,7 @@ if opts['parent']: raise util.Abort(_('cannot use --parent on non-merge changeset')) parent = p1 + hg.clean(repo, node, show_stats=False) revert_opts = opts.copy() revert_opts['date'] = None @@ -237,6 +246,90 @@ ui.status(_('(use "backout --merge" ' 'if you want to auto-merge)\n')) +def bisect(ui, repo, rev=None, extra=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. Once you have performed tests, mark the + working directory as bad or good and bisect will either update to + another candidate changeset or announce that it has found the bad + revision. + """ + # backward compatibility + if rev in "good bad reset init".split(): + ui.warn(_("(use of 'hg bisect <cmd>' 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 > 1: + raise util.Abort("Incompatible arguments") + + if reset: + p = repo.join("bisect.state") + if os.path.exists(p): + os.unlink(p) + return + + # load state + state = {'good': [], 'bad': [], 'skip': []} + if os.path.exists(repo.join("bisect.state")): + for l in repo.opener("bisect.state"): + kind, node = l[:-1].split() + node = repo.lookup(node) + if kind not in state: + raise util.Abort(_("unknown bisect kind %s") % kind) + state[kind].append(node) + + # update state + node = repo.lookup(rev or '.') + if good: + state['good'].append(node) + elif bad: + state['bad'].append(node) + elif skip: + state['skip'].append(node) + + # save state + f = repo.opener("bisect.state", "w", atomictemp=True) + wlock = repo.wlock() + try: + for kind in state: + for node in state[kind]: + f.write("%s %s\n" % (kind, hg.hex(node))) + f.rename() + finally: + del wlock + + if not state['good'] or not state['bad']: + return + + # actually bisect + node, changesets, good = hbisect.bisect(repo.changelog, state) + if changesets == 0: + ui.write(_("The first %s revision is:\n") % (good and "good" or "bad")) + displayer = cmdutil.show_changeset(ui, repo, {}) + displayer.show(changenode=node) + elif node is not None: + # 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 %s:%s " + "(%s changesets remaining, ~%s tests)\n") + % (rev, hg.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 @@ -246,6 +339,8 @@ Unless --force is specified, branch will not let you set a branch name that shadows an existing branch. + + Use the command 'hg update' to switch to an existing branch. """ if label: @@ -265,6 +360,8 @@ inactive. If active is specified, only show active branches. A branch is considered active if it contains unmerged heads. + + Use the command 'hg update' to switch to an existing branch. """ b = repo.branchtags() heads = dict.fromkeys(repo.heads(), 1) @@ -295,6 +392,7 @@ If no destination repository is specified the destination is assumed to have all the nodes specified by one or more --base parameters. + To create a bundle containing all changesets, use --base null. The bundle file can then be transferred using conventional means and applied to another repository with the unbundle or pull command. @@ -336,7 +434,7 @@ visit.append(p) else: cmdutil.setremoteconfig(ui, opts) - dest, revs = cmdutil.parseurl( + dest, revs, checkout = hg.parseurl( ui.expandpath(dest or 'default-push', dest or 'default'), revs) other = hg.repository(ui, dest) o = repo.findoutgoing(other, force=opts['force']) @@ -425,244 +523,13 @@ If a list of files is omitted, all changes reported by "hg status" will be committed. - If no commit message is specified, the editor configured in your hgrc - or in the EDITOR environment variable is started to enter a message. + If no commit message is specified, the configured editor is started to + enter a message. """ - message = cmdutil.logmessage(opts) - - if opts['addremove']: - cmdutil.addremove(repo, pats, opts) - fns, match, anypats = cmdutil.matchpats(repo, pats, opts) - if pats: - status = repo.status(files=fns, match=match) - modified, added, removed, deleted, unknown = status[:5] - files = modified + added + removed - slist = None - for f in fns: - if f == '.': - continue - if f not in files: - rf = repo.wjoin(f) - try: - mode = os.lstat(rf)[stat.ST_MODE] - except OSError: - raise util.Abort(_("file %s not found!") % rf) - if stat.S_ISDIR(mode): - name = f + '/' - if slist is None: - slist = list(files) - slist.sort() - i = bisect.bisect(slist, name) - if i >= len(slist) or not slist[i].startswith(name): - raise util.Abort(_("no match under directory %s!") - % rf) - elif not (stat.S_ISREG(mode) or stat.S_ISLNK(mode)): - raise util.Abort(_("can't commit %s: " - "unsupported file type!") % rf) - elif f not in repo.dirstate: - raise util.Abort(_("file %s not tracked!") % rf) - else: - files = [] - try: - repo.commit(files, message, opts['user'], opts['date'], match, - force_editor=opts.get('force_editor')) - except ValueError, inst: - raise util.Abort(str(inst)) - -def docopy(ui, repo, pats, opts): - # called with the repo lock held - # - # hgsep => pathname that uses "/" to separate directories - # ossep => pathname that uses os.sep to separate directories - cwd = repo.getcwd() - errors = 0 - copied = [] - targets = {} - - # abs: hgsep - # rel: ossep - # return: hgsep - def okaytocopy(abs, rel, exact): - reasons = {'?': _('is not managed'), - 'r': _('has been marked for remove')} - state = repo.dirstate[abs] - reason = reasons.get(state) - if reason: - if exact: - ui.warn(_('%s: not copying - file %s\n') % (rel, reason)) - else: - if state == 'a': - origsrc = repo.dirstate.copied(abs) - if origsrc is not None: - return origsrc - return abs - - # origsrc: hgsep - # abssrc: hgsep - # relsrc: ossep - # otarget: ossep - def copy(origsrc, abssrc, relsrc, otarget, exact): - abstarget = util.canonpath(repo.root, cwd, otarget) - reltarget = repo.pathto(abstarget, cwd) - prevsrc = targets.get(abstarget) - src = repo.wjoin(abssrc) - target = repo.wjoin(abstarget) - if prevsrc is not None: - ui.warn(_('%s: not overwriting - %s collides with %s\n') % - (reltarget, repo.pathto(abssrc, cwd), - repo.pathto(prevsrc, cwd))) - return - if (not opts['after'] and os.path.exists(target) or - opts['after'] and repo.dirstate[abstarget] in 'mn'): - if not opts['force']: - ui.warn(_('%s: not overwriting - file exists\n') % - reltarget) - return - if not opts['after'] and not opts.get('dry_run'): - os.unlink(target) - if opts['after']: - if not os.path.exists(target): - return - else: - targetdir = os.path.dirname(target) or '.' - if not os.path.isdir(targetdir) and not opts.get('dry_run'): - os.makedirs(targetdir) - try: - restore = repo.dirstate[abstarget] == 'r' - if restore and not opts.get('dry_run'): - repo.undelete([abstarget]) - try: - if not opts.get('dry_run'): - util.copyfile(src, target) - restore = False - finally: - if restore: - repo.remove([abstarget]) - except IOError, inst: - if inst.errno == errno.ENOENT: - ui.warn(_('%s: deleted in working copy\n') % relsrc) - else: - ui.warn(_('%s: cannot copy - %s\n') % - (relsrc, inst.strerror)) - errors += 1 - return - if ui.verbose or not exact: - ui.status(_('copying %s to %s\n') % (relsrc, reltarget)) - targets[abstarget] = abssrc - if abstarget != origsrc: - if repo.dirstate[origsrc] == 'a': - if not ui.quiet: - ui.warn(_("%s has not been committed yet, so no copy " - "data will be stored for %s.\n") - % (repo.pathto(origsrc, cwd), reltarget)) - if abstarget not in repo.dirstate and not opts.get('dry_run'): - repo.add([abstarget]) - elif not opts.get('dry_run'): - repo.copy(origsrc, abstarget) - copied.append((abssrc, relsrc, exact)) - - # pat: ossep - # dest ossep - # srcs: list of (hgsep, hgsep, ossep, bool) - # return: function that takes hgsep and returns ossep - def targetpathfn(pat, dest, srcs): - if os.path.isdir(pat): - abspfx = util.canonpath(repo.root, cwd, pat) - abspfx = util.localpath(abspfx) - if destdirexists: - striplen = len(os.path.split(abspfx)[0]) - else: - striplen = len(abspfx) - if striplen: - striplen += len(os.sep) - res = lambda p: os.path.join(dest, util.localpath(p)[striplen:]) - elif destdirexists: - res = lambda p: os.path.join(dest, - os.path.basename(util.localpath(p))) - else: - res = lambda p: dest - return res - - # pat: ossep - # dest ossep - # srcs: list of (hgsep, hgsep, ossep, bool) - # return: function that takes hgsep and returns ossep - def targetpathafterfn(pat, dest, srcs): - if util.patkind(pat, None)[0]: - # a mercurial pattern - res = lambda p: os.path.join(dest, - os.path.basename(util.localpath(p))) - else: - abspfx = util.canonpath(repo.root, cwd, pat) - if len(abspfx) < len(srcs[0][0]): - # A directory. Either the target path contains the last - # component of the source path or it does not. - def evalpath(striplen): - score = 0 - for s in srcs: - t = os.path.join(dest, util.localpath(s[0])[striplen:]) - if os.path.exists(t): - score += 1 - return score - - abspfx = util.localpath(abspfx) - striplen = len(abspfx) - if striplen: - striplen += len(os.sep) - if os.path.isdir(os.path.join(dest, os.path.split(abspfx)[1])): - score = evalpath(striplen) - striplen1 = len(os.path.split(abspfx)[0]) - if striplen1: - striplen1 += len(os.sep) - if evalpath(striplen1) > score: - striplen = striplen1 - res = lambda p: os.path.join(dest, - util.localpath(p)[striplen:]) - else: - # a file - if destdirexists: - res = lambda p: os.path.join(dest, - os.path.basename(util.localpath(p))) - else: - res = lambda p: dest - return res - - - pats = util.expand_glob(pats) - if not pats: - raise util.Abort(_('no source or destination specified')) - if len(pats) == 1: - raise util.Abort(_('no destination specified')) - dest = pats.pop() - destdirexists = os.path.isdir(dest) - if (len(pats) > 1 or util.patkind(pats[0], None)[0]) and not destdirexists: - raise util.Abort(_('with multiple sources, destination must be an ' - 'existing directory')) - if opts['after']: - tfn = targetpathafterfn - else: - tfn = targetpathfn - copylist = [] - for pat in pats: - srcs = [] - for tag, abssrc, relsrc, exact in cmdutil.walk(repo, [pat], opts, - globbed=True): - origsrc = okaytocopy(abssrc, relsrc, exact) - if origsrc: - srcs.append((origsrc, abssrc, relsrc, exact)) - if not srcs: - continue - copylist.append((tfn(pat, dest, srcs), srcs)) - if not copylist: - raise util.Abort(_('no files to copy')) - - for targetpath, srcs in copylist: - for origsrc, abssrc, relsrc, exact in srcs: - copy(origsrc, abssrc, relsrc, targetpath(abssrc), exact) - - if errors: - ui.warn(_('(consider using --after)\n')) - return errors, copied + def commitfunc(ui, repo, files, message, match, opts): + return repo.commit(files, message, opts['user'], opts['date'], match, + force_editor=opts.get('force_editor')) + cmdutil.commit(ui, repo, commitfunc, pats, opts) def copy(ui, repo, *pats, **opts): """mark files as copied for the next commit @@ -680,10 +547,9 @@ """ wlock = repo.wlock(False) try: - errs, copied = docopy(ui, repo, pats, opts) + return cmdutil.copy(ui, repo, pats, opts) finally: del wlock - return errs def debugancestor(ui, index, rev1, rev2): """find the ancestor revision of two revisions in a given index""" @@ -698,7 +564,7 @@ options = [] otables = [globalopts] if cmd: - aliases, entry = cmdutil.findcmd(ui, cmd) + aliases, entry = cmdutil.findcmd(ui, cmd, table) otables.append(entry[1]) for t in otables: for o in t: @@ -708,10 +574,18 @@ ui.write("%s\n" % "\n".join(options)) return - clist = cmdutil.findpossible(ui, cmd).keys() + clist = cmdutil.findpossible(ui, cmd, table).keys() clist.sort() ui.write("%s\n" % "\n".join(clist)) +def debugfsinfo(ui, path = "."): + file('.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.checkfolding('.debugfsinfo') + and 'yes' or 'no')) + os.unlink('.debugfsinfo') + def debugrebuildstate(ui, repo, rev=""): """rebuild the dirstate as it would look like for the given revision""" if rev == "": @@ -795,20 +669,21 @@ def debugstate(ui, repo): """show the contents of the current dirstate""" - dc = repo.dirstate._map - k = dc.keys() + k = repo.dirstate._map.items() k.sort() - for file_ in k: - if dc[file_][3] == -1: + for file_, ent in k: + if ent[3] == -1: # Pad or slice to locale representation - locale_len = len(time.strftime("%x %X", time.localtime(0))) + 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("%x %X", time.localtime(dc[file_][3])) - ui.write("%c %3o %10d %s %s\n" - % (dc[file_][0], dc[file_][1] & 0777, dc[file_][2], - timestr, file_)) + 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)) @@ -839,7 +714,10 @@ " nodeid p1 p2\n") for i in xrange(r.count()): node = r.node(i) - pp = r.parents(node) + 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(node), short(node), short(pp[0]), short(pp[1]))) @@ -899,82 +777,45 @@ # patch ui.status(_("Checking patch...\n")) - patcher = ui.config('ui', 'patch') - patcher = ((patcher and util.find_exe(patcher)) or - util.find_exe('gpatch') or - util.find_exe('patch')) - if not patcher: - ui.write(_(" Can't find patch or gpatch in PATH\n")) - ui.write(_(" (specify a patch utility in your .hgrc file)\n")) - problems += 1 + 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: - # actually attempt a patch here - 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)) - 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") - problems += 1 + if list(files) != [os.path.basename(fa)]: + ui.write(_(" unexpected patch output!\n")) + patchproblems += 1 + a = file(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 .hgrc file)\n")) else: - if list(files) != [os.path.basename(fa)]: - ui.write(_(" unexpected patch output!")) - ui.write(_(" (you may have an incompatible version of patch)\n")) - problems += 1 - a = file(fa).read() - if a != b: - ui.write(_(" patch test failed!")) - ui.write(_(" (you may have an incompatible version of patch)\n")) - problems += 1 - - os.unlink(fa) - os.unlink(fd) - - # merge helper - ui.status(_("Checking merge helper...\n")) - cmd = (os.environ.get("HGMERGE") or ui.config("ui", "merge") - or "hgmerge") - cmdpath = util.find_exe(cmd) or util.find_exe(cmd.split()[0]) - if not cmdpath: - if cmd == 'hgmerge': - ui.write(_(" No merge helper set and can't find default" - " hgmerge script in PATH\n")) - ui.write(_(" (specify a merge helper in your .hgrc file)\n")) - else: - ui.write(_(" Can't find merge helper '%s' in PATH\n") % cmd) - ui.write(_(" (specify a merge helper in your .hgrc file)\n")) - problems += 1 - else: - # actually attempt a patch here - fa = writetemp("1\n2\n3\n4\n") - fl = writetemp("1\n2\n3\ninsert\n4\n") - fr = writetemp("begin\n1\n2\n3\n4\n") - r = util.system('%s "%s" "%s" "%s"' % (cmd, fl, fa, fr)) - if r: - ui.write(_(" Got unexpected merge error %d!\n") % r) - problems += 1 - m = file(fl).read() - if m != "begin\n1\n2\n3\ninsert\n4\n": - ui.write(_(" Got unexpected merge results!\n")) - ui.write(_(" (your merge helper may have the" - " wrong argument order)\n")) - ui.write(_(" Result: %r\n") % m) - problems += 1 - os.unlink(fa) - os.unlink(fl) - os.unlink(fr) + ui.write(_(" Internal patcher failure, please report this error" + " to http://www.selenic.com/mercurial/bts\n")) + problems += patchproblems + + os.unlink(fa) + os.unlink(fd) # editor ui.status(_("Checking commit editor...\n")) - editor = (os.environ.get("HGEDITOR") or - ui.config("ui", "editor") or - os.environ.get("EDITOR", "vi")) + editor = ui.geteditor() cmdpath = util.find_exe(editor) or util.find_exe(editor.split()[0]) if not cmdpath: if editor == 'vi': @@ -1011,7 +852,8 @@ ctx = repo.changectx(opts.get('rev', 'tip')) for src, abs, rel, exact in cmdutil.walk(repo, (file1,) + pats, opts, ctx.node()): - m = ctx.filectx(abs).renamed() + fctx = ctx.filectx(abs) + m = fctx.filelog().renamed(fctx.filenode()) if m: ui.write(_("%s renamed from %s:%s\n") % (rel, m[0], hex(m[1]))) else: @@ -1223,9 +1065,9 @@ for fn in fns: if fn in skip: continue - fstate.setdefault(fn, {}) try: grepbody(fn, rev, getfile(fn).read(mf[fn])) + fstate.setdefault(fn, []) if follow: copied = getfile(fn).renamed(mf[fn]) if copied: @@ -1342,9 +1184,13 @@ if with_version: version_(ui) ui.write('\n') - aliases, i = cmdutil.findcmd(ui, name) + aliases, i = cmdutil.findcmd(ui, name, table) # synopsis - ui.write("%s\n\n" % i[2]) + ui.write("%s\n" % i[2]) + + # aliases + if not ui.quiet and len(aliases) > 1: + ui.write(_("\naliases: %s\n") % ', '.join(aliases[1:])) # description doc = i[0].__doc__ @@ -1352,13 +1198,9 @@ doc = _("(No help text available)") if ui.quiet: doc = doc.splitlines(0)[0] - ui.write("%s\n" % doc.rstrip()) + ui.write("\n%s\n" % doc.rstrip()) if not ui.quiet: - # aliases - if len(aliases) > 1: - ui.write(_("\naliases: %s\n") % ', '.join(aliases[1:])) - # options if i[1]: option_lists.append((_("options:\n"), i[1])) @@ -1505,12 +1347,16 @@ name for non-default branches. """ + 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 = [] if source: - source, revs = cmdutil.parseurl(ui.expandpath(source), []) + source, revs, checkout = hg.parseurl(ui.expandpath(source), []) srepo = hg.repository(ui, source) if not rev and revs: rev = revs[0] @@ -1603,8 +1449,10 @@ data = patch.extract(ui, sys.stdin) else: ui.status(_("applying %s\n") % p) - data = patch.extract(ui, file(pf, 'rb')) - + if os.path.exists(pf): + data = patch.extract(ui, file(pf, 'rb')) + else: + data = patch.extract(ui, urllib.urlopen(pf)) tmpname, message, user, date, branch, nodeid, p1, p2 = data if tmpname is None: @@ -1650,16 +1498,21 @@ files=files) finally: files = patch.updatedir(ui, repo, files) - n = repo.commit(files, message, user, date) - if opts.get('exact'): - if hex(n) != nodeid: - repo.rollback() - raise util.Abort(_('patch is damaged' + - ' or loses information')) + if not opts.get('no_commit'): + n = repo.commit(files, message, opts.get('user') or user, + opts.get('date') or date) + 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() finally: os.unlink(tmpname) finally: - del wlock, lock + del lock, wlock def incoming(ui, repo, source="default", **opts): """show new changesets found in source @@ -1673,17 +1526,13 @@ See pull for valid source format details. """ - source, revs = cmdutil.parseurl(ui.expandpath(source), opts['rev']) + source, revs, checkout = hg.parseurl(ui.expandpath(source), opts['rev']) cmdutil.setremoteconfig(ui, opts) other = hg.repository(ui, source) - ui.status(_('comparing with %s\n') % source) + ui.status(_('comparing with %s\n') % util.hidepassword(source)) if revs: - if 'lookup' in other.capabilities: - revs = [other.lookup(rev) for rev in revs] - else: - error = _("Other repository doesn't support revision lookup, so a rev cannot be specified.") - raise util.Abort(error) + revs = [other.lookup(rev) for rev in revs] incoming = repo.findincoming(other, heads=revs, force=opts["force"]) if not incoming: try: @@ -1701,8 +1550,6 @@ if revs is None: cg = other.changegroup(incoming, "incoming") else: - if 'changegroupsubset' not in other.capabilities: - raise util.Abort(_("Partial incoming cannot be done because other repository doesn't support changegroupsubset.")) cg = other.changegroupsubset(incoming, revs, 'incoming') bundletype = other.local() and "HG10BZ" or "HG10UN" fname = cleanup = changegroup.writebundle(cg, fname, bundletype) @@ -1831,8 +1678,7 @@ endrev = repo.changelog.count() rcache = {} ncache = {} - dcache = [] - def getrenamed(fn, rev, man): + def getrenamed(fn, rev): '''looks up all renames for a file (up to endrev) the first time the file is given. It indexes on the changerev and only parses the manifest if linkrev != changerev. @@ -1852,13 +1698,14 @@ break if rev in rcache[fn]: return rcache[fn][rev] - mr = repo.manifest.rev(man) - if repo.manifest.parentrevs(mr) != (mr - 1, nullrev): - return ncache[fn].get(repo.manifest.find(man, fn)[0]) - if not dcache or dcache[0] != man: - dcache[:] = [man, repo.manifest.readdelta(man)] - if fn in dcache[1]: - return ncache[fn].get(dcache[1][fn]) + + # If linkrev != rev (i.e. rev not found in rcache) fallback to + # filectx logic. + + try: + return repo.changectx(rev).filectx(fn).renamed() + except revlog.LookupError: + pass return None df = False @@ -1895,9 +1742,8 @@ copies = [] if opts.get('copies') and rev: - mf = get(rev)[0] for fn in get(rev)[3]: - rename = getrenamed(fn, rev, mf) + rename = getrenamed(fn, rev) if rename: copies.append((fn, rename[0])) displayer.show(rev, changenode, copies=copies) @@ -1906,7 +1752,7 @@ if displayer.flush(rev): count += 1 -def manifest(ui, repo, rev=None): +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. @@ -1916,11 +1762,17 @@ The manifest is the list of files being version controlled. If no revision is given then the first parent of the working directory is used. - With -v flag, print file permissions. With --debug flag, print - file revision hashes. + With -v flag, print file permissions, symlink and executable bits. With + --debug flag, print file revision hashes. """ - m = repo.changectx(rev).manifest() + if rev and node: + raise util.Abort(_("please specify just one revision")) + + if not node: + node = rev + + m = repo.changectx(node).manifest() files = m.keys() files.sort() @@ -1928,7 +1780,9 @@ if ui.debugflag: ui.write("%40s " % hex(m[f])) if ui.verbose: - ui.write("%3s " % (m.execf(f) and "755" or "644")) + type = m.execf(f) and "*" or m.linkf(f) and "@" or " " + perm = m.execf(f) and "755" or "644" + ui.write("%3s %1s " % (perm, type)) ui.write("%s\n" % f) def merge(ui, repo, node=None, force=None, rev=None): @@ -1947,7 +1801,6 @@ if rev and node: raise util.Abort(_("please specify just one revision")) - if not node: node = rev @@ -1957,10 +1810,13 @@ raise util.Abort(_('repo has %d heads - ' 'please merge with an explicit rev') % len(heads)) + parent = repo.dirstate.parents()[0] if len(heads) == 1: - raise util.Abort(_('there is nothing to merge - ' - 'use "hg update" instead')) - parent = repo.dirstate.parents()[0] + msg = _('there is nothing to merge') + if parent != repo.lookup(repo.workingctx().branch()): + msg = _('%s - use "hg update" instead') % msg + raise util.Abort(msg) + if parent not in heads: raise util.Abort(_('working dir not at a head rev - ' 'use "hg update" or merge with an explicit rev')) @@ -1976,14 +1832,14 @@ See pull for valid destination format details. """ - dest, revs = cmdutil.parseurl( + dest, revs, checkout = hg.parseurl( ui.expandpath(dest or 'default-push', dest or 'default'), opts['rev']) cmdutil.setremoteconfig(ui, opts) if revs: revs = [repo.lookup(rev) for rev in revs] other = hg.repository(ui, dest) - ui.status(_('comparing with %s\n') % dest) + ui.status(_('comparing with %s\n') % util.hidepassword(dest)) o = repo.findoutgoing(other, force=opts['force']) if not o: ui.status(_("no changes found\n")) @@ -2008,16 +1864,30 @@ revision or the argument to --rev if given) is printed. """ rev = opts.get('rev') + if rev: + ctx = repo.changectx(rev) + else: + ctx = repo.workingctx() + if file_: files, match, anypats = cmdutil.matchpats(repo, (file_,), opts) if anypats or len(files) != 1: raise util.Abort(_('can only specify an explicit file name')) - ctx = repo.filectx(files[0], changeid=rev) - elif rev: - ctx = repo.changectx(rev) + file_ = files[0] + filenodes = [] + for cp in ctx.parents(): + if not cp: + continue + try: + filenodes.append(cp.filenode(file_)) + except revlog.LookupError: + pass + if not filenodes: + raise util.Abort(_("'%s' not found in manifest!") % file_) + fl = repo.file(file_) + p = [repo.lookup(fl.linkrev(fn)) for fn in filenodes] else: - ctx = repo.workingctx() - p = [cp.node() for cp in ctx.parents()] + p = [cp.node() for cp in ctx.parents()] displayer = cmdutil.show_changeset(ui, repo, opts) for n in p: @@ -2044,12 +1914,12 @@ for name, path in ui.configitems("paths"): ui.write("%s = %s\n" % (name, path)) -def postincoming(ui, repo, modheads, optupdate): +def postincoming(ui, repo, modheads, optupdate, checkout): if modheads == 0: return if optupdate: - if modheads == 1: - return hg.update(repo, None) + if modheads <= 1 or checkout: + return hg.update(repo, checkout) else: ui.status(_("not updating, since new heads added\n")) if modheads > 1: @@ -2098,20 +1968,21 @@ Alternatively specify "ssh -C" as your ssh command in your hgrc or with the --ssh command line option. """ - source, revs = cmdutil.parseurl(ui.expandpath(source), opts['rev']) + source, revs, checkout = hg.parseurl(ui.expandpath(source), opts['rev']) cmdutil.setremoteconfig(ui, opts) other = hg.repository(ui, source) - ui.status(_('pulling from %s\n') % (source)) + ui.status(_('pulling from %s\n') % util.hidepassword(source)) if revs: - if 'lookup' in other.capabilities: + try: revs = [other.lookup(rev) for rev in revs] - else: - error = _("Other repository doesn't support revision lookup, so a rev cannot be specified.") + except repo.NoCapability: + error = _("Other repository doesn't support revision lookup, " + "so a rev cannot be specified.") raise util.Abort(error) modheads = repo.pull(other, heads=revs, force=opts['force']) - return postincoming(ui, repo, modheads, opts['update']) + return postincoming(ui, repo, modheads, opts['update'], checkout) def push(ui, repo, dest=None, **opts): """push changes to the specified destination @@ -2143,12 +2014,12 @@ Pushing to http:// and https:// URLs is only possible, if this feature is explicitly enabled on the remote Mercurial server. """ - dest, revs = cmdutil.parseurl( + dest, revs, checkout = hg.parseurl( ui.expandpath(dest or 'default-push', dest or 'default'), opts['rev']) cmdutil.setremoteconfig(ui, opts) other = hg.repository(ui, dest) - ui.status('pushing to %s\n' % (dest)) + ui.status('pushing to %s\n' % util.hidepassword(dest)) if revs: revs = [repo.lookup(rev) for rev in revs] r = repo.push(other, opts['force'], revs=revs) @@ -2228,6 +2099,7 @@ forget.append(abs) continue reason = _('has been marked for add (use -f to force removal)') + exact = 1 # force the message elif abs not in repo.dirstate: reason = _('is not managed') elif opts['after'] and not exact and abs not in deleted: @@ -2260,20 +2132,15 @@ """ wlock = repo.wlock(False) try: - errs, copied = docopy(ui, repo, pats, opts) - names = [] - for abs, rel, exact in copied: - if ui.verbose or not exact: - ui.status(_('removing %s\n') % rel) - names.append(abs) - if not opts.get('dry_run'): - repo.remove(names, True) - return errs + return cmdutil.copy(ui, repo, pats, opts, rename=True) finally: del wlock def revert(ui, repo, *pats, **opts): - """revert files or dirs to their states as of some revision + """restore individual files or dirs to an earlier state + + (use update -r to check out earlier revisions, revert does not + change the working dir parents) With no revision specified, revert the named files or directories to the contents they had in the parent of the working directory. @@ -2282,12 +2149,9 @@ working directory has two parents, you must explicitly specify the revision to revert to. - Modified files are saved with a .orig suffix before reverting. - To disable these backups, use --no-backup. - Using the -r 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 a change that should not have been committed. + back" some or all of an earlier change. Revert modifies the working directory. It does not commit any changes, or change the parent of the working directory. If you @@ -2301,6 +2165,9 @@ 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. """ if opts["date"]: @@ -2361,6 +2228,14 @@ changes = repo.status(match=names.has_key)[:5] modified, added, removed, deleted, unknown = map(dict.fromkeys, 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[src] = None + names[src] = (repo.pathto(src, cwd), True) + revert = ([], _('reverting %s\n')) add = ([], _('adding %s\n')) remove = ([], _('removing %s\n')) @@ -2443,10 +2318,12 @@ del wlock def rollback(ui, repo): - """roll back the last transaction in this repository - - Roll back the last transaction in this repository, restoring the - project to its state prior to the transaction. + """roll back the last transaction + + 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. Transactions are used to encapsulate the effects of all commands that create new changesets or propagate existing changesets into a @@ -2459,11 +2336,6 @@ push (with this repository as destination) unbundle - 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, which - may lose subsequent dirstate changes. - 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 @@ -2497,12 +2369,12 @@ s.serve_forever() parentui = ui.parentui or ui - optlist = ("name templates style address port ipv6" + optlist = ("name templates style address port prefix ipv6" " accesslog errorlog webdir_conf certificate") for o in optlist.split(): if opts[o]: parentui.setconfig("web", o, str(opts[o])) - if repo.ui != parentui: + if (repo is not None) and (repo.ui != parentui): repo.ui.setconfig("web", o, str(opts[o])) if repo is None and not ui.config("web", "webdir_conf"): @@ -2519,11 +2391,17 @@ if not ui.verbose: return + if self.httpd.prefix: + prefix = self.httpd.prefix.strip('/') + '/' + else: + prefix = '' + if self.httpd.port != 80: - ui.status(_('listening at http://%s:%d/\n') % - (self.httpd.addr, self.httpd.port)) + ui.status(_('listening at http://%s:%d/%s\n') % + (self.httpd.addr, self.httpd.port, prefix)) else: - ui.status(_('listening at http://%s/\n') % self.httpd.addr) + ui.status(_('listening at http://%s/%s\n') % + (self.httpd.addr, prefix)) def run(self): self.httpd.serve_forever() @@ -2536,8 +2414,10 @@ """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, are - not listed unless -c (clean), -i (ignored) or -A is given. + files that match are shown. Files that are clean or ignored or + source of a copy/move operation, are not listed unless -c (clean), + -i (ignored), -C (copies) or -A is given. Unless options described + with "show only ..." are given, the options -mardu are used. NOTE: status may appear to disagree with diff if permissions have changed or a merge has occurred. The standard diff format does not @@ -2554,7 +2434,7 @@ C = clean ! = deleted, but still tracked ? = not tracked - I = ignored (not shown by default) + I = ignored = the previous added file was copied from here """ @@ -2626,8 +2506,15 @@ rev_ = opts['rev'] message = opts['message'] if opts['remove']: - if not name in repo.tags(): + tagtype = repo.tagtype(name) + + if not tagtype: raise util.Abort(_('tag %s does not exist') % name) + if opts['local'] and tagtype == 'global': + raise util.Abort(_('%s tag is global') % name) + if not opts['local'] and tagtype == 'local': + raise util.Abort(_('%s tag is local') % name) + rev_ = nullid if not message: message = _('Removed tag %s') % name @@ -2649,23 +2536,33 @@ List the repository tags. - This lists both regular and local tags. + This lists both regular and local tags. When the -v/--verbose switch + is used, a third column "local" is printed for local tags. """ l = repo.tagslist() l.reverse() hexfunc = ui.debugflag and hex or short + tagtype = "" + for t, n in l: + if ui.quiet: + ui.write("%s\n" % t) + continue + try: hn = hexfunc(n) - r = "%5d:%s" % (repo.changelog.rev(n), hexfunc(n)) + r = "%5d:%s" % (repo.changelog.rev(n), hn) except revlog.LookupError: r = " ?:%s" % hn - if ui.quiet: - ui.write("%s\n" % t) else: spaces = " " * (30 - util.locallen(t)) - ui.write("%s%s %s\n" % (t, spaces, r)) + 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 @@ -2689,7 +2586,7 @@ gen = changegroup.readbundle(f, fname) modheads = repo.addchangegroup(gen, 'unbundle', 'bundle:' + fname) - return postincoming(ui, repo, modheads, opts['update']) + return postincoming(ui, repo, modheads, opts['update'], None) def update(ui, repo, node=None, rev=None, clean=False, date=None): """update working directory @@ -2740,7 +2637,7 @@ ui.write(_("Mercurial Distributed SCM (version %s)\n") % version.get_version()) ui.status(_( - "\nCopyright (C) 2005-2007 Matt Mackall <mpm@selenic.com> and others\n" + "\nCopyright (C) 2005-2008 Matt Mackall <mpm@selenic.com> 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" @@ -2787,6 +2684,11 @@ ('l', 'logfile', '', _('read commit message from <file>')), ] +commitopts2 = [ + ('d', 'date', '', _('record datecode as commit date')), + ('u', 'user', '', _('record user as committer')), +] + table = { "^add": (add, walkopts + dryrunopts, _('hg add [OPTION]... [FILE]...')), "addremove": @@ -2820,17 +2722,23 @@ (backout, [('', 'merge', None, _('merge with old dirstate parent after backout')), - ('d', 'date', '', _('record datecode as commit date')), ('', 'parent', '', _('parent to choose when backing out merge')), - ('u', 'user', '', _('record user as committer')), ('r', 'rev', '', _('revision to backout')), - ] + walkopts + commitopts, + ] + walkopts + commitopts + commitopts2, _('hg backout [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')), + ('U', 'noupdate', False, _('do not update to target'))], + _("hg bisect [-gbsr] [REV]")), "branch": (branch, [('f', 'force', None, _('set branch name even if it shadows an existing branch'))], - _('hg branch [NAME]')), + _('hg branch [-f] [NAME]')), "branches": (branches, [('a', 'active', False, @@ -2866,9 +2774,7 @@ (commit, [('A', 'addremove', None, _('mark new/missing files as added/removed before committing')), - ('d', 'date', '', _('record datecode as commit date')), - ('u', 'user', '', _('record user as commiter')), - ] + walkopts + commitopts, + ] + walkopts + commitopts + commitopts2, _('hg commit [OPTION]... [FILE]...')), "copy|cp": (copy, @@ -2877,31 +2783,41 @@ _('forcibly copy over an existing managed file')), ] + walkopts + dryrunopts, _('hg copy [OPTION]... [SOURCE]... DEST')), - "debugancestor": (debugancestor, [], _('debugancestor INDEX REV1 REV2')), + "debugancestor": (debugancestor, [], _('hg debugancestor INDEX REV1 REV2')), + "debugcheckstate": (debugcheckstate, [], _('hg debugcheckstate')), "debugcomplete": (debugcomplete, [('o', 'options', None, _('show the command options'))], - _('debugcomplete [-o] CMD')), - "debuginstall": (debuginstall, [], _('debuginstall')), + _('hg debugcomplete [-o] CMD')), + "debugdate": + (debugdate, + [('e', 'extended', None, _('try extended date formats'))], + _('hg debugdate [-e] DATE [RANGE]')), + "debugdata": (debugdata, [], _('hg debugdata FILE REV')), + "debugfsinfo": (debugfsinfo, [], _('hg debugfsinfo [PATH]')), + "debugindex": (debugindex, [], _('hg debugindex FILE')), + "debugindexdot": (debugindexdot, [], _('hg debugindexdot FILE')), + "debuginstall": (debuginstall, [], _('hg debuginstall')), + "debugrawcommit|rawcommit": + (rawcommit, + [('p', 'parent', [], _('parent')), + ('F', 'files', '', _('file list')) + ] + commitopts + commitopts2, + _('hg debugrawcommit [OPTION]... [FILE]...')), "debugrebuildstate": (debugrebuildstate, [('r', 'rev', '', _('revision to rebuild to'))], - _('debugrebuildstate [-r REV] [REV]')), - "debugcheckstate": (debugcheckstate, [], _('debugcheckstate')), - "debugsetparents": (debugsetparents, [], _('debugsetparents REV1 [REV2]')), - "debugstate": (debugstate, [], _('debugstate')), - "debugdate": - (debugdate, - [('e', 'extended', None, _('try extended date formats'))], - _('debugdate [-e] DATE [RANGE]')), - "debugdata": (debugdata, [], _('debugdata FILE REV')), - "debugindex": (debugindex, [], _('debugindex FILE')), - "debugindexdot": (debugindexdot, [], _('debugindexdot FILE')), + _('hg debugrebuildstate [-r REV] [REV]')), "debugrename": (debugrename, [('r', 'rev', '', _('revision to debug'))], - _('debugrename [-r REV] FILE')), - "debugwalk": (debugwalk, walkopts, _('debugwalk [OPTION]... [FILE]...')), + _('hg debugrename [-r REV] FILE')), + "debugsetparents": + (debugsetparents, + [], + _('hg debugsetparents REV1 [REV2]')), + "debugstate": (debugstate, [], _('hg debugstate')), + "debugwalk": (debugwalk, walkopts, _('hg debugwalk [OPTION]... [FILE]...')), "^diff": (diff, [('r', 'rev', [], _('revision')), @@ -2965,12 +2881,15 @@ ('b', 'base', '', _('base 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, - _('hg import [-p NUM] [-m MESSAGE] [-f] PATCH...')), - "incoming|in": (incoming, + _('Use any branch information in patch (implied by --exact)'))] + + commitopts + commitopts2, + _('hg import [OPTION]... PATCH...')), + "incoming|in": + (incoming, [('M', 'no-merges', None, _('do not show merges')), ('f', 'force', None, _('run even when remote repository is unrelated')), @@ -3016,14 +2935,18 @@ ('', 'template', '', _('display with template')), ] + walkopts, _('hg log [OPTION]... [FILE]')), - "manifest": (manifest, [], _('hg manifest [REV]')), + "manifest": + (manifest, + [('r', 'rev', '', _('revision to display'))], + _('hg manifest [-r REV]')), "^merge": (merge, [('f', 'force', None, _('force a merge with outstanding changes')), ('r', 'rev', '', _('revision to merge')), ], _('hg merge [-f] [[-r] REV]')), - "outgoing|out": (outgoing, + "outgoing|out": + (outgoing, [('M', 'no-merges', None, _('do not show merges')), ('f', 'force', None, _('run even when remote repository is unrelated')), @@ -3057,18 +2980,10 @@ ('r', 'rev', [], _('a specific revision you would like to push')), ] + remoteopts, _('hg push [-f] [-r REV]... [-e CMD] [--remotecmd CMD] [DEST]')), - "debugrawcommit|rawcommit": - (rawcommit, - [('p', 'parent', [], _('parent')), - ('d', 'date', '', _('date code')), - ('u', 'user', '', _('user')), - ('F', 'files', '', _('file list')) - ] + commitopts, - _('hg debugrawcommit [OPTION]... [FILE]...')), "recover": (recover, [], _('hg recover')), "^remove|rm": (remove, - [('A', 'after', None, _('record remove that has already occurred')), + [('A', 'after', None, _('record remove without deleting')), ('f', 'force', None, _('remove file even if modified')), ] + walkopts, _('hg remove [OPTION]... FILE...')), @@ -3079,7 +2994,7 @@ _('forcibly copy over an existing managed file')), ] + walkopts + dryrunopts, _('hg rename [OPTION]... SOURCE... DEST')), - "^revert": + "revert": (revert, [('a', 'all', None, _('revert all changes when no arguments given')), ('d', 'date', '', _('tipmost revision matching date')), @@ -3089,10 +3004,6 @@ _('hg revert [OPTION]... [-r REV] [NAME]...')), "rollback": (rollback, [], _('hg rollback')), "root": (root, [], _('hg root')), - "showconfig|debugconfig": - (showconfig, - [('u', 'untrusted', None, _('show untrusted configuration options'))], - _('showconfig [-u] [NAME]...')), "^serve": (serve, [('A', 'accesslog', '', _('name of access log file to write to')), @@ -3101,6 +3012,7 @@ ('E', 'errorlog', '', _('name of error log file to write to')), ('p', 'port', 0, _('port to use (default: 8000)')), ('a', 'address', '', _('address to use')), + ('', 'prefix', '', _('prefix path to serve from (default: server root)')), ('n', 'name', '', _('name to show in web pages (default: working dir)')), ('', 'webdir-conf', '', _('name of the webdir config file' @@ -3112,6 +3024,10 @@ ('6', 'ipv6', None, _('use IPv6 in addition to IPv4')), ('', 'certificate', '', _('SSL certificate file'))], _('hg serve [OPTION]...')), + "showconfig|debugconfig": + (showconfig, + [('u', 'untrusted', None, _('show untrusted configuration options'))], + _('hg showconfig [-u] [NAME]...')), "^status|st": (status, [('A', 'all', None, _('show status of all files')), @@ -3133,11 +3049,11 @@ (tag, [('f', 'force', None, _('replace existing tag')), ('l', 'local', None, _('make the tag local')), - ('m', 'message', '', _('message for tag commit log entry')), - ('d', 'date', '', _('record datecode as commit date')), - ('u', 'user', '', _('record user as commiter')), ('r', 'rev', '', _('revision to tag')), - ('', 'remove', None, _('remove a tag'))], + ('', 'remove', None, _('remove a tag')), + # -l/--local is already there, commitopts cannot be used + ('m', 'message', '', _('use <text> as commit message')), + ] + commitopts2, _('hg tag [-l] [-m TEXT] [-d DATE] [-u USER] [-r REV] NAME')), "tags": (tags, [], _('hg tags')), "tip": @@ -3161,19 +3077,6 @@ "version": (version_, [], _('hg version')), } -extensions.commandtable = table - norepo = ("clone init version help debugancestor debugcomplete debugdata" - " debugindex debugindexdot debugdate debuginstall") -optionalrepo = ("paths serve showconfig") - -def dispatch(args, argv0=None): - try: - u = ui.ui(traceback='--traceback' in args) - except util.Abort, inst: - sys.stderr.write(_("abort: %s\n") % inst) - return -1 - return cmdutil.runcatch(u, args, argv0=argv0) - -def run(): - sys.exit(dispatch(sys.argv[1:], argv0=sys.argv[0])) + " debugindex debugindexdot debugdate debuginstall debugfsinfo") +optionalrepo = ("identify paths serve showconfig")