diff options
| author | Richard Lowe <richlowe@richlowe.net> | 2018-01-26 19:14:28 +0000 |
|---|---|---|
| committer | Richard Lowe <richlowe@richlowe.net> | 2018-02-03 17:47:56 +0000 |
| commit | 0f554fe53a6056402dbaf025d945afa8659af0c7 (patch) | |
| tree | 13db20440711f948e8410777da5cf2ea4771744d | |
| parent | a96858a0ffbb0a37a4c126a6889a50342c8e0ac5 (diff) | |
| download | illumos-joyent-0f554fe53a6056402dbaf025d945afa8659af0c7.tar.gz | |
9001 cdm is useless, remove it
9002 webrev should know how to get the git user name
Reviewed by: Yuri Pankov <yuri.pankov@nexenta.com>
Reviewed by: Joshua M. Clulow <josh@sysmgr.org>
Approved by: Gordon Ross <gwr@nexenta.com>
| -rw-r--r-- | usr/src/pkg/manifests/developer-build-onbld.mf | 19 | ||||
| -rw-r--r-- | usr/src/tools/Makefile | 1 | ||||
| -rw-r--r-- | usr/src/tools/README.tools | 19 | ||||
| -rw-r--r-- | usr/src/tools/onbld/Makefile | 1 | ||||
| -rw-r--r-- | usr/src/tools/onbld/Scm/Backup.py | 980 | ||||
| -rw-r--r-- | usr/src/tools/onbld/Scm/Makefile | 5 | ||||
| -rw-r--r-- | usr/src/tools/onbld/Scm/Version.py | 122 | ||||
| -rw-r--r-- | usr/src/tools/onbld/Scm/WorkSpace.py | 1076 | ||||
| -rw-r--r-- | usr/src/tools/onbld/hgext/Makefile | 58 | ||||
| -rw-r--r-- | usr/src/tools/onbld/hgext/__init__.py | 28 | ||||
| -rw-r--r-- | usr/src/tools/onbld/hgext/cdm.py | 1530 | ||||
| -rw-r--r-- | usr/src/tools/scripts/Makefile | 4 | ||||
| -rw-r--r-- | usr/src/tools/scripts/hg-active.py | 130 | ||||
| -rw-r--r-- | usr/src/tools/scripts/hgsetup.1onbld | 68 | ||||
| -rw-r--r-- | usr/src/tools/scripts/hgsetup.sh | 189 | ||||
| -rw-r--r-- | usr/src/tools/scripts/hgstyle | 24 | ||||
| -rw-r--r-- | usr/src/tools/scripts/webrev.1onbld | 10 | ||||
| -rw-r--r-- | usr/src/tools/scripts/webrev.sh | 294 |
18 files changed, 15 insertions, 4543 deletions
diff --git a/usr/src/pkg/manifests/developer-build-onbld.mf b/usr/src/pkg/manifests/developer-build-onbld.mf index 6c3b0eddb1..fe07ff881c 100644 --- a/usr/src/pkg/manifests/developer-build-onbld.mf +++ b/usr/src/pkg/manifests/developer-build-onbld.mf @@ -55,7 +55,6 @@ dir path=opt/onbld/lib/python$(PYTHON_VERSION) dir path=opt/onbld/lib/python$(PYTHON_VERSION)/onbld dir path=opt/onbld/lib/python$(PYTHON_VERSION)/onbld/Checks dir path=opt/onbld/lib/python$(PYTHON_VERSION)/onbld/Scm -dir path=opt/onbld/lib/python$(PYTHON_VERSION)/onbld/hgext dir path=opt/onbld/man dir path=opt/onbld/man/man1onbld dir path=opt/onbld/share @@ -104,8 +103,6 @@ file path=opt/onbld/bin/flg.flp mode=0555 file path=opt/onbld/bin/genoffsets mode=0555 file path=opt/onbld/bin/git-pbchk mode=0555 file path=opt/onbld/bin/hdrchk mode=0555 -file path=opt/onbld/bin/hg-active mode=0555 -file path=opt/onbld/bin/hgsetup mode=0555 file path=opt/onbld/bin/interface_check mode=0555 file path=opt/onbld/bin/interface_cmp mode=0555 file path=opt/onbld/bin/jstyle mode=0555 @@ -130,7 +127,6 @@ file path=opt/onbld/env/illumos file path=opt/onbld/etc/exception_lists/check_rtime file path=opt/onbld/etc/exception_lists/interface_check file path=opt/onbld/etc/exception_lists/interface_cmp -file path=opt/onbld/etc/hgstyle file path=opt/onbld/etc/its.conf file path=opt/onbld/etc/its.reg file path=opt/onbld/lib/$(ARCH)/64/libmakestate.so.1 @@ -193,28 +189,14 @@ file path=opt/onbld/lib/python$(PYTHON_VERSION)/onbld/Checks/__init__.py \ mode=0444 file path=opt/onbld/lib/python$(PYTHON_VERSION)/onbld/Checks/__init__.pyc \ mode=0444 -file path=opt/onbld/lib/python$(PYTHON_VERSION)/onbld/Scm/Backup.py mode=0444 -file path=opt/onbld/lib/python$(PYTHON_VERSION)/onbld/Scm/Backup.pyc mode=0444 file path=opt/onbld/lib/python$(PYTHON_VERSION)/onbld/Scm/Ignore.py mode=0444 file path=opt/onbld/lib/python$(PYTHON_VERSION)/onbld/Scm/Ignore.pyc mode=0444 -file path=opt/onbld/lib/python$(PYTHON_VERSION)/onbld/Scm/Version.py mode=0444 -file path=opt/onbld/lib/python$(PYTHON_VERSION)/onbld/Scm/Version.pyc \ - mode=0444 -file path=opt/onbld/lib/python$(PYTHON_VERSION)/onbld/Scm/WorkSpace.py \ - mode=0444 -file path=opt/onbld/lib/python$(PYTHON_VERSION)/onbld/Scm/WorkSpace.pyc \ - mode=0444 file path=opt/onbld/lib/python$(PYTHON_VERSION)/onbld/Scm/__init__.py \ mode=0444 file path=opt/onbld/lib/python$(PYTHON_VERSION)/onbld/Scm/__init__.pyc \ mode=0444 file path=opt/onbld/lib/python$(PYTHON_VERSION)/onbld/__init__.py mode=0444 file path=opt/onbld/lib/python$(PYTHON_VERSION)/onbld/__init__.pyc mode=0444 -file path=opt/onbld/lib/python$(PYTHON_VERSION)/onbld/hgext/__init__.py \ - mode=0444 -file path=opt/onbld/lib/python$(PYTHON_VERSION)/onbld/hgext/__init__.pyc \ - mode=0444 -file path=opt/onbld/lib/python$(PYTHON_VERSION)/onbld/hgext/cdm.py mode=0444 file path=opt/onbld/man/man1onbld/Install.1onbld file path=opt/onbld/man/man1onbld/bldenv.1onbld file path=opt/onbld/man/man1onbld/bringovercheck.1onbld @@ -230,7 +212,6 @@ file path=opt/onbld/man/man1onbld/findunref.1onbld file path=opt/onbld/man/man1onbld/flg.flp.1onbld file path=opt/onbld/man/man1onbld/git-pbchk.1onbld file path=opt/onbld/man/man1onbld/hdrchk.1onbld -file path=opt/onbld/man/man1onbld/hgsetup.1onbld file path=opt/onbld/man/man1onbld/interface_check.1onbld file path=opt/onbld/man/man1onbld/interface_cmp.1onbld file path=opt/onbld/man/man1onbld/jstyle.1onbld diff --git a/usr/src/tools/Makefile b/usr/src/tools/Makefile index 5bc163d15a..f2d7fcafb9 100644 --- a/usr/src/tools/Makefile +++ b/usr/src/tools/Makefile @@ -100,7 +100,6 @@ ROOTDIRS= \ $(ROOTONBLD)/lib/python$(PYTHON_VERSION) \ $(ROOTONBLD)/lib/python$(PYTHON_VERSION)/onbld \ $(ROOTONBLD)/lib/python$(PYTHON_VERSION)/onbld/Checks \ - $(ROOTONBLD)/lib/python$(PYTHON_VERSION)/onbld/hgext \ $(ROOTONBLD)/lib/python$(PYTHON_VERSION)/onbld/Scm \ $(ROOTONBLD)/env \ $(ROOTONBLD)/etc \ diff --git a/usr/src/tools/README.tools b/usr/src/tools/README.tools index b910e73079..8612af43f7 100644 --- a/usr/src/tools/README.tools +++ b/usr/src/tools/README.tools @@ -49,15 +49,9 @@ Layout of /opt/onbld /opt/onbld/lib/python<version>/ python modules used by the build tools. -/opt/onbld/lib/python<version>/onbld/hgext - Mercurial extensions. - /opt/onbld/lib/python/ symlink to the modules directory of the currently preferred - python version. This exists to retain compatibility both for - tools expecting only one supported version of python, and for - user .hgrc files that expect to find cdm.py in - /opt/onbld/lib/python/onbld/hgext. + python version. /opt/onbld/man rudimentary man pages for some of the tools. @@ -79,10 +73,6 @@ build_cscope builds cscope databases in the uts, the platform subdirectories of uts, and in usr/src. Uses cscope-fast. -cdm - A Mercurial extension providing various commands useful for ON - development - check_rtime checks ELF attributes used by ELF dynamic objects in the proto area. Used by 'nightly's -r option, to check a number of ELF runtime @@ -162,13 +152,6 @@ hdrchk checks headers for compliance with OS/Net standards (form, includes, C++ guards). -hgsetup - creates a basic Mercurial configuration for the user. - -hg-active - helper used by webrev to generate file lists for Mercurial - workspaces. - install.bin binary version of /usr/sbin/install. Used to be vastly faster (since /usr/sbin/install is a shell script), but may only be a bit diff --git a/usr/src/tools/onbld/Makefile b/usr/src/tools/onbld/Makefile index 94ca2e97dd..8e5bb7d98f 100644 --- a/usr/src/tools/onbld/Makefile +++ b/usr/src/tools/onbld/Makefile @@ -28,7 +28,6 @@ include ../Makefile.tools SUBDIRS= \ Checks \ - hgext \ Scm PYSRCS = \ diff --git a/usr/src/tools/onbld/Scm/Backup.py b/usr/src/tools/onbld/Scm/Backup.py deleted file mode 100644 index 6715243d95..0000000000 --- a/usr/src/tools/onbld/Scm/Backup.py +++ /dev/null @@ -1,980 +0,0 @@ -# -# This program is free software; you can redistribute it and/or modify -# 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 2009 Sun Microsystems, Inc. All rights reserved. -# Use is subject to license terms. -# -# Copyright 2008, 2011, Richard Lowe -# - - -''' -Workspace backup - -Backup format is: - backupdir/ - wsname/ - generation#/ - dirstate (handled by CdmUncommittedBackup) - File containing dirstate nodeid (the changeset we need - to update the workspace to after applying the bundle). - This is the node to which the working copy changes - (see 'diff', below) will be applied if applicable. - - bundle (handled by CdmCommittedBackup) - An Hg bundle containing outgoing committed changes. - - nodes (handled by CdmCommittedBackup) - A text file listing the full (hex) nodeid of all nodes in - bundle, used by need_backup. - - diff (handled by CdmUncommittedBackup) - A Git-formatted diff containing uncommitted changes. - - renames (handled by CdmUncommittedBackup) - A list of renames in the working copy that have to be - applied manually, rather than by the diff. - - metadata.tar.gz (handled by CdmMetadataBackup) - $CODEMGR_WS/.hg/hgrc - $CODEMGR_WS/.hg/localtags - $CODEMGR_WS/.hg/patches (Mq data) - - clear.tar.gz (handled by CdmClearBackup) - <short node>/ - copies of each modified or added file, as it is in - this head. - - ... for each outgoing head - - working/ - copies of each modified or added file in the - working copy if any. - - latest -> generation# - Newest backup generation. - -All files in a given backup generation, with the exception of -dirstate, are optional. -''' - -import grp, os, pwd, shutil, tarfile, time, traceback -from cStringIO import StringIO - -from mercurial import changegroup, cmdutil, error, node, patch, util -from onbld.Scm import Version - - -class CdmNodeMissing(util.Abort): - '''a required node is not present in the destination workspace. - - This may occur both in the case where the bundle contains a - changeset which is a child of a node not present in the - destination workspace (because the destination workspace is not as - up-to-date as the source), or because the source and destination - workspace are not related. - - It may also happen in cases where the uncommitted changes need to - be applied onto a node that the workspace does not possess even - after application of the bundle (on a branch not present - in the bundle or destination workspace, for instance)''' - - def __init__(self, msg, name): - # - # If e.name is a string 20 characters long, it is - # assumed to be a node. (Mercurial makes this - # same assumption, when creating a LookupError) - # - if isinstance(name, str) and len(name) == 20: - n = node.short(name) - else: - n = name - - util.Abort.__init__(self, "%s: changeset '%s' is missing\n" - "Your workspace is either not " - "sufficiently up to date,\n" - "or is unrelated to the workspace from " - "which the backup was taken.\n" % (msg, n)) - - -class CdmTarFile(tarfile.TarFile): - '''Tar file access + simple comparison to the filesystem, and - creation addition of files from Mercurial filectx objects.''' - - def __init__(self, *args, **kwargs): - tarfile.TarFile.__init__(self, *args, **kwargs) - self.errorlevel = 2 - - def members_match_fs(self, rootpath): - '''Compare the contents of the tar archive to the directory - specified by rootpath. Return False if they differ. - - Every file in the archive must match the equivalent file in - the filesystem. - - The existence, modification time, and size of each file are - compared, content is not.''' - - def _member_matches_fs(member, rootpath): - '''Compare a single member to its filesystem counterpart''' - fpath = os.path.join(rootpath, member.name) - - if not os.path.exists(fpath): - return False - elif ((os.path.isfile(fpath) != member.isfile()) or - (os.path.isdir(fpath) != member.isdir()) or - (os.path.islink(fpath) != member.issym())): - return False - - # - # The filesystem may return a modification time with a - # fractional component (as a float), whereas the tar format - # only stores it to the whole second, perform the comparison - # using integers (truncated, not rounded) - # - elif member.mtime != int(os.path.getmtime(fpath)): - return False - elif not member.isdir() and member.size != os.path.getsize(fpath): - return False - else: - return True - - for elt in self: - if not _member_matches_fs(elt, rootpath): - return False - - return True - - def addfilectx(self, filectx, path=None): - '''Add a filectx object to the archive. - - Use the path specified by the filectx object or, if specified, - the PATH argument. - - The size, modification time, type and permissions of the tar - member are taken from the filectx object, user and group id - are those of the invoking user, user and group name are those - of the invoking user if information is available, or "unknown" - if it is not. - ''' - - t = tarfile.TarInfo(path or filectx.path()) - t.size = filectx.size() - t.mtime = filectx.date()[0] - t.uid = os.getuid() - t.gid = os.getgid() - - try: - t.uname = pwd.getpwuid(t.uid).pw_name - except KeyError: - t.uname = "unknown" - - try: - t.gname = grp.getgrgid(t.gid).gr_name - except KeyError: - t.gname = "unknown" - - # - # Mercurial versions symlinks by setting a flag and storing - # the destination path in place of the file content. The - # actual contents (in the tar), should be empty. - # - if 'l' in filectx.flags(): - t.type = tarfile.SYMTYPE - t.mode = 0777 - t.linkname = filectx.data() - data = None - else: - t.type = tarfile.REGTYPE - t.mode = 'x' in filectx.flags() and 0755 or 0644 - data = StringIO(filectx.data()) - - self.addfile(t, data) - - -class CdmCommittedBackup(object): - '''Backup of committed changes''' - - def __init__(self, backup, ws): - self.ws = ws - self.bu = backup - self.files = ('bundle', 'nodes') - - def _outgoing_nodes(self, parent): - '''Return a list of all outgoing nodes in hex format''' - - if parent: - outgoing = self.ws.findoutgoing(parent) - nodes = self.ws.repo.changelog.nodesbetween(outgoing)[0] - return map(node.hex, nodes) - else: - return [] - - def backup(self): - '''Backup committed changes''' - parent = self.ws.parent() - - if not parent: - self.ws.ui.warn('Workspace has no parent, committed changes will ' - 'not be backed up\n') - return - - out = self.ws.findoutgoing(parent) - if not out: - return - - cg = self.ws.repo.changegroup(out, 'bundle') - changegroup.writebundle(cg, self.bu.backupfile('bundle'), 'HG10BZ') - - outnodes = self._outgoing_nodes(parent) - if not outnodes: - return - - fp = None - try: - try: - fp = self.bu.open('nodes', 'w') - fp.write('%s\n' % '\n'.join(outnodes)) - except EnvironmentError, e: - raise util.Abort("couldn't store outgoing nodes: %s" % e) - finally: - if fp and not fp.closed: - fp.close() - - def restore(self): - '''Restore committed changes from backup''' - - if not self.bu.exists('bundle'): - return - - bpath = self.bu.backupfile('bundle') - f = None - try: - try: - f = self.bu.open('bundle') - bundle = changegroup.readbundle(f, bpath) - self.ws.repo.addchangegroup(bundle, 'strip', - 'bundle:%s' % bpath) - except EnvironmentError, e: - raise util.Abort("couldn't restore committed changes: %s\n" - " %s" % (bpath, e)) - except error.LookupError, e: - raise CdmNodeMissing("couldn't restore committed changes", - e.name) - finally: - if f and not f.closed: - f.close() - - def need_backup(self): - '''Compare backup of committed changes to workspace''' - - if self.bu.exists('nodes'): - f = None - try: - try: - f = self.bu.open('nodes') - bnodes = set(line.rstrip('\r\n') for line in f.readlines()) - f.close() - except EnvironmentError, e: - raise util.Abort("couldn't open backup node list: %s" % e) - finally: - if f and not f.closed: - f.close() - else: - bnodes = set() - - outnodes = set(self._outgoing_nodes(self.ws.parent())) - - # - # If there are outgoing nodes not in the prior backup we need - # to take a new backup; it's fine if there are nodes in the - # old backup which are no longer outgoing, however. - # - if not outnodes <= bnodes: - return True - - return False - - def cleanup(self): - '''Remove backed up committed changes''' - - for f in self.files: - self.bu.unlink(f) - - -class CdmUncommittedBackup(object): - '''Backup of uncommitted changes''' - - def __init__(self, backup, ws): - self.ws = ws - self.bu = backup - self.wctx = self.ws.workingctx(worklist=True) - - def _clobbering_renames(self): - '''Return a list of pairs of files representing renames/copies - that clobber already versioned files. [(old-name new-name)...] - ''' - - # - # Note that this doesn't handle uncommitted merges - # as CdmUncommittedBackup itself doesn't. - # - parent = self.wctx.parents()[0] - - ret = [] - for fname in self.wctx.added() + self.wctx.modified(): - rn = self.wctx.filectx(fname).renamed() - if rn and fname in parent: - ret.append((rn[0], fname)) - return ret - - def backup(self): - '''Backup uncommitted changes''' - - if self.ws.merged(): - raise util.Abort("Unable to backup an uncommitted merge.\n" - "Please complete your merge and commit") - - dirstate = node.hex(self.wctx.parents()[0].node()) - - fp = None - try: - try: - fp = self.bu.open('dirstate', 'w') - fp.write(dirstate + '\n') - fp.close() - except EnvironmentError, e: - raise util.Abort("couldn't save working copy parent: %s" % e) - - try: - fp = self.bu.open('renames', 'w') - for cons in self._clobbering_renames(): - fp.write("%s %s\n" % cons) - fp.close() - except EnvironmentError, e: - raise util.Abort("couldn't save clobbering copies: %s" % e) - - try: - fp = self.bu.open('diff', 'w') - match = self.ws.matcher(files=self.wctx.files()) - fp.write(self.ws.diff(opts={'git': True}, match=match)) - except EnvironmentError, e: - raise util.Abort("couldn't save working copy diff: %s" % e) - finally: - if fp and not fp.closed: - fp.close() - - def _dirstate(self): - '''Return the desired working copy node from the backup''' - fp = None - try: - try: - fp = self.bu.open('dirstate') - dirstate = fp.readline().strip() - except EnvironmentError, e: - raise util.Abort("couldn't read saved parent: %s" % e) - finally: - if fp and not fp.closed: - fp.close() - - return dirstate - - def restore(self): - '''Restore uncommitted changes''' - dirstate = self._dirstate() - - # - # Check that the patch's parent changeset exists. - # - try: - n = node.bin(dirstate) - self.ws.repo.changelog.lookup(n) - except error.LookupError, e: - raise CdmNodeMissing("couldn't restore uncommitted changes", - e.name) - - try: - self.ws.clean(rev=dirstate) - except util.Abort, e: - raise util.Abort("couldn't update to saved node: %s" % e) - - if not self.bu.exists('diff'): - return - - # - # There's a race here whereby if the patch (or part thereof) - # is applied within the same second as the clean above (such - # that modification time doesn't change) and if the size of - # that file does not change, Hg may not see the change. - # - # We sleep a full second to avoid this, as sleeping merely - # until the next second begins would require very close clock - # synchronization on network filesystems. - # - time.sleep(1) - - files = {} - try: - diff = self.bu.backupfile('diff') - try: - fuzz = patch.patch(diff, self.ws.ui, strip=1, - cwd=self.ws.repo.root, files=files) - if fuzz: - raise util.Abort('working copy diff applied with fuzz') - except Exception, e: - raise util.Abort("couldn't apply working copy diff: %s\n" - " %s" % (diff, e)) - finally: - if Version.at_least("1.7"): - cmdutil.updatedir(self.ws.ui, self.ws.repo, files) - else: - patch.updatedir(self.ws.ui, self.ws.repo, files) - - if not self.bu.exists('renames'): - return - - # - # We need to re-apply name changes where the new name - # (rename/copy destination) is an already versioned file, as - # Hg would otherwise ignore them. - # - try: - fp = self.bu.open('renames') - for line in fp: - source, dest = line.strip().split() - self.ws.copy(source, dest) - except EnvironmentError, e: - raise util.Abort('unable to open renames file: %s' % e) - except ValueError: - raise util.Abort('corrupt renames file: %s' % - self.bu.backupfile('renames')) - - def need_backup(self): - '''Compare backup of uncommitted changes to workspace''' - cnode = self.wctx.parents()[0].node() - if self._dirstate() != node.hex(cnode): - return True - - fd = None - match = self.ws.matcher(files=self.wctx.files()) - curdiff = self.ws.diff(opts={'git': True}, match=match) - - try: - if self.bu.exists('diff'): - try: - fd = self.bu.open('diff') - backdiff = fd.read() - fd.close() - except EnvironmentError, e: - raise util.Abort("couldn't open backup diff %s\n" - " %s" % (self.bu.backupfile('diff'), e)) - else: - backdiff = '' - - if backdiff != curdiff: - return True - - currrenamed = self._clobbering_renames() - bakrenamed = None - - if self.bu.exists('renames'): - try: - fd = self.bu.open('renames') - bakrenamed = [tuple(line.strip().split(' ')) for line in fd] - fd.close() - except EnvironmentError, e: - raise util.Abort("couldn't open renames file %s: %s\n" % - (self.bu.backupfile('renames'), e)) - - if currrenamed != bakrenamed: - return True - finally: - if fd and not fd.closed: - fd.close() - - return False - - def cleanup(self): - '''Remove backed up uncommitted changes''' - - for f in ('dirstate', 'diff', 'renames'): - self.bu.unlink(f) - - -class CdmMetadataBackup(object): - '''Backup of workspace metadata''' - - def __init__(self, backup, ws): - self.bu = backup - self.ws = ws - self.files = ('hgrc', 'localtags', 'patches', 'cdm') - - def backup(self): - '''Backup workspace metadata''' - - tarpath = self.bu.backupfile('metadata.tar.gz') - - # - # Files is a list of tuples (name, path), where name is as in - # self.files, and path is the absolute path. - # - files = filter(lambda (name, path): os.path.exists(path), - zip(self.files, map(self.ws.repo.join, self.files))) - - if not files: - return - - try: - tar = CdmTarFile.gzopen(tarpath, 'w') - except (EnvironmentError, tarfile.TarError), e: - raise util.Abort("couldn't open %s for writing: %s" % - (tarpath, e)) - - try: - for name, path in files: - try: - tar.add(path, name) - except (EnvironmentError, tarfile.TarError), e: - # - # tarfile.TarError doesn't include the tar member or file - # in question, so we have to do so ourselves. - # - if isinstance(e, tarfile.TarError): - errstr = "%s: %s" % (name, e) - else: - errstr = str(e) - - raise util.Abort("couldn't backup metadata to %s:\n" - " %s" % (tarpath, errstr)) - finally: - tar.close() - - def old_restore(self): - '''Restore workspace metadata from an pre-tar backup''' - - for fname in self.files: - if self.bu.exists(fname): - bfile = self.bu.backupfile(fname) - wfile = self.ws.repo.join(fname) - - try: - shutil.copy2(bfile, wfile) - except EnvironmentError, e: - raise util.Abort("couldn't restore metadata from %s:\n" - " %s" % (bfile, e)) - - def tar_restore(self): - '''Restore workspace metadata (from a tar-style backup)''' - - if not self.bu.exists('metadata.tar.gz'): - return - - tarpath = self.bu.backupfile('metadata.tar.gz') - - try: - tar = CdmTarFile.gzopen(tarpath) - except (EnvironmentError, tarfile.TarError), e: - raise util.Abort("couldn't open %s: %s" % (tarpath, e)) - - try: - for elt in tar: - try: - tar.extract(elt, path=self.ws.repo.path) - except (EnvironmentError, tarfile.TarError), e: - # Make sure the member name is in the exception message. - if isinstance(e, tarfile.TarError): - errstr = "%s: %s" % (elt.name, e) - else: - errstr = str(e) - - raise util.Abort("couldn't restore metadata from %s:\n" - " %s" % - (tarpath, errstr)) - finally: - if tar and not tar.closed: - tar.close() - - def restore(self): - '''Restore workspace metadata''' - - if self.bu.exists('hgrc'): - self.old_restore() - else: - self.tar_restore() - - def _walk(self): - '''Yield the repo-relative path to each file we operate on, - including each file within any affected directory''' - - for elt in self.files: - path = self.ws.repo.join(elt) - - if not os.path.exists(path): - continue - - if os.path.isdir(path): - for root, dirs, files in os.walk(path, topdown=True): - yield root - - for f in files: - yield os.path.join(root, f) - else: - yield path - - def need_backup(self): - '''Compare backed up workspace metadata to workspace''' - - def strip_trailing_pathsep(pathname): - '''Remove a possible trailing path separator from PATHNAME''' - return pathname.endswith('/') and pathname[:-1] or pathname - - if self.bu.exists('metadata.tar.gz'): - tarpath = self.bu.backupfile('metadata.tar.gz') - try: - tar = CdmTarFile.gzopen(tarpath) - except (EnvironmentError, tarfile.TarError), e: - raise util.Abort("couldn't open metadata tarball: %s\n" - " %s" % (tarpath, e)) - - if not tar.members_match_fs(self.ws.repo.path): - tar.close() - return True - - tarnames = map(strip_trailing_pathsep, tar.getnames()) - tar.close() - else: - tarnames = [] - - repopath = self.ws.repo.path - if not repopath.endswith('/'): - repopath += '/' - - for path in self._walk(): - if path.replace(repopath, '', 1) not in tarnames: - return True - - return False - - def cleanup(self): - '''Remove backed up workspace metadata''' - self.bu.unlink('metadata.tar.gz') - - -class CdmClearBackup(object): - '''A backup (in tar format) of complete source files from every - workspace head. - - Paths in the tarball are prefixed by the revision and node of the - head, or "working" for the working directory. - - This is done purely for the benefit of the user, and as such takes - no part in restore or need_backup checking, restore always - succeeds, need_backup always returns False - ''' - - def __init__(self, backup, ws): - self.bu = backup - self.ws = ws - - def _branch_pairs(self): - '''Return a list of tuples (parenttip, localtip) for each - outgoing head. If the working copy contains modified files, - it is a head, and neither of its parents are. - ''' - - parent = self.ws.parent() - - if parent: - outgoing = self.ws.findoutgoing(parent) - outnodes = set(self.ws.repo.changelog.nodesbetween(outgoing)[0]) - - heads = [self.ws.repo.changectx(n) for n in self.ws.repo.heads() - if n in outnodes] - else: - heads = [] - outnodes = [] - - wctx = self.ws.workingctx() - if wctx.files(): # We only care about file changes. - heads = filter(lambda x: x not in wctx.parents(), heads) + [wctx] - - pairs = [] - for head in heads: - if head.rev() is None: - c = head.parents() - else: - c = [head] - - pairs.append((self.ws.parenttip(c, outnodes), head)) - return pairs - - def backup(self): - '''Save a clear copy of each source file modified between each - head and that head's parenttip (see WorkSpace.parenttip). - ''' - - tarpath = self.bu.backupfile('clear.tar.gz') - branches = self._branch_pairs() - - if not branches: - return - - try: - tar = CdmTarFile.gzopen(tarpath, 'w') - except (EnvironmentError, tarfile.TarError), e: - raise util.Abort("Could not open %s for writing: %s" % - (tarpath, e)) - - try: - for parent, child in branches: - tpath = child.node() and node.short(child.node()) or "working" - - for fname, change in self.ws.status(parent, child).iteritems(): - if change not in ('added', 'modified'): - continue - - try: - tar.addfilectx(child.filectx(fname), - os.path.join(tpath, fname)) - except ValueError, e: - crev = child.rev() - if crev is None: - crev = "working copy" - raise util.Abort("Could not backup clear file %s " - "from %s: %s\n" % (fname, crev, e)) - finally: - tar.close() - - def cleanup(self): - '''Cleanup a failed Clear backup. - - Remove the clear tarball from the backup directory. - ''' - - self.bu.unlink('clear.tar.gz') - - def restore(self): - '''Clear backups are never restored, do nothing''' - pass - - def need_backup(self): - '''Clear backups are never compared, return False (no backup needed). - - Should a backup actually be needed, one of the other - implementation classes would notice in any situation we would. - ''' - - return False - - -class CdmBackup(object): - '''A backup of a given workspace''' - - def __init__(self, ui, ws, name): - self.ws = ws - self.ui = ui - self.backupdir = self._find_backup_dir(name) - - # - # The order of instances here controls the order the various operations - # are run. - # - # There's some inherent dependence, in that on restore we need - # to restore committed changes prior to uncommitted changes - # (as the parent revision of any uncommitted changes is quite - # likely to not exist until committed changes are restored). - # Metadata restore can happen at any point, but happens last - # as a matter of convention. - # - self.modules = [x(self, ws) for x in [CdmCommittedBackup, - CdmUncommittedBackup, - CdmClearBackup, - CdmMetadataBackup]] - - if os.path.exists(os.path.join(self.backupdir, 'latest')): - generation = os.readlink(os.path.join(self.backupdir, 'latest')) - self.generation = int(os.path.split(generation)[1]) - else: - self.generation = 0 - - def _find_backup_dir(self, name): - '''Find the path to an appropriate backup directory based on NAME''' - - if os.path.isabs(name): - return name - - if self.ui.config('cdm', 'backupdir'): - backupbase = os.path.expanduser(self.ui.config('cdm', 'backupdir')) - else: - home = None - - try: - home = os.getenv('HOME') or pwd.getpwuid(os.getuid()).pw_dir - except KeyError: - pass # Handled anyway - - if not home: - raise util.Abort('Could not determine your HOME directory to ' - 'find backup path') - - backupbase = os.path.join(home, 'cdm.backup') - - backupdir = os.path.join(backupbase, name) - - # If backupdir exists, it must be a directory. - if (os.path.exists(backupdir) and not os.path.isdir(backupdir)): - raise util.Abort('%s exists but is not a directory' % backupdir) - - return backupdir - - def _update_latest(self, gen): - '''Update latest symlink to point to the current generation''' - linkpath = os.path.join(self.backupdir, 'latest') - - if os.path.lexists(linkpath): - os.unlink(linkpath) - - os.symlink(str(gen), linkpath) - - def _create_gen(self, gen): - '''Create a new backup generation''' - try: - os.makedirs(os.path.join(self.backupdir, str(gen))) - self._update_latest(gen) - except EnvironmentError, e: - raise util.Abort("Couldn't create backup generation %s: %s" % - (os.path.join(self.backupdir, str(gen)), e)) - - def backupfile(self, path): - '''return full path to backup file FILE at GEN''' - return os.path.join(self.backupdir, str(self.generation), path) - - def unlink(self, name): - '''Unlink the specified path from the backup directory. - A no-op if the path does not exist. - ''' - - fpath = self.backupfile(name) - if os.path.exists(fpath): - os.unlink(fpath) - - def open(self, name, mode='r'): - '''Open the specified file in the backup directory''' - return open(self.backupfile(name), mode) - - def exists(self, name): - '''Return boolean indicating wether a given file exists in the - backup directory.''' - return os.path.exists(self.backupfile(name)) - - def need_backup(self): - '''Compare backed up changes to workspace''' - # - # If there's no current backup generation, or the last backup was - # invalid (lacking the dirstate file), we need a backup regardless - # of anything else. - # - if not self.generation or not self.exists('dirstate'): - return True - - for x in self.modules: - if x.need_backup(): - return True - - return False - - def backup(self): - '''Take a backup of the current workspace - - Calling code is expected to hold both the working copy lock - and repository lock.''' - - if not os.path.exists(self.backupdir): - try: - os.makedirs(self.backupdir) - except EnvironmentError, e: - raise util.Abort('Could not create backup directory %s: %s' % - (self.backupdir, e)) - - self.generation += 1 - self._create_gen(self.generation) - - try: - for x in self.modules: - x.backup() - except Exception, e: - if isinstance(e, KeyboardInterrupt): - self.ws.ui.warn("Interrupted\n") - else: - self.ws.ui.warn("Error: %s\n" % e) - show_traceback = self.ws.ui.configbool('ui', 'traceback', - False) - - # - # If it's not a 'normal' error, we want to print a stack - # trace now in case the attempt to remove the partial - # backup also fails, and raises a second exception. - # - if (not isinstance(e, (EnvironmentError, util.Abort)) - or show_traceback): - traceback.print_exc() - - for x in self.modules: - x.cleanup() - - os.rmdir(os.path.join(self.backupdir, str(self.generation))) - self.generation -= 1 - - if self.generation != 0: - self._update_latest(self.generation) - else: - os.unlink(os.path.join(self.backupdir, 'latest')) - - raise util.Abort('Backup failed') - - def restore(self, gen=None): - '''Restore workspace from backup - - Restores from backup generation GEN (defaulting to the latest) - into workspace WS. - - Calling code is expected to hold both the working copy lock - and repository lock of the destination workspace.''' - - if not os.path.exists(self.backupdir): - raise util.Abort('Backup directory does not exist: %s' % - (self.backupdir)) - - if gen: - if not os.path.exists(os.path.join(self.backupdir, str(gen))): - raise util.Abort('Backup generation does not exist: %s' % - (os.path.join(self.backupdir, str(gen)))) - self.generation = int(gen) - - if not self.generation: # This is OK, 0 is not a valid generation - raise util.Abort('Backup has no generations: %s' % self.backupdir) - - if not self.exists('dirstate'): - raise util.Abort('Backup %s/%s is incomplete (dirstate missing)' % - (self.backupdir, self.generation)) - - try: - for x in self.modules: - x.restore() - except util.Abort, e: - raise util.Abort('Error restoring workspace:\n' - '%s\n' - 'Workspace may be partially restored' % e) diff --git a/usr/src/tools/onbld/Scm/Makefile b/usr/src/tools/onbld/Scm/Makefile index a71883c297..c3fbca605f 100644 --- a/usr/src/tools/onbld/Scm/Makefile +++ b/usr/src/tools/onbld/Scm/Makefile @@ -29,10 +29,7 @@ include ../../Makefile.tools PYSRCS = \ __init__.py \ - Backup.py \ - Ignore.py \ - Version.py \ - WorkSpace.py + Ignore.py PYOBJS = $(PYSRCS:%.py=%.pyc) PYTOPDIR = $(ROOTONBLDLIB) diff --git a/usr/src/tools/onbld/Scm/Version.py b/usr/src/tools/onbld/Scm/Version.py deleted file mode 100644 index 017ef3e514..0000000000 --- a/usr/src/tools/onbld/Scm/Version.py +++ /dev/null @@ -1,122 +0,0 @@ -# -# This program is free software; you can redistribute it and/or modify -# 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 2009 Sun Microsystems, Inc. All rights reserved. -# Use is subject to license terms. -# -# Copyright 2008, 2011, Richard Lowe -# - -''' -Deal with Mercurial versioning. - -At a basic level, code to verify that the version of Mercurial in use -is suitable for use with Cadmium, and compare that version for the -sake of adapting to Mercurial API changes. -''' - -# -# It is important that this module rely on as little of Mercurial as -# is possible. -# - -# -# Mercurial >= 1.2 has util.version(), prior versions -# version.get_version() We discover which to use this way, rather than -# via ImportError to account for mercurial.demandimport delaying the -# ImportError exception. -# -# This code needs to remain, even though versions prior to 1.2 aren't -# supported, to allow us to produce the error message which states that they -# are not supported. -# -from mercurial import util -if hasattr(util, 'version'): - hg_version = util.version -else: - from mercurial import version - hg_version = version.get_version - - -class VersionMismatch(Exception): - "Exception used to indicate a mismatch between SCM tools and Mercurial" - pass - -# -# List of versions that are explicitly acceptable to us -# -GOOD_VERSIONS = ['1.3.1', '1.4.2', '1.5.4', '1.6.2', '1.6.3', '1.7.5', - '1.8', '1.8.1'] - - -def check_version(): - '''Check that we're running on a suitable version of Mercurial''' - - def versionstring(versions): - '''return the list, versions, as a vaguely grammatical string''' - if len(versions) > 1: - return "%s or %s" % (', '.join(versions[0:-1]), versions[-1]) - else: - return versions[0] - - if hg_version() not in GOOD_VERSIONS: - raise VersionMismatch("Scm expects Mercurial version %s, " - "actual version is %s." % - (versionstring(GOOD_VERSIONS), - hg_version())) - - -def _split_version(ver): - '''Return the Mercurial version as a list [MAJOR, MINOR, MICRO], - if this is not a released Mercurial return None.''' - - try: - l = map(int, ver.split('.')) - # If there's only one element, it's not really a tagged version - if len(l) <= 1: - return None - else: - return l - except ValueError: - return None - - -def at_least(desired): - '''Return boolean indicating if the running version is greater - than or equal to, the version specified by major, minor, micro''' - - hgver = _split_version(hg_version()) - desired = map(int, desired.split('.')) - - # - # If _split_version() returns None, we're running on a Mercurial that - # has not been tagged as a release. We assume this to be newer - # than any released version. - # - if hgver == None: - return True - - # Pad our versions to the same overall length, appending 0's - while len(hgver) < len(desired): - hgver.append(0) - while len(desired) < len(hgver): - desired.append(0) - - for real, req in zip(hgver, desired): - if real != req: - return real > req - - return True diff --git a/usr/src/tools/onbld/Scm/WorkSpace.py b/usr/src/tools/onbld/Scm/WorkSpace.py deleted file mode 100644 index 3abd4d8149..0000000000 --- a/usr/src/tools/onbld/Scm/WorkSpace.py +++ /dev/null @@ -1,1076 +0,0 @@ -# -# This program is free software; you can redistribute it and/or modify -# 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 (c) 2008, 2010, Oracle and/or its affiliates. All rights reserved. -# Copyright 2008, 2011, Richard Lowe -# - -# -# Theory: -# -# Workspaces have a non-binding parent/child relationship. -# All important operations apply to the changes between the two. -# -# However, for the sake of remote operation, the 'parent' of a -# workspace is not seen as a literal entity, instead the figurative -# parent contains the last changeset common to both parent and child, -# as such the 'parent tip' is actually nothing of the sort, but instead a -# convenient imitation. -# -# Any change made to a workspace is a change to a file therein, such -# changes can be represented briefly as whether the file was -# modified/added/removed as compared to the parent workspace, whether -# the file has a different name in the parent and if so, whether it -# was renamed or merely copied. Each changed file has an -# associated ActiveEntry. -# -# The ActiveList, being a list of ActiveEntry objects, can thus -# present the entire change in workspace state between a parent and -# its child and is the important bit here (in that if it is incorrect, -# everything else will be as incorrect, or more) -# - -import cStringIO -import os -from mercurial import cmdutil, context, error, hg, node, patch, repair, util -from hgext import mq - -from onbld.Scm import Version - - -# -# Mercurial 1.6 moves findoutgoing into a discover module -# -if Version.at_least("1.6"): - from mercurial import discovery - - -class ActiveEntry(object): - '''Representation of the changes made to a single file. - - MODIFIED - Contents changed, but no other changes were made - ADDED - File is newly created - REMOVED - File is being removed - - Copies are represented by an Entry whose .parentname is non-nil - - Truly copied files have non-nil .parentname and .renamed = False - Renames have non-nil .parentname and .renamed = True - - Do not access any of this information directly, do so via the - - .is_<change>() methods.''' - - MODIFIED = intern('modified') - ADDED = intern('added') - REMOVED = intern('removed') - - def __init__(self, name, change): - self.name = name - self.change = intern(change) - - assert change in (self.MODIFIED, self.ADDED, self.REMOVED) - - self.parentname = None - # As opposed to copied (or neither) - self.renamed = False - self.comments = [] - - def __cmp__(self, other): - return cmp(self.name, other.name) - - def is_added(self): - '''Return True if this ActiveEntry represents an added file''' - return self.change is self.ADDED - - def is_modified(self): - '''Return True if this ActiveEntry represents a modified file''' - return self.change is self.MODIFIED - - def is_removed(self): - '''Return True if this ActiveEntry represents a removed file''' - return self.change is self.REMOVED - - def is_renamed(self): - '''Return True if this ActiveEntry represents a renamed file''' - return self.parentname and self.renamed - - def is_copied(self): - '''Return True if this ActiveEntry represents a copied file''' - return self.parentname and not self.renamed - - -class ActiveList(object): - '''Complete representation of change between two changesets. - - In practice, a container for ActiveEntry objects, and methods to - create them, and deal with them as a group.''' - - def __init__(self, ws, parenttip, revs=None): - '''Initialize the ActiveList - - parenttip is the revision with which to compare (likely to be - from the parent), revs is a topologically sorted list of - revisions ending with the revision to compare with (likely to - be the child-local revisions).''' - - assert parenttip is not None - - self.ws = ws - self.revs = revs - self.parenttip = parenttip - self.localtip = None - - self._active = {} - self._comments = [] - - if revs: - self.localtip = revs[-1] - self._build() - - def _status(self): - '''Return the status of any file mentioned in any of the - changesets making up this active list.''' - - files = set() - for c in self.revs: - files.update(c.files()) - - # - # Any file not in the parenttip or the localtip is ephemeral - # and can be ignored. Mercurial will complain regarding these - # files if the localtip is a workingctx, so remove them in - # that case. - # - # Compare against the dirstate because a workingctx manifest - # is created on-demand and is particularly expensive. - # - if self.localtip.rev() is None: - for f in files.copy(): - if f not in self.parenttip and f not in self.ws.repo.dirstate: - files.remove(f) - - return self.ws.status(self.parenttip, self.localtip, files=files) - - def _build(self): - '''Construct ActiveEntry objects for each changed file. - - This works in 3 stages: - - - Create entries for every changed file with - semi-appropriate change type - - - Track renames/copies, and set change comments (both - ActiveList-wide, and per-file). - - - Cleanup - - Drop circular renames - - Drop the removal of the old name of any rename - - Drop entries for modified files that haven't actually changed''' - - # - # Keep a cache of filectx objects (keyed on pathname) so that - # we can avoid opening filelogs numerous times. - # - fctxcache = {} - - def oldname(ctx, fname): - '''Return the name 'fname' held prior to any possible - rename/copy in the given changeset.''' - try: - if fname in fctxcache: - octx = fctxcache[fname] - fctx = ctx.filectx(fname, filelog=octx.filelog()) - else: - fctx = ctx.filectx(fname) - # - # workingfilectx objects may not refer to the - # right filelog (in case of rename). Don't cache - # them. - # - if not isinstance(fctx, context.workingfilectx): - fctxcache[fname] = fctx - except error.LookupError: - return None - - rn = fctx.renamed() - return rn and rn[0] or fname - - status = self._status() - self._active = dict((fname, ActiveEntry(fname, kind)) - for fname, kind in status.iteritems() - if kind in ('modified', 'added', 'removed')) - - # - # We do two things: - # - Gather checkin comments (for the entire ActiveList, and - # per-file) - # - Set the .parentname of any copied/renamed file - # - # renames/copies: - # We walk the list of revisions backward such that only files - # that ultimately remain active need be considered. - # - # At each iteration (revision) we update the .parentname of - # any active file renamed or copied in that revision (the - # current .parentname if set, or .name otherwise, reflects - # the name of a given active file in the revision currently - # being looked at) - # - for ctx in reversed(self.revs): - desc = ctx.description().splitlines() - self._comments = desc + self._comments - cfiles = set(ctx.files()) - - for entry in self: - fname = entry.parentname or entry.name - if fname not in cfiles: - continue - - entry.comments = desc + entry.comments - - # - # We don't care about the name history of any file - # that ends up being removed, since that trumps any - # possible renames or copies along the way. - # - # Changes that we may care about involving an - # intermediate name of a removed file will appear - # separately (related to the eventual name along - # that line) - # - if not entry.is_removed(): - entry.parentname = oldname(ctx, fname) - - for entry in self._active.values(): - # - # For any file marked as copied or renamed, clear the - # .parentname if the copy or rename is cyclic (source == - # destination) or if the .parentname did not exist in the - # parenttip. - # - # If the parentname is marked as removed, set the renamed - # flag and remove any ActiveEntry we may have for the - # .parentname. - # - if entry.parentname: - if (entry.parentname == entry.name or - entry.parentname not in self.parenttip): - entry.parentname = None - elif status.get(entry.parentname) == 'removed': - entry.renamed = True - - if entry.parentname in self: - del self[entry.parentname] - - # - # There are cases during a merge where a file will be seen - # as modified by status but in reality be an addition (not - # in the parenttip), so we have to check whether the file - # is in the parenttip and set it as an addition, if not. - # - # If a file is modified (and not a copy or rename), we do - # a full comparison to the copy in the parenttip and - # ignore files that are parts of active revisions but - # unchanged. - # - if entry.name not in self.parenttip: - entry.change = ActiveEntry.ADDED - elif entry.is_modified(): - if not self._changed_file(entry.name): - del self[entry.name] - - def __contains__(self, fname): - return fname in self._active - - def __getitem__(self, key): - return self._active[key] - - def __setitem__(self, key, value): - self._active[key] = value - - def __delitem__(self, key): - del self._active[key] - - def __iter__(self): - return self._active.itervalues() - - def files(self): - '''Return the list of pathnames of all files touched by this - ActiveList - - Where files have been renamed, this will include both their - current name and the name which they had in the parent tip. - ''' - - ret = self._active.keys() - ret.extend(x.parentname for x in self if x.is_renamed()) - return set(ret) - - def comments(self): - '''Return the full set of changeset comments associated with - this ActiveList''' - - return self._comments - - def bases(self): - '''Return the list of changesets that are roots of the ActiveList. - - This is the set of active changesets where neither parent - changeset is itself active.''' - - revset = set(self.revs) - return filter(lambda ctx: not [p for p in ctx.parents() if p in revset], - self.revs) - - def tags(self): - '''Find tags that refer to a changeset in the ActiveList, - returning a list of 3-tuples (tag, node, is_local) for each. - - We return all instances of a tag that refer to such a node, - not just that which takes precedence.''' - - def colliding_tags(iterable, nodes, local): - for nd, name in [line.rstrip().split(' ', 1) for line in iterable]: - if nd in nodes: - yield (name, self.ws.repo.lookup(nd), local) - - tags = [] - nodes = set(node.hex(ctx.node()) for ctx in self.revs) - - if os.path.exists(self.ws.repo.join('localtags')): - fh = self.ws.repo.opener('localtags') - tags.extend(colliding_tags(fh, nodes, True)) - fh.close() - - # We want to use the tags file from the localtip - if '.hgtags' in self.localtip: - data = self.localtip.filectx('.hgtags').data().splitlines() - tags.extend(colliding_tags(data, nodes, False)) - - return tags - - def prune_tags(self, data): - '''Return a copy of data, which should correspond to the - contents of a Mercurial tags file, with any tags that refer to - changesets which are components of the ActiveList removed.''' - - nodes = set(node.hex(ctx.node()) for ctx in self.revs) - return [t for t in data if t.split(' ', 1)[0] not in nodes] - - def _changed_file(self, path): - '''Compare the parent and local versions of a given file. - Return True if file changed, False otherwise. - - Note that this compares the given path in both versions, not the given - entry; renamed and copied files are compared by name, not history. - - The fast path compares file metadata, slow path is a - real comparison of file content.''' - - if ((path in self.parenttip) != (path in self.localtip)): - return True - - parentfile = self.parenttip.filectx(path) - localfile = self.localtip.filectx(path) - - # - # NB: Keep these ordered such as to make every attempt - # to short-circuit the more time consuming checks. - # - if parentfile.size() != localfile.size(): - return True - - if parentfile.flags() != localfile.flags(): - return True - - if Version.at_least("1.7"): - if parentfile.cmp(localfile): - return True - else: - if parentfile.cmp(localfile.data()): - return True - - def context(self, message, user): - '''Return a Mercurial context object representing the entire - ActiveList as one change.''' - return activectx(self, message, user) - - def as_text(self, paths): - '''Return the ActiveList as a block of text in a format - intended to aid debugging and simplify the test suite. - - paths should be a list of paths for which file-level data - should be included. If it is empty, the whole active list is - included.''' - - cstr = cStringIO.StringIO() - - cstr.write('parent tip: %s:%s\n' % (self.parenttip.rev(), - self.parenttip)) - if self.localtip: - rev = self.localtip.rev() - cstr.write('local tip: %s:%s\n' % - (rev is None and "working" or rev, self.localtip)) - else: - cstr.write('local tip: None\n') - - cstr.write('entries:\n') - for entry in self: - if paths and self.ws.filepath(entry.name) not in paths: - continue - - cstr.write(' - %s\n' % entry.name) - cstr.write(' parentname: %s\n' % entry.parentname) - cstr.write(' change: %s\n' % entry.change) - cstr.write(' renamed: %s\n' % entry.renamed) - cstr.write(' comments:\n') - cstr.write(' ' + '\n '.join(entry.comments) + '\n') - cstr.write('\n') - - return cstr.getvalue() - - -class WorkList(object): - '''A (user-maintained) list of files changed in this workspace as - compared to any parent workspace. - - Internally, the WorkList is stored in .hg/cdm/worklist as a list - of file pathnames, one per-line. - - This may only safely be used as a hint regarding possible - modifications to the working copy, it should not be relied upon to - suggest anything about committed changes.''' - - def __init__(self, ws): - '''Load the WorkList for the specified WorkSpace from disk.''' - - self._ws = ws - self._repo = ws.repo - self._file = os.path.join('cdm', 'worklist') - self._files = set() - self._valid = False - - if os.path.exists(self._repo.join(self._file)): - self.load() - - def __nonzero__(self): - '''A WorkList object is true if it was loaded from disk, - rather than freshly created. - ''' - - return self._valid - - def list(self): - '''List of pathnames contained in the WorkList - ''' - - return list(self._files) - - def status(self): - '''Return the status (in tuple form) of files from the - WorkList as they are in the working copy - ''' - - match = self._ws.matcher(files=self.list()) - return self._repo.status(match=match) - - def add(self, fname): - '''Add FNAME to the WorkList. - ''' - - self._files.add(fname) - - def write(self): - '''Write the WorkList out to disk. - ''' - - dirn = os.path.split(self._file)[0] - - if dirn and not os.path.exists(self._repo.join(dirn)): - try: - os.makedirs(self._repo.join(dirn)) - except EnvironmentError, e: - raise util.Abort("Couldn't create directory %s: %s" % - (self._repo.join(dirn), e)) - - fh = self._repo.opener(self._file, 'w', atomictemp=True) - - for name in self._files: - fh.write("%s\n" % name) - - fh.rename() - fh.close() - - def load(self): - '''Read in the WorkList from disk. - ''' - - fh = self._repo.opener(self._file, 'r') - self._files = set(l.rstrip('\n') for l in fh) - self._valid = True - fh.close() - - def delete(self): - '''Empty the WorkList - - Remove the on-disk WorkList and clear the file-list of the - in-memory copy - ''' - - if os.path.exists(self._repo.join(self._file)): - os.unlink(self._repo.join(self._file)) - - self._files = set() - self._valid = False - - -class activectx(context.memctx): - '''Represent an ActiveList as a Mercurial context object. - - Part of the WorkSpace.squishdeltas implementation.''' - - def __init__(self, active, message, user): - '''Build an activectx object. - - active - The ActiveList object used as the source for all data. - message - Changeset description - user - Committing user''' - - def filectxfn(repository, ctx, fname): - fctx = active.localtip.filectx(fname) - data = fctx.data() - - # - # .hgtags is a special case, tags referring to active list - # component changesets should be elided. - # - if fname == '.hgtags': - data = '\n'.join(active.prune_tags(data.splitlines())) - - return context.memfilectx(fname, data, 'l' in fctx.flags(), - 'x' in fctx.flags(), - active[fname].parentname) - - self.__active = active - parents = (active.parenttip.node(), node.nullid) - extra = {'branch': active.localtip.branch()} - context.memctx.__init__(self, active.ws.repo, parents, message, - active.files(), filectxfn, user=user, - extra=extra) - - def modified(self): - return [entry.name for entry in self.__active if entry.is_modified()] - - def added(self): - return [entry.name for entry in self.__active if entry.is_added()] - - def removed(self): - ret = set(entry.name for entry in self.__active if entry.is_removed()) - ret.update(set(x.parentname for x in self.__active if x.is_renamed())) - return list(ret) - - def files(self): - return self.__active.files() - - -class WorkSpace(object): - - def __init__(self, repository): - self.repo = repository - self.ui = self.repo.ui - self.name = self.repo.root - - self.activecache = {} - - def parent(self, spec=None): - '''Return the canonical workspace parent, either SPEC (which - will be expanded) if provided or the default parent - otherwise.''' - - if spec: - return self.ui.expandpath(spec) - - p = self.ui.expandpath('default') - if p == 'default': - return None - else: - return p - - def _localtip(self, outgoing, wctx): - '''Return the most representative changeset to act as the - localtip. - - If the working directory is modified (has file changes, is a - merge, or has switched branches), this will be a workingctx. - - If the working directory is unmodified, this will be the most - recent (highest revision number) local (outgoing) head on the - current branch, if no heads are determined to be outgoing, it - will be the most recent head on the current branch. - ''' - - if (wctx.files() or len(wctx.parents()) > 1 or - wctx.branch() != wctx.parents()[0].branch()): - return wctx - - heads = self.repo.heads(start=wctx.parents()[0].node()) - headctxs = [self.repo.changectx(n) for n in heads] - localctxs = [c for c in headctxs if c.node() in outgoing] - - ltip = sorted(localctxs or headctxs, key=lambda x: x.rev())[-1] - - if len(heads) > 1: - self.ui.warn('The current branch has more than one head, ' - 'using %s\n' % ltip.rev()) - - return ltip - - def parenttip(self, heads, outgoing): - '''Return the highest-numbered, non-outgoing changeset that is - an ancestor of a changeset in heads. - - This returns the most recent changeset on a given branch that - is shared between a parent and child workspace, in effect the - common ancestor of the chosen local tip and the parent - workspace. - ''' - - def tipmost_shared(head, outnodes): - '''Return the changeset on the same branch as head that is - not in outnodes and is closest to the tip. - - Walk outgoing changesets from head to the bottom of the - workspace (revision 0) and return the the first changeset - we see that is not in outnodes. - - If none is found (all revisions >= 0 are outgoing), the - only possible parenttip is the null node (node.nullid) - which is returned explicitly. - ''' - for ctx in self._walkctxs(head, self.repo.changectx(0), - follow=True, - pick=lambda c: c.node() not in outnodes): - return ctx - - return self.repo.changectx(node.nullid) - - nodes = set(outgoing) - ptips = map(lambda x: tipmost_shared(x, nodes), heads) - return sorted(ptips, key=lambda x: x.rev(), reverse=True)[0] - - def status(self, base='.', head=None, files=None): - '''Translate from the hg 6-tuple status format to a hash keyed - on change-type''' - - states = ['modified', 'added', 'removed', 'deleted', 'unknown', - 'ignored'] - - match = self.matcher(files=files) - chngs = self.repo.status(base, head, match=match) - - ret = {} - for paths, change in zip(chngs, states): - ret.update((f, change) for f in paths) - return ret - - def findoutgoing(self, parent): - '''Return the base set of outgoing nodes. - - A caching wrapper around mercurial.localrepo.findoutgoing(). - Complains (to the user), if the parent workspace is - non-existent or inaccessible''' - - self.ui.pushbuffer() - try: - try: - ui = self.ui - if hasattr(cmdutil, 'remoteui'): - ui = cmdutil.remoteui(ui, {}) - pws = hg.repository(ui, parent) - if Version.at_least("1.6"): - return discovery.findoutgoing(self.repo, pws) - else: - return self.repo.findoutgoing(pws) - except error.RepoError: - self.ui.warn("Warning: Parent workspace '%s' is not " - "accessible\n" - "active list will be incomplete\n\n" % parent) - return [] - finally: - self.ui.popbuffer() - findoutgoing = util.cachefunc(findoutgoing) - - def modified(self): - '''Return a list of files modified in the workspace''' - - wctx = self.workingctx() - return sorted(wctx.files() + wctx.deleted()) or None - - def merged(self): - '''Return boolean indicating whether the workspace has an uncommitted - merge''' - - wctx = self.workingctx() - return len(wctx.parents()) > 1 - - def branched(self): - '''Return boolean indicating whether the workspace has an - uncommitted named branch''' - - wctx = self.workingctx() - return wctx.branch() != wctx.parents()[0].branch() - - def active(self, parent=None, thorough=False): - '''Return an ActiveList describing changes between workspace - and parent workspace (including uncommitted changes). - If the workspace has no parent, ActiveList will still describe any - uncommitted changes. - - If thorough is True use neither the WorkList nor any cached - results (though the result of this call will be cached for - future, non-thorough, calls).''' - - parent = self.parent(parent) - - # - # Use the cached copy if we can (we have one, and weren't - # asked to be thorough) - # - if not thorough and parent in self.activecache: - return self.activecache[parent] - - # - # outbases: The set of outgoing nodes with no outgoing ancestors - # outnodes: The full set of outgoing nodes - # - if parent: - outbases = self.findoutgoing(parent) - outnodes = self.repo.changelog.nodesbetween(outbases)[0] - else: # No parent, no outgoing nodes - outbases = [] - outnodes = [] - - wctx = self.workingctx(worklist=not thorough) - localtip = self._localtip(outnodes, wctx) - - if localtip.rev() is None: - heads = localtip.parents() - else: - heads = [localtip] - - parenttip = self.parenttip(heads, outnodes) - - # - # If we couldn't find a parenttip, the two repositories must - # be unrelated (Hg catches most of this, but this case is - # valid for it but invalid for us) - # - if parenttip == None: - raise util.Abort('repository is unrelated') - - headnodes = [h.node() for h in heads] - ctxs = [self.repo.changectx(n) for n in - self.repo.changelog.nodesbetween(outbases, headnodes)[0]] - - if localtip.rev() is None: - ctxs.append(localtip) - - act = ActiveList(self, parenttip, ctxs) - self.activecache[parent] = act - - return act - - def squishdeltas(self, active, message, user=None): - '''Create a single conglomerate changeset based on a given - active list. Removes the original changesets comprising the - given active list, and any tags pointing to them. - - Operation: - - - Commit an activectx object representing the specified - active list, - - - Remove any local tags pointing to changesets in the - specified active list. - - - Remove the changesets comprising the specified active - list. - - - Remove any metadata that may refer to changesets that were - removed. - - Calling code is expected to hold both the working copy lock - and repository lock of the destination workspace - ''' - - def strip_local_tags(active): - '''Remove any local tags referring to the specified nodes.''' - - if os.path.exists(self.repo.join('localtags')): - fh = None - try: - fh = self.repo.opener('localtags') - tags = active.prune_tags(fh) - fh.close() - - fh = self.repo.opener('localtags', 'w', atomictemp=True) - fh.writelines(tags) - fh.rename() - finally: - if fh and not fh.closed: - fh.close() - - if active.files(): - for entry in active: - # - # Work around Mercurial issue #1666, if the source - # file of a rename exists in the working copy - # Mercurial will complain, and remove the file. - # - # We preemptively remove the file to avoid the - # complaint (the user was asked about this in - # cdm_recommit) - # - if entry.is_renamed(): - path = self.repo.wjoin(entry.parentname) - if os.path.exists(path): - os.unlink(path) - - self.repo.commitctx(active.context(message, user)) - wsstate = "recommitted" - destination = self.repo.changelog.tip() - else: - # - # If all we're doing is stripping the old nodes, we want to - # update the working copy such that we're not at a revision - # that's about to go away. - # - wsstate = "tip" - destination = active.parenttip.node() - - self.clean(destination) - - # - # Tags were elided by the activectx object. Local tags, - # however, must be removed manually. - # - try: - strip_local_tags(active) - except EnvironmentError, e: - raise util.Abort('Could not recommit tags: %s\n' % e) - - # Silence all the strip and update fun - self.ui.pushbuffer() - - # - # Remove the previous child-local changes by stripping the - # nodes that form the base of the ActiveList (removing their - # children in the process). - # - try: - try: - for base in active.bases(): - # - # Any cached information about the repository is - # likely to be invalid during the strip. The - # caching of branch tags is especially - # problematic. - # - self.repo.invalidate() - repair.strip(self.ui, self.repo, base.node(), backup=False) - except: - # - # If this fails, it may leave us in a surprising place in - # the history. - # - # We want to warn the user that something went wrong, - # and what will happen next, re-raise the exception, and - # bring the working copy back into a consistent state - # (which the finally block will do) - # - self.ui.warn("stripping failed, your workspace will have " - "superfluous heads.\n" - "your workspace has been updated to the " - "%s changeset.\n" % wsstate) - raise # Re-raise the exception - finally: - self.clean() - self.repo.dirstate.write() # Flush the dirstate - self.repo.invalidate() # Invalidate caches - - # - # We need to remove Hg's undo information (used for rollback), - # since it refers to data that will probably not exist after - # the strip. - # - if os.path.exists(self.repo.sjoin('undo')): - try: - os.unlink(self.repo.sjoin('undo')) - except EnvironmentError, e: - raise util.Abort('failed to remove undo data: %s\n' % e) - - self.ui.popbuffer() - - def filepath(self, path): - 'Return the full path to a workspace file.' - - return self.repo.pathto(path) - - def clean(self, rev=None): - '''Bring workspace up to REV (or tip) forcefully (discarding in - progress changes)''' - - if rev != None: - rev = self.repo.lookup(rev) - else: - rev = self.repo.changelog.tip() - - hg.clean(self.repo, rev, show_stats=False) - - def mq_applied(self): - '''True if the workspace has Mq patches applied''' - - q = mq.queue(self.ui, self.repo.join('')) - return q.applied - - def workingctx(self, worklist=False): - '''Return a workingctx object representing the working copy. - - If worklist is true, return a workingctx object created based - on the status of files in the workspace's worklist.''' - - wl = WorkList(self) - - if worklist and wl: - return context.workingctx(self.repo, changes=wl.status()) - else: - return self.repo.changectx(None) - - def matcher(self, pats=None, opts=None, files=None): - '''Return a match object suitable for Mercurial based on - specified criteria. - - If files is specified it is a list of pathnames relative to - the repository root to be matched precisely. - - If pats and/or opts are specified, these are as to - cmdutil.match''' - - of_patterns = pats is not None or opts is not None - of_files = files is not None - opts = opts or {} # must be a dict - - assert not (of_patterns and of_files) - - if of_patterns: - return cmdutil.match(self.repo, pats, opts) - elif of_files: - return cmdutil.matchfiles(self.repo, files) - else: - return cmdutil.matchall(self.repo) - - def diff(self, node1=None, node2=None, match=None, opts=None): - '''Return the diff of changes between two changesets as a string''' - - # - # Retain compatibility by only calling diffopts() if it - # obviously has not already been done. - # - if isinstance(opts, dict): - opts = patch.diffopts(self.ui, opts) - - ret = cStringIO.StringIO() - for chunk in patch.diff(self.repo, node1, node2, match=match, - opts=opts): - ret.write(chunk) - - return ret.getvalue() - - if Version.at_least("1.6"): - def copy(self, src, dest): - '''Copy a file from src to dest - ''' - - self.workingctx().copy(src, dest) - else: - def copy(self, src, dest): - '''Copy a file from src to dest - ''' - - self.repo.copy(src, dest) - - - if Version.at_least("1.4"): - - def _walkctxs(self, base, head, follow=False, pick=None): - '''Generate changectxs between BASE and HEAD. - - Walk changesets between BASE and HEAD (in the order implied by - their relation), following a given branch if FOLLOW is a true - value, yielding changectxs where PICK (if specified) returns a - true value. - - PICK is a function of one argument, a changectx.''' - - chosen = {} - - def prep(ctx, fns): - chosen[ctx.rev()] = not pick or pick(ctx) - - opts = {'rev': ['%s:%s' % (base.rev(), head.rev())], - 'follow': follow} - matcher = cmdutil.matchall(self.repo) - - for ctx in cmdutil.walkchangerevs(self.repo, matcher, opts, prep): - if chosen[ctx.rev()]: - yield ctx - else: - - def _walkctxs(self, base, head, follow=False, pick=None): - '''Generate changectxs between BASE and HEAD. - - Walk changesets between BASE and HEAD (in the order implied by - their relation), following a given branch if FOLLOW is a true - value, yielding changectxs where PICK (if specified) returns a - true value. - - PICK is a function of one argument, a changectx.''' - - opts = {'rev': ['%s:%s' % (base.rev(), head.rev())], - 'follow': follow} - - changectx = self.repo.changectx - getcset = util.cachefunc(lambda r: changectx(r).changeset()) - - # - # See the docstring of mercurial.cmdutil.walkchangerevs() for - # the phased approach to the iterator returned. The important - # part to note is that the 'add' phase gathers nodes, which - # the 'iter' phase then iterates through. - # - changeiter = cmdutil.walkchangerevs(self.ui, self.repo, - [], getcset, opts)[0] - - matched = {} - for st, rev, fns in changeiter: - if st == 'add': - ctx = changectx(rev) - if not pick or pick(ctx): - matched[rev] = ctx - elif st == 'iter': - if rev in matched: - yield matched[rev] diff --git a/usr/src/tools/onbld/hgext/Makefile b/usr/src/tools/onbld/hgext/Makefile deleted file mode 100644 index 2247cb1f45..0000000000 --- a/usr/src/tools/onbld/hgext/Makefile +++ /dev/null @@ -1,58 +0,0 @@ -# -# CDDL HEADER START -# -# The contents of this file are subject to the terms of the -# Common Development and Distribution License (the "License"). -# You may not use this file except in compliance with the License. -# -# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE -# or http://www.opensolaris.org/os/licensing. -# See the License for the specific language governing permissions -# and limitations under the License. -# -# When distributing Covered Code, include this CDDL HEADER in each -# file and include the License file at usr/src/OPENSOLARIS.LICENSE. -# If applicable, add the following below this CDDL HEADER, with the -# fields enclosed by brackets "[]" replaced with your own identifying -# information: Portions Copyright [yyyy] [name of copyright owner] -# -# CDDL HEADER END -# - -# -# Copyright (c) 2008, 2010, Oracle and/or its affiliates. All rights reserved. -# - -include $(SRC)/Makefile.master -include ../../Makefile.tools - -PYSRCS = \ - __init__.py \ - cdm.py - -# -# We deliver .pyc files specific to the python version, with the -# exception of cdm.pyc, which we don't deliver at all. -# -# An exception to this is cdm.py, which is typically loaded using an -# extension path in an .hgrc. This path might refer to a Python -# version that's different from the one Mercurial is using. If we -# delivered a cdm.pyc, differing Pythons running Mercurial could cause -# it to be overwritten, causing packaging noise. Logic within cdm.py -# causes modules loaded from there to be those built by the correct -# version of Python. -# -PYSRC2 = $(PYSRCS:cdm.py=) -PYOBJS = $(PYSRC2:%.py=%.pyc) -PYTOPDIR = $(ROOTONBLDLIB) -PYMODDIR = onbld/hgext - -include ../../Makefile.python - -all: $(PYVERSOBJS) - -install: all $(ROOTPYFILES) - -clean: - -clobber: clean pyclobber diff --git a/usr/src/tools/onbld/hgext/__init__.py b/usr/src/tools/onbld/hgext/__init__.py deleted file mode 100644 index f45ecbc95f..0000000000 --- a/usr/src/tools/onbld/hgext/__init__.py +++ /dev/null @@ -1,28 +0,0 @@ -#! /usr/bin/python -# -# CDDL HEADER START -# -# The contents of this file are subject to the terms of the -# Common Development and Distribution License (the "License"). -# You may not use this file except in compliance with the License. -# -# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE -# or http://www.opensolaris.org/os/licensing. -# See the License for the specific language governing permissions -# and limitations under the License. -# -# When distributing Covered Code, include this CDDL HEADER in each -# file and include the License file at usr/src/OPENSOLARIS.LICENSE. -# If applicable, add the following below this CDDL HEADER, with the -# fields enclosed by brackets "[]" replaced with your own identifying -# information: Portions Copyright [yyyy] [name of copyright owner] -# -# CDDL HEADER END -# - -# -# Copyright 2008 Sun Microsystems, Inc. All rights reserved. -# Use is subject to license terms. -# -# ident "%Z%%M% %I% %E% SMI" -# diff --git a/usr/src/tools/onbld/hgext/cdm.py b/usr/src/tools/onbld/hgext/cdm.py deleted file mode 100644 index 28e45ed9a0..0000000000 --- a/usr/src/tools/onbld/hgext/cdm.py +++ /dev/null @@ -1,1530 +0,0 @@ -# -# This program is free software; you can redistribute it and/or modify -# 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 (c) 2008, 2010, Oracle and/or its affiliates. All rights reserved. -# Copyright 2008, 2011 Richard Lowe -# Copyright 2014 Garrett D'Amore <garrett@damore.org> -# Copyright (c) 2014, Joyent, Inc. -# - -'''OpenSolaris extensions to Mercurial - - This extension contains a number of commands to help you work with -the OpenSolaris consolidations. It provides commands to check your -changes against the various style rules used for OpenSolaris, to -backup and restore your changes, to generate code reviews, and to -prepare your changes for integration. - - -The Parent - - To provide a uniform notion of parent workspace regardless of -filesystem-based access, Cadmium uses the highest numbered changeset -on the current branch that is also in the parent workspace to -represent the parent workspace. - - -The Active List - - Many Cadmium commands operate on the active list, the set of -files ('active files') you have changed in this workspace in relation -to its parent workspace, and the metadata (commentary, primarily) -associated with those changes. - - -NOT Files - - Many of Cadmium's commands to check that your work obeys the -various stylistic rules of the OpenSolaris consolidations (such as -those run by 'hg nits') allow files to be excluded from this checking -by means of NOT files kept in the .hg/cdm/ directory of the Mercurial -repository for one-time exceptions, and in the exception_lists -directory at the repository root for permanent exceptions. (For ON, -these would mean one in $CODEMGR_WS and one in -$CODEMGR_WS/usr/closed). - - These files are in the same format as the Mercurial hgignore -file, a description of which is available in the hgignore(5) manual -page. - - -Common Tasks - - - Show diffs relative to parent workspace - pdiffs - - Check source style rules - nits - - Run pre-integration checks - pbchk - - Collapse all your changes into a single changeset - recommit -''' - -import atexit, os, re, sys, stat, termios - - -# -# Adjust the load path based on the location of cdm.py and the version -# of python into which it is being loaded. This assumes the normal -# onbld directory structure, where cdm.py is in -# lib/python(version)?/onbld/hgext/. If that changes so too must -# this. -# -# This and the case below are not equivalent. In this case we may be -# loading a cdm.py in python2.X/ via the lib/python/ symlink but need -# python2.Y in sys.path. -# -sys.path.insert(1, os.path.join(os.path.dirname(__file__), "..", "..", "..", - "python%d.%d" % sys.version_info[:2])) - -# -# Add the relative path from cdm.py to usr/src/tools to the load path, -# such that a cdm.py loaded from the source tree uses the modules also -# within the source tree. -# -sys.path.insert(2, os.path.join(os.path.dirname(__file__), "..", "..")) - -from onbld.Scm import Version -from onbld.Scm import Ignore -from mercurial import util - -try: - Version.check_version() -except Version.VersionMismatch, badversion: - raise util.Abort("Version Mismatch:\n %s\n" % badversion) - -from mercurial import cmdutil, node, patch - -from onbld.Scm.WorkSpace import WorkSpace, WorkList -from onbld.Scm.Backup import CdmBackup -from onbld.Checks import Cddl, Comments, Copyright, CStyle, HdrChk -from onbld.Checks import JStyle, Keywords, ManLint, Mapfile - - -def yes_no(ui, msg, default): - if default: - prompt = ' [Y/n]:' - defanswer = 'y' - else: - prompt = ' [y/N]:' - defanswer = 'n' - - if Version.at_least("1.4"): - index = ui.promptchoice(msg + prompt, ['&yes', '&no'], - default=['y', 'n'].index(defanswer)) - resp = ('y', 'n')[index] - else: - resp = ui.prompt(msg + prompt, ['&yes', '&no'], default=defanswer) - - return resp[0] in ('Y', 'y') - - -def buildfilelist(ws, parent, files): - '''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: - return [(path, None) for path in sorted(files)] - else: - active = ws.active(parent=parent) - return [(ws.filepath(e.name), e) for e in sorted(active)] -buildfilelist = util.cachefunc(buildfilelist) - - -def not_check(repo, cmd): - '''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): - cpath = util.canonpath(repo.root, repo.getcwd(), path) - return ignfunc(cpath) - return f - - ignorefiles = [] - - for f in [repo.join('cdm/%s.NOT' % cmd), - repo.wjoin('exception_lists/%s' % cmd)]: - if os.path.exists(f): - ignorefiles.append(f) - - if ignorefiles: - ign = Ignore.ignore(repo.root, ignorefiles) - return canonified_check(ign) - else: - return util.never - - -def abort_if_dirty(ws): - '''Abort if the workspace has uncommitted changes, merges, - branches, or has Mq patches applied''' - - if ws.modified(): - raise util.Abort('workspace has uncommitted changes') - if ws.merged(): - raise util.Abort('workspace contains uncommitted merge') - if ws.branched(): - raise util.Abort('workspace contains uncommitted branch') - if ws.mq_applied(): - raise util.Abort('workspace has Mq patches 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 = {} - - -def reposetup(ui, repo): - if repo.local() and repo not in wslist: - wslist[repo] = WorkSpace(repo) - - if ui.interactive() and sys.stdin.isatty(): - ui.setconfig('hooks', 'preoutgoing.cdm_pbconfirm', - 'python:hgext_cdm.pbconfirm') - - -def pbconfirm(ui, repo, hooktype, source): - def wrapper(settings=None): - termios.tcsetattr(sys.stdin.fileno(), termios.TCSANOW, settings) - - if source == 'push': - if not yes_no(ui, "Are you sure you wish to push?", False): - return 1 - else: - settings = termios.tcgetattr(sys.stdin.fileno()) - orig = list(settings) - atexit.register(wrapper, orig) - settings[3] = settings[3] & (~termios.ISIG) # c_lflag - termios.tcsetattr(sys.stdin.fileno(), termios.TCSANOW, settings) - - -def cdm_pdiffs(ui, repo, *pats, **opts): - '''diff workspace against its parent - - Show differences between this workspace and its parent workspace - in the same manner as 'hg diff'. - - For a description of the changeset used to represent the parent - workspace, see The Parent in the extension documentation ('hg help - cdm'). - ''' - - act = wslist[repo].active(opts.get('parent')) - if not act.revs: - return - - # - # If no patterns were specified, either explicitly or via -I or -X - # use the active list files to avoid a workspace walk. - # - if pats or opts.get('include') or opts.get('exclude'): - matchfunc = wslist[repo].matcher(pats=pats, opts=opts) - else: - matchfunc = wslist[repo].matcher(files=act.files()) - - opts = patch.diffopts(ui, opts) - diffs = wslist[repo].diff(act.parenttip.node(), act.localtip.node(), - match=matchfunc, opts=opts) - if diffs: - ui.write(diffs) - - -def cdm_list(ui, repo, **opts): - '''list active files (those changed in this workspace) - - Display a list of files changed in this workspace as compared to - its parent workspace. - - File names are displayed one-per line, grouped by manner in which - they changed (added, modified, removed). Information about - renames or copies is output in parentheses following the file - name. - - For a description of the changeset used to represent the parent - workspace, see The Parent in the extension documentation ('hg help - cdm'). - - Output can be filtered by change type with --added, --modified, - and --removed. By default, all files are shown. - ''' - - act = wslist[repo].active(opts['parent']) - wanted = set(x for x in ('added', 'modified', 'removed') if opts[x]) - changes = {} - - for entry in act: - if wanted and (entry.change not in wanted): - continue - - if entry.change not in changes: - changes[entry.change] = [] - changes[entry.change].append(entry) - - for change in sorted(changes.keys()): - ui.write(change + ':\n') - - for entry in sorted(changes[change]): - if entry.is_renamed(): - ui.write('\t%s (renamed from %s)\n' % (entry.name, - entry.parentname)) - elif entry.is_copied(): - ui.write('\t%s (copied from %s)\n' % (entry.name, - entry.parentname)) - else: - ui.write('\t%s\n' % entry.name) - - -def cdm_bugs(ui, repo, parent=None): - '''show all bug IDs referenced in changeset comments''' - - act = wslist[repo].active(parent) - - for elt in set(filter(Comments.isBug, act.comments())): - ui.write(elt + '\n') - - -def cdm_comments(ui, repo, parent=None): - '''show changeset commentary for all active changesets''' - act = wslist[repo].active(parent) - - for elt in act.comments(): - ui.write(elt + '\n') - - -def cdm_renamed(ui, repo, parent=None): - '''show renamed active files - - Renamed files are shown in the format:: - - new-name old-name - - One pair per-line. - ''' - - act = wslist[repo].active(parent) - - for entry in sorted(filter(lambda x: x.is_renamed(), act)): - ui.write('%s %s\n' % (entry.name, entry.parentname)) - - -def cdm_comchk(ui, repo, **opts): - '''check active changeset comment formatting - - Check that active changeset comments conform to O/N rules. - - Each comment line must contain either one bug or ARC case ID - followed by its synopsis, or credit an external contributor. - ''' - - active = wslist[repo].active(opts.get('parent')) - - ui.write('Comments check:\n') - - check_db = not opts.get('nocheck') - return Comments.comchk(active.comments(), check_db=check_db, output=ui) - - -def cdm_cddlchk(ui, repo, *args, **opts): - '''check for a valid CDDL header comment in all active files. - - Check active files for a valid Common Development and Distribution - License (CDDL) block comment. - - Newly added files are checked for a copy of the CDDL header - comment. Modified files are only checked if they contain what - appears to be an existing CDDL header comment. - - Files can be excluded from this check using the cddlchk.NOT file. - See NOT Files in the extension documentation ('hg help cdm'). - ''' - - filelist = buildfilelist(wslist[repo], opts.get('parent'), args) - exclude = not_check(repo, 'cddlchk') - lenient = True - ret = 0 - - ui.write('CDDL block check:\n') - - for f, e in filelist: - if e and e.is_removed(): - continue - elif (e or opts.get('honour_nots')) and exclude(f): - ui.status('Skipping %s...\n' % f) - continue - elif e and e.is_added(): - lenient = False - else: - lenient = True - - fh = open(f, 'r') - ret |= Cddl.cddlchk(fh, lenient=lenient, output=ui) - fh.close() - return ret - - -def cdm_manlintchk(ui, repo, *args, **opts): - '''check for mandoc lint - - Check for man page formatting errors. - - Files can be excluded from this check using the manlint.NOT - file. See NOT Files in the extension documentation ('hg help - cdm'). - ''' - - filelist = buildfilelist(wslist[repo], opts.get('parent'), args) - exclude = not_check(repo, 'manlint') - ret = 0 - - # Man pages are identified as having a suffix starting with a digit. - ManfileRE = re.compile(r'.*\.[0-9][a-z]*$', re.IGNORECASE) - - ui.write('Man format check:\n') - - for f, e in filelist: - if e and e.is_removed(): - continue - elif (not ManfileRE.match(f)): - continue - elif (e or opts.get('honour_nots')) and exclude(f): - ui.status('Skipping %s...\n' % f) - continue - - fh = open(f, 'r') - ret |= ManLint.manlint(fh, output=ui, picky=True) - fh.close() - return ret - - -def cdm_mapfilechk(ui, repo, *args, **opts): - '''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). - - Files can be excluded from this check using the mapfilechk.NOT - file. See NOT Files in the extension documentation ('hg help - cdm'). - ''' - - filelist = buildfilelist(wslist[repo], opts.get('parent'), args) - exclude = not_check(repo, 'mapfilechk') - ret = 0 - - # We are interested in examining any file that has the following - # in its final path segment: - # - Contains the word 'mapfile' - # - Begins with 'map.' - # - Ends with '.map' - # We don't want to match unless these things occur in final path segment - # because directory names with these strings don't indicate a mapfile. - # We also ignore files with suffixes that tell us that the files - # are not mapfiles. - MapfileRE = re.compile(r'.*((mapfile[^/]*)|(/map\.+[^/]*)|(\.map))$', - re.IGNORECASE) - NotMapSuffixRE = re.compile(r'.*\.[ch]$', re.IGNORECASE) - - ui.write('Mapfile comment check:\n') - - for f, e in filelist: - if e and e.is_removed(): - continue - elif (not MapfileRE.match(f)) or NotMapSuffixRE.match(f): - continue - elif (e or opts.get('honour_nots')) and exclude(f): - ui.status('Skipping %s...\n' % f) - continue - - fh = open(f, 'r') - ret |= Mapfile.mapfilechk(fh, output=ui) - fh.close() - return ret - - -def cdm_copyright(ui, repo, *args, **opts): - '''check each active file for a current and correct copyright notice - - Check that all active files have a correctly formed copyright - notice containing the current year. - - See the Non-Formatting Considerations section of the OpenSolaris - Developer's Reference for more info on the correct form of - copyright notice. - (http://hub.opensolaris.org/bin/view/Community+Group+on/devref_7#H723NonFormattingConsiderations) - - Files can be excluded from this check using the copyright.NOT file. - See NOT Files in the extension documentation ('hg help cdm'). - ''' - - filelist = buildfilelist(wslist[repo], opts.get('parent'), args) - exclude = not_check(repo, 'copyright') - ret = 0 - - ui.write('Copyright check:\n') - - for f, e in filelist: - if e and e.is_removed(): - continue - elif (e or opts.get('honour_nots')) and exclude(f): - ui.status('Skipping %s...\n' % f) - continue - - fh = open(f, 'r') - ret |= Copyright.copyright(fh, output=ui) - fh.close() - return ret - - -def cdm_hdrchk(ui, repo, *args, **opts): - '''check active C header files conform to the O/N header rules - - Check that any added or modified C header files conform to the O/N - header rules. - - See the section 'HEADER STANDARDS' in the hdrchk(1) manual page - for more information on the rules for O/N header file formatting. - - Files can be excluded from this check using the hdrchk.NOT file. - See NOT Files in the extension documentation ('hg help cdm'). - ''' - - filelist = buildfilelist(wslist[repo], opts.get('parent'), args) - exclude = not_check(repo, 'hdrchk') - ret = 0 - - ui.write('Header format check:\n') - - for f, e in filelist: - if e and e.is_removed(): - continue - elif not f.endswith('.h'): - continue - elif (e or opts.get('honour_nots')) and exclude(f): - ui.status('Skipping %s...\n' % f) - continue - - fh = open(f, 'r') - ret |= HdrChk.hdrchk(fh, lenient=True, output=ui) - fh.close() - return ret - - -def cdm_cstyle(ui, repo, *args, **opts): - '''check active C source files conform to the C Style Guide - - Check that any added or modified C source file conform to the C - Style Guide. - - See the C Style Guide for more information about correct C source - formatting. - (http://hub.opensolaris.org/bin/download/Community+Group+on/WebHome/cstyle.ms.pdf) - - Files can be excluded from this check using the cstyle.NOT file. - See NOT Files in the extension documentation ('hg help cdm'). - ''' - - filelist = buildfilelist(wslist[repo], opts.get('parent'), args) - exclude = not_check(repo, 'cstyle') - ret = 0 - - ui.write('C style check:\n') - - for f, e in filelist: - if e and e.is_removed(): - continue - elif not (f.endswith('.c') or f.endswith('.h')): - continue - elif (e or opts.get('honour_nots')) and exclude(f): - ui.status('Skipping %s...\n' % f) - continue - - fh = open(f, 'r') - ret |= CStyle.cstyle(fh, output=ui, - picky=True, check_posix_types=True, - check_continuation=True) - fh.close() - return ret - - -def cdm_jstyle(ui, repo, *args, **opts): - '''check active Java source files for common stylistic errors - - Files can be excluded from this check using the jstyle.NOT file. - See NOT Files in the extension documentation ('hg help cdm'). - ''' - - filelist = buildfilelist(wslist[repo], opts.get('parent'), args) - exclude = not_check(repo, 'jstyle') - ret = 0 - - ui.write('Java style check:\n') - - for f, e in filelist: - if e and e.is_removed(): - continue - elif not f.endswith('.java'): - continue - elif (e or opts.get('honour_nots')) and exclude(f): - ui.status('Skipping %s...\n' % f) - continue - - fh = open(f, 'r') - ret |= JStyle.jstyle(fh, output=ui, picky=True) - fh.close() - return ret - - -def cdm_permchk(ui, repo, *args, **opts): - '''check the permissions of each active file - - Check that the file permissions of each added or modified file do not - contain the executable bit. - - Files can be excluded from this check using the permchk.NOT file. - See NOT Files in the extension documentation ('hg help cdm'). - ''' - - filelist = buildfilelist(wslist[repo], opts.get('parent'), args) - exclude = not_check(repo, 'permchk') - exeFiles = [] - - ui.write('File permission check:\n') - - for f, e in filelist: - if e and e.is_removed(): - continue - elif (e or opts.get('honour_nots')) and exclude(f): - ui.status('Skipping %s...\n' % f) - continue - - mode = stat.S_IMODE(os.stat(f)[stat.ST_MODE]) - if mode & stat.S_IEXEC: - exeFiles.append(f) - - if len(exeFiles) > 0: - ui.write('Warning: the following active file(s) have executable mode ' - '(+x) permission set,\nremove unless intentional:\n') - for fname in exeFiles: - ui.write(" %s\n" % fname) - - return len(exeFiles) > 0 - - -def cdm_tagchk(ui, repo, **opts): - '''check modification of workspace tags - - Check for any modification of the repository's .hgtags file. - - With the exception of the gatekeepers, nobody should introduce or - modify a repository's tags. - ''' - - active = wslist[repo].active(opts.get('parent')) - - ui.write('Checking for new tags:\n') - - if ".hgtags" in active: - tfile = wslist[repo].filepath('.hgtags') - ptip = active.parenttip.rev() - - ui.write('Warning: Workspace contains new non-local tags.\n' - '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' % - (ptip, tfile, tfile)) - - return 1 - - return 0 - - -def cdm_branchchk(ui, repo, **opts): - '''check for changes in number or name of branches - - Check that the workspace contains only a single head, that it is - on the branch 'default', and that no new branches have been - introduced. - ''' - - ui.write('Checking for multiple heads (or branches):\n') - - heads = set(repo.heads()) - parents = set([x.node() for x in wslist[repo].workingctx().parents()]) - - # - # 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). - # - if len(heads) > 1 and heads != parents: - ui.write('Workspace has multiple heads (or branches):\n') - for head in [repo.changectx(head) for head in heads]: - ui.write(" %d:%s\t%s\n" % - (head.rev(), str(head), head.description().splitlines()[0])) - ui.write('You must merge and recommit.\n') - return 1 - - ui.write('\nChecking for branch changes:\n') - - if repo.dirstate.branch() != 'default': - ui.write("Warning: Workspace tip has named branch: '%s'\n" - "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" % - (repo.dirstate.branch())) - return 1 - - branches = repo.branchtags().keys() - if len(branches) > 1: - ui.write('Warning: Workspace has named branches:\n') - for t in branches: - if t == 'default': - continue - ui.write("\t%s\n" % t) - - ui.write("Only gatekeepers should push new branches.\n" - "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 - - -def cdm_keywords(ui, repo, *args, **opts): - '''check active files for SCCS keywords - - Check that any added or modified files do not contain SCCS keywords - (#ident lines, etc.). - - Files can be excluded from this check using the keywords.NOT file. - See NOT Files in the extension documentation ('hg help cdm'). - ''' - - filelist = buildfilelist(wslist[repo], opts.get('parent'), args) - exclude = not_check(repo, 'keywords') - ret = 0 - - ui.write('Keywords check:\n') - - for f, e in filelist: - if e and e.is_removed(): - continue - elif (e or opts.get('honour_nots')) and exclude(f): - ui.status('Skipping %s...\n' % f) - continue - - fh = open(f, 'r') - ret |= Keywords.keywords(fh, output=ui) - fh.close() - 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. -# -def cdm_outchk(ui, repo, **opts): - '''Warn the user if they have uncommitted changes''' - - ui.write('Checking for uncommitted changes:\n') - - st = wslist[repo].modified() - if st: - ui.write('Warning: the following files have uncommitted changes:\n') - for elt in st: - ui.write(' %s\n' % elt) - return 1 - return 0 - - -def cdm_mergechk(ui, repo, **opts): - '''Warn the user if their workspace contains merges''' - - active = wslist[repo].active(opts.get('parent')) - - ui.write('Checking for merges:\n') - - merges = filter(lambda x: len(x.parents()) == 2 and x.parents()[1], - active.revs) - - if merges: - ui.write('Workspace contains the following merges:\n') - for rev in merges: - desc = rev.description().splitlines() - ui.write(' %s:%s\t%s\n' % - (rev.rev() or "working", str(rev), - desc and desc[0] or "*** uncommitted change ***")) - return 1 - return 0 - - -def run_checks(ws, cmds, *args, **opts): - '''Run CMDS (with OPTS) over active files in WS''' - - ret = 0 - - for cmd in cmds: - name = cmd.func_name.split('_')[1] - if not ws.ui.configbool('cdm', name, True): - ws.ui.status('Skipping %s check...\n' % name) - else: - ws.ui.pushbuffer() - result = cmd(ws.ui, ws.repo, honour_nots=True, *args, **opts) - output = ws.ui.popbuffer() - - ret |= result - - if not ws.ui.quiet or result != 0: - ws.ui.write(output, '\n') - return ret - - -def cdm_nits(ui, repo, *args, **opts): - '''check for stylistic nits in active files - - Check each active file for basic stylistic errors. - - The following checks are run over each active file (see 'hg help - <check>' for more information about each): - - - copyright (copyright statements) - - cstyle (C source style) - - hdrchk (C header style) - - jstyle (java source style) - - manlint (man page formatting) - - mapfilechk (link-editor mapfiles) - - permchk (file permissions) - - keywords (SCCS keywords) - - With the global -q/--quiet option, only provide output for those - checks which fail. - ''' - - cmds = [cdm_copyright, - cdm_cstyle, - cdm_hdrchk, - cdm_jstyle, - cmd_manlintchk, - cdm_mapfilechk, - cdm_permchk, - cdm_keywords] - - return run_checks(wslist[repo], cmds, *args, **opts) - - -def cdm_pbchk(ui, repo, **opts): - '''run pre-integration checks on this workspace - - Check this workspace for common errors prior to integration. - - The following checks are run over the active list (see 'hg help - <check>' for more information about each): - - - branchchk (addition/modification of branches) - - comchk (changeset descriptions) - - copyright (copyright statements) - - cstyle (C source style) - - hdrchk (C header style) - - jstyle (java source style) - - keywords (SCCS keywords) - - manlint (man page formatting) - - mapfilechk (link-editor mapfiles) - - permchk (file permissions) - - tagchk (addition/modification of tags) - - Additionally, the workspace is checked for outgoing merges (which - should be removed with 'hg recommit'), and uncommitted changes. - - With the global -q/--quiet option, only provide output for those - checks which fail. - ''' - - # - # 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 are run. - # - cmds = [cdm_copyright, - cdm_cstyle, - cdm_hdrchk, - cdm_jstyle, - cdm_manlintchk, - cdm_mapfilechk, - cdm_permchk, - cdm_keywords, - cdm_comchk, - cdm_tagchk, - cdm_branchchk, - cdm_outchk, - cdm_mergechk] - - return run_checks(wslist[repo], cmds, **opts) - - -def cdm_recommit(ui, repo, **opts): - '''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). - ''' - - ws = wslist[repo] - - if not os.getcwd().startswith(repo.root): - raise util.Abort('recommit is not safe to run with -R') - - abort_if_dirty(ws) - - wlock = repo.wlock() - lock = repo.lock() - - try: - parent = ws.parent(opts['parent']) - between = repo.changelog.nodesbetween(ws.findoutgoing(parent))[2] - heads = set(between) & set(repo.heads()) - - if len(heads) > 1: - ui.warn('Workspace has multiple outgoing heads (or branches):\n') - for head in sorted(map(repo.changelog.rev, heads), reverse=True): - ui.warn('\t%d\n' % head) - raise util.Abort('you must merge before recommitting') - - # - # We can safely use the worklist here, as we know (from the - # abort_if_dirty() check above) that the working copy has not been - # modified. - # - active = ws.active(parent) - - if filter(lambda b: len(b.parents()) > 1, active.bases()): - raise util.Abort('Cannot recommit a merge of two non-outgoing ' - 'changesets') - - if len(active.revs) <= 0: - raise util.Abort("no changes to recommit") - - if len(active.files()) <= 0: - ui.warn("Recommitting %d active changesets, but no active files\n" % - len(active.revs)) - - # - # 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. - # - bogosity = [f.parentname for f in active if f.is_renamed() and - os.path.exists(repo.wjoin(f.parentname))] - if bogosity: - ui.warn("The following file names are the original name of a " - "rename and also present\n" - "in the working directory:\n") - - for fname in bogosity: - ui.warn(" %s\n" % fname) - - if not yes_no(ui, "These files will be removed by recommit." - " Continue?", - False): - raise util.Abort("recommit would clobber files") - - user = opts['user'] or ui.username() - comments = '\n'.join(active.comments()) - - message = cmdutil.logmessage(opts) or ui.edit(comments, user) - if not message: - raise util.Abort('empty commit message') - - bk = CdmBackup(ui, ws, backup_name(repo.root)) - if bk.need_backup(): - if yes_no(ui, 'Do you want to backup files first?', True): - bk.backup() - - oldtags = repo.tags() - clearedtags = [(name, nd, repo.changelog.rev(nd), local) - for name, nd, local in active.tags()] - - ws.squishdeltas(active, message, user=user) - finally: - lock.release() - wlock.release() - - if clearedtags: - ui.write("Removed tags:\n") - for name, nd, rev, local in sorted(clearedtags, - key=lambda x: x[0].lower()): - ui.write(" %5s:%s:\t%s%s\n" % (rev, node.short(nd), - name, (local and ' (local)' or ''))) - - for ntag, nnode in sorted(repo.tags().items(), - key=lambda x: x[0].lower()): - if ntag in oldtags and ntag != "tip": - if oldtags[ntag] != nnode: - ui.write("tag '%s' now refers to revision %d:%s\n" % - (ntag, repo.changelog.rev(nnode), - node.short(nnode))) - - -def do_eval(cmd, files, root, changedir=True): - if not changedir: - os.chdir(root) - - for path in sorted(files): - dirn, base = os.path.split(path) - - if changedir: - os.chdir(os.path.join(root, dirn)) - - os.putenv('workspace', root) - os.putenv('filepath', path) - os.putenv('dir', dirn) - os.putenv('file', base) - os.system(cmd) - - -def cdm_eval(ui, repo, *command, **opts): - '''run specified command for each active file - - Run the command specified on the command line for each active - file, with the following variables present in the environment: - - :$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. - ''' - - act = wslist[repo].active(opts['parent']) - cmd = ' '.join(command) - files = [x.name for x in act if not x.is_removed()] - - do_eval(cmd, files, repo.root, not opts['remain']) - - -def cdm_apply(ui, repo, *command, **opts): - '''apply specified command to all active files - - Run the command specified on the command line over each active - file. - - For example 'hg apply "wc -l"' will output a count of the lines in - each active file. - ''' - - act = wslist[repo].active(opts['parent']) - - if opts['remain']: - appnd = ' $filepath' - else: - appnd = ' $file' - - cmd = ' '.join(command) + appnd - files = [x.name for x in act if not x.is_removed()] - - do_eval(cmd, files, repo.root, not opts['remain']) - - -def cdm_reparent(ui, repo, parent): - '''reparent your workspace - - Update the 'default' path alias that is used as the default source - for 'hg pull' and the default destination for 'hg push' (unless - there is a 'default-push' alias). This is also the path all - Cadmium commands treat as your parent workspace. - ''' - - def append_new_parent(parent): - fp = None - try: - fp = repo.opener('hgrc', 'a', atomictemp=True) - if fp.tell() != 0: - fp.write('\n') - fp.write('[paths]\n' - 'default = %s\n\n' % parent) - fp.rename() - finally: - if fp and not fp.closed: - fp.close() - - def update_parent(path, line, parent): - line = line - 1 # The line number we're passed will be 1-based - fp = None - - try: - fp = open(path) - data = fp.readlines() - finally: - if fp and not fp.closed: - fp.close() - - # - # line will be the last line of any continued block, go back - # to the first removing the continuation as we go. - # - while data[line][0].isspace(): - data.pop(line) - line -= 1 - - assert data[line].startswith('default') - - data[line] = "default = %s\n" % parent - if data[-1] != '\n': - data.append('\n') - - try: - fp = util.atomictempfile(path, 'w', 0644) - fp.writelines(data) - fp.rename() - finally: - if fp and not fp.closed: - fp.close() - - from mercurial import config - parent = ui.expandpath(parent) - - if not os.path.exists(repo.join('hgrc')): - append_new_parent(parent) - return - - cfg = config.config() - cfg.read(repo.join('hgrc')) - source = cfg.source('paths', 'default') - - if not source: - append_new_parent(parent) - return - else: - path, target = source.rsplit(':', 1) - - if path != repo.join('hgrc'): - raise util.Abort("Cannot edit path specification not in repo hgrc\n" - "default path is from: %s" % source) - - update_parent(path, int(target), parent) - - -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''' - - special = ['usr/closed'] - - fullpath = fullpath.rstrip(os.path.sep).split(os.path.sep) - - # - # 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 - # - for elt in special: - elt = elt.split(os.path.sep) - pathpos = len(elt) - - if fullpath[-pathpos:] == elt: - return "%s-%s" % (fullpath[-pathpos - 1], elt[-1]) - else: - return fullpath[-1] - - -def cdm_backup(ui, repo, if_newer=False): - '''backup workspace changes and metadata - - Create a backup copy of changes made in this workspace as compared - to its parent workspace, as well as important metadata of this - workspace. - - NOTE: Only changes as compared to the parent workspace are backed - up. If you lose this workspace and its parent, you will not be - able to restore a backup into a clone of the grandparent - workspace. - - By default, backups are stored in the cdm.backup/ directory in - your home directory. This is configurable using the cdm.backupdir - configuration variable, for example: - - hg backup --config cdm.backupdir=/net/foo/backups - - or place the following in an appropriate hgrc file:: - - [cdm] - backupdir = /net/foo/backups - - Backups have the same name as the workspace in which they were - taken, with '-closed' appended in the case of O/N's usr/closed. - ''' - - name = backup_name(repo.root) - bk = CdmBackup(ui, wslist[repo], name) - - wlock = repo.wlock() - lock = repo.lock() - - try: - if if_newer and not bk.need_backup(): - ui.status('backup is up-to-date\n') - else: - bk.backup() - finally: - lock.release() - wlock.release() - - -def cdm_restore(ui, repo, backup, **opts): - '''restore workspace from backup - - Restore this workspace from a backup (taken by 'hg backup'). - - If the specified backup directory does not exist, it is assumed to - be relative to the cadmium backup directory (~/cdm.backup/ by - default). - - For example:: - - % hg restore on-rfe - Restore the latest backup of ~/cdm.backup/on-rfe - % hg restore -g3 on-rfe - Restore the 3rd backup of ~/cdm.backup/on-rfe - % hg restore /net/foo/backup/on-rfe - Restore from an explicit path - ''' - - if not os.getcwd().startswith(repo.root): - raise util.Abort('restore is not safe to run with -R') - - abort_if_dirty(wslist[repo]) - - if opts['generation']: - gen = int(opts['generation']) - else: - gen = None - - if os.path.exists(backup): - backup = os.path.abspath(backup) - - wlock = repo.wlock() - lock = repo.lock() - - try: - bk = CdmBackup(ui, wslist[repo], backup) - bk.restore(gen) - finally: - lock.release() - wlock.release() - - -def cdm_webrev(ui, repo, **opts): - '''generate web-based code review and optionally upload it - - Generate a web-based code review using webrev(1) and optionally - upload it. All known arguments are passed through to webrev(1). - ''' - - webrev_args = "" - for key in opts.keys(): - if opts[key]: - if type(opts[key]) == type(True): - webrev_args += '-' + key + ' ' - else: - webrev_args += '-' + key + ' ' + opts[key] + ' ' - - retval = os.system('webrev ' + webrev_args) - if retval != 0: - return retval - 255 - - return 0 - - -def cdm_debugcdmal(ui, repo, *pats, **opts): - '''dump the active list for the sake of debugging/testing''' - - ui.write(wslist[repo].active(opts['parent']).as_text(pats)) - - -def cdm_changed(ui, repo, *pats, **opts): - '''mark a file as changed in the working copy - - Maintain a list of files checked for modification in the working - copy. If the list exists, most cadmium commands will only check - the working copy for changes to those files, rather than checking - the whole workspace (this does not apply to committed changes, - which are always seen). - - Since this list functions only as a hint as to where in the - working copy to look for changes, entries that have not actually - been modified (in the working copy, or in general) are not - problematic. - - - Note: If such a list exists, it must be kept up-to-date. - - - Renamed files can be added with reference only to their new name: - $ hg mv foo bar - $ hg changed bar - - Without arguments, 'hg changed' will list all files recorded as - altered, such that, for instance: - $ hg status $(hg changed) - $ hg diff $(hg changed) - Become useful (generally faster than their unadorned counterparts) - - To create an initially empty list: - $ hg changed -i - Until files are added to the list it is equivalent to saying - "Nothing has been changed" - - Update the list based on the current active list: - $ hg changed -u - The old list is emptied, and replaced with paths from the - current active list. - - Remove the list entirely: - $ hg changed -d - ''' - - def modded_files(repo, parent): - out = wslist[repo].findoutgoing(wslist[repo].parent(parent)) - outnodes = repo.changelog.nodesbetween(out)[0] - - files = set() - for n in outnodes: - files.update(repo.changectx(n).files()) - - files.update(wslist[repo].status().keys()) - return files - - # - # specced_pats is convenient to treat as a boolean indicating - # whether any file patterns or paths were specified. - # - specced_pats = pats or opts['include'] or opts['exclude'] - if len(filter(None, [opts['delete'], opts['update'], opts['init'], - specced_pats])) > 1: - raise util.Abort("-d, -u, -i and patterns are mutually exclusive") - - wl = WorkList(wslist[repo]) - - if (not wl and specced_pats) or opts['init']: - wl.delete() - if yes_no(ui, "Create a list based on your changes thus far?", True): - map(wl.add, modded_files(repo, opts.get('parent'))) - - if opts['delete']: - wl.delete() - elif opts['update']: - wl.delete() - map(wl.add, modded_files(repo, opts.get('parent'))) - wl.write() - elif opts['init']: # Any possible old list was deleted above - wl.write() - elif specced_pats: - sources = [] - - match = wslist[repo].matcher(pats=pats, opts=opts) - for abso in repo.walk(match): - if abso in repo.dirstate: - wl.add(abso) - # - # Store the source name of any copy. We use this so - # both the add and delete of a rename can be entered - # into the WorkList with only the destination name - # explicitly being mentioned. - # - fctx = wslist[repo].workingctx().filectx(abso) - rn = fctx.renamed() - if rn: - sources.append(rn[0]) - else: - ui.warn("%s is not version controlled -- skipping\n" % - match.rel(abso)) - - if sources: - for fname, chng in wslist[repo].status(files=sources).iteritems(): - if chng == 'removed': - wl.add(fname) - wl.write() - else: - for elt in sorted(wl.list()): - ui.write("%s\n" % wslist[repo].filepath(elt)) - - -cmdtable = { - 'apply': (cdm_apply, [('p', 'parent', '', 'parent workspace'), - ('r', 'remain', None, 'do not change directory')], - 'hg apply [-p PARENT] [-r] command...'), - '^backup|bu': (cdm_backup, [('t', 'if-newer', None, - 'only backup if workspace files are newer')], - 'hg backup [-t]'), - 'branchchk': (cdm_branchchk, [('p', 'parent', '', 'parent workspace')], - 'hg branchchk [-p PARENT]'), - 'bugs': (cdm_bugs, [('p', 'parent', '', 'parent workspace')], - 'hg bugs [-p PARENT]'), - 'cddlchk': (cdm_cddlchk, [('p', 'parent', '', 'parent workspace')], - 'hg cddlchk [-p PARENT]'), - 'changed': (cdm_changed, [('d', 'delete', None, 'delete the file list'), - ('u', 'update', None, 'mark all changed files'), - ('i', 'init', None, 'create an empty file list'), - ('p', 'parent', '', 'parent workspace'), - ('I', 'include', [], - 'include names matching the given patterns'), - ('X', 'exclude', [], - 'exclude names matching the given patterns')], - 'hg changed -d\n' - 'hg changed -u\n' - 'hg changed -i\n' - 'hg changed [-I PATTERN...] [-X PATTERN...] [FILE...]'), - 'comchk': (cdm_comchk, [('p', 'parent', '', 'parent workspace'), - ('N', 'nocheck', None, - 'do not compare comments with databases')], - 'hg comchk [-p PARENT]'), - 'comments': (cdm_comments, [('p', 'parent', '', 'parent workspace')], - 'hg comments [-p PARENT]'), - 'copyright': (cdm_copyright, [('p', 'parent', '', 'parent workspace')], - 'hg copyright [-p PARENT]'), - 'cstyle': (cdm_cstyle, [('p', 'parent', '', 'parent workspace')], - 'hg cstyle [-p PARENT]'), - 'debugcdmal': (cdm_debugcdmal, [('p', 'parent', '', 'parent workspace')], - 'hg debugcdmal [-p PARENT] [FILE...]'), - 'eval': (cdm_eval, [('p', 'parent', '', 'parent workspace'), - ('r', 'remain', None, 'do not change directory')], - 'hg eval [-p PARENT] [-r] command...'), - 'hdrchk': (cdm_hdrchk, [('p', 'parent', '', 'parent workspace')], - 'hg hdrchk [-p PARENT]'), - 'jstyle': (cdm_jstyle, [('p', 'parent', '', 'parent workspace')], - 'hg jstyle [-p PARENT]'), - 'keywords': (cdm_keywords, [('p', 'parent', '', 'parent workspace')], - 'hg keywords [-p PARENT]'), - '^list|active': (cdm_list, [('p', 'parent', '', 'parent workspace'), - ('a', 'added', None, 'show added files'), - ('m', 'modified', None, 'show modified files'), - ('r', 'removed', None, 'show removed files')], - 'hg list [-amrRu] [-p PARENT]'), - 'manlint': (cdm_manlintchk, [('p', 'parent', '', 'parent workspace')], - 'hg manlint [-p PARENT]'), - 'mapfilechk': (cdm_mapfilechk, [('p', 'parent', '', 'parent workspace')], - 'hg mapfilechk [-p PARENT]'), - '^nits': (cdm_nits, [('p', 'parent', '', 'parent workspace')], - 'hg nits [-p PARENT]'), - '^pbchk': (cdm_pbchk, [('p', 'parent', '', 'parent workspace'), - ('N', 'nocheck', None, 'skip database checks')], - 'hg pbchk [-N] [-p PARENT]'), - 'permchk': (cdm_permchk, [('p', 'parent', '', 'parent workspace')], - 'hg permchk [-p PARENT]'), - '^pdiffs': (cdm_pdiffs, [('p', 'parent', '', 'parent workspace'), - ('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 whose 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...]'), - '^recommit|reci': (cdm_recommit, [('p', 'parent', '', 'parent workspace'), - ('m', 'message', '', - 'use <text> as commit message'), - ('l', 'logfile', '', - 'read commit message from file'), - ('u', 'user', '', - 'record user as committer')], - 'hg recommit [-m TEXT] [-l FILE] [-u USER] [-p PARENT]'), - 'renamed': (cdm_renamed, [('p', 'parent', '', 'parent workspace')], - 'hg renamed [-p PARENT]'), - 'reparent': (cdm_reparent, [], 'hg reparent PARENT'), - '^restore': (cdm_restore, [('g', 'generation', '', 'generation number')], - 'hg restore [-g GENERATION] BACKUP'), - 'tagchk': (cdm_tagchk, [('p', 'parent', '', 'parent workspace')], - 'hg tagchk [-p PARENT]'), - 'webrev': (cdm_webrev, [('C', 'C', '', 'ITS priority file'), - ('D', 'D', '', 'delete remote webrev'), - ('I', 'I', '', 'ITS configuration file'), - ('i', 'i', '', 'include file'), - ('N', 'N', None, 'suppress 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]'), -} diff --git a/usr/src/tools/scripts/Makefile b/usr/src/tools/scripts/Makefile index fe847412e2..e254a76a08 100644 --- a/usr/src/tools/scripts/Makefile +++ b/usr/src/tools/scripts/Makefile @@ -35,7 +35,6 @@ SHFILES= \ elfcmp \ flg.flp \ genoffsets \ - hgsetup \ nightly \ onu \ protocmp.terse \ @@ -65,7 +64,6 @@ PYFILES= \ copyrightchk \ git-pbchk \ hdrchk \ - hg-active \ mapfilechk \ validate_pkg \ wsdiff @@ -87,7 +85,6 @@ MAN1ONBLDFILES= \ hdrchk.1onbld \ interface_check.1onbld \ interface_cmp.1onbld \ - hgsetup.1onbld \ jstyle.1onbld \ mapfilechk.1onbld \ nightly.1onbld \ @@ -106,7 +103,6 @@ MAKEFILES= \ xref.mk ETCFILES= \ - hgstyle \ its.conf \ its.reg diff --git a/usr/src/tools/scripts/hg-active.py b/usr/src/tools/scripts/hg-active.py deleted file mode 100644 index 495cdfc0db..0000000000 --- a/usr/src/tools/scripts/hg-active.py +++ /dev/null @@ -1,130 +0,0 @@ -#!@PYTHON@ -# -# This program is free software; you can redistribute it and/or modify -# 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 (c) 2008, 2010, Oracle and/or its affiliates. All rights reserved. -# - -''' -Create a wx-style active list on stdout based on a Mercurial -workspace in support of webrev's Mercurial support. -''' - -# -# NB: This assumes the normal onbld directory structure -# -import sys, os - -sys.path.insert(1, os.path.join(os.path.dirname(__file__), "..", "lib", - "python%d.%d" % sys.version_info[:2])) - -# Allow running from the source tree, using the modules in the source tree -sys.path.insert(2, os.path.join(os.path.dirname(__file__), "..")) - -from onbld.Scm import Version - -try: - Version.check_version() -except Version.VersionMismatch, versionerror: - sys.stderr.write("Error: %s\n" % versionerror) - sys.exit(1) - - -import getopt, binascii -from mercurial import error, hg, ui, util -from onbld.Scm.WorkSpace import WorkSpace - - -def usage(): - sys.stderr.write("usage: %s [-p parent] -w workspace\n" % - os.path.basename(__file__)) - sys.exit(2) - - -def main(argv): - try: - opts = getopt.getopt(argv, 'w:o:p:')[0] - except getopt.GetoptError, e: - sys.stderr.write(str(e) + '\n') - usage() - - parentpath = None - wspath = None - outputfile = None - - for opt, arg in opts: - if opt == '-w': - wspath = arg - elif opt == '-o': - outputfile = arg - elif opt == '-p': - parentpath = arg - - if not wspath: - usage() - - try: - repository = hg.repository(ui.ui(), wspath) - except error.RepoError, e: - sys.stderr.write("failed to open repository: %s\n" % e) - sys.exit(1) - - ws = WorkSpace(repository) - act = ws.active(parentpath) - - node = act.parenttip.node() - parenttip = binascii.hexlify(node) - - fh = None - if outputfile: - try: - fh = open(outputfile, 'w') - except EnvironmentError, e: - sys.stderr.write("could not open output file: %s\n" % e) - sys.exit(1) - else: - fh = sys.stdout - - fh.write("HG_PARENT=%s\n" % parenttip) - - entries = [i for i in act] - entries.sort() - - for entry in entries: - if entry.is_renamed() or entry.is_copied(): - fh.write("%s %s\n" % (entry.name, entry.parentname)) - else: - fh.write("%s\n" % entry.name) - - # Strip blank lines. - comments = filter(lambda x: x and not x.isspace(), - entry.comments) - - fh.write('\n') - if comments: - fh.write('%s\n' % '\n'.join(comments)) - else: - fh.write("*** NO COMMENTS ***\n") - fh.write('\n') - -if __name__ == '__main__': - try: - main(sys.argv[1:]) - except KeyboardInterrupt: - sys.exit(1) - except util.Abort, msg: - sys.stderr.write("Abort: %s\n" % msg) - sys.exit(1) diff --git a/usr/src/tools/scripts/hgsetup.1onbld b/usr/src/tools/scripts/hgsetup.1onbld deleted file mode 100644 index 022c1a4ce3..0000000000 --- a/usr/src/tools/scripts/hgsetup.1onbld +++ /dev/null @@ -1,68 +0,0 @@ -.\" CDDL HEADER START -.\" -.\" The contents of this file are subject to the terms of the -.\" Common Development and Distribution License (the "License"). -.\" You may not use this file except in compliance with the License. -.\" -.\" You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE -.\" or http://www.opensolaris.org/os/licensing. -.\" See the License for the specific language governing permissions -.\" and limitations under the License. -.\" -.\" When distributing Covered Code, include this CDDL HEADER in each -.\" file and include the License file at usr/src/OPENSOLARIS.LICENSE. -.\" If applicable, add the following below this CDDL HEADER, with the -.\" fields enclosed by brackets "[]" replaced with your own identifying -.\" information: Portions Copyright [yyyy] [name of copyright owner] -.\" -.\" CDDL HEADER END -.\" -.\" Copyright 2008 Sun Microsystems, Inc. All rights reserved. -.\" Use is subject to license terms." -.\" -.TH HGSETUP 1ONBLD "Oct 2, 2008" -.SH NAME -.I hgsetup -\- sets up a user's ~/.hgrc for development of ON with Mercurial -.SH SYNOPSIS -\fBhgsetup [-f] [-c \fIcdm_path\fP] [-m \fImerge_path\fP] [-n \fIname\fP] [-e \fIemail\fP] [-p \fIproxy\fP] [-s \fIstyle_path\fP] -.SH DESCRIPTION -The hgsetup script sets up a user's ~/.hgrc (hgrc(5)) file for development -of ON (OS/Net) with Mercurial. -.LP -It populates the author and email fields with the user's information. -It loads the Cadmium extension. -It aliases canonical repositories for onnv-gate. -It configures Mercurial to use appropriate merge tools. -.SH OPTIONS -.TP 10 -.B -f -Force the user's ~/.hgrc to be overwritten if one already exists -.TP 10 -.B -c -Override the path to the Cadmium extension for Mercurial to load -.TP 10 -.B -m -Override the default merge configuration, such that a single -specified tool is used. -.TP 10 -.B -n -Specify the name to use for commits (default is taken from passwd file) -.TP 10 -.B -e -Specify the email address to use for commits. -For SWAN users the default is taken from an LDAP query, otherwise -e -is mandatory. -.TP 10 -.B -p -Specify an http_proxy to use for interacting with HTTP repositories -.TP 10 -.B -s -Override the path to the Mercurial style file -.SH OUTPUT -.LP -Generates a .hgrc file in the user's home directory. -.SH SEE ALSO -.LP -.IR hgrc(5), -.IR hg(1), diff --git a/usr/src/tools/scripts/hgsetup.sh b/usr/src/tools/scripts/hgsetup.sh deleted file mode 100644 index ba087a9c76..0000000000 --- a/usr/src/tools/scripts/hgsetup.sh +++ /dev/null @@ -1,189 +0,0 @@ -#! /usr/bin/ksh -# -# CDDL HEADER START -# -# The contents of this file are subject to the terms of the -# Common Development and Distribution License (the "License"). -# You may not use this file except in compliance with the License. -# -# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE -# or http://www.opensolaris.org/os/licensing. -# See the License for the specific language governing permissions -# and limitations under the License. -# -# When distributing Covered Code, include this CDDL HEADER in each -# file and include the License file at usr/src/OPENSOLARIS.LICENSE. -# If applicable, add the following below this CDDL HEADER, with the -# fields enclosed by brackets "[]" replaced with your own identifying -# information: Portions Copyright [yyyy] [name of copyright owner] -# -# CDDL HEADER END -# - -# -# Copyright 2009 Sun Microsystems, Inc. All rights reserved. -# Use is subject to license terms. -# - -# Copyright 2010, Richard Lowe - -# -# Easy setup script for populating a user's ~/.hgrc -# This currently does the following: -# * Load the cadmium extension -# * Populate the author/email fields to be correct -# * Alias canonical repositories like onnv-gate -# * Configures mercurial to use appropriate merge tools -# -# See hgrc(5) for more information -# - -HGRC=$HOME/.hgrc - -usage() { - prog=$(basename "$0") - echo \ -"usage: $prog [-f] [-c cdm_path] [-m merge_path] [-n name] [-e email] [-p proxy] [-s style_path] - -f : force overwriting $HGRC - -c cdm_path : override Cadmium path - -m merge_path : override path to merge tool - -n name : override name (for ui.username) - -e email : override email (for email.from) - -p proxy : enable use of web proxy with specified proxy - -s style_path : override path to style file - - if -n isn't provided, the entry from /etc/passwd is used - - proxy should be in the form of hostname:port - " - exit 1 -} - -while getopts c:e:fm:n:p:s: opt; do - case "$opt" in - c) cdm_path=$OPTARG;; - e) email=$OPTARG;; - f) force=1;; - m) merge_path=$OPTARG;; - n) name=$OPTARG;; - p) proxy=$OPTARG;; - s) style_path=$OPTARG;; - *) usage;; - esac -done - -if [ -f $HGRC -a "$force" -eq 0 ]; then - echo "Error: You have an existing .hgrc in $HGRC" - echo "Please move it aside." - exit 1 -fi - -AWK="/usr/xpg4/bin/awk" -SED="/usr/bin/sed" -LDAPCLIENT="/usr/bin/ldapsearch" - -login=$(/usr/bin/id -un) - -# -# Try and determine where SUNWonbld is installed. In order of -# preference, look in: -# -# 1. $(whence $0), on the assumption that you want the version -# of SUNWonbld that best matches the hgsetup script you invoked -# -# 2. /opt/onbld, because local is generally better -# -# 3. /ws/onnv-tools/onbld, it's nfs and it might be slow, but it -# should resolve from most places on-SWAN -# -paths="$(dirname $(dirname $(whence $0))) /opt/onbld /ws/onnv-tools/onbld" -cdmbin="lib/python/onbld/hgext/cdm.py" -stylefile="etc/hgstyle" - -for dir in $paths; do - if [[ -f "$dir/$cdmbin" && -z "$cdm_path" ]]; then - cdm_path="$dir/$cdmbin" - fi - - if [[ -f "$dir/$stylefile" && -z "$style_path" ]]; then - style_path="$dir/$stylefile" - fi - - if [[ -n "$cdm_path" && -n "$style_path" ]]; then - break - fi -done - -if [[ -n $proxy ]]; then - proxyConfig="[http_proxy] -host=$proxy -" -fi - -if [[ -z $email ]]; then - my_id=$(id -un) - my_hostname=$(hostname) - possible_fqhns=$(getent hosts $my_hostname | cut -f 2-) - my_fqhn=`for i in $possible_fqhns; do case $i in *.*) echo $i; break;; esac; done` - email="$my_id@$my_fqhn" - echo "No e-mail address provided, defaulting to $email" -fi - -if [[ -z "$name" ]]; then - name=${name:=$(getent passwd $login | awk -F: '{print $5}')} -fi -username="$name <$email>" - -echo "Configured the following:" -if [[ -n $proxy ]]; then - echo " proxy: $proxy" -fi -echo " email: $email" -echo " username: $name" -echo " style: $style_path" -echo " cadmium: $cdm_path" - -if [[ -z "$cdm_path" ]]; then - echo "Warning: you will need to edit your .hgrc file\n" \ - "to specify a path for cadmium." -fi - -if [[ -n $merge_path ]]; then - echo " merge: $merge_path" -fi - -cat <<EOF >$HGRC -$proxyConfig[extensions] -hgext.cdm=$cdm_path - -[email] -from=$email - -[paths] -onnv-gate=ssh://anon@hg.opensolaris.org//hg/onnv/onnv-gate -illumos-gate=ssh://anonhg@hg.illumos.org/illumos-gate - -[merge-tools] -filemerge.gui=True -filemerge.args=-a \$base \$local \$other \$output -filemerge.priority=1 -filemerge.premerge=False - -meld.gui=True -meld.priority=0 -meld.premerge=False - -gpyfm.gui=True -gpyfm.priority=0 -gpyfm.premerge=False - -[ui] -username=$username -style=$style_path -EOF - -if [[ -n $merge_path ]]; then - echo "merge=$merge_path" >> $HGRC -fi - -echo "Please check $HGRC and verify everything looks correct" diff --git a/usr/src/tools/scripts/hgstyle b/usr/src/tools/scripts/hgstyle deleted file mode 100644 index 61b4d34cde..0000000000 --- a/usr/src/tools/scripts/hgstyle +++ /dev/null @@ -1,24 +0,0 @@ -changeset = 'changeset: {rev}:{node|short}\n{branches}{tags}{parents}user: {author}\ndate: {date|date}\ndescription:\n\t{desc|strip|tabindent}\n\n' -changeset_quiet = '{rev}:{node|short}\n' -changeset_verbose = '\nchangeset: {rev}:{node|short}\n{branches}{tags}{parents}user: {author}\ndate: {date|date}\n\ndescription:\n\t{desc|strip|tabindent}\n\n{file_mods}{file_adds}{file_dels}{file_copies}\n' -changeset_debug = 'changeset: {rev}:{node}\n{branches}{tags}{parents}{manifest}{extras}user: {author}\ndate: {date|date}\ndescription:\n\t{desc|strip|tabindent}\n\n{file_mods}{file_adds}{file_dels}{file_copies}\n' -start_files = 'files:\n' -file = ' {file}\n' -end_files = '' -start_file_mods = 'modified:\n' -file_mod = ' {file_mod}\n' -end_file_mods = '' -start_file_adds = 'added:\n' -file_add = ' {file_add}\n' -end_file_adds = '' -start_file_dels = 'removed:\n' -file_del = ' {file_del}\n' -end_file_dels = '' -start_file_copies = 'copied:\n' -file_copy = ' {name}\n (from {source})\n' -end_file_copies = '' -parent = 'parent: {rev}:{node|formatnode}\n' -manifest = 'manifest: {rev}:{node}\n' -branch = 'branch: {branch}\n' -tag = 'tag: {tag}\n' -extra = 'extra: {key}={value|stringescape}\n' diff --git a/usr/src/tools/scripts/webrev.1onbld b/usr/src/tools/scripts/webrev.1onbld index 0bb65d4850..c166ea8cfa 100644 --- a/usr/src/tools/scripts/webrev.1onbld +++ b/usr/src/tools/scripts/webrev.1onbld @@ -50,7 +50,7 @@ webrev \- Generate HTML codereview materials .B webrev builds a set of HTML files suitable for performing code review of source changes in a web browser. -It supports Mercurial, Git and Subversion repositories. +It supports Git and Subversion repositories. At its most basic, usage is: .nf $ webrev @@ -140,13 +140,6 @@ makes use of .BR which_scm (1ONBLD) to determine the SCM in use for a given workspace. -.SS Mercurial -In the case of Mercurial \fBwebrev\fR will attempt to use the output -from the -.BR hg (1) -"hg root" command to identify the workspace root, and the -"hg path default" command to identify the parent workspace. - .SS Git In the case of Git \fBwebrev\fR will attempt to use the output from the .BR git (1) @@ -436,7 +429,6 @@ For the nested directory case it is necessary to specify the full target: This will remove just the \fIbugfix.onnv\fR directory. .SH SEE ALSO -.BR hg "(1)," .BR git "(1)," .BR ssh_config "(4)," .BR svn "(1)," diff --git a/usr/src/tools/scripts/webrev.sh b/usr/src/tools/scripts/webrev.sh index 934bb86129..d0fbde262c 100644 --- a/usr/src/tools/scripts/webrev.sh +++ b/usr/src/tools/scripts/webrev.sh @@ -1569,8 +1569,6 @@ source_to_html() # HTML; if the latter, embedded bugids (sequence of 5 or more digits) # are turned into URLs. # -# This is also used with Mercurial and the file list provided by hg-active. -# comments_from_wx() { typeset fmt=$1 @@ -1611,10 +1609,7 @@ getcomments() if [[ -n $Nflag ]]; then return fi - # - # Mercurial support uses a file list in wx format, so this - # will be used there, too - # + if [[ -n $wxfile ]]; then comments_from_wx $fmt $p fi @@ -1795,41 +1790,6 @@ function flist_from_wx } # -# Call hg-active to get the active list output in the wx active list format -# -function hg_active_wxfile -{ - typeset child=$1 - typeset parent=$2 - - TMPFLIST=/tmp/$$.active - $HG_ACTIVE -w $child -p $parent -o $TMPFLIST - wxfile=$TMPFLIST -} - -# -# flist_from_mercurial -# Call hg-active to get a wx-style active list, and hand it off to -# flist_from_wx -# -function flist_from_mercurial -{ - typeset child=$1 - typeset parent=$2 - - print " File list from: hg-active -p $parent ...\c" - if [[ ! -x $HG_ACTIVE ]]; then - print # Blank line for the \c above - print -u2 "Error: hg-active tool not found. Exiting" - exit 1 - fi - hg_active_wxfile $child $parent - - # flist_from_wx prints the Done, so we don't have to. - flist_from_wx $TMPFLIST -} - -# # Transform a specified 'git log' output format into a wx-like active list. # function git_wxfile @@ -1979,82 +1939,6 @@ function get_file_mode ' $1 } -function build_old_new_mercurial -{ - typeset olddir="$1" - typeset newdir="$2" - typeset old_mode= - typeset new_mode= - typeset file - - # - # Get old file mode, from the parent revision manifest entry. - # Mercurial only stores a "file is executable" flag, but the - # manifest will display an octal mode "644" or "755". - # - if [[ "$PDIR" == "." ]]; then - file="$PF" - else - file="$PDIR/$PF" - fi - file=`echo $file | $SED 's#/#\\\/#g'` - # match the exact filename, and return only the permission digits - old_mode=`$SED -n -e "/^\\(...\\) . ${file}$/s//\\1/p" \ - < $HG_PARENT_MANIFEST` - - # - # Get new file mode, directly from the filesystem. - # Normalize the mode to match Mercurial's behavior. - # - new_mode=`get_file_mode $CWS/$DIR/$F` - if [[ -n "$new_mode" ]]; then - if [[ "$new_mode" = *[1357]* ]]; then - new_mode=755 - else - new_mode=644 - fi - fi - - # - # new version of the file. - # - rm -rf $newdir/$DIR/$F - if [[ -e $CWS/$DIR/$F ]]; then - cp $CWS/$DIR/$F $newdir/$DIR/$F - if [[ -n $new_mode ]]; then - chmod $new_mode $newdir/$DIR/$F - else - # should never happen - print -u2 "ERROR: set mode of $newdir/$DIR/$F" - fi - fi - - # - # parent's version of the file - # - # Note that we get this from the last version common to both - # ourselves and the parent. References are via $CWS since we have no - # guarantee that the parent workspace is reachable via the filesystem. - # - if [[ -n $parent_webrev && -e $PWS/$PDIR/$PF ]]; then - cp $PWS/$PDIR/$PF $olddir/$PDIR/$PF - elif [[ -n $HG_PARENT ]]; then - hg cat -R $CWS -r $HG_PARENT $CWS/$PDIR/$PF > \ - $olddir/$PDIR/$PF 2>/dev/null - - if (( $? != 0 )); then - rm -f $olddir/$PDIR/$PF - else - if [[ -n $old_mode ]]; then - chmod $old_mode $olddir/$PDIR/$PF - else - # should never happen - print -u2 "ERROR: set mode of $olddir/$PDIR/$PF" - fi - fi - fi -} - function build_old_new_git { typeset olddir="$1" @@ -2169,9 +2053,7 @@ function build_old_new mkdir -p $olddir/$PDIR mkdir -p $newdir/$DIR - if [[ $SCM_MODE == "mercurial" ]]; then - build_old_new_mercurial "$olddir" "$newdir" - elif [[ $SCM_MODE == "git" ]]; then + if [[ $SCM_MODE == "git" ]]; then build_old_new_git "$olddir" "$newdir" elif [[ $SCM_MODE == "subversion" ]]; then build_old_new_subversion "$olddir" "$newdir" @@ -2237,7 +2119,6 @@ PATH=$(/bin/dirname "$(whence $0)"):$PATH [[ -z $WDIFF ]] && WDIFF=`look_for_prog wdiff` [[ -z $WX ]] && WX=`look_for_prog wx` -[[ -z $HG_ACTIVE ]] && HG_ACTIVE=`look_for_prog hg-active` [[ -z $GIT ]] && GIT=`look_for_prog git` [[ -z $WHICH_SCM ]] && WHICH_SCM=`look_for_prog which_scm` [[ -z $CODEREVIEW ]] && CODEREVIEW=`look_for_prog codereview` @@ -2310,10 +2191,6 @@ Uflag= wflag= remote_target= -# -# NOTE: when adding/removing options it is necessary to sync the list -# with usr/src/tools/onbld/hgext/cdm.py -# while getopts "c:C:Dh:i:I:lnNo:Op:t:Uw" opt do case $opt in @@ -2384,32 +2261,8 @@ fi # logic. # $WHICH_SCM | read SCM_MODE junk || exit 1 -if [[ $SCM_MODE == "mercurial" ]]; then - # - # Mercurial priorities: - # 1. hg root from CODEMGR_WS environment variable - # 1a. hg root from CODEMGR_WS/usr/closed if we're somewhere under - # usr/closed when we run webrev - # 2. hg root from directory of invocation - # - if [[ ${PWD} =~ "usr/closed" ]]; then - testparent=${CODEMGR_WS}/usr/closed - # If we're in OpenSolaris mode, we enforce a minor policy: - # help to make sure the reviewer doesn't accidentally publish - # source which is under usr/closed - if [[ -n "$Oflag" ]]; then - print -u2 "OpenSolaris output not permitted with" \ - "usr/closed changes" - exit 1 - fi - else - testparent=${CODEMGR_WS} - fi - [[ -z $codemgr_ws && -n $testparent ]] && \ - codemgr_ws=$(hg root -R $testparent 2>/dev/null) - [[ -z $codemgr_ws ]] && codemgr_ws=$(hg root 2>/dev/null) - CWS=$codemgr_ws -elif [[ $SCM_MODE == "git" ]]; then + +if [[ $SCM_MODE == "git" ]]; then # # Git priorities: # 1. git rev-parse --git-dir from CODEMGR_WS environment variable @@ -2512,7 +2365,7 @@ fi # is in use. # case "$SCM_MODE" in -mercurial|git|subversion) +git|subversion) ;; unknown) if [[ $flist_mode == "auto" ]]; then @@ -2570,95 +2423,8 @@ if [[ $# -gt 0 ]]; then print -u2 "WARNING: unused arguments: $*" fi -# -# Before we entered the DO_EVERYTHING loop, we should have already set CWS -# and CODEMGR_WS as needed. Here, we set the parent workspace. -# -if [[ $SCM_MODE == "mercurial" ]]; then - # - # Parent can either be specified with -p - # Specified with CODEMGR_PARENT in the environment - # or taken from hg's default path. - # - - if [[ -z $codemgr_parent && -n $CODEMGR_PARENT ]]; then - codemgr_parent=$CODEMGR_PARENT - fi - - if [[ -z $codemgr_parent ]]; then - codemgr_parent=`hg path -R $codemgr_ws default 2>/dev/null` - fi - - PWS=$codemgr_parent - - # - # If the parent is a webrev, we want to do some things against - # the natural workspace parent (file list, comments, etc) - # - if [[ -n $parent_webrev ]]; then - real_parent=$(hg path -R $codemgr_ws default 2>/dev/null) - else - real_parent=$PWS - fi - - # - # If hg-active exists, then we run it. In the case of no explicit - # flist given, we'll use it for our comments. In the case of an - # explicit flist given we'll try to use it for comments for any - # files mentioned in the flist. - # - if [[ -z $flist_done ]]; then - flist_from_mercurial $CWS $real_parent - flist_done=1 - fi - - # - # If we have a file list now, pull out any variables set - # therein. We do this now (rather than when we possibly use - # hg-active to find comments) to avoid stomping specifications - # in the user-specified flist. - # - if [[ -n $flist_done ]]; then - env_from_flist - fi - - # - # Only call hg-active if we don't have a wx formatted file already - # - if [[ -x $HG_ACTIVE && -z $wxfile ]]; then - print " Comments from: hg-active -p $real_parent ...\c" - hg_active_wxfile $CWS $real_parent - print " Done." - fi - - # - # At this point we must have a wx flist either from hg-active, - # or in general. Use it to try and find our parent revision, - # if we don't have one. - # - if [[ -z $HG_PARENT ]]; then - eval `$SED -e "s/#.*$//" $wxfile | $GREP HG_PARENT=` - fi - - # - # If we still don't have a parent, we must have been given a - # wx-style active list with no HG_PARENT specification, run - # hg-active and pull an HG_PARENT out of it, ignore the rest. - # - if [[ -z $HG_PARENT && -x $HG_ACTIVE ]]; then - $HG_ACTIVE -w $codemgr_ws -p $real_parent | \ - eval `$SED -e "s/#.*$//" | $GREP HG_PARENT=` - elif [[ -z $HG_PARENT ]]; then - print -u2 "Error: Cannot discover parent revision" - exit 1 - fi - pnode=$(trim_digest $HG_PARENT) - PRETTY_PWS="${PWS} (at ${pnode})" - cnode=$(hg parent -R $codemgr_ws --template '{node|short}' \ - 2>/dev/null) - PRETTY_CWS="${CWS} (at ${cnode})"} -elif [[ $SCM_MODE == "git" ]]; then +if [[ $SCM_MODE == "git" ]]; then # Check that "head" revision specified with -c or -h is sane if [[ -n $cflag || -n $hflag ]]; then head_rev=$($GIT rev-parse --verify --quiet "$codemgr_head") @@ -3075,41 +2841,6 @@ $SED -e "s/#.*$//" -e "/=/d" -e "/^[ ]*$/d" $FLIST > /tmp/$$.flist.clean FLIST=/tmp/$$.flist.clean # -# For Mercurial, create a cache of manifest entries. -# -if [[ $SCM_MODE == "mercurial" ]]; then - # - # Transform the FLIST into a temporary sed script that matches - # relevant entries in the Mercurial manifest as follows: - # 1) The script will be used against the parent revision manifest, - # so for FLIST lines that have two filenames (a renamed file) - # keep only the old name. - # 2) Escape all forward slashes the filename. - # 3) Change the filename into another sed command that matches - # that file in "hg manifest -v" output: start of line, three - # octal digits for file permissions, space, a file type flag - # character, space, the filename, end of line. - # 4) Eliminate any duplicate entries. (This can occur if a - # file has been used as the source of an hg cp and it's - # also been modified in the same changeset.) - # - SEDFILE=/tmp/$$.manifest.sed - $SED ' - s#^[^ ]* ## - s#/#\\\/#g - s#^.*$#/^... . &$/p# - ' < $FLIST | $SORT -u > $SEDFILE - - # - # Apply the generated script to the output of "hg manifest -v" - # to get the relevant subset for this webrev. - # - HG_PARENT_MANIFEST=/tmp/$$.manifest - hg -R $CWS manifest -v -r $HG_PARENT | - $SED -n -f $SEDFILE > $HG_PARENT_MANIFEST -fi - -# # First pass through the files: generate the per-file webrev HTML-files. # cat $FLIST | while read LINE @@ -3423,16 +3154,16 @@ print "<table>" # # Get the preparer's name: # -# If the SCM detected is Mercurial, and the configuration property -# ui.username is available, use that, but be careful to properly escape -# angle brackets (HTML syntax characters) in the email address. +# If the SCM detected is Git, and the configuration property user.name is +# available, use that, but be careful to properly escape angle brackets (HTML +# syntax characters) in the email address. # # Otherwise, use the current userid in the form "John Doe (jdoe)", but # to maintain compatibility with passwd(4), we must support '&' substitutions. # preparer= -if [[ "$SCM_MODE" == mercurial ]]; then - preparer=`hg showconfig ui.username 2>/dev/null` +if [[ "$SCM_MODE" == git ]]; then + preparer=$(git config user.name 2>/dev/null) if [[ -n "$preparer" ]]; then preparer="$(echo "$preparer" | html_quote)" fi @@ -3660,8 +3391,7 @@ do rm $F.count fi - if [[ $SCM_MODE == "mercurial" || - $SCM_MODE == "unknown" ]]; then + if [[ $SCM_MODE == "unknown" ]]; then # Include warnings for important file mode situations: # 1) New executable files # 2) Permission changes of any kind |
