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