xref: /onnv-gate/usr/src/tools/onbld/hgext/cdm.py (revision 12709:237fa25a1db0)
17078Smjnelson#
27078Smjnelson#  This program is free software; you can redistribute it and/or modify
37078Smjnelson#  it under the terms of the GNU General Public License version 2
47078Smjnelson#  as published by the Free Software Foundation.
57078Smjnelson#
67078Smjnelson#  This program is distributed in the hope that it will be useful,
77078Smjnelson#  but WITHOUT ANY WARRANTY; without even the implied warranty of
87078Smjnelson#  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
97078Smjnelson#  GNU General Public License for more details.
107078Smjnelson#
117078Smjnelson#  You should have received a copy of the GNU General Public License
127078Smjnelson#  along with this program; if not, write to the Free Software
137078Smjnelson#  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
147078Smjnelson#
157078Smjnelson
167078Smjnelson#
1712216Srichlowe@richlowe.net# Copyright (c) 2008, 2010, Oracle and/or its affiliates. All rights reserved.
187078Smjnelson#
197078Smjnelson
2010399Srichlowe@richlowe.net'''OpenSolaris workspace extensions for mercurial
217078Smjnelson
227078SmjnelsonThis extension contains a number of commands to help you work within
237078Smjnelsonthe OpenSolaris consolidations.
247078Smjnelson
257078SmjnelsonCommon uses:
267078Smjnelson
277078SmjnelsonShow diffs relative to parent workspace			- pdiffs
287078SmjnelsonCheck source style rules				- nits
297078SmjnelsonRun pre-putback checks					- pbchk
307078SmjnelsonCollapse all your changes into a single changeset	- recommit'''
317078Smjnelson
327078Smjnelson
3312692SAli.Bahrami@Oracle.COMimport atexit, os, re, stat, sys, termios
3410399Srichlowe@richlowe.net
3512216Srichlowe@richlowe.net#
3612216Srichlowe@richlowe.net# Adjust the load path based on the location of cdm.py and the version
3712216Srichlowe@richlowe.net# of python into which it is being loaded.  This assumes the normal
3812216Srichlowe@richlowe.net# onbld directory structure, where cdm.py is in
3912216Srichlowe@richlowe.net# lib/python(version)?/onbld/hgext/.  If that changes so too must
4012216Srichlowe@richlowe.net# this.
4112216Srichlowe@richlowe.net#
4212216Srichlowe@richlowe.net# This and the case below are not equivalent.  In this case we may be
4312216Srichlowe@richlowe.net# loading a cdm.py in python2.X/ via the lib/python/ symlink but need
4412216Srichlowe@richlowe.net# python2.Y in sys.path.
4512216Srichlowe@richlowe.net#
4612216Srichlowe@richlowe.netsys.path.insert(1, os.path.join(os.path.dirname(__file__), "..", "..", "..",
4712216Srichlowe@richlowe.net                                "python%d.%d" % sys.version_info[:2]))
4812216Srichlowe@richlowe.net
4912216Srichlowe@richlowe.net#
5012216Srichlowe@richlowe.net# Add the relative path from cdm.py to usr/src/tools to the load path,
5112216Srichlowe@richlowe.net# such that a cdm.py loaded from the source tree uses the modules also
5212216Srichlowe@richlowe.net# within the source tree.
5312216Srichlowe@richlowe.net#
5412216Srichlowe@richlowe.netsys.path.insert(2, os.path.join(os.path.dirname(__file__), "..", ".."))
557078Smjnelson
567078Smjnelsonfrom onbld.Scm import Version
577078Smjnelsonfrom mercurial import util
587078Smjnelson
597078Smjnelsontry:
607078Smjnelson    Version.check_version()
617078Smjnelsonexcept Version.VersionMismatch, badversion:
627078Smjnelson    raise util.Abort("Version Mismatch:\n %s\n" % badversion)
637078Smjnelson
647078Smjnelsonimport ConfigParser
6510399Srichlowe@richlowe.netfrom mercurial import cmdutil, ignore, node
667078Smjnelson
6710399Srichlowe@richlowe.netfrom onbld.Scm.WorkSpace import ActiveEntry, WorkSpace
687078Smjnelsonfrom onbld.Scm.Backup import CdmBackup
697078Smjnelsonfrom onbld.Checks import Cddl, Comments, Copyright, CStyle, HdrChk
708744SAli.Bahrami@Sun.COMfrom onbld.Checks import JStyle, Keywords, Mapfile, Rti, onSWAN
717078Smjnelson
727078Smjnelson
737078Smjnelsondef yes_no(ui, msg, default):
747078Smjnelson    if default:
757078Smjnelson        prompt = ' [Y/n]:'
767078Smjnelson        defanswer = 'y'
777078Smjnelson    else:
787078Smjnelson        prompt = ' [y/N]:'
797078Smjnelson        defanswer = 'n'
807078Smjnelson
8110399Srichlowe@richlowe.net    if Version.at_least("1.3.0"):
8210399Srichlowe@richlowe.net        resp = ui.prompt(msg + prompt, ['&yes', '&no'], default=defanswer)
837078Smjnelson    else:
8410399Srichlowe@richlowe.net        resp = ui.prompt(msg + prompt, r'([Yy(es)?|[Nn]o?)?',
8510399Srichlowe@richlowe.net                         default=defanswer)
8610399Srichlowe@richlowe.net
8710399Srichlowe@richlowe.net    return resp[0] in ('Y', 'y')
887078Smjnelson
897298SMark.J.Nelson@Sun.COM
9010263Srichlowe@richlowe.netdef buildfilelist(ws, parent, files):
9110263Srichlowe@richlowe.net    '''Build a list of files in which we're interested.
927298SMark.J.Nelson@Sun.COM
9310263Srichlowe@richlowe.net    If no files are specified take files from the active list relative
9410263Srichlowe@richlowe.net    to 'parent'.
957298SMark.J.Nelson@Sun.COM
9610263Srichlowe@richlowe.net    Return a list of 2-tuples the first element being a path relative
9710263Srichlowe@richlowe.net    to the current directory and the second an entry from the active
9810263Srichlowe@richlowe.net    list, or None if an explicit file list was given.'''
997298SMark.J.Nelson@Sun.COM
10010263Srichlowe@richlowe.net    if files:
10110263Srichlowe@richlowe.net        return [(path, None) for path in sorted(files)]
1027298SMark.J.Nelson@Sun.COM    else:
10310263Srichlowe@richlowe.net        active = ws.active(parent=parent)
10410263Srichlowe@richlowe.net        return [(ws.filepath(e.name), e) for e in sorted(active)]
10510263Srichlowe@richlowe.netbuildfilelist = util.cachefunc(buildfilelist)
1067298SMark.J.Nelson@Sun.COM
1077298SMark.J.Nelson@Sun.COM
1087298SMark.J.Nelson@Sun.COMdef not_check(repo, cmd):
1097298SMark.J.Nelson@Sun.COM    '''return a function which returns boolean indicating whether a file
1107298SMark.J.Nelson@Sun.COM    should be skipped for CMD.'''
1117298SMark.J.Nelson@Sun.COM
1128744SAli.Bahrami@Sun.COM    #
1138744SAli.Bahrami@Sun.COM    # The ignore routines need a canonical path to the file (relative to the
1148744SAli.Bahrami@Sun.COM    # repo root), whereas the check commands get paths relative to the cwd.
1158744SAli.Bahrami@Sun.COM    #
1168744SAli.Bahrami@Sun.COM    # Wrap our argument such that the path is canonified before it is checked.
1178744SAli.Bahrami@Sun.COM    #
1188744SAli.Bahrami@Sun.COM    def canonified_check(ignfunc):
1198744SAli.Bahrami@Sun.COM        def f(path):
1208744SAli.Bahrami@Sun.COM            cpath = util.canonpath(repo.root, repo.getcwd(), path)
1218744SAli.Bahrami@Sun.COM            return ignfunc(cpath)
1228744SAli.Bahrami@Sun.COM        return f
1238744SAli.Bahrami@Sun.COM
1248744SAli.Bahrami@Sun.COM    ignorefiles = []
1258744SAli.Bahrami@Sun.COM
1269006Srichlowe@richlowe.net    for f in [repo.join('cdm/%s.NOT' % cmd),
1279006Srichlowe@richlowe.net               repo.wjoin('exception_lists/%s' % cmd)]:
1288744SAli.Bahrami@Sun.COM        if os.path.exists(f):
1299006Srichlowe@richlowe.net            ignorefiles.append(f)
1308744SAli.Bahrami@Sun.COM
1318744SAli.Bahrami@Sun.COM    if ignorefiles:
1328744SAli.Bahrami@Sun.COM        ign = ignore.ignore(repo.root, ignorefiles, repo.ui.warn)
1338744SAli.Bahrami@Sun.COM        return canonified_check(ign)
1347298SMark.J.Nelson@Sun.COM    else:
1357298SMark.J.Nelson@Sun.COM        return util.never
1367298SMark.J.Nelson@Sun.COM
1377298SMark.J.Nelson@Sun.COM
13810399Srichlowe@richlowe.netdef abort_if_dirty(ws):
13910399Srichlowe@richlowe.net    '''Abort if the workspace has uncommitted changes, merges,
14010399Srichlowe@richlowe.net    branches, or has Mq patches applied'''
14110399Srichlowe@richlowe.net
14210399Srichlowe@richlowe.net    if ws.modified():
14310399Srichlowe@richlowe.net        raise util.Abort('workspace has uncommitted changes')
14410399Srichlowe@richlowe.net    if ws.merged():
14510399Srichlowe@richlowe.net        raise util.Abort('workspace contains uncommitted merge')
14610399Srichlowe@richlowe.net    if ws.branched():
14710399Srichlowe@richlowe.net        raise util.Abort('workspace contains uncommitted branch')
14810399Srichlowe@richlowe.net    if ws.mq_applied():
14910399Srichlowe@richlowe.net        raise util.Abort('workspace has Mq patches applied')
15010399Srichlowe@richlowe.net
15110399Srichlowe@richlowe.net
1527078Smjnelson#
1537078Smjnelson# Adding a reference to WorkSpace from a repo causes a circular reference
1547078Smjnelson# repo <-> WorkSpace.
1557078Smjnelson#
1567078Smjnelson# This prevents repo, WorkSpace and members thereof from being garbage
1577078Smjnelson# collected.  Since transactions are aborted when the transaction object
1587078Smjnelson# is collected, and localrepo holds a reference to the most recently created
1597078Smjnelson# transaction, this prevents transactions from cleanly aborting.
1607078Smjnelson#
1617078Smjnelson# Instead, we hold the repo->WorkSpace association in a dictionary, breaking
1627078Smjnelson# that dependence.
1637078Smjnelson#
1647078Smjnelsonwslist = {}
1657078Smjnelson
1669006Srichlowe@richlowe.net
1677078Smjnelsondef reposetup(ui, repo):
1687078Smjnelson    if repo.local() and repo not in wslist:
1697078Smjnelson        wslist[repo] = WorkSpace(repo)
1707078Smjnelson
17110399Srichlowe@richlowe.net        if Version.at_least("1.3"):
17210399Srichlowe@richlowe.net            interactive = ui.interactive()
17310399Srichlowe@richlowe.net        else:
17410399Srichlowe@richlowe.net            interactive = ui.interactive
17510399Srichlowe@richlowe.net
17610399Srichlowe@richlowe.net        if interactive and sys.stdin.isatty():
1779006Srichlowe@richlowe.net            ui.setconfig('hooks', 'preoutgoing.cdm_pbconfirm',
1789006Srichlowe@richlowe.net                         'python:hgext_cdm.pbconfirm')
1799006Srichlowe@richlowe.net
1807078Smjnelson
1817298SMark.J.Nelson@Sun.COMdef pbconfirm(ui, repo, hooktype, source):
1827298SMark.J.Nelson@Sun.COM    def wrapper(settings=None):
1839006Srichlowe@richlowe.net        termios.tcsetattr(sys.stdin.fileno(), termios.TCSANOW, settings)
1847298SMark.J.Nelson@Sun.COM
1857298SMark.J.Nelson@Sun.COM    if source == 'push':
1867298SMark.J.Nelson@Sun.COM        if not yes_no(ui, "Are you sure you wish to push?", False):
1877298SMark.J.Nelson@Sun.COM            return 1
1887298SMark.J.Nelson@Sun.COM        else:
1897298SMark.J.Nelson@Sun.COM            settings = termios.tcgetattr(sys.stdin.fileno())
1909006Srichlowe@richlowe.net            orig = list(settings)
1919006Srichlowe@richlowe.net            atexit.register(wrapper, orig)
1929006Srichlowe@richlowe.net            settings[3] = settings[3] & (~termios.ISIG) # c_lflag
1939006Srichlowe@richlowe.net            termios.tcsetattr(sys.stdin.fileno(), termios.TCSANOW, settings)
1949006Srichlowe@richlowe.net
1957298SMark.J.Nelson@Sun.COM
1967298SMark.J.Nelson@Sun.COMdef cdm_pdiffs(ui, repo, *pats, **opts):
1977078Smjnelson    '''list workspace diffs relative to parent workspace
1987078Smjnelson
1997078Smjnelson    The parent tip is taken to be the latest revision shared between
2007078Smjnelson    us and the parent workspace.'''
2017298SMark.J.Nelson@Sun.COM
2027298SMark.J.Nelson@Sun.COM    parent = opts['parent']
2037298SMark.J.Nelson@Sun.COM
2047298SMark.J.Nelson@Sun.COM    diffs = wslist[repo].pdiff(pats, opts, parent=parent)
2057078Smjnelson    if diffs:
2067078Smjnelson        ui.write(diffs)
2077078Smjnelson
2087078Smjnelson
2097078Smjnelsondef cdm_list(ui, repo, **opts):
2107078Smjnelson    '''list files changed relative to parent workspace
2117078Smjnelson
2127078Smjnelson    The parent tip is taken to be the latest revision shared between
2137078Smjnelson    us and the parent workspace.'''
2147078Smjnelson
2157078Smjnelson    wanted = []
2167078Smjnelson
2177078Smjnelson    if opts['added']:
2187078Smjnelson        wanted.append(ActiveEntry.ADDED)
2197078Smjnelson    if opts['modified']:
2207078Smjnelson        wanted.append(ActiveEntry.MODIFIED)
2217078Smjnelson    if opts['removed']:
2227078Smjnelson        wanted.append(ActiveEntry.REMOVED)
2237078Smjnelson
2247078Smjnelson    act = wslist[repo].active(opts['parent'])
2257078Smjnelson    chngmap = {ActiveEntry.MODIFIED: 'modified',
2267078Smjnelson               ActiveEntry.ADDED: 'added',
2277078Smjnelson               ActiveEntry.REMOVED: 'removed'}
2287078Smjnelson
2297078Smjnelson    lst = {}
2307078Smjnelson    for entry in act:
2317078Smjnelson        if wanted and (entry.change not in wanted):
2327078Smjnelson            continue
2337078Smjnelson
2347078Smjnelson        chngstr = chngmap[entry.change]
2357078Smjnelson        if chngstr not in lst:
2367078Smjnelson            lst[chngstr] = []
2377078Smjnelson        lst[chngstr].append(entry)
2387078Smjnelson
2397078Smjnelson    for chng in sorted(lst.keys()):
2407078Smjnelson        ui.write(chng + ':\n')
2417078Smjnelson        for elt in sorted(lst[chng]):
2427078Smjnelson            if elt.is_renamed():
2437078Smjnelson                ui.write('\t%s (renamed from %s)\n' % (elt.name,
2447078Smjnelson                                                      elt.parentname))
2457078Smjnelson            elif elt.is_copied():
2467078Smjnelson                ui.write('\t%s (copied from %s)\n' % (elt.name,
2477078Smjnelson                                                      elt.parentname))
2487078Smjnelson            else:
2497078Smjnelson                ui.write('\t%s\n' % elt.name)
2507078Smjnelson
2517078Smjnelson
2527078Smjnelsondef cdm_arcs(ui, repo, parent=None):
2537078Smjnelson    'show all ARC cases in checkin comments'
2547078Smjnelson    act = wslist[repo].active(parent)
2557078Smjnelson
2567078Smjnelson    # We take a set of the appropriate comments to eliminate duplicates.
2577078Smjnelson    for elt in set(filter(Comments.isARC, act.comments())):
2587078Smjnelson        ui.write(elt + '\n')
2597078Smjnelson
2607078Smjnelson
2617078Smjnelsondef cdm_bugs(ui, repo, parent=None):
2627078Smjnelson    'show all bug IDs in checkin comments'
2637078Smjnelson    act = wslist[repo].active(parent)
2647078Smjnelson
2657078Smjnelson    for elt in set(filter(Comments.isBug, act.comments())):
2667078Smjnelson        ui.write(elt + '\n')
2677078Smjnelson
2687078Smjnelson
2697078Smjnelsondef cdm_comments(ui, repo, parent=None):
2707078Smjnelson    'show checkin comments for active files'
2717078Smjnelson    act = wslist[repo].active(parent)
2727078Smjnelson
2737078Smjnelson    for elt in act.comments():
2747078Smjnelson        ui.write(elt + '\n')
2757078Smjnelson
2767078Smjnelson
2777078Smjnelsondef cdm_renamed(ui, repo, parent=None):
2787078Smjnelson    '''show renamed active files
2797078Smjnelson
2807078Smjnelson    Renamed files are shown in the format
2817078Smjnelson
2827078Smjnelson       newname oldname
2837078Smjnelson
2847078Smjnelson    One pair per-line.'''
2857078Smjnelson
2867078Smjnelson    act = wslist[repo].active(parent)
2877078Smjnelson
2887078Smjnelson    for entry in sorted(filter(lambda x: x.is_renamed(), act)):
2897078Smjnelson        ui.write('%s %s\n' % (entry.name, entry.parentname))
2907078Smjnelson
2917078Smjnelson
2927078Smjnelsondef cdm_comchk(ui, repo, **opts):
2937078Smjnelson    '''check checkin comments for active files
2947078Smjnelson
2957078Smjnelson    Check that checkin comments conform to O/N rules.'''
2967078Smjnelson
2977298SMark.J.Nelson@Sun.COM    active = wslist[repo].active(opts.get('parent'))
2987078Smjnelson
2997078Smjnelson    ui.write('Comments check:\n')
3007078Smjnelson
3017298SMark.J.Nelson@Sun.COM    check_db = not opts.get('nocheck')
3029920SMark.J.Nelson@Sun.COM    return Comments.comchk(active.comments(), check_db=check_db, output=ui,
3039920SMark.J.Nelson@Sun.COM                           arcPath=ui.config('cdm', 'arcpath', None))
3047078Smjnelson
3057078Smjnelson
3067298SMark.J.Nelson@Sun.COMdef cdm_cddlchk(ui, repo, *args, **opts):
3077078Smjnelson    '''check for a valid CDDL block in active files
3087078Smjnelson
3097078Smjnelson    See http://www.opensolaris.org/os/community/on/devref_toc/devref_7/#7_2_3_nonformatting_considerations
3107078Smjnelson    for more info.'''
3117078Smjnelson
31210263Srichlowe@richlowe.net    filelist = buildfilelist(wslist[repo], opts.get('parent'), args)
31310263Srichlowe@richlowe.net    exclude = not_check(repo, 'cddlchk')
31410263Srichlowe@richlowe.net    lenient = True
31510263Srichlowe@richlowe.net    ret = 0
3167078Smjnelson
3177078Smjnelson    ui.write('CDDL block check:\n')
3187078Smjnelson
31910263Srichlowe@richlowe.net    for f, e in filelist:
3207298SMark.J.Nelson@Sun.COM        if e and e.is_removed():
3217078Smjnelson            continue
3227298SMark.J.Nelson@Sun.COM        elif (e or opts.get('honour_nots')) and exclude(f):
3237298SMark.J.Nelson@Sun.COM            ui.status('Skipping %s...\n' % f)
3247298SMark.J.Nelson@Sun.COM            continue
3257298SMark.J.Nelson@Sun.COM        elif e and e.is_added():
3267078Smjnelson            lenient = False
3277078Smjnelson        else:
3287078Smjnelson            lenient = True
3297078Smjnelson
3307298SMark.J.Nelson@Sun.COM        fh = open(f, 'r')
3317078Smjnelson        ret |= Cddl.cddlchk(fh, lenient=lenient, output=ui)
3327078Smjnelson        fh.close()
3337078Smjnelson    return ret
3347078Smjnelson
3357078Smjnelson
3368744SAli.Bahrami@Sun.COMdef cdm_mapfilechk(ui, repo, *args, **opts):
3378744SAli.Bahrami@Sun.COM    '''check for a valid MAPFILE header block in active files
3388744SAli.Bahrami@Sun.COM
3398744SAli.Bahrami@Sun.COM    Check that all link-editor mapfiles contain the standard mapfile
3408744SAli.Bahrami@Sun.COM    header comment directing the reader to the document containing
3418744SAli.Bahrami@Sun.COM    Solaris object versioning rules (README.mapfile).'''
3428744SAli.Bahrami@Sun.COM
34310263Srichlowe@richlowe.net    filelist = buildfilelist(wslist[repo], opts.get('parent'), args)
34410263Srichlowe@richlowe.net    exclude = not_check(repo, 'mapfilechk')
34510263Srichlowe@richlowe.net    ret = 0
3468744SAli.Bahrami@Sun.COM
34712692SAli.Bahrami@Oracle.COM    # We are interested in examining any file that has the following
34812692SAli.Bahrami@Oracle.COM    # in its final path segment:
34912692SAli.Bahrami@Oracle.COM    #    - Contains the word 'mapfile'
35012692SAli.Bahrami@Oracle.COM    #    - Begins with 'map.'
35112692SAli.Bahrami@Oracle.COM    #    - Ends with '.map'
35212692SAli.Bahrami@Oracle.COM    # We don't want to match unless these things occur in final path segment
35312692SAli.Bahrami@Oracle.COM    # because directory names with these strings don't indicate a mapfile.
354*12709SAli.Bahrami@Oracle.COM    # We also ignore files with suffixes that tell us that the files
355*12709SAli.Bahrami@Oracle.COM    # are not mapfiles.
356*12709SAli.Bahrami@Oracle.COM    MapfileRE = re.compile(r'.*((mapfile[^/]*)|(/map\.+[^/]*)|(\.map))$',
35712692SAli.Bahrami@Oracle.COM    	re.IGNORECASE)
358*12709SAli.Bahrami@Oracle.COM    NotMapSuffixRE = re.compile(r'.*\.[ch]$', re.IGNORECASE)
35912692SAli.Bahrami@Oracle.COM
3608744SAli.Bahrami@Sun.COM    ui.write('Mapfile comment check:\n')
3618744SAli.Bahrami@Sun.COM
36210263Srichlowe@richlowe.net    for f, e in filelist:
3638744SAli.Bahrami@Sun.COM        if e and e.is_removed():
3648744SAli.Bahrami@Sun.COM            continue
365*12709SAli.Bahrami@Oracle.COM        elif (not MapfileRE.match(f)) or NotMapSuffixRE.match(f):
3668744SAli.Bahrami@Sun.COM            continue
3678744SAli.Bahrami@Sun.COM        elif (e or opts.get('honour_nots')) and exclude(f):
3688744SAli.Bahrami@Sun.COM            ui.status('Skipping %s...\n' % f)
3698744SAli.Bahrami@Sun.COM            continue
3708744SAli.Bahrami@Sun.COM
3718744SAli.Bahrami@Sun.COM        fh = open(f, 'r')
3728744SAli.Bahrami@Sun.COM        ret |= Mapfile.mapfilechk(fh, output=ui)
3738744SAli.Bahrami@Sun.COM        fh.close()
3748744SAli.Bahrami@Sun.COM    return ret
3758744SAli.Bahrami@Sun.COM
3768744SAli.Bahrami@Sun.COM
3777298SMark.J.Nelson@Sun.COMdef cdm_copyright(ui, repo, *args, **opts):
3787078Smjnelson    '''check active files for valid copyrights
3797078Smjnelson
3807078Smjnelson    Check that all active files have a valid copyright containing the
3817078Smjnelson    current year (and *only* the current year).
3827078Smjnelson    See http://www.opensolaris.org/os/project/muskoka/on_dev/golden_rules.txt
3837078Smjnelson    for more info.'''
3847078Smjnelson
38510263Srichlowe@richlowe.net    filelist = buildfilelist(wslist[repo], opts.get('parent'), args)
38610263Srichlowe@richlowe.net    exclude = not_check(repo, 'copyright')
38710263Srichlowe@richlowe.net    ret = 0
3887078Smjnelson
3897078Smjnelson    ui.write('Copyright check:\n')
3907078Smjnelson
39110263Srichlowe@richlowe.net    for f, e in filelist:
3927298SMark.J.Nelson@Sun.COM        if e and e.is_removed():
3937298SMark.J.Nelson@Sun.COM            continue
3947298SMark.J.Nelson@Sun.COM        elif (e or opts.get('honour_nots')) and exclude(f):
3957298SMark.J.Nelson@Sun.COM            ui.status('Skipping %s...\n' % f)
3967078Smjnelson            continue
3977078Smjnelson
3987298SMark.J.Nelson@Sun.COM        fh = open(f, 'r')
3997078Smjnelson        ret |= Copyright.copyright(fh, output=ui)
4007078Smjnelson        fh.close()
4017078Smjnelson    return ret
4027078Smjnelson
4037078Smjnelson
4047298SMark.J.Nelson@Sun.COMdef cdm_hdrchk(ui, repo, *args, **opts):
4057078Smjnelson    '''check active header files conform to O/N rules'''
4067078Smjnelson
40710263Srichlowe@richlowe.net    filelist = buildfilelist(wslist[repo], opts.get('parent'), args)
40810263Srichlowe@richlowe.net    exclude = not_check(repo, 'hdrchk')
40910263Srichlowe@richlowe.net    ret = 0
4107078Smjnelson
4117078Smjnelson    ui.write('Header format check:\n')
4127078Smjnelson
41310263Srichlowe@richlowe.net    for f, e in filelist:
4147298SMark.J.Nelson@Sun.COM        if e and e.is_removed():
4157298SMark.J.Nelson@Sun.COM            continue
4167298SMark.J.Nelson@Sun.COM        elif not f.endswith('.h'):
4177298SMark.J.Nelson@Sun.COM            continue
4187298SMark.J.Nelson@Sun.COM        elif (e or opts.get('honour_nots')) and exclude(f):
4197298SMark.J.Nelson@Sun.COM            ui.status('Skipping %s...\n' % f)
4207078Smjnelson            continue
4217078Smjnelson
4227298SMark.J.Nelson@Sun.COM        fh = open(f, 'r')
4237298SMark.J.Nelson@Sun.COM        ret |= HdrChk.hdrchk(fh, lenient=True, output=ui)
4247298SMark.J.Nelson@Sun.COM        fh.close()
4257078Smjnelson    return ret
4267078Smjnelson
4277078Smjnelson
4287298SMark.J.Nelson@Sun.COMdef cdm_cstyle(ui, repo, *args, **opts):
4297078Smjnelson    '''check active C source files conform to the C Style Guide
4307078Smjnelson
4317078Smjnelson    See http://opensolaris.org/os/community/documentation/getting_started_docs/cstyle.ms.pdf'''
4327078Smjnelson
43310263Srichlowe@richlowe.net    filelist = buildfilelist(wslist[repo], opts.get('parent'), args)
43410263Srichlowe@richlowe.net    exclude = not_check(repo, 'cstyle')
43510263Srichlowe@richlowe.net    ret = 0
4367078Smjnelson
4377078Smjnelson    ui.write('C style check:\n')
4387078Smjnelson
43910263Srichlowe@richlowe.net    for f, e in filelist:
4407298SMark.J.Nelson@Sun.COM        if e and e.is_removed():
4417298SMark.J.Nelson@Sun.COM            continue
4427298SMark.J.Nelson@Sun.COM        elif not (f.endswith('.c') or f.endswith('.h')):
4437298SMark.J.Nelson@Sun.COM            continue
4447298SMark.J.Nelson@Sun.COM        elif (e or opts.get('honour_nots')) and exclude(f):
4457298SMark.J.Nelson@Sun.COM            ui.status('Skipping %s...\n' % f)
4467078Smjnelson            continue
4477078Smjnelson
4487298SMark.J.Nelson@Sun.COM        fh = open(f, 'r')
4497298SMark.J.Nelson@Sun.COM        ret |= CStyle.cstyle(fh, output=ui,
4507298SMark.J.Nelson@Sun.COM                             picky=True, check_posix_types=True,
4517298SMark.J.Nelson@Sun.COM                             check_continuation=True)
4527298SMark.J.Nelson@Sun.COM        fh.close()
4537078Smjnelson    return ret
4547078Smjnelson
4557078Smjnelson
4567298SMark.J.Nelson@Sun.COMdef cdm_jstyle(ui, repo, *args, **opts):
4577078Smjnelson    'check active Java source files for common stylistic errors'
4587078Smjnelson
45910263Srichlowe@richlowe.net    filelist = buildfilelist(wslist[repo], opts.get('parent'), args)
46010263Srichlowe@richlowe.net    exclude = not_check(repo, 'jstyle')
46110263Srichlowe@richlowe.net    ret = 0
4627078Smjnelson
4637078Smjnelson    ui.write('Java style check:\n')
4647078Smjnelson
46510263Srichlowe@richlowe.net    for f, e in filelist:
4667298SMark.J.Nelson@Sun.COM        if e and e.is_removed():
4677298SMark.J.Nelson@Sun.COM            continue
4687298SMark.J.Nelson@Sun.COM        elif not f.endswith('.java'):
4697298SMark.J.Nelson@Sun.COM            continue
4707298SMark.J.Nelson@Sun.COM        elif (e or opts.get('honour_nots')) and exclude(f):
4717298SMark.J.Nelson@Sun.COM            ui.status('Skipping %s...\n' % f)
4727078Smjnelson            continue
4737078Smjnelson
4747298SMark.J.Nelson@Sun.COM        fh = open(f, 'r')
4757298SMark.J.Nelson@Sun.COM        ret |= JStyle.jstyle(fh, output=ui, picky=True)
4767298SMark.J.Nelson@Sun.COM        fh.close()
4777078Smjnelson    return ret
4787078Smjnelson
4797078Smjnelson
4807298SMark.J.Nelson@Sun.COMdef cdm_permchk(ui, repo, *args, **opts):
4817078Smjnelson    '''check active files permission - warn +x (execute) mode'''
4827078Smjnelson
48310263Srichlowe@richlowe.net    filelist = buildfilelist(wslist[repo], opts.get('parent'), args)
48410263Srichlowe@richlowe.net    exclude = not_check(repo, 'permchk')
48510263Srichlowe@richlowe.net    exeFiles = []
4867078Smjnelson
4877078Smjnelson    ui.write('File permission check:\n')
4887078Smjnelson
48910263Srichlowe@richlowe.net    for f, e in filelist:
4907298SMark.J.Nelson@Sun.COM        if e and e.is_removed():
4917298SMark.J.Nelson@Sun.COM            continue
4927298SMark.J.Nelson@Sun.COM        elif (e or opts.get('honour_nots')) and exclude(f):
4937298SMark.J.Nelson@Sun.COM            ui.status('Skipping %s...\n' % f)
4947078Smjnelson            continue
4957078Smjnelson
4967298SMark.J.Nelson@Sun.COM        mode = stat.S_IMODE(os.stat(f)[stat.ST_MODE])
4977298SMark.J.Nelson@Sun.COM        if mode & stat.S_IEXEC:
4987298SMark.J.Nelson@Sun.COM            exeFiles.append(f)
4997078Smjnelson
5007078Smjnelson    if len(exeFiles) > 0:
5017078Smjnelson        ui.write('Warning: the following active file(s) have executable mode '
5027078Smjnelson            '(+x) permission set,\nremove unless intentional:\n')
5037078Smjnelson        for fname in exeFiles:
5047078Smjnelson            ui.write("  %s\n" % fname)
5057078Smjnelson
5067078Smjnelson    return len(exeFiles) > 0
5077078Smjnelson
5087078Smjnelson
5097078Smjnelsondef cdm_tagchk(ui, repo, **opts):
5107078Smjnelson    '''check if .hgtags is active and issue warning
5117078Smjnelson
5127078Smjnelson    Tag sharing among repositories is restricted to gatekeepers'''
5137078Smjnelson
5147298SMark.J.Nelson@Sun.COM    active = wslist[repo].active(opts.get('parent'))
5157078Smjnelson
5167078Smjnelson    ui.write('Checking for new tags:\n')
5177078Smjnelson
5187078Smjnelson    if ".hgtags" in active:
5197078Smjnelson        tfile = wslist[repo].filepath('.hgtags')
5207078Smjnelson        ptip = active.parenttip.rev()
5217078Smjnelson
5227078Smjnelson        ui.write('Warning: Workspace contains new non-local tags.\n'
5237078Smjnelson                 'Only gatekeepers should add or modify such tags.\n'
5247078Smjnelson                 'Use the following commands to revert these changes:\n'
5257078Smjnelson                 '  hg revert -r%d %s\n'
5267078Smjnelson                 '  hg commit %s\n'
5277078Smjnelson                 'You should also recommit before integration\n' %
5287078Smjnelson                 (ptip, tfile, tfile))
5297078Smjnelson
5307078Smjnelson        return 1
5317078Smjnelson
5327078Smjnelson    return 0
5337078Smjnelson
5347078Smjnelson
5357078Smjnelsondef cdm_branchchk(ui, repo, **opts):
5367078Smjnelson    '''check if multiple heads (or branches) are present, or if
5377078Smjnelson    branch changes are made'''
5387078Smjnelson
5397078Smjnelson    ui.write('Checking for multiple heads (or branches):\n')
5407078Smjnelson
5417078Smjnelson    heads = set(repo.heads())
5429006Srichlowe@richlowe.net    parents = set([x.node() for x in wslist[repo].workingctx().parents()])
5437078Smjnelson
5447078Smjnelson    #
5457078Smjnelson    # We care if there's more than one head, and those heads aren't
5467078Smjnelson    # identical to the dirstate parents (if they are identical, it's
5477078Smjnelson    # an uncommitted merge which mergechk will catch, no need to
5487078Smjnelson    # complain twice).
5497078Smjnelson    #
5507078Smjnelson    if len(heads) > 1 and heads != parents:
5517078Smjnelson        ui.write('Workspace has multiple heads (or branches):\n')
5527078Smjnelson        for head in [repo.changectx(head) for head in heads]:
5537078Smjnelson            ui.write("  %d:%s\t%s\n" %
5547078Smjnelson                (head.rev(), str(head), head.description().splitlines()[0]))
5557078Smjnelson        ui.write('You must merge and recommit.\n')
5567078Smjnelson        return 1
5577078Smjnelson
5587078Smjnelson    ui.write('\nChecking for branch changes:\n')
5597078Smjnelson
5607123Smjnelson    if repo.dirstate.branch() != 'default':
5617078Smjnelson        ui.write("Warning: Workspace tip has named branch: '%s'\n"
5627078Smjnelson                 "Only gatekeepers should push new branches.\n"
5637078Smjnelson                 "Use the following commands to restore the branch name:\n"
5647078Smjnelson                 "  hg branch [-f] default\n"
5657078Smjnelson                 "  hg commit\n"
5667078Smjnelson                 "You should also recommit before integration\n" %
5677123Smjnelson                 (repo.dirstate.branch()))
5687078Smjnelson        return 1
5697078Smjnelson
5707078Smjnelson    branches = repo.branchtags().keys()
5717078Smjnelson    if len(branches) > 1:
5727078Smjnelson        ui.write('Warning: Workspace has named branches:\n')
5737078Smjnelson        for t in branches:
5747078Smjnelson            if t == 'default':
5757078Smjnelson                continue
5767078Smjnelson            ui.write("\t%s\n" % t)
5777078Smjnelson
5787078Smjnelson        ui.write("Only gatekeepers should push new branches.\n"
5797078Smjnelson                 "Use the following commands to remove extraneous branches.\n"
5807078Smjnelson                 "  hg branch [-f] default\n"
5817078Smjnelson                 "  hg commit"
5827078Smjnelson                 "You should also recommit before integration\n")
5837078Smjnelson        return 1
5847078Smjnelson
5857078Smjnelson    return 0
5867078Smjnelson
5877078Smjnelson
5887078Smjnelsondef cdm_rtichk(ui, repo, **opts):
5897078Smjnelson    '''check active bug/RFEs for approved RTIs
5907078Smjnelson
5917078Smjnelson    Only works on SWAN.'''
5927078Smjnelson
5937298SMark.J.Nelson@Sun.COM    if opts.get('nocheck') or os.path.exists(repo.join('cdm/rtichk.NOT')):
5947078Smjnelson        ui.status('Skipping RTI checks...\n')
5957078Smjnelson        return 0
5967078Smjnelson
5977078Smjnelson    if not onSWAN():
5987078Smjnelson        ui.write('RTI checks only work on SWAN, skipping...\n')
5997078Smjnelson        return 0
6007078Smjnelson
6017298SMark.J.Nelson@Sun.COM    parent = wslist[repo].parent(opts.get('parent'))
6027298SMark.J.Nelson@Sun.COM    active = wslist[repo].active(parent)
6037078Smjnelson
6047078Smjnelson    ui.write('RTI check:\n')
6057078Smjnelson
6067078Smjnelson    bugs = []
6077078Smjnelson
6087078Smjnelson    for com in active.comments():
6097078Smjnelson        match = Comments.isBug(com)
6107078Smjnelson        if match and match.group(1) not in bugs:
6117078Smjnelson            bugs.append(match.group(1))
6127078Smjnelson
6137078Smjnelson    # RTI normalizes the gate path for us
6147298SMark.J.Nelson@Sun.COM    return int(not Rti.rti(bugs, gatePath=parent, output=ui))
6157078Smjnelson
6167078Smjnelson
6177298SMark.J.Nelson@Sun.COMdef cdm_keywords(ui, repo, *args, **opts):
6187078Smjnelson    '''check source files do not contain SCCS keywords'''
6197078Smjnelson
62010263Srichlowe@richlowe.net    filelist = buildfilelist(wslist[repo], opts.get('parent'), args)
62110263Srichlowe@richlowe.net    exclude = not_check(repo, 'keywords')
62210263Srichlowe@richlowe.net    ret = 0
6237078Smjnelson
6247078Smjnelson    ui.write('Keywords check:\n')
6257078Smjnelson
62610263Srichlowe@richlowe.net    for f, e in filelist:
6277298SMark.J.Nelson@Sun.COM        if e and e.is_removed():
6287298SMark.J.Nelson@Sun.COM            continue
6297298SMark.J.Nelson@Sun.COM        elif (e or opts.get('honour_nots')) and exclude(f):
6307298SMark.J.Nelson@Sun.COM            ui.status('Skipping %s...\n' % f)
6317078Smjnelson            continue
6327078Smjnelson
6337298SMark.J.Nelson@Sun.COM        fh = open(f, 'r')
6347078Smjnelson        ret |= Keywords.keywords(fh, output=ui)
6357078Smjnelson        fh.close()
6367078Smjnelson    return ret
6377078Smjnelson
6387078Smjnelson
6397078Smjnelson#
6407078Smjnelson# NB:
6417078Smjnelson#    There's no reason to hook this up as an invokable command, since
6427078Smjnelson#    we have 'hg status', but it must accept the same arguments.
6437078Smjnelson#
6447078Smjnelsondef cdm_outchk(ui, repo, **opts):
6457078Smjnelson    '''Warn the user if they have uncommitted changes'''
6467078Smjnelson
6477078Smjnelson    ui.write('Checking for uncommitted changes:\n')
6487078Smjnelson
6497078Smjnelson    st = wslist[repo].modified()
6507078Smjnelson    if st:
6517078Smjnelson        ui.write('Warning: the following files have uncommitted changes:\n')
6527078Smjnelson        for elt in st:
6537078Smjnelson            ui.write('   %s\n' % elt)
6547078Smjnelson        return 1
6557078Smjnelson    return 0
6567078Smjnelson
6577078Smjnelson
6587078Smjnelsondef cdm_mergechk(ui, repo, **opts):
6597078Smjnelson    '''Warn the user if their workspace contains merges'''
6607078Smjnelson
6617298SMark.J.Nelson@Sun.COM    active = wslist[repo].active(opts.get('parent'))
6627078Smjnelson
6637078Smjnelson    ui.write('Checking for merges:\n')
6647078Smjnelson
6657078Smjnelson    merges = filter(lambda x: len(x.parents()) == 2 and x.parents()[1],
6667078Smjnelson                   active.revs)
6677078Smjnelson
6687078Smjnelson    if merges:
6697078Smjnelson        ui.write('Workspace contains the following merges:\n')
6707078Smjnelson        for rev in merges:
6717078Smjnelson            desc = rev.description().splitlines()
6727078Smjnelson            ui.write('  %s:%s\t%s\n' %
6737078Smjnelson                     (rev.rev() or "working", str(rev),
6747078Smjnelson                      desc and desc[0] or "*** uncommitted change ***"))
6757078Smjnelson        return 1
6767078Smjnelson    return 0
6777078Smjnelson
6787078Smjnelson
6797298SMark.J.Nelson@Sun.COMdef run_checks(ws, cmds, *args, **opts):
6807078Smjnelson    '''Run CMDS (with OPTS) over active files in WS'''
6817078Smjnelson
6827078Smjnelson    ret = 0
6837078Smjnelson
6847078Smjnelson    for cmd in cmds:
6857078Smjnelson        name = cmd.func_name.split('_')[1]
6867078Smjnelson        if not ws.ui.configbool('cdm', name, True):
6877078Smjnelson            ws.ui.status('Skipping %s check...\n' % name)
6887078Smjnelson        else:
6897078Smjnelson            ws.ui.pushbuffer()
69010263Srichlowe@richlowe.net            result = cmd(ws.ui, ws.repo, honour_nots=True, *args, **opts)
69110263Srichlowe@richlowe.net            output = ws.ui.popbuffer()
6927078Smjnelson
6937078Smjnelson            ret |= result
6947078Smjnelson
6957078Smjnelson            if not ws.ui.quiet or result != 0:
6967078Smjnelson                ws.ui.write(output, '\n')
6977078Smjnelson    return ret
6987078Smjnelson
6997078Smjnelson
7007298SMark.J.Nelson@Sun.COMdef cdm_nits(ui, repo, *args, **opts):
7017078Smjnelson    '''check for stylistic nits in active files
7027078Smjnelson
7038744SAli.Bahrami@Sun.COM    Run cddlchk, copyright, cstyle, hdrchk, jstyle, mapfilechk,
7048744SAli.Bahrami@Sun.COM    permchk, and keywords checks.'''
7057078Smjnelson
7067078Smjnelson    cmds = [cdm_cddlchk,
7077078Smjnelson        cdm_copyright,
7087078Smjnelson        cdm_cstyle,
7097078Smjnelson        cdm_hdrchk,
7107078Smjnelson        cdm_jstyle,
7118744SAli.Bahrami@Sun.COM        cdm_mapfilechk,
7127078Smjnelson        cdm_permchk,
7137078Smjnelson        cdm_keywords]
7147078Smjnelson
7157298SMark.J.Nelson@Sun.COM    return run_checks(wslist[repo], cmds, *args, **opts)
7167078Smjnelson
7177078Smjnelson
7189007Srichlowe@richlowe.netdef cdm_pbchk(ui, repo, **opts):
7197078Smjnelson    '''pre-putback check all active files
7207078Smjnelson
7218744SAli.Bahrami@Sun.COM    Run cddlchk, comchk, copyright, cstyle, hdrchk, jstyle, mapfilechk,
7228744SAli.Bahrami@Sun.COM    permchk, tagchk, branchchk, keywords and rtichk checks.  Additionally,
7238744SAli.Bahrami@Sun.COM    warn about uncommitted changes.'''
7247078Smjnelson
7257078Smjnelson    #
7267078Smjnelson    # The current ordering of these is that the commands from cdm_nits
7277078Smjnelson    # run first in the same order as they would in cdm_nits.  Then the
7287078Smjnelson    # pbchk specifics run
7297078Smjnelson    #
7307078Smjnelson    cmds = [cdm_cddlchk,
7317078Smjnelson        cdm_copyright,
7327078Smjnelson        cdm_cstyle,
7337078Smjnelson        cdm_hdrchk,
7347078Smjnelson        cdm_jstyle,
7358744SAli.Bahrami@Sun.COM        cdm_mapfilechk,
7367078Smjnelson        cdm_permchk,
7377078Smjnelson        cdm_keywords,
7387078Smjnelson        cdm_comchk,
7397078Smjnelson        cdm_tagchk,
7407078Smjnelson        cdm_branchchk,
7417078Smjnelson        cdm_rtichk,
7427078Smjnelson        cdm_outchk,
7437078Smjnelson        cdm_mergechk]
7447078Smjnelson
7459007Srichlowe@richlowe.net    return run_checks(wslist[repo], cmds, **opts)
7467078Smjnelson
7477078Smjnelson
7487078Smjnelsondef cdm_recommit(ui, repo, **opts):
74911646SJames.McPherson@Sun.COM    '''replace outgoing changesets with a single equivalent changeset
75011646SJames.McPherson@Sun.COM
75111646SJames.McPherson@Sun.COM    Replace all outgoing changesets with a single changeset containing
75211646SJames.McPherson@Sun.COM    equivalent changes.  This removes uninteresting changesets created
75311646SJames.McPherson@Sun.COM    during development that would only serve as noise in the gate.
75411646SJames.McPherson@Sun.COM
75511646SJames.McPherson@Sun.COM    Any changed file that is now identical in content to that in the
75611646SJames.McPherson@Sun.COM    parent workspace (whether identical in history or otherwise) will
75711646SJames.McPherson@Sun.COM    not be included in the new changeset.  Any merges information will
75811646SJames.McPherson@Sun.COM    also be removed.
75911646SJames.McPherson@Sun.COM
76011646SJames.McPherson@Sun.COM    If no files are changed in comparison to the parent workspace, the
76111646SJames.McPherson@Sun.COM    outgoing changesets will be removed, but no new changeset created.
76211646SJames.McPherson@Sun.COM
76311646SJames.McPherson@Sun.COM    recommit will refuse to run if the workspace contains more than
76411646SJames.McPherson@Sun.COM    one outgoing head, even if those heads are on the same branch.  To
76511646SJames.McPherson@Sun.COM    recommit with only one branch containing outgoing changesets, your
76611646SJames.McPherson@Sun.COM    workspace must be on that branch and at that branch head.
76711646SJames.McPherson@Sun.COM
76811646SJames.McPherson@Sun.COM    recommit will prompt you to take a backup if your workspace has
76911646SJames.McPherson@Sun.COM    been changed since the last backup was taken.  In almost all
77011646SJames.McPherson@Sun.COM    cases, you should allow it to take one (the default).
77111646SJames.McPherson@Sun.COM
77211646SJames.McPherson@Sun.COM    recommit cannot be run if the workspace contains any uncommitted
77311646SJames.McPherson@Sun.COM    changes, applied Mq patches, or has multiple outgoing heads (or
77411646SJames.McPherson@Sun.COM    branches).
77511646SJames.McPherson@Sun.COM    '''
77611646SJames.McPherson@Sun.COM
77711646SJames.McPherson@Sun.COM    ws = wslist[repo]
7787078Smjnelson
7797078Smjnelson    if not os.getcwd().startswith(repo.root):
7807078Smjnelson        raise util.Abort('recommit is not safe to run with -R')
7817078Smjnelson
78211646SJames.McPherson@Sun.COM    abort_if_dirty(ws)
7837078Smjnelson
78410399Srichlowe@richlowe.net    wlock = repo.wlock()
78510399Srichlowe@richlowe.net    lock = repo.lock()
78610399Srichlowe@richlowe.net
78710399Srichlowe@richlowe.net    try:
78811646SJames.McPherson@Sun.COM        parent = ws.parent(opts['parent'])
78911646SJames.McPherson@Sun.COM        between = repo.changelog.nodesbetween(ws.findoutgoing(parent))[2]
79011646SJames.McPherson@Sun.COM        heads = set(between) & set(repo.heads())
79111646SJames.McPherson@Sun.COM
79211646SJames.McPherson@Sun.COM        if len(heads) > 1:
79311646SJames.McPherson@Sun.COM            ui.warn('Workspace has multiple outgoing heads (or branches):\n')
79411646SJames.McPherson@Sun.COM            for head in sorted(map(repo.changelog.rev, heads), reverse=True):
79511646SJames.McPherson@Sun.COM                ui.warn('\t%d\n' % head)
79611646SJames.McPherson@Sun.COM            raise util.Abort('you must merge before recommitting')
79711646SJames.McPherson@Sun.COM
79811646SJames.McPherson@Sun.COM        active = ws.active(parent)
79911646SJames.McPherson@Sun.COM
80011646SJames.McPherson@Sun.COM        if filter(lambda b: len(b.parents()) > 1, active.bases()):
80111646SJames.McPherson@Sun.COM            raise util.Abort('Cannot recommit a merge of two non-outgoing '
80211646SJames.McPherson@Sun.COM                             'changesets')
8037078Smjnelson
80410399Srichlowe@richlowe.net        if len(active.revs) <= 0:
80510399Srichlowe@richlowe.net            raise util.Abort("no changes to recommit")
8067078Smjnelson
80710399Srichlowe@richlowe.net        if len(active.files()) <= 0:
80810399Srichlowe@richlowe.net            ui.warn("Recommitting %d active changesets, but no active files\n" %
80910399Srichlowe@richlowe.net                    len(active.revs))
8107078Smjnelson
81110399Srichlowe@richlowe.net        #
81210399Srichlowe@richlowe.net        # During the course of a recommit, any file bearing a name
81310399Srichlowe@richlowe.net        # matching the source name of any renamed file will be
81410399Srichlowe@richlowe.net        # clobbered by the operation.
81510399Srichlowe@richlowe.net        #
81610399Srichlowe@richlowe.net        # As such, we ask the user before proceeding.
81710399Srichlowe@richlowe.net        #
81810399Srichlowe@richlowe.net        bogosity = [f.parentname for f in active if f.is_renamed() and
81910399Srichlowe@richlowe.net                    os.path.exists(repo.wjoin(f.parentname))]
82010399Srichlowe@richlowe.net        if bogosity:
82110399Srichlowe@richlowe.net            ui.warn("The following file names are the original name of a "
82210399Srichlowe@richlowe.net                    "rename and also present\n"
82310399Srichlowe@richlowe.net                    "in the working directory:\n")
8247078Smjnelson
82510399Srichlowe@richlowe.net            for fname in bogosity:
82610399Srichlowe@richlowe.net                ui.warn("  %s\n" % fname)
8277078Smjnelson
82810399Srichlowe@richlowe.net            if not yes_no(ui, "These files will be removed by recommit."
82910399Srichlowe@richlowe.net                          "  Continue?",
83010399Srichlowe@richlowe.net                          False):
83110399Srichlowe@richlowe.net                raise util.Abort("recommit would clobber files")
83210399Srichlowe@richlowe.net
83310399Srichlowe@richlowe.net        user = opts['user'] or ui.username()
83410399Srichlowe@richlowe.net        comments = '\n'.join(active.comments())
8357078Smjnelson
83610399Srichlowe@richlowe.net        message = cmdutil.logmessage(opts) or ui.edit(comments, user)
83710399Srichlowe@richlowe.net        if not message:
83810399Srichlowe@richlowe.net            raise util.Abort('empty commit message')
83910399Srichlowe@richlowe.net
84011646SJames.McPherson@Sun.COM        bk = CdmBackup(ui, ws, backup_name(repo.root))
84110399Srichlowe@richlowe.net        if bk.need_backup():
84210399Srichlowe@richlowe.net            if yes_no(ui, 'Do you want to backup files first?', True):
84310399Srichlowe@richlowe.net                bk.backup()
8447078Smjnelson
84510399Srichlowe@richlowe.net        oldtags = repo.tags()
84610399Srichlowe@richlowe.net        clearedtags = [(name, nd, repo.changelog.rev(nd), local)
84710399Srichlowe@richlowe.net                for name, nd, local in active.tags()]
8487078Smjnelson
84911646SJames.McPherson@Sun.COM        ws.squishdeltas(active, message, user=user)
85010399Srichlowe@richlowe.net    finally:
85110399Srichlowe@richlowe.net        lock.release()
85210399Srichlowe@richlowe.net        wlock.release()
8537078Smjnelson
8547078Smjnelson    if clearedtags:
8557078Smjnelson        ui.write("Removed tags:\n")
8569400Srichlowe@richlowe.net        for name, nd, rev, local in sorted(clearedtags,
8579400Srichlowe@richlowe.net                                           key=lambda x: x[0].lower()):
8589400Srichlowe@richlowe.net            ui.write("  %5s:%s:\t%s%s\n" % (rev, node.short(nd),
8599400Srichlowe@richlowe.net                                            name, (local and ' (local)' or '')))
8607078Smjnelson
8619400Srichlowe@richlowe.net        for ntag, nnode in sorted(repo.tags().items(),
8629400Srichlowe@richlowe.net                                  key=lambda x: x[0].lower()):
8639400Srichlowe@richlowe.net            if ntag in oldtags and ntag != "tip":
8649400Srichlowe@richlowe.net                if oldtags[ntag] != nnode:
8659400Srichlowe@richlowe.net                    ui.write("tag '%s' now refers to revision %d:%s\n" %
8669400Srichlowe@richlowe.net                             (ntag, repo.changelog.rev(nnode),
8679400Srichlowe@richlowe.net                              node.short(nnode)))
8687078Smjnelson
8697078Smjnelson
8707078Smjnelsondef do_eval(cmd, files, root, changedir=True):
8717078Smjnelson    if not changedir:
8727078Smjnelson        os.chdir(root)
8737078Smjnelson
8747078Smjnelson    for path in sorted(files):
8757078Smjnelson        dirn, base = os.path.split(path)
8767078Smjnelson
8777078Smjnelson        if changedir:
8787078Smjnelson            os.chdir(os.path.join(root, dirn))
8797078Smjnelson
8807078Smjnelson        os.putenv('workspace', root)
8817078Smjnelson        os.putenv('filepath', path)
8827078Smjnelson        os.putenv('dir', dirn)
8837078Smjnelson        os.putenv('file', base)
8847078Smjnelson        os.system(cmd)
8857078Smjnelson
8867078Smjnelson
8877078Smjnelsondef cdm_eval(ui, repo, *command, **opts):
8887078Smjnelson    '''run cmd for each active file
8897078Smjnelson
8907078Smjnelson    cmd can refer to:
8917078Smjnelson      $file      -	active file basename.
8927078Smjnelson      $dir       -	active file dirname.
8937078Smjnelson      $filepath  -	path from workspace root to active file.
8947078Smjnelson      $workspace -	full path to workspace root.
8957078Smjnelson
8967078Smjnelson    For example "hg eval 'echo $dir; hg log -l3 $file'" will show the last
8977078Smjnelson    the 3 log entries for each active file, preceded by its directory.'''
8987078Smjnelson
8997078Smjnelson    act = wslist[repo].active(opts['parent'])
9007078Smjnelson    cmd = ' '.join(command)
9017078Smjnelson    files = [x.name for x in act if not x.is_removed()]
9027078Smjnelson
9037078Smjnelson    do_eval(cmd, files, repo.root, not opts['remain'])
9047078Smjnelson
9057078Smjnelson
9067078Smjnelsondef cdm_apply(ui, repo, *command, **opts):
9077078Smjnelson    '''apply cmd to all active files
9087078Smjnelson
9097078Smjnelson    For example 'hg apply wc -l' outputs a line count of active files.'''
9107078Smjnelson
9117078Smjnelson    act = wslist[repo].active(opts['parent'])
9127078Smjnelson
9137078Smjnelson    if opts['remain']:
9147078Smjnelson        appnd = ' $filepath'
9157078Smjnelson    else:
9167078Smjnelson        appnd = ' $file'
9177078Smjnelson
9187078Smjnelson    cmd = ' '.join(command) + appnd
9197078Smjnelson    files = [x.name for x in act if not x.is_removed()]
9207078Smjnelson
9217078Smjnelson    do_eval(cmd, files, repo.root, not opts['remain'])
9227078Smjnelson
9237078Smjnelson
92410399Srichlowe@richlowe.netdef cdm_reparent_11(ui, repo, parent):
9257078Smjnelson    '''reparent your workspace
9267078Smjnelson
9277078Smjnelson    Updates the 'default' path.'''
9287078Smjnelson
9297078Smjnelson    filename = repo.join('hgrc')
9307078Smjnelson
9317506SVenkateshwara.Tv@Sun.COM    p = ui.expandpath(parent)
93210399Srichlowe@richlowe.net    cp = util.configparser()
9337506SVenkateshwara.Tv@Sun.COM
9347078Smjnelson    try:
9357078Smjnelson        cp.read(filename)
9367078Smjnelson    except ConfigParser.ParsingError, inst:
9377078Smjnelson        raise util.Abort('failed to parse %s\n%s' % (filename, inst))
9387078Smjnelson
9397078Smjnelson    try:
9407078Smjnelson        fh = open(filename, 'w')
9417078Smjnelson    except IOError, e:
9427078Smjnelson        raise util.Abort('Failed to open workspace configuration: %s' % e)
9437078Smjnelson
9447078Smjnelson    if not cp.has_section('paths'):
9457078Smjnelson        cp.add_section('paths')
9467506SVenkateshwara.Tv@Sun.COM    cp.set('paths', 'default', p)
9477078Smjnelson    cp.write(fh)
9487078Smjnelson    fh.close()
9497078Smjnelson
9507078Smjnelson
95110399Srichlowe@richlowe.netdef cdm_reparent_13(ui, repo, parent):
95210399Srichlowe@richlowe.net    '''reparent your workspace
95310399Srichlowe@richlowe.net
95410399Srichlowe@richlowe.net    Updates the 'default' path in this repository's .hg/hgrc.'''
95510399Srichlowe@richlowe.net
95610399Srichlowe@richlowe.net    def append_new_parent(parent):
95710399Srichlowe@richlowe.net        fp = None
95810399Srichlowe@richlowe.net        try:
95910399Srichlowe@richlowe.net            fp = repo.opener('hgrc', 'a', atomictemp=True)
96010399Srichlowe@richlowe.net            if fp.tell() != 0:
96110399Srichlowe@richlowe.net                fp.write('\n')
96210399Srichlowe@richlowe.net            fp.write('[paths]\n'
96310399Srichlowe@richlowe.net                     'default = %s\n\n' % parent)
96410399Srichlowe@richlowe.net            fp.rename()
96510399Srichlowe@richlowe.net        finally:
96610399Srichlowe@richlowe.net            if fp and not fp.closed:
96710399Srichlowe@richlowe.net                fp.close()
96810399Srichlowe@richlowe.net
96910399Srichlowe@richlowe.net    def update_parent(path, line, parent):
97010399Srichlowe@richlowe.net        line = line - 1 # The line number we're passed will be 1-based
97110399Srichlowe@richlowe.net        fp = None
97210399Srichlowe@richlowe.net
97310399Srichlowe@richlowe.net        try:
97410399Srichlowe@richlowe.net            fp = open(path)
97510399Srichlowe@richlowe.net            data = fp.readlines()
97610399Srichlowe@richlowe.net        finally:
97710399Srichlowe@richlowe.net            if fp and not fp.closed:
97810399Srichlowe@richlowe.net                fp.close()
97910399Srichlowe@richlowe.net
98010399Srichlowe@richlowe.net        #
98110399Srichlowe@richlowe.net        # line will be the last line of any continued block, go back
98210399Srichlowe@richlowe.net        # to the first removing the continuation as we go.
98310399Srichlowe@richlowe.net        #
98410399Srichlowe@richlowe.net        while data[line][0].isspace():
98510399Srichlowe@richlowe.net            data.pop(line)
98610399Srichlowe@richlowe.net            line -= 1
98710399Srichlowe@richlowe.net
98810399Srichlowe@richlowe.net        assert data[line].startswith('default')
98910399Srichlowe@richlowe.net
99010399Srichlowe@richlowe.net        data[line] = "default = %s\n" % parent
99110399Srichlowe@richlowe.net        if data[-1] != '\n':
99210399Srichlowe@richlowe.net            data.append('\n')
99310399Srichlowe@richlowe.net
99410399Srichlowe@richlowe.net        try:
99510399Srichlowe@richlowe.net            fp = util.atomictempfile(path, 'w', 0644)
99610399Srichlowe@richlowe.net            fp.writelines(data)
99710399Srichlowe@richlowe.net            fp.rename()
99810399Srichlowe@richlowe.net        finally:
99910399Srichlowe@richlowe.net            if fp and not fp.closed:
100010399Srichlowe@richlowe.net                fp.close()
100110399Srichlowe@richlowe.net
100210399Srichlowe@richlowe.net    from mercurial import config
100310399Srichlowe@richlowe.net    parent = ui.expandpath(parent)
100410399Srichlowe@richlowe.net
100510399Srichlowe@richlowe.net    if not os.path.exists(repo.join('hgrc')):
100610399Srichlowe@richlowe.net        append_new_parent(parent)
100710399Srichlowe@richlowe.net        return
100810399Srichlowe@richlowe.net
100910399Srichlowe@richlowe.net    cfg = config.config()
101010399Srichlowe@richlowe.net    cfg.read(repo.join('hgrc'))
101110399Srichlowe@richlowe.net    source = cfg.source('paths', 'default')
101210399Srichlowe@richlowe.net
101310399Srichlowe@richlowe.net    if not source:
101410399Srichlowe@richlowe.net        append_new_parent(parent)
101510399Srichlowe@richlowe.net        return
101610399Srichlowe@richlowe.net    else:
101710399Srichlowe@richlowe.net        path, target = source.rsplit(':', 1)
101810399Srichlowe@richlowe.net
101910399Srichlowe@richlowe.net        if path != repo.join('hgrc'):
102010399Srichlowe@richlowe.net            raise util.Abort("Cannot edit path specification not in repo hgrc\n"
102110399Srichlowe@richlowe.net                             "default path is from: %s" % source)
102210399Srichlowe@richlowe.net
102310399Srichlowe@richlowe.net        update_parent(path, int(target), parent)
102410399Srichlowe@richlowe.net
102510399Srichlowe@richlowe.netif Version.at_least("1.3"):
102610399Srichlowe@richlowe.net    cdm_reparent = cdm_reparent_13
102710399Srichlowe@richlowe.netelse:
102810399Srichlowe@richlowe.net    cdm_reparent = cdm_reparent_11
102910399Srichlowe@richlowe.net
103010399Srichlowe@richlowe.net
10317078Smjnelsondef backup_name(fullpath):
10327078Smjnelson    '''Create a backup directory name based on the specified path.
10337078Smjnelson
10347078Smjnelson    In most cases this is the basename of the path specified, but
10357078Smjnelson    certain cases are handled specially to create meaningful names'''
10367078Smjnelson
10377078Smjnelson    special = ['usr/closed']
10387078Smjnelson
10397078Smjnelson    fullpath = fullpath.rstrip(os.path.sep).split(os.path.sep)
10407078Smjnelson
10417078Smjnelson    #
10427078Smjnelson    # If a path is 'special', we append the basename of the path to
10437078Smjnelson    # the path element preceding the constant, special, part.
10447078Smjnelson    #
10457078Smjnelson    # Such that for instance:
10467078Smjnelson    #     /foo/bar/onnv-fixes/usr/closed
10477078Smjnelson    #  has a backup name of:
10487078Smjnelson    #     onnv-fixes-closed
10497078Smjnelson    #
10507078Smjnelson    for elt in special:
10517078Smjnelson        elt = elt.split(os.path.sep)
10527078Smjnelson        pathpos = len(elt)
10537078Smjnelson
10547078Smjnelson        if fullpath[-pathpos:] == elt:
10557078Smjnelson            return "%s-%s" % (fullpath[-pathpos - 1], elt[-1])
10567078Smjnelson    else:
10577078Smjnelson        return fullpath[-1]
10587078Smjnelson
10597078Smjnelson
10607078Smjnelsondef cdm_backup(ui, repo, if_newer=False):
10617078Smjnelson    '''make backup copies of all workspace changes
10627078Smjnelson
10637078Smjnelson    Backups will be stored in ~/cdm.backup/<basename of workspace>.'''
10647078Smjnelson
10657078Smjnelson    name = backup_name(repo.root)
10667078Smjnelson    bk = CdmBackup(ui, wslist[repo], name)
10677078Smjnelson
106810399Srichlowe@richlowe.net    wlock = repo.wlock()
106910399Srichlowe@richlowe.net    lock = repo.lock()
107010399Srichlowe@richlowe.net
107110399Srichlowe@richlowe.net    try:
107210399Srichlowe@richlowe.net        if if_newer and not bk.need_backup():
107310399Srichlowe@richlowe.net            ui.status('backup is up-to-date\n')
107410399Srichlowe@richlowe.net        else:
107510399Srichlowe@richlowe.net            bk.backup()
107610399Srichlowe@richlowe.net    finally:
107710399Srichlowe@richlowe.net        lock.release()
107810399Srichlowe@richlowe.net        wlock.release()
10797078Smjnelson
10807078Smjnelson
10817078Smjnelsondef cdm_restore(ui, repo, backup, **opts):
10827078Smjnelson    '''restore workspace from backup
10837078Smjnelson
10847078Smjnelson    Restores a workspace from the specified backup directory and generation
10857078Smjnelson    (which defaults to the latest).'''
10867078Smjnelson
10877078Smjnelson    if not os.getcwd().startswith(repo.root):
10887078Smjnelson        raise util.Abort('restore is not safe to run with -R')
108910399Srichlowe@richlowe.net
109010399Srichlowe@richlowe.net    abort_if_dirty(wslist[repo])
10917078Smjnelson
10927078Smjnelson    if opts['generation']:
10937078Smjnelson        gen = int(opts['generation'])
10947078Smjnelson    else:
10957078Smjnelson        gen = None
10967078Smjnelson
10977078Smjnelson    if os.path.exists(backup):
10987078Smjnelson        backup = os.path.abspath(backup)
10997078Smjnelson
110010399Srichlowe@richlowe.net    wlock = repo.wlock()
110110399Srichlowe@richlowe.net    lock = repo.lock()
110210399Srichlowe@richlowe.net
110310399Srichlowe@richlowe.net    try:
110410399Srichlowe@richlowe.net        bk = CdmBackup(ui, wslist[repo], backup)
110510399Srichlowe@richlowe.net        bk.restore(gen)
110610399Srichlowe@richlowe.net    finally:
110710399Srichlowe@richlowe.net        lock.release()
110810399Srichlowe@richlowe.net        wlock.release()
11097078Smjnelson
11108018SVladimir.Kotal@Sun.COM
11118018SVladimir.Kotal@Sun.COMdef cdm_webrev(ui, repo, **opts):
11128018SVladimir.Kotal@Sun.COM    '''generate webrev and optionally upload it
11138018SVladimir.Kotal@Sun.COM
11148018SVladimir.Kotal@Sun.COM    This command passes all arguments to webrev script'''
11158018SVladimir.Kotal@Sun.COM
11168018SVladimir.Kotal@Sun.COM    webrev_args = ""
11178018SVladimir.Kotal@Sun.COM    for key in opts.keys():
11189006Srichlowe@richlowe.net        if opts[key]:
11199006Srichlowe@richlowe.net            if type(opts[key]) == type(True):
11209006Srichlowe@richlowe.net                webrev_args += '-' + key + ' '
11218018SVladimir.Kotal@Sun.COM            else:
11229006Srichlowe@richlowe.net                webrev_args += '-' + key + ' ' + opts[key] + ' '
11238018SVladimir.Kotal@Sun.COM
11248018SVladimir.Kotal@Sun.COM    retval = os.system('webrev ' + webrev_args)
11258018SVladimir.Kotal@Sun.COM    if retval != 0:
11268018SVladimir.Kotal@Sun.COM        return retval - 255
11278018SVladimir.Kotal@Sun.COM
11288018SVladimir.Kotal@Sun.COM    return 0
11298018SVladimir.Kotal@Sun.COM
11308018SVladimir.Kotal@Sun.COM
11317078Smjnelsoncmdtable = {
11327078Smjnelson    'apply': (cdm_apply, [('p', 'parent', '', 'parent workspace'),
11337078Smjnelson                          ('r', 'remain', None, 'do not change directories')],
11347078Smjnelson              'hg apply [-p PARENT] [-r] command...'),
11357078Smjnelson    'arcs': (cdm_arcs, [('p', 'parent', '', 'parent workspace')],
11367078Smjnelson             'hg arcs [-p PARENT]'),
11377078Smjnelson    '^backup|bu': (cdm_backup, [('t', 'if-newer', None,
11387078Smjnelson                             'only backup if workspace files are newer')],
11397078Smjnelson               'hg backup [-t]'),
11407078Smjnelson    'branchchk': (cdm_branchchk, [('p', 'parent', '', 'parent workspace')],
11417078Smjnelson                  'hg branchchk [-p PARENT]'),
11427078Smjnelson    'bugs': (cdm_bugs, [('p', 'parent', '', 'parent workspace')],
11437078Smjnelson             'hg bugs [-p PARENT]'),
11447078Smjnelson    'cddlchk': (cdm_cddlchk, [('p', 'parent', '', 'parent workspace')],
11457078Smjnelson                'hg cddlchk [-p PARENT]'),
11467078Smjnelson    'comchk': (cdm_comchk, [('p', 'parent', '', 'parent workspace'),
11477078Smjnelson                            ('N', 'nocheck', None,
11487078Smjnelson                             'do not compare comments with databases')],
11497078Smjnelson               'hg comchk [-p PARENT]'),
11507078Smjnelson    'comments': (cdm_comments, [('p', 'parent', '', 'parent workspace')],
11517078Smjnelson                 'hg comments [-p PARENT]'),
11527078Smjnelson    'copyright': (cdm_copyright, [('p', 'parent', '', 'parent workspace')],
11537078Smjnelson                  'hg copyright [-p PARENT]'),
11547078Smjnelson    'cstyle': (cdm_cstyle, [('p', 'parent', '', 'parent workspace')],
11557078Smjnelson               'hg cstyle [-p PARENT]'),
11567078Smjnelson    'eval': (cdm_eval, [('p', 'parent', '', 'parent workspace'),
11577078Smjnelson                        ('r', 'remain', None, 'do not change directories')],
11587078Smjnelson             'hg eval [-p PARENT] [-r] command...'),
11597078Smjnelson    'hdrchk': (cdm_hdrchk, [('p', 'parent', '', 'parent workspace')],
11607078Smjnelson               'hg hdrchk [-p PARENT]'),
11617078Smjnelson    'jstyle': (cdm_jstyle, [('p', 'parent', '', 'parent workspace')],
11627078Smjnelson               'hg jstyle [-p PARENT]'),
11637078Smjnelson    'keywords': (cdm_keywords, [('p', 'parent', '', 'parent workspace')],
11647078Smjnelson                 'hg keywords [-p PARENT]'),
11657078Smjnelson    '^list|active': (cdm_list, [('p', 'parent', '', 'parent workspace'),
11667078Smjnelson                                ('r', 'removed', None, 'show removed files'),
11677078Smjnelson                                ('a', 'added', None, 'show added files'),
11687078Smjnelson                                ('m', 'modified', None, 'show modified files')],
11697078Smjnelson                    'hg list [-amrRu] [-p PARENT]'),
11708744SAli.Bahrami@Sun.COM    'mapfilechk': (cdm_mapfilechk, [('p', 'parent', '', 'parent workspace')],
11718744SAli.Bahrami@Sun.COM                'hg mapfilechk [-p PARENT]'),
11727078Smjnelson    '^nits': (cdm_nits, [('p', 'parent', '', 'parent workspace')],
11737078Smjnelson             'hg nits [-p PARENT]'),
11747078Smjnelson    '^pbchk': (cdm_pbchk, [('p', 'parent', '', 'parent workspace'),
11757078Smjnelson                           ('N', 'nocheck', None, 'skip RTI check')],
11767078Smjnelson              'hg pbchk [-N] [-p PARENT]'),
11777078Smjnelson    'permchk': (cdm_permchk, [('p', 'parent', '', 'parent workspace')],
11787078Smjnelson                'hg permchk [-p PARENT]'),
11797298SMark.J.Nelson@Sun.COM    '^pdiffs': (cdm_pdiffs, [('p', 'parent', '', 'parent workspace'),
11807298SMark.J.Nelson@Sun.COM                             ('a', 'text', None, 'treat all files as text'),
11817298SMark.J.Nelson@Sun.COM                             ('g', 'git', None, 'use extended git diff format'),
11827298SMark.J.Nelson@Sun.COM                             ('w', 'ignore-all-space', None,
11837298SMark.J.Nelson@Sun.COM                              'ignore white space when comparing lines'),
11847298SMark.J.Nelson@Sun.COM                             ('b', 'ignore-space-change', None,
11857298SMark.J.Nelson@Sun.COM                              'ignore changes in the amount of white space'),
11867298SMark.J.Nelson@Sun.COM                             ('B', 'ignore-blank-lines', None,
11877298SMark.J.Nelson@Sun.COM                              'ignore changes whos lines are all blank'),
11887298SMark.J.Nelson@Sun.COM                             ('U', 'unified', 3,
11897298SMark.J.Nelson@Sun.COM                              'number of lines of context to show'),
11907298SMark.J.Nelson@Sun.COM                             ('I', 'include', [],
11917298SMark.J.Nelson@Sun.COM                              'include names matching the given patterns'),
11927298SMark.J.Nelson@Sun.COM                             ('X', 'exclude', [],
11937298SMark.J.Nelson@Sun.COM                              'exclude names matching the given patterns')],
11947298SMark.J.Nelson@Sun.COM               'hg pdiffs [OPTION...] [-p PARENT] [FILE...]'),
11957078Smjnelson    '^recommit|reci': (cdm_recommit, [('p', 'parent', '', 'parent workspace'),
11967078Smjnelson                                      ('f', 'force', None, 'force operation'),
11977078Smjnelson                                      ('m', 'message', '',
11987078Smjnelson                                       'use <text> as commit message'),
11997078Smjnelson                                      ('l', 'logfile', '',
12007078Smjnelson                                       'read commit message from file'),
12017078Smjnelson                                      ('u', 'user', '',
12027078Smjnelson                                       'record user as committer')],
12037078Smjnelson                       'hg recommit [-f] [-p PARENT]'),
12047078Smjnelson    'renamed': (cdm_renamed, [('p', 'parent', '', 'parent workspace')],
12057078Smjnelson                'hg renamed [-p PARENT]'),
12067078Smjnelson    'reparent': (cdm_reparent, [], 'hg reparent PARENT'),
12077078Smjnelson    '^restore': (cdm_restore, [('g', 'generation', '', 'generation number')],
12087078Smjnelson                 'hg restore [-g GENERATION] BACKUP'),
12097078Smjnelson    'rtichk': (cdm_rtichk, [('p', 'parent', '', 'parent workspace'),
12107078Smjnelson                            ('N', 'nocheck', None, 'skip RTI check')],
12117078Smjnelson               'hg rtichk [-N] [-p PARENT]'),
12127078Smjnelson    'tagchk': (cdm_tagchk, [('p', 'parent', '', 'parent workspace')],
12137078Smjnelson               'hg tagchk [-p PARENT]'),
12149561SVladimir.Kotal@Sun.COM    'webrev': (cdm_webrev, [('C', 'C', '', 'ITS priority file'),
12159561SVladimir.Kotal@Sun.COM                            ('D', 'D', '', 'delete remote webrev'),
12169561SVladimir.Kotal@Sun.COM                            ('I', 'I', '', 'ITS configuration file'),
12178365SVladimir.Kotal@Sun.COM                            ('i', 'i', '', 'include file'),
12188018SVladimir.Kotal@Sun.COM                            ('l', 'l', '', 'extract file list from putback -n'),
12198018SVladimir.Kotal@Sun.COM                            ('N', 'N', None, 'supress comments'),
12208018SVladimir.Kotal@Sun.COM                            ('n', 'n', None, 'do not generate webrev'),
12219006Srichlowe@richlowe.net                            ('O', 'O', None, 'OpenSolaris mode'),
12228018SVladimir.Kotal@Sun.COM                            ('o', 'o', '', 'output directory'),
12238018SVladimir.Kotal@Sun.COM                            ('p', 'p', '', 'use specified parent'),
12248018SVladimir.Kotal@Sun.COM                            ('t', 't', '', 'upload target'),
12259006Srichlowe@richlowe.net                            ('U', 'U', None, 'upload the webrev'),
12268018SVladimir.Kotal@Sun.COM                            ('w', 'w', '', 'use wx active file')],
12278018SVladimir.Kotal@Sun.COM               'hg webrev [WEBREV_OPTIONS]'),
12287078Smjnelson}
1229