cdm.py revision c7f512e49da83ae2cd3d4b339e1a8366544471e2
#
# it under the terms of the GNU General Public License version 2
# as published by the Free Software Foundation.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
#
#
# Copyright 2010 Sun Microsystems, Inc. All rights reserved.
# Use is subject to license terms.
#
'''OpenSolaris workspace extensions for mercurial
This extension contains a number of commands to help you work within
the OpenSolaris consolidations.
Common uses:
Show diffs relative to parent workspace - pdiffs
Check source style rules - nits
Run pre-putback checks - pbchk
Collapse all your changes into a single changeset - recommit'''
#
# NB: This assumes the normal directory structure, with this
#
# If you change that, change this
#
try:
import ConfigParser
if default:
prompt = ' [Y/n]:'
defanswer = 'y'
else:
prompt = ' [y/N]:'
defanswer = 'n'
else:
'''Build a list of files in which we're interested.
If no files are specified take files from the active list relative
to 'parent'.
Return a list of 2-tuples the first element being a path relative
to the current directory and the second an entry from the active
list, or None if an explicit file list was given.'''
if files:
else:
'''return a function which returns boolean indicating whether a file
should be skipped for CMD.'''
#
# The ignore routines need a canonical path to the file (relative to the
# repo root), whereas the check commands get paths relative to the cwd.
#
# Wrap our argument such that the path is canonified before it is checked.
#
def canonified_check(ignfunc):
def f(path):
return f
ignorefiles = []
if ignorefiles:
return canonified_check(ign)
else:
def abort_if_dirty(ws):
'''Abort if the workspace has uncommitted changes, merges,
branches, or has Mq patches applied'''
if ws.mq_applied():
#
# Adding a reference to WorkSpace from a repo causes a circular reference
# repo <-> WorkSpace.
#
# This prevents repo, WorkSpace and members thereof from being garbage
# collected. Since transactions are aborted when the transaction object
# is collected, and localrepo holds a reference to the most recently created
# transaction, this prevents transactions from cleanly aborting.
#
# Instead, we hold the repo->WorkSpace association in a dictionary, breaking
# that dependence.
#
wslist = {}
else:
'python:hgext_cdm.pbconfirm')
if source == 'push':
return 1
else:
'''list workspace diffs relative to parent workspace
The parent tip is taken to be the latest revision shared between
us and the parent workspace.'''
if diffs:
'''list files changed relative to parent workspace
The parent tip is taken to be the latest revision shared between
us and the parent workspace.'''
wanted = []
if opts['added']:
if opts['modified']:
if opts['removed']:
lst = {}
continue
if elt.is_renamed():
else:
'show all ARC cases in checkin comments'
# We take a set of the appropriate comments to eliminate duplicates.
'show all bug IDs in checkin comments'
'show checkin comments for active files'
'''show renamed active files
Renamed files are shown in the format
newname oldname
One pair per-line.'''
'''check checkin comments for active files
Check that checkin comments conform to O/N rules.'''
'''check for a valid CDDL block in active files
See http://www.opensolaris.org/os/community/on/devref_toc/devref_7/#7_2_3_nonformatting_considerations
for more info.'''
ret = 0
for f, e in filelist:
if e and e.is_removed():
continue
continue
elif e and e.is_added():
else:
return ret
'''check for a valid MAPFILE header block in active files
Check that all link-editor mapfiles contain the standard mapfile
header comment directing the reader to the document containing
Solaris object versioning rules (README.mapfile).'''
ret = 0
for f, e in filelist:
if e and e.is_removed():
continue
continue
continue
return ret
'''check active files for valid copyrights
Check that all active files have a valid copyright containing the
current year (and *only* the current year).
for more info.'''
ret = 0
for f, e in filelist:
if e and e.is_removed():
continue
continue
return ret
'''check active header files conform to O/N rules'''
ret = 0
for f, e in filelist:
if e and e.is_removed():
continue
elif not f.endswith('.h'):
continue
continue
return ret
'''check active C source files conform to the C Style Guide
ret = 0
for f, e in filelist:
if e and e.is_removed():
continue
continue
continue
return ret
'check active Java source files for common stylistic errors'
ret = 0
for f, e in filelist:
if e and e.is_removed():
continue
elif not f.endswith('.java'):
continue
continue
return ret
'''check active files permission - warn +x (execute) mode'''
exeFiles = []
for f, e in filelist:
if e and e.is_removed():
continue
continue
'(+x) permission set,\nremove unless intentional:\n')
'''check if .hgtags is active and issue warning
Tag sharing among repositories is restricted to gatekeepers'''
if ".hgtags" in active:
'Only gatekeepers should add or modify such tags.\n'
'Use the following commands to revert these changes:\n'
' hg revert -r%d %s\n'
' hg commit %s\n'
'You should also recommit before integration\n' %
return 1
return 0
'''check if multiple heads (or branches) are present, or if
branch changes are made'''
#
# We care if there's more than one head, and those heads aren't
# identical to the dirstate parents (if they are identical, it's
# an uncommitted merge which mergechk will catch, no need to
# complain twice).
#
return 1
"Only gatekeepers should push new branches.\n"
"Use the following commands to restore the branch name:\n"
" hg branch [-f] default\n"
" hg commit\n"
"You should also recommit before integration\n" %
return 1
for t in branches:
if t == 'default':
continue
"Use the following commands to remove extraneous branches.\n"
" hg branch [-f] default\n"
" hg commit"
"You should also recommit before integration\n")
return 1
return 0
Only works on SWAN.'''
return 0
if not onSWAN():
return 0
bugs = []
# RTI normalizes the gate path for us
'''check source files do not contain SCCS keywords'''
ret = 0
for f, e in filelist:
if e and e.is_removed():
continue
continue
return ret
#
# NB:
# There's no reason to hook this up as an invokable command, since
# we have 'hg status', but it must accept the same arguments.
#
'''Warn the user if they have uncommitted changes'''
if st:
return 1
return 0
'''Warn the user if their workspace contains merges'''
if merges:
return 1
return 0
'''Run CMDS (with OPTS) over active files in WS'''
ret = 0
else:
return ret
'''check for stylistic nits in active files
Run cddlchk, copyright, cstyle, hdrchk, jstyle, mapfilechk,
permchk, and keywords checks.'''
cmds = [cdm_cddlchk,
'''pre-putback check all active files
Run cddlchk, comchk, copyright, cstyle, hdrchk, jstyle, mapfilechk,
permchk, tagchk, branchchk, keywords and rtichk checks. Additionally,
warn about uncommitted changes.'''
#
# The current ordering of these is that the commands from cdm_nits
# run first in the same order as they would in cdm_nits. Then the
# pbchk specifics run
#
cmds = [cdm_cddlchk,
'''replace outgoing changesets with a single equivalent changeset
Replace all outgoing changesets with a single changeset containing
equivalent changes. This removes uninteresting changesets created
during development that would only serve as noise in the gate.
Any changed file that is now identical in content to that in the
parent workspace (whether identical in history or otherwise) will
not be included in the new changeset. Any merges information will
also be removed.
If no files are changed in comparison to the parent workspace, the
outgoing changesets will be removed, but no new changeset created.
recommit will refuse to run if the workspace contains more than
one outgoing head, even if those heads are on the same branch. To
recommit with only one branch containing outgoing changesets, your
workspace must be on that branch and at that branch head.
recommit will prompt you to take a backup if your workspace has
been changed since the last backup was taken. In almost all
cases, you should allow it to take one (the default).
recommit cannot be run if the workspace contains any uncommitted
changes, applied Mq patches, or has multiple outgoing heads (or
branches).
'''
try:
'changesets')
#
# During the course of a recommit, any file bearing a name
# matching the source name of any renamed file will be
# clobbered by the operation.
#
# As such, we ask the user before proceeding.
#
if bogosity:
"rename and also present\n"
"in the working directory:\n")
" Continue?",
False):
if not message:
if bk.need_backup():
finally:
if clearedtags:
if not changedir:
if changedir:
'''run cmd for each active file
cmd can refer to:
$file - active file basename.
$dir - active file dirname.
$filepath - path from workspace root to active file.
$workspace - full path to workspace root.
For example "hg eval 'echo $dir; hg log -l3 $file'" will show the last
the 3 log entries for each active file, preceded by its directory.'''
'''apply cmd to all active files
For example 'hg apply wc -l' outputs a line count of active files.'''
if opts['remain']:
appnd = ' $filepath'
else:
appnd = ' $file'
'''reparent your workspace
Updates the 'default' path.'''
try:
try:
except IOError, e:
'''reparent your workspace
def append_new_parent(parent):
fp = None
try:
'default = %s\n\n' % parent)
finally:
fp = None
try:
finally:
#
# line will be the last line of any continued block, go back
# to the first removing the continuation as we go.
#
line -= 1
try:
finally:
return
if not source:
return
else:
"default path is from: %s" % source)
else:
def backup_name(fullpath):
'''Create a backup directory name based on the specified path.
In most cases this is the basename of the path specified, but
certain cases are handled specially to create meaningful names'''
#
# If a path is 'special', we append the basename of the path to
# the path element preceding the constant, special, part.
#
# Such that for instance:
# /foo/bar/onnv-fixes/usr/closed
# has a backup name of:
# onnv-fixes-closed
#
else:
return fullpath[-1]
'''make backup copies of all workspace changes
Backups will be stored in ~/cdm.backup/<basename of workspace>.'''
try:
else:
finally:
'''restore workspace from backup
Restores a workspace from the specified backup directory and generation
(which defaults to the latest).'''
if opts['generation']:
else:
gen = None
try:
finally:
'''generate webrev and optionally upload it
This command passes all arguments to webrev script'''
webrev_args = ""
else:
if retval != 0:
return retval - 255
return 0
cmdtable = {
('r', 'remain', None, 'do not change directories')],
'hg apply [-p PARENT] [-r] command...'),
'hg arcs [-p PARENT]'),
'only backup if workspace files are newer')],
'hg backup [-t]'),
'hg branchchk [-p PARENT]'),
'hg bugs [-p PARENT]'),
'hg cddlchk [-p PARENT]'),
('N', 'nocheck', None,
'do not compare comments with databases')],
'hg comchk [-p PARENT]'),
'hg comments [-p PARENT]'),
'hg copyright [-p PARENT]'),
'hg cstyle [-p PARENT]'),
('r', 'remain', None, 'do not change directories')],
'hg eval [-p PARENT] [-r] command...'),
'hg hdrchk [-p PARENT]'),
'hg jstyle [-p PARENT]'),
'hg keywords [-p PARENT]'),
('r', 'removed', None, 'show removed files'),
('a', 'added', None, 'show added files'),
('m', 'modified', None, 'show modified files')],
'hg list [-amrRu] [-p PARENT]'),
'hg mapfilechk [-p PARENT]'),
'hg nits [-p PARENT]'),
('N', 'nocheck', None, 'skip RTI check')],
'hg pbchk [-N] [-p PARENT]'),
'hg permchk [-p PARENT]'),
('a', 'text', None, 'treat all files as text'),
('g', 'git', None, 'use extended git diff format'),
('w', 'ignore-all-space', None,
'ignore white space when comparing lines'),
('b', 'ignore-space-change', None,
'ignore changes in the amount of white space'),
('B', 'ignore-blank-lines', None,
'ignore changes whos lines are all blank'),
('U', 'unified', 3,
'number of lines of context to show'),
('I', 'include', [],
'include names matching the given patterns'),
('X', 'exclude', [],
'exclude names matching the given patterns')],
'hg pdiffs [OPTION...] [-p PARENT] [FILE...]'),
('f', 'force', None, 'force operation'),
('m', 'message', '',
'use <text> as commit message'),
('l', 'logfile', '',
'read commit message from file'),
('u', 'user', '',
'record user as committer')],
'hg recommit [-f] [-p PARENT]'),
'hg renamed [-p PARENT]'),
'hg restore [-g GENERATION] BACKUP'),
('N', 'nocheck', None, 'skip RTI check')],
'hg rtichk [-N] [-p PARENT]'),
'hg tagchk [-p PARENT]'),
('D', 'D', '', 'delete remote webrev'),
('I', 'I', '', 'ITS configuration file'),
('i', 'i', '', 'include file'),
('l', 'l', '', 'extract file list from putback -n'),
('N', 'N', None, 'supress comments'),
('n', 'n', None, 'do not generate webrev'),
('O', 'O', None, 'OpenSolaris mode'),
('o', 'o', '', 'output directory'),
('p', 'p', '', 'use specified parent'),
('t', 't', '', 'upload target'),
('U', 'U', None, 'upload the webrev'),
('w', 'w', '', 'use wx active file')],
'hg webrev [WEBREV_OPTIONS]'),
}