ref: 4e5f39796d548fc1b530ae87e25f2db0830b5851
parent: e94f3a16677feafdddce8e38576eb2d6373308f9
author: Ori Bernstein <ori@eigenstate.org>
date: Fri Sep 22 11:52:08 EDT 2023
hg: actually remove hg this time
--- a/sys/src/cmd/hg/hgext/convert/__init__.py
+++ /dev/null
@@ -1,296 +1,0 @@
-# convert.py Foreign SCM converter
-#
-# Copyright 2005-2007 Matt Mackall <mpm@selenic.com>
-#
-# This software may be used and distributed according to the terms of the
-# GNU General Public License version 2, incorporated herein by reference.
-
-'''import revisions from foreign VCS repositories into Mercurial'''
-
-import convcmd
-import cvsps
-import subversion
-from mercurial import commands
-from mercurial.i18n import _
-
-# Commands definition was moved elsewhere to ease demandload job.
-
-def convert(ui, src, dest=None, revmapfile=None, **opts):
- """convert a foreign SCM repository to a Mercurial one.
-
- Accepted source formats [identifiers]:
-
- - Mercurial [hg]
- - CVS [cvs]
- - Darcs [darcs]
- - git [git]
- - Subversion [svn]
- - Monotone [mtn]
- - GNU Arch [gnuarch]
- - Bazaar [bzr]
- - Perforce [p4]
-
- Accepted destination formats [identifiers]:
-
- - Mercurial [hg]
- - Subversion [svn] (history on branches is not preserved)
-
- If no revision is given, all revisions will be converted.
- Otherwise, convert will only import up to the named revision
- (given in a format understood by the source).
-
- If no destination directory name is specified, it defaults to the
- basename of the source with '-hg' appended. If the destination
- repository doesn't exist, it will be created.
-
- By default, all sources except Mercurial will use --branchsort.
- Mercurial uses --sourcesort to preserve original revision numbers
- order. Sort modes have the following effects:
-
- --branchsort convert from parent to child revision when possible,
- which means branches are usually converted one after
- the other. It generates more compact repositories.
-
- --datesort sort revisions by date. Converted repositories have
- good-looking changelogs but are often an order of
- magnitude larger than the same ones generated by
- --branchsort.
-
- --sourcesort try to preserve source revisions order, only
- supported by Mercurial sources.
-
- If <REVMAP> isn't given, it will be put in a default location
- (<dest>/.hg/shamap by default). The <REVMAP> is a simple text file
- that maps each source commit ID to the destination ID for that
- revision, like so::
-
- <source ID> <destination ID>
-
- If the file doesn't exist, it's automatically created. It's
- updated on each commit copied, so convert-repo can be interrupted
- and can be run repeatedly to copy new commits.
-
- The [username mapping] file is a simple text file that maps each
- source commit author to a destination commit author. It is handy
- for source SCMs that use unix logins to identify authors (eg:
- CVS). One line per author mapping and the line format is:
- srcauthor=whatever string you want
-
- The filemap is a file that allows filtering and remapping of files
- and directories. Comment lines start with '#'. Each line can
- contain one of the following directives::
-
- include path/to/file
-
- exclude path/to/file
-
- rename from/file to/file
-
- The 'include' directive causes a file, or all files under a
- directory, to be included in the destination repository, and the
- exclusion of all other files and directories not explicitly
- included. The 'exclude' directive causes files or directories to
- be omitted. The 'rename' directive renames a file or directory. To
- rename from a subdirectory into the root of the repository, use
- '.' as the path to rename to.
-
- The splicemap is a file that allows insertion of synthetic
- history, letting you specify the parents of a revision. This is
- useful if you want to e.g. give a Subversion merge two parents, or
- graft two disconnected series of history together. Each entry
- contains a key, followed by a space, followed by one or two
- comma-separated values. The key is the revision ID in the source
- revision control system whose parents should be modified (same
- format as a key in .hg/shamap). The values are the revision IDs
- (in either the source or destination revision control system) that
- should be used as the new parents for that node.
-
- The branchmap is a file that allows you to rename a branch when it is
- being brought in from whatever external repository. When used in
- conjunction with a splicemap, it allows for a powerful combination
- to help fix even the most badly mismanaged repositories and turn them
- into nicely structured Mercurial repositories. The branchmap contains
- lines of the form "original_branch_name new_branch_name".
- "original_branch_name" is the name of the branch in the source
- repository, and "new_branch_name" is the name of the branch is the
- destination repository. This can be used to (for instance) move code
- in one repository from "default" to a named branch.
-
- Mercurial Source
- ----------------
-
- --config convert.hg.ignoreerrors=False (boolean)
- ignore integrity errors when reading. Use it to fix Mercurial
- repositories with missing revlogs, by converting from and to
- Mercurial.
- --config convert.hg.saverev=False (boolean)
- store original revision ID in changeset (forces target IDs to
- change)
- --config convert.hg.startrev=0 (hg revision identifier)
- convert start revision and its descendants
-
- CVS Source
- ----------
-
- CVS source will use a sandbox (i.e. a checked-out copy) from CVS
- to indicate the starting point of what will be converted. Direct
- access to the repository files is not needed, unless of course the
- repository is :local:. The conversion uses the top level directory
- in the sandbox to find the CVS repository, and then uses CVS rlog
- commands to find files to convert. This means that unless a
- filemap is given, all files under the starting directory will be
- converted, and that any directory reorganization in the CVS
- sandbox is ignored.
-
- Because CVS does not have changesets, it is necessary to collect
- individual commits to CVS and merge them into changesets. CVS
- source uses its internal changeset merging code by default but can
- be configured to call the external 'cvsps' program by setting::
-
- --config convert.cvsps='cvsps -A -u --cvs-direct -q'
-
- This option is deprecated and will be removed in Mercurial 1.4.
-
- The options shown are the defaults.
-
- Internal cvsps is selected by setting ::
-
- --config convert.cvsps=builtin
-
- and has a few more configurable options:
-
- --config convert.cvsps.cache=True (boolean)
- Set to False to disable remote log caching, for testing and
- debugging purposes.
- --config convert.cvsps.fuzz=60 (integer)
- Specify the maximum time (in seconds) that is allowed between
- commits with identical user and log message in a single
- changeset. When very large files were checked in as part of a
- changeset then the default may not be long enough.
- --config convert.cvsps.mergeto='{{mergetobranch ([-\\w]+)}}'
- Specify a regular expression to which commit log messages are
- matched. If a match occurs, then the conversion process will
- insert a dummy revision merging the branch on which this log
- message occurs to the branch indicated in the regex.
- --config convert.cvsps.mergefrom='{{mergefrombranch ([-\\w]+)}}'
- Specify a regular expression to which commit log messages are
- matched. If a match occurs, then the conversion process will
- add the most recent revision on the branch indicated in the
- regex as the second parent of the changeset.
-
- The hgext/convert/cvsps wrapper script allows the builtin
- changeset merging code to be run without doing a conversion. Its
- parameters and output are similar to that of cvsps 2.1.
-
- Subversion Source
- -----------------
-
- Subversion source detects classical trunk/branches/tags layouts.
- By default, the supplied "svn://repo/path/" source URL is
- converted as a single branch. If "svn://repo/path/trunk" exists it
- replaces the default branch. If "svn://repo/path/branches" exists,
- its subdirectories are listed as possible branches. If
- "svn://repo/path/tags" exists, it is looked for tags referencing
- converted branches. Default "trunk", "branches" and "tags" values
- can be overridden with following options. Set them to paths
- relative to the source URL, or leave them blank to disable auto
- detection.
-
- --config convert.svn.branches=branches (directory name)
- specify the directory containing branches
- --config convert.svn.tags=tags (directory name)
- specify the directory containing tags
- --config convert.svn.trunk=trunk (directory name)
- specify the name of the trunk branch
-
- Source history can be retrieved starting at a specific revision,
- instead of being integrally converted. Only single branch
- conversions are supported.
-
- --config convert.svn.startrev=0 (svn revision number)
- specify start Subversion revision.
-
- Perforce Source
- ---------------
-
- The Perforce (P4) importer can be given a p4 depot path or a
- client specification as source. It will convert all files in the
- source to a flat Mercurial repository, ignoring labels, branches
- and integrations. Note that when a depot path is given you then
- usually should specify a target directory, because otherwise the
- target may be named ...-hg.
-
- It is possible to limit the amount of source history to be
- converted by specifying an initial Perforce revision.
-
- --config convert.p4.startrev=0 (perforce changelist number)
- specify initial Perforce revision.
-
- Mercurial Destination
- ---------------------
-
- --config convert.hg.clonebranches=False (boolean)
- dispatch source branches in separate clones.
- --config convert.hg.tagsbranch=default (branch name)
- tag revisions branch name
- --config convert.hg.usebranchnames=True (boolean)
- preserve branch names
-
- """
- return convcmd.convert(ui, src, dest, revmapfile, **opts)
-
-def debugsvnlog(ui, **opts):
- return subversion.debugsvnlog(ui, **opts)
-
-def debugcvsps(ui, *args, **opts):
- '''create changeset information from CVS
-
- This command is intended as a debugging tool for the CVS to
- Mercurial converter, and can be used as a direct replacement for
- cvsps.
-
- Hg debugcvsps reads the CVS rlog for current directory (or any
- named directory) in the CVS repository, and converts the log to a
- series of changesets based on matching commit log entries and
- dates.'''
- return cvsps.debugcvsps(ui, *args, **opts)
-
-commands.norepo += " convert debugsvnlog debugcvsps"
-
-cmdtable = {
- "convert":
- (convert,
- [('A', 'authors', '', _('username mapping filename')),
- ('d', 'dest-type', '', _('destination repository type')),
- ('', 'filemap', '', _('remap file names using contents of file')),
- ('r', 'rev', '', _('import up to target revision REV')),
- ('s', 'source-type', '', _('source repository type')),
- ('', 'splicemap', '', _('splice synthesized history into place')),
- ('', 'branchmap', '', _('change branch names while converting')),
- ('', 'branchsort', None, _('try to sort changesets by branches')),
- ('', 'datesort', None, _('try to sort changesets by date')),
- ('', 'sourcesort', None, _('preserve source changesets order'))],
- _('hg convert [OPTION]... SOURCE [DEST [REVMAP]]')),
- "debugsvnlog":
- (debugsvnlog,
- [],
- 'hg debugsvnlog'),
- "debugcvsps":
- (debugcvsps,
- [
- # Main options shared with cvsps-2.1
- ('b', 'branches', [], _('only return changes on specified branches')),
- ('p', 'prefix', '', _('prefix to remove from file names')),
- ('r', 'revisions', [], _('only return changes after or between specified tags')),
- ('u', 'update-cache', None, _("update cvs log cache")),
- ('x', 'new-cache', None, _("create new cvs log cache")),
- ('z', 'fuzz', 60, _('set commit time fuzz in seconds')),
- ('', 'root', '', _('specify cvsroot')),
- # Options specific to builtin cvsps
- ('', 'parents', '', _('show parent changesets')),
- ('', 'ancestors', '', _('show current changeset in ancestor branches')),
- # Options that are ignored for compatibility with cvsps-2.1
- ('A', 'cvs-direct', None, _('ignored for compatibility')),
- ],
- _('hg debugcvsps [OPTION]... [PATH]...')),
-}
--- a/sys/src/cmd/hg/hgext/convert/bzr.py
+++ /dev/null
@@ -1,259 +1,0 @@
-# bzr.py - bzr support for the convert extension
-#
-# Copyright 2008, 2009 Marek Kubica <marek@xivilization.net> and others
-#
-# This software may be used and distributed according to the terms of the
-# GNU General Public License version 2, incorporated herein by reference.
-
-# This module is for handling 'bzr', that was formerly known as Bazaar-NG;
-# it cannot access 'bar' repositories, but they were never used very much
-
-import os
-from mercurial import demandimport
-# these do not work with demandimport, blacklist
-demandimport.ignore.extend([
- 'bzrlib.transactions',
- 'bzrlib.urlutils',
- ])
-
-from mercurial.i18n import _
-from mercurial import util
-from common import NoRepo, commit, converter_source
-
-try:
- # bazaar imports
- from bzrlib import branch, revision, errors
- from bzrlib.revisionspec import RevisionSpec
-except ImportError:
- pass
-
-supportedkinds = ('file', 'symlink')
-
-class bzr_source(converter_source):
- """Reads Bazaar repositories by using the Bazaar Python libraries"""
-
- def __init__(self, ui, path, rev=None):
- super(bzr_source, self).__init__(ui, path, rev=rev)
-
- if not os.path.exists(os.path.join(path, '.bzr')):
- raise NoRepo('%s does not look like a Bazaar repo' % path)
-
- try:
- # access bzrlib stuff
- branch
- except NameError:
- raise NoRepo('Bazaar modules could not be loaded')
-
- path = os.path.abspath(path)
- self._checkrepotype(path)
- self.branch = branch.Branch.open(path)
- self.sourcerepo = self.branch.repository
- self._parentids = {}
-
- def _checkrepotype(self, path):
- # Lightweight checkouts detection is informational but probably
- # fragile at API level. It should not terminate the conversion.
- try:
- from bzrlib import bzrdir
- dir = bzrdir.BzrDir.open_containing(path)[0]
- try:
- tree = dir.open_workingtree(recommend_upgrade=False)
- branch = tree.branch
- except (errors.NoWorkingTree, errors.NotLocalUrl), e:
- tree = None
- branch = dir.open_branch()
- if (tree is not None and tree.bzrdir.root_transport.base !=
- branch.bzrdir.root_transport.base):
- self.ui.warn(_('warning: lightweight checkouts may cause '
- 'conversion failures, try with a regular '
- 'branch instead.\n'))
- except:
- self.ui.note(_('bzr source type could not be determined\n'))
-
- def before(self):
- """Before the conversion begins, acquire a read lock
- for all the operations that might need it. Fortunately
- read locks don't block other reads or writes to the
- repository, so this shouldn't have any impact on the usage of
- the source repository.
-
- The alternative would be locking on every operation that
- needs locks (there are currently two: getting the file and
- getting the parent map) and releasing immediately after,
- but this approach can take even 40% longer."""
- self.sourcerepo.lock_read()
-
- def after(self):
- self.sourcerepo.unlock()
-
- def getheads(self):
- if not self.rev:
- return [self.branch.last_revision()]
- try:
- r = RevisionSpec.from_string(self.rev)
- info = r.in_history(self.branch)
- except errors.BzrError:
- raise util.Abort(_('%s is not a valid revision in current branch')
- % self.rev)
- return [info.rev_id]
-
- def getfile(self, name, rev):
- revtree = self.sourcerepo.revision_tree(rev)
- fileid = revtree.path2id(name.decode(self.encoding or 'utf-8'))
- kind = None
- if fileid is not None:
- kind = revtree.kind(fileid)
- if kind not in supportedkinds:
- # the file is not available anymore - was deleted
- raise IOError(_('%s is not available in %s anymore') %
- (name, rev))
- if kind == 'symlink':
- target = revtree.get_symlink_target(fileid)
- if target is None:
- raise util.Abort(_('%s.%s symlink has no target')
- % (name, rev))
- return target
- else:
- sio = revtree.get_file(fileid)
- return sio.read()
-
- def getmode(self, name, rev):
- return self._modecache[(name, rev)]
-
- def getchanges(self, version):
- # set up caches: modecache and revtree
- self._modecache = {}
- self._revtree = self.sourcerepo.revision_tree(version)
- # get the parentids from the cache
- parentids = self._parentids.pop(version)
- # only diff against first parent id
- prevtree = self.sourcerepo.revision_tree(parentids[0])
- return self._gettreechanges(self._revtree, prevtree)
-
- def getcommit(self, version):
- rev = self.sourcerepo.get_revision(version)
- # populate parent id cache
- if not rev.parent_ids:
- parents = []
- self._parentids[version] = (revision.NULL_REVISION,)
- else:
- parents = self._filterghosts(rev.parent_ids)
- self._parentids[version] = parents
-
- return commit(parents=parents,
- date='%d %d' % (rev.timestamp, -rev.timezone),
- author=self.recode(rev.committer),
- # bzr returns bytestrings or unicode, depending on the content
- desc=self.recode(rev.message),
- rev=version)
-
- def gettags(self):
- if not self.branch.supports_tags():
- return {}
- tagdict = self.branch.tags.get_tag_dict()
- bytetags = {}
- for name, rev in tagdict.iteritems():
- bytetags[self.recode(name)] = rev
- return bytetags
-
- def getchangedfiles(self, rev, i):
- self._modecache = {}
- curtree = self.sourcerepo.revision_tree(rev)
- if i is not None:
- parentid = self._parentids[rev][i]
- else:
- # no parent id, get the empty revision
- parentid = revision.NULL_REVISION
-
- prevtree = self.sourcerepo.revision_tree(parentid)
- changes = [e[0] for e in self._gettreechanges(curtree, prevtree)[0]]
- return changes
-
- def _gettreechanges(self, current, origin):
- revid = current._revision_id;
- changes = []
- renames = {}
- for (fileid, paths, changed_content, versioned, parent, name,
- kind, executable) in current.iter_changes(origin):
-
- if paths[0] == u'' or paths[1] == u'':
- # ignore changes to tree root
- continue
-
- # bazaar tracks directories, mercurial does not, so
- # we have to rename the directory contents
- if kind[1] == 'directory':
- if kind[0] not in (None, 'directory'):
- # Replacing 'something' with a directory, record it
- # so it can be removed.
- changes.append((self.recode(paths[0]), revid))
-
- if None not in paths and paths[0] != paths[1]:
- # neither an add nor an delete - a move
- # rename all directory contents manually
- subdir = origin.inventory.path2id(paths[0])
- # get all child-entries of the directory
- for name, entry in origin.inventory.iter_entries(subdir):
- # hg does not track directory renames
- if entry.kind == 'directory':
- continue
- frompath = self.recode(paths[0] + '/' + name)
- topath = self.recode(paths[1] + '/' + name)
- # register the files as changed
- changes.append((frompath, revid))
- changes.append((topath, revid))
- # add to mode cache
- mode = ((entry.executable and 'x') or (entry.kind == 'symlink' and 's')
- or '')
- self._modecache[(topath, revid)] = mode
- # register the change as move
- renames[topath] = frompath
-
- # no futher changes, go to the next change
- continue
-
- # we got unicode paths, need to convert them
- path, topath = [self.recode(part) for part in paths]
-
- if topath is None:
- # file deleted
- changes.append((path, revid))
- continue
-
- # renamed
- if path and path != topath:
- renames[topath] = path
- changes.append((path, revid))
-
- # populate the mode cache
- kind, executable = [e[1] for e in (kind, executable)]
- mode = ((executable and 'x') or (kind == 'symlink' and 'l')
- or '')
- self._modecache[(topath, revid)] = mode
- changes.append((topath, revid))
-
- return changes, renames
-
- def _filterghosts(self, ids):
- """Filters out ghost revisions which hg does not support, see
- <http://bazaar-vcs.org/GhostRevision>
- """
- parentmap = self.sourcerepo.get_parent_map(ids)
- parents = tuple([parent for parent in ids if parent in parentmap])
- return parents
-
- def recode(self, s, encoding=None):
- """This version of recode tries to encode unicode to bytecode,
- and preferably using the UTF-8 codec.
- Other types than Unicode are silently returned, this is by
- intention, e.g. the None-type is not going to be encoded but instead
- just passed through
- """
- if not encoding:
- encoding = self.encoding or 'utf-8'
-
- if isinstance(s, unicode):
- return s.encode(encoding)
- else:
- # leave it alone
- return s
--- a/sys/src/cmd/hg/hgext/convert/common.py
+++ /dev/null
@@ -1,389 +1,0 @@
-# common.py - common code for the convert extension
-#
-# Copyright 2005-2009 Matt Mackall <mpm@selenic.com> and others
-#
-# This software may be used and distributed according to the terms of the
-# GNU General Public License version 2, incorporated herein by reference.
-
-import base64, errno
-import os
-import cPickle as pickle
-from mercurial import util
-from mercurial.i18n import _
-
-def encodeargs(args):
- def encodearg(s):
- lines = base64.encodestring(s)
- lines = [l.splitlines()[0] for l in lines]
- return ''.join(lines)
-
- s = pickle.dumps(args)
- return encodearg(s)
-
-def decodeargs(s):
- s = base64.decodestring(s)
- return pickle.loads(s)
-
-class MissingTool(Exception): pass
-
-def checktool(exe, name=None, abort=True):
- name = name or exe
- if not util.find_exe(exe):
- exc = abort and util.Abort or MissingTool
- raise exc(_('cannot find required "%s" tool') % name)
-
-class NoRepo(Exception): pass
-
-SKIPREV = 'SKIP'
-
-class commit(object):
- def __init__(self, author, date, desc, parents, branch=None, rev=None,
- extra={}, sortkey=None):
- self.author = author or 'unknown'
- self.date = date or '0 0'
- self.desc = desc
- self.parents = parents
- self.branch = branch
- self.rev = rev
- self.extra = extra
- self.sortkey = sortkey
-
-class converter_source(object):
- """Conversion source interface"""
-
- def __init__(self, ui, path=None, rev=None):
- """Initialize conversion source (or raise NoRepo("message")
- exception if path is not a valid repository)"""
- self.ui = ui
- self.path = path
- self.rev = rev
-
- self.encoding = 'utf-8'
-
- def before(self):
- pass
-
- def after(self):
- pass
-
- def setrevmap(self, revmap):
- """set the map of already-converted revisions"""
- pass
-
- def getheads(self):
- """Return a list of this repository's heads"""
- raise NotImplementedError()
-
- def getfile(self, name, rev):
- """Return file contents as a string. rev is the identifier returned
- by a previous call to getchanges(). Raise IOError to indicate that
- name was deleted in rev.
- """
- raise NotImplementedError()
-
- def getmode(self, name, rev):
- """Return file mode, eg. '', 'x', or 'l'. rev is the identifier
- returned by a previous call to getchanges().
- """
- raise NotImplementedError()
-
- def getchanges(self, version):
- """Returns a tuple of (files, copies).
-
- files is a sorted list of (filename, id) tuples for all files
- changed between version and its first parent returned by
- getcommit(). id is the source revision id of the file.
-
- copies is a dictionary of dest: source
- """
- raise NotImplementedError()
-
- def getcommit(self, version):
- """Return the commit object for version"""
- raise NotImplementedError()
-
- def gettags(self):
- """Return the tags as a dictionary of name: revision
-
- Tag names must be UTF-8 strings.
- """
- raise NotImplementedError()
-
- def recode(self, s, encoding=None):
- if not encoding:
- encoding = self.encoding or 'utf-8'
-
- if isinstance(s, unicode):
- return s.encode("utf-8")
- try:
- return s.decode(encoding).encode("utf-8")
- except:
- try:
- return s.decode("latin-1").encode("utf-8")
- except:
- return s.decode(encoding, "replace").encode("utf-8")
-
- def getchangedfiles(self, rev, i):
- """Return the files changed by rev compared to parent[i].
-
- i is an index selecting one of the parents of rev. The return
- value should be the list of files that are different in rev and
- this parent.
-
- If rev has no parents, i is None.
-
- This function is only needed to support --filemap
- """
- raise NotImplementedError()
-
- def converted(self, rev, sinkrev):
- '''Notify the source that a revision has been converted.'''
- pass
-
- def hasnativeorder(self):
- """Return true if this source has a meaningful, native revision
- order. For instance, Mercurial revisions are store sequentially
- while there is no such global ordering with Darcs.
- """
- return False
-
- def lookuprev(self, rev):
- """If rev is a meaningful revision reference in source, return
- the referenced identifier in the same format used by getcommit().
- return None otherwise.
- """
- return None
-
-class converter_sink(object):
- """Conversion sink (target) interface"""
-
- def __init__(self, ui, path):
- """Initialize conversion sink (or raise NoRepo("message")
- exception if path is not a valid repository)
-
- created is a list of paths to remove if a fatal error occurs
- later"""
- self.ui = ui
- self.path = path
- self.created = []
-
- def getheads(self):
- """Return a list of this repository's heads"""
- raise NotImplementedError()
-
- def revmapfile(self):
- """Path to a file that will contain lines
- source_rev_id sink_rev_id
- mapping equivalent revision identifiers for each system."""
- raise NotImplementedError()
-
- def authorfile(self):
- """Path to a file that will contain lines
- srcauthor=dstauthor
- mapping equivalent authors identifiers for each system."""
- return None
-
- def putcommit(self, files, copies, parents, commit, source, revmap):
- """Create a revision with all changed files listed in 'files'
- and having listed parents. 'commit' is a commit object
- containing at a minimum the author, date, and message for this
- changeset. 'files' is a list of (path, version) tuples,
- 'copies' is a dictionary mapping destinations to sources,
- 'source' is the source repository, and 'revmap' is a mapfile
- of source revisions to converted revisions. Only getfile(),
- getmode(), and lookuprev() should be called on 'source'.
-
- Note that the sink repository is not told to update itself to
- a particular revision (or even what that revision would be)
- before it receives the file data.
- """
- raise NotImplementedError()
-
- def puttags(self, tags):
- """Put tags into sink.
-
- tags: {tagname: sink_rev_id, ...} where tagname is an UTF-8 string.
- """
- raise NotImplementedError()
-
- def setbranch(self, branch, pbranches):
- """Set the current branch name. Called before the first putcommit
- on the branch.
- branch: branch name for subsequent commits
- pbranches: (converted parent revision, parent branch) tuples"""
- pass
-
- def setfilemapmode(self, active):
- """Tell the destination that we're using a filemap
-
- Some converter_sources (svn in particular) can claim that a file
- was changed in a revision, even if there was no change. This method
- tells the destination that we're using a filemap and that it should
- filter empty revisions.
- """
- pass
-
- def before(self):
- pass
-
- def after(self):
- pass
-
-
-class commandline(object):
- def __init__(self, ui, command):
- self.ui = ui
- self.command = command
-
- def prerun(self):
- pass
-
- def postrun(self):
- pass
-
- def _cmdline(self, cmd, *args, **kwargs):
- cmdline = [self.command, cmd] + list(args)
- for k, v in kwargs.iteritems():
- if len(k) == 1:
- cmdline.append('-' + k)
- else:
- cmdline.append('--' + k.replace('_', '-'))
- try:
- if len(k) == 1:
- cmdline.append('' + v)
- else:
- cmdline[-1] += '=' + v
- except TypeError:
- pass
- cmdline = [util.shellquote(arg) for arg in cmdline]
- if not self.ui.debugflag:
- cmdline += ['2>', util.nulldev]
- cmdline += ['<', util.nulldev]
- cmdline = ' '.join(cmdline)
- return cmdline
-
- def _run(self, cmd, *args, **kwargs):
- cmdline = self._cmdline(cmd, *args, **kwargs)
- self.ui.debug(_('running: %s\n') % (cmdline,))
- self.prerun()
- try:
- return util.popen(cmdline)
- finally:
- self.postrun()
-
- def run(self, cmd, *args, **kwargs):
- fp = self._run(cmd, *args, **kwargs)
- output = fp.read()
- self.ui.debug(output)
- return output, fp.close()
-
- def runlines(self, cmd, *args, **kwargs):
- fp = self._run(cmd, *args, **kwargs)
- output = fp.readlines()
- self.ui.debug(''.join(output))
- return output, fp.close()
-
- def checkexit(self, status, output=''):
- if status:
- if output:
- self.ui.warn(_('%s error:\n') % self.command)
- self.ui.warn(output)
- msg = util.explain_exit(status)[0]
- raise util.Abort('%s %s' % (self.command, msg))
-
- def run0(self, cmd, *args, **kwargs):
- output, status = self.run(cmd, *args, **kwargs)
- self.checkexit(status, output)
- return output
-
- def runlines0(self, cmd, *args, **kwargs):
- output, status = self.runlines(cmd, *args, **kwargs)
- self.checkexit(status, ''.join(output))
- return output
-
- def getargmax(self):
- if '_argmax' in self.__dict__:
- return self._argmax
-
- # POSIX requires at least 4096 bytes for ARG_MAX
- self._argmax = 4096
- try:
- self._argmax = os.sysconf("SC_ARG_MAX")
- except:
- pass
-
- # Windows shells impose their own limits on command line length,
- # down to 2047 bytes for cmd.exe under Windows NT/2k and 2500 bytes
- # for older 4nt.exe. See http://support.microsoft.com/kb/830473 for
- # details about cmd.exe limitations.
-
- # Since ARG_MAX is for command line _and_ environment, lower our limit
- # (and make happy Windows shells while doing this).
-
- self._argmax = self._argmax/2 - 1
- return self._argmax
-
- def limit_arglist(self, arglist, cmd, *args, **kwargs):
- limit = self.getargmax() - len(self._cmdline(cmd, *args, **kwargs))
- bytes = 0
- fl = []
- for fn in arglist:
- b = len(fn) + 3
- if bytes + b < limit or len(fl) == 0:
- fl.append(fn)
- bytes += b
- else:
- yield fl
- fl = [fn]
- bytes = b
- if fl:
- yield fl
-
- def xargs(self, arglist, cmd, *args, **kwargs):
- for l in self.limit_arglist(arglist, cmd, *args, **kwargs):
- self.run0(cmd, *(list(args) + l), **kwargs)
-
-class mapfile(dict):
- def __init__(self, ui, path):
- super(mapfile, self).__init__()
- self.ui = ui
- self.path = path
- self.fp = None
- self.order = []
- self._read()
-
- def _read(self):
- if not self.path:
- return
- try:
- fp = open(self.path, 'r')
- except IOError, err:
- if err.errno != errno.ENOENT:
- raise
- return
- for i, line in enumerate(fp):
- try:
- key, value = line[:-1].rsplit(' ', 1)
- except ValueError:
- raise util.Abort(_('syntax error in %s(%d): key/value pair expected')
- % (self.path, i+1))
- if key not in self:
- self.order.append(key)
- super(mapfile, self).__setitem__(key, value)
- fp.close()
-
- def __setitem__(self, key, value):
- if self.fp is None:
- try:
- self.fp = open(self.path, 'a')
- except IOError, err:
- raise util.Abort(_('could not open map file %r: %s') %
- (self.path, err.strerror))
- self.fp.write('%s %s\n' % (key, value))
- self.fp.flush()
- super(mapfile, self).__setitem__(key, value)
-
- def close(self):
- if self.fp:
- self.fp.close()
- self.fp = None
--- a/sys/src/cmd/hg/hgext/convert/convcmd.py
+++ /dev/null
@@ -1,396 +1,0 @@
-# convcmd - convert extension commands definition
-#
-# Copyright 2005-2007 Matt Mackall <mpm@selenic.com>
-#
-# This software may be used and distributed according to the terms of the
-# GNU General Public License version 2, incorporated herein by reference.
-
-from common import NoRepo, MissingTool, SKIPREV, mapfile
-from cvs import convert_cvs
-from darcs import darcs_source
-from git import convert_git
-from hg import mercurial_source, mercurial_sink
-from subversion import svn_source, svn_sink
-from monotone import monotone_source
-from gnuarch import gnuarch_source
-from bzr import bzr_source
-from p4 import p4_source
-import filemap
-
-import os, shutil
-from mercurial import hg, util, encoding
-from mercurial.i18n import _
-
-orig_encoding = 'ascii'
-
-def recode(s):
- if isinstance(s, unicode):
- return s.encode(orig_encoding, 'replace')
- else:
- return s.decode('utf-8').encode(orig_encoding, 'replace')
-
-source_converters = [
- ('cvs', convert_cvs, 'branchsort'),
- ('git', convert_git, 'branchsort'),
- ('svn', svn_source, 'branchsort'),
- ('hg', mercurial_source, 'sourcesort'),
- ('darcs', darcs_source, 'branchsort'),
- ('mtn', monotone_source, 'branchsort'),
- ('gnuarch', gnuarch_source, 'branchsort'),
- ('bzr', bzr_source, 'branchsort'),
- ('p4', p4_source, 'branchsort'),
- ]
-
-sink_converters = [
- ('hg', mercurial_sink),
- ('svn', svn_sink),
- ]
-
-def convertsource(ui, path, type, rev):
- exceptions = []
- for name, source, sortmode in source_converters:
- try:
- if not type or name == type:
- return source(ui, path, rev), sortmode
- except (NoRepo, MissingTool), inst:
- exceptions.append(inst)
- if not ui.quiet:
- for inst in exceptions:
- ui.write("%s\n" % inst)
- raise util.Abort(_('%s: missing or unsupported repository') % path)
-
-def convertsink(ui, path, type):
- for name, sink in sink_converters:
- try:
- if not type or name == type:
- return sink(ui, path)
- except NoRepo, inst:
- ui.note(_("convert: %s\n") % inst)
- raise util.Abort(_('%s: unknown repository type') % path)
-
-class converter(object):
- def __init__(self, ui, source, dest, revmapfile, opts):
-
- self.source = source
- self.dest = dest
- self.ui = ui
- self.opts = opts
- self.commitcache = {}
- self.authors = {}
- self.authorfile = None
-
- # Record converted revisions persistently: maps source revision
- # ID to target revision ID (both strings). (This is how
- # incremental conversions work.)
- self.map = mapfile(ui, revmapfile)
-
- # Read first the dst author map if any
- authorfile = self.dest.authorfile()
- if authorfile and os.path.exists(authorfile):
- self.readauthormap(authorfile)
- # Extend/Override with new author map if necessary
- if opts.get('authors'):
- self.readauthormap(opts.get('authors'))
- self.authorfile = self.dest.authorfile()
-
- self.splicemap = mapfile(ui, opts.get('splicemap'))
- self.branchmap = mapfile(ui, opts.get('branchmap'))
-
- def walktree(self, heads):
- '''Return a mapping that identifies the uncommitted parents of every
- uncommitted changeset.'''
- visit = heads
- known = set()
- parents = {}
- while visit:
- n = visit.pop(0)
- if n in known or n in self.map: continue
- known.add(n)
- commit = self.cachecommit(n)
- parents[n] = []
- for p in commit.parents:
- parents[n].append(p)
- visit.append(p)
-
- return parents
-
- def toposort(self, parents, sortmode):
- '''Return an ordering such that every uncommitted changeset is
- preceeded by all its uncommitted ancestors.'''
-
- def mapchildren(parents):
- """Return a (children, roots) tuple where 'children' maps parent
- revision identifiers to children ones, and 'roots' is the list of
- revisions without parents. 'parents' must be a mapping of revision
- identifier to its parents ones.
- """
- visit = parents.keys()
- seen = set()
- children = {}
- roots = []
-
- while visit:
- n = visit.pop(0)
- if n in seen:
- continue
- seen.add(n)
- # Ensure that nodes without parents are present in the
- # 'children' mapping.
- children.setdefault(n, [])
- hasparent = False
- for p in parents[n]:
- if not p in self.map:
- visit.append(p)
- hasparent = True
- children.setdefault(p, []).append(n)
- if not hasparent:
- roots.append(n)
-
- return children, roots
-
- # Sort functions are supposed to take a list of revisions which
- # can be converted immediately and pick one
-
- def makebranchsorter():
- """If the previously converted revision has a child in the
- eligible revisions list, pick it. Return the list head
- otherwise. Branch sort attempts to minimize branch
- switching, which is harmful for Mercurial backend
- compression.
- """
- prev = [None]
- def picknext(nodes):
- next = nodes[0]
- for n in nodes:
- if prev[0] in parents[n]:
- next = n
- break
- prev[0] = next
- return next
- return picknext
-
- def makesourcesorter():
- """Source specific sort."""
- keyfn = lambda n: self.commitcache[n].sortkey
- def picknext(nodes):
- return sorted(nodes, key=keyfn)[0]
- return picknext
-
- def makedatesorter():
- """Sort revisions by date."""
- dates = {}
- def getdate(n):
- if n not in dates:
- dates[n] = util.parsedate(self.commitcache[n].date)
- return dates[n]
-
- def picknext(nodes):
- return min([(getdate(n), n) for n in nodes])[1]
-
- return picknext
-
- if sortmode == 'branchsort':
- picknext = makebranchsorter()
- elif sortmode == 'datesort':
- picknext = makedatesorter()
- elif sortmode == 'sourcesort':
- picknext = makesourcesorter()
- else:
- raise util.Abort(_('unknown sort mode: %s') % sortmode)
-
- children, actives = mapchildren(parents)
-
- s = []
- pendings = {}
- while actives:
- n = picknext(actives)
- actives.remove(n)
- s.append(n)
-
- # Update dependents list
- for c in children.get(n, []):
- if c not in pendings:
- pendings[c] = [p for p in parents[c] if p not in self.map]
- try:
- pendings[c].remove(n)
- except ValueError:
- raise util.Abort(_('cycle detected between %s and %s')
- % (recode(c), recode(n)))
- if not pendings[c]:
- # Parents are converted, node is eligible
- actives.insert(0, c)
- pendings[c] = None
-
- if len(s) != len(parents):
- raise util.Abort(_("not all revisions were sorted"))
-
- return s
-
- def writeauthormap(self):
- authorfile = self.authorfile
- if authorfile:
- self.ui.status(_('Writing author map file %s\n') % authorfile)
- ofile = open(authorfile, 'w+')
- for author in self.authors:
- ofile.write("%s=%s\n" % (author, self.authors[author]))
- ofile.close()
-
- def readauthormap(self, authorfile):
- afile = open(authorfile, 'r')
- for line in afile:
-
- line = line.strip()
- if not line or line.startswith('#'):
- continue
-
- try:
- srcauthor, dstauthor = line.split('=', 1)
- except ValueError:
- msg = _('Ignoring bad line in author map file %s: %s\n')
- self.ui.warn(msg % (authorfile, line.rstrip()))
- continue
-
- srcauthor = srcauthor.strip()
- dstauthor = dstauthor.strip()
- if self.authors.get(srcauthor) in (None, dstauthor):
- msg = _('mapping author %s to %s\n')
- self.ui.debug(msg % (srcauthor, dstauthor))
- self.authors[srcauthor] = dstauthor
- continue
-
- m = _('overriding mapping for author %s, was %s, will be %s\n')
- self.ui.status(m % (srcauthor, self.authors[srcauthor], dstauthor))
-
- afile.close()
-
- def cachecommit(self, rev):
- commit = self.source.getcommit(rev)
- commit.author = self.authors.get(commit.author, commit.author)
- commit.branch = self.branchmap.get(commit.branch, commit.branch)
- self.commitcache[rev] = commit
- return commit
-
- def copy(self, rev):
- commit = self.commitcache[rev]
-
- changes = self.source.getchanges(rev)
- if isinstance(changes, basestring):
- if changes == SKIPREV:
- dest = SKIPREV
- else:
- dest = self.map[changes]
- self.map[rev] = dest
- return
- files, copies = changes
- pbranches = []
- if commit.parents:
- for prev in commit.parents:
- if prev not in self.commitcache:
- self.cachecommit(prev)
- pbranches.append((self.map[prev],
- self.commitcache[prev].branch))
- self.dest.setbranch(commit.branch, pbranches)
- try:
- parents = self.splicemap[rev].replace(',', ' ').split()
- self.ui.status(_('spliced in %s as parents of %s\n') %
- (parents, rev))
- parents = [self.map.get(p, p) for p in parents]
- except KeyError:
- parents = [b[0] for b in pbranches]
- newnode = self.dest.putcommit(files, copies, parents, commit,
- self.source, self.map)
- self.source.converted(rev, newnode)
- self.map[rev] = newnode
-
- def convert(self, sortmode):
- try:
- self.source.before()
- self.dest.before()
- self.source.setrevmap(self.map)
- self.ui.status(_("scanning source...\n"))
- heads = self.source.getheads()
- parents = self.walktree(heads)
- self.ui.status(_("sorting...\n"))
- t = self.toposort(parents, sortmode)
- num = len(t)
- c = None
-
- self.ui.status(_("converting...\n"))
- for c in t:
- num -= 1
- desc = self.commitcache[c].desc
- if "\n" in desc:
- desc = desc.splitlines()[0]
- # convert log message to local encoding without using
- # tolocal() because encoding.encoding conver() use it as
- # 'utf-8'
- self.ui.status("%d %s\n" % (num, recode(desc)))
- self.ui.note(_("source: %s\n") % recode(c))
- self.copy(c)
-
- tags = self.source.gettags()
- ctags = {}
- for k in tags:
- v = tags[k]
- if self.map.get(v, SKIPREV) != SKIPREV:
- ctags[k] = self.map[v]
-
- if c and ctags:
- nrev = self.dest.puttags(ctags)
- # write another hash correspondence to override the previous
- # one so we don't end up with extra tag heads
- if nrev:
- self.map[c] = nrev
-
- self.writeauthormap()
- finally:
- self.cleanup()
-
- def cleanup(self):
- try:
- self.dest.after()
- finally:
- self.source.after()
- self.map.close()
-
-def convert(ui, src, dest=None, revmapfile=None, **opts):
- global orig_encoding
- orig_encoding = encoding.encoding
- encoding.encoding = 'UTF-8'
-
- if not dest:
- dest = hg.defaultdest(src) + "-hg"
- ui.status(_("assuming destination %s\n") % dest)
-
- destc = convertsink(ui, dest, opts.get('dest_type'))
-
- try:
- srcc, defaultsort = convertsource(ui, src, opts.get('source_type'),
- opts.get('rev'))
- except Exception:
- for path in destc.created:
- shutil.rmtree(path, True)
- raise
-
- sortmodes = ('branchsort', 'datesort', 'sourcesort')
- sortmode = [m for m in sortmodes if opts.get(m)]
- if len(sortmode) > 1:
- raise util.Abort(_('more than one sort mode specified'))
- sortmode = sortmode and sortmode[0] or defaultsort
- if sortmode == 'sourcesort' and not srcc.hasnativeorder():
- raise util.Abort(_('--sourcesort is not supported by this data source'))
-
- fmap = opts.get('filemap')
- if fmap:
- srcc = filemap.filemap_source(ui, srcc, fmap)
- destc.setfilemapmode(True)
-
- if not revmapfile:
- try:
- revmapfile = destc.revmapfile()
- except:
- revmapfile = os.path.join(destc, "map")
-
- c = converter(ui, srcc, destc, revmapfile, opts)
- c.convert(sortmode)
-
--- a/sys/src/cmd/hg/hgext/convert/cvs.py
+++ /dev/null
@@ -1,372 +1,0 @@
-# cvs.py: CVS conversion code inspired by hg-cvs-import and git-cvsimport
-#
-# Copyright 2005-2009 Matt Mackall <mpm@selenic.com> and others
-#
-# This software may be used and distributed according to the terms of the
-# GNU General Public License version 2, incorporated herein by reference.
-
-import os, locale, re, socket, errno
-from cStringIO import StringIO
-from mercurial import util
-from mercurial.i18n import _
-
-from common import NoRepo, commit, converter_source, checktool
-import cvsps
-
-class convert_cvs(converter_source):
- def __init__(self, ui, path, rev=None):
- super(convert_cvs, self).__init__(ui, path, rev=rev)
-
- cvs = os.path.join(path, "CVS")
- if not os.path.exists(cvs):
- raise NoRepo("%s does not look like a CVS checkout" % path)
-
- checktool('cvs')
- self.cmd = ui.config('convert', 'cvsps', 'builtin')
- cvspsexe = self.cmd.split(None, 1)[0]
- self.builtin = cvspsexe == 'builtin'
- if not self.builtin:
- ui.warn(_('warning: support for external cvsps is deprecated and '
- 'will be removed in Mercurial 1.4\n'))
-
- if not self.builtin:
- checktool(cvspsexe)
-
- self.changeset = None
- self.files = {}
- self.tags = {}
- self.lastbranch = {}
- self.parent = {}
- self.socket = None
- self.cvsroot = open(os.path.join(cvs, "Root")).read()[:-1]
- self.cvsrepo = open(os.path.join(cvs, "Repository")).read()[:-1]
- self.encoding = locale.getpreferredencoding()
-
- self._connect()
-
- def _parse(self):
- if self.changeset is not None:
- return
- self.changeset = {}
-
- maxrev = 0
- cmd = self.cmd
- if self.rev:
- # TODO: handle tags
- try:
- # patchset number?
- maxrev = int(self.rev)
- except ValueError:
- try:
- # date
- util.parsedate(self.rev, ['%Y/%m/%d %H:%M:%S'])
- cmd = '%s -d "1970/01/01 00:00:01" -d "%s"' % (cmd, self.rev)
- except util.Abort:
- raise util.Abort(_('revision %s is not a patchset number or date') % self.rev)
-
- d = os.getcwd()
- try:
- os.chdir(self.path)
- id = None
- state = 0
- filerevids = {}
-
- if self.builtin:
- # builtin cvsps code
- self.ui.status(_('using builtin cvsps\n'))
-
- cache = 'update'
- if not self.ui.configbool('convert', 'cvsps.cache', True):
- cache = None
- db = cvsps.createlog(self.ui, cache=cache)
- db = cvsps.createchangeset(self.ui, db,
- fuzz=int(self.ui.config('convert', 'cvsps.fuzz', 60)),
- mergeto=self.ui.config('convert', 'cvsps.mergeto', None),
- mergefrom=self.ui.config('convert', 'cvsps.mergefrom', None))
-
- for cs in db:
- if maxrev and cs.id>maxrev:
- break
- id = str(cs.id)
- cs.author = self.recode(cs.author)
- self.lastbranch[cs.branch] = id
- cs.comment = self.recode(cs.comment)
- date = util.datestr(cs.date)
- self.tags.update(dict.fromkeys(cs.tags, id))
-
- files = {}
- for f in cs.entries:
- files[f.file] = "%s%s" % ('.'.join([str(x) for x in f.revision]),
- ['', '(DEAD)'][f.dead])
-
- # add current commit to set
- c = commit(author=cs.author, date=date,
- parents=[str(p.id) for p in cs.parents],
- desc=cs.comment, branch=cs.branch or '')
- self.changeset[id] = c
- self.files[id] = files
- else:
- # external cvsps
- for l in util.popen(cmd):
- if state == 0: # header
- if l.startswith("PatchSet"):
- id = l[9:-2]
- if maxrev and int(id) > maxrev:
- # ignore everything
- state = 3
- elif l.startswith("Date:"):
- date = util.parsedate(l[6:-1], ["%Y/%m/%d %H:%M:%S"])
- date = util.datestr(date)
- elif l.startswith("Branch:"):
- branch = l[8:-1]
- self.parent[id] = self.lastbranch.get(branch, 'bad')
- self.lastbranch[branch] = id
- elif l.startswith("Ancestor branch:"):
- ancestor = l[17:-1]
- # figure out the parent later
- self.parent[id] = self.lastbranch[ancestor]
- elif l.startswith("Author:"):
- author = self.recode(l[8:-1])
- elif l.startswith("Tag:") or l.startswith("Tags:"):
- t = l[l.index(':')+1:]
- t = [ut.strip() for ut in t.split(',')]
- if (len(t) > 1) or (t[0] and (t[0] != "(none)")):
- self.tags.update(dict.fromkeys(t, id))
- elif l.startswith("Log:"):
- # switch to gathering log
- state = 1
- log = ""
- elif state == 1: # log
- if l == "Members: \n":
- # switch to gathering members
- files = {}
- oldrevs = []
- log = self.recode(log[:-1])
- state = 2
- else:
- # gather log
- log += l
- elif state == 2: # members
- if l == "\n": # start of next entry
- state = 0
- p = [self.parent[id]]
- if id == "1":
- p = []
- if branch == "HEAD":
- branch = ""
- if branch:
- latest = 0
- # the last changeset that contains a base
- # file is our parent
- for r in oldrevs:
- latest = max(filerevids.get(r, 0), latest)
- if latest:
- p = [latest]
-
- # add current commit to set
- c = commit(author=author, date=date, parents=p,
- desc=log, branch=branch)
- self.changeset[id] = c
- self.files[id] = files
- else:
- colon = l.rfind(':')
- file = l[1:colon]
- rev = l[colon+1:-2]
- oldrev, rev = rev.split("->")
- files[file] = rev
-
- # save some information for identifying branch points
- oldrevs.append("%s:%s" % (oldrev, file))
- filerevids["%s:%s" % (rev, file)] = id
- elif state == 3:
- # swallow all input
- continue
-
- self.heads = self.lastbranch.values()
- finally:
- os.chdir(d)
-
- def _connect(self):
- root = self.cvsroot
- conntype = None
- user, host = None, None
- cmd = ['cvs', 'server']
-
- self.ui.status(_("connecting to %s\n") % root)
-
- if root.startswith(":pserver:"):
- root = root[9:]
- m = re.match(r'(?:(.*?)(?::(.*?))?@)?([^:\/]*)(?::(\d*))?(.*)',
- root)
- if m:
- conntype = "pserver"
- user, passw, serv, port, root = m.groups()
- if not user:
- user = "anonymous"
- if not port:
- port = 2401
- else:
- port = int(port)
- format0 = ":pserver:%s@%s:%s" % (user, serv, root)
- format1 = ":pserver:%s@%s:%d%s" % (user, serv, port, root)
-
- if not passw:
- passw = "A"
- cvspass = os.path.expanduser("~/.cvspass")
- try:
- pf = open(cvspass)
- for line in pf.read().splitlines():
- part1, part2 = line.split(' ', 1)
- if part1 == '/1':
- # /1 :pserver:user@example.com:2401/cvsroot/foo Ah<Z
- part1, part2 = part2.split(' ', 1)
- format = format1
- else:
- # :pserver:user@example.com:/cvsroot/foo Ah<Z
- format = format0
- if part1 == format:
- passw = part2
- break
- pf.close()
- except IOError, inst:
- if inst.errno != errno.ENOENT:
- if not getattr(inst, 'filename', None):
- inst.filename = cvspass
- raise
-
- sck = socket.socket()
- sck.connect((serv, port))
- sck.send("\n".join(["BEGIN AUTH REQUEST", root, user, passw,
- "END AUTH REQUEST", ""]))
- if sck.recv(128) != "I LOVE YOU\n":
- raise util.Abort(_("CVS pserver authentication failed"))
-
- self.writep = self.readp = sck.makefile('r+')
-
- if not conntype and root.startswith(":local:"):
- conntype = "local"
- root = root[7:]
-
- if not conntype:
- # :ext:user@host/home/user/path/to/cvsroot
- if root.startswith(":ext:"):
- root = root[5:]
- m = re.match(r'(?:([^@:/]+)@)?([^:/]+):?(.*)', root)
- # Do not take Windows path "c:\foo\bar" for a connection strings
- if os.path.isdir(root) or not m:
- conntype = "local"
- else:
- conntype = "rsh"
- user, host, root = m.group(1), m.group(2), m.group(3)
-
- if conntype != "pserver":
- if conntype == "rsh":
- rsh = os.environ.get("CVS_RSH") or "ssh"
- if user:
- cmd = [rsh, '-l', user, host] + cmd
- else:
- cmd = [rsh, host] + cmd
-
- # popen2 does not support argument lists under Windows
- cmd = [util.shellquote(arg) for arg in cmd]
- cmd = util.quotecommand(' '.join(cmd))
- self.writep, self.readp = util.popen2(cmd)
-
- self.realroot = root
-
- self.writep.write("Root %s\n" % root)
- self.writep.write("Valid-responses ok error Valid-requests Mode"
- " M Mbinary E Checked-in Created Updated"
- " Merged Removed\n")
- self.writep.write("valid-requests\n")
- self.writep.flush()
- r = self.readp.readline()
- if not r.startswith("Valid-requests"):
- raise util.Abort(_("unexpected response from CVS server "
- "(expected \"Valid-requests\", but got %r)")
- % r)
- if "UseUnchanged" in r:
- self.writep.write("UseUnchanged\n")
- self.writep.flush()
- r = self.readp.readline()
-
- def getheads(self):
- self._parse()
- return self.heads
-
- def _getfile(self, name, rev):
-
- def chunkedread(fp, count):
- # file-objects returned by socked.makefile() do not handle
- # large read() requests very well.
- chunksize = 65536
- output = StringIO()
- while count > 0:
- data = fp.read(min(count, chunksize))
- if not data:
- raise util.Abort(_("%d bytes missing from remote file") % count)
- count -= len(data)
- output.write(data)
- return output.getvalue()
-
- if rev.endswith("(DEAD)"):
- raise IOError
-
- args = ("-N -P -kk -r %s --" % rev).split()
- args.append(self.cvsrepo + '/' + name)
- for x in args:
- self.writep.write("Argument %s\n" % x)
- self.writep.write("Directory .\n%s\nco\n" % self.realroot)
- self.writep.flush()
-
- data = ""
- while 1:
- line = self.readp.readline()
- if line.startswith("Created ") or line.startswith("Updated "):
- self.readp.readline() # path
- self.readp.readline() # entries
- mode = self.readp.readline()[:-1]
- count = int(self.readp.readline()[:-1])
- data = chunkedread(self.readp, count)
- elif line.startswith(" "):
- data += line[1:]
- elif line.startswith("M "):
- pass
- elif line.startswith("Mbinary "):
- count = int(self.readp.readline()[:-1])
- data = chunkedread(self.readp, count)
- else:
- if line == "ok\n":
- return (data, "x" in mode and "x" or "")
- elif line.startswith("E "):
- self.ui.warn(_("cvs server: %s\n") % line[2:])
- elif line.startswith("Remove"):
- self.readp.readline()
- else:
- raise util.Abort(_("unknown CVS response: %s") % line)
-
- def getfile(self, file, rev):
- self._parse()
- data, mode = self._getfile(file, rev)
- self.modecache[(file, rev)] = mode
- return data
-
- def getmode(self, file, rev):
- return self.modecache[(file, rev)]
-
- def getchanges(self, rev):
- self._parse()
- self.modecache = {}
- return sorted(self.files[rev].iteritems()), {}
-
- def getcommit(self, rev):
- self._parse()
- return self.changeset[rev]
-
- def gettags(self):
- self._parse()
- return self.tags
-
- def getchangedfiles(self, rev, i):
- self._parse()
- return sorted(self.files[rev])
--- a/sys/src/cmd/hg/hgext/convert/cvsps.py
+++ /dev/null
@@ -1,831 +1,0 @@
-#
-# Mercurial built-in replacement for cvsps.
-#
-# Copyright 2008, Frank Kingswood <frank@kingswood-consulting.co.uk>
-#
-# This software may be used and distributed according to the terms of the
-# GNU General Public License version 2, incorporated herein by reference.
-
-import os
-import re
-import cPickle as pickle
-from mercurial import util
-from mercurial.i18n import _
-
-class logentry(object):
- '''Class logentry has the following attributes:
- .author - author name as CVS knows it
- .branch - name of branch this revision is on
- .branches - revision tuple of branches starting at this revision
- .comment - commit message
- .date - the commit date as a (time, tz) tuple
- .dead - true if file revision is dead
- .file - Name of file
- .lines - a tuple (+lines, -lines) or None
- .parent - Previous revision of this entry
- .rcs - name of file as returned from CVS
- .revision - revision number as tuple
- .tags - list of tags on the file
- .synthetic - is this a synthetic "file ... added on ..." revision?
- .mergepoint- the branch that has been merged from
- (if present in rlog output)
- .branchpoints- the branches that start at the current entry
- '''
- def __init__(self, **entries):
- self.__dict__.update(entries)
-
- def __repr__(self):
- return "<%s at 0x%x: %s %s>" % (self.__class__.__name__,
- id(self),
- self.file,
- ".".join(map(str, self.revision)))
-
-class logerror(Exception):
- pass
-
-def getrepopath(cvspath):
- """Return the repository path from a CVS path.
-
- >>> getrepopath('/foo/bar')
- '/foo/bar'
- >>> getrepopath('c:/foo/bar')
- 'c:/foo/bar'
- >>> getrepopath(':pserver:10/foo/bar')
- '/foo/bar'
- >>> getrepopath(':pserver:10c:/foo/bar')
- '/foo/bar'
- >>> getrepopath(':pserver:/foo/bar')
- '/foo/bar'
- >>> getrepopath(':pserver:c:/foo/bar')
- 'c:/foo/bar'
- >>> getrepopath(':pserver:truc@foo.bar:/foo/bar')
- '/foo/bar'
- >>> getrepopath(':pserver:truc@foo.bar:c:/foo/bar')
- 'c:/foo/bar'
- """
- # According to CVS manual, CVS paths are expressed like:
- # [:method:][[user][:password]@]hostname[:[port]]/path/to/repository
- #
- # Unfortunately, Windows absolute paths start with a drive letter
- # like 'c:' making it harder to parse. Here we assume that drive
- # letters are only one character long and any CVS component before
- # the repository path is at least 2 characters long, and use this
- # to disambiguate.
- parts = cvspath.split(':')
- if len(parts) == 1:
- return parts[0]
- # Here there is an ambiguous case if we have a port number
- # immediately followed by a Windows driver letter. We assume this
- # never happens and decide it must be CVS path component,
- # therefore ignoring it.
- if len(parts[-2]) > 1:
- return parts[-1].lstrip('0123456789')
- return parts[-2] + ':' + parts[-1]
-
-def createlog(ui, directory=None, root="", rlog=True, cache=None):
- '''Collect the CVS rlog'''
-
- # Because we store many duplicate commit log messages, reusing strings
- # saves a lot of memory and pickle storage space.
- _scache = {}
- def scache(s):
- "return a shared version of a string"
- return _scache.setdefault(s, s)
-
- ui.status(_('collecting CVS rlog\n'))
-
- log = [] # list of logentry objects containing the CVS state
-
- # patterns to match in CVS (r)log output, by state of use
- re_00 = re.compile('RCS file: (.+)$')
- re_01 = re.compile('cvs \\[r?log aborted\\]: (.+)$')
- re_02 = re.compile('cvs (r?log|server): (.+)\n$')
- re_03 = re.compile("(Cannot access.+CVSROOT)|"
- "(can't create temporary directory.+)$")
- re_10 = re.compile('Working file: (.+)$')
- re_20 = re.compile('symbolic names:')
- re_30 = re.compile('\t(.+): ([\\d.]+)$')
- re_31 = re.compile('----------------------------$')
- re_32 = re.compile('======================================='
- '======================================$')
- re_50 = re.compile('revision ([\\d.]+)(\s+locked by:\s+.+;)?$')
- re_60 = re.compile(r'date:\s+(.+);\s+author:\s+(.+);\s+state:\s+(.+?);'
- r'(\s+lines:\s+(\+\d+)?\s+(-\d+)?;)?'
- r'(.*mergepoint:\s+([^;]+);)?')
- re_70 = re.compile('branches: (.+);$')
-
- file_added_re = re.compile(r'file [^/]+ was (initially )?added on branch')
-
- prefix = '' # leading path to strip of what we get from CVS
-
- if directory is None:
- # Current working directory
-
- # Get the real directory in the repository
- try:
- prefix = open(os.path.join('CVS','Repository')).read().strip()
- if prefix == ".":
- prefix = ""
- directory = prefix
- except IOError:
- raise logerror('Not a CVS sandbox')
-
- if prefix and not prefix.endswith(os.sep):
- prefix += os.sep
-
- # Use the Root file in the sandbox, if it exists
- try:
- root = open(os.path.join('CVS','Root')).read().strip()
- except IOError:
- pass
-
- if not root:
- root = os.environ.get('CVSROOT', '')
-
- # read log cache if one exists
- oldlog = []
- date = None
-
- if cache:
- cachedir = os.path.expanduser('~/.hg.cvsps')
- if not os.path.exists(cachedir):
- os.mkdir(cachedir)
-
- # The cvsps cache pickle needs a uniquified name, based on the
- # repository location. The address may have all sort of nasties
- # in it, slashes, colons and such. So here we take just the
- # alphanumerics, concatenated in a way that does not mix up the
- # various components, so that
- # :pserver:user@server:/path
- # and
- # /pserver/user/server/path
- # are mapped to different cache file names.
- cachefile = root.split(":") + [directory, "cache"]
- cachefile = ['-'.join(re.findall(r'\w+', s)) for s in cachefile if s]
- cachefile = os.path.join(cachedir,
- '.'.join([s for s in cachefile if s]))
-
- if cache == 'update':
- try:
- ui.note(_('reading cvs log cache %s\n') % cachefile)
- oldlog = pickle.load(open(cachefile))
- ui.note(_('cache has %d log entries\n') % len(oldlog))
- except Exception, e:
- ui.note(_('error reading cache: %r\n') % e)
-
- if oldlog:
- date = oldlog[-1].date # last commit date as a (time,tz) tuple
- date = util.datestr(date, '%Y/%m/%d %H:%M:%S %1%2')
-
- # build the CVS commandline
- cmd = ['cvs', '-q']
- if root:
- cmd.append('-d%s' % root)
- p = util.normpath(getrepopath(root))
- if not p.endswith('/'):
- p += '/'
- prefix = p + util.normpath(prefix)
- cmd.append(['log', 'rlog'][rlog])
- if date:
- # no space between option and date string
- cmd.append('-d>%s' % date)
- cmd.append(directory)
-
- # state machine begins here
- tags = {} # dictionary of revisions on current file with their tags
- branchmap = {} # mapping between branch names and revision numbers
- state = 0
- store = False # set when a new record can be appended
-
- cmd = [util.shellquote(arg) for arg in cmd]
- ui.note(_("running %s\n") % (' '.join(cmd)))
- ui.debug(_("prefix=%r directory=%r root=%r\n") % (prefix, directory, root))
-
- pfp = util.popen(' '.join(cmd))
- peek = pfp.readline()
- while True:
- line = peek
- if line == '':
- break
- peek = pfp.readline()
- if line.endswith('\n'):
- line = line[:-1]
- #ui.debug('state=%d line=%r\n' % (state, line))
-
- if state == 0:
- # initial state, consume input until we see 'RCS file'
- match = re_00.match(line)
- if match:
- rcs = match.group(1)
- tags = {}
- if rlog:
- filename = util.normpath(rcs[:-2])
- if filename.startswith(prefix):
- filename = filename[len(prefix):]
- if filename.startswith('/'):
- filename = filename[1:]
- if filename.startswith('Attic/'):
- filename = filename[6:]
- else:
- filename = filename.replace('/Attic/', '/')
- state = 2
- continue
- state = 1
- continue
- match = re_01.match(line)
- if match:
- raise Exception(match.group(1))
- match = re_02.match(line)
- if match:
- raise Exception(match.group(2))
- if re_03.match(line):
- raise Exception(line)
-
- elif state == 1:
- # expect 'Working file' (only when using log instead of rlog)
- match = re_10.match(line)
- assert match, _('RCS file must be followed by working file')
- filename = util.normpath(match.group(1))
- state = 2
-
- elif state == 2:
- # expect 'symbolic names'
- if re_20.match(line):
- branchmap = {}
- state = 3
-
- elif state == 3:
- # read the symbolic names and store as tags
- match = re_30.match(line)
- if match:
- rev = [int(x) for x in match.group(2).split('.')]
-
- # Convert magic branch number to an odd-numbered one
- revn = len(rev)
- if revn > 3 and (revn % 2) == 0 and rev[-2] == 0:
- rev = rev[:-2] + rev[-1:]
- rev = tuple(rev)
-
- if rev not in tags:
- tags[rev] = []
- tags[rev].append(match.group(1))
- branchmap[match.group(1)] = match.group(2)
-
- elif re_31.match(line):
- state = 5
- elif re_32.match(line):
- state = 0
-
- elif state == 4:
- # expecting '------' separator before first revision
- if re_31.match(line):
- state = 5
- else:
- assert not re_32.match(line), _('must have at least '
- 'some revisions')
-
- elif state == 5:
- # expecting revision number and possibly (ignored) lock indication
- # we create the logentry here from values stored in states 0 to 4,
- # as this state is re-entered for subsequent revisions of a file.
- match = re_50.match(line)
- assert match, _('expected revision number')
- e = logentry(rcs=scache(rcs), file=scache(filename),
- revision=tuple([int(x) for x in match.group(1).split('.')]),
- branches=[], parent=None,
- synthetic=False)
- state = 6
-
- elif state == 6:
- # expecting date, author, state, lines changed
- match = re_60.match(line)
- assert match, _('revision must be followed by date line')
- d = match.group(1)
- if d[2] == '/':
- # Y2K
- d = '19' + d
-
- if len(d.split()) != 3:
- # cvs log dates always in GMT
- d = d + ' UTC'
- e.date = util.parsedate(d, ['%y/%m/%d %H:%M:%S',
- '%Y/%m/%d %H:%M:%S',
- '%Y-%m-%d %H:%M:%S'])
- e.author = scache(match.group(2))
- e.dead = match.group(3).lower() == 'dead'
-
- if match.group(5):
- if match.group(6):
- e.lines = (int(match.group(5)), int(match.group(6)))
- else:
- e.lines = (int(match.group(5)), 0)
- elif match.group(6):
- e.lines = (0, int(match.group(6)))
- else:
- e.lines = None
-
- if match.group(7): # cvsnt mergepoint
- myrev = match.group(8).split('.')
- if len(myrev) == 2: # head
- e.mergepoint = 'HEAD'
- else:
- myrev = '.'.join(myrev[:-2] + ['0', myrev[-2]])
- branches = [b for b in branchmap if branchmap[b] == myrev]
- assert len(branches) == 1, 'unknown branch: %s' % e.mergepoint
- e.mergepoint = branches[0]
- else:
- e.mergepoint = None
- e.comment = []
- state = 7
-
- elif state == 7:
- # read the revision numbers of branches that start at this revision
- # or store the commit log message otherwise
- m = re_70.match(line)
- if m:
- e.branches = [tuple([int(y) for y in x.strip().split('.')])
- for x in m.group(1).split(';')]
- state = 8
- elif re_31.match(line) and re_50.match(peek):
- state = 5
- store = True
- elif re_32.match(line):
- state = 0
- store = True
- else:
- e.comment.append(line)
-
- elif state == 8:
- # store commit log message
- if re_31.match(line):
- state = 5
- store = True
- elif re_32.match(line):
- state = 0
- store = True
- else:
- e.comment.append(line)
-
- # When a file is added on a branch B1, CVS creates a synthetic
- # dead trunk revision 1.1 so that the branch has a root.
- # Likewise, if you merge such a file to a later branch B2 (one
- # that already existed when the file was added on B1), CVS
- # creates a synthetic dead revision 1.1.x.1 on B2. Don't drop
- # these revisions now, but mark them synthetic so
- # createchangeset() can take care of them.
- if (store and
- e.dead and
- e.revision[-1] == 1 and # 1.1 or 1.1.x.1
- len(e.comment) == 1 and
- file_added_re.match(e.comment[0])):
- ui.debug(_('found synthetic revision in %s: %r\n')
- % (e.rcs, e.comment[0]))
- e.synthetic = True
-
- if store:
- # clean up the results and save in the log.
- store = False
- e.tags = sorted([scache(x) for x in tags.get(e.revision, [])])
- e.comment = scache('\n'.join(e.comment))
-
- revn = len(e.revision)
- if revn > 3 and (revn % 2) == 0:
- e.branch = tags.get(e.revision[:-1], [None])[0]
- else:
- e.branch = None
-
- # find the branches starting from this revision
- branchpoints = set()
- for branch, revision in branchmap.iteritems():
- revparts = tuple([int(i) for i in revision.split('.')])
- if revparts[-2] == 0 and revparts[-1] % 2 == 0:
- # normal branch
- if revparts[:-2] == e.revision:
- branchpoints.add(branch)
- elif revparts == (1,1,1): # vendor branch
- if revparts in e.branches:
- branchpoints.add(branch)
- e.branchpoints = branchpoints
-
- log.append(e)
-
- if len(log) % 100 == 0:
- ui.status(util.ellipsis('%d %s' % (len(log), e.file), 80)+'\n')
-
- log.sort(key=lambda x: (x.rcs, x.revision))
-
- # find parent revisions of individual files
- versions = {}
- for e in log:
- branch = e.revision[:-1]
- p = versions.get((e.rcs, branch), None)
- if p is None:
- p = e.revision[:-2]
- e.parent = p
- versions[(e.rcs, branch)] = e.revision
-
- # update the log cache
- if cache:
- if log:
- # join up the old and new logs
- log.sort(key=lambda x: x.date)
-
- if oldlog and oldlog[-1].date >= log[0].date:
- raise logerror('Log cache overlaps with new log entries,'
- ' re-run without cache.')
-
- log = oldlog + log
-
- # write the new cachefile
- ui.note(_('writing cvs log cache %s\n') % cachefile)
- pickle.dump(log, open(cachefile, 'w'))
- else:
- log = oldlog
-
- ui.status(_('%d log entries\n') % len(log))
-
- return log
-
-
-class changeset(object):
- '''Class changeset has the following attributes:
- .id - integer identifying this changeset (list index)
- .author - author name as CVS knows it
- .branch - name of branch this changeset is on, or None
- .comment - commit message
- .date - the commit date as a (time,tz) tuple
- .entries - list of logentry objects in this changeset
- .parents - list of one or two parent changesets
- .tags - list of tags on this changeset
- .synthetic - from synthetic revision "file ... added on branch ..."
- .mergepoint- the branch that has been merged from
- (if present in rlog output)
- .branchpoints- the branches that start at the current entry
- '''
- def __init__(self, **entries):
- self.__dict__.update(entries)
-
- def __repr__(self):
- return "<%s at 0x%x: %s>" % (self.__class__.__name__,
- id(self),
- getattr(self, 'id', "(no id)"))
-
-def createchangeset(ui, log, fuzz=60, mergefrom=None, mergeto=None):
- '''Convert log into changesets.'''
-
- ui.status(_('creating changesets\n'))
-
- # Merge changesets
-
- log.sort(key=lambda x: (x.comment, x.author, x.branch, x.date))
-
- changesets = []
- files = set()
- c = None
- for i, e in enumerate(log):
-
- # Check if log entry belongs to the current changeset or not.
-
- # Since CVS is file centric, two different file revisions with
- # different branchpoints should be treated as belonging to two
- # different changesets (and the ordering is important and not
- # honoured by cvsps at this point).
- #
- # Consider the following case:
- # foo 1.1 branchpoints: [MYBRANCH]
- # bar 1.1 branchpoints: [MYBRANCH, MYBRANCH2]
- #
- # Here foo is part only of MYBRANCH, but not MYBRANCH2, e.g. a
- # later version of foo may be in MYBRANCH2, so foo should be the
- # first changeset and bar the next and MYBRANCH and MYBRANCH2
- # should both start off of the bar changeset. No provisions are
- # made to ensure that this is, in fact, what happens.
- if not (c and
- e.comment == c.comment and
- e.author == c.author and
- e.branch == c.branch and
- (not hasattr(e, 'branchpoints') or
- not hasattr (c, 'branchpoints') or
- e.branchpoints == c.branchpoints) and
- ((c.date[0] + c.date[1]) <=
- (e.date[0] + e.date[1]) <=
- (c.date[0] + c.date[1]) + fuzz) and
- e.file not in files):
- c = changeset(comment=e.comment, author=e.author,
- branch=e.branch, date=e.date, entries=[],
- mergepoint=getattr(e, 'mergepoint', None),
- branchpoints=getattr(e, 'branchpoints', set()))
- changesets.append(c)
- files = set()
- if len(changesets) % 100 == 0:
- t = '%d %s' % (len(changesets), repr(e.comment)[1:-1])
- ui.status(util.ellipsis(t, 80) + '\n')
-
- c.entries.append(e)
- files.add(e.file)
- c.date = e.date # changeset date is date of latest commit in it
-
- # Mark synthetic changesets
-
- for c in changesets:
- # Synthetic revisions always get their own changeset, because
- # the log message includes the filename. E.g. if you add file3
- # and file4 on a branch, you get four log entries and three
- # changesets:
- # "File file3 was added on branch ..." (synthetic, 1 entry)
- # "File file4 was added on branch ..." (synthetic, 1 entry)
- # "Add file3 and file4 to fix ..." (real, 2 entries)
- # Hence the check for 1 entry here.
- synth = getattr(c.entries[0], 'synthetic', None)
- c.synthetic = (len(c.entries) == 1 and synth)
-
- # Sort files in each changeset
-
- for c in changesets:
- def pathcompare(l, r):
- 'Mimic cvsps sorting order'
- l = l.split('/')
- r = r.split('/')
- nl = len(l)
- nr = len(r)
- n = min(nl, nr)
- for i in range(n):
- if i + 1 == nl and nl < nr:
- return -1
- elif i + 1 == nr and nl > nr:
- return +1
- elif l[i] < r[i]:
- return -1
- elif l[i] > r[i]:
- return +1
- return 0
- def entitycompare(l, r):
- return pathcompare(l.file, r.file)
-
- c.entries.sort(entitycompare)
-
- # Sort changesets by date
-
- def cscmp(l, r):
- d = sum(l.date) - sum(r.date)
- if d:
- return d
-
- # detect vendor branches and initial commits on a branch
- le = {}
- for e in l.entries:
- le[e.rcs] = e.revision
- re = {}
- for e in r.entries:
- re[e.rcs] = e.revision
-
- d = 0
- for e in l.entries:
- if re.get(e.rcs, None) == e.parent:
- assert not d
- d = 1
- break
-
- for e in r.entries:
- if le.get(e.rcs, None) == e.parent:
- assert not d
- d = -1
- break
-
- return d
-
- changesets.sort(cscmp)
-
- # Collect tags
-
- globaltags = {}
- for c in changesets:
- for e in c.entries:
- for tag in e.tags:
- # remember which is the latest changeset to have this tag
- globaltags[tag] = c
-
- for c in changesets:
- tags = set()
- for e in c.entries:
- tags.update(e.tags)
- # remember tags only if this is the latest changeset to have it
- c.tags = sorted(tag for tag in tags if globaltags[tag] is c)
-
- # Find parent changesets, handle {{mergetobranch BRANCHNAME}}
- # by inserting dummy changesets with two parents, and handle
- # {{mergefrombranch BRANCHNAME}} by setting two parents.
-
- if mergeto is None:
- mergeto = r'{{mergetobranch ([-\w]+)}}'
- if mergeto:
- mergeto = re.compile(mergeto)
-
- if mergefrom is None:
- mergefrom = r'{{mergefrombranch ([-\w]+)}}'
- if mergefrom:
- mergefrom = re.compile(mergefrom)
-
- versions = {} # changeset index where we saw any particular file version
- branches = {} # changeset index where we saw a branch
- n = len(changesets)
- i = 0
- while i<n:
- c = changesets[i]
-
- for f in c.entries:
- versions[(f.rcs, f.revision)] = i
-
- p = None
- if c.branch in branches:
- p = branches[c.branch]
- else:
- # first changeset on a new branch
- # the parent is a changeset with the branch in its
- # branchpoints such that it is the latest possible
- # commit without any intervening, unrelated commits.
-
- for candidate in xrange(i):
- if c.branch not in changesets[candidate].branchpoints:
- if p is not None:
- break
- continue
- p = candidate
-
- c.parents = []
- if p is not None:
- p = changesets[p]
-
- # Ensure no changeset has a synthetic changeset as a parent.
- while p.synthetic:
- assert len(p.parents) <= 1, \
- _('synthetic changeset cannot have multiple parents')
- if p.parents:
- p = p.parents[0]
- else:
- p = None
- break
-
- if p is not None:
- c.parents.append(p)
-
- if c.mergepoint:
- if c.mergepoint == 'HEAD':
- c.mergepoint = None
- c.parents.append(changesets[branches[c.mergepoint]])
-
- if mergefrom:
- m = mergefrom.search(c.comment)
- if m:
- m = m.group(1)
- if m == 'HEAD':
- m = None
- try:
- candidate = changesets[branches[m]]
- except KeyError:
- ui.warn(_("warning: CVS commit message references "
- "non-existent branch %r:\n%s\n")
- % (m, c.comment))
- if m in branches and c.branch != m and not candidate.synthetic:
- c.parents.append(candidate)
-
- if mergeto:
- m = mergeto.search(c.comment)
- if m:
- try:
- m = m.group(1)
- if m == 'HEAD':
- m = None
- except:
- m = None # if no group found then merge to HEAD
- if m in branches and c.branch != m:
- # insert empty changeset for merge
- cc = changeset(author=c.author, branch=m, date=c.date,
- comment='convert-repo: CVS merge from branch %s' % c.branch,
- entries=[], tags=[], parents=[changesets[branches[m]], c])
- changesets.insert(i + 1, cc)
- branches[m] = i + 1
-
- # adjust our loop counters now we have inserted a new entry
- n += 1
- i += 2
- continue
-
- branches[c.branch] = i
- i += 1
-
- # Drop synthetic changesets (safe now that we have ensured no other
- # changesets can have them as parents).
- i = 0
- while i < len(changesets):
- if changesets[i].synthetic:
- del changesets[i]
- else:
- i += 1
-
- # Number changesets
-
- for i, c in enumerate(changesets):
- c.id = i + 1
-
- ui.status(_('%d changeset entries\n') % len(changesets))
-
- return changesets
-
-
-def debugcvsps(ui, *args, **opts):
- '''Read CVS rlog for current directory or named path in
- repository, and convert the log to changesets based on matching
- commit log entries and dates.
- '''
- if opts["new_cache"]:
- cache = "write"
- elif opts["update_cache"]:
- cache = "update"
- else:
- cache = None
-
- revisions = opts["revisions"]
-
- try:
- if args:
- log = []
- for d in args:
- log += createlog(ui, d, root=opts["root"], cache=cache)
- else:
- log = createlog(ui, root=opts["root"], cache=cache)
- except logerror, e:
- ui.write("%r\n"%e)
- return
-
- changesets = createchangeset(ui, log, opts["fuzz"])
- del log
-
- # Print changesets (optionally filtered)
-
- off = len(revisions)
- branches = {} # latest version number in each branch
- ancestors = {} # parent branch
- for cs in changesets:
-
- if opts["ancestors"]:
- if cs.branch not in branches and cs.parents and cs.parents[0].id:
- ancestors[cs.branch] = (changesets[cs.parents[0].id-1].branch,
- cs.parents[0].id)
- branches[cs.branch] = cs.id
-
- # limit by branches
- if opts["branches"] and (cs.branch or 'HEAD') not in opts["branches"]:
- continue
-
- if not off:
- # Note: trailing spaces on several lines here are needed to have
- # bug-for-bug compatibility with cvsps.
- ui.write('---------------------\n')
- ui.write('PatchSet %d \n' % cs.id)
- ui.write('Date: %s\n' % util.datestr(cs.date,
- '%Y/%m/%d %H:%M:%S %1%2'))
- ui.write('Author: %s\n' % cs.author)
- ui.write('Branch: %s\n' % (cs.branch or 'HEAD'))
- ui.write('Tag%s: %s \n' % (['', 's'][len(cs.tags)>1],
- ','.join(cs.tags) or '(none)'))
- branchpoints = getattr(cs, 'branchpoints', None)
- if branchpoints:
- ui.write('Branchpoints: %s \n' % ', '.join(branchpoints))
- if opts["parents"] and cs.parents:
- if len(cs.parents)>1:
- ui.write('Parents: %s\n' % (','.join([str(p.id) for p in cs.parents])))
- else:
- ui.write('Parent: %d\n' % cs.parents[0].id)
-
- if opts["ancestors"]:
- b = cs.branch
- r = []
- while b:
- b, c = ancestors[b]
- r.append('%s:%d:%d' % (b or "HEAD", c, branches[b]))
- if r:
- ui.write('Ancestors: %s\n' % (','.join(r)))
-
- ui.write('Log:\n')
- ui.write('%s\n\n' % cs.comment)
- ui.write('Members: \n')
- for f in cs.entries:
- fn = f.file
- if fn.startswith(opts["prefix"]):
- fn = fn[len(opts["prefix"]):]
- ui.write('\t%s:%s->%s%s \n' % (fn, '.'.join([str(x) for x in f.parent]) or 'INITIAL',
- '.'.join([str(x) for x in f.revision]), ['', '(DEAD)'][f.dead]))
- ui.write('\n')
-
- # have we seen the start tag?
- if revisions and off:
- if revisions[0] == str(cs.id) or \
- revisions[0] in cs.tags:
- off = False
-
- # see if we reached the end tag
- if len(revisions)>1 and not off:
- if revisions[1] == str(cs.id) or \
- revisions[1] in cs.tags:
- break
--- a/sys/src/cmd/hg/hgext/convert/darcs.py
+++ /dev/null
@@ -1,135 +1,0 @@
-# darcs.py - darcs support for the convert extension
-#
-# Copyright 2007-2009 Matt Mackall <mpm@selenic.com> and others
-#
-# This software may be used and distributed according to the terms of the
-# GNU General Public License version 2, incorporated herein by reference.
-
-from common import NoRepo, checktool, commandline, commit, converter_source
-from mercurial.i18n import _
-from mercurial import util
-import os, shutil, tempfile
-
-# The naming drift of ElementTree is fun!
-
-try: from xml.etree.cElementTree import ElementTree
-except ImportError:
- try: from xml.etree.ElementTree import ElementTree
- except ImportError:
- try: from elementtree.cElementTree import ElementTree
- except ImportError:
- try: from elementtree.ElementTree import ElementTree
- except ImportError: ElementTree = None
-
-
-class darcs_source(converter_source, commandline):
- def __init__(self, ui, path, rev=None):
- converter_source.__init__(self, ui, path, rev=rev)
- commandline.__init__(self, ui, 'darcs')
-
- # check for _darcs, ElementTree, _darcs/inventory so that we can
- # easily skip test-convert-darcs if ElementTree is not around
- if not os.path.exists(os.path.join(path, '_darcs', 'inventories')):
- raise NoRepo("%s does not look like a darcs repo" % path)
-
- if not os.path.exists(os.path.join(path, '_darcs')):
- raise NoRepo("%s does not look like a darcs repo" % path)
-
- checktool('darcs')
- version = self.run0('--version').splitlines()[0].strip()
- if version < '2.1':
- raise util.Abort(_('darcs version 2.1 or newer needed (found %r)') %
- version)
-
- if ElementTree is None:
- raise util.Abort(_("Python ElementTree module is not available"))
-
- self.path = os.path.realpath(path)
-
- self.lastrev = None
- self.changes = {}
- self.parents = {}
- self.tags = {}
-
- def before(self):
- self.tmppath = tempfile.mkdtemp(
- prefix='convert-' + os.path.basename(self.path) + '-')
- output, status = self.run('init', repodir=self.tmppath)
- self.checkexit(status)
-
- tree = self.xml('changes', xml_output=True, summary=True,
- repodir=self.path)
- tagname = None
- child = None
- for elt in tree.findall('patch'):
- node = elt.get('hash')
- name = elt.findtext('name', '')
- if name.startswith('TAG '):
- tagname = name[4:].strip()
- elif tagname is not None:
- self.tags[tagname] = node
- tagname = None
- self.changes[node] = elt
- self.parents[child] = [node]
- child = node
- self.parents[child] = []
-
- def after(self):
- self.ui.debug(_('cleaning up %s\n') % self.tmppath)
- shutil.rmtree(self.tmppath, ignore_errors=True)
-
- def xml(self, cmd, **kwargs):
- etree = ElementTree()
- fp = self._run(cmd, **kwargs)
- etree.parse(fp)
- self.checkexit(fp.close())
- return etree.getroot()
-
- def getheads(self):
- return self.parents[None]
-
- def getcommit(self, rev):
- elt = self.changes[rev]
- date = util.strdate(elt.get('local_date'), '%a %b %d %H:%M:%S %Z %Y')
- desc = elt.findtext('name') + '\n' + elt.findtext('comment', '')
- return commit(author=elt.get('author'), date=util.datestr(date),
- desc=desc.strip(), parents=self.parents[rev])
-
- def pull(self, rev):
- output, status = self.run('pull', self.path, all=True,
- match='hash %s' % rev,
- no_test=True, no_posthook=True,
- external_merge='/bin/false',
- repodir=self.tmppath)
- if status:
- if output.find('We have conflicts in') == -1:
- self.checkexit(status, output)
- output, status = self.run('revert', all=True, repodir=self.tmppath)
- self.checkexit(status, output)
-
- def getchanges(self, rev):
- self.pull(rev)
- copies = {}
- changes = []
- for elt in self.changes[rev].find('summary').getchildren():
- if elt.tag in ('add_directory', 'remove_directory'):
- continue
- if elt.tag == 'move':
- changes.append((elt.get('from'), rev))
- copies[elt.get('from')] = elt.get('to')
- else:
- changes.append((elt.text.strip(), rev))
- self.lastrev = rev
- return sorted(changes), copies
-
- def getfile(self, name, rev):
- if rev != self.lastrev:
- raise util.Abort(_('internal calling inconsistency'))
- return open(os.path.join(self.tmppath, name), 'rb').read()
-
- def getmode(self, name, rev):
- mode = os.lstat(os.path.join(self.tmppath, name)).st_mode
- return (mode & 0111) and 'x' or ''
-
- def gettags(self):
- return self.tags
--- a/sys/src/cmd/hg/hgext/convert/filemap.py
+++ /dev/null
@@ -1,359 +1,0 @@
-# Copyright 2007 Bryan O'Sullivan <bos@serpentine.com>
-# Copyright 2007 Alexis S. L. Carvalho <alexis@cecm.usp.br>
-#
-# This software may be used and distributed according to the terms of the
-# GNU General Public License version 2, incorporated herein by reference.
-
-import shlex
-from mercurial.i18n import _
-from mercurial import util
-from common import SKIPREV, converter_source
-
-def rpairs(name):
- yield '.', name
- e = len(name)
- while e != -1:
- yield name[:e], name[e+1:]
- e = name.rfind('/', 0, e)
-
-class filemapper(object):
- '''Map and filter filenames when importing.
- A name can be mapped to itself, a new name, or None (omit from new
- repository).'''
-
- def __init__(self, ui, path=None):
- self.ui = ui
- self.include = {}
- self.exclude = {}
- self.rename = {}
- if path:
- if self.parse(path):
- raise util.Abort(_('errors in filemap'))
-
- def parse(self, path):
- errs = 0
- def check(name, mapping, listname):
- if name in mapping:
- self.ui.warn(_('%s:%d: %r already in %s list\n') %
- (lex.infile, lex.lineno, name, listname))
- return 1
- return 0
- lex = shlex.shlex(open(path), path, True)
- lex.wordchars += '!@#$%^&*()-=+[]{}|;:,./<>?'
- cmd = lex.get_token()
- while cmd:
- if cmd == 'include':
- name = lex.get_token()
- errs += check(name, self.exclude, 'exclude')
- self.include[name] = name
- elif cmd == 'exclude':
- name = lex.get_token()
- errs += check(name, self.include, 'include')
- errs += check(name, self.rename, 'rename')
- self.exclude[name] = name
- elif cmd == 'rename':
- src = lex.get_token()
- dest = lex.get_token()
- errs += check(src, self.exclude, 'exclude')
- self.rename[src] = dest
- elif cmd == 'source':
- errs += self.parse(lex.get_token())
- else:
- self.ui.warn(_('%s:%d: unknown directive %r\n') %
- (lex.infile, lex.lineno, cmd))
- errs += 1
- cmd = lex.get_token()
- return errs
-
- def lookup(self, name, mapping):
- for pre, suf in rpairs(name):
- try:
- return mapping[pre], pre, suf
- except KeyError:
- pass
- return '', name, ''
-
- def __call__(self, name):
- if self.include:
- inc = self.lookup(name, self.include)[0]
- else:
- inc = name
- if self.exclude:
- exc = self.lookup(name, self.exclude)[0]
- else:
- exc = ''
- if not inc or exc:
- return None
- newpre, pre, suf = self.lookup(name, self.rename)
- if newpre:
- if newpre == '.':
- return suf
- if suf:
- return newpre + '/' + suf
- return newpre
- return name
-
- def active(self):
- return bool(self.include or self.exclude or self.rename)
-
-# This class does two additional things compared to a regular source:
-#
-# - Filter and rename files. This is mostly wrapped by the filemapper
-# class above. We hide the original filename in the revision that is
-# returned by getchanges to be able to find things later in getfile
-# and getmode.
-#
-# - Return only revisions that matter for the files we're interested in.
-# This involves rewriting the parents of the original revision to
-# create a graph that is restricted to those revisions.
-#
-# This set of revisions includes not only revisions that directly
-# touch files we're interested in, but also merges that merge two
-# or more interesting revisions.
-
-class filemap_source(converter_source):
- def __init__(self, ui, baseconverter, filemap):
- super(filemap_source, self).__init__(ui)
- self.base = baseconverter
- self.filemapper = filemapper(ui, filemap)
- self.commits = {}
- # if a revision rev has parent p in the original revision graph, then
- # rev will have parent self.parentmap[p] in the restricted graph.
- self.parentmap = {}
- # self.wantedancestors[rev] is the set of all ancestors of rev that
- # are in the restricted graph.
- self.wantedancestors = {}
- self.convertedorder = None
- self._rebuilt = False
- self.origparents = {}
- self.children = {}
- self.seenchildren = {}
-
- def before(self):
- self.base.before()
-
- def after(self):
- self.base.after()
-
- def setrevmap(self, revmap):
- # rebuild our state to make things restartable
- #
- # To avoid calling getcommit for every revision that has already
- # been converted, we rebuild only the parentmap, delaying the
- # rebuild of wantedancestors until we need it (i.e. until a
- # merge).
- #
- # We assume the order argument lists the revisions in
- # topological order, so that we can infer which revisions were
- # wanted by previous runs.
- self._rebuilt = not revmap
- seen = {SKIPREV: SKIPREV}
- dummyset = set()
- converted = []
- for rev in revmap.order:
- mapped = revmap[rev]
- wanted = mapped not in seen
- if wanted:
- seen[mapped] = rev
- self.parentmap[rev] = rev
- else:
- self.parentmap[rev] = seen[mapped]
- self.wantedancestors[rev] = dummyset
- arg = seen[mapped]
- if arg == SKIPREV:
- arg = None
- converted.append((rev, wanted, arg))
- self.convertedorder = converted
- return self.base.setrevmap(revmap)
-
- def rebuild(self):
- if self._rebuilt:
- return True
- self._rebuilt = True
- self.parentmap.clear()
- self.wantedancestors.clear()
- self.seenchildren.clear()
- for rev, wanted, arg in self.convertedorder:
- if rev not in self.origparents:
- self.origparents[rev] = self.getcommit(rev).parents
- if arg is not None:
- self.children[arg] = self.children.get(arg, 0) + 1
-
- for rev, wanted, arg in self.convertedorder:
- parents = self.origparents[rev]
- if wanted:
- self.mark_wanted(rev, parents)
- else:
- self.mark_not_wanted(rev, arg)
- self._discard(arg, *parents)
-
- return True
-
- def getheads(self):
- return self.base.getheads()
-
- def getcommit(self, rev):
- # We want to save a reference to the commit objects to be able
- # to rewrite their parents later on.
- c = self.commits[rev] = self.base.getcommit(rev)
- for p in c.parents:
- self.children[p] = self.children.get(p, 0) + 1
- return c
-
- def _discard(self, *revs):
- for r in revs:
- if r is None:
- continue
- self.seenchildren[r] = self.seenchildren.get(r, 0) + 1
- if self.seenchildren[r] == self.children[r]:
- del self.wantedancestors[r]
- del self.parentmap[r]
- del self.seenchildren[r]
- if self._rebuilt:
- del self.children[r]
-
- def wanted(self, rev, i):
- # Return True if we're directly interested in rev.
- #
- # i is an index selecting one of the parents of rev (if rev
- # has no parents, i is None). getchangedfiles will give us
- # the list of files that are different in rev and in the parent
- # indicated by i. If we're interested in any of these files,
- # we're interested in rev.
- try:
- files = self.base.getchangedfiles(rev, i)
- except NotImplementedError:
- raise util.Abort(_("source repository doesn't support --filemap"))
- for f in files:
- if self.filemapper(f):
- return True
- return False
-
- def mark_not_wanted(self, rev, p):
- # Mark rev as not interesting and update data structures.
-
- if p is None:
- # A root revision. Use SKIPREV to indicate that it doesn't
- # map to any revision in the restricted graph. Put SKIPREV
- # in the set of wanted ancestors to simplify code elsewhere
- self.parentmap[rev] = SKIPREV
- self.wantedancestors[rev] = set((SKIPREV,))
- return
-
- # Reuse the data from our parent.
- self.parentmap[rev] = self.parentmap[p]
- self.wantedancestors[rev] = self.wantedancestors[p]
-
- def mark_wanted(self, rev, parents):
- # Mark rev ss wanted and update data structures.
-
- # rev will be in the restricted graph, so children of rev in
- # the original graph should still have rev as a parent in the
- # restricted graph.
- self.parentmap[rev] = rev
-
- # The set of wanted ancestors of rev is the union of the sets
- # of wanted ancestors of its parents. Plus rev itself.
- wrev = set()
- for p in parents:
- wrev.update(self.wantedancestors[p])
- wrev.add(rev)
- self.wantedancestors[rev] = wrev
-
- def getchanges(self, rev):
- parents = self.commits[rev].parents
- if len(parents) > 1:
- self.rebuild()
-
- # To decide whether we're interested in rev we:
- #
- # - calculate what parents rev will have if it turns out we're
- # interested in it. If it's going to have more than 1 parent,
- # we're interested in it.
- #
- # - otherwise, we'll compare it with the single parent we found.
- # If any of the files we're interested in is different in the
- # the two revisions, we're interested in rev.
-
- # A parent p is interesting if its mapped version (self.parentmap[p]):
- # - is not SKIPREV
- # - is still not in the list of parents (we don't want duplicates)
- # - is not an ancestor of the mapped versions of the other parents
- mparents = []
- wp = None
- for i, p1 in enumerate(parents):
- mp1 = self.parentmap[p1]
- if mp1 == SKIPREV or mp1 in mparents:
- continue
- for p2 in parents:
- if p1 == p2 or mp1 == self.parentmap[p2]:
- continue
- if mp1 in self.wantedancestors[p2]:
- break
- else:
- mparents.append(mp1)
- wp = i
-
- if wp is None and parents:
- wp = 0
-
- self.origparents[rev] = parents
-
- if len(mparents) < 2 and not self.wanted(rev, wp):
- # We don't want this revision.
- # Update our state and tell the convert process to map this
- # revision to the same revision its parent as mapped to.
- p = None
- if parents:
- p = parents[wp]
- self.mark_not_wanted(rev, p)
- self.convertedorder.append((rev, False, p))
- self._discard(*parents)
- return self.parentmap[rev]
-
- # We want this revision.
- # Rewrite the parents of the commit object
- self.commits[rev].parents = mparents
- self.mark_wanted(rev, parents)
- self.convertedorder.append((rev, True, None))
- self._discard(*parents)
-
- # Get the real changes and do the filtering/mapping.
- # To be able to get the files later on in getfile and getmode,
- # we hide the original filename in the rev part of the return
- # value.
- changes, copies = self.base.getchanges(rev)
- newnames = {}
- files = []
- for f, r in changes:
- newf = self.filemapper(f)
- if newf:
- files.append((newf, (f, r)))
- newnames[f] = newf
-
- ncopies = {}
- for c in copies:
- newc = self.filemapper(c)
- if newc:
- newsource = self.filemapper(copies[c])
- if newsource:
- ncopies[newc] = newsource
-
- return files, ncopies
-
- def getfile(self, name, rev):
- realname, realrev = rev
- return self.base.getfile(realname, realrev)
-
- def getmode(self, name, rev):
- realname, realrev = rev
- return self.base.getmode(realname, realrev)
-
- def gettags(self):
- return self.base.gettags()
-
- def hasnativeorder(self):
- return self.base.hasnativeorder()
-
- def lookuprev(self, rev):
- return self.base.lookuprev(rev)
--- a/sys/src/cmd/hg/hgext/convert/git.py
+++ /dev/null
@@ -1,152 +1,0 @@
-# git.py - git support for the convert extension
-#
-# Copyright 2005-2009 Matt Mackall <mpm@selenic.com> and others
-#
-# This software may be used and distributed according to the terms of the
-# GNU General Public License version 2, incorporated herein by reference.
-
-import os
-from mercurial import util
-
-from common import NoRepo, commit, converter_source, checktool
-
-class convert_git(converter_source):
- # Windows does not support GIT_DIR= construct while other systems
- # cannot remove environment variable. Just assume none have
- # both issues.
- if hasattr(os, 'unsetenv'):
- def gitcmd(self, s):
- prevgitdir = os.environ.get('GIT_DIR')
- os.environ['GIT_DIR'] = self.path
- try:
- return util.popen(s, 'rb')
- finally:
- if prevgitdir is None:
- del os.environ['GIT_DIR']
- else:
- os.environ['GIT_DIR'] = prevgitdir
- else:
- def gitcmd(self, s):
- return util.popen('GIT_DIR=%s %s' % (self.path, s), 'rb')
-
- def __init__(self, ui, path, rev=None):
- super(convert_git, self).__init__(ui, path, rev=rev)
-
- if os.path.isdir(path + "/.git"):
- path += "/.git"
- if not os.path.exists(path + "/objects"):
- raise NoRepo("%s does not look like a Git repo" % path)
-
- checktool('git', 'git')
-
- self.path = path
-
- def getheads(self):
- if not self.rev:
- return self.gitcmd('git rev-parse --branches --remotes').read().splitlines()
- else:
- fh = self.gitcmd("git rev-parse --verify %s" % self.rev)
- return [fh.read()[:-1]]
-
- def catfile(self, rev, type):
- if rev == "0" * 40: raise IOError()
- fh = self.gitcmd("git cat-file %s %s" % (type, rev))
- return fh.read()
-
- def getfile(self, name, rev):
- return self.catfile(rev, "blob")
-
- def getmode(self, name, rev):
- return self.modecache[(name, rev)]
-
- def getchanges(self, version):
- self.modecache = {}
- fh = self.gitcmd("git diff-tree -z --root -m -r %s" % version)
- changes = []
- seen = set()
- entry = None
- for l in fh.read().split('\x00'):
- if not entry:
- if not l.startswith(':'):
- continue
- entry = l
- continue
- f = l
- if f not in seen:
- seen.add(f)
- entry = entry.split()
- h = entry[3]
- p = (entry[1] == "100755")
- s = (entry[1] == "120000")
- self.modecache[(f, h)] = (p and "x") or (s and "l") or ""
- changes.append((f, h))
- entry = None
- return (changes, {})
-
- def getcommit(self, version):
- c = self.catfile(version, "commit") # read the commit hash
- end = c.find("\n\n")
- message = c[end+2:]
- message = self.recode(message)
- l = c[:end].splitlines()
- parents = []
- author = committer = None
- for e in l[1:]:
- n, v = e.split(" ", 1)
- if n == "author":
- p = v.split()
- tm, tz = p[-2:]
- author = " ".join(p[:-2])
- if author[0] == "<": author = author[1:-1]
- author = self.recode(author)
- if n == "committer":
- p = v.split()
- tm, tz = p[-2:]
- committer = " ".join(p[:-2])
- if committer[0] == "<": committer = committer[1:-1]
- committer = self.recode(committer)
- if n == "parent": parents.append(v)
-
- if committer and committer != author:
- message += "\ncommitter: %s\n" % committer
- tzs, tzh, tzm = tz[-5:-4] + "1", tz[-4:-2], tz[-2:]
- tz = -int(tzs) * (int(tzh) * 3600 + int(tzm))
- date = tm + " " + str(tz)
-
- c = commit(parents=parents, date=date, author=author, desc=message,
- rev=version)
- return c
-
- def gettags(self):
- tags = {}
- fh = self.gitcmd('git ls-remote --tags "%s"' % self.path)
- prefix = 'refs/tags/'
- for line in fh:
- line = line.strip()
- if not line.endswith("^{}"):
- continue
- node, tag = line.split(None, 1)
- if not tag.startswith(prefix):
- continue
- tag = tag[len(prefix):-3]
- tags[tag] = node
-
- return tags
-
- def getchangedfiles(self, version, i):
- changes = []
- if i is None:
- fh = self.gitcmd("git diff-tree --root -m -r %s" % version)
- for l in fh:
- if "\t" not in l:
- continue
- m, f = l[:-1].split("\t")
- changes.append(f)
- fh.close()
- else:
- fh = self.gitcmd('git diff-tree --name-only --root -r %s "%s^%s" --'
- % (version, version, i+1))
- changes = [f.rstrip('\n') for f in fh]
- fh.close()
-
- return changes
--- a/sys/src/cmd/hg/hgext/convert/gnuarch.py
+++ /dev/null
@@ -1,342 +1,0 @@
-# gnuarch.py - GNU Arch support for the convert extension
-#
-# Copyright 2008, 2009 Aleix Conchillo Flaque <aleix@member.fsf.org>
-# and others
-#
-# This software may be used and distributed according to the terms of the
-# GNU General Public License version 2, incorporated herein by reference.
-
-from common import NoRepo, commandline, commit, converter_source
-from mercurial.i18n import _
-from mercurial import util
-import os, shutil, tempfile, stat, locale
-from email.Parser import Parser
-
-class gnuarch_source(converter_source, commandline):
-
- class gnuarch_rev(object):
- def __init__(self, rev):
- self.rev = rev
- self.summary = ''
- self.date = None
- self.author = ''
- self.continuationof = None
- self.add_files = []
- self.mod_files = []
- self.del_files = []
- self.ren_files = {}
- self.ren_dirs = {}
-
- def __init__(self, ui, path, rev=None):
- super(gnuarch_source, self).__init__(ui, path, rev=rev)
-
- if not os.path.exists(os.path.join(path, '{arch}')):
- raise NoRepo(_("%s does not look like a GNU Arch repo") % path)
-
- # Could use checktool, but we want to check for baz or tla.
- self.execmd = None
- if util.find_exe('baz'):
- self.execmd = 'baz'
- else:
- if util.find_exe('tla'):
- self.execmd = 'tla'
- else:
- raise util.Abort(_('cannot find a GNU Arch tool'))
-
- commandline.__init__(self, ui, self.execmd)
-
- self.path = os.path.realpath(path)
- self.tmppath = None
-
- self.treeversion = None
- self.lastrev = None
- self.changes = {}
- self.parents = {}
- self.tags = {}
- self.modecache = {}
- self.catlogparser = Parser()
- self.locale = locale.getpreferredencoding()
- self.archives = []
-
- def before(self):
- # Get registered archives
- self.archives = [i.rstrip('\n')
- for i in self.runlines0('archives', '-n')]
-
- if self.execmd == 'tla':
- output = self.run0('tree-version', self.path)
- else:
- output = self.run0('tree-version', '-d', self.path)
- self.treeversion = output.strip()
-
- # Get name of temporary directory
- version = self.treeversion.split('/')
- self.tmppath = os.path.join(tempfile.gettempdir(),
- 'hg-%s' % version[1])
-
- # Generate parents dictionary
- self.parents[None] = []
- treeversion = self.treeversion
- child = None
- while treeversion:
- self.ui.status(_('analyzing tree version %s...\n') % treeversion)
-
- archive = treeversion.split('/')[0]
- if archive not in self.archives:
- self.ui.status(_('tree analysis stopped because it points to '
- 'an unregistered archive %s...\n') % archive)
- break
-
- # Get the complete list of revisions for that tree version
- output, status = self.runlines('revisions', '-r', '-f', treeversion)
- self.checkexit(status, 'failed retrieveing revisions for %s' % treeversion)
-
- # No new iteration unless a revision has a continuation-of header
- treeversion = None
-
- for l in output:
- rev = l.strip()
- self.changes[rev] = self.gnuarch_rev(rev)
- self.parents[rev] = []
-
- # Read author, date and summary
- catlog, status = self.run('cat-log', '-d', self.path, rev)
- if status:
- catlog = self.run0('cat-archive-log', rev)
- self._parsecatlog(catlog, rev)
-
- # Populate the parents map
- self.parents[child].append(rev)
-
- # Keep track of the current revision as the child of the next
- # revision scanned
- child = rev
-
- # Check if we have to follow the usual incremental history
- # or if we have to 'jump' to a different treeversion given
- # by the continuation-of header.
- if self.changes[rev].continuationof:
- treeversion = '--'.join(self.changes[rev].continuationof.split('--')[:-1])
- break
-
- # If we reached a base-0 revision w/o any continuation-of
- # header, it means the tree history ends here.
- if rev[-6:] == 'base-0':
- break
-
- def after(self):
- self.ui.debug(_('cleaning up %s\n') % self.tmppath)
- shutil.rmtree(self.tmppath, ignore_errors=True)
-
- def getheads(self):
- return self.parents[None]
-
- def getfile(self, name, rev):
- if rev != self.lastrev:
- raise util.Abort(_('internal calling inconsistency'))
-
- # Raise IOError if necessary (i.e. deleted files).
- if not os.path.exists(os.path.join(self.tmppath, name)):
- raise IOError
-
- data, mode = self._getfile(name, rev)
- self.modecache[(name, rev)] = mode
-
- return data
-
- def getmode(self, name, rev):
- return self.modecache[(name, rev)]
-
- def getchanges(self, rev):
- self.modecache = {}
- self._update(rev)
- changes = []
- copies = {}
-
- for f in self.changes[rev].add_files:
- changes.append((f, rev))
-
- for f in self.changes[rev].mod_files:
- changes.append((f, rev))
-
- for f in self.changes[rev].del_files:
- changes.append((f, rev))
-
- for src in self.changes[rev].ren_files:
- to = self.changes[rev].ren_files[src]
- changes.append((src, rev))
- changes.append((to, rev))
- copies[to] = src
-
- for src in self.changes[rev].ren_dirs:
- to = self.changes[rev].ren_dirs[src]
- chgs, cps = self._rendirchanges(src, to);
- changes += [(f, rev) for f in chgs]
- copies.update(cps)
-
- self.lastrev = rev
- return sorted(set(changes)), copies
-
- def getcommit(self, rev):
- changes = self.changes[rev]
- return commit(author=changes.author, date=changes.date,
- desc=changes.summary, parents=self.parents[rev], rev=rev)
-
- def gettags(self):
- return self.tags
-
- def _execute(self, cmd, *args, **kwargs):
- cmdline = [self.execmd, cmd]
- cmdline += args
- cmdline = [util.shellquote(arg) for arg in cmdline]
- cmdline += ['>', util.nulldev, '2>', util.nulldev]
- cmdline = util.quotecommand(' '.join(cmdline))
- self.ui.debug(cmdline, '\n')
- return os.system(cmdline)
-
- def _update(self, rev):
- self.ui.debug(_('applying revision %s...\n') % rev)
- changeset, status = self.runlines('replay', '-d', self.tmppath,
- rev)
- if status:
- # Something went wrong while merging (baz or tla
- # issue?), get latest revision and try from there
- shutil.rmtree(self.tmppath, ignore_errors=True)
- self._obtainrevision(rev)
- else:
- old_rev = self.parents[rev][0]
- self.ui.debug(_('computing changeset between %s and %s...\n')
- % (old_rev, rev))
- self._parsechangeset(changeset, rev)
-
- def _getfile(self, name, rev):
- mode = os.lstat(os.path.join(self.tmppath, name)).st_mode
- if stat.S_ISLNK(mode):
- data = os.readlink(os.path.join(self.tmppath, name))
- mode = mode and 'l' or ''
- else:
- data = open(os.path.join(self.tmppath, name), 'rb').read()
- mode = (mode & 0111) and 'x' or ''
- return data, mode
-
- def _exclude(self, name):
- exclude = [ '{arch}', '.arch-ids', '.arch-inventory' ]
- for exc in exclude:
- if name.find(exc) != -1:
- return True
- return False
-
- def _readcontents(self, path):
- files = []
- contents = os.listdir(path)
- while len(contents) > 0:
- c = contents.pop()
- p = os.path.join(path, c)
- # os.walk could be used, but here we avoid internal GNU
- # Arch files and directories, thus saving a lot time.
- if not self._exclude(p):
- if os.path.isdir(p):
- contents += [os.path.join(c, f) for f in os.listdir(p)]
- else:
- files.append(c)
- return files
-
- def _rendirchanges(self, src, dest):
- changes = []
- copies = {}
- files = self._readcontents(os.path.join(self.tmppath, dest))
- for f in files:
- s = os.path.join(src, f)
- d = os.path.join(dest, f)
- changes.append(s)
- changes.append(d)
- copies[d] = s
- return changes, copies
-
- def _obtainrevision(self, rev):
- self.ui.debug(_('obtaining revision %s...\n') % rev)
- output = self._execute('get', rev, self.tmppath)
- self.checkexit(output)
- self.ui.debug(_('analyzing revision %s...\n') % rev)
- files = self._readcontents(self.tmppath)
- self.changes[rev].add_files += files
-
- def _stripbasepath(self, path):
- if path.startswith('./'):
- return path[2:]
- return path
-
- def _parsecatlog(self, data, rev):
- try:
- catlog = self.catlogparser.parsestr(data)
-
- # Commit date
- self.changes[rev].date = util.datestr(
- util.strdate(catlog['Standard-date'],
- '%Y-%m-%d %H:%M:%S'))
-
- # Commit author
- self.changes[rev].author = self.recode(catlog['Creator'])
-
- # Commit description
- self.changes[rev].summary = '\n\n'.join((catlog['Summary'],
- catlog.get_payload()))
- self.changes[rev].summary = self.recode(self.changes[rev].summary)
-
- # Commit revision origin when dealing with a branch or tag
- if catlog.has_key('Continuation-of'):
- self.changes[rev].continuationof = self.recode(catlog['Continuation-of'])
- except Exception:
- raise util.Abort(_('could not parse cat-log of %s') % rev)
-
- def _parsechangeset(self, data, rev):
- for l in data:
- l = l.strip()
- # Added file (ignore added directory)
- if l.startswith('A') and not l.startswith('A/'):
- file = self._stripbasepath(l[1:].strip())
- if not self._exclude(file):
- self.changes[rev].add_files.append(file)
- # Deleted file (ignore deleted directory)
- elif l.startswith('D') and not l.startswith('D/'):
- file = self._stripbasepath(l[1:].strip())
- if not self._exclude(file):
- self.changes[rev].del_files.append(file)
- # Modified binary file
- elif l.startswith('Mb'):
- file = self._stripbasepath(l[2:].strip())
- if not self._exclude(file):
- self.changes[rev].mod_files.append(file)
- # Modified link
- elif l.startswith('M->'):
- file = self._stripbasepath(l[3:].strip())
- if not self._exclude(file):
- self.changes[rev].mod_files.append(file)
- # Modified file
- elif l.startswith('M'):
- file = self._stripbasepath(l[1:].strip())
- if not self._exclude(file):
- self.changes[rev].mod_files.append(file)
- # Renamed file (or link)
- elif l.startswith('=>'):
- files = l[2:].strip().split(' ')
- if len(files) == 1:
- files = l[2:].strip().split('\t')
- src = self._stripbasepath(files[0])
- dst = self._stripbasepath(files[1])
- if not self._exclude(src) and not self._exclude(dst):
- self.changes[rev].ren_files[src] = dst
- # Conversion from file to link or from link to file (modified)
- elif l.startswith('ch'):
- file = self._stripbasepath(l[2:].strip())
- if not self._exclude(file):
- self.changes[rev].mod_files.append(file)
- # Renamed directory
- elif l.startswith('/>'):
- dirs = l[2:].strip().split(' ')
- if len(dirs) == 1:
- dirs = l[2:].strip().split('\t')
- src = self._stripbasepath(dirs[0])
- dst = self._stripbasepath(dirs[1])
- if not self._exclude(src) and not self._exclude(dst):
- self.changes[rev].ren_dirs[src] = dst
--- a/sys/src/cmd/hg/hgext/convert/hg.py
+++ /dev/null
@@ -1,363 +1,0 @@
-# hg.py - hg backend for convert extension
-#
-# Copyright 2005-2009 Matt Mackall <mpm@selenic.com> and others
-#
-# This software may be used and distributed according to the terms of the
-# GNU General Public License version 2, incorporated herein by reference.
-
-# Notes for hg->hg conversion:
-#
-# * Old versions of Mercurial didn't trim the whitespace from the ends
-# of commit messages, but new versions do. Changesets created by
-# those older versions, then converted, may thus have different
-# hashes for changesets that are otherwise identical.
-#
-# * Using "--config convert.hg.saverev=true" will make the source
-# identifier to be stored in the converted revision. This will cause
-# the converted revision to have a different identity than the
-# source.
-
-
-import os, time, cStringIO
-from mercurial.i18n import _
-from mercurial.node import bin, hex, nullid
-from mercurial import hg, util, context, error
-
-from common import NoRepo, commit, converter_source, converter_sink
-
-class mercurial_sink(converter_sink):
- def __init__(self, ui, path):
- converter_sink.__init__(self, ui, path)
- self.branchnames = ui.configbool('convert', 'hg.usebranchnames', True)
- self.clonebranches = ui.configbool('convert', 'hg.clonebranches', False)
- self.tagsbranch = ui.config('convert', 'hg.tagsbranch', 'default')
- self.lastbranch = None
- if os.path.isdir(path) and len(os.listdir(path)) > 0:
- try:
- self.repo = hg.repository(self.ui, path)
- if not self.repo.local():
- raise NoRepo(_('%s is not a local Mercurial repo') % path)
- except error.RepoError, err:
- ui.traceback()
- raise NoRepo(err.args[0])
- else:
- try:
- ui.status(_('initializing destination %s repository\n') % path)
- self.repo = hg.repository(self.ui, path, create=True)
- if not self.repo.local():
- raise NoRepo(_('%s is not a local Mercurial repo') % path)
- self.created.append(path)
- except error.RepoError:
- ui.traceback()
- raise NoRepo("could not create hg repo %s as sink" % path)
- self.lock = None
- self.wlock = None
- self.filemapmode = False
-
- def before(self):
- self.ui.debug(_('run hg sink pre-conversion action\n'))
- self.wlock = self.repo.wlock()
- self.lock = self.repo.lock()
-
- def after(self):
- self.ui.debug(_('run hg sink post-conversion action\n'))
- self.lock.release()
- self.wlock.release()
-
- def revmapfile(self):
- return os.path.join(self.path, ".hg", "shamap")
-
- def authorfile(self):
- return os.path.join(self.path, ".hg", "authormap")
-
- def getheads(self):
- h = self.repo.changelog.heads()
- return [ hex(x) for x in h ]
-
- def setbranch(self, branch, pbranches):
- if not self.clonebranches:
- return
-
- setbranch = (branch != self.lastbranch)
- self.lastbranch = branch
- if not branch:
- branch = 'default'
- pbranches = [(b[0], b[1] and b[1] or 'default') for b in pbranches]
- pbranch = pbranches and pbranches[0][1] or 'default'
-
- branchpath = os.path.join(self.path, branch)
- if setbranch:
- self.after()
- try:
- self.repo = hg.repository(self.ui, branchpath)
- except:
- self.repo = hg.repository(self.ui, branchpath, create=True)
- self.before()
-
- # pbranches may bring revisions from other branches (merge parents)
- # Make sure we have them, or pull them.
- missings = {}
- for b in pbranches:
- try:
- self.repo.lookup(b[0])
- except:
- missings.setdefault(b[1], []).append(b[0])
-
- if missings:
- self.after()
- for pbranch, heads in missings.iteritems():
- pbranchpath = os.path.join(self.path, pbranch)
- prepo = hg.repository(self.ui, pbranchpath)
- self.ui.note(_('pulling from %s into %s\n') % (pbranch, branch))
- self.repo.pull(prepo, [prepo.lookup(h) for h in heads])
- self.before()
-
- def _rewritetags(self, source, revmap, data):
- fp = cStringIO.StringIO()
- for line in data.splitlines():
- s = line.split(' ', 1)
- if len(s) != 2:
- continue
- revid = revmap.get(source.lookuprev(s[0]))
- if not revid:
- continue
- fp.write('%s %s\n' % (revid, s[1]))
- return fp.getvalue()
-
- def putcommit(self, files, copies, parents, commit, source, revmap):
-
- files = dict(files)
- def getfilectx(repo, memctx, f):
- v = files[f]
- data = source.getfile(f, v)
- e = source.getmode(f, v)
- if f == '.hgtags':
- data = self._rewritetags(source, revmap, data)
- return context.memfilectx(f, data, 'l' in e, 'x' in e, copies.get(f))
-
- pl = []
- for p in parents:
- if p not in pl:
- pl.append(p)
- parents = pl
- nparents = len(parents)
- if self.filemapmode and nparents == 1:
- m1node = self.repo.changelog.read(bin(parents[0]))[0]
- parent = parents[0]
-
- if len(parents) < 2: parents.append(nullid)
- if len(parents) < 2: parents.append(nullid)
- p2 = parents.pop(0)
-
- text = commit.desc
- extra = commit.extra.copy()
- if self.branchnames and commit.branch:
- extra['branch'] = commit.branch
- if commit.rev:
- extra['convert_revision'] = commit.rev
-
- while parents:
- p1 = p2
- p2 = parents.pop(0)
- ctx = context.memctx(self.repo, (p1, p2), text, files.keys(), getfilectx,
- commit.author, commit.date, extra)
- self.repo.commitctx(ctx)
- text = "(octopus merge fixup)\n"
- p2 = hex(self.repo.changelog.tip())
-
- if self.filemapmode and nparents == 1:
- man = self.repo.manifest
- mnode = self.repo.changelog.read(bin(p2))[0]
- if not man.cmp(m1node, man.revision(mnode)):
- self.ui.status(_("filtering out empty revision\n"))
- self.repo.rollback()
- return parent
- return p2
-
- def puttags(self, tags):
- try:
- parentctx = self.repo[self.tagsbranch]
- tagparent = parentctx.node()
- except error.RepoError:
- parentctx = None
- tagparent = nullid
-
- try:
- oldlines = sorted(parentctx['.hgtags'].data().splitlines(True))
- except:
- oldlines = []
-
- newlines = sorted([("%s %s\n" % (tags[tag], tag)) for tag in tags])
- if newlines == oldlines:
- return None
- data = "".join(newlines)
- def getfilectx(repo, memctx, f):
- return context.memfilectx(f, data, False, False, None)
-
- self.ui.status(_("updating tags\n"))
- date = "%s 0" % int(time.mktime(time.gmtime()))
- extra = {'branch': self.tagsbranch}
- ctx = context.memctx(self.repo, (tagparent, None), "update tags",
- [".hgtags"], getfilectx, "convert-repo", date,
- extra)
- self.repo.commitctx(ctx)
- return hex(self.repo.changelog.tip())
-
- def setfilemapmode(self, active):
- self.filemapmode = active
-
-class mercurial_source(converter_source):
- def __init__(self, ui, path, rev=None):
- converter_source.__init__(self, ui, path, rev)
- self.ignoreerrors = ui.configbool('convert', 'hg.ignoreerrors', False)
- self.ignored = set()
- self.saverev = ui.configbool('convert', 'hg.saverev', False)
- try:
- self.repo = hg.repository(self.ui, path)
- # try to provoke an exception if this isn't really a hg
- # repo, but some other bogus compatible-looking url
- if not self.repo.local():
- raise error.RepoError()
- except error.RepoError:
- ui.traceback()
- raise NoRepo("%s is not a local Mercurial repo" % path)
- self.lastrev = None
- self.lastctx = None
- self._changescache = None
- self.convertfp = None
- # Restrict converted revisions to startrev descendants
- startnode = ui.config('convert', 'hg.startrev')
- if startnode is not None:
- try:
- startnode = self.repo.lookup(startnode)
- except error.RepoError:
- raise util.Abort(_('%s is not a valid start revision')
- % startnode)
- startrev = self.repo.changelog.rev(startnode)
- children = {startnode: 1}
- for rev in self.repo.changelog.descendants(startrev):
- children[self.repo.changelog.node(rev)] = 1
- self.keep = children.__contains__
- else:
- self.keep = util.always
-
- def changectx(self, rev):
- if self.lastrev != rev:
- self.lastctx = self.repo[rev]
- self.lastrev = rev
- return self.lastctx
-
- def parents(self, ctx):
- return [p.node() for p in ctx.parents()
- if p and self.keep(p.node())]
-
- def getheads(self):
- if self.rev:
- heads = [self.repo[self.rev].node()]
- else:
- heads = self.repo.heads()
- return [hex(h) for h in heads if self.keep(h)]
-
- def getfile(self, name, rev):
- try:
- return self.changectx(rev)[name].data()
- except error.LookupError, err:
- raise IOError(err)
-
- def getmode(self, name, rev):
- return self.changectx(rev).manifest().flags(name)
-
- def getchanges(self, rev):
- ctx = self.changectx(rev)
- parents = self.parents(ctx)
- if not parents:
- files = sorted(ctx.manifest())
- if self.ignoreerrors:
- # calling getcopies() is a simple way to detect missing
- # revlogs and populate self.ignored
- self.getcopies(ctx, files)
- return [(f, rev) for f in files if f not in self.ignored], {}
- if self._changescache and self._changescache[0] == rev:
- m, a, r = self._changescache[1]
- else:
- m, a, r = self.repo.status(parents[0], ctx.node())[:3]
- # getcopies() detects missing revlogs early, run it before
- # filtering the changes.
- copies = self.getcopies(ctx, m + a)
- changes = [(name, rev) for name in m + a + r
- if name not in self.ignored]
- return sorted(changes), copies
-
- def getcopies(self, ctx, files):
- copies = {}
- for name in files:
- if name in self.ignored:
- continue
- try:
- copysource, copynode = ctx.filectx(name).renamed()
- if copysource in self.ignored or not self.keep(copynode):
- continue
- copies[name] = copysource
- except TypeError:
- pass
- except error.LookupError, e:
- if not self.ignoreerrors:
- raise
- self.ignored.add(name)
- self.ui.warn(_('ignoring: %s\n') % e)
- return copies
-
- def getcommit(self, rev):
- ctx = self.changectx(rev)
- parents = [hex(p) for p in self.parents(ctx)]
- if self.saverev:
- crev = rev
- else:
- crev = None
- return commit(author=ctx.user(), date=util.datestr(ctx.date()),
- desc=ctx.description(), rev=crev, parents=parents,
- branch=ctx.branch(), extra=ctx.extra(),
- sortkey=ctx.rev())
-
- def gettags(self):
- tags = [t for t in self.repo.tagslist() if t[0] != 'tip']
- return dict([(name, hex(node)) for name, node in tags
- if self.keep(node)])
-
- def getchangedfiles(self, rev, i):
- ctx = self.changectx(rev)
- parents = self.parents(ctx)
- if not parents and i is None:
- i = 0
- changes = [], ctx.manifest().keys(), []
- else:
- i = i or 0
- changes = self.repo.status(parents[i], ctx.node())[:3]
- changes = [[f for f in l if f not in self.ignored] for l in changes]
-
- if i == 0:
- self._changescache = (rev, changes)
-
- return changes[0] + changes[1] + changes[2]
-
- def converted(self, rev, destrev):
- if self.convertfp is None:
- self.convertfp = open(os.path.join(self.path, '.hg', 'shamap'),
- 'a')
- self.convertfp.write('%s %s\n' % (destrev, rev))
- self.convertfp.flush()
-
- def before(self):
- self.ui.debug(_('run hg source pre-conversion action\n'))
-
- def after(self):
- self.ui.debug(_('run hg source post-conversion action\n'))
-
- def hasnativeorder(self):
- return True
-
- def lookuprev(self, rev):
- try:
- return hex(self.repo.lookup(rev))
- except error.RepoError:
- return None
--- a/sys/src/cmd/hg/hgext/convert/monotone.py
+++ /dev/null
@@ -1,217 +1,0 @@
-# monotone.py - monotone support for the convert extension
-#
-# Copyright 2008, 2009 Mikkel Fahnoe Jorgensen <mikkel@dvide.com> and
-# others
-#
-# This software may be used and distributed according to the terms of the
-# GNU General Public License version 2, incorporated herein by reference.
-
-import os, re
-from mercurial import util
-from common import NoRepo, commit, converter_source, checktool
-from common import commandline
-from mercurial.i18n import _
-
-class monotone_source(converter_source, commandline):
- def __init__(self, ui, path=None, rev=None):
- converter_source.__init__(self, ui, path, rev)
- commandline.__init__(self, ui, 'mtn')
-
- self.ui = ui
- self.path = path
-
- norepo = NoRepo (_("%s does not look like a monotone repo") % path)
- if not os.path.exists(os.path.join(path, '_MTN')):
- # Could be a monotone repository (SQLite db file)
- try:
- header = file(path, 'rb').read(16)
- except:
- header = ''
- if header != 'SQLite format 3\x00':
- raise norepo
-
- # regular expressions for parsing monotone output
- space = r'\s*'
- name = r'\s+"((?:\\"|[^"])*)"\s*'
- value = name
- revision = r'\s+\[(\w+)\]\s*'
- lines = r'(?:.|\n)+'
-
- self.dir_re = re.compile(space + "dir" + name)
- self.file_re = re.compile(space + "file" + name + "content" + revision)
- self.add_file_re = re.compile(space + "add_file" + name + "content" + revision)
- self.patch_re = re.compile(space + "patch" + name + "from" + revision + "to" + revision)
- self.rename_re = re.compile(space + "rename" + name + "to" + name)
- self.delete_re = re.compile(space + "delete" + name)
- self.tag_re = re.compile(space + "tag" + name + "revision" + revision)
- self.cert_re = re.compile(lines + space + "name" + name + "value" + value)
-
- attr = space + "file" + lines + space + "attr" + space
- self.attr_execute_re = re.compile(attr + '"mtn:execute"' + space + '"true"')
-
- # cached data
- self.manifest_rev = None
- self.manifest = None
- self.files = None
- self.dirs = None
-
- checktool('mtn', abort=False)
-
- # test if there are any revisions
- self.rev = None
- try:
- self.getheads()
- except:
- raise norepo
- self.rev = rev
-
- def mtnrun(self, *args, **kwargs):
- kwargs['d'] = self.path
- return self.run0('automate', *args, **kwargs)
-
- def mtnloadmanifest(self, rev):
- if self.manifest_rev == rev:
- return
- self.manifest = self.mtnrun("get_manifest_of", rev).split("\n\n")
- self.manifest_rev = rev
- self.files = {}
- self.dirs = {}
-
- for e in self.manifest:
- m = self.file_re.match(e)
- if m:
- attr = ""
- name = m.group(1)
- node = m.group(2)
- if self.attr_execute_re.match(e):
- attr += "x"
- self.files[name] = (node, attr)
- m = self.dir_re.match(e)
- if m:
- self.dirs[m.group(1)] = True
-
- def mtnisfile(self, name, rev):
- # a non-file could be a directory or a deleted or renamed file
- self.mtnloadmanifest(rev)
- return name in self.files
-
- def mtnisdir(self, name, rev):
- self.mtnloadmanifest(rev)
- return name in self.dirs
-
- def mtngetcerts(self, rev):
- certs = {"author":"<missing>", "date":"<missing>",
- "changelog":"<missing>", "branch":"<missing>"}
- cert_list = self.mtnrun("certs", rev).split('\n\n key "')
- for e in cert_list:
- m = self.cert_re.match(e)
- if m:
- name, value = m.groups()
- value = value.replace(r'\"', '"')
- value = value.replace(r'\\', '\\')
- certs[name] = value
- # Monotone may have subsecond dates: 2005-02-05T09:39:12.364306
- # and all times are stored in UTC
- certs["date"] = certs["date"].split('.')[0] + " UTC"
- return certs
-
- # implement the converter_source interface:
-
- def getheads(self):
- if not self.rev:
- return self.mtnrun("leaves").splitlines()
- else:
- return [self.rev]
-
- def getchanges(self, rev):
- #revision = self.mtncmd("get_revision %s" % rev).split("\n\n")
- revision = self.mtnrun("get_revision", rev).split("\n\n")
- files = {}
- ignoremove = {}
- renameddirs = []
- copies = {}
- for e in revision:
- m = self.add_file_re.match(e)
- if m:
- files[m.group(1)] = rev
- ignoremove[m.group(1)] = rev
- m = self.patch_re.match(e)
- if m:
- files[m.group(1)] = rev
- # Delete/rename is handled later when the convert engine
- # discovers an IOError exception from getfile,
- # but only if we add the "from" file to the list of changes.
- m = self.delete_re.match(e)
- if m:
- files[m.group(1)] = rev
- m = self.rename_re.match(e)
- if m:
- toname = m.group(2)
- fromname = m.group(1)
- if self.mtnisfile(toname, rev):
- ignoremove[toname] = 1
- copies[toname] = fromname
- files[toname] = rev
- files[fromname] = rev
- elif self.mtnisdir(toname, rev):
- renameddirs.append((fromname, toname))
-
- # Directory renames can be handled only once we have recorded
- # all new files
- for fromdir, todir in renameddirs:
- renamed = {}
- for tofile in self.files:
- if tofile in ignoremove:
- continue
- if tofile.startswith(todir + '/'):
- renamed[tofile] = fromdir + tofile[len(todir):]
- # Avoid chained moves like:
- # d1(/a) => d3/d1(/a)
- # d2 => d3
- ignoremove[tofile] = 1
- for tofile, fromfile in renamed.items():
- self.ui.debug (_("copying file in renamed directory "
- "from '%s' to '%s'")
- % (fromfile, tofile), '\n')
- files[tofile] = rev
- copies[tofile] = fromfile
- for fromfile in renamed.values():
- files[fromfile] = rev
-
- return (files.items(), copies)
-
- def getmode(self, name, rev):
- self.mtnloadmanifest(rev)
- node, attr = self.files.get(name, (None, ""))
- return attr
-
- def getfile(self, name, rev):
- if not self.mtnisfile(name, rev):
- raise IOError() # file was deleted or renamed
- try:
- return self.mtnrun("get_file_of", name, r=rev)
- except:
- raise IOError() # file was deleted or renamed
-
- def getcommit(self, rev):
- certs = self.mtngetcerts(rev)
- return commit(
- author=certs["author"],
- date=util.datestr(util.strdate(certs["date"], "%Y-%m-%dT%H:%M:%S")),
- desc=certs["changelog"],
- rev=rev,
- parents=self.mtnrun("parents", rev).splitlines(),
- branch=certs["branch"])
-
- def gettags(self):
- tags = {}
- for e in self.mtnrun("tags").split("\n\n"):
- m = self.tag_re.match(e)
- if m:
- tags[m.group(1)] = m.group(2)
- return tags
-
- def getchangedfiles(self, rev, i):
- # This function is only needed to support --filemap
- # ... and we don't support that
- raise NotImplementedError()
--- a/sys/src/cmd/hg/hgext/convert/p4.py
+++ /dev/null
@@ -1,205 +1,0 @@
-#
-# Perforce source for convert extension.
-#
-# Copyright 2009, Frank Kingswood <frank@kingswood-consulting.co.uk>
-#
-# This software may be used and distributed according to the terms of the
-# GNU General Public License version 2, incorporated herein by reference.
-#
-
-from mercurial import util
-from mercurial.i18n import _
-
-from common import commit, converter_source, checktool, NoRepo
-import marshal
-import re
-
-def loaditer(f):
- "Yield the dictionary objects generated by p4"
- try:
- while True:
- d = marshal.load(f)
- if not d:
- break
- yield d
- except EOFError:
- pass
-
-class p4_source(converter_source):
- def __init__(self, ui, path, rev=None):
- super(p4_source, self).__init__(ui, path, rev=rev)
-
- if "/" in path and not path.startswith('//'):
- raise NoRepo('%s does not look like a P4 repo' % path)
-
- checktool('p4', abort=False)
-
- self.p4changes = {}
- self.heads = {}
- self.changeset = {}
- self.files = {}
- self.tags = {}
- self.lastbranch = {}
- self.parent = {}
- self.encoding = "latin_1"
- self.depotname = {} # mapping from local name to depot name
- self.modecache = {}
- self.re_type = re.compile("([a-z]+)?(text|binary|symlink|apple|resource|unicode|utf\d+)(\+\w+)?$")
- self.re_keywords = re.compile(r"\$(Id|Header|Date|DateTime|Change|File|Revision|Author):[^$\n]*\$")
- self.re_keywords_old = re.compile("\$(Id|Header):[^$\n]*\$")
-
- self._parse(ui, path)
-
- def _parse_view(self, path):
- "Read changes affecting the path"
- cmd = 'p4 -G changes -s submitted "%s"' % path
- stdout = util.popen(cmd)
- for d in loaditer(stdout):
- c = d.get("change", None)
- if c:
- self.p4changes[c] = True
-
- def _parse(self, ui, path):
- "Prepare list of P4 filenames and revisions to import"
- ui.status(_('reading p4 views\n'))
-
- # read client spec or view
- if "/" in path:
- self._parse_view(path)
- if path.startswith("//") and path.endswith("/..."):
- views = {path[:-3]:""}
- else:
- views = {"//": ""}
- else:
- cmd = 'p4 -G client -o "%s"' % path
- clientspec = marshal.load(util.popen(cmd))
-
- views = {}
- for client in clientspec:
- if client.startswith("View"):
- sview, cview = clientspec[client].split()
- self._parse_view(sview)
- if sview.endswith("...") and cview.endswith("..."):
- sview = sview[:-3]
- cview = cview[:-3]
- cview = cview[2:]
- cview = cview[cview.find("/") + 1:]
- views[sview] = cview
-
- # list of changes that affect our source files
- self.p4changes = self.p4changes.keys()
- self.p4changes.sort(key=int)
-
- # list with depot pathnames, longest first
- vieworder = views.keys()
- vieworder.sort(key=len, reverse=True)
-
- # handle revision limiting
- startrev = self.ui.config('convert', 'p4.startrev', default=0)
- self.p4changes = [x for x in self.p4changes
- if ((not startrev or int(x) >= int(startrev)) and
- (not self.rev or int(x) <= int(self.rev)))]
-
- # now read the full changelists to get the list of file revisions
- ui.status(_('collecting p4 changelists\n'))
- lastid = None
- for change in self.p4changes:
- cmd = "p4 -G describe %s" % change
- stdout = util.popen(cmd)
- d = marshal.load(stdout)
-
- desc = self.recode(d["desc"])
- shortdesc = desc.split("\n", 1)[0]
- t = '%s %s' % (d["change"], repr(shortdesc)[1:-1])
- ui.status(util.ellipsis(t, 80) + '\n')
-
- if lastid:
- parents = [lastid]
- else:
- parents = []
-
- date = (int(d["time"]), 0) # timezone not set
- c = commit(author=self.recode(d["user"]), date=util.datestr(date),
- parents=parents, desc=desc, branch='', extra={"p4": change})
-
- files = []
- i = 0
- while ("depotFile%d" % i) in d and ("rev%d" % i) in d:
- oldname = d["depotFile%d" % i]
- filename = None
- for v in vieworder:
- if oldname.startswith(v):
- filename = views[v] + oldname[len(v):]
- break
- if filename:
- files.append((filename, d["rev%d" % i]))
- self.depotname[filename] = oldname
- i += 1
- self.changeset[change] = c
- self.files[change] = files
- lastid = change
-
- if lastid:
- self.heads = [lastid]
-
- def getheads(self):
- return self.heads
-
- def getfile(self, name, rev):
- cmd = 'p4 -G print "%s#%s"' % (self.depotname[name], rev)
- stdout = util.popen(cmd)
-
- mode = None
- contents = ""
- keywords = None
-
- for d in loaditer(stdout):
- code = d["code"]
- data = d.get("data")
-
- if code == "error":
- raise IOError(d["generic"], data)
-
- elif code == "stat":
- p4type = self.re_type.match(d["type"])
- if p4type:
- mode = ""
- flags = (p4type.group(1) or "") + (p4type.group(3) or "")
- if "x" in flags:
- mode = "x"
- if p4type.group(2) == "symlink":
- mode = "l"
- if "ko" in flags:
- keywords = self.re_keywords_old
- elif "k" in flags:
- keywords = self.re_keywords
-
- elif code == "text" or code == "binary":
- contents += data
-
- if mode is None:
- raise IOError(0, "bad stat")
-
- self.modecache[(name, rev)] = mode
-
- if keywords:
- contents = keywords.sub("$\\1$", contents)
- if mode == "l" and contents.endswith("\n"):
- contents = contents[:-1]
-
- return contents
-
- def getmode(self, name, rev):
- return self.modecache[(name, rev)]
-
- def getchanges(self, rev):
- return self.files[rev], {}
-
- def getcommit(self, rev):
- return self.changeset[rev]
-
- def gettags(self):
- return self.tags
-
- def getchangedfiles(self, rev, i):
- return sorted([x[0] for x in self.files[rev]])
--- a/sys/src/cmd/hg/hgext/convert/subversion.py
+++ /dev/null
@@ -1,1136 +1,0 @@
-# Subversion 1.4/1.5 Python API backend
-#
-# Copyright(C) 2007 Daniel Holth et al
-
-import os
-import re
-import sys
-import cPickle as pickle
-import tempfile
-import urllib
-
-from mercurial import strutil, util, encoding
-from mercurial.i18n import _
-
-# Subversion stuff. Works best with very recent Python SVN bindings
-# e.g. SVN 1.5 or backports. Thanks to the bzr folks for enhancing
-# these bindings.
-
-from cStringIO import StringIO
-
-from common import NoRepo, MissingTool, commit, encodeargs, decodeargs
-from common import commandline, converter_source, converter_sink, mapfile
-
-try:
- from svn.core import SubversionException, Pool
- import svn
- import svn.client
- import svn.core
- import svn.ra
- import svn.delta
- import transport
- import warnings
- warnings.filterwarnings('ignore',
- module='svn.core',
- category=DeprecationWarning)
-
-except ImportError:
- pass
-
-class SvnPathNotFound(Exception):
- pass
-
-def geturl(path):
- try:
- return svn.client.url_from_path(svn.core.svn_path_canonicalize(path))
- except SubversionException:
- pass
- if os.path.isdir(path):
- path = os.path.normpath(os.path.abspath(path))
- if os.name == 'nt':
- path = '/' + util.normpath(path)
- # Module URL is later compared with the repository URL returned
- # by svn API, which is UTF-8.
- path = encoding.tolocal(path)
- return 'file://%s' % urllib.quote(path)
- return path
-
-def optrev(number):
- optrev = svn.core.svn_opt_revision_t()
- optrev.kind = svn.core.svn_opt_revision_number
- optrev.value.number = number
- return optrev
-
-class changedpath(object):
- def __init__(self, p):
- self.copyfrom_path = p.copyfrom_path
- self.copyfrom_rev = p.copyfrom_rev
- self.action = p.action
-
-def get_log_child(fp, url, paths, start, end, limit=0, discover_changed_paths=True,
- strict_node_history=False):
- protocol = -1
- def receiver(orig_paths, revnum, author, date, message, pool):
- if orig_paths is not None:
- for k, v in orig_paths.iteritems():
- orig_paths[k] = changedpath(v)
- pickle.dump((orig_paths, revnum, author, date, message),
- fp, protocol)
-
- try:
- # Use an ra of our own so that our parent can consume
- # our results without confusing the server.
- t = transport.SvnRaTransport(url=url)
- svn.ra.get_log(t.ra, paths, start, end, limit,
- discover_changed_paths,
- strict_node_history,
- receiver)
- except SubversionException, (inst, num):
- pickle.dump(num, fp, protocol)
- except IOError:
- # Caller may interrupt the iteration
- pickle.dump(None, fp, protocol)
- else:
- pickle.dump(None, fp, protocol)
- fp.close()
- # With large history, cleanup process goes crazy and suddenly
- # consumes *huge* amount of memory. The output file being closed,
- # there is no need for clean termination.
- os._exit(0)
-
-def debugsvnlog(ui, **opts):
- """Fetch SVN log in a subprocess and channel them back to parent to
- avoid memory collection issues.
- """
- util.set_binary(sys.stdin)
- util.set_binary(sys.stdout)
- args = decodeargs(sys.stdin.read())
- get_log_child(sys.stdout, *args)
-
-class logstream(object):
- """Interruptible revision log iterator."""
- def __init__(self, stdout):
- self._stdout = stdout
-
- def __iter__(self):
- while True:
- entry = pickle.load(self._stdout)
- try:
- orig_paths, revnum, author, date, message = entry
- except:
- if entry is None:
- break
- raise SubversionException("child raised exception", entry)
- yield entry
-
- def close(self):
- if self._stdout:
- self._stdout.close()
- self._stdout = None
-
-
-# Check to see if the given path is a local Subversion repo. Verify this by
-# looking for several svn-specific files and directories in the given
-# directory.
-def filecheck(path, proto):
- for x in ('locks', 'hooks', 'format', 'db', ):
- if not os.path.exists(os.path.join(path, x)):
- return False
- return True
-
-# Check to see if a given path is the root of an svn repo over http. We verify
-# this by requesting a version-controlled URL we know can't exist and looking
-# for the svn-specific "not found" XML.
-def httpcheck(path, proto):
- return ('<m:human-readable errcode="160013">' in
- urllib.urlopen('%s://%s/!svn/ver/0/.svn' % (proto, path)).read())
-
-protomap = {'http': httpcheck,
- 'https': httpcheck,
- 'file': filecheck,
- }
-def issvnurl(url):
- try:
- proto, path = url.split('://', 1)
- path = urllib.url2pathname(path)
- except ValueError:
- proto = 'file'
- path = os.path.abspath(url)
- path = path.replace(os.sep, '/')
- check = protomap.get(proto, lambda p, p2: False)
- while '/' in path:
- if check(path, proto):
- return True
- path = path.rsplit('/', 1)[0]
- return False
-
-# SVN conversion code stolen from bzr-svn and tailor
-#
-# Subversion looks like a versioned filesystem, branches structures
-# are defined by conventions and not enforced by the tool. First,
-# we define the potential branches (modules) as "trunk" and "branches"
-# children directories. Revisions are then identified by their
-# module and revision number (and a repository identifier).
-#
-# The revision graph is really a tree (or a forest). By default, a
-# revision parent is the previous revision in the same module. If the
-# module directory is copied/moved from another module then the
-# revision is the module root and its parent the source revision in
-# the parent module. A revision has at most one parent.
-#
-class svn_source(converter_source):
- def __init__(self, ui, url, rev=None):
- super(svn_source, self).__init__(ui, url, rev=rev)
-
- if not (url.startswith('svn://') or url.startswith('svn+ssh://') or
- (os.path.exists(url) and
- os.path.exists(os.path.join(url, '.svn'))) or
- issvnurl(url)):
- raise NoRepo("%s does not look like a Subversion repo" % url)
-
- try:
- SubversionException
- except NameError:
- raise MissingTool(_('Subversion python bindings could not be loaded'))
-
- try:
- version = svn.core.SVN_VER_MAJOR, svn.core.SVN_VER_MINOR
- if version < (1, 4):
- raise MissingTool(_('Subversion python bindings %d.%d found, '
- '1.4 or later required') % version)
- except AttributeError:
- raise MissingTool(_('Subversion python bindings are too old, 1.4 '
- 'or later required'))
-
- self.lastrevs = {}
-
- latest = None
- try:
- # Support file://path@rev syntax. Useful e.g. to convert
- # deleted branches.
- at = url.rfind('@')
- if at >= 0:
- latest = int(url[at+1:])
- url = url[:at]
- except ValueError:
- pass
- self.url = geturl(url)
- self.encoding = 'UTF-8' # Subversion is always nominal UTF-8
- try:
- self.transport = transport.SvnRaTransport(url=self.url)
- self.ra = self.transport.ra
- self.ctx = self.transport.client
- self.baseurl = svn.ra.get_repos_root(self.ra)
- # Module is either empty or a repository path starting with
- # a slash and not ending with a slash.
- self.module = urllib.unquote(self.url[len(self.baseurl):])
- self.prevmodule = None
- self.rootmodule = self.module
- self.commits = {}
- self.paths = {}
- self.uuid = svn.ra.get_uuid(self.ra)
- except SubversionException:
- ui.traceback()
- raise NoRepo("%s does not look like a Subversion repo" % self.url)
-
- if rev:
- try:
- latest = int(rev)
- except ValueError:
- raise util.Abort(_('svn: revision %s is not an integer') % rev)
-
- self.startrev = self.ui.config('convert', 'svn.startrev', default=0)
- try:
- self.startrev = int(self.startrev)
- if self.startrev < 0:
- self.startrev = 0
- except ValueError:
- raise util.Abort(_('svn: start revision %s is not an integer')
- % self.startrev)
-
- self.head = self.latest(self.module, latest)
- if not self.head:
- raise util.Abort(_('no revision found in module %s')
- % self.module)
- self.last_changed = self.revnum(self.head)
-
- self._changescache = None
-
- if os.path.exists(os.path.join(url, '.svn/entries')):
- self.wc = url
- else:
- self.wc = None
- self.convertfp = None
-
- def setrevmap(self, revmap):
- lastrevs = {}
- for revid in revmap.iterkeys():
- uuid, module, revnum = self.revsplit(revid)
- lastrevnum = lastrevs.setdefault(module, revnum)
- if revnum > lastrevnum:
- lastrevs[module] = revnum
- self.lastrevs = lastrevs
-
- def exists(self, path, optrev):
- try:
- svn.client.ls(self.url.rstrip('/') + '/' + urllib.quote(path),
- optrev, False, self.ctx)
- return True
- except SubversionException:
- return False
-
- def getheads(self):
-
- def isdir(path, revnum):
- kind = self._checkpath(path, revnum)
- return kind == svn.core.svn_node_dir
-
- def getcfgpath(name, rev):
- cfgpath = self.ui.config('convert', 'svn.' + name)
- if cfgpath is not None and cfgpath.strip() == '':
- return None
- path = (cfgpath or name).strip('/')
- if not self.exists(path, rev):
- if cfgpath:
- raise util.Abort(_('expected %s to be at %r, but not found')
- % (name, path))
- return None
- self.ui.note(_('found %s at %r\n') % (name, path))
- return path
-
- rev = optrev(self.last_changed)
- oldmodule = ''
- trunk = getcfgpath('trunk', rev)
- self.tags = getcfgpath('tags', rev)
- branches = getcfgpath('branches', rev)
-
- # If the project has a trunk or branches, we will extract heads
- # from them. We keep the project root otherwise.
- if trunk:
- oldmodule = self.module or ''
- self.module += '/' + trunk
- self.head = self.latest(self.module, self.last_changed)
- if not self.head:
- raise util.Abort(_('no revision found in module %s')
- % self.module)
-
- # First head in the list is the module's head
- self.heads = [self.head]
- if self.tags is not None:
- self.tags = '%s/%s' % (oldmodule , (self.tags or 'tags'))
-
- # Check if branches bring a few more heads to the list
- if branches:
- rpath = self.url.strip('/')
- branchnames = svn.client.ls(rpath + '/' + urllib.quote(branches),
- rev, False, self.ctx)
- for branch in branchnames.keys():
- module = '%s/%s/%s' % (oldmodule, branches, branch)
- if not isdir(module, self.last_changed):
- continue
- brevid = self.latest(module, self.last_changed)
- if not brevid:
- self.ui.note(_('ignoring empty branch %s\n') % branch)
- continue
- self.ui.note(_('found branch %s at %d\n') %
- (branch, self.revnum(brevid)))
- self.heads.append(brevid)
-
- if self.startrev and self.heads:
- if len(self.heads) > 1:
- raise util.Abort(_('svn: start revision is not supported '
- 'with more than one branch'))
- revnum = self.revnum(self.heads[0])
- if revnum < self.startrev:
- raise util.Abort(_('svn: no revision found after start revision %d')
- % self.startrev)
-
- return self.heads
-
- def getfile(self, file, rev):
- data, mode = self._getfile(file, rev)
- self.modecache[(file, rev)] = mode
- return data
-
- def getmode(self, file, rev):
- return self.modecache[(file, rev)]
-
- def getchanges(self, rev):
- if self._changescache and self._changescache[0] == rev:
- return self._changescache[1]
- self._changescache = None
- self.modecache = {}
- (paths, parents) = self.paths[rev]
- if parents:
- files, copies = self.expandpaths(rev, paths, parents)
- else:
- # Perform a full checkout on roots
- uuid, module, revnum = self.revsplit(rev)
- entries = svn.client.ls(self.baseurl + urllib.quote(module),
- optrev(revnum), True, self.ctx)
- files = [n for n,e in entries.iteritems()
- if e.kind == svn.core.svn_node_file]
- copies = {}
-
- files.sort()
- files = zip(files, [rev] * len(files))
-
- # caller caches the result, so free it here to release memory
- del self.paths[rev]
- return (files, copies)
-
- def getchangedfiles(self, rev, i):
- changes = self.getchanges(rev)
- self._changescache = (rev, changes)
- return [f[0] for f in changes[0]]
-
- def getcommit(self, rev):
- if rev not in self.commits:
- uuid, module, revnum = self.revsplit(rev)
- self.module = module
- self.reparent(module)
- # We assume that:
- # - requests for revisions after "stop" come from the
- # revision graph backward traversal. Cache all of them
- # down to stop, they will be used eventually.
- # - requests for revisions before "stop" come to get
- # isolated branches parents. Just fetch what is needed.
- stop = self.lastrevs.get(module, 0)
- if revnum < stop:
- stop = revnum + 1
- self._fetch_revisions(revnum, stop)
- commit = self.commits[rev]
- # caller caches the result, so free it here to release memory
- del self.commits[rev]
- return commit
-
- def gettags(self):
- tags = {}
- if self.tags is None:
- return tags
-
- # svn tags are just a convention, project branches left in a
- # 'tags' directory. There is no other relationship than
- # ancestry, which is expensive to discover and makes them hard
- # to update incrementally. Worse, past revisions may be
- # referenced by tags far away in the future, requiring a deep
- # history traversal on every calculation. Current code
- # performs a single backward traversal, tracking moves within
- # the tags directory (tag renaming) and recording a new tag
- # everytime a project is copied from outside the tags
- # directory. It also lists deleted tags, this behaviour may
- # change in the future.
- pendings = []
- tagspath = self.tags
- start = svn.ra.get_latest_revnum(self.ra)
- try:
- for entry in self._getlog([self.tags], start, self.startrev):
- origpaths, revnum, author, date, message = entry
- copies = [(e.copyfrom_path, e.copyfrom_rev, p) for p, e
- in origpaths.iteritems() if e.copyfrom_path]
- # Apply moves/copies from more specific to general
- copies.sort(reverse=True)
-
- srctagspath = tagspath
- if copies and copies[-1][2] == tagspath:
- # Track tags directory moves
- srctagspath = copies.pop()[0]
-
- for source, sourcerev, dest in copies:
- if not dest.startswith(tagspath + '/'):
- continue
- for tag in pendings:
- if tag[0].startswith(dest):
- tagpath = source + tag[0][len(dest):]
- tag[:2] = [tagpath, sourcerev]
- break
- else:
- pendings.append([source, sourcerev, dest])
-
- # Filter out tags with children coming from different
- # parts of the repository like:
- # /tags/tag.1 (from /trunk:10)
- # /tags/tag.1/foo (from /branches/foo:12)
- # Here/tags/tag.1 discarded as well as its children.
- # It happens with tools like cvs2svn. Such tags cannot
- # be represented in mercurial.
- addeds = dict((p, e.copyfrom_path) for p, e
- in origpaths.iteritems()
- if e.action == 'A' and e.copyfrom_path)
- badroots = set()
- for destroot in addeds:
- for source, sourcerev, dest in pendings:
- if (not dest.startswith(destroot + '/')
- or source.startswith(addeds[destroot] + '/')):
- continue
- badroots.add(destroot)
- break
-
- for badroot in badroots:
- pendings = [p for p in pendings if p[2] != badroot
- and not p[2].startswith(badroot + '/')]
-
- # Tell tag renamings from tag creations
- remainings = []
- for source, sourcerev, dest in pendings:
- tagname = dest.split('/')[-1]
- if source.startswith(srctagspath):
- remainings.append([source, sourcerev, tagname])
- continue
- if tagname in tags:
- # Keep the latest tag value
- continue
- # From revision may be fake, get one with changes
- try:
- tagid = self.latest(source, sourcerev)
- if tagid and tagname not in tags:
- tags[tagname] = tagid
- except SvnPathNotFound:
- # It happens when we are following directories
- # we assumed were copied with their parents
- # but were really created in the tag
- # directory.
- pass
- pendings = remainings
- tagspath = srctagspath
-
- except SubversionException:
- self.ui.note(_('no tags found at revision %d\n') % start)
- return tags
-
- def converted(self, rev, destrev):
- if not self.wc:
- return
- if self.convertfp is None:
- self.convertfp = open(os.path.join(self.wc, '.svn', 'hg-shamap'),
- 'a')
- self.convertfp.write('%s %d\n' % (destrev, self.revnum(rev)))
- self.convertfp.flush()
-
- def revid(self, revnum, module=None):
- return 'svn:%s%s@%s' % (self.uuid, module or self.module, revnum)
-
- def revnum(self, rev):
- return int(rev.split('@')[-1])
-
- def revsplit(self, rev):
- url, revnum = rev.rsplit('@', 1)
- revnum = int(revnum)
- parts = url.split('/', 1)
- uuid = parts.pop(0)[4:]
- mod = ''
- if parts:
- mod = '/' + parts[0]
- return uuid, mod, revnum
-
- def latest(self, path, stop=0):
- """Find the latest revid affecting path, up to stop. It may return
- a revision in a different module, since a branch may be moved without
- a change being reported. Return None if computed module does not
- belong to rootmodule subtree.
- """
- if not path.startswith(self.rootmodule):
- # Requests on foreign branches may be forbidden at server level
- self.ui.debug(_('ignoring foreign branch %r\n') % path)
- return None
-
- if not stop:
- stop = svn.ra.get_latest_revnum(self.ra)
- try:
- prevmodule = self.reparent('')
- dirent = svn.ra.stat(self.ra, path.strip('/'), stop)
- self.reparent(prevmodule)
- except SubversionException:
- dirent = None
- if not dirent:
- raise SvnPathNotFound(_('%s not found up to revision %d') % (path, stop))
-
- # stat() gives us the previous revision on this line of
- # development, but it might be in *another module*. Fetch the
- # log and detect renames down to the latest revision.
- stream = self._getlog([path], stop, dirent.created_rev)
- try:
- for entry in stream:
- paths, revnum, author, date, message = entry
- if revnum <= dirent.created_rev:
- break
-
- for p in paths:
- if not path.startswith(p) or not paths[p].copyfrom_path:
- continue
- newpath = paths[p].copyfrom_path + path[len(p):]
- self.ui.debug(_("branch renamed from %s to %s at %d\n") %
- (path, newpath, revnum))
- path = newpath
- break
- finally:
- stream.close()
-
- if not path.startswith(self.rootmodule):
- self.ui.debug(_('ignoring foreign branch %r\n') % path)
- return None
- return self.revid(dirent.created_rev, path)
-
- def reparent(self, module):
- """Reparent the svn transport and return the previous parent."""
- if self.prevmodule == module:
- return module
- svnurl = self.baseurl + urllib.quote(module)
- prevmodule = self.prevmodule
- if prevmodule is None:
- prevmodule = ''
- self.ui.debug(_("reparent to %s\n") % svnurl)
- svn.ra.reparent(self.ra, svnurl)
- self.prevmodule = module
- return prevmodule
-
- def expandpaths(self, rev, paths, parents):
- entries = []
- # Map of entrypath, revision for finding source of deleted
- # revisions.
- copyfrom = {}
- copies = {}
-
- new_module, revnum = self.revsplit(rev)[1:]
- if new_module != self.module:
- self.module = new_module
- self.reparent(self.module)
-
- for path, ent in paths:
- entrypath = self.getrelpath(path)
-
- kind = self._checkpath(entrypath, revnum)
- if kind == svn.core.svn_node_file:
- entries.append(self.recode(entrypath))
- if not ent.copyfrom_path or not parents:
- continue
- # Copy sources not in parent revisions cannot be
- # represented, ignore their origin for now
- pmodule, prevnum = self.revsplit(parents[0])[1:]
- if ent.copyfrom_rev < prevnum:
- continue
- copyfrom_path = self.getrelpath(ent.copyfrom_path, pmodule)
- if not copyfrom_path:
- continue
- self.ui.debug(_("copied to %s from %s@%s\n") %
- (entrypath, copyfrom_path, ent.copyfrom_rev))
- copies[self.recode(entrypath)] = self.recode(copyfrom_path)
- elif kind == 0: # gone, but had better be a deleted *file*
- self.ui.debug(_("gone from %s\n") % ent.copyfrom_rev)
- pmodule, prevnum = self.revsplit(parents[0])[1:]
- parentpath = pmodule + "/" + entrypath
- self.ui.debug(_("entry %s\n") % parentpath)
-
- # We can avoid the reparent calls if the module has
- # not changed but it probably does not worth the pain.
- prevmodule = self.reparent('')
- fromkind = svn.ra.check_path(self.ra, parentpath.strip('/'), prevnum)
- self.reparent(prevmodule)
-
- if fromkind == svn.core.svn_node_file:
- entries.append(self.recode(entrypath))
- elif fromkind == svn.core.svn_node_dir:
- if ent.action == 'C':
- children = self._find_children(path, prevnum)
- else:
- oroot = parentpath.strip('/')
- nroot = path.strip('/')
- children = self._find_children(oroot, prevnum)
- children = [s.replace(oroot,nroot) for s in children]
-
- for child in children:
- childpath = self.getrelpath("/" + child, pmodule)
- if not childpath:
- continue
- if childpath in copies:
- del copies[childpath]
- entries.append(childpath)
- else:
- self.ui.debug(_('unknown path in revision %d: %s\n') % \
- (revnum, path))
- elif kind == svn.core.svn_node_dir:
- # If the directory just had a prop change,
- # then we shouldn't need to look for its children.
- if ent.action == 'M':
- continue
-
- children = sorted(self._find_children(path, revnum))
- for child in children:
- # Can we move a child directory and its
- # parent in the same commit? (probably can). Could
- # cause problems if instead of revnum -1,
- # we have to look in (copyfrom_path, revnum - 1)
- entrypath = self.getrelpath("/" + child)
- if entrypath:
- # Need to filter out directories here...
- kind = self._checkpath(entrypath, revnum)
- if kind != svn.core.svn_node_dir:
- entries.append(self.recode(entrypath))
-
- # Handle directory copies
- if not ent.copyfrom_path or not parents:
- continue
- # Copy sources not in parent revisions cannot be
- # represented, ignore their origin for now
- pmodule, prevnum = self.revsplit(parents[0])[1:]
- if ent.copyfrom_rev < prevnum:
- continue
- copyfrompath = self.getrelpath(ent.copyfrom_path, pmodule)
- if not copyfrompath:
- continue
- copyfrom[path] = ent
- self.ui.debug(_("mark %s came from %s:%d\n")
- % (path, copyfrompath, ent.copyfrom_rev))
- children = self._find_children(ent.copyfrom_path, ent.copyfrom_rev)
- children.sort()
- for child in children:
- entrypath = self.getrelpath("/" + child, pmodule)
- if not entrypath:
- continue
- copytopath = path + entrypath[len(copyfrompath):]
- copytopath = self.getrelpath(copytopath)
- copies[self.recode(copytopath)] = self.recode(entrypath)
-
- return (list(set(entries)), copies)
-
- def _fetch_revisions(self, from_revnum, to_revnum):
- if from_revnum < to_revnum:
- from_revnum, to_revnum = to_revnum, from_revnum
-
- self.child_cset = None
-
- def parselogentry(orig_paths, revnum, author, date, message):
- """Return the parsed commit object or None, and True if
- the revision is a branch root.
- """
- self.ui.debug(_("parsing revision %d (%d changes)\n") %
- (revnum, len(orig_paths)))
-
- branched = False
- rev = self.revid(revnum)
- # branch log might return entries for a parent we already have
-
- if rev in self.commits or revnum < to_revnum:
- return None, branched
-
- parents = []
- # check whether this revision is the start of a branch or part
- # of a branch renaming
- orig_paths = sorted(orig_paths.iteritems())
- root_paths = [(p,e) for p,e in orig_paths if self.module.startswith(p)]
- if root_paths:
- path, ent = root_paths[-1]
- if ent.copyfrom_path:
- branched = True
- newpath = ent.copyfrom_path + self.module[len(path):]
- # ent.copyfrom_rev may not be the actual last revision
- previd = self.latest(newpath, ent.copyfrom_rev)
- if previd is not None:
- prevmodule, prevnum = self.revsplit(previd)[1:]
- if prevnum >= self.startrev:
- parents = [previd]
- self.ui.note(_('found parent of branch %s at %d: %s\n') %
- (self.module, prevnum, prevmodule))
- else:
- self.ui.debug(_("no copyfrom path, don't know what to do.\n"))
-
- paths = []
- # filter out unrelated paths
- for path, ent in orig_paths:
- if self.getrelpath(path) is None:
- continue
- paths.append((path, ent))
-
- # Example SVN datetime. Includes microseconds.
- # ISO-8601 conformant
- # '2007-01-04T17:35:00.902377Z'
- date = util.parsedate(date[:19] + " UTC", ["%Y-%m-%dT%H:%M:%S"])
-
- log = message and self.recode(message) or ''
- author = author and self.recode(author) or ''
- try:
- branch = self.module.split("/")[-1]
- if branch == 'trunk':
- branch = ''
- except IndexError:
- branch = None
-
- cset = commit(author=author,
- date=util.datestr(date),
- desc=log,
- parents=parents,
- branch=branch,
- rev=rev)
-
- self.commits[rev] = cset
- # The parents list is *shared* among self.paths and the
- # commit object. Both will be updated below.
- self.paths[rev] = (paths, cset.parents)
- if self.child_cset and not self.child_cset.parents:
- self.child_cset.parents[:] = [rev]
- self.child_cset = cset
- return cset, branched
-
- self.ui.note(_('fetching revision log for "%s" from %d to %d\n') %
- (self.module, from_revnum, to_revnum))
-
- try:
- firstcset = None
- lastonbranch = False
- stream = self._getlog([self.module], from_revnum, to_revnum)
- try:
- for entry in stream:
- paths, revnum, author, date, message = entry
- if revnum < self.startrev:
- lastonbranch = True
- break
- if not paths:
- self.ui.debug(_('revision %d has no entries\n') % revnum)
- continue
- cset, lastonbranch = parselogentry(paths, revnum, author,
- date, message)
- if cset:
- firstcset = cset
- if lastonbranch:
- break
- finally:
- stream.close()
-
- if not lastonbranch and firstcset and not firstcset.parents:
- # The first revision of the sequence (the last fetched one)
- # has invalid parents if not a branch root. Find the parent
- # revision now, if any.
- try:
- firstrevnum = self.revnum(firstcset.rev)
- if firstrevnum > 1:
- latest = self.latest(self.module, firstrevnum - 1)
- if latest:
- firstcset.parents.append(latest)
- except SvnPathNotFound:
- pass
- except SubversionException, (inst, num):
- if num == svn.core.SVN_ERR_FS_NO_SUCH_REVISION:
- raise util.Abort(_('svn: branch has no revision %s') % to_revnum)
- raise
-
- def _getfile(self, file, rev):
- # TODO: ra.get_file transmits the whole file instead of diffs.
- mode = ''
- try:
- new_module, revnum = self.revsplit(rev)[1:]
- if self.module != new_module:
- self.module = new_module
- self.reparent(self.module)
- io = StringIO()
- info = svn.ra.get_file(self.ra, file, revnum, io)
- data = io.getvalue()
- # ra.get_files() seems to keep a reference on the input buffer
- # preventing collection. Release it explicitely.
- io.close()
- if isinstance(info, list):
- info = info[-1]
- mode = ("svn:executable" in info) and 'x' or ''
- mode = ("svn:special" in info) and 'l' or mode
- except SubversionException, e:
- notfound = (svn.core.SVN_ERR_FS_NOT_FOUND,
- svn.core.SVN_ERR_RA_DAV_PATH_NOT_FOUND)
- if e.apr_err in notfound: # File not found
- raise IOError()
- raise
- if mode == 'l':
- link_prefix = "link "
- if data.startswith(link_prefix):
- data = data[len(link_prefix):]
- return data, mode
-
- def _find_children(self, path, revnum):
- path = path.strip('/')
- pool = Pool()
- rpath = '/'.join([self.baseurl, urllib.quote(path)]).strip('/')
- return ['%s/%s' % (path, x) for x in
- svn.client.ls(rpath, optrev(revnum), True, self.ctx, pool).keys()]
-
- def getrelpath(self, path, module=None):
- if module is None:
- module = self.module
- # Given the repository url of this wc, say
- # "http://server/plone/CMFPlone/branches/Plone-2_0-branch"
- # extract the "entry" portion (a relative path) from what
- # svn log --xml says, ie
- # "/CMFPlone/branches/Plone-2_0-branch/tests/PloneTestCase.py"
- # that is to say "tests/PloneTestCase.py"
- if path.startswith(module):
- relative = path.rstrip('/')[len(module):]
- if relative.startswith('/'):
- return relative[1:]
- elif relative == '':
- return relative
-
- # The path is outside our tracked tree...
- self.ui.debug(_('%r is not under %r, ignoring\n') % (path, module))
- return None
-
- def _checkpath(self, path, revnum):
- # ra.check_path does not like leading slashes very much, it leads
- # to PROPFIND subversion errors
- return svn.ra.check_path(self.ra, path.strip('/'), revnum)
-
- def _getlog(self, paths, start, end, limit=0, discover_changed_paths=True,
- strict_node_history=False):
- # Normalize path names, svn >= 1.5 only wants paths relative to
- # supplied URL
- relpaths = []
- for p in paths:
- if not p.startswith('/'):
- p = self.module + '/' + p
- relpaths.append(p.strip('/'))
- args = [self.baseurl, relpaths, start, end, limit, discover_changed_paths,
- strict_node_history]
- arg = encodeargs(args)
- hgexe = util.hgexecutable()
- cmd = '%s debugsvnlog' % util.shellquote(hgexe)
- stdin, stdout = util.popen2(cmd)
- stdin.write(arg)
- stdin.close()
- return logstream(stdout)
-
-pre_revprop_change = '''#!/bin/sh
-
-REPOS="$1"
-REV="$2"
-USER="$3"
-PROPNAME="$4"
-ACTION="$5"
-
-if [ "$ACTION" = "M" -a "$PROPNAME" = "svn:log" ]; then exit 0; fi
-if [ "$ACTION" = "A" -a "$PROPNAME" = "hg:convert-branch" ]; then exit 0; fi
-if [ "$ACTION" = "A" -a "$PROPNAME" = "hg:convert-rev" ]; then exit 0; fi
-
-echo "Changing prohibited revision property" >&2
-exit 1
-'''
-
-class svn_sink(converter_sink, commandline):
- commit_re = re.compile(r'Committed revision (\d+).', re.M)
-
- def prerun(self):
- if self.wc:
- os.chdir(self.wc)
-
- def postrun(self):
- if self.wc:
- os.chdir(self.cwd)
-
- def join(self, name):
- return os.path.join(self.wc, '.svn', name)
-
- def revmapfile(self):
- return self.join('hg-shamap')
-
- def authorfile(self):
- return self.join('hg-authormap')
-
- def __init__(self, ui, path):
- converter_sink.__init__(self, ui, path)
- commandline.__init__(self, ui, 'svn')
- self.delete = []
- self.setexec = []
- self.delexec = []
- self.copies = []
- self.wc = None
- self.cwd = os.getcwd()
-
- path = os.path.realpath(path)
-
- created = False
- if os.path.isfile(os.path.join(path, '.svn', 'entries')):
- self.wc = path
- self.run0('update')
- else:
- wcpath = os.path.join(os.getcwd(), os.path.basename(path) + '-wc')
-
- if os.path.isdir(os.path.dirname(path)):
- if not os.path.exists(os.path.join(path, 'db', 'fs-type')):
- ui.status(_('initializing svn repo %r\n') %
- os.path.basename(path))
- commandline(ui, 'svnadmin').run0('create', path)
- created = path
- path = util.normpath(path)
- if not path.startswith('/'):
- path = '/' + path
- path = 'file://' + path
-
- ui.status(_('initializing svn wc %r\n') % os.path.basename(wcpath))
- self.run0('checkout', path, wcpath)
-
- self.wc = wcpath
- self.opener = util.opener(self.wc)
- self.wopener = util.opener(self.wc)
- self.childmap = mapfile(ui, self.join('hg-childmap'))
- self.is_exec = util.checkexec(self.wc) and util.is_exec or None
-
- if created:
- hook = os.path.join(created, 'hooks', 'pre-revprop-change')
- fp = open(hook, 'w')
- fp.write(pre_revprop_change)
- fp.close()
- util.set_flags(hook, False, True)
-
- xport = transport.SvnRaTransport(url=geturl(path))
- self.uuid = svn.ra.get_uuid(xport.ra)
-
- def wjoin(self, *names):
- return os.path.join(self.wc, *names)
-
- def putfile(self, filename, flags, data):
- if 'l' in flags:
- self.wopener.symlink(data, filename)
- else:
- try:
- if os.path.islink(self.wjoin(filename)):
- os.unlink(filename)
- except OSError:
- pass
- self.wopener(filename, 'w').write(data)
-
- if self.is_exec:
- was_exec = self.is_exec(self.wjoin(filename))
- else:
- # On filesystems not supporting execute-bit, there is no way
- # to know if it is set but asking subversion. Setting it
- # systematically is just as expensive and much simpler.
- was_exec = 'x' not in flags
-
- util.set_flags(self.wjoin(filename), False, 'x' in flags)
- if was_exec:
- if 'x' not in flags:
- self.delexec.append(filename)
- else:
- if 'x' in flags:
- self.setexec.append(filename)
-
- def _copyfile(self, source, dest):
- # SVN's copy command pukes if the destination file exists, but
- # our copyfile method expects to record a copy that has
- # already occurred. Cross the semantic gap.
- wdest = self.wjoin(dest)
- exists = os.path.exists(wdest)
- if exists:
- fd, tempname = tempfile.mkstemp(
- prefix='hg-copy-', dir=os.path.dirname(wdest))
- os.close(fd)
- os.unlink(tempname)
- os.rename(wdest, tempname)
- try:
- self.run0('copy', source, dest)
- finally:
- if exists:
- try:
- os.unlink(wdest)
- except OSError:
- pass
- os.rename(tempname, wdest)
-
- def dirs_of(self, files):
- dirs = set()
- for f in files:
- if os.path.isdir(self.wjoin(f)):
- dirs.add(f)
- for i in strutil.rfindall(f, '/'):
- dirs.add(f[:i])
- return dirs
-
- def add_dirs(self, files):
- add_dirs = [d for d in sorted(self.dirs_of(files))
- if not os.path.exists(self.wjoin(d, '.svn', 'entries'))]
- if add_dirs:
- self.xargs(add_dirs, 'add', non_recursive=True, quiet=True)
- return add_dirs
-
- def add_files(self, files):
- if files:
- self.xargs(files, 'add', quiet=True)
- return files
-
- def tidy_dirs(self, names):
- deleted = []
- for d in sorted(self.dirs_of(names), reverse=True):
- wd = self.wjoin(d)
- if os.listdir(wd) == '.svn':
- self.run0('delete', d)
- deleted.append(d)
- return deleted
-
- def addchild(self, parent, child):
- self.childmap[parent] = child
-
- def revid(self, rev):
- return u"svn:%s@%s" % (self.uuid, rev)
-
- def putcommit(self, files, copies, parents, commit, source, revmap):
- # Apply changes to working copy
- for f, v in files:
- try:
- data = source.getfile(f, v)
- except IOError:
- self.delete.append(f)
- else:
- e = source.getmode(f, v)
- self.putfile(f, e, data)
- if f in copies:
- self.copies.append([copies[f], f])
- files = [f[0] for f in files]
-
- for parent in parents:
- try:
- return self.revid(self.childmap[parent])
- except KeyError:
- pass
- entries = set(self.delete)
- files = frozenset(files)
- entries.update(self.add_dirs(files.difference(entries)))
- if self.copies:
- for s, d in self.copies:
- self._copyfile(s, d)
- self.copies = []
- if self.delete:
- self.xargs(self.delete, 'delete')
- self.delete = []
- entries.update(self.add_files(files.difference(entries)))
- entries.update(self.tidy_dirs(entries))
- if self.delexec:
- self.xargs(self.delexec, 'propdel', 'svn:executable')
- self.delexec = []
- if self.setexec:
- self.xargs(self.setexec, 'propset', 'svn:executable', '*')
- self.setexec = []
-
- fd, messagefile = tempfile.mkstemp(prefix='hg-convert-')
- fp = os.fdopen(fd, 'w')
- fp.write(commit.desc)
- fp.close()
- try:
- output = self.run0('commit',
- username=util.shortuser(commit.author),
- file=messagefile,
- encoding='utf-8')
- try:
- rev = self.commit_re.search(output).group(1)
- except AttributeError:
- self.ui.warn(_('unexpected svn output:\n'))
- self.ui.warn(output)
- raise util.Abort(_('unable to cope with svn output'))
- if commit.rev:
- self.run('propset', 'hg:convert-rev', commit.rev,
- revprop=True, revision=rev)
- if commit.branch and commit.branch != 'default':
- self.run('propset', 'hg:convert-branch', commit.branch,
- revprop=True, revision=rev)
- for parent in parents:
- self.addchild(parent, rev)
- return self.revid(rev)
- finally:
- os.unlink(messagefile)
-
- def puttags(self, tags):
- self.ui.warn(_('XXX TAGS NOT IMPLEMENTED YET\n'))
--- a/sys/src/cmd/hg/hgext/convert/transport.py
+++ /dev/null
@@ -1,128 +1,0 @@
-# -*- coding: utf-8 -*-
-
-# Copyright (C) 2007 Daniel Holth <dholth@fastmail.fm>
-# This is a stripped-down version of the original bzr-svn transport.py,
-# Copyright (C) 2006 Jelmer Vernooij <jelmer@samba.org>
-
-# This program is free software; you can redistribute it and/or modify
-# it under the terms of the GNU General Public License as published by
-# the Free Software Foundation; either version 2 of the License, or
-# (at your option) any later version.
-
-# 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
-
-from svn.core import SubversionException, Pool
-import svn.ra
-import svn.client
-import svn.core
-
-# Some older versions of the Python bindings need to be
-# explicitly initialized. But what we want to do probably
-# won't work worth a darn against those libraries anyway!
-svn.ra.initialize()
-
-svn_config = svn.core.svn_config_get_config(None)
-
-
-def _create_auth_baton(pool):
- """Create a Subversion authentication baton. """
- import svn.client
- # Give the client context baton a suite of authentication
- # providers.h
- providers = [
- svn.client.get_simple_provider(pool),
- svn.client.get_username_provider(pool),
- svn.client.get_ssl_client_cert_file_provider(pool),
- svn.client.get_ssl_client_cert_pw_file_provider(pool),
- svn.client.get_ssl_server_trust_file_provider(pool),
- ]
- # Platform-dependant authentication methods
- getprovider = getattr(svn.core, 'svn_auth_get_platform_specific_provider',
- None)
- if getprovider:
- # Available in svn >= 1.6
- for name in ('gnome_keyring', 'keychain', 'kwallet', 'windows'):
- for type in ('simple', 'ssl_client_cert_pw', 'ssl_server_trust'):
- p = getprovider(name, type, pool)
- if p:
- providers.append(p)
- else:
- if hasattr(svn.client, 'get_windows_simple_provider'):
- providers.append(svn.client.get_windows_simple_provider(pool))
-
- return svn.core.svn_auth_open(providers, pool)
-
-class NotBranchError(SubversionException):
- pass
-
-class SvnRaTransport(object):
- """
- Open an ra connection to a Subversion repository.
- """
- def __init__(self, url="", ra=None):
- self.pool = Pool()
- self.svn_url = url
- self.username = ''
- self.password = ''
-
- # Only Subversion 1.4 has reparent()
- if ra is None or not hasattr(svn.ra, 'reparent'):
- self.client = svn.client.create_context(self.pool)
- ab = _create_auth_baton(self.pool)
- if False:
- svn.core.svn_auth_set_parameter(
- ab, svn.core.SVN_AUTH_PARAM_DEFAULT_USERNAME, self.username)
- svn.core.svn_auth_set_parameter(
- ab, svn.core.SVN_AUTH_PARAM_DEFAULT_PASSWORD, self.password)
- self.client.auth_baton = ab
- self.client.config = svn_config
- try:
- self.ra = svn.client.open_ra_session(
- self.svn_url.encode('utf8'),
- self.client, self.pool)
- except SubversionException, (inst, num):
- if num in (svn.core.SVN_ERR_RA_ILLEGAL_URL,
- svn.core.SVN_ERR_RA_LOCAL_REPOS_OPEN_FAILED,
- svn.core.SVN_ERR_BAD_URL):
- raise NotBranchError(url)
- raise
- else:
- self.ra = ra
- svn.ra.reparent(self.ra, self.svn_url.encode('utf8'))
-
- class Reporter(object):
- def __init__(self, (reporter, report_baton)):
- self._reporter = reporter
- self._baton = report_baton
-
- def set_path(self, path, revnum, start_empty, lock_token, pool=None):
- svn.ra.reporter2_invoke_set_path(self._reporter, self._baton,
- path, revnum, start_empty, lock_token, pool)
-
- def delete_path(self, path, pool=None):
- svn.ra.reporter2_invoke_delete_path(self._reporter, self._baton,
- path, pool)
-
- def link_path(self, path, url, revision, start_empty, lock_token,
- pool=None):
- svn.ra.reporter2_invoke_link_path(self._reporter, self._baton,
- path, url, revision, start_empty, lock_token,
- pool)
-
- def finish_report(self, pool=None):
- svn.ra.reporter2_invoke_finish_report(self._reporter,
- self._baton, pool)
-
- def abort_report(self, pool=None):
- svn.ra.reporter2_invoke_abort_report(self._reporter,
- self._baton, pool)
-
- def do_update(self, revnum, path, *args, **kwargs):
- return self.Reporter(svn.ra.do_update(self.ra, revnum, path, *args, **kwargs))
--- a/sys/src/cmd/hg/hgext/inotify/linux/__init__.py
+++ /dev/null
@@ -1,41 +1,0 @@
-# __init__.py - low-level interfaces to the Linux inotify subsystem
-
-# Copyright 2006 Bryan O'Sullivan <bos@serpentine.com>
-
-# This library is free software; you can redistribute it and/or modify
-# it under the terms of version 2.1 of the GNU Lesser General Public
-# License, incorporated herein by reference.
-
-'''Low-level interface to the Linux inotify subsystem.
-
-The inotify subsystem provides an efficient mechanism for file status
-monitoring and change notification.
-
-This package provides the low-level inotify system call interface and
-associated constants and helper functions.
-
-For a higher-level interface that remains highly efficient, use the
-inotify.watcher package.'''
-
-__author__ = "Bryan O'Sullivan <bos@serpentine.com>"
-
-from _inotify import *
-
-procfs_path = '/proc/sys/fs/inotify'
-
-def _read_procfs_value(name):
- def read_value():
- try:
- return int(open(procfs_path + '/' + name).read())
- except OSError:
- return None
-
- read_value.__doc__ = '''Return the value of the %s setting from /proc.
-
- If inotify is not enabled on this system, return None.''' % name
-
- return read_value
-
-max_queued_events = _read_procfs_value('max_queued_events')
-max_user_instances = _read_procfs_value('max_user_instances')
-max_user_watches = _read_procfs_value('max_user_watches')
--- a/sys/src/cmd/hg/hgext/inotify/linux/_inotify.c
+++ /dev/null
@@ -1,608 +1,0 @@
-/*
- * _inotify.c - Python extension interfacing to the Linux inotify subsystem
- *
- * Copyright 2006 Bryan O'Sullivan <bos@serpentine.com>
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of version 2.1 of the GNU Lesser General
- * Public License, incorporated herein by reference.
- */
-
-#include <Python.h>
-#include <alloca.h>
-#include <sys/inotify.h>
-#include <stdint.h>
-#include <sys/ioctl.h>
-#include <unistd.h>
-
-static PyObject *init(PyObject *self, PyObject *args)
-{
- PyObject *ret = NULL;
- int fd = -1;
-
- if (!PyArg_ParseTuple(args, ":init"))
- goto bail;
-
- Py_BEGIN_ALLOW_THREADS
- fd = inotify_init();
- Py_END_ALLOW_THREADS
-
- if (fd == -1) {
- PyErr_SetFromErrno(PyExc_OSError);
- goto bail;
- }
-
- ret = PyInt_FromLong(fd);
- if (ret == NULL)
- goto bail;
-
- goto done;
-
-bail:
- if (fd != -1)
- close(fd);
-
- Py_CLEAR(ret);
-
-done:
- return ret;
-}
-
-PyDoc_STRVAR(
- init_doc,
- "init() -> fd\n"
- "\n"
- "Initialise an inotify instance.\n"
- "Return a file descriptor associated with a new inotify event queue.");
-
-static PyObject *add_watch(PyObject *self, PyObject *args)
-{
- PyObject *ret = NULL;
- uint32_t mask;
- int wd = -1;
- char *path;
- int fd;
-
- if (!PyArg_ParseTuple(args, "isI:add_watch", &fd, &path, &mask))
- goto bail;
-
- Py_BEGIN_ALLOW_THREADS
- wd = inotify_add_watch(fd, path, mask);
- Py_END_ALLOW_THREADS
-
- if (wd == -1) {
- PyErr_SetFromErrnoWithFilename(PyExc_OSError, path);
- goto bail;
- }
-
- ret = PyInt_FromLong(wd);
- if (ret == NULL)
- goto bail;
-
- goto done;
-
-bail:
- if (wd != -1)
- inotify_rm_watch(fd, wd);
-
- Py_CLEAR(ret);
-
-done:
- return ret;
-}
-
-PyDoc_STRVAR(
- add_watch_doc,
- "add_watch(fd, path, mask) -> wd\n"
- "\n"
- "Add a watch to an inotify instance, or modify an existing watch.\n"
- "\n"
- " fd: file descriptor returned by init()\n"
- " path: path to watch\n"
- " mask: mask of events to watch for\n"
- "\n"
- "Return a unique numeric watch descriptor for the inotify instance\n"
- "mapped by the file descriptor.");
-
-static PyObject *remove_watch(PyObject *self, PyObject *args)
-{
- PyObject *ret = NULL;
- uint32_t wd;
- int fd;
- int r;
-
- if (!PyArg_ParseTuple(args, "iI:remove_watch", &fd, &wd))
- goto bail;
-
- Py_BEGIN_ALLOW_THREADS
- r = inotify_rm_watch(fd, wd);
- Py_END_ALLOW_THREADS
-
- if (r == -1) {
- PyErr_SetFromErrno(PyExc_OSError);
- goto bail;
- }
-
- Py_INCREF(Py_None);
-
- goto done;
-
-bail:
- Py_CLEAR(ret);
-
-done:
- return ret;
-}
-
-PyDoc_STRVAR(
- remove_watch_doc,
- "remove_watch(fd, wd)\n"
- "\n"
- " fd: file descriptor returned by init()\n"
- " wd: watch descriptor returned by add_watch()\n"
- "\n"
- "Remove a watch associated with the watch descriptor wd from the\n"
- "inotify instance associated with the file descriptor fd.\n"
- "\n"
- "Removing a watch causes an IN_IGNORED event to be generated for this\n"
- "watch descriptor.");
-
-#define bit_name(x) {x, #x}
-
-static struct {
- int bit;
- const char *name;
- PyObject *pyname;
-} bit_names[] = {
- bit_name(IN_ACCESS),
- bit_name(IN_MODIFY),
- bit_name(IN_ATTRIB),
- bit_name(IN_CLOSE_WRITE),
- bit_name(IN_CLOSE_NOWRITE),
- bit_name(IN_OPEN),
- bit_name(IN_MOVED_FROM),
- bit_name(IN_MOVED_TO),
- bit_name(IN_CREATE),
- bit_name(IN_DELETE),
- bit_name(IN_DELETE_SELF),
- bit_name(IN_MOVE_SELF),
- bit_name(IN_UNMOUNT),
- bit_name(IN_Q_OVERFLOW),
- bit_name(IN_IGNORED),
- bit_name(IN_ONLYDIR),
- bit_name(IN_DONT_FOLLOW),
- bit_name(IN_MASK_ADD),
- bit_name(IN_ISDIR),
- bit_name(IN_ONESHOT),
- {0}
-};
-
-static PyObject *decode_mask(int mask)
-{
- PyObject *ret = PyList_New(0);
- int i;
-
- if (ret == NULL)
- goto bail;
-
- for (i = 0; bit_names[i].bit; i++) {
- if (mask & bit_names[i].bit) {
- if (bit_names[i].pyname == NULL) {
- bit_names[i].pyname = PyString_FromString(bit_names[i].name);
- if (bit_names[i].pyname == NULL)
- goto bail;
- }
- Py_INCREF(bit_names[i].pyname);
- if (PyList_Append(ret, bit_names[i].pyname) == -1)
- goto bail;
- }
- }
-
- goto done;
-
-bail:
- Py_CLEAR(ret);
-
-done:
- return ret;
-}
-
-static PyObject *pydecode_mask(PyObject *self, PyObject *args)
-{
- int mask;
-
- if (!PyArg_ParseTuple(args, "i:decode_mask", &mask))
- return NULL;
-
- return decode_mask(mask);
-}
-
-PyDoc_STRVAR(
- decode_mask_doc,
- "decode_mask(mask) -> list_of_strings\n"
- "\n"
- "Decode an inotify mask value into a list of strings that give the\n"
- "name of each bit set in the mask.");
-
-static char doc[] = "Low-level inotify interface wrappers.";
-
-static void define_const(PyObject *dict, const char *name, uint32_t val)
-{
- PyObject *pyval = PyInt_FromLong(val);
- PyObject *pyname = PyString_FromString(name);
-
- if (!pyname || !pyval)
- goto bail;
-
- PyDict_SetItem(dict, pyname, pyval);
-
-bail:
- Py_XDECREF(pyname);
- Py_XDECREF(pyval);
-}
-
-static void define_consts(PyObject *dict)
-{
- define_const(dict, "IN_ACCESS", IN_ACCESS);
- define_const(dict, "IN_MODIFY", IN_MODIFY);
- define_const(dict, "IN_ATTRIB", IN_ATTRIB);
- define_const(dict, "IN_CLOSE_WRITE", IN_CLOSE_WRITE);
- define_const(dict, "IN_CLOSE_NOWRITE", IN_CLOSE_NOWRITE);
- define_const(dict, "IN_OPEN", IN_OPEN);
- define_const(dict, "IN_MOVED_FROM", IN_MOVED_FROM);
- define_const(dict, "IN_MOVED_TO", IN_MOVED_TO);
-
- define_const(dict, "IN_CLOSE", IN_CLOSE);
- define_const(dict, "IN_MOVE", IN_MOVE);
-
- define_const(dict, "IN_CREATE", IN_CREATE);
- define_const(dict, "IN_DELETE", IN_DELETE);
- define_const(dict, "IN_DELETE_SELF", IN_DELETE_SELF);
- define_const(dict, "IN_MOVE_SELF", IN_MOVE_SELF);
- define_const(dict, "IN_UNMOUNT", IN_UNMOUNT);
- define_const(dict, "IN_Q_OVERFLOW", IN_Q_OVERFLOW);
- define_const(dict, "IN_IGNORED", IN_IGNORED);
-
- define_const(dict, "IN_ONLYDIR", IN_ONLYDIR);
- define_const(dict, "IN_DONT_FOLLOW", IN_DONT_FOLLOW);
- define_const(dict, "IN_MASK_ADD", IN_MASK_ADD);
- define_const(dict, "IN_ISDIR", IN_ISDIR);
- define_const(dict, "IN_ONESHOT", IN_ONESHOT);
- define_const(dict, "IN_ALL_EVENTS", IN_ALL_EVENTS);
-}
-
-struct event {
- PyObject_HEAD
- PyObject *wd;
- PyObject *mask;
- PyObject *cookie;
- PyObject *name;
-};
-
-static PyObject *event_wd(PyObject *self, void *x)
-{
- struct event *evt = (struct event *) self;
- Py_INCREF(evt->wd);
- return evt->wd;
-}
-
-static PyObject *event_mask(PyObject *self, void *x)
-{
- struct event *evt = (struct event *) self;
- Py_INCREF(evt->mask);
- return evt->mask;
-}
-
-static PyObject *event_cookie(PyObject *self, void *x)
-{
- struct event *evt = (struct event *) self;
- Py_INCREF(evt->cookie);
- return evt->cookie;
-}
-
-static PyObject *event_name(PyObject *self, void *x)
-{
- struct event *evt = (struct event *) self;
- Py_INCREF(evt->name);
- return evt->name;
-}
-
-static struct PyGetSetDef event_getsets[] = {
- {"wd", event_wd, NULL,
- "watch descriptor"},
- {"mask", event_mask, NULL,
- "event mask"},
- {"cookie", event_cookie, NULL,
- "rename cookie, if rename-related event"},
- {"name", event_name, NULL,
- "file name"},
- {NULL}
-};
-
-PyDoc_STRVAR(
- event_doc,
- "event: Structure describing an inotify event.");
-
-static PyObject *event_new(PyTypeObject *t, PyObject *a, PyObject *k)
-{
- return (*t->tp_alloc)(t, 0);
-}
-
-static void event_dealloc(struct event *evt)
-{
- Py_XDECREF(evt->wd);
- Py_XDECREF(evt->mask);
- Py_XDECREF(evt->cookie);
- Py_XDECREF(evt->name);
-
- (*evt->ob_type->tp_free)(evt);
-}
-
-static PyObject *event_repr(struct event *evt)
-{
- int wd = PyInt_AsLong(evt->wd);
- int cookie = evt->cookie == Py_None ? -1 : PyInt_AsLong(evt->cookie);
- PyObject *ret = NULL, *pymasks = NULL, *pymask = NULL;
- PyObject *join = NULL;
- char *maskstr;
-
- join = PyString_FromString("|");
- if (join == NULL)
- goto bail;
-
- pymasks = decode_mask(PyInt_AsLong(evt->mask));
- if (pymasks == NULL)
- goto bail;
-
- pymask = _PyString_Join(join, pymasks);
- if (pymask == NULL)
- goto bail;
-
- maskstr = PyString_AsString(pymask);
-
- if (evt->name != Py_None) {
- PyObject *pyname = PyString_Repr(evt->name, 1);
- char *name = pyname ? PyString_AsString(pyname) : "???";
-
- if (cookie == -1)
- ret = PyString_FromFormat("event(wd=%d, mask=%s, name=%s)",
- wd, maskstr, name);
- else
- ret = PyString_FromFormat("event(wd=%d, mask=%s, "
- "cookie=0x%x, name=%s)",
- wd, maskstr, cookie, name);
-
- Py_XDECREF(pyname);
- } else {
- if (cookie == -1)
- ret = PyString_FromFormat("event(wd=%d, mask=%s)",
- wd, maskstr);
- else {
- ret = PyString_FromFormat("event(wd=%d, mask=%s, cookie=0x%x)",
- wd, maskstr, cookie);
- }
- }
-
- goto done;
-bail:
- Py_CLEAR(ret);
-
-done:
- Py_XDECREF(pymask);
- Py_XDECREF(pymasks);
- Py_XDECREF(join);
-
- return ret;
-}
-
-static PyTypeObject event_type = {
- PyObject_HEAD_INIT(NULL)
- 0, /*ob_size*/
- "_inotify.event", /*tp_name*/
- sizeof(struct event), /*tp_basicsize*/
- 0, /*tp_itemsize*/
- (destructor)event_dealloc, /*tp_dealloc*/
- 0, /*tp_print*/
- 0, /*tp_getattr*/
- 0, /*tp_setattr*/
- 0, /*tp_compare*/
- (reprfunc)event_repr, /*tp_repr*/
- 0, /*tp_as_number*/
- 0, /*tp_as_sequence*/
- 0, /*tp_as_mapping*/
- 0, /*tp_hash */
- 0, /*tp_call*/
- 0, /*tp_str*/
- 0, /*tp_getattro*/
- 0, /*tp_setattro*/
- 0, /*tp_as_buffer*/
- Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /*tp_flags*/
- event_doc, /* tp_doc */
- 0, /* tp_traverse */
- 0, /* tp_clear */
- 0, /* tp_richcompare */
- 0, /* tp_weaklistoffset */
- 0, /* tp_iter */
- 0, /* tp_iternext */
- 0, /* tp_methods */
- 0, /* tp_members */
- event_getsets, /* tp_getset */
- 0, /* tp_base */
- 0, /* tp_dict */
- 0, /* tp_descr_get */
- 0, /* tp_descr_set */
- 0, /* tp_dictoffset */
- 0, /* tp_init */
- 0, /* tp_alloc */
- event_new, /* tp_new */
-};
-
-PyObject *read_events(PyObject *self, PyObject *args)
-{
- PyObject *ctor_args = NULL;
- PyObject *pybufsize = NULL;
- PyObject *ret = NULL;
- int bufsize = 65536;
- char *buf = NULL;
- int nread, pos;
- int fd;
-
- if (!PyArg_ParseTuple(args, "i|O:read", &fd, &pybufsize))
- goto bail;
-
- if (pybufsize && pybufsize != Py_None)
- bufsize = PyInt_AsLong(pybufsize);
-
- ret = PyList_New(0);
- if (ret == NULL)
- goto bail;
-
- if (bufsize <= 0) {
- int r;
-
- Py_BEGIN_ALLOW_THREADS
- r = ioctl(fd, FIONREAD, &bufsize);
- Py_END_ALLOW_THREADS
-
- if (r == -1) {
- PyErr_SetFromErrno(PyExc_OSError);
- goto bail;
- }
- if (bufsize == 0)
- goto done;
- }
- else {
- static long name_max;
- static long name_fd = -1;
- long min;
-
- if (name_fd != fd) {
- name_fd = fd;
- Py_BEGIN_ALLOW_THREADS
- name_max = fpathconf(fd, _PC_NAME_MAX);
- Py_END_ALLOW_THREADS
- }
-
- min = sizeof(struct inotify_event) + name_max + 1;
-
- if (bufsize < min) {
- PyErr_Format(PyExc_ValueError, "bufsize must be at least %d",
- (int) min);
- goto bail;
- }
- }
-
- buf = alloca(bufsize);
-
- Py_BEGIN_ALLOW_THREADS
- nread = read(fd, buf, bufsize);
- Py_END_ALLOW_THREADS
-
- if (nread == -1) {
- PyErr_SetFromErrno(PyExc_OSError);
- goto bail;
- }
-
- ctor_args = PyTuple_New(0);
-
- if (ctor_args == NULL)
- goto bail;
-
- pos = 0;
-
- while (pos < nread) {
- struct inotify_event *in = (struct inotify_event *) (buf + pos);
- struct event *evt;
- PyObject *obj;
-
- obj = PyObject_CallObject((PyObject *) &event_type, ctor_args);
-
- if (obj == NULL)
- goto bail;
-
- evt = (struct event *) obj;
-
- evt->wd = PyInt_FromLong(in->wd);
- evt->mask = PyInt_FromLong(in->mask);
- if (in->mask & IN_MOVE)
- evt->cookie = PyInt_FromLong(in->cookie);
- else {
- Py_INCREF(Py_None);
- evt->cookie = Py_None;
- }
- if (in->len)
- evt->name = PyString_FromString(in->name);
- else {
- Py_INCREF(Py_None);
- evt->name = Py_None;
- }
-
- if (!evt->wd || !evt->mask || !evt->cookie || !evt->name)
- goto mybail;
-
- if (PyList_Append(ret, obj) == -1)
- goto mybail;
-
- pos += sizeof(struct inotify_event) + in->len;
- continue;
-
- mybail:
- Py_CLEAR(evt->wd);
- Py_CLEAR(evt->mask);
- Py_CLEAR(evt->cookie);
- Py_CLEAR(evt->name);
- Py_DECREF(obj);
-
- goto bail;
- }
-
- goto done;
-
-bail:
- Py_CLEAR(ret);
-
-done:
- Py_XDECREF(ctor_args);
-
- return ret;
-}
-
-PyDoc_STRVAR(
- read_doc,
- "read(fd, bufsize[=65536]) -> list_of_events\n"
- "\n"
- "\nRead inotify events from a file descriptor.\n"
- "\n"
- " fd: file descriptor returned by init()\n"
- " bufsize: size of buffer to read into, in bytes\n"
- "\n"
- "Return a list of event objects.\n"
- "\n"
- "If bufsize is > 0, block until events are available to be read.\n"
- "Otherwise, immediately return all events that can be read without\n"
- "blocking.");
-
-
-static PyMethodDef methods[] = {
- {"init", init, METH_VARARGS, init_doc},
- {"add_watch", add_watch, METH_VARARGS, add_watch_doc},
- {"remove_watch", remove_watch, METH_VARARGS, remove_watch_doc},
- {"read", read_events, METH_VARARGS, read_doc},
- {"decode_mask", pydecode_mask, METH_VARARGS, decode_mask_doc},
- {NULL},
-};
-
-void init_inotify(void)
-{
- PyObject *mod, *dict;
-
- if (PyType_Ready(&event_type) == -1)
- return;
-
- mod = Py_InitModule3("_inotify", methods, doc);
-
- dict = PyModule_GetDict(mod);
-
- if (dict)
- define_consts(dict);
-}
--- a/sys/src/cmd/hg/hgext/inotify/linux/watcher.py
+++ /dev/null
@@ -1,335 +1,0 @@
-# watcher.py - high-level interfaces to the Linux inotify subsystem
-
-# Copyright 2006 Bryan O'Sullivan <bos@serpentine.com>
-
-# This library is free software; you can redistribute it and/or modify
-# it under the terms of version 2.1 of the GNU Lesser General Public
-# License, incorporated herein by reference.
-
-'''High-level interfaces to the Linux inotify subsystem.
-
-The inotify subsystem provides an efficient mechanism for file status
-monitoring and change notification.
-
-The watcher class hides the low-level details of the inotify
-interface, and provides a Pythonic wrapper around it. It generates
-events that provide somewhat more information than raw inotify makes
-available.
-
-The autowatcher class is more useful, as it automatically watches
-newly-created directories on your behalf.'''
-
-__author__ = "Bryan O'Sullivan <bos@serpentine.com>"
-
-import _inotify as inotify
-import array
-import errno
-import fcntl
-import os
-import termios
-
-
-class event(object):
- '''Derived inotify event class.
-
- The following fields are available:
-
- mask: event mask, indicating what kind of event this is
-
- cookie: rename cookie, if a rename-related event
-
- path: path of the directory in which the event occurred
-
- name: name of the directory entry to which the event occurred
- (may be None if the event happened to a watched directory)
-
- fullpath: complete path at which the event occurred
-
- wd: watch descriptor that triggered this event'''
-
- __slots__ = (
- 'cookie',
- 'fullpath',
- 'mask',
- 'name',
- 'path',
- 'raw',
- 'wd',
- )
-
- def __init__(self, raw, path):
- self.path = path
- self.raw = raw
- if raw.name:
- self.fullpath = path + '/' + raw.name
- else:
- self.fullpath = path
-
- self.wd = raw.wd
- self.mask = raw.mask
- self.cookie = raw.cookie
- self.name = raw.name
-
- def __repr__(self):
- r = repr(self.raw)
- return 'event(path=' + repr(self.path) + ', ' + r[r.find('(')+1:]
-
-
-_event_props = {
- 'access': 'File was accessed',
- 'modify': 'File was modified',
- 'attrib': 'Attribute of a directory entry was changed',
- 'close_write': 'File was closed after being written to',
- 'close_nowrite': 'File was closed without being written to',
- 'open': 'File was opened',
- 'moved_from': 'Directory entry was renamed from this name',
- 'moved_to': 'Directory entry was renamed to this name',
- 'create': 'Directory entry was created',
- 'delete': 'Directory entry was deleted',
- 'delete_self': 'The watched directory entry was deleted',
- 'move_self': 'The watched directory entry was renamed',
- 'unmount': 'Directory was unmounted, and can no longer be watched',
- 'q_overflow': 'Kernel dropped events due to queue overflow',
- 'ignored': 'Directory entry is no longer being watched',
- 'isdir': 'Event occurred on a directory',
- }
-
-for k, v in _event_props.iteritems():
- mask = getattr(inotify, 'IN_' + k.upper())
- def getter(self):
- return self.mask & mask
- getter.__name__ = k
- getter.__doc__ = v
- setattr(event, k, property(getter, doc=v))
-
-del _event_props
-
-
-class watcher(object):
- '''Provide a Pythonic interface to the low-level inotify API.
-
- Also adds derived information to each event that is not available
- through the normal inotify API, such as directory name.'''
-
- __slots__ = (
- 'fd',
- '_paths',
- '_wds',
- )
-
- def __init__(self):
- '''Create a new inotify instance.'''
-
- self.fd = inotify.init()
- self._paths = {}
- self._wds = {}
-
- def fileno(self):
- '''Return the file descriptor this watcher uses.
-
- Useful for passing to select and poll.'''
-
- return self.fd
-
- def add(self, path, mask):
- '''Add or modify a watch.
-
- Return the watch descriptor added or modified.'''
-
- path = os.path.normpath(path)
- wd = inotify.add_watch(self.fd, path, mask)
- self._paths[path] = wd, mask
- self._wds[wd] = path, mask
- return wd
-
- def remove(self, wd):
- '''Remove the given watch.'''
-
- inotify.remove_watch(self.fd, wd)
- self._remove(wd)
-
- def _remove(self, wd):
- path_mask = self._wds.pop(wd, None)
- if path_mask is not None:
- self._paths.pop(path_mask[0])
-
- def path(self, path):
- '''Return a (watch descriptor, event mask) pair for the given path.
-
- If the path is not being watched, return None.'''
-
- return self._paths.get(path)
-
- def wd(self, wd):
- '''Return a (path, event mask) pair for the given watch descriptor.
-
- If the watch descriptor is not valid or not associated with
- this watcher, return None.'''
-
- return self._wds.get(wd)
-
- def read(self, bufsize=None):
- '''Read a list of queued inotify events.
-
- If bufsize is zero, only return those events that can be read
- immediately without blocking. Otherwise, block until events are
- available.'''
-
- events = []
- for evt in inotify.read(self.fd, bufsize):
- events.append(event(evt, self._wds[evt.wd][0]))
- if evt.mask & inotify.IN_IGNORED:
- self._remove(evt.wd)
- elif evt.mask & inotify.IN_UNMOUNT:
- self.close()
- return events
-
- def close(self):
- '''Shut down this watcher.
-
- All subsequent method calls are likely to raise exceptions.'''
-
- os.close(self.fd)
- self.fd = None
- self._paths = None
- self._wds = None
-
- def __len__(self):
- '''Return the number of active watches.'''
-
- return len(self._paths)
-
- def __iter__(self):
- '''Yield a (path, watch descriptor, event mask) tuple for each
- entry being watched.'''
-
- for path, (wd, mask) in self._paths.iteritems():
- yield path, wd, mask
-
- def __del__(self):
- if self.fd is not None:
- os.close(self.fd)
-
- ignored_errors = [errno.ENOENT, errno.EPERM, errno.ENOTDIR]
-
- def add_iter(self, path, mask, onerror=None):
- '''Add or modify watches over path and its subdirectories.
-
- Yield each added or modified watch descriptor.
-
- To ensure that this method runs to completion, you must
- iterate over all of its results, even if you do not care what
- they are. For example:
-
- for wd in w.add_iter(path, mask):
- pass
-
- By default, errors are ignored. If optional arg "onerror" is
- specified, it should be a function; it will be called with one
- argument, an OSError instance. It can report the error to
- continue with the walk, or raise the exception to abort the
- walk.'''
-
- # Add the IN_ONLYDIR flag to the event mask, to avoid a possible
- # race when adding a subdirectory. In the time between the
- # event being queued by the kernel and us processing it, the
- # directory may have been deleted, or replaced with a different
- # kind of entry with the same name.
-
- submask = mask | inotify.IN_ONLYDIR
-
- try:
- yield self.add(path, mask)
- except OSError, err:
- if onerror and err.errno not in self.ignored_errors:
- onerror(err)
- for root, dirs, names in os.walk(path, topdown=False, onerror=onerror):
- for d in dirs:
- try:
- yield self.add(root + '/' + d, submask)
- except OSError, err:
- if onerror and err.errno not in self.ignored_errors:
- onerror(err)
-
- def add_all(self, path, mask, onerror=None):
- '''Add or modify watches over path and its subdirectories.
-
- Return a list of added or modified watch descriptors.
-
- By default, errors are ignored. If optional arg "onerror" is
- specified, it should be a function; it will be called with one
- argument, an OSError instance. It can report the error to
- continue with the walk, or raise the exception to abort the
- walk.'''
-
- return [w for w in self.add_iter(path, mask, onerror)]
-
-
-class autowatcher(watcher):
- '''watcher class that automatically watches newly created directories.'''
-
- __slots__ = (
- 'addfilter',
- )
-
- def __init__(self, addfilter=None):
- '''Create a new inotify instance.
-
- This instance will automatically watch newly created
- directories.
-
- If the optional addfilter parameter is not None, it must be a
- callable that takes one parameter. It will be called each time
- a directory is about to be automatically watched. If it returns
- True, the directory will be watched if it still exists,
- otherwise, it will beb skipped.'''
-
- super(autowatcher, self).__init__()
- self.addfilter = addfilter
-
- _dir_create_mask = inotify.IN_ISDIR | inotify.IN_CREATE
-
- def read(self, bufsize=None):
- events = super(autowatcher, self).read(bufsize)
- for evt in events:
- if evt.mask & self._dir_create_mask == self._dir_create_mask:
- if self.addfilter is None or self.addfilter(evt):
- parentmask = self._wds[evt.wd][1]
- # See note about race avoidance via IN_ONLYDIR above.
- mask = parentmask | inotify.IN_ONLYDIR
- try:
- self.add_all(evt.fullpath, mask)
- except OSError, err:
- if err.errno not in self.ignored_errors:
- raise
- return events
-
-
-class threshold(object):
- '''Class that indicates whether a file descriptor has reached a
- threshold of readable bytes available.
-
- This class is not thread-safe.'''
-
- __slots__ = (
- 'fd',
- 'threshold',
- '_iocbuf',
- )
-
- def __init__(self, fd, threshold=1024):
- self.fd = fd
- self.threshold = threshold
- self._iocbuf = array.array('i', [0])
-
- def readable(self):
- '''Return the number of bytes readable on this file descriptor.'''
-
- fcntl.ioctl(self.fd, termios.FIONREAD, self._iocbuf, True)
- return self._iocbuf[0]
-
- def __call__(self):
- '''Indicate whether the number of readable bytes has met or
- exceeded the threshold.'''
-
- return self.readable() >= self.threshold
--- a/sys/src/cmd/hg/mercurial/hgweb/__init__.py
+++ /dev/null
@@ -1,16 +1,0 @@
-# hgweb/__init__.py - web interface to a mercurial repository
-#
-# Copyright 21 May 2005 - (c) 2005 Jake Edge <jake@edge2.net>
-# Copyright 2005 Matt Mackall <mpm@selenic.com>
-#
-# This software may be used and distributed according to the terms of the
-# GNU General Public License version 2, incorporated herein by reference.
-
-import hgweb_mod, hgwebdir_mod
-
-def hgweb(*args, **kwargs):
- return hgweb_mod.hgweb(*args, **kwargs)
-
-def hgwebdir(*args, **kwargs):
- return hgwebdir_mod.hgwebdir(*args, **kwargs)
-
--- a/sys/src/cmd/hg/mercurial/hgweb/common.py
+++ /dev/null
@@ -1,105 +1,0 @@
-# hgweb/common.py - Utility functions needed by hgweb_mod and hgwebdir_mod
-#
-# Copyright 21 May 2005 - (c) 2005 Jake Edge <jake@edge2.net>
-# Copyright 2005, 2006 Matt Mackall <mpm@selenic.com>
-#
-# This software may be used and distributed according to the terms of the
-# GNU General Public License version 2, incorporated herein by reference.
-
-import errno, mimetypes, os
-
-HTTP_OK = 200
-HTTP_BAD_REQUEST = 400
-HTTP_UNAUTHORIZED = 401
-HTTP_FORBIDDEN = 403
-HTTP_NOT_FOUND = 404
-HTTP_METHOD_NOT_ALLOWED = 405
-HTTP_SERVER_ERROR = 500
-
-class ErrorResponse(Exception):
- def __init__(self, code, message=None, headers=[]):
- Exception.__init__(self)
- self.code = code
- self.headers = headers
- if message is not None:
- self.message = message
- else:
- self.message = _statusmessage(code)
-
-def _statusmessage(code):
- from BaseHTTPServer import BaseHTTPRequestHandler
- responses = BaseHTTPRequestHandler.responses
- return responses.get(code, ('Error', 'Unknown error'))[0]
-
-def statusmessage(code):
- return '%d %s' % (code, _statusmessage(code))
-
-def get_mtime(repo_path):
- store_path = os.path.join(repo_path, ".hg")
- if not os.path.isdir(os.path.join(store_path, "data")):
- store_path = os.path.join(store_path, "store")
- cl_path = os.path.join(store_path, "00changelog.i")
- if os.path.exists(cl_path):
- return os.stat(cl_path).st_mtime
- else:
- return os.stat(store_path).st_mtime
-
-def staticfile(directory, fname, req):
- """return a file inside directory with guessed Content-Type header
-
- fname always uses '/' as directory separator and isn't allowed to
- contain unusual path components.
- Content-Type is guessed using the mimetypes module.
- Return an empty string if fname is illegal or file not found.
-
- """
- parts = fname.split('/')
- for part in parts:
- if (part in ('', os.curdir, os.pardir) or
- os.sep in part or os.altsep is not None and os.altsep in part):
- return ""
- fpath = os.path.join(*parts)
- if isinstance(directory, str):
- directory = [directory]
- for d in directory:
- path = os.path.join(d, fpath)
- if os.path.exists(path):
- break
- try:
- os.stat(path)
- ct = mimetypes.guess_type(path)[0] or "text/plain"
- req.respond(HTTP_OK, ct, length = os.path.getsize(path))
- return open(path, 'rb').read()
- except TypeError:
- raise ErrorResponse(HTTP_SERVER_ERROR, 'illegal filename')
- except OSError, err:
- if err.errno == errno.ENOENT:
- raise ErrorResponse(HTTP_NOT_FOUND)
- else:
- raise ErrorResponse(HTTP_SERVER_ERROR, err.strerror)
-
-def paritygen(stripecount, offset=0):
- """count parity of horizontal stripes for easier reading"""
- if stripecount and offset:
- # account for offset, e.g. due to building the list in reverse
- count = (stripecount + offset) % stripecount
- parity = (stripecount + offset) / stripecount & 1
- else:
- count = 0
- parity = 0
- while True:
- yield parity
- count += 1
- if stripecount and count >= stripecount:
- parity = 1 - parity
- count = 0
-
-def get_contact(config):
- """Return repo contact information or empty string.
-
- web.contact is the primary source, but if that is not set, try
- ui.username or $EMAIL as a fallback to display something useful.
- """
- return (config("web", "contact") or
- config("ui", "username") or
- os.environ.get("EMAIL") or "")
--- a/sys/src/cmd/hg/mercurial/hgweb/hgweb_mod.py
+++ /dev/null
@@ -1,315 +1,0 @@
-# hgweb/hgweb_mod.py - Web interface for a repository.
-#
-# Copyright 21 May 2005 - (c) 2005 Jake Edge <jake@edge2.net>
-# Copyright 2005-2007 Matt Mackall <mpm@selenic.com>
-#
-# This software may be used and distributed according to the terms of the
-# GNU General Public License version 2, incorporated herein by reference.
-
-import os
-from mercurial import ui, hg, hook, error, encoding, templater
-from common import get_mtime, ErrorResponse
-from common import HTTP_OK, HTTP_BAD_REQUEST, HTTP_NOT_FOUND, HTTP_SERVER_ERROR
-from common import HTTP_UNAUTHORIZED, HTTP_METHOD_NOT_ALLOWED
-from request import wsgirequest
-import webcommands, protocol, webutil
-
-perms = {
- 'changegroup': 'pull',
- 'changegroupsubset': 'pull',
- 'unbundle': 'push',
- 'stream_out': 'pull',
-}
-
-class hgweb(object):
- def __init__(self, repo, name=None):
- if isinstance(repo, str):
- u = ui.ui()
- u.setconfig('ui', 'report_untrusted', 'off')
- u.setconfig('ui', 'interactive', 'off')
- self.repo = hg.repository(u, repo)
- else:
- self.repo = repo
-
- hook.redirect(True)
- self.mtime = -1
- self.reponame = name
- self.archives = 'zip', 'gz', 'bz2'
- self.stripecount = 1
- # a repo owner may set web.templates in .hg/hgrc to get any file
- # readable by the user running the CGI script
- self.templatepath = self.config('web', 'templates')
-
- # The CGI scripts are often run by a user different from the repo owner.
- # Trust the settings from the .hg/hgrc files by default.
- def config(self, section, name, default=None, untrusted=True):
- return self.repo.ui.config(section, name, default,
- untrusted=untrusted)
-
- def configbool(self, section, name, default=False, untrusted=True):
- return self.repo.ui.configbool(section, name, default,
- untrusted=untrusted)
-
- def configlist(self, section, name, default=None, untrusted=True):
- return self.repo.ui.configlist(section, name, default,
- untrusted=untrusted)
-
- def refresh(self):
- mtime = get_mtime(self.repo.root)
- if mtime != self.mtime:
- self.mtime = mtime
- self.repo = hg.repository(self.repo.ui, self.repo.root)
- self.maxchanges = int(self.config("web", "maxchanges", 10))
- self.stripecount = int(self.config("web", "stripes", 1))
- self.maxshortchanges = int(self.config("web", "maxshortchanges", 60))
- self.maxfiles = int(self.config("web", "maxfiles", 10))
- self.allowpull = self.configbool("web", "allowpull", True)
- encoding.encoding = self.config("web", "encoding",
- encoding.encoding)
-
- def run(self):
- if not os.environ.get('GATEWAY_INTERFACE', '').startswith("CGI/1."):
- raise RuntimeError("This function is only intended to be "
- "called while running as a CGI script.")
- import mercurial.hgweb.wsgicgi as wsgicgi
- wsgicgi.launch(self)
-
- def __call__(self, env, respond):
- req = wsgirequest(env, respond)
- return self.run_wsgi(req)
-
- def run_wsgi(self, req):
-
- self.refresh()
-
- # work with CGI variables to create coherent structure
- # use SCRIPT_NAME, PATH_INFO and QUERY_STRING as well as our REPO_NAME
-
- req.url = req.env['SCRIPT_NAME']
- if not req.url.endswith('/'):
- req.url += '/'
- if 'REPO_NAME' in req.env:
- req.url += req.env['REPO_NAME'] + '/'
-
- if 'PATH_INFO' in req.env:
- parts = req.env['PATH_INFO'].strip('/').split('/')
- repo_parts = req.env.get('REPO_NAME', '').split('/')
- if parts[:len(repo_parts)] == repo_parts:
- parts = parts[len(repo_parts):]
- query = '/'.join(parts)
- else:
- query = req.env['QUERY_STRING'].split('&', 1)[0]
- query = query.split(';', 1)[0]
-
- # process this if it's a protocol request
- # protocol bits don't need to create any URLs
- # and the clients always use the old URL structure
-
- cmd = req.form.get('cmd', [''])[0]
- if cmd and cmd in protocol.__all__:
- if query:
- raise ErrorResponse(HTTP_NOT_FOUND)
- try:
- if cmd in perms:
- try:
- self.check_perm(req, perms[cmd])
- except ErrorResponse, inst:
- if cmd == 'unbundle':
- req.drain()
- raise
- method = getattr(protocol, cmd)
- return method(self.repo, req)
- except ErrorResponse, inst:
- req.respond(inst, protocol.HGTYPE)
- if not inst.message:
- return []
- return '0\n%s\n' % inst.message,
-
- # translate user-visible url structure to internal structure
-
- args = query.split('/', 2)
- if 'cmd' not in req.form and args and args[0]:
-
- cmd = args.pop(0)
- style = cmd.rfind('-')
- if style != -1:
- req.form['style'] = [cmd[:style]]
- cmd = cmd[style+1:]
-
- # avoid accepting e.g. style parameter as command
- if hasattr(webcommands, cmd):
- req.form['cmd'] = [cmd]
- else:
- cmd = ''
-
- if cmd == 'static':
- req.form['file'] = ['/'.join(args)]
- else:
- if args and args[0]:
- node = args.pop(0)
- req.form['node'] = [node]
- if args:
- req.form['file'] = args
-
- if cmd == 'archive':
- fn = req.form['node'][0]
- for type_, spec in self.archive_specs.iteritems():
- ext = spec[2]
- if fn.endswith(ext):
- req.form['node'] = [fn[:-len(ext)]]
- req.form['type'] = [type_]
-
- # process the web interface request
-
- try:
- tmpl = self.templater(req)
- ctype = tmpl('mimetype', encoding=encoding.encoding)
- ctype = templater.stringify(ctype)
-
- # check read permissions non-static content
- if cmd != 'static':
- self.check_perm(req, None)
-
- if cmd == '':
- req.form['cmd'] = [tmpl.cache['default']]
- cmd = req.form['cmd'][0]
-
- if cmd not in webcommands.__all__:
- msg = 'no such method: %s' % cmd
- raise ErrorResponse(HTTP_BAD_REQUEST, msg)
- elif cmd == 'file' and 'raw' in req.form.get('style', []):
- self.ctype = ctype
- content = webcommands.rawfile(self, req, tmpl)
- else:
- content = getattr(webcommands, cmd)(self, req, tmpl)
- req.respond(HTTP_OK, ctype)
-
- return content
-
- except error.LookupError, err:
- req.respond(HTTP_NOT_FOUND, ctype)
- msg = str(err)
- if 'manifest' not in msg:
- msg = 'revision not found: %s' % err.name
- return tmpl('error', error=msg)
- except (error.RepoError, error.RevlogError), inst:
- req.respond(HTTP_SERVER_ERROR, ctype)
- return tmpl('error', error=str(inst))
- except ErrorResponse, inst:
- req.respond(inst, ctype)
- return tmpl('error', error=inst.message)
-
- def templater(self, req):
-
- # determine scheme, port and server name
- # this is needed to create absolute urls
-
- proto = req.env.get('wsgi.url_scheme')
- if proto == 'https':
- proto = 'https'
- default_port = "443"
- else:
- proto = 'http'
- default_port = "80"
-
- port = req.env["SERVER_PORT"]
- port = port != default_port and (":" + port) or ""
- urlbase = '%s://%s%s' % (proto, req.env['SERVER_NAME'], port)
- staticurl = self.config("web", "staticurl") or req.url + 'static/'
- if not staticurl.endswith('/'):
- staticurl += '/'
-
- # some functions for the templater
-
- def header(**map):
- yield tmpl('header', encoding=encoding.encoding, **map)
-
- def footer(**map):
- yield tmpl("footer", **map)
-
- def motd(**map):
- yield self.config("web", "motd", "")
-
- # figure out which style to use
-
- vars = {}
- style = self.config("web", "style", "paper")
- if 'style' in req.form:
- style = req.form['style'][0]
- vars['style'] = style
-
- start = req.url[-1] == '?' and '&' or '?'
- sessionvars = webutil.sessionvars(vars, start)
- mapfile = templater.stylemap(style, self.templatepath)
-
- if not self.reponame:
- self.reponame = (self.config("web", "name")
- or req.env.get('REPO_NAME')
- or req.url.strip('/') or self.repo.root)
-
- # create the templater
-
- tmpl = templater.templater(mapfile,
- defaults={"url": req.url,
- "staticurl": staticurl,
- "urlbase": urlbase,
- "repo": self.reponame,
- "header": header,
- "footer": footer,
- "motd": motd,
- "sessionvars": sessionvars
- })
- return tmpl
-
- def archivelist(self, nodeid):
- allowed = self.configlist("web", "allow_archive")
- for i, spec in self.archive_specs.iteritems():
- if i in allowed or self.configbool("web", "allow" + i):
- yield {"type" : i, "extension" : spec[2], "node" : nodeid}
-
- archive_specs = {
- 'bz2': ('application/x-tar', 'tbz2', '.tar.bz2', None),
- 'gz': ('application/x-tar', 'tgz', '.tar.gz', None),
- 'zip': ('application/zip', 'zip', '.zip', None),
- }
-
- def check_perm(self, req, op):
- '''Check permission for operation based on request data (including
- authentication info). Return if op allowed, else raise an ErrorResponse
- exception.'''
-
- user = req.env.get('REMOTE_USER')
-
- deny_read = self.configlist('web', 'deny_read')
- if deny_read and (not user or deny_read == ['*'] or user in deny_read):
- raise ErrorResponse(HTTP_UNAUTHORIZED, 'read not authorized')
-
- allow_read = self.configlist('web', 'allow_read')
- result = (not allow_read) or (allow_read == ['*'])
- if not (result or user in allow_read):
- raise ErrorResponse(HTTP_UNAUTHORIZED, 'read not authorized')
-
- if op == 'pull' and not self.allowpull:
- raise ErrorResponse(HTTP_UNAUTHORIZED, 'pull not authorized')
- elif op == 'pull' or op is None: # op is None for interface requests
- return
-
- # enforce that you can only push using POST requests
- if req.env['REQUEST_METHOD'] != 'POST':
- msg = 'push requires POST request'
- raise ErrorResponse(HTTP_METHOD_NOT_ALLOWED, msg)
-
- # require ssl by default for pushing, auth info cannot be sniffed
- # and replayed
- scheme = req.env.get('wsgi.url_scheme')
- if self.configbool('web', 'push_ssl', True) and scheme != 'https':
- raise ErrorResponse(HTTP_OK, 'ssl required')
-
- deny = self.configlist('web', 'deny_push')
- if deny and (not user or deny == ['*'] or user in deny):
- raise ErrorResponse(HTTP_UNAUTHORIZED, 'push not authorized')
-
- allow = self.configlist('web', 'allow_push')
- result = allow and (allow == ['*'] or user in allow)
- if not result:
- raise ErrorResponse(HTTP_UNAUTHORIZED, 'push not authorized')
--- a/sys/src/cmd/hg/mercurial/hgweb/hgwebdir_mod.py
+++ /dev/null
@@ -1,333 +1,0 @@
-# hgweb/hgwebdir_mod.py - Web interface for a directory of repositories.
-#
-# Copyright 21 May 2005 - (c) 2005 Jake Edge <jake@edge2.net>
-# Copyright 2005, 2006 Matt Mackall <mpm@selenic.com>
-#
-# This software may be used and distributed according to the terms of the
-# GNU General Public License version 2, incorporated herein by reference.
-
-import os, re, time
-from mercurial.i18n import _
-from mercurial import ui, hg, util, templater
-from mercurial import error, encoding
-from common import ErrorResponse, get_mtime, staticfile, paritygen,\
- get_contact, HTTP_OK, HTTP_NOT_FOUND, HTTP_SERVER_ERROR
-from hgweb_mod import hgweb
-from request import wsgirequest
-import webutil
-
-def cleannames(items):
- return [(util.pconvert(name).strip('/'), path) for name, path in items]
-
-def findrepos(paths):
- repos = {}
- for prefix, root in cleannames(paths):
- roothead, roottail = os.path.split(root)
- # "foo = /bar/*" makes every subrepo of /bar/ to be
- # mounted as foo/subrepo
- # and "foo = /bar/**" also recurses into the subdirectories,
- # remember to use it without working dir.
- try:
- recurse = {'*': False, '**': True}[roottail]
- except KeyError:
- repos[prefix] = root
- continue
- roothead = os.path.normpath(roothead)
- for path in util.walkrepos(roothead, followsym=True, recurse=recurse):
- path = os.path.normpath(path)
- name = util.pconvert(path[len(roothead):]).strip('/')
- if prefix:
- name = prefix + '/' + name
- repos[name] = path
- return repos.items()
-
-class hgwebdir(object):
- refreshinterval = 20
-
- def __init__(self, conf, baseui=None):
- self.conf = conf
- self.baseui = baseui
- self.lastrefresh = 0
- self.refresh()
-
- def refresh(self):
- if self.lastrefresh + self.refreshinterval > time.time():
- return
-
- if self.baseui:
- self.ui = self.baseui.copy()
- else:
- self.ui = ui.ui()
- self.ui.setconfig('ui', 'report_untrusted', 'off')
- self.ui.setconfig('ui', 'interactive', 'off')
-
- if not isinstance(self.conf, (dict, list, tuple)):
- map = {'paths': 'hgweb-paths'}
- self.ui.readconfig(self.conf, remap=map, trust=True)
- paths = self.ui.configitems('hgweb-paths')
- elif isinstance(self.conf, (list, tuple)):
- paths = self.conf
- elif isinstance(self.conf, dict):
- paths = self.conf.items()
-
- encoding.encoding = self.ui.config('web', 'encoding',
- encoding.encoding)
- self.motd = self.ui.config('web', 'motd')
- self.style = self.ui.config('web', 'style', 'paper')
- self.stripecount = self.ui.config('web', 'stripes', 1)
- if self.stripecount:
- self.stripecount = int(self.stripecount)
- self._baseurl = self.ui.config('web', 'baseurl')
-
- self.repos = findrepos(paths)
- for prefix, root in self.ui.configitems('collections'):
- prefix = util.pconvert(prefix)
- for path in util.walkrepos(root, followsym=True):
- repo = os.path.normpath(path)
- name = util.pconvert(repo)
- if name.startswith(prefix):
- name = name[len(prefix):]
- self.repos.append((name.lstrip('/'), repo))
-
- self.repos.sort()
- self.lastrefresh = time.time()
-
- def run(self):
- if not os.environ.get('GATEWAY_INTERFACE', '').startswith("CGI/1."):
- raise RuntimeError("This function is only intended to be "
- "called while running as a CGI script.")
- import mercurial.hgweb.wsgicgi as wsgicgi
- wsgicgi.launch(self)
-
- def __call__(self, env, respond):
- req = wsgirequest(env, respond)
- return self.run_wsgi(req)
-
- def read_allowed(self, ui, req):
- """Check allow_read and deny_read config options of a repo's ui object
- to determine user permissions. By default, with neither option set (or
- both empty), allow all users to read the repo. There are two ways a
- user can be denied read access: (1) deny_read is not empty, and the
- user is unauthenticated or deny_read contains user (or *), and (2)
- allow_read is not empty and the user is not in allow_read. Return True
- if user is allowed to read the repo, else return False."""
-
- user = req.env.get('REMOTE_USER')
-
- deny_read = ui.configlist('web', 'deny_read', untrusted=True)
- if deny_read and (not user or deny_read == ['*'] or user in deny_read):
- return False
-
- allow_read = ui.configlist('web', 'allow_read', untrusted=True)
- # by default, allow reading if no allow_read option has been set
- if (not allow_read) or (allow_read == ['*']) or (user in allow_read):
- return True
-
- return False
-
- def run_wsgi(self, req):
- try:
- try:
- self.refresh()
-
- virtual = req.env.get("PATH_INFO", "").strip('/')
- tmpl = self.templater(req)
- ctype = tmpl('mimetype', encoding=encoding.encoding)
- ctype = templater.stringify(ctype)
-
- # a static file
- if virtual.startswith('static/') or 'static' in req.form:
- if virtual.startswith('static/'):
- fname = virtual[7:]
- else:
- fname = req.form['static'][0]
- static = templater.templatepath('static')
- return (staticfile(static, fname, req),)
-
- # top-level index
- elif not virtual:
- req.respond(HTTP_OK, ctype)
- return self.makeindex(req, tmpl)
-
- # nested indexes and hgwebs
-
- repos = dict(self.repos)
- while virtual:
- real = repos.get(virtual)
- if real:
- req.env['REPO_NAME'] = virtual
- try:
- repo = hg.repository(self.ui, real)
- return hgweb(repo).run_wsgi(req)
- except IOError, inst:
- msg = inst.strerror
- raise ErrorResponse(HTTP_SERVER_ERROR, msg)
- except error.RepoError, inst:
- raise ErrorResponse(HTTP_SERVER_ERROR, str(inst))
-
- # browse subdirectories
- subdir = virtual + '/'
- if [r for r in repos if r.startswith(subdir)]:
- req.respond(HTTP_OK, ctype)
- return self.makeindex(req, tmpl, subdir)
-
- up = virtual.rfind('/')
- if up < 0:
- break
- virtual = virtual[:up]
-
- # prefixes not found
- req.respond(HTTP_NOT_FOUND, ctype)
- return tmpl("notfound", repo=virtual)
-
- except ErrorResponse, err:
- req.respond(err, ctype)
- return tmpl('error', error=err.message or '')
- finally:
- tmpl = None
-
- def makeindex(self, req, tmpl, subdir=""):
-
- def archivelist(ui, nodeid, url):
- allowed = ui.configlist("web", "allow_archive", untrusted=True)
- for i in [('zip', '.zip'), ('gz', '.tar.gz'), ('bz2', '.tar.bz2')]:
- if i[0] in allowed or ui.configbool("web", "allow" + i[0],
- untrusted=True):
- yield {"type" : i[0], "extension": i[1],
- "node": nodeid, "url": url}
-
- sortdefault = 'name', False
- def entries(sortcolumn="", descending=False, subdir="", **map):
- rows = []
- parity = paritygen(self.stripecount)
- for name, path in self.repos:
- if not name.startswith(subdir):
- continue
- name = name[len(subdir):]
-
- u = self.ui.copy()
- try:
- u.readconfig(os.path.join(path, '.hg', 'hgrc'))
- except Exception, e:
- u.warn(_('error reading %s/.hg/hgrc: %s\n') % (path, e))
- continue
- def get(section, name, default=None):
- return u.config(section, name, default, untrusted=True)
-
- if u.configbool("web", "hidden", untrusted=True):
- continue
-
- if not self.read_allowed(u, req):
- continue
-
- parts = [name]
- if 'PATH_INFO' in req.env:
- parts.insert(0, req.env['PATH_INFO'].rstrip('/'))
- if req.env['SCRIPT_NAME']:
- parts.insert(0, req.env['SCRIPT_NAME'])
- m = re.match('((?:https?://)?)(.*)', '/'.join(parts))
- # squish repeated slashes out of the path component
- url = m.group(1) + re.sub('/+', '/', m.group(2)) + '/'
-
- # update time with local timezone
- try:
- d = (get_mtime(path), util.makedate()[1])
- except OSError:
- continue
-
- contact = get_contact(get)
- description = get("web", "description", "")
- name = get("web", "name", name)
- row = dict(contact=contact or "unknown",
- contact_sort=contact.upper() or "unknown",
- name=name,
- name_sort=name,
- url=url,
- description=description or "unknown",
- description_sort=description.upper() or "unknown",
- lastchange=d,
- lastchange_sort=d[1]-d[0],
- archives=archivelist(u, "tip", url))
- if (not sortcolumn or (sortcolumn, descending) == sortdefault):
- # fast path for unsorted output
- row['parity'] = parity.next()
- yield row
- else:
- rows.append((row["%s_sort" % sortcolumn], row))
- if rows:
- rows.sort()
- if descending:
- rows.reverse()
- for key, row in rows:
- row['parity'] = parity.next()
- yield row
-
- self.refresh()
- sortable = ["name", "description", "contact", "lastchange"]
- sortcolumn, descending = sortdefault
- if 'sort' in req.form:
- sortcolumn = req.form['sort'][0]
- descending = sortcolumn.startswith('-')
- if descending:
- sortcolumn = sortcolumn[1:]
- if sortcolumn not in sortable:
- sortcolumn = ""
-
- sort = [("sort_%s" % column,
- "%s%s" % ((not descending and column == sortcolumn)
- and "-" or "", column))
- for column in sortable]
-
- self.refresh()
- if self._baseurl is not None:
- req.env['SCRIPT_NAME'] = self._baseurl
-
- return tmpl("index", entries=entries, subdir=subdir,
- sortcolumn=sortcolumn, descending=descending,
- **dict(sort))
-
- def templater(self, req):
-
- def header(**map):
- yield tmpl('header', encoding=encoding.encoding, **map)
-
- def footer(**map):
- yield tmpl("footer", **map)
-
- def motd(**map):
- if self.motd is not None:
- yield self.motd
- else:
- yield config('web', 'motd', '')
-
- def config(section, name, default=None, untrusted=True):
- return self.ui.config(section, name, default, untrusted)
-
- if self._baseurl is not None:
- req.env['SCRIPT_NAME'] = self._baseurl
-
- url = req.env.get('SCRIPT_NAME', '')
- if not url.endswith('/'):
- url += '/'
-
- vars = {}
- style = self.style
- if 'style' in req.form:
- vars['style'] = style = req.form['style'][0]
- start = url[-1] == '?' and '&' or '?'
- sessionvars = webutil.sessionvars(vars, start)
-
- staticurl = config('web', 'staticurl') or url + 'static/'
- if not staticurl.endswith('/'):
- staticurl += '/'
-
- style = 'style' in req.form and req.form['style'][0] or self.style
- mapfile = templater.stylemap(style)
- tmpl = templater.templater(mapfile,
- defaults={"header": header,
- "footer": footer,
- "motd": motd,
- "url": url,
- "staticurl": staticurl,
- "sessionvars": sessionvars})
- return tmpl
--- a/sys/src/cmd/hg/mercurial/hgweb/protocol.py
+++ /dev/null
@@ -1,206 +1,0 @@
-#
-# Copyright 21 May 2005 - (c) 2005 Jake Edge <jake@edge2.net>
-# Copyright 2005-2007 Matt Mackall <mpm@selenic.com>
-#
-# This software may be used and distributed according to the terms of the
-# GNU General Public License version 2, incorporated herein by reference.
-
-import cStringIO, zlib, tempfile, errno, os, sys, urllib
-from mercurial import util, streamclone
-from mercurial.node import bin, hex
-from mercurial import changegroup as changegroupmod
-from common import ErrorResponse, HTTP_OK, HTTP_NOT_FOUND, HTTP_SERVER_ERROR
-
-# __all__ is populated with the allowed commands. Be sure to add to it if
-# you're adding a new command, or the new command won't work.
-
-__all__ = [
- 'lookup', 'heads', 'branches', 'between', 'changegroup',
- 'changegroupsubset', 'capabilities', 'unbundle', 'stream_out',
- 'branchmap',
-]
-
-HGTYPE = 'application/mercurial-0.1'
-
-def lookup(repo, req):
- try:
- r = hex(repo.lookup(req.form['key'][0]))
- success = 1
- except Exception, inst:
- r = str(inst)
- success = 0
- resp = "%s %s\n" % (success, r)
- req.respond(HTTP_OK, HGTYPE, length=len(resp))
- yield resp
-
-def heads(repo, req):
- resp = " ".join(map(hex, repo.heads())) + "\n"
- req.respond(HTTP_OK, HGTYPE, length=len(resp))
- yield resp
-
-def branchmap(repo, req):
- branches = repo.branchmap()
- heads = []
- for branch, nodes in branches.iteritems():
- branchname = urllib.quote(branch)
- branchnodes = [hex(node) for node in nodes]
- heads.append('%s %s' % (branchname, ' '.join(branchnodes)))
- resp = '\n'.join(heads)
- req.respond(HTTP_OK, HGTYPE, length=len(resp))
- yield resp
-
-def branches(repo, req):
- nodes = []
- if 'nodes' in req.form:
- nodes = map(bin, req.form['nodes'][0].split(" "))
- resp = cStringIO.StringIO()
- for b in repo.branches(nodes):
- resp.write(" ".join(map(hex, b)) + "\n")
- resp = resp.getvalue()
- req.respond(HTTP_OK, HGTYPE, length=len(resp))
- yield resp
-
-def between(repo, req):
- if 'pairs' in req.form:
- pairs = [map(bin, p.split("-"))
- for p in req.form['pairs'][0].split(" ")]
- resp = cStringIO.StringIO()
- for b in repo.between(pairs):
- resp.write(" ".join(map(hex, b)) + "\n")
- resp = resp.getvalue()
- req.respond(HTTP_OK, HGTYPE, length=len(resp))
- yield resp
-
-def changegroup(repo, req):
- req.respond(HTTP_OK, HGTYPE)
- nodes = []
-
- if 'roots' in req.form:
- nodes = map(bin, req.form['roots'][0].split(" "))
-
- z = zlib.compressobj()
- f = repo.changegroup(nodes, 'serve')
- while 1:
- chunk = f.read(4096)
- if not chunk:
- break
- yield z.compress(chunk)
-
- yield z.flush()
-
-def changegroupsubset(repo, req):
- req.respond(HTTP_OK, HGTYPE)
- bases = []
- heads = []
-
- if 'bases' in req.form:
- bases = [bin(x) for x in req.form['bases'][0].split(' ')]
- if 'heads' in req.form:
- heads = [bin(x) for x in req.form['heads'][0].split(' ')]
-
- z = zlib.compressobj()
- f = repo.changegroupsubset(bases, heads, 'serve')
- while 1:
- chunk = f.read(4096)
- if not chunk:
- break
- yield z.compress(chunk)
-
- yield z.flush()
-
-def capabilities(repo, req):
- caps = ['lookup', 'changegroupsubset', 'branchmap']
- if repo.ui.configbool('server', 'uncompressed', untrusted=True):
- caps.append('stream=%d' % repo.changelog.version)
- if changegroupmod.bundlepriority:
- caps.append('unbundle=%s' % ','.join(changegroupmod.bundlepriority))
- rsp = ' '.join(caps)
- req.respond(HTTP_OK, HGTYPE, length=len(rsp))
- yield rsp
-
-def unbundle(repo, req):
-
- proto = req.env.get('wsgi.url_scheme') or 'http'
- their_heads = req.form['heads'][0].split(' ')
-
- def check_heads():
- heads = map(hex, repo.heads())
- return their_heads == [hex('force')] or their_heads == heads
-
- # fail early if possible
- if not check_heads():
- req.drain()
- raise ErrorResponse(HTTP_OK, 'unsynced changes')
-
- # do not lock repo until all changegroup data is
- # streamed. save to temporary file.
-
- fd, tempname = tempfile.mkstemp(prefix='hg-unbundle-')
- fp = os.fdopen(fd, 'wb+')
- try:
- length = int(req.env['CONTENT_LENGTH'])
- for s in util.filechunkiter(req, limit=length):
- fp.write(s)
-
- try:
- lock = repo.lock()
- try:
- if not check_heads():
- raise ErrorResponse(HTTP_OK, 'unsynced changes')
-
- fp.seek(0)
- header = fp.read(6)
- if header.startswith('HG') and not header.startswith('HG10'):
- raise ValueError('unknown bundle version')
- elif header not in changegroupmod.bundletypes:
- raise ValueError('unknown bundle compression type')
- gen = changegroupmod.unbundle(header, fp)
-
- # send addchangegroup output to client
-
- oldio = sys.stdout, sys.stderr
- sys.stderr = sys.stdout = cStringIO.StringIO()
-
- try:
- url = 'remote:%s:%s:%s' % (
- proto,
- urllib.quote(req.env.get('REMOTE_HOST', '')),
- urllib.quote(req.env.get('REMOTE_USER', '')))
- try:
- ret = repo.addchangegroup(gen, 'serve', url)
- except util.Abort, inst:
- sys.stdout.write("abort: %s\n" % inst)
- ret = 0
- finally:
- val = sys.stdout.getvalue()
- sys.stdout, sys.stderr = oldio
- req.respond(HTTP_OK, HGTYPE)
- return '%d\n%s' % (ret, val),
- finally:
- lock.release()
- except ValueError, inst:
- raise ErrorResponse(HTTP_OK, inst)
- except (OSError, IOError), inst:
- filename = getattr(inst, 'filename', '')
- # Don't send our filesystem layout to the client
- if filename.startswith(repo.root):
- filename = filename[len(repo.root)+1:]
- else:
- filename = ''
- error = getattr(inst, 'strerror', 'Unknown error')
- if inst.errno == errno.ENOENT:
- code = HTTP_NOT_FOUND
- else:
- code = HTTP_SERVER_ERROR
- raise ErrorResponse(code, '%s: %s' % (error, filename))
- finally:
- fp.close()
- os.unlink(tempname)
-
-def stream_out(repo, req):
- req.respond(HTTP_OK, HGTYPE)
- try:
- for chunk in streamclone.stream_out(repo, untrusted=True):
- yield chunk
- except streamclone.StreamException, inst:
- yield str(inst)
--- a/sys/src/cmd/hg/mercurial/hgweb/request.py
+++ /dev/null
@@ -1,134 +1,0 @@
-# hgweb/request.py - An http request from either CGI or the standalone server.
-#
-# Copyright 21 May 2005 - (c) 2005 Jake Edge <jake@edge2.net>
-# Copyright 2005, 2006 Matt Mackall <mpm@selenic.com>
-#
-# This software may be used and distributed according to the terms of the
-# GNU General Public License version 2, incorporated herein by reference.
-
-import socket, cgi, errno
-from mercurial import util
-from common import ErrorResponse, statusmessage
-
-shortcuts = {
- 'cl': [('cmd', ['changelog']), ('rev', None)],
- 'sl': [('cmd', ['shortlog']), ('rev', None)],
- 'cs': [('cmd', ['changeset']), ('node', None)],
- 'f': [('cmd', ['file']), ('filenode', None)],
- 'fl': [('cmd', ['filelog']), ('filenode', None)],
- 'fd': [('cmd', ['filediff']), ('node', None)],
- 'fa': [('cmd', ['annotate']), ('filenode', None)],
- 'mf': [('cmd', ['manifest']), ('manifest', None)],
- 'ca': [('cmd', ['archive']), ('node', None)],
- 'tags': [('cmd', ['tags'])],
- 'tip': [('cmd', ['changeset']), ('node', ['tip'])],
- 'static': [('cmd', ['static']), ('file', None)]
-}
-
-def expand(form):
- for k in shortcuts.iterkeys():
- if k in form:
- for name, value in shortcuts[k]:
- if value is None:
- value = form[k]
- form[name] = value
- del form[k]
- return form
-
-class wsgirequest(object):
- def __init__(self, wsgienv, start_response):
- version = wsgienv['wsgi.version']
- if (version < (1, 0)) or (version >= (2, 0)):
- raise RuntimeError("Unknown and unsupported WSGI version %d.%d"
- % version)
- self.inp = wsgienv['wsgi.input']
- self.err = wsgienv['wsgi.errors']
- self.threaded = wsgienv['wsgi.multithread']
- self.multiprocess = wsgienv['wsgi.multiprocess']
- self.run_once = wsgienv['wsgi.run_once']
- self.env = wsgienv
- self.form = expand(cgi.parse(self.inp, self.env, keep_blank_values=1))
- self._start_response = start_response
- self.server_write = None
- self.headers = []
-
- def __iter__(self):
- return iter([])
-
- def read(self, count=-1):
- return self.inp.read(count)
-
- def drain(self):
- '''need to read all data from request, httplib is half-duplex'''
- length = int(self.env.get('CONTENT_LENGTH', 0))
- for s in util.filechunkiter(self.inp, limit=length):
- pass
-
- def respond(self, status, type=None, filename=None, length=0):
- if self._start_response is not None:
-
- self.httphdr(type, filename, length)
- if not self.headers:
- raise RuntimeError("request.write called before headers sent")
-
- for k, v in self.headers:
- if not isinstance(v, str):
- raise TypeError('header value must be string: %r' % v)
-
- if isinstance(status, ErrorResponse):
- self.header(status.headers)
- status = statusmessage(status.code)
- elif status == 200:
- status = '200 Script output follows'
- elif isinstance(status, int):
- status = statusmessage(status)
-
- self.server_write = self._start_response(status, self.headers)
- self._start_response = None
- self.headers = []
-
- def write(self, thing):
- if hasattr(thing, "__iter__"):
- for part in thing:
- self.write(part)
- else:
- thing = str(thing)
- try:
- self.server_write(thing)
- except socket.error, inst:
- if inst[0] != errno.ECONNRESET:
- raise
-
- def writelines(self, lines):
- for line in lines:
- self.write(line)
-
- def flush(self):
- return None
-
- def close(self):
- return None
-
- def header(self, headers=[('Content-Type','text/html')]):
- self.headers.extend(headers)
-
- def httphdr(self, type=None, filename=None, length=0, headers={}):
- headers = headers.items()
- if type is not None:
- headers.append(('Content-Type', type))
- if filename:
- filename = (filename.split('/')[-1]
- .replace('\\', '\\\\').replace('"', '\\"'))
- headers.append(('Content-Disposition',
- 'inline; filename="%s"' % filename))
- if length:
- headers.append(('Content-Length', str(length)))
- self.header(headers)
-
-def wsgiapplication(app_maker):
- '''For compatibility with old CGI scripts. A plain hgweb() or hgwebdir()
- can and should now be used as a WSGI application.'''
- application = app_maker()
- def run_wsgi(env, respond):
- return application(env, respond)
- return run_wsgi
--- a/sys/src/cmd/hg/mercurial/hgweb/server.py
+++ /dev/null
@@ -1,298 +1,0 @@
-# hgweb/server.py - The standalone hg web server.
-#
-# Copyright 21 May 2005 - (c) 2005 Jake Edge <jake@edge2.net>
-# Copyright 2005-2007 Matt Mackall <mpm@selenic.com>
-#
-# This software may be used and distributed according to the terms of the
-# GNU General Public License version 2, incorporated herein by reference.
-
-import os, sys, errno, urllib, BaseHTTPServer, socket, SocketServer, traceback
-from mercurial import hg, util, error
-from hgweb_mod import hgweb
-from hgwebdir_mod import hgwebdir
-from mercurial.i18n import _
-
-def _splitURI(uri):
- """ Return path and query splited from uri
-
- Just like CGI environment, the path is unquoted, the query is
- not.
- """
- if '?' in uri:
- path, query = uri.split('?', 1)
- else:
- path, query = uri, ''
- return urllib.unquote(path), query
-
-class _error_logger(object):
- def __init__(self, handler):
- self.handler = handler
- def flush(self):
- pass
- def write(self, str):
- self.writelines(str.split('\n'))
- def writelines(self, seq):
- for msg in seq:
- self.handler.log_error("HG error: %s", msg)
-
-class _hgwebhandler(BaseHTTPServer.BaseHTTPRequestHandler):
-
- url_scheme = 'http'
-
- def __init__(self, *args, **kargs):
- self.protocol_version = 'HTTP/1.1'
- BaseHTTPServer.BaseHTTPRequestHandler.__init__(self, *args, **kargs)
-
- def _log_any(self, fp, format, *args):
- fp.write("%s - - [%s] %s\n" % (self.client_address[0],
- self.log_date_time_string(),
- format % args))
- fp.flush()
-
- def log_error(self, format, *args):
- self._log_any(self.server.errorlog, format, *args)
-
- def log_message(self, format, *args):
- self._log_any(self.server.accesslog, format, *args)
-
- def do_write(self):
- try:
- self.do_hgweb()
- except socket.error, inst:
- if inst[0] != errno.EPIPE:
- raise
-
- def do_POST(self):
- try:
- self.do_write()
- except StandardError:
- self._start_response("500 Internal Server Error", [])
- self._write("Internal Server Error")
- tb = "".join(traceback.format_exception(*sys.exc_info()))
- self.log_error("Exception happened during processing "
- "request '%s':\n%s", self.path, tb)
-
- def do_GET(self):
- self.do_POST()
-
- def do_hgweb(self):
- path, query = _splitURI(self.path)
-
- env = {}
- env['GATEWAY_INTERFACE'] = 'CGI/1.1'
- env['REQUEST_METHOD'] = self.command
- env['SERVER_NAME'] = self.server.server_name
- env['SERVER_PORT'] = str(self.server.server_port)
- env['REQUEST_URI'] = self.path
- env['SCRIPT_NAME'] = self.server.prefix
- env['PATH_INFO'] = path[len(self.server.prefix):]
- env['REMOTE_HOST'] = self.client_address[0]
- env['REMOTE_ADDR'] = self.client_address[0]
- if query:
- env['QUERY_STRING'] = query
-
- if self.headers.typeheader is None:
- env['CONTENT_TYPE'] = self.headers.type
- else:
- env['CONTENT_TYPE'] = self.headers.typeheader
- length = self.headers.getheader('content-length')
- if length:
- env['CONTENT_LENGTH'] = length
- for header in [h for h in self.headers.keys()
- if h not in ('content-type', 'content-length')]:
- hkey = 'HTTP_' + header.replace('-', '_').upper()
- hval = self.headers.getheader(header)
- hval = hval.replace('\n', '').strip()
- if hval:
- env[hkey] = hval
- env['SERVER_PROTOCOL'] = self.request_version
- env['wsgi.version'] = (1, 0)
- env['wsgi.url_scheme'] = self.url_scheme
- env['wsgi.input'] = self.rfile
- env['wsgi.errors'] = _error_logger(self)
- env['wsgi.multithread'] = isinstance(self.server,
- SocketServer.ThreadingMixIn)
- env['wsgi.multiprocess'] = isinstance(self.server,
- SocketServer.ForkingMixIn)
- env['wsgi.run_once'] = 0
-
- self.close_connection = True
- self.saved_status = None
- self.saved_headers = []
- self.sent_headers = False
- self.length = None
- for chunk in self.server.application(env, self._start_response):
- self._write(chunk)
-
- def send_headers(self):
- if not self.saved_status:
- raise AssertionError("Sending headers before "
- "start_response() called")
- saved_status = self.saved_status.split(None, 1)
- saved_status[0] = int(saved_status[0])
- self.send_response(*saved_status)
- should_close = True
- for h in self.saved_headers:
- self.send_header(*h)
- if h[0].lower() == 'content-length':
- should_close = False
- self.length = int(h[1])
- # The value of the Connection header is a list of case-insensitive
- # tokens separated by commas and optional whitespace.
- if 'close' in [token.strip().lower() for token in
- self.headers.get('connection', '').split(',')]:
- should_close = True
- if should_close:
- self.send_header('Connection', 'close')
- self.close_connection = should_close
- self.end_headers()
- self.sent_headers = True
-
- def _start_response(self, http_status, headers, exc_info=None):
- code, msg = http_status.split(None, 1)
- code = int(code)
- self.saved_status = http_status
- bad_headers = ('connection', 'transfer-encoding')
- self.saved_headers = [h for h in headers
- if h[0].lower() not in bad_headers]
- return self._write
-
- def _write(self, data):
- if not self.saved_status:
- raise AssertionError("data written before start_response() called")
- elif not self.sent_headers:
- self.send_headers()
- if self.length is not None:
- if len(data) > self.length:
- raise AssertionError("Content-length header sent, but more "
- "bytes than specified are being written.")
- self.length = self.length - len(data)
- self.wfile.write(data)
- self.wfile.flush()
-
-class _shgwebhandler(_hgwebhandler):
-
- url_scheme = 'https'
-
- def setup(self):
- self.connection = self.request
- self.rfile = socket._fileobject(self.request, "rb", self.rbufsize)
- self.wfile = socket._fileobject(self.request, "wb", self.wbufsize)
-
- def do_write(self):
- from OpenSSL.SSL import SysCallError
- try:
- super(_shgwebhandler, self).do_write()
- except SysCallError, inst:
- if inst.args[0] != errno.EPIPE:
- raise
-
- def handle_one_request(self):
- from OpenSSL.SSL import SysCallError, ZeroReturnError
- try:
- super(_shgwebhandler, self).handle_one_request()
- except (SysCallError, ZeroReturnError):
- self.close_connection = True
- pass
-
-def create_server(ui, repo):
- use_threads = True
-
- def openlog(opt, default):
- if opt and opt != '-':
- return open(opt, 'a')
- return default
-
- if repo is None:
- myui = ui
- else:
- myui = repo.ui
- address = myui.config("web", "address", "")
- port = int(myui.config("web", "port", 8000))
- prefix = myui.config("web", "prefix", "")
- if prefix:
- prefix = "/" + prefix.strip("/")
- use_ipv6 = myui.configbool("web", "ipv6")
- webdir_conf = myui.config("web", "webdir_conf")
- ssl_cert = myui.config("web", "certificate")
- accesslog = openlog(myui.config("web", "accesslog", "-"), sys.stdout)
- errorlog = openlog(myui.config("web", "errorlog", "-"), sys.stderr)
-
- if use_threads:
- try:
- from threading import activeCount
- except ImportError:
- use_threads = False
-
- if use_threads:
- _mixin = SocketServer.ThreadingMixIn
- else:
- if hasattr(os, "fork"):
- _mixin = SocketServer.ForkingMixIn
- else:
- class _mixin:
- pass
-
- class MercurialHTTPServer(object, _mixin, BaseHTTPServer.HTTPServer):
-
- # SO_REUSEADDR has broken semantics on windows
- if os.name == 'nt':
- allow_reuse_address = 0
-
- def __init__(self, *args, **kargs):
- BaseHTTPServer.HTTPServer.__init__(self, *args, **kargs)
- self.accesslog = accesslog
- self.errorlog = errorlog
- self.daemon_threads = True
- def make_handler():
- if webdir_conf:
- hgwebobj = hgwebdir(webdir_conf, ui)
- elif repo is not None:
- hgwebobj = hgweb(hg.repository(repo.ui, repo.root))
- else:
- raise error.RepoError(_("There is no Mercurial repository"
- " here (.hg not found)"))
- return hgwebobj
- self.application = make_handler()
-
- if ssl_cert:
- try:
- from OpenSSL import SSL
- ctx = SSL.Context(SSL.SSLv23_METHOD)
- except ImportError:
- raise util.Abort(_("SSL support is unavailable"))
- ctx.use_privatekey_file(ssl_cert)
- ctx.use_certificate_file(ssl_cert)
- sock = socket.socket(self.address_family, self.socket_type)
- self.socket = SSL.Connection(ctx, sock)
- self.server_bind()
- self.server_activate()
-
- self.addr, self.port = self.socket.getsockname()[0:2]
- self.prefix = prefix
- self.fqaddr = socket.getfqdn(address)
-
- class IPv6HTTPServer(MercurialHTTPServer):
- address_family = getattr(socket, 'AF_INET6', None)
-
- def __init__(self, *args, **kwargs):
- if self.address_family is None:
- raise error.RepoError(_('IPv6 is not available on this system'))
- super(IPv6HTTPServer, self).__init__(*args, **kwargs)
-
- if ssl_cert:
- handler = _shgwebhandler
- else:
- handler = _hgwebhandler
-
- # ugly hack due to python issue5853 (for threaded use)
- import mimetypes; mimetypes.init()
-
- try:
- if use_ipv6:
- return IPv6HTTPServer((address, port), handler)
- else:
- return MercurialHTTPServer((address, port), handler)
- except socket.error, inst:
- raise util.Abort(_("cannot start server at '%s:%d': %s")
- % (address, port, inst.args[1]))
--- a/sys/src/cmd/hg/mercurial/hgweb/webcommands.py
+++ /dev/null
@@ -1,690 +1,0 @@
-#
-# Copyright 21 May 2005 - (c) 2005 Jake Edge <jake@edge2.net>
-# Copyright 2005-2007 Matt Mackall <mpm@selenic.com>
-#
-# This software may be used and distributed according to the terms of the
-# GNU General Public License version 2, incorporated herein by reference.
-
-import os, mimetypes, re, cgi, copy
-import webutil
-from mercurial import error, archival, templater, templatefilters
-from mercurial.node import short, hex
-from mercurial.util import binary
-from common import paritygen, staticfile, get_contact, ErrorResponse
-from common import HTTP_OK, HTTP_FORBIDDEN, HTTP_NOT_FOUND
-from mercurial import graphmod
-
-# __all__ is populated with the allowed commands. Be sure to add to it if
-# you're adding a new command, or the new command won't work.
-
-__all__ = [
- 'log', 'rawfile', 'file', 'changelog', 'shortlog', 'changeset', 'rev',
- 'manifest', 'tags', 'branches', 'summary', 'filediff', 'diff', 'annotate',
- 'filelog', 'archive', 'static', 'graph',
-]
-
-def log(web, req, tmpl):
- if 'file' in req.form and req.form['file'][0]:
- return filelog(web, req, tmpl)
- else:
- return changelog(web, req, tmpl)
-
-def rawfile(web, req, tmpl):
- path = webutil.cleanpath(web.repo, req.form.get('file', [''])[0])
- if not path:
- content = manifest(web, req, tmpl)
- req.respond(HTTP_OK, web.ctype)
- return content
-
- try:
- fctx = webutil.filectx(web.repo, req)
- except error.LookupError, inst:
- try:
- content = manifest(web, req, tmpl)
- req.respond(HTTP_OK, web.ctype)
- return content
- except ErrorResponse:
- raise inst
-
- path = fctx.path()
- text = fctx.data()
- mt = mimetypes.guess_type(path)[0]
- if mt is None:
- mt = binary(text) and 'application/octet-stream' or 'text/plain'
-
- req.respond(HTTP_OK, mt, path, len(text))
- return [text]
-
-def _filerevision(web, tmpl, fctx):
- f = fctx.path()
- text = fctx.data()
- parity = paritygen(web.stripecount)
-
- if binary(text):
- mt = mimetypes.guess_type(f)[0] or 'application/octet-stream'
- text = '(binary:%s)' % mt
-
- def lines():
- for lineno, t in enumerate(text.splitlines(True)):
- yield {"line": t,
- "lineid": "l%d" % (lineno + 1),
- "linenumber": "% 6d" % (lineno + 1),
- "parity": parity.next()}
-
- return tmpl("filerevision",
- file=f,
- path=webutil.up(f),
- text=lines(),
- rev=fctx.rev(),
- node=hex(fctx.node()),
- author=fctx.user(),
- date=fctx.date(),
- desc=fctx.description(),
- branch=webutil.nodebranchnodefault(fctx),
- parent=webutil.parents(fctx),
- child=webutil.children(fctx),
- rename=webutil.renamelink(fctx),
- permissions=fctx.manifest().flags(f))
-
-def file(web, req, tmpl):
- path = webutil.cleanpath(web.repo, req.form.get('file', [''])[0])
- if not path:
- return manifest(web, req, tmpl)
- try:
- return _filerevision(web, tmpl, webutil.filectx(web.repo, req))
- except error.LookupError, inst:
- try:
- return manifest(web, req, tmpl)
- except ErrorResponse:
- raise inst
-
-def _search(web, tmpl, query):
-
- def changelist(**map):
- cl = web.repo.changelog
- count = 0
- qw = query.lower().split()
-
- def revgen():
- for i in xrange(len(cl) - 1, 0, -100):
- l = []
- for j in xrange(max(0, i - 100), i + 1):
- ctx = web.repo[j]
- l.append(ctx)
- l.reverse()
- for e in l:
- yield e
-
- for ctx in revgen():
- miss = 0
- for q in qw:
- if not (q in ctx.user().lower() or
- q in ctx.description().lower() or
- q in " ".join(ctx.files()).lower()):
- miss = 1
- break
- if miss:
- continue
-
- count += 1
- n = ctx.node()
- showtags = webutil.showtag(web.repo, tmpl, 'changelogtag', n)
- files = webutil.listfilediffs(tmpl, ctx.files(), n, web.maxfiles)
-
- yield tmpl('searchentry',
- parity=parity.next(),
- author=ctx.user(),
- parent=webutil.parents(ctx),
- child=webutil.children(ctx),
- changelogtag=showtags,
- desc=ctx.description(),
- date=ctx.date(),
- files=files,
- rev=ctx.rev(),
- node=hex(n),
- tags=webutil.nodetagsdict(web.repo, n),
- inbranch=webutil.nodeinbranch(web.repo, ctx),
- branches=webutil.nodebranchdict(web.repo, ctx))
-
- if count >= web.maxchanges:
- break
-
- cl = web.repo.changelog
- parity = paritygen(web.stripecount)
-
- return tmpl('search',
- query=query,
- node=hex(cl.tip()),
- entries=changelist,
- archives=web.archivelist("tip"))
-
-def changelog(web, req, tmpl, shortlog = False):
- if 'node' in req.form:
- ctx = webutil.changectx(web.repo, req)
- else:
- if 'rev' in req.form:
- hi = req.form['rev'][0]
- else:
- hi = len(web.repo) - 1
- try:
- ctx = web.repo[hi]
- except error.RepoError:
- return _search(web, tmpl, hi) # XXX redirect to 404 page?
-
- def changelist(limit=0, **map):
- l = [] # build a list in forward order for efficiency
- for i in xrange(start, end):
- ctx = web.repo[i]
- n = ctx.node()
- showtags = webutil.showtag(web.repo, tmpl, 'changelogtag', n)
- files = webutil.listfilediffs(tmpl, ctx.files(), n, web.maxfiles)
-
- l.insert(0, {"parity": parity.next(),
- "author": ctx.user(),
- "parent": webutil.parents(ctx, i - 1),
- "child": webutil.children(ctx, i + 1),
- "changelogtag": showtags,
- "desc": ctx.description(),
- "date": ctx.date(),
- "files": files,
- "rev": i,
- "node": hex(n),
- "tags": webutil.nodetagsdict(web.repo, n),
- "inbranch": webutil.nodeinbranch(web.repo, ctx),
- "branches": webutil.nodebranchdict(web.repo, ctx)
- })
-
- if limit > 0:
- l = l[:limit]
-
- for e in l:
- yield e
-
- maxchanges = shortlog and web.maxshortchanges or web.maxchanges
- cl = web.repo.changelog
- count = len(cl)
- pos = ctx.rev()
- start = max(0, pos - maxchanges + 1)
- end = min(count, start + maxchanges)
- pos = end - 1
- parity = paritygen(web.stripecount, offset=start-end)
-
- changenav = webutil.revnavgen(pos, maxchanges, count, web.repo.changectx)
-
- return tmpl(shortlog and 'shortlog' or 'changelog',
- changenav=changenav,
- node=hex(ctx.node()),
- rev=pos, changesets=count,
- entries=lambda **x: changelist(limit=0,**x),
- latestentry=lambda **x: changelist(limit=1,**x),
- archives=web.archivelist("tip"))
-
-def shortlog(web, req, tmpl):
- return changelog(web, req, tmpl, shortlog = True)
-
-def changeset(web, req, tmpl):
- ctx = webutil.changectx(web.repo, req)
- showtags = webutil.showtag(web.repo, tmpl, 'changesettag', ctx.node())
- showbranch = webutil.nodebranchnodefault(ctx)
-
- files = []
- parity = paritygen(web.stripecount)
- for f in ctx.files():
- template = f in ctx and 'filenodelink' or 'filenolink'
- files.append(tmpl(template,
- node=ctx.hex(), file=f,
- parity=parity.next()))
-
- parity = paritygen(web.stripecount)
- diffs = webutil.diffs(web.repo, tmpl, ctx, None, parity)
- return tmpl('changeset',
- diff=diffs,
- rev=ctx.rev(),
- node=ctx.hex(),
- parent=webutil.parents(ctx),
- child=webutil.children(ctx),
- changesettag=showtags,
- changesetbranch=showbranch,
- author=ctx.user(),
- desc=ctx.description(),
- date=ctx.date(),
- files=files,
- archives=web.archivelist(ctx.hex()),
- tags=webutil.nodetagsdict(web.repo, ctx.node()),
- branch=webutil.nodebranchnodefault(ctx),
- inbranch=webutil.nodeinbranch(web.repo, ctx),
- branches=webutil.nodebranchdict(web.repo, ctx))
-
-rev = changeset
-
-def manifest(web, req, tmpl):
- ctx = webutil.changectx(web.repo, req)
- path = webutil.cleanpath(web.repo, req.form.get('file', [''])[0])
- mf = ctx.manifest()
- node = ctx.node()
-
- files = {}
- dirs = {}
- parity = paritygen(web.stripecount)
-
- if path and path[-1] != "/":
- path += "/"
- l = len(path)
- abspath = "/" + path
-
- for f, n in mf.iteritems():
- if f[:l] != path:
- continue
- remain = f[l:]
- elements = remain.split('/')
- if len(elements) == 1:
- files[remain] = f
- else:
- h = dirs # need to retain ref to dirs (root)
- for elem in elements[0:-1]:
- if elem not in h:
- h[elem] = {}
- h = h[elem]
- if len(h) > 1:
- break
- h[None] = None # denotes files present
-
- if mf and not files and not dirs:
- raise ErrorResponse(HTTP_NOT_FOUND, 'path not found: ' + path)
-
- def filelist(**map):
- for f in sorted(files):
- full = files[f]
-
- fctx = ctx.filectx(full)
- yield {"file": full,
- "parity": parity.next(),
- "basename": f,
- "date": fctx.date(),
- "size": fctx.size(),
- "permissions": mf.flags(full)}
-
- def dirlist(**map):
- for d in sorted(dirs):
-
- emptydirs = []
- h = dirs[d]
- while isinstance(h, dict) and len(h) == 1:
- k,v = h.items()[0]
- if v:
- emptydirs.append(k)
- h = v
-
- path = "%s%s" % (abspath, d)
- yield {"parity": parity.next(),
- "path": path,
- "emptydirs": "/".join(emptydirs),
- "basename": d}
-
- return tmpl("manifest",
- rev=ctx.rev(),
- node=hex(node),
- path=abspath,
- up=webutil.up(abspath),
- upparity=parity.next(),
- fentries=filelist,
- dentries=dirlist,
- archives=web.archivelist(hex(node)),
- tags=webutil.nodetagsdict(web.repo, node),
- inbranch=webutil.nodeinbranch(web.repo, ctx),
- branches=webutil.nodebranchdict(web.repo, ctx))
-
-def tags(web, req, tmpl):
- i = web.repo.tagslist()
- i.reverse()
- parity = paritygen(web.stripecount)
-
- def entries(notip=False, limit=0, **map):
- count = 0
- for k, n in i:
- if notip and k == "tip":
- continue
- if limit > 0 and count >= limit:
- continue
- count = count + 1
- yield {"parity": parity.next(),
- "tag": k,
- "date": web.repo[n].date(),
- "node": hex(n)}
-
- return tmpl("tags",
- node=hex(web.repo.changelog.tip()),
- entries=lambda **x: entries(False,0, **x),
- entriesnotip=lambda **x: entries(True,0, **x),
- latestentry=lambda **x: entries(True,1, **x))
-
-def branches(web, req, tmpl):
- b = web.repo.branchtags()
- tips = (web.repo[n] for t, n in web.repo.branchtags().iteritems())
- heads = web.repo.heads()
- parity = paritygen(web.stripecount)
- sortkey = lambda ctx: ('close' not in ctx.extra(), ctx.rev())
-
- def entries(limit, **map):
- count = 0
- for ctx in sorted(tips, key=sortkey, reverse=True):
- if limit > 0 and count >= limit:
- return
- count += 1
- if ctx.node() not in heads:
- status = 'inactive'
- elif not web.repo.branchheads(ctx.branch()):
- status = 'closed'
- else:
- status = 'open'
- yield {'parity': parity.next(),
- 'branch': ctx.branch(),
- 'status': status,
- 'node': ctx.hex(),
- 'date': ctx.date()}
-
- return tmpl('branches', node=hex(web.repo.changelog.tip()),
- entries=lambda **x: entries(0, **x),
- latestentry=lambda **x: entries(1, **x))
-
-def summary(web, req, tmpl):
- i = web.repo.tagslist()
- i.reverse()
-
- def tagentries(**map):
- parity = paritygen(web.stripecount)
- count = 0
- for k, n in i:
- if k == "tip": # skip tip
- continue
-
- count += 1
- if count > 10: # limit to 10 tags
- break
-
- yield tmpl("tagentry",
- parity=parity.next(),
- tag=k,
- node=hex(n),
- date=web.repo[n].date())
-
- def branches(**map):
- parity = paritygen(web.stripecount)
-
- b = web.repo.branchtags()
- l = [(-web.repo.changelog.rev(n), n, t) for t, n in b.iteritems()]
- for r,n,t in sorted(l):
- yield {'parity': parity.next(),
- 'branch': t,
- 'node': hex(n),
- 'date': web.repo[n].date()}
-
- def changelist(**map):
- parity = paritygen(web.stripecount, offset=start-end)
- l = [] # build a list in forward order for efficiency
- for i in xrange(start, end):
- ctx = web.repo[i]
- n = ctx.node()
- hn = hex(n)
-
- l.insert(0, tmpl(
- 'shortlogentry',
- parity=parity.next(),
- author=ctx.user(),
- desc=ctx.description(),
- date=ctx.date(),
- rev=i,
- node=hn,
- tags=webutil.nodetagsdict(web.repo, n),
- inbranch=webutil.nodeinbranch(web.repo, ctx),
- branches=webutil.nodebranchdict(web.repo, ctx)))
-
- yield l
-
- cl = web.repo.changelog
- count = len(cl)
- start = max(0, count - web.maxchanges)
- end = min(count, start + web.maxchanges)
-
- return tmpl("summary",
- desc=web.config("web", "description", "unknown"),
- owner=get_contact(web.config) or "unknown",
- lastchange=cl.read(cl.tip())[2],
- tags=tagentries,
- branches=branches,
- shortlog=changelist,
- node=hex(cl.tip()),
- archives=web.archivelist("tip"))
-
-def filediff(web, req, tmpl):
- fctx, ctx = None, None
- try:
- fctx = webutil.filectx(web.repo, req)
- except LookupError:
- ctx = webutil.changectx(web.repo, req)
- path = webutil.cleanpath(web.repo, req.form['file'][0])
- if path not in ctx.files():
- raise
-
- if fctx is not None:
- n = fctx.node()
- path = fctx.path()
- else:
- n = ctx.node()
- # path already defined in except clause
-
- parity = paritygen(web.stripecount)
- diffs = webutil.diffs(web.repo, tmpl, fctx or ctx, [path], parity)
- rename = fctx and webutil.renamelink(fctx) or []
- ctx = fctx and fctx or ctx
- return tmpl("filediff",
- file=path,
- node=hex(n),
- rev=ctx.rev(),
- date=ctx.date(),
- desc=ctx.description(),
- author=ctx.user(),
- rename=rename,
- branch=webutil.nodebranchnodefault(ctx),
- parent=webutil.parents(ctx),
- child=webutil.children(ctx),
- diff=diffs)
-
-diff = filediff
-
-def annotate(web, req, tmpl):
- fctx = webutil.filectx(web.repo, req)
- f = fctx.path()
- parity = paritygen(web.stripecount)
-
- def annotate(**map):
- last = None
- if binary(fctx.data()):
- mt = (mimetypes.guess_type(fctx.path())[0]
- or 'application/octet-stream')
- lines = enumerate([((fctx.filectx(fctx.filerev()), 1),
- '(binary:%s)' % mt)])
- else:
- lines = enumerate(fctx.annotate(follow=True, linenumber=True))
- for lineno, ((f, targetline), l) in lines:
- fnode = f.filenode()
-
- if last != fnode:
- last = fnode
-
- yield {"parity": parity.next(),
- "node": hex(f.node()),
- "rev": f.rev(),
- "author": f.user(),
- "desc": f.description(),
- "file": f.path(),
- "targetline": targetline,
- "line": l,
- "lineid": "l%d" % (lineno + 1),
- "linenumber": "% 6d" % (lineno + 1)}
-
- return tmpl("fileannotate",
- file=f,
- annotate=annotate,
- path=webutil.up(f),
- rev=fctx.rev(),
- node=hex(fctx.node()),
- author=fctx.user(),
- date=fctx.date(),
- desc=fctx.description(),
- rename=webutil.renamelink(fctx),
- branch=webutil.nodebranchnodefault(fctx),
- parent=webutil.parents(fctx),
- child=webutil.children(fctx),
- permissions=fctx.manifest().flags(f))
-
-def filelog(web, req, tmpl):
-
- try:
- fctx = webutil.filectx(web.repo, req)
- f = fctx.path()
- fl = fctx.filelog()
- except error.LookupError:
- f = webutil.cleanpath(web.repo, req.form['file'][0])
- fl = web.repo.file(f)
- numrevs = len(fl)
- if not numrevs: # file doesn't exist at all
- raise
- rev = webutil.changectx(web.repo, req).rev()
- first = fl.linkrev(0)
- if rev < first: # current rev is from before file existed
- raise
- frev = numrevs - 1
- while fl.linkrev(frev) > rev:
- frev -= 1
- fctx = web.repo.filectx(f, fl.linkrev(frev))
-
- count = fctx.filerev() + 1
- pagelen = web.maxshortchanges
- start = max(0, fctx.filerev() - pagelen + 1) # first rev on this page
- end = min(count, start + pagelen) # last rev on this page
- parity = paritygen(web.stripecount, offset=start-end)
-
- def entries(limit=0, **map):
- l = []
-
- repo = web.repo
- for i in xrange(start, end):
- iterfctx = fctx.filectx(i)
-
- l.insert(0, {"parity": parity.next(),
- "filerev": i,
- "file": f,
- "node": hex(iterfctx.node()),
- "author": iterfctx.user(),
- "date": iterfctx.date(),
- "rename": webutil.renamelink(iterfctx),
- "parent": webutil.parents(iterfctx),
- "child": webutil.children(iterfctx),
- "desc": iterfctx.description(),
- "tags": webutil.nodetagsdict(repo, iterfctx.node()),
- "branch": webutil.nodebranchnodefault(iterfctx),
- "inbranch": webutil.nodeinbranch(repo, iterfctx),
- "branches": webutil.nodebranchdict(repo, iterfctx)})
-
- if limit > 0:
- l = l[:limit]
-
- for e in l:
- yield e
-
- nodefunc = lambda x: fctx.filectx(fileid=x)
- nav = webutil.revnavgen(end - 1, pagelen, count, nodefunc)
- return tmpl("filelog", file=f, node=hex(fctx.node()), nav=nav,
- entries=lambda **x: entries(limit=0, **x),
- latestentry=lambda **x: entries(limit=1, **x))
-
-
-def archive(web, req, tmpl):
- type_ = req.form.get('type', [None])[0]
- allowed = web.configlist("web", "allow_archive")
- key = req.form['node'][0]
-
- if type_ not in web.archives:
- msg = 'Unsupported archive type: %s' % type_
- raise ErrorResponse(HTTP_NOT_FOUND, msg)
-
- if not ((type_ in allowed or
- web.configbool("web", "allow" + type_, False))):
- msg = 'Archive type not allowed: %s' % type_
- raise ErrorResponse(HTTP_FORBIDDEN, msg)
-
- reponame = re.sub(r"\W+", "-", os.path.basename(web.reponame))
- cnode = web.repo.lookup(key)
- arch_version = key
- if cnode == key or key == 'tip':
- arch_version = short(cnode)
- name = "%s-%s" % (reponame, arch_version)
- mimetype, artype, extension, encoding = web.archive_specs[type_]
- headers = [
- ('Content-Type', mimetype),
- ('Content-Disposition', 'attachment; filename=%s%s' % (name, extension))
- ]
- if encoding:
- headers.append(('Content-Encoding', encoding))
- req.header(headers)
- req.respond(HTTP_OK)
- archival.archive(web.repo, req, cnode, artype, prefix=name)
- return []
-
-
-def static(web, req, tmpl):
- fname = req.form['file'][0]
- # a repo owner may set web.static in .hg/hgrc to get any file
- # readable by the user running the CGI script
- static = web.config("web", "static", None, untrusted=False)
- if not static:
- tp = web.templatepath or templater.templatepath()
- if isinstance(tp, str):
- tp = [tp]
- static = [os.path.join(p, 'static') for p in tp]
- return [staticfile(static, fname, req)]
-
-def graph(web, req, tmpl):
- rev = webutil.changectx(web.repo, req).rev()
- bg_height = 39
-
- revcount = 25
- if 'revcount' in req.form:
- revcount = int(req.form.get('revcount', [revcount])[0])
- tmpl.defaults['sessionvars']['revcount'] = revcount
-
- lessvars = copy.copy(tmpl.defaults['sessionvars'])
- lessvars['revcount'] = revcount / 2
- morevars = copy.copy(tmpl.defaults['sessionvars'])
- morevars['revcount'] = revcount * 2
-
- max_rev = len(web.repo) - 1
- revcount = min(max_rev, revcount)
- revnode = web.repo.changelog.node(rev)
- revnode_hex = hex(revnode)
- uprev = min(max_rev, rev + revcount)
- downrev = max(0, rev - revcount)
- count = len(web.repo)
- changenav = webutil.revnavgen(rev, revcount, count, web.repo.changectx)
-
- dag = graphmod.revisions(web.repo, rev, downrev)
- tree = list(graphmod.colored(dag))
- canvasheight = (len(tree) + 1) * bg_height - 27;
- data = []
- for (id, type, ctx, vtx, edges) in tree:
- if type != graphmod.CHANGESET:
- continue
- node = short(ctx.node())
- age = templatefilters.age(ctx.date())
- desc = templatefilters.firstline(ctx.description())
- desc = cgi.escape(templatefilters.nonempty(desc))
- user = cgi.escape(templatefilters.person(ctx.user()))
- branch = ctx.branch()
- branch = branch, web.repo.branchtags().get(branch) == ctx.node()
- data.append((node, vtx, edges, desc, user, age, branch, ctx.tags()))
-
- return tmpl('graph', rev=rev, revcount=revcount, uprev=uprev,
- lessvars=lessvars, morevars=morevars, downrev=downrev,
- canvasheight=canvasheight, jsdata=data, bg_height=bg_height,
- node=revnode_hex, changenav=changenav)
--- a/sys/src/cmd/hg/mercurial/hgweb/webutil.py
+++ /dev/null
@@ -1,218 +1,0 @@
-# hgweb/webutil.py - utility library for the web interface.
-#
-# Copyright 21 May 2005 - (c) 2005 Jake Edge <jake@edge2.net>
-# Copyright 2005-2007 Matt Mackall <mpm@selenic.com>
-#
-# This software may be used and distributed according to the terms of the
-# GNU General Public License version 2, incorporated herein by reference.
-
-import os, copy
-from mercurial import match, patch, util, error
-from mercurial.node import hex, nullid
-
-def up(p):
- if p[0] != "/":
- p = "/" + p
- if p[-1] == "/":
- p = p[:-1]
- up = os.path.dirname(p)
- if up == "/":
- return "/"
- return up + "/"
-
-def revnavgen(pos, pagelen, limit, nodefunc):
- def seq(factor, limit=None):
- if limit:
- yield limit
- if limit >= 20 and limit <= 40:
- yield 50
- else:
- yield 1 * factor
- yield 3 * factor
- for f in seq(factor * 10):
- yield f
-
- def nav(**map):
- l = []
- last = 0
- for f in seq(1, pagelen):
- if f < pagelen or f <= last:
- continue
- if f > limit:
- break
- last = f
- if pos + f < limit:
- l.append(("+%d" % f, hex(nodefunc(pos + f).node())))
- if pos - f >= 0:
- l.insert(0, ("-%d" % f, hex(nodefunc(pos - f).node())))
-
- try:
- yield {"label": "(0)", "node": hex(nodefunc('0').node())}
-
- for label, node in l:
- yield {"label": label, "node": node}
-
- yield {"label": "tip", "node": "tip"}
- except error.RepoError:
- pass
-
- return nav
-
-def _siblings(siblings=[], hiderev=None):
- siblings = [s for s in siblings if s.node() != nullid]
- if len(siblings) == 1 and siblings[0].rev() == hiderev:
- return
- for s in siblings:
- d = {'node': hex(s.node()), 'rev': s.rev()}
- d['user'] = s.user()
- d['date'] = s.date()
- d['description'] = s.description()
- d['branch'] = s.branch()
- if hasattr(s, 'path'):
- d['file'] = s.path()
- yield d
-
-def parents(ctx, hide=None):
- return _siblings(ctx.parents(), hide)
-
-def children(ctx, hide=None):
- return _siblings(ctx.children(), hide)
-
-def renamelink(fctx):
- r = fctx.renamed()
- if r:
- return [dict(file=r[0], node=hex(r[1]))]
- return []
-
-def nodetagsdict(repo, node):
- return [{"name": i} for i in repo.nodetags(node)]
-
-def nodebranchdict(repo, ctx):
- branches = []
- branch = ctx.branch()
- # If this is an empty repo, ctx.node() == nullid,
- # ctx.branch() == 'default', but branchtags() is
- # an empty dict. Using dict.get avoids a traceback.
- if repo.branchtags().get(branch) == ctx.node():
- branches.append({"name": branch})
- return branches
-
-def nodeinbranch(repo, ctx):
- branches = []
- branch = ctx.branch()
- if branch != 'default' and repo.branchtags().get(branch) != ctx.node():
- branches.append({"name": branch})
- return branches
-
-def nodebranchnodefault(ctx):
- branches = []
- branch = ctx.branch()
- if branch != 'default':
- branches.append({"name": branch})
- return branches
-
-def showtag(repo, tmpl, t1, node=nullid, **args):
- for t in repo.nodetags(node):
- yield tmpl(t1, tag=t, **args)
-
-def cleanpath(repo, path):
- path = path.lstrip('/')
- return util.canonpath(repo.root, '', path)
-
-def changectx(repo, req):
- changeid = "tip"
- if 'node' in req.form:
- changeid = req.form['node'][0]
- elif 'manifest' in req.form:
- changeid = req.form['manifest'][0]
-
- try:
- ctx = repo[changeid]
- except error.RepoError:
- man = repo.manifest
- ctx = repo[man.linkrev(man.rev(man.lookup(changeid)))]
-
- return ctx
-
-def filectx(repo, req):
- path = cleanpath(repo, req.form['file'][0])
- if 'node' in req.form:
- changeid = req.form['node'][0]
- else:
- changeid = req.form['filenode'][0]
- try:
- fctx = repo[changeid][path]
- except error.RepoError:
- fctx = repo.filectx(path, fileid=changeid)
-
- return fctx
-
-def listfilediffs(tmpl, files, node, max):
- for f in files[:max]:
- yield tmpl('filedifflink', node=hex(node), file=f)
- if len(files) > max:
- yield tmpl('fileellipses')
-
-def diffs(repo, tmpl, ctx, files, parity):
-
- def countgen():
- start = 1
- while True:
- yield start
- start += 1
-
- blockcount = countgen()
- def prettyprintlines(diff):
- blockno = blockcount.next()
- for lineno, l in enumerate(diff.splitlines(True)):
- lineno = "%d.%d" % (blockno, lineno + 1)
- if l.startswith('+'):
- ltype = "difflineplus"
- elif l.startswith('-'):
- ltype = "difflineminus"
- elif l.startswith('@'):
- ltype = "difflineat"
- else:
- ltype = "diffline"
- yield tmpl(ltype,
- line=l,
- lineid="l%s" % lineno,
- linenumber="% 8s" % lineno)
-
- if files:
- m = match.exact(repo.root, repo.getcwd(), files)
- else:
- m = match.always(repo.root, repo.getcwd())
-
- diffopts = patch.diffopts(repo.ui, untrusted=True)
- parents = ctx.parents()
- node1 = parents and parents[0].node() or nullid
- node2 = ctx.node()
-
- block = []
- for chunk in patch.diff(repo, node1, node2, m, opts=diffopts):
- if chunk.startswith('diff') and block:
- yield tmpl('diffblock', parity=parity.next(),
- lines=prettyprintlines(''.join(block)))
- block = []
- if chunk.startswith('diff'):
- chunk = ''.join(chunk.splitlines(True)[1:])
- block.append(chunk)
- yield tmpl('diffblock', parity=parity.next(),
- lines=prettyprintlines(''.join(block)))
-
-class sessionvars(object):
- def __init__(self, vars, start='?'):
- self.start = start
- self.vars = vars
- def __getitem__(self, key):
- return self.vars[key]
- def __setitem__(self, key, value):
- self.vars[key] = value
- def __copy__(self):
- return sessionvars(copy.copy(self.vars), self.start)
- def __iter__(self):
- separator = self.start
- for key, value in self.vars.iteritems():
- yield {'name': key, 'value': str(value), 'separator': separator}
- separator = '&'
--- a/sys/src/cmd/hg/mercurial/hgweb/wsgicgi.py
+++ /dev/null
@@ -1,70 +1,0 @@
-# hgweb/wsgicgi.py - CGI->WSGI translator
-#
-# Copyright 2006 Eric Hopper <hopper@omnifarious.org>
-#
-# This software may be used and distributed according to the terms of the
-# GNU General Public License version 2, incorporated herein by reference.
-#
-# This was originally copied from the public domain code at
-# http://www.python.org/dev/peps/pep-0333/#the-server-gateway-side
-
-import os, sys
-from mercurial import util
-
-def launch(application):
- util.set_binary(sys.stdin)
- util.set_binary(sys.stdout)
-
- environ = dict(os.environ.iteritems())
- environ.setdefault('PATH_INFO', '')
- if '.cgi' in environ['PATH_INFO']:
- environ['PATH_INFO'] = environ['PATH_INFO'].split('.cgi', 1)[1]
-
- environ['wsgi.input'] = sys.stdin
- environ['wsgi.errors'] = sys.stderr
- environ['wsgi.version'] = (1, 0)
- environ['wsgi.multithread'] = False
- environ['wsgi.multiprocess'] = True
- environ['wsgi.run_once'] = True
-
- if environ.get('HTTPS','off').lower() in ('on','1','yes'):
- environ['wsgi.url_scheme'] = 'https'
- else:
- environ['wsgi.url_scheme'] = 'http'
-
- headers_set = []
- headers_sent = []
- out = sys.stdout
-
- def write(data):
- if not headers_set:
- raise AssertionError("write() before start_response()")
-
- elif not headers_sent:
- # Before the first output, send the stored headers
- status, response_headers = headers_sent[:] = headers_set
- out.write('Status: %s\r\n' % status)
- for header in response_headers:
- out.write('%s: %s\r\n' % header)
- out.write('\r\n')
-
- out.write(data)
- out.flush()
-
- def start_response(status, response_headers, exc_info=None):
- if exc_info:
- try:
- if headers_sent:
- # Re-raise original exception if headers sent
- raise exc_info[0](exc_info[1], exc_info[2])
- finally:
- exc_info = None # avoid dangling circular ref
- elif headers_set:
- raise AssertionError("Headers already set!")
-
- headers_set[:] = [status, response_headers]
- return write
-
- content = application(environ, start_response)
- for chunk in content:
- write(chunk)
--- a/sys/src/cmd/hg/mercurial/pure/base85.py
+++ /dev/null
@@ -1,74 +1,0 @@
-# base85.py: pure python base85 codec
-#
-# Copyright (C) 2009 Brendan Cully <brendan@kublai.com>
-#
-# This software may be used and distributed according to the terms of the
-# GNU General Public License version 2, incorporated herein by reference.
-
-import struct
-
-_b85chars = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ" \
- "abcdefghijklmnopqrstuvwxyz!#$%&()*+-;<=>?@^_`{|}~"
-_b85chars2 = [(a + b) for a in _b85chars for b in _b85chars]
-_b85dec = {}
-
-def _mkb85dec():
- for i, c in enumerate(_b85chars):
- _b85dec[c] = i
-
-def b85encode(text, pad=False):
- """encode text in base85 format"""
- l = len(text)
- r = l % 4
- if r:
- text += '\0' * (4 - r)
- longs = len(text) >> 2
- words = struct.unpack('>%dL' % (longs), text)
-
- out = ''.join(_b85chars[(word // 52200625) % 85] +
- _b85chars2[(word // 7225) % 7225] +
- _b85chars2[word % 7225]
- for word in words)
-
- if pad:
- return out
-
- # Trim padding
- olen = l % 4
- if olen:
- olen += 1
- olen += l // 4 * 5
- return out[:olen]
-
-def b85decode(text):
- """decode base85-encoded text"""
- if not _b85dec:
- _mkb85dec()
-
- l = len(text)
- out = []
- for i in range(0, len(text), 5):
- chunk = text[i:i+5]
- acc = 0
- for j, c in enumerate(chunk):
- try:
- acc = acc * 85 + _b85dec[c]
- except KeyError:
- raise TypeError('Bad base85 character at byte %d' % (i + j))
- if acc > 4294967295:
- raise OverflowError('Base85 overflow in hunk starting at byte %d' % i)
- out.append(acc)
-
- # Pad final chunk if necessary
- cl = l % 5
- if cl:
- acc *= 85 ** (5 - cl)
- if cl > 1:
- acc += 0xffffff >> (cl - 2) * 8
- out[-1] = acc
-
- out = struct.pack('>%dL' % (len(out)), *out)
- if cl:
- out = out[:-(5 - cl)]
-
- return out
--- a/sys/src/cmd/hg/mercurial/pure/bdiff.py
+++ /dev/null
@@ -1,76 +1,0 @@
-# bdiff.py - Python implementation of bdiff.c
-#
-# Copyright 2009 Matt Mackall <mpm@selenic.com> and others
-#
-# This software may be used and distributed according to the terms of the
-# GNU General Public License version 2, incorporated herein by reference.
-
-import struct, difflib
-
-def splitnewlines(text):
- '''like str.splitlines, but only split on newlines.'''
- lines = [l + '\n' for l in text.split('\n')]
- if lines:
- if lines[-1] == '\n':
- lines.pop()
- else:
- lines[-1] = lines[-1][:-1]
- return lines
-
-def _normalizeblocks(a, b, blocks):
- prev = None
- for curr in blocks:
- if prev is None:
- prev = curr
- continue
- shift = 0
-
- a1, b1, l1 = prev
- a1end = a1 + l1
- b1end = b1 + l1
-
- a2, b2, l2 = curr
- a2end = a2 + l2
- b2end = b2 + l2
- if a1end == a2:
- while a1end+shift < a2end and a[a1end+shift] == b[b1end+shift]:
- shift += 1
- elif b1end == b2:
- while b1end+shift < b2end and a[a1end+shift] == b[b1end+shift]:
- shift += 1
- yield a1, b1, l1+shift
- prev = a2+shift, b2+shift, l2-shift
- yield prev
-
-def bdiff(a, b):
- a = str(a).splitlines(True)
- b = str(b).splitlines(True)
-
- if not a:
- s = "".join(b)
- return s and (struct.pack(">lll", 0, 0, len(s)) + s)
-
- bin = []
- p = [0]
- for i in a: p.append(p[-1] + len(i))
-
- d = difflib.SequenceMatcher(None, a, b).get_matching_blocks()
- d = _normalizeblocks(a, b, d)
- la = 0
- lb = 0
- for am, bm, size in d:
- s = "".join(b[lb:bm])
- if am > la or s:
- bin.append(struct.pack(">lll", p[la], p[am], len(s)) + s)
- la = am + size
- lb = bm + size
-
- return "".join(bin)
-
-def blocks(a, b):
- an = splitnewlines(a)
- bn = splitnewlines(b)
- d = difflib.SequenceMatcher(None, an, bn).get_matching_blocks()
- d = _normalizeblocks(an, bn, d)
- return [(i, i + n, j, j + n) for (i, j, n) in d]
-
--- a/sys/src/cmd/hg/mercurial/pure/diffhelpers.py
+++ /dev/null
@@ -1,56 +1,0 @@
-# diffhelpers.py - pure Python implementation of diffhelpers.c
-#
-# Copyright 2009 Matt Mackall <mpm@selenic.com> and others
-#
-# This software may be used and distributed according to the terms of the
-# GNU General Public License version 2, incorporated herein by reference.
-
-def addlines(fp, hunk, lena, lenb, a, b):
- while True:
- todoa = lena - len(a)
- todob = lenb - len(b)
- num = max(todoa, todob)
- if num == 0:
- break
- for i in xrange(num):
- s = fp.readline()
- c = s[0]
- if s == "\\ No newline at end of file\n":
- fix_newline(hunk, a, b)
- continue
- if c == "\n":
- # Some patches may be missing the control char
- # on empty lines. Supply a leading space.
- s = " \n"
- hunk.append(s)
- if c == "+":
- b.append(s[1:])
- elif c == "-":
- a.append(s)
- else:
- b.append(s[1:])
- a.append(s)
- return 0
-
-def fix_newline(hunk, a, b):
- l = hunk[-1]
- c = l[0]
- hline = l[:-1]
-
- if c == " " or c == "+":
- b[-1] = l[1:-1]
- if c == " " or c == "-":
- a[-1] = hline
- hunk[-1] = hline
- return 0
-
-
-def testhunk(a, b, bstart):
- alen = len(a)
- blen = len(b)
- if alen > blen - bstart:
- return -1
- for i in xrange(alen):
- if a[i][1:] != b[i + bstart]:
- return -1
- return 0
--- a/sys/src/cmd/hg/mercurial/pure/mpatch.py
+++ /dev/null
@@ -1,116 +1,0 @@
-# mpatch.py - Python implementation of mpatch.c
-#
-# Copyright 2009 Matt Mackall <mpm@selenic.com> and others
-#
-# This software may be used and distributed according to the terms of the
-# GNU General Public License version 2, incorporated herein by reference.
-
-import struct
-try:
- from cStringIO import StringIO
-except ImportError:
- from StringIO import StringIO
-
-# This attempts to apply a series of patches in time proportional to
-# the total size of the patches, rather than patches * len(text). This
-# means rather than shuffling strings around, we shuffle around
-# pointers to fragments with fragment lists.
-#
-# When the fragment lists get too long, we collapse them. To do this
-# efficiently, we do all our operations inside a buffer created by
-# mmap and simply use memmove. This avoids creating a bunch of large
-# temporary string buffers.
-
-def patches(a, bins):
- if not bins: return a
-
- plens = [len(x) for x in bins]
- pl = sum(plens)
- bl = len(a) + pl
- tl = bl + bl + pl # enough for the patches and two working texts
- b1, b2 = 0, bl
-
- if not tl: return a
-
- m = StringIO()
- def move(dest, src, count):
- """move count bytes from src to dest
-
- The file pointer is left at the end of dest.
- """
- m.seek(src)
- buf = m.read(count)
- m.seek(dest)
- m.write(buf)
-
- # load our original text
- m.write(a)
- frags = [(len(a), b1)]
-
- # copy all the patches into our segment so we can memmove from them
- pos = b2 + bl
- m.seek(pos)
- for p in bins: m.write(p)
-
- def pull(dst, src, l): # pull l bytes from src
- while l:
- f = src.pop(0)
- if f[0] > l: # do we need to split?
- src.insert(0, (f[0] - l, f[1] + l))
- dst.append((l, f[1]))
- return
- dst.append(f)
- l -= f[0]
-
- def collect(buf, list):
- start = buf
- for l, p in list:
- move(buf, p, l)
- buf += l
- return (buf - start, start)
-
- for plen in plens:
- # if our list gets too long, execute it
- if len(frags) > 128:
- b2, b1 = b1, b2
- frags = [collect(b1, frags)]
-
- new = []
- end = pos + plen
- last = 0
- while pos < end:
- m.seek(pos)
- p1, p2, l = struct.unpack(">lll", m.read(12))
- pull(new, frags, p1 - last) # what didn't change
- pull([], frags, p2 - p1) # what got deleted
- new.append((l, pos + 12)) # what got added
- pos += l + 12
- last = p2
- frags = new + frags # what was left at the end
-
- t = collect(b2, frags)
-
- m.seek(t[1])
- return m.read(t[0])
-
-def patchedsize(orig, delta):
- outlen, last, bin = 0, 0, 0
- binend = len(delta)
- data = 12
-
- while data <= binend:
- decode = delta[bin:bin + 12]
- start, end, length = struct.unpack(">lll", decode)
- if start > end:
- break
- bin = data + length
- data = bin + 12
- outlen += start - last
- last = end
- outlen += length
-
- if bin != binend:
- raise Exception("patch cannot be decoded")
-
- outlen += orig - last
- return outlen
--- a/sys/src/cmd/hg/mercurial/pure/osutil.py
+++ /dev/null
@@ -1,52 +1,0 @@
-# osutil.py - pure Python version of osutil.c
-#
-# Copyright 2009 Matt Mackall <mpm@selenic.com> and others
-#
-# This software may be used and distributed according to the terms of the
-# GNU General Public License version 2, incorporated herein by reference.
-
-import os
-import stat as _stat
-
-posixfile = open
-
-def _mode_to_kind(mode):
- if _stat.S_ISREG(mode): return _stat.S_IFREG
- if _stat.S_ISDIR(mode): return _stat.S_IFDIR
- if _stat.S_ISLNK(mode): return _stat.S_IFLNK
- if _stat.S_ISBLK(mode): return _stat.S_IFBLK
- if _stat.S_ISCHR(mode): return _stat.S_IFCHR
- if _stat.S_ISFIFO(mode): return _stat.S_IFIFO
- if _stat.S_ISSOCK(mode): return _stat.S_IFSOCK
- return mode
-
-def listdir(path, stat=False, skip=None):
- '''listdir(path, stat=False) -> list_of_tuples
-
- Return a sorted list containing information about the entries
- in the directory.
-
- If stat is True, each element is a 3-tuple:
-
- (name, type, stat object)
-
- Otherwise, each element is a 2-tuple:
-
- (name, type)
- '''
- result = []
- prefix = path
- if not prefix.endswith(os.sep):
- prefix += os.sep
- names = os.listdir(path)
- names.sort()
- for fn in names:
- st = os.lstat(prefix + fn)
- if fn == skip and _stat.S_ISDIR(st.st_mode):
- return []
- if stat:
- result.append((fn, _mode_to_kind(st.st_mode), st))
- else:
- result.append((fn, _mode_to_kind(st.st_mode)))
- return result
-
--- a/sys/src/cmd/hg/mercurial/pure/parsers.py
+++ /dev/null
@@ -1,90 +1,0 @@
-# parsers.py - Python implementation of parsers.c
-#
-# Copyright 2009 Matt Mackall <mpm@selenic.com> and others
-#
-# This software may be used and distributed according to the terms of the
-# GNU General Public License version 2, incorporated herein by reference.
-
-from mercurial.node import bin, nullid, nullrev
-from mercurial import util
-import struct, zlib
-
-_pack = struct.pack
-_unpack = struct.unpack
-_compress = zlib.compress
-_decompress = zlib.decompress
-_sha = util.sha1
-
-def parse_manifest(mfdict, fdict, lines):
- for l in lines.splitlines():
- f, n = l.split('\0')
- if len(n) > 40:
- fdict[f] = n[40:]
- mfdict[f] = bin(n[:40])
- else:
- mfdict[f] = bin(n)
-
-def parse_index(data, inline):
- def gettype(q):
- return int(q & 0xFFFF)
-
- def offset_type(offset, type):
- return long(long(offset) << 16 | type)
-
- indexformatng = ">Qiiiiii20s12x"
-
- s = struct.calcsize(indexformatng)
- index = []
- cache = None
- nodemap = {nullid: nullrev}
- n = off = 0
- # if we're not using lazymap, always read the whole index
- l = len(data) - s
- append = index.append
- if inline:
- cache = (0, data)
- while off <= l:
- e = _unpack(indexformatng, data[off:off + s])
- nodemap[e[7]] = n
- append(e)
- n += 1
- if e[1] < 0:
- break
- off += e[1] + s
- else:
- while off <= l:
- e = _unpack(indexformatng, data[off:off + s])
- nodemap[e[7]] = n
- append(e)
- n += 1
- off += s
-
- e = list(index[0])
- type = gettype(e[0])
- e[0] = offset_type(0, type)
- index[0] = tuple(e)
-
- # add the magic null revision at -1
- index.append((0, 0, 0, -1, -1, -1, -1, nullid))
-
- return index, nodemap, cache
-
-def parse_dirstate(dmap, copymap, st):
- parents = [st[:20], st[20: 40]]
- # deref fields so they will be local in loop
- format = ">cllll"
- e_size = struct.calcsize(format)
- pos1 = 40
- l = len(st)
-
- # the inner loop
- while pos1 < l:
- pos2 = pos1 + e_size
- e = _unpack(">cllll", st[pos1:pos2]) # a literal here is faster
- pos1 = pos2 + e[4]
- f = st[pos2:pos1]
- if '\0' in f:
- f, c = f.split('\0')
- copymap[f] = c
- dmap[f] = e[:4]
- return parents
--- a/sys/src/cmd/hg/templates/atom/changelog.tmpl
+++ /dev/null
@@ -1,10 +1,0 @@
-{header}
- <!-- Changelog -->
- <id>{urlbase}{url}</id>
- <link rel="self" href="{urlbase}{url}atom-log"/>
- <link rel="alternate" href="{urlbase}{url}"/>
- <title>{repo|escape} Changelog</title>
- {latestentry%feedupdated}
-
-{entries%changelogentry}
-</feed>
--- a/sys/src/cmd/hg/templates/atom/changelogentry.tmpl
+++ /dev/null
@@ -1,16 +1,0 @@
- <entry>
- <title>{desc|strip|firstline|strip|escape|nonempty}</title>
- <id>{urlbase}{url}#changeset-{node}</id>
- <link href="{urlbase}{url}rev/{node}"/>
- <author>
- <name>{author|person|escape}</name>
- <email>{author|email|obfuscate}</email>
- </author>
- <updated>{date|rfc3339date}</updated>
- <published>{date|rfc3339date}</published>
- <content type="xhtml">
- <div xmlns="http://www.w3.org/1999/xhtml">
- <pre xml:space="preserve">{desc|escape|nonempty}</pre>
- </div>
- </content>
- </entry>
--- a/sys/src/cmd/hg/templates/atom/error.tmpl
+++ /dev/null
@@ -1,17 +1,0 @@
-{header}
- <!-- Error -->
- <id>{urlbase}{url}</id>
- <link rel="self" href="{urlbase}{url}atom-log"/>
- <link rel="alternate" href="{urlbase}{url}"/>
- <title>Error</title>
- <updated>1970-01-01T00:00:00+00:00</updated>
- <entry>
- <title>Error</title>
- <id>http://mercurial.selenic.com/#error</id>
- <author>
- <name>mercurial</name>
- </author>
- <updated>1970-01-01T00:00:00+00:00</updated>
- <content type="text">{error|escape}</content>
- </entry>
-</feed>
--- a/sys/src/cmd/hg/templates/atom/filelog.tmpl
+++ /dev/null
@@ -1,8 +1,0 @@
-{header}
- <id>{urlbase}{url}atom-log/tip/{file|escape}</id>
- <link rel="self" href="{urlbase}{url}atom-log/tip/{file|urlescape}"/>
- <title>{repo|escape}: {file|escape} history</title>
- {latestentry%feedupdated}
-
-{entries%changelogentry}
-</feed>
--- a/sys/src/cmd/hg/templates/atom/header.tmpl
+++ /dev/null
@@ -1,2 +1,0 @@
-<?xml version="1.0" encoding="{encoding}"?>
-<feed xmlns="http://www.w3.org/2005/Atom">
\ No newline at end of file
--- a/sys/src/cmd/hg/templates/atom/map
+++ /dev/null
@@ -1,11 +1,0 @@
-default = 'changelog'
-feedupdated = '<updated>{date|rfc3339date}</updated>'
-mimetype = 'application/atom+xml; charset={encoding}'
-header = header.tmpl
-changelog = changelog.tmpl
-changelogentry = changelogentry.tmpl
-filelog = filelog.tmpl
-filelogentry = filelogentry.tmpl
-tags = tags.tmpl
-tagentry = tagentry.tmpl
-error = error.tmpl
--- a/sys/src/cmd/hg/templates/atom/tagentry.tmpl
+++ /dev/null
@@ -1,8 +1,0 @@
- <entry>
- <title>{tag|escape}</title>
- <link rel="alternate" href="{urlbase}{url}rev/{node}"/>
- <id>{urlbase}{url}#tag-{node}</id>
- <updated>{date|rfc3339date}</updated>
- <published>{date|rfc3339date}</published>
- <content type="text">{tag|strip|escape}</content>
- </entry>
--- a/sys/src/cmd/hg/templates/atom/tags.tmpl
+++ /dev/null
@@ -1,11 +1,0 @@
-{header}
- <id>{urlbase}{url}</id>
- <link rel="self" href="{urlbase}{url}atom-tags"/>
- <link rel="alternate" href="{urlbase}{url}tags"/>
- <title>{repo|escape}: tags</title>
- <summary>{repo|escape} tag history</summary>
- <author><name>Mercurial SCM</name></author>
- {latestentry%feedupdated}
-
-{entriesnotip%tagentry}
-</feed>
--- a/sys/src/cmd/hg/templates/coal/header.tmpl
+++ /dev/null
@@ -1,6 +1,0 @@
-<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
-<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en-US">
-<head>
-<link rel="icon" href="{staticurl}hgicon.png" type="image/png" />
-<meta name="robots" content="index, nofollow" />
-<link rel="stylesheet" href="{staticurl}style-coal.css" type="text/css" />
--- a/sys/src/cmd/hg/templates/coal/map
+++ /dev/null
@@ -1,191 +1,0 @@
-default = 'shortlog'
-
-mimetype = 'text/html; charset={encoding}'
-header = header.tmpl
-footer = ../paper/footer.tmpl
-search = ../paper/search.tmpl
-
-changelog = ../paper/shortlog.tmpl
-shortlog = ../paper/shortlog.tmpl
-shortlogentry = ../paper/shortlogentry.tmpl
-graph = ../paper/graph.tmpl
-
-naventry = '<a href="{url}log/{node|short}{sessionvars%urlparameter}">{label|escape}</a> '
-navshortentry = '<a href="{url}shortlog/{node|short}{sessionvars%urlparameter}">{label|escape}</a> '
-navgraphentry = '<a href="{url}graph/{node|short}{sessionvars%urlparameter}">{label|escape}</a> '
-filenaventry = '<a href="{url}log/{node|short}/{file|urlescape}{sessionvars%urlparameter}">{label|escape}</a> '
-filedifflink = '<a href="{url}diff/{node|short}/{file|urlescape}{sessionvars%urlparameter}">{file|escape}</a> '
-filenodelink = '<a href="{url}file/{node|short}/{file|urlescape}{sessionvars%urlparameter}">{file|escape}</a> '
-filenolink = '{file|escape} '
-fileellipses = '...'
-changelogentry = ../paper/shortlogentry.tmpl
-searchentry = ../paper/shortlogentry.tmpl
-changeset = ../paper/changeset.tmpl
-manifest = ../paper/manifest.tmpl
-
-direntry = '
- <tr class="fileline parity{parity}">
- <td class="name">
- <a href="{url}file/{node|short}{path|urlescape}{sessionvars%urlparameter}">
- <img src="{staticurl}coal-folder.png" alt="dir."/> {basename|escape}/
- </a>
- <a href="{url}file/{node|short}{path|urlescape}/{emptydirs|urlescape}{sessionvars%urlparameter}">
- {emptydirs|escape}
- </a>
- </td>
- <td class="size"></td>
- <td class="permissions">drwxr-xr-x</td>
- </tr>'
-
-fileentry = '
- <tr class="fileline parity{parity}">
- <td class="filename">
- <a href="{url}file/{node|short}/{file|urlescape}{sessionvars%urlparameter}">
- <img src="{staticurl}coal-file.png" alt="file"/> {basename|escape}
- </a>
- </td>
- <td class="size">{size}</td>
- <td class="permissions">{permissions|permissions}</td>
- </tr>'
-
-filerevision = ../paper/filerevision.tmpl
-fileannotate = ../paper/fileannotate.tmpl
-filediff = ../paper/filediff.tmpl
-filelog = ../paper/filelog.tmpl
-fileline = '
- <div class="parity{parity} source"><a href="#{lineid}" id="{lineid}">{linenumber}</a> {line|escape}</div>'
-filelogentry = ../paper/filelogentry.tmpl
-
-annotateline = '
- <tr class="parity{parity}">
- <td class="annotate">
- <a href="{url}annotate/{node|short}/{file|urlescape}{sessionvars%urlparameter}#{targetline}"
- title="{node|short}: {desc|escape|firstline}">{author|user}@{rev}</a>
- </td>
- <td class="source"><a href="#{lineid}" id="{lineid}">{linenumber}</a> {line|escape}</td>
- </tr>'
-
-diffblock = '<div class="source bottomline parity{parity}"><pre>{lines}</pre></div>'
-difflineplus = '<a href="#{lineid}" id="{lineid}">{linenumber}</a> <span class="plusline">{line|escape}</span>'
-difflineminus = '<a href="#{lineid}" id="{lineid}">{linenumber}</a> <span class="minusline">{line|escape}</span>'
-difflineat = '<a href="#{lineid}" id="{lineid}">{linenumber}</a> <span class="atline">{line|escape}</span>'
-diffline = '<a href="#{lineid}" id="{lineid}">{linenumber}</a> {line|escape}'
-
-changelogparent = '
- <tr>
- <th class="parent">parent {rev}:</th>
- <td class="parent"><a href="{url}rev/{node|short}{sessionvars%urlparameter}">{node|short}</a></td>
- </tr>'
-
-changesetparent = '<a href="{url}rev/{node|short}{sessionvars%urlparameter}">{node|short}</a> '
-
-filerevparent = '<a href="{url}file/{node|short}/{file|urlescape}{sessionvars%urlparameter}">{rename%filerename}{node|short}</a> '
-filerevchild = '<a href="{url}file/{node|short}/{file|urlescape}{sessionvars%urlparameter}">{node|short}</a> '
-
-filerename = '{file|escape}@'
-filelogrename = '
- <tr>
- <th>base:</th>
- <td>
- <a href="{url}file/{node|short}/{file|urlescape}{sessionvars%urlparameter}">
- {file|escape}@{node|short}
- </a>
- </td>
- </tr>'
-fileannotateparent = '
- <tr>
- <td class="metatag">parent:</td>
- <td>
- <a href="{url}annotate/{node|short}/{file|urlescape}{sessionvars%urlparameter}">
- {rename%filerename}{node|short}
- </a>
- </td>
- </tr>'
-changesetchild = ' <a href="{url}rev/{node|short}{sessionvars%urlparameter}">{node|short}</a>'
-changelogchild = '
- <tr>
- <th class="child">child</th>
- <td class="child">
- <a href="{url}rev/{node|short}{sessionvars%urlparameter}">
- {node|short}
- </a>
- </td>
- </tr>'
-fileannotatechild = '
- <tr>
- <td class="metatag">child:</td>
- <td>
- <a href="{url}annotate/{node|short}/{file|urlescape}{sessionvars%urlparameter}">
- {node|short}
- </a>
- </td>
- </tr>'
-tags = ../paper/tags.tmpl
-tagentry = '
- <tr class="tagEntry parity{parity}">
- <td>
- <a href="{url}rev/{node|short}{sessionvars%urlparameter}">
- {tag|escape}
- </a>
- </td>
- <td class="node">
- {node|short}
- </td>
- </tr>'
-branches = ../paper/branches.tmpl
-branchentry = '
- <tr class="tagEntry parity{parity}">
- <td>
- <a href="{url}shortlog/{node|short}{sessionvars%urlparameter}" class="{status}">
- {branch|escape}
- </a>
- </td>
- <td class="node">
- {node|short}
- </td>
- </tr>'
-changelogtag = '<span class="tag">{name|escape}</span> '
-changesettag = '<span class="tag">{tag|escape}</span> '
-changelogbranchhead = '<span class="branchhead">{name|escape}</span> '
-changelogbranchname = '<span class="branchname">{name|escape}</span> '
-
-filediffparent = '
- <tr>
- <th class="parent">parent {rev}:</th>
- <td class="parent"><a href="{url}rev/{node|short}{sessionvars%urlparameter}">{node|short}</a></td>
- </tr>'
-filelogparent = '
- <tr>
- <th>parent {rev}:</th>
- <td><a href="{url}file/{node|short}/{file|urlescape}{sessionvars%urlparameter}">{node|short}</a></td>
- </tr>'
-filediffchild = '
- <tr>
- <th class="child">child {rev}:</th>
- <td class="child"><a href="{url}rev/{node|short}{sessionvars%urlparameter}">{node|short}</a>
- </td>
- </tr>'
-filelogchild = '
- <tr>
- <th>child {rev}:</th>
- <td><a href="{url}file/{node|short}/{file|urlescape}{sessionvars%urlparameter}">{node|short}</a></td>
- </tr>'
-
-indexentry = '
- <tr class="parity{parity}">
- <td><a href="{url}{sessionvars%urlparameter}">{name|escape}</a></td>
- <td>{description}</td>
- <td>{contact|obfuscate}</td>
- <td class="age">{lastchange|age} ago</td>
- <td class="indexlinks">{archives%indexarchiveentry}</td>
- </tr>\n'
-indexarchiveentry = '<a href="{url}archive/{node|short}{extension|urlescape}"> ↓{type|escape}</a>'
-index = ../paper/index.tmpl
-archiveentry = '
- <li>
- <a href="{url}archive/{node|short}{extension|urlescape}">{type|escape}</a>
- </li>'
-notfound = ../paper/notfound.tmpl
-error = ../paper/error.tmpl
-urlparameter = '{separator}{name}={value|urlescape}'
-hiddenformentry = '<input type="hidden" name="{name}" value="{value|escape}" />'
--- a/sys/src/cmd/hg/templates/gitweb/branches.tmpl
+++ /dev/null
@@ -1,30 +1,0 @@
-{header}
-<title>{repo|escape}: Branches</title>
-<link rel="alternate" type="application/atom+xml"
- href="{url}atom-tags" title="Atom feed for {repo|escape}"/>
-<link rel="alternate" type="application/rss+xml"
- href="{url}rss-tags" title="RSS feed for {repo|escape}"/>
-</head>
-<body>
-
-<div class="page_header">
-<a href="http://mercurial.selenic.com/" title="Mercurial" style="float: right;">Mercurial</a><a href="{url}summary{sessionvars%urlparameter}">{repo|escape}</a> / branches
-</div>
-
-<div class="page_nav">
-<a href="{url}summary{sessionvars%urlparameter}">summary</a> |
-<a href="{url}shortlog{sessionvars%urlparameter}">shortlog</a> |
-<a href="{url}log{sessionvars%urlparameter}">changelog</a> |
-<a href="{url}graph{sessionvars%urlparameter}">graph</a> |
-<a href="{url}tags{sessionvars%urlparameter}">tags</a> |
-branches |
-<a href="{url}file/{node|short}{sessionvars%urlparameter}">files</a>
-<br/>
-</div>
-
-<div class="title"> </div>
-<table cellspacing="0">
-{entries%branchentry}
-</table>
-
-{footer}
--- a/sys/src/cmd/hg/templates/gitweb/changelog.tmpl
+++ /dev/null
@@ -1,39 +1,0 @@
-{header}
-<title>{repo|escape}: Changelog</title>
-<link rel="alternate" type="application/atom+xml"
- href="{url}atom-log" title="Atom feed for {repo|escape}"/>
-<link rel="alternate" type="application/rss+xml"
- href="{url}rss-log" title="RSS feed for {repo|escape}"/>
-</head>
-<body>
-
-<div class="page_header">
-<a href="http://mercurial.selenic.com/" title="Mercurial" style="float: right;">Mercurial</a><a href="{url}summary{sessionvars%urlparameter}">{repo|escape}</a> / changelog
-</div>
-
-<form action="{url}log">
-{sessionvars%hiddenformentry}
-<div class="search">
-<input type="text" name="rev" />
-</div>
-</form>
-
-<div class="page_nav">
-<a href="{url}summary{sessionvars%urlparameter}">summary</a> |
-<a href="{url}shortlog/{rev}{sessionvars%urlparameter}">shortlog</a> |
-changelog |
-<a href="{url}graph{sessionvars%urlparameter}">graph</a> |
-<a href="{url}tags{sessionvars%urlparameter}">tags</a> |
-<a href="{url}branches{sessionvars%urlparameter}">branches</a> |
-<a href="{url}file/{node|short}{sessionvars%urlparameter}">files</a>{archives%archiveentry}
-<br/>
-{changenav%naventry}<br/>
-</div>
-
-{entries%changelogentry}
-
-<div class="page_nav">
-{changenav%naventry}<br/>
-</div>
-
-{footer}
--- a/sys/src/cmd/hg/templates/gitweb/changelogentry.tmpl
+++ /dev/null
@@ -1,14 +1,0 @@
-<div>
-<a class="title" href="{url}rev/{node|short}{sessionvars%urlparameter}"><span class="age">{date|age} ago</span>{desc|strip|firstline|escape|nonempty}<span class="logtags"> {inbranch%inbranchtag}{branches%branchtag}{tags%tagtag}</span></a>
-</div>
-<div class="title_text">
-<div class="log_link">
-<a href="{url}rev/{node|short}{sessionvars%urlparameter}">changeset</a><br/>
-</div>
-<i>{author|obfuscate} [{date|rfc822date}] rev {rev}</i><br/>
-</div>
-<div class="log_body">
-{desc|strip|escape|addbreaks|nonempty}
-<br/>
-<br/>
-</div>
--- a/sys/src/cmd/hg/templates/gitweb/changeset.tmpl
+++ /dev/null
@@ -1,50 +1,0 @@
-{header}
-<title>{repo|escape}: changeset {rev}:{node|short}</title>
-<link rel="alternate" type="application/atom+xml"
- href="{url}atom-log" title="Atom feed for {repo|escape}"/>
-<link rel="alternate" type="application/rss+xml"
- href="{url}rss-log" title="RSS feed for {repo|escape}"/>
-</head>
-<body>
-
-<div class="page_header">
-<a href="http://mercurial.selenic.com/" title="Mercurial" style="float: right;">Mercurial</a><a href="{url}summary{sessionvars%urlparameter}">{repo|escape}</a> / changeset
-</div>
-
-<div class="page_nav">
-<a href="{url}summary{sessionvars%urlparameter}">summary</a> |
-<a href="{url}shortlog/{rev}{sessionvars%urlparameter}">shortlog</a> |
-<a href="{url}log/{rev}{sessionvars%urlparameter}">changelog</a> |
-<a href="{url}graph{sessionvars%urlparameter}">graph</a> |
-<a href="{url}tags{sessionvars%urlparameter}">tags</a> |
-<a href="{url}branches{sessionvars%urlparameter}">branches</a> |
-<a href="{url}file/{node|short}{sessionvars%urlparameter}">files</a> |
-changeset |
-<a href="{url}raw-rev/{node|short}">raw</a> {archives%archiveentry}<br/>
-</div>
-
-<div>
-<a class="title" href="{url}raw-rev/{node|short}">{desc|strip|escape|firstline|nonempty} <span class="logtags">{inbranch%inbranchtag}{branches%branchtag}{tags%tagtag}</span></a>
-</div>
-<div class="title_text">
-<table cellspacing="0">
-<tr><td>author</td><td>{author|obfuscate}</td></tr>
-<tr><td></td><td>{date|date} ({date|age} ago)</td></tr>
-{branch%changesetbranch}
-<tr><td>changeset {rev}</td><td style="font-family:monospace">{node|short}</td></tr>
-{parent%changesetparent}
-{child%changesetchild}
-</table></div>
-
-<div class="page_body">
-{desc|strip|escape|addbreaks|nonempty}
-</div>
-<div class="list_head"></div>
-<div class="title_text">
-<table cellspacing="0">
-{files}
-</table></div>
-
-<div class="page_body">{diff}</div>
-
-{footer}
--- a/sys/src/cmd/hg/templates/gitweb/error.tmpl
+++ /dev/null
@@ -1,25 +1,0 @@
-{header}
-<title>{repo|escape}: Error</title>
-<link rel="alternate" type="application/atom+xml"
- href="{url}atom-log" title="Atom feed for {repo|escape}"/>
-<link rel="alternate" type="application/rss+xml"
- href="{url}rss-log" title="RSS feed for {repo|escape}"/>
-</head>
-<body>
-
-<div class="page_header">
-<a href="http://mercurial.selenic.com/" title="Mercurial" style="float: right;">Mercurial</a><a href="{url}summary{sessionvars%urlparameter}">{repo|escape}</a> / error
-</div>
-
-<div class="page_nav">
-<a href="{url}summary{sessionvars%urlparameter}">summary</a> | <a href="{url}shortlog{sessionvars%urlparameter}">shortlog</a> | <a href="{url}log{sessionvars%urlparameter}">changelog</a> | <a href="{url}tags{sessionvars%urlparameter}">tags</a> | <a href="{url}file/{node|short}{sessionvars%urlparameter}">files</a><br/>
-</div>
-
-<div class="page_body">
-<br/>
-<i>An error occurred while processing your request</i><br/>
-<br/>
-{error|escape}
-</div>
-
-{footer}
--- a/sys/src/cmd/hg/templates/gitweb/fileannotate.tmpl
+++ /dev/null
@@ -1,61 +1,0 @@
-{header}
-<title>{repo|escape}: {file|escape}@{node|short} (annotated)</title>
-<link rel="alternate" type="application/atom+xml"
- href="{url}atom-log" title="Atom feed for {repo|escape}"/>
-<link rel="alternate" type="application/rss+xml"
- href="{url}rss-log" title="RSS feed for {repo|escape}"/>
-</head>
-<body>
-
-<div class="page_header">
-<a href="http://mercurial.selenic.com/" title="Mercurial" style="float: right;">Mercurial</a><a href="{url}summary{sessionvars%urlparameter}">{repo|escape}</a> / annotate
-</div>
-
-<div class="page_nav">
-<a href="{url}summary{sessionvars%urlparameter}">summary</a> |
-<a href="{url}shortlog{sessionvars%urlparameter}">shortlog</a> |
-<a href="{url}log{sessionvars%urlparameter}">changelog</a> |
-<a href="{url}graph{sessionvars%urlparameter}">graph</a> |
-<a href="{url}tags{sessionvars%urlparameter}">tags</a> |
-<a href="{url}branches{sessionvars%urlparameter}">branches</a> |
-<a href="{url}file/{node|short}{path|urlescape}{sessionvars%urlparameter}">files</a> |
-<a href="{url}rev/{node|short}{sessionvars%urlparameter}">changeset</a> |
-<a href="{url}file/{node|short}/{file|urlescape}{sessionvars%urlparameter}">file</a> |
-<a href="{url}log/{node|short}/{file|urlescape}{sessionvars%urlparameter}">revisions</a> |
-annotate |
-<a href="{url}diff/{node|short}/{file|urlescape}{sessionvars%urlparameter}">diff</a> |
-<a href="{url}raw-annotate/{node|short}/{file|urlescape}">raw</a><br/>
-</div>
-
-<div class="title">{file|escape}</div>
-
-<div class="title_text">
-<table cellspacing="0">
-<tr>
- <td>author</td>
- <td>{author|obfuscate}</td></tr>
-<tr>
- <td></td>
- <td>{date|date} ({date|age} ago)</td></tr>
-{branch%filerevbranch}
-<tr>
- <td>changeset {rev}</td>
- <td style="font-family:monospace"><a class="list" href="{url}rev/{node|short}{sessionvars%urlparameter}">{node|short}</a></td></tr>
-{parent%fileannotateparent}
-{child%fileannotatechild}
-<tr>
- <td>permissions</td>
- <td style="font-family:monospace">{permissions|permissions}</td></tr>
-</table>
-</div>
-
-<div class="page_path">
-{desc|strip|escape|addbreaks|nonempty}
-</div>
-<div class="page_body">
-<table>
-{annotate%annotateline}
-</table>
-</div>
-
-{footer}
--- a/sys/src/cmd/hg/templates/gitweb/filediff.tmpl
+++ /dev/null
@@ -1,47 +1,0 @@
-{header}
-<title>{repo|escape}: diff {file|escape}</title>
-<link rel="alternate" type="application/atom+xml"
- href="{url}atom-log" title="Atom feed for {repo|escape}"/>
-<link rel="alternate" type="application/rss+xml"
- href="{url}rss-log" title="RSS feed for {repo|escape}"/>
-</head>
-<body>
-
-<div class="page_header">
-<a href="http://mercurial.selenic.com/" title="Mercurial" style="float: right;">Mercurial</a><a href="{url}summary{sessionvars%urlparameter}">{repo|escape}</a> / diff
-</div>
-
-<div class="page_nav">
-<a href="{url}summary{sessionvars%urlparameter}">summary</a> |
-<a href="{url}shortlog{sessionvars%urlparameter}">shortlog</a> |
-<a href="{url}log{sessionvars%urlparameter}">changelog</a> |
-<a href="{url}graph{sessionvars%urlparameter}">graph</a> |
-<a href="{url}tags{sessionvars%urlparameter}">tags</a> |
-<a href="{url}branches{sessionvars%urlparameter}">branches</a> |
-<a href="{url}file/{node|short}{path|urlescape}{sessionvars%urlparameter}">files</a> |
-<a href="{url}rev/{node|short}{sessionvars%urlparameter}">changeset</a> |
-<a href="{url}file/{node|short}/{file|urlescape}{sessionvars%urlparameter}">file</a> |
-<a href="{url}log/{node|short}/{file|urlescape}{sessionvars%urlparameter}">revisions</a> |
-<a href="{url}annotate/{node|short}/{file|urlescape}{sessionvars%urlparameter}">annotate</a> |
-diff |
-<a href="{url}raw-diff/{node|short}/{file|urlescape}">raw</a><br/>
-</div>
-
-<div class="title">{file|escape}</div>
-
-<table>
-{branch%filerevbranch}
-<tr>
- <td>changeset {rev}</td>
- <td style="font-family:monospace"><a class="list" href="{url}rev/{node|short}{sessionvars%urlparameter}">{node|short}</a></td></tr>
-{parent%filediffparent}
-{child%filediffchild}
-</table>
-
-<div class="list_head"></div>
-
-<div class="page_body">
-{diff}
-</div>
-
-{footer}
--- a/sys/src/cmd/hg/templates/gitweb/filelog.tmpl
+++ /dev/null
@@ -1,40 +1,0 @@
-{header}
-<title>{repo|escape}: File revisions</title>
-<link rel="alternate" type="application/atom+xml"
- href="{url}atom-log" title="Atom feed for {repo|escape}"/>
-<link rel="alternate" type="application/rss+xml"
- href="{url}rss-log" title="RSS feed for {repo|escape}"/>
-</head>
-<body>
-
-<div class="page_header">
-<a href="http://mercurial.selenic.com/" title="Mercurial" style="float: right;">Mercurial</a><a href="{url}summary{sessionvars%urlparameter}">{repo|escape}</a> / file revisions
-</div>
-
-<div class="page_nav">
-<a href="{url}summary{sessionvars%urlparameter}">summary</a> |
-<a href="{url}shortlog{sessionvars%urlparameter}">shortlog</a> |
-<a href="{url}log{sessionvars%urlparameter}">changelog</a> |
-<a href="{url}graph{sessionvars%urlparameter}">graph</a> |
-<a href="{url}tags{sessionvars%urlparameter}">tags</a> |
-<a href="{url}branches{sessionvars%urlparameter}">branches</a> |
-<a href="{url}file/{node|short}/{file|urlescape}{sessionvars%urlparameter}">file</a> |
-revisions |
-<a href="{url}annotate/{node|short}/{file|urlescape}{sessionvars%urlparameter}">annotate</a> |
-<a href="{url}diff/{node|short}/{file|urlescape}{sessionvars%urlparameter}">diff</a> |
-<a href="{url}rss-log/{node|short}/{file|urlescape}">rss</a>
-<br/>
-{nav%filenaventry}
-</div>
-
-<div class="title" >{file|urlescape}</div>
-
-<table>
-{entries%filelogentry}
-</table>
-
-<div class="page_nav">
-{nav%filenaventry}
-</div>
-
-{footer}
--- a/sys/src/cmd/hg/templates/gitweb/filerevision.tmpl
+++ /dev/null
@@ -1,60 +1,0 @@
-{header}
-<title>{repo|escape}: {file|escape}@{node|short}</title>
-<link rel="alternate" type="application/atom+xml"
- href="{url}atom-log" title="Atom feed for {repo|escape}"/>
-<link rel="alternate" type="application/rss+xml"
- href="{url}rss-log" title="RSS feed for {repo|escape}"/>
-</head>
-<body>
-
-<div class="page_header">
-<a href="http://mercurial.selenic.com/" title="Mercurial" style="float: right;">Mercurial</a><a href="{url}summary{sessionvars%urlparameter}">{repo|escape}</a> / file revision
-</div>
-
-<div class="page_nav">
-<a href="{url}summary{sessionvars%urlparameter}">summary</a> |
-<a href="{url}shortlog{sessionvars%urlparameter}">shortlog</a> |
-<a href="{url}log{sessionvars%urlparameter}">changelog</a> |
-<a href="{url}graph{sessionvars%urlparameter}">graph</a> |
-<a href="{url}tags{sessionvars%urlparameter}">tags</a> |
-<a href="{url}branches{sessionvars%urlparameter}">branches</a> |
-<a href="{url}file/{node|short}{path|urlescape}{sessionvars%urlparameter}">files</a> |
-<a href="{url}rev/{node|short}{sessionvars%urlparameter}">changeset</a> |
-file |
-<a href="{url}log/{node|short}/{file|urlescape}{sessionvars%urlparameter}">revisions</a> |
-<a href="{url}annotate/{node|short}/{file|urlescape}{sessionvars%urlparameter}">annotate</a> |
-<a href="{url}diff/{node|short}/{file|urlescape}{sessionvars%urlparameter}">diff</a> |
-<a href="{url}raw-file/{node|short}/{file|urlescape}">raw</a><br/>
-</div>
-
-<div class="title">{file|escape}</div>
-
-<div class="title_text">
-<table cellspacing="0">
-<tr>
- <td>author</td>
- <td>{author|obfuscate}</td></tr>
-<tr>
- <td></td>
- <td>{date|date} ({date|age} ago)</td></tr>
-{branch%filerevbranch}
-<tr>
- <td>changeset {rev}</td>
- <td style="font-family:monospace"><a class="list" href="{url}rev/{node|short}{sessionvars%urlparameter}">{node|short}</a></td></tr>
-{parent%filerevparent}
-{child%filerevchild}
-<tr>
- <td>permissions</td>
- <td style="font-family:monospace">{permissions|permissions}</td></tr>
-</table>
-</div>
-
-<div class="page_path">
-{desc|strip|escape|addbreaks|nonempty}
-</div>
-
-<div class="page_body">
-{text%fileline}
-</div>
-
-{footer}
--- a/sys/src/cmd/hg/templates/gitweb/footer.tmpl
+++ /dev/null
@@ -1,11 +1,0 @@
-<div class="page_footer">
-<div class="page_footer_text">{repo|escape}</div>
-<div class="rss_logo">
-<a href="{url}rss-log">RSS</a>
-<a href="{url}atom-log">Atom</a>
-</div>
-<br />
-{motd}
-</div>
-</body>
-</html>
--- a/sys/src/cmd/hg/templates/gitweb/graph.tmpl
+++ /dev/null
@@ -1,121 +1,0 @@
-{header}
-<title>{repo|escape}: Graph</title>
-<link rel="alternate" type="application/atom+xml"
- href="{url}atom-log" title="Atom feed for {repo|escape}"/>
-<link rel="alternate" type="application/rss+xml"
- href="{url}rss-log" title="RSS feed for {repo|escape}"/>
-<!--[if IE]><script type="text/javascript" src="{staticurl}excanvas.js"></script><![endif]-->
-</head>
-<body>
-
-<div class="page_header">
-<a href="http://mercurial.selenic.com/" title="Mercurial" style="float: right;">Mercurial</a><a href="{url}summary{sessionvars%urlparameter}">{repo|escape}</a> / graph
-</div>
-
-<form action="{url}log">
-{sessionvars%hiddenformentry}
-<div class="search">
-<input type="text" name="rev" />
-</div>
-</form>
-<div class="page_nav">
-<a href="{url}summary{sessionvars%urlparameter}">summary</a> |
-<a href="{url}shortlog{sessionvars%urlparameter}">shortlog</a> |
-<a href="{url}log/{rev}{sessionvars%urlparameter}">changelog</a> |
-graph |
-<a href="{url}tags{sessionvars%urlparameter}">tags</a> |
-<a href="{url}branches{sessionvars%urlparameter}">branches</a> |
-<a href="{url}file/{node|short}{sessionvars%urlparameter}">files</a>
-<br/>
-<a href="{url}graph/{rev}{lessvars%urlparameter}">less</a>
-<a href="{url}graph/{rev}{morevars%urlparameter}">more</a>
-| {changenav%navgraphentry}<br/>
-</div>
-
-<div class="title"> </div>
-
-<noscript>The revision graph only works with JavaScript-enabled browsers.</noscript>
-
-<div id="wrapper">
-<ul id="nodebgs"></ul>
-<canvas id="graph" width="224" height="{canvasheight}"></canvas>
-<ul id="graphnodes"></ul>
-</div>
-
-<script type="text/javascript" src="{staticurl}graph.js"></script>
-<script>
-<!-- hide script content
-
-var data = {jsdata|json};
-var graph = new Graph();
-graph.scale({bg_height});
-
-graph.edge = function(x0, y0, x1, y1, color) {
-
- this.setColor(color, 0.0, 0.65);
- this.ctx.beginPath();
- this.ctx.moveTo(x0, y0);
- this.ctx.lineTo(x1, y1);
- this.ctx.stroke();
-
-}
-
-var revlink = '<li style="_STYLE"><span class="desc">';
-revlink += '<a class="list" href="{url}rev/_NODEID{sessionvars%urlparameter}" title="_NODEID"><b>_DESC</b></a>';
-revlink += '</span> _TAGS';
-revlink += '<span class="info">_DATE ago, by _USER</span></li>';
-
-graph.vertex = function(x, y, color, parity, cur) {
-
- this.ctx.beginPath();
- color = this.setColor(color, 0.25, 0.75);
- this.ctx.arc(x, y, radius, 0, Math.PI * 2, true);
- this.ctx.fill();
-
- var bg = '<li class="bg parity' + parity + '"></li>';
- var left = (this.columns + 1) * this.bg_height;
- var nstyle = 'padding-left: ' + left + 'px;';
- var item = revlink.replace(/_STYLE/, nstyle);
- item = item.replace(/_PARITY/, 'parity' + parity);
- item = item.replace(/_NODEID/, cur[0]);
- item = item.replace(/_NODEID/, cur[0]);
- item = item.replace(/_DESC/, cur[3]);
- item = item.replace(/_USER/, cur[4]);
- item = item.replace(/_DATE/, cur[5]);
-
- var tagspan = '';
- if (cur[7].length || (cur[6][0] != 'default' || cur[6][1])) {
- tagspan = '<span class="logtags">';
- if (cur[6][1]) {
- tagspan += '<span class="branchtag" title="' + cur[6][0] + '">';
- tagspan += cur[6][0] + '</span> ';
- } else if (!cur[6][1] && cur[6][0] != 'default') {
- tagspan += '<span class="inbranchtag" title="' + cur[6][0] + '">';
- tagspan += cur[6][0] + '</span> ';
- }
- if (cur[7].length) {
- for (var t in cur[7]) {
- var tag = cur[7][t];
- tagspan += '<span class="tagtag">' + tag + '</span> ';
- }
- }
- tagspan += '</span>';
- }
-
- item = item.replace(/_TAGS/, tagspan);
- return [bg, item];
-
-}
-
-graph.render(data);
-
-// stop hiding script -->
-</script>
-
-<div class="page_nav">
-<a href="{url}graph/{rev}{lessvars%urlparameter}">less</a>
-<a href="{url}graph/{rev}{morevars%urlparameter}">more</a>
-| {changenav%navgraphentry}
-</div>
-
-{footer}
--- a/sys/src/cmd/hg/templates/gitweb/header.tmpl
+++ /dev/null
@@ -1,8 +1,0 @@
-<?xml version="1.0" encoding="{encoding}"?>
-<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
-<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en-US" lang="en-US">
-<head>
-<link rel="icon" href="{staticurl}hgicon.png" type="image/png" />
-<meta name="robots" content="index, nofollow"/>
-<link rel="stylesheet" href="{staticurl}style-gitweb.css" type="text/css" />
-
--- a/sys/src/cmd/hg/templates/gitweb/index.tmpl
+++ /dev/null
@@ -1,26 +1,0 @@
-{header}
-<title>Mercurial repositories index</title>
-</head>
-<body>
-
-<div class="page_header">
- <a href="http://mercurial.selenic.com/" title="Mercurial" style="float: right;">Mercurial</a>
- Repositories list
-</div>
-
-<table cellspacing="0">
- <tr>
- <td><a href="?sort={sort_name}">Name</a></td>
- <td><a href="?sort={sort_description}">Description</a></td>
- <td><a href="?sort={sort_contact}">Contact</a></td>
- <td><a href="?sort={sort_lastchange}">Last change</a></td>
- <td> </td>
- <td> </td>
- </tr>
- {entries%indexentry}
-</table>
-<div class="page_footer">
-{motd}
-</div>
-</body>
-</html>
--- a/sys/src/cmd/hg/templates/gitweb/manifest.tmpl
+++ /dev/null
@@ -1,38 +1,0 @@
-{header}
-<title>{repo|escape}: files</title>
-<link rel="alternate" type="application/atom+xml"
- href="{url}atom-log" title="Atom feed for {repo|escape}"/>
-<link rel="alternate" type="application/rss+xml"
- href="{url}rss-log" title="RSS feed for {repo|escape}"/>
-</head>
-<body>
-
-<div class="page_header">
-<a href="http://mercurial.selenic.com/" title="Mercurial" style="float: right;">Mercurial</a><a href="{url}summary{sessionvars%urlparameter}">{repo|escape}</a> / files
-</div>
-
-<div class="page_nav">
-<a href="{url}summary{sessionvars%urlparameter}">summary</a> |
-<a href="{url}shortlog{sessionvars%urlparameter}">shortlog</a> |
-<a href="{url}log{sessionvars%urlparameter}">changelog</a> |
-<a href="{url}graph{sessionvars%urlparameter}">graph</a> |
-<a href="{url}tags{sessionvars%urlparameter}">tags</a> |
-<a href="{url}branches{sessionvars%urlparameter}">branches</a> |
-files |
-<a href="{url}rev/{node|short}{sessionvars%urlparameter}">changeset</a> {archives%archiveentry}<br/>
-</div>
-
-<div class="title">{path|escape} <span class="logtags">{inbranch%inbranchtag}{branches%branchtag}{tags%tagtag}</span></div>
-<table cellspacing="0">
-<tr class="parity{upparity}">
-<td style="font-family:monospace">drwxr-xr-x</td>
-<td style="font-family:monospace"></td>
-<td style="font-family:monospace"></td>
-<td><a href="{url}file/{node|short}{up|urlescape}{sessionvars%urlparameter}">[up]</a></td>
-<td class="link"> </td>
-</tr>
-{dentries%direntry}
-{fentries%fileentry}
-</table>
-
-{footer}
--- a/sys/src/cmd/hg/templates/gitweb/map
+++ /dev/null
@@ -1,248 +1,0 @@
-default = 'summary'
-mimetype = 'text/html; charset={encoding}'
-header = header.tmpl
-footer = footer.tmpl
-search = search.tmpl
-changelog = changelog.tmpl
-summary = summary.tmpl
-error = error.tmpl
-notfound = notfound.tmpl
-naventry = '<a href="{url}log/{node|short}{sessionvars%urlparameter}">{label|escape}</a> '
-navshortentry = '<a href="{url}shortlog/{node|short}{sessionvars%urlparameter}">{label|escape}</a> '
-navgraphentry = '<a href="{url}graph/{node|short}{sessionvars%urlparameter}">{label|escape}</a> '
-filenaventry = '<a href="{url}log/{node|short}/{file|urlescape}{sessionvars%urlparameter}">{label|escape}</a> '
-filedifflink = '<a href="{url}diff/{node|short}/{file|urlescape}{sessionvars%urlparameter}">{file|escape}</a> '
-filenodelink = '
- <tr class="parity{parity}">
- <td><a class="list" href="{url}diff/{node|short}/{file|urlescape}{sessionvars%urlparameter}">{file|escape}</a></td>
- <td></td>
- <td class="link">
- <a href="{url}file/{node|short}/{file|urlescape}{sessionvars%urlparameter}">file</a> |
- <a href="{url}annotate/{node|short}/{file|urlescape}{sessionvars%urlparameter}">annotate</a> |
- <a href="{url}diff/{node|short}/{file|urlescape}{sessionvars%urlparameter}">diff</a> |
- <a href="{url}log/{node|short}/{file|urlescape}{sessionvars%urlparameter}">revisions</a>
- </td>
- </tr>'
-filenolink = '
- <tr class="parity{parity}">
- <td><a class="list" href="{url}diff/{node|short}/{file|urlescape}{sessionvars%urlparameter}">{file|escape}</a></td>
- <td></td>
- <td class="link">
- file |
- annotate |
- <a href="{url}diff/{node|short}/{file|urlescape}{sessionvars%urlparameter}">diff</a> |
- <a href="{url}log/{node|short}/{file|urlescape}{sessionvars%urlparameter}">revisions</a>
- </td>
- </tr>'
-fileellipses = '...'
-changelogentry = changelogentry.tmpl
-searchentry = changelogentry.tmpl
-changeset = changeset.tmpl
-manifest = manifest.tmpl
-direntry = '
- <tr class="parity{parity}">
- <td style="font-family:monospace">drwxr-xr-x</td>
- <td style="font-family:monospace"></td>
- <td style="font-family:monospace"></td>
- <td>
- <a href="{url}file/{node|short}{path|urlescape}{sessionvars%urlparameter}">{basename|escape}</a>
- <a href="{url}file/{node|short}{path|urlescape}/{emptydirs|urlescape}{sessionvars%urlparameter}">{emptydirs|escape}</a>
- </td>
- <td class="link">
- <a href="{url}file/{node|short}{path|urlescape}{sessionvars%urlparameter}">files</a>
- </td>
- </tr>'
-fileentry = '
- <tr class="parity{parity}">
- <td style="font-family:monospace">{permissions|permissions}</td>
- <td style="font-family:monospace" align=right>{date|isodate}</td>
- <td style="font-family:monospace" align=right>{size}</td>
- <td class="list">
- <a class="list" href="{url}file/{node|short}/{file|urlescape}{sessionvars%urlparameter}">{basename|escape}</a>
- </td>
- <td class="link">
- <a href="{url}file/{node|short}/{file|urlescape}{sessionvars%urlparameter}">file</a> |
- <a href="{url}log/{node|short}/{file|urlescape}{sessionvars%urlparameter}">revisions</a> |
- <a href="{url}annotate/{node|short}/{file|urlescape}{sessionvars%urlparameter}">annotate</a>
- </td>
- </tr>'
-filerevision = filerevision.tmpl
-fileannotate = fileannotate.tmpl
-filediff = filediff.tmpl
-filelog = filelog.tmpl
-fileline = '
- <div style="font-family:monospace" class="parity{parity}">
- <pre><a class="linenr" href="#{lineid}" id="{lineid}">{linenumber}</a> {line|escape}</pre>
- </div>'
-annotateline = '
- <tr style="font-family:monospace" class="parity{parity}">
- <td class="linenr" style="text-align: right;">
- <a href="{url}annotate/{node|short}/{file|urlescape}{sessionvars%urlparameter}#l{targetline}"
- title="{node|short}: {desc|escape|firstline}">{author|user}@{rev}</a>
- </td>
- <td><pre><a class="linenr" href="#{lineid}" id="{lineid}">{linenumber}</a></pre></td>
- <td><pre>{line|escape}</pre></td>
- </tr>'
-difflineplus = '<span style="color:#008800;"><a class="linenr" href="#{lineid}" id="{lineid}">{linenumber}</a> {line|escape}</span>'
-difflineminus = '<span style="color:#cc0000;"><a class="linenr" href="#{lineid}" id="{lineid}">{linenumber}</a> {line|escape}</span>'
-difflineat = '<span style="color:#990099;"><a class="linenr" href="#{lineid}" id="{lineid}">{linenumber}</a> {line|escape}</span>'
-diffline = '<span><a class="linenr" href="#{lineid}" id="{lineid}">{linenumber}</a> {line|escape}</span>'
-changelogparent = '
- <tr>
- <th class="parent">parent {rev}:</th>
- <td class="parent">
- <a href="{url}rev/{node|short}{sessionvars%urlparameter}">{node|short}</a>
- </td>
- </tr>'
-changesetbranch = '<tr><td>branch</td><td>{name}</td></tr>'
-changesetparent = '
- <tr>
- <td>parent {rev}</td>
- <td style="font-family:monospace">
- <a class="list" href="{url}rev/{node|short}{sessionvars%urlparameter}">{node|short}</a>
- </td>
- </tr>'
-filerevbranch = '<tr><td>branch</td><td>{name}</td></tr>'
-filerevparent = '
- <tr>
- <td>parent {rev}</td>
- <td style="font-family:monospace">
- <a class="list" href="{url}file/{node|short}/{file|urlescape}{sessionvars%urlparameter}">
- {rename%filerename}{node|short}
- </a>
- </td>
- </tr>'
-filerename = '{file|escape}@'
-filelogrename = '| <a href="{url}file/{node|short}/{file|urlescape}{sessionvars%urlparameter}">base</a>'
-fileannotateparent = '
- <tr>
- <td>parent {rev}</td>
- <td style="font-family:monospace">
- <a class="list" href="{url}annotate/{node|short}/{file|urlescape}{sessionvars%urlparameter}">
- {rename%filerename}{node|short}
- </a>
- </td>
- </tr>'
-changelogchild = '
- <tr>
- <th class="child">child {rev}:</th>
- <td class="child"><a href="{url}rev/{node|short}{sessionvars%urlparameter}">{node|short}</a></td>
- </tr>'
-changesetchild = '
- <tr>
- <td>child {rev}</td>
- <td style="font-family:monospace">
- <a class="list" href="{url}rev/{node|short}{sessionvars%urlparameter}">{node|short}</a>
- </td>
- </tr>'
-filerevchild = '
- <tr>
- <td>child {rev}</td>
- <td style="font-family:monospace">
- <a class="list" href="{url}file/{node|short}/{file|urlescape}{sessionvars%urlparameter}">{node|short}</a></td>
- </tr>'
-fileannotatechild = '
- <tr>
- <td>child {rev}</td>
- <td style="font-family:monospace">
- <a class="list" href="{url}annotate/{node|short}/{file|urlescape}{sessionvars%urlparameter}">{node|short}</a></td>
- </tr>'
-tags = tags.tmpl
-tagentry = '
- <tr class="parity{parity}">
- <td class="age"><i>{date|age} ago</i></td>
- <td><a class="list" href="{url}rev/{node|short}{sessionvars%urlparameter}"><b>{tag|escape}</b></a></td>
- <td class="link">
- <a href="{url}rev/{node|short}{sessionvars%urlparameter}">changeset</a> |
- <a href="{url}log/{node|short}{sessionvars%urlparameter}">changelog</a> |
- <a href="{url}file/{node|short}{sessionvars%urlparameter}">files</a>
- </td>
- </tr>'
-branches = branches.tmpl
-branchentry = '
- <tr class="parity{parity}">
- <td class="age"><i>{date|age} ago</i></td>
- <td><a class="list" href="{url}shortlog/{node|short}{sessionvars%urlparameter}"><b>{node|short}</b></a></td>
- <td class="{status}">{branch|escape}</td>
- <td class="link">
- <a href="{url}changeset/{node|short}{sessionvars%urlparameter}">changeset</a> |
- <a href="{url}log/{node|short}{sessionvars%urlparameter}">changelog</a> |
- <a href="{url}file/{node|short}{sessionvars%urlparameter}">files</a>
- </td>
- </tr>'
-diffblock = '<pre>{lines}</pre>'
-filediffparent = '
- <tr>
- <td>parent {rev}</td>
- <td style="font-family:monospace">
- <a class="list" href="{url}diff/{node|short}/{file|urlescape}{sessionvars%urlparameter}">
- {node|short}
- </a>
- </td>
- </tr>'
-filelogparent = '
- <tr>
- <td align="right">parent {rev}: </td>
- <td><a href="{url}file/{node|short}/{file|urlescape}{sessionvars%urlparameter}">{node|short}</a></td>
- </tr>'
-filediffchild = '
- <tr>
- <td>child {rev}</td>
- <td style="font-family:monospace">
- <a class="list" href="{url}diff/{node|short}/{file|urlescape}{sessionvars%urlparameter}">{node|short}</a>
- </td>
- </tr>'
-filelogchild = '
- <tr>
- <td align="right">child {rev}: </td>
- <td><a href="{url}file{node|short}/{file|urlescape}{sessionvars%urlparameter}">{node|short}</a></td>
- </tr>'
-shortlog = shortlog.tmpl
-graph = graph.tmpl
-tagtag = '<span class="tagtag" title="{name}">{name}</span> '
-branchtag = '<span class="branchtag" title="{name}">{name}</span> '
-inbranchtag = '<span class="inbranchtag" title="{name}">{name}</span> '
-shortlogentry = '
- <tr class="parity{parity}">
- <td class="age"><i>{date|age} ago</i></td>
- <td><i>{author|person}</i></td>
- <td>
- <a class="list" href="{url}rev/{node|short}{sessionvars%urlparameter}">
- <b>{desc|strip|firstline|escape|nonempty}</b>
- <span class="logtags">{inbranch%inbranchtag}{branches%branchtag}{tags%tagtag}</span>
- </a>
- </td>
- <td class="link" nowrap>
- <a href="{url}rev/{node|short}{sessionvars%urlparameter}">changeset</a> |
- <a href="{url}file/{node|short}{sessionvars%urlparameter}">files</a>
- </td>
- </tr>'
-filelogentry = '
- <tr class="parity{parity}">
- <td class="age"><i>{date|age} ago</i></td>
- <td>
- <a class="list" href="{url}rev/{node|short}{sessionvars%urlparameter}">
- <b>{desc|strip|firstline|escape|nonempty}</b>
- </a>
- </td>
- <td class="link">
- <a href="{url}file/{node|short}/{file|urlescape}{sessionvars%urlparameter}">file</a> | <a href="{url}diff/{node|short}/{file|urlescape}{sessionvars%urlparameter}">diff</a> | <a href="{url}annotate/{node|short}/{file|urlescape}{sessionvars%urlparameter}">annotate</a> {rename%filelogrename}</td>
- </tr>'
-archiveentry = ' | <a href="{url}archive/{node|short}{extension}">{type|escape}</a> '
-indexentry = '
- <tr class="parity{parity}">
- <td>
- <a class="list" href="{url}{sessionvars%urlparameter}">
- <b>{name|escape}</b>
- </a>
- </td>
- <td>{description}</td>
- <td>{contact|obfuscate}</td>
- <td class="age">{lastchange|age} ago</td>
- <td class="indexlinks">{archives%indexarchiveentry}</td>
- <td><div class="rss_logo"><a href="{url}rss-log">RSS</a> <a href="{url}atom-log">Atom</a></div></td>
- </tr>\n'
-indexarchiveentry = ' <a href="{url}archive/{node|short}{extension}">{type|escape}</a> '
-index = index.tmpl
-urlparameter = '{separator}{name}={value|urlescape}'
-hiddenformentry = '<input type="hidden" name="{name}" value="{value|escape}" />'
--- a/sys/src/cmd/hg/templates/gitweb/notfound.tmpl
+++ /dev/null
@@ -1,18 +1,0 @@
-{header}
-<title>Mercurial repository not found</title>
-</head>
-
-<body>
-
-<div class="page_header">
-<a href="http://mercurial.selenic.com/" title="Mercurial" style="float: right;">Mercurial</a> Not found: {repo|escape}
-</div>
-
-<div class="page_body">
-The specified repository "{repo|escape}" is unknown, sorry.
-<br/>
-<br/>
-Please go back to the <a href="{url}">main repository list page</a>.
-</div>
-
-{footer}
--- a/sys/src/cmd/hg/templates/gitweb/search.tmpl
+++ /dev/null
@@ -1,36 +1,0 @@
-{header}
-<title>{repo|escape}: Search</title>
-<link rel="alternate" type="application/atom+xml"
- href="{url}atom-log" title="Atom feed for {repo|escape}"/>
-<link rel="alternate" type="application/rss+xml"
- href="{url}rss-log" title="RSS feed for {repo|escape}"/>
-</head>
-<body>
-
-<div class="page_header">
-<a href="http://mercurial.selenic.com/" title="Mercurial" style="float: right;">Mercurial</a><a href="{url}summary{sessionvars%urlparameter}">{repo|escape}</a> / search
-
-<form action="{url}log">
-{sessionvars%hiddenformentry}
-<div class="search">
-<input type="text" name="rev" value="{query|escape}" />
-</div>
-</form>
-</div>
-
-<div class="page_nav">
-<a href="{url}summary{sessionvars%urlparameter}">summary</a> |
-<a href="{url}shortlog{sessionvars%urlparameter}">shortlog</a> |
-<a href="{url}log{sessionvars%urlparameter}">changelog</a> |
-<a href="{url}graph{sessionvars%urlparameter}">graph</a> |
-<a href="{url}tags{sessionvars%urlparameter}">tags</a> |
-<a href="{url}branches{sessionvars%urlparameter}">branches</a> |
-<a href="{url}file/{node|short}{sessionvars%urlparameter}">files</a>{archives%archiveentry}
-<br/>
-</div>
-
-<div class="title">searching for {query|escape}</div>
-
-{entries}
-
-{footer}
--- a/sys/src/cmd/hg/templates/gitweb/shortlog.tmpl
+++ /dev/null
@@ -1,41 +1,0 @@
-{header}
-<title>{repo|escape}: Shortlog</title>
-<link rel="alternate" type="application/atom+xml"
- href="{url}atom-log" title="Atom feed for {repo|escape}"/>
-<link rel="alternate" type="application/rss+xml"
- href="{url}rss-log" title="RSS feed for {repo|escape}"/>
-</head>
-<body>
-
-<div class="page_header">
-<a href="http://mercurial.selenic.com/" title="Mercurial" style="float: right;">Mercurial</a><a href="{url}summary{sessionvars%urlparameter}">{repo|escape}</a> / shortlog
-</div>
-
-<form action="{url}log">
-{sessionvars%hiddenformentry}
-<div class="search">
-<input type="text" name="rev" />
-</div>
-</form>
-<div class="page_nav">
-<a href="{url}summary{sessionvars%urlparameter}">summary</a> |
-shortlog |
-<a href="{url}log/{rev}{sessionvars%urlparameter}">changelog</a> |
-<a href="{url}graph{sessionvars%urlparameter}">graph</a> |
-<a href="{url}tags{sessionvars%urlparameter}">tags</a> |
-<a href="{url}branches{sessionvars%urlparameter}">branches</a> |
-<a href="{url}file/{node|short}{sessionvars%urlparameter}">files</a>{archives%archiveentry}
-<br/>
-{changenav%navshortentry}<br/>
-</div>
-
-<div class="title"> </div>
-<table cellspacing="0">
-{entries%shortlogentry}
-</table>
-
-<div class="page_nav">
-{changenav%navshortentry}
-</div>
-
-{footer}
--- a/sys/src/cmd/hg/templates/gitweb/summary.tmpl
+++ /dev/null
@@ -1,58 +1,0 @@
-{header}
-<title>{repo|escape}: Summary</title>
-<link rel="alternate" type="application/atom+xml"
- href="{url}atom-log" title="Atom feed for {repo|escape}"/>
-<link rel="alternate" type="application/rss+xml"
- href="{url}rss-log" title="RSS feed for {repo|escape}"/>
-</head>
-<body>
-
-<div class="page_header">
-<a href="http://mercurial.selenic.com/" title="Mercurial" style="float: right;">Mercurial</a><a href="{url}summary{sessionvars%urlparameter}">{repo|escape}</a> / summary
-
-<form action="{url}log">
-{sessionvars%hiddenformentry}
-<div class="search">
-<input type="text" name="rev" />
-</div>
-</form>
-</div>
-
-<div class="page_nav">
-summary |
-<a href="{url}shortlog{sessionvars%urlparameter}">shortlog</a> |
-<a href="{url}log{sessionvars%urlparameter}">changelog</a> |
-<a href="{url}graph{sessionvars%urlparameter}">graph</a> |
-<a href="{url}tags{sessionvars%urlparameter}">tags</a> |
-<a href="{url}branches{sessionvars%urlparameter}">branches</a> |
-<a href="{url}file/{node|short}{sessionvars%urlparameter}">files</a>{archives%archiveentry}
-<br/>
-</div>
-
-<div class="title"> </div>
-<table cellspacing="0">
-<tr><td>description</td><td>{desc}</td></tr>
-<tr><td>owner</td><td>{owner|obfuscate}</td></tr>
-<tr><td>last change</td><td>{lastchange|rfc822date}</td></tr>
-</table>
-
-<div><a class="title" href="{url}shortlog{sessionvars%urlparameter}">changes</a></div>
-<table cellspacing="0">
-{shortlog}
-<tr class="light"><td colspan="4"><a class="list" href="{url}shortlog{sessionvars%urlparameter}">...</a></td></tr>
-</table>
-
-<div><a class="title" href="{url}tags{sessionvars%urlparameter}">tags</a></div>
-<table cellspacing="0">
-{tags}
-<tr class="light"><td colspan="3"><a class="list" href="{url}tags{sessionvars%urlparameter}">...</a></td></tr>
-</table>
-
-<div><a class="title" href="#">branches</a></div>
-<table cellspacing="0">
-{branches%branchentry}
-<tr class="light">
- <td colspan="4"><a class="list" href="#">...</a></td>
-</tr>
-</table>
-{footer}
--- a/sys/src/cmd/hg/templates/gitweb/tags.tmpl
+++ /dev/null
@@ -1,30 +1,0 @@
-{header}
-<title>{repo|escape}: Tags</title>
-<link rel="alternate" type="application/atom+xml"
- href="{url}atom-tags" title="Atom feed for {repo|escape}"/>
-<link rel="alternate" type="application/rss+xml"
- href="{url}rss-tags" title="RSS feed for {repo|escape}"/>
-</head>
-<body>
-
-<div class="page_header">
-<a href="http://mercurial.selenic.com/" title="Mercurial" style="float: right;">Mercurial</a><a href="{url}summary{sessionvars%urlparameter}">{repo|escape}</a> / tags
-</div>
-
-<div class="page_nav">
-<a href="{url}summary{sessionvars%urlparameter}">summary</a> |
-<a href="{url}shortlog{sessionvars%urlparameter}">shortlog</a> |
-<a href="{url}log{sessionvars%urlparameter}">changelog</a> |
-<a href="{url}graph{sessionvars%urlparameter}">graph</a> |
-tags |
-<a href="{url}branches{sessionvars%urlparameter}">branches</a> |
-<a href="{url}file/{node|short}{sessionvars%urlparameter}">files</a>
-<br/>
-</div>
-
-<div class="title"> </div>
-<table cellspacing="0">
-{entries%tagentry}
-</table>
-
-{footer}
--- a/sys/src/cmd/hg/templates/monoblue/branches.tmpl
+++ /dev/null
@@ -1,36 +1,0 @@
-{header}
- <title>{repo|escape}: Branches</title>
- <link rel="alternate" type="application/atom+xml" href="{url}atom-log" title="Atom feed for {repo|escape}"/>
- <link rel="alternate" type="application/rss+xml" href="{url}rss-log" title="RSS feed for {repo|escape}"/>
-</head>
-
-<body>
-<div id="container">
- <div class="page-header">
- <h1><a href="{url}summary{sessionvars%urlparameter}">{repo|escape}</a> / Branches</h1>
-
- <form action="{url}log">
- {sessionvars%hiddenformentry}
- <dl class="search">
- <dt><label>Search: </label></dt>
- <dd><input type="text" name="rev" /></dd>
- </dl>
- </form>
-
- <ul class="page-nav">
- <li><a href="{url}summary{sessionvars%urlparameter}">summary</a></li>
- <li><a href="{url}shortlog{sessionvars%urlparameter}">shortlog</a></li>
- <li><a href="{url}changelog{sessionvars%urlparameter}">changelog</a></li>
- <li><a href="{url}graph/{node|short}{sessionvars%urlparameter}">graph</a></li>
- <li><a href="{url}tags{sessionvars%urlparameter}">tags</a></li>
- <li class="current">branches</li>
- <li><a href="{url}file/{node|short}{sessionvars%urlparameter}">files</a></li>
- </ul>
- </div>
-
- <h2 class="no-link no-border">tags</h2>
- <table cellspacing="0">
-{entries%branchentry}
- </table>
-
-{footer}
--- a/sys/src/cmd/hg/templates/monoblue/changelog.tmpl
+++ /dev/null
@@ -1,40 +1,0 @@
-{header}
- <title>{repo|escape}: changelog</title>
- <link rel="alternate" type="application/atom+xml" href="{url}atom-log" title="Atom feed for {repo|escape}"/>
- <link rel="alternate" type="application/rss+xml" href="{url}rss-log" title="RSS feed for {repo|escape}"/>
-</head>
-
-<body>
-<div id="container">
- <div class="page-header">
- <h1><a href="{url}summary{sessionvars%urlparameter}">{repo|escape}</a> / changelog</h1>
-
- <form action="{url}log">
- {sessionvars%hiddenformentry}
- <dl class="search">
- <dt><label>Search: </label></dt>
- <dd><input type="text" name="rev" /></dd>
- </dl>
- </form>
-
- <ul class="page-nav">
- <li><a href="{url}summary{sessionvars%urlparameter}">summary</a></li>
- <li><a href="{url}shortlog{sessionvars%urlparameter}">shortlog</a></li>
- <li class="current">changelog</li>
- <li><a href="{url}graph/{node|short}{sessionvars%urlparameter}">graph</a></li>
- <li><a href="{url}tags{sessionvars%urlparameter}">tags</a></li>
- <li><a href="{url}branches{sessionvars%urlparameter}">branches</a></li>
- <li><a href="{url}file/{node|short}{sessionvars%urlparameter}">files</a>{archives%archiveentry}</li>
- </ul>
- </div>
-
- <h2 class="no-link no-border">changelog</h2>
- <div>
- {entries%changelogentry}
- </div>
-
- <div class="page-path">
-{changenav%naventry}
- </div>
-
-{footer}
--- a/sys/src/cmd/hg/templates/monoblue/changelogentry.tmpl
+++ /dev/null
@@ -1,6 +1,0 @@
-<h3 class="changelog"><a class="title" href="{url}rev/{node|short}{sessionvars%urlparameter}">{desc|strip|firstline|escape|nonempty}<span class="logtags"> {inbranch%inbranchtag}{branches%branchtag}{tags%tagtag}</span></a></h3>
-<ul class="changelog-entry">
- <li class="age">{date|age} ago</li>
- <li>by <span class="name">{author|obfuscate}</span> <span class="revdate">[{date|rfc822date}] rev {rev}</span></li>
- <li class="description">{desc|strip|escape|addbreaks|nonempty}</li>
-</ul>
--- a/sys/src/cmd/hg/templates/monoblue/changeset.tmpl
+++ /dev/null
@@ -1,63 +1,0 @@
-{header}
-<title>{repo|escape}: changeset {rev}:{node|short}</title>
- <link rel="alternate" type="application/atom+xml" href="{url}atom-log" title="Atom feed for {repo|escape}"/>
- <link rel="alternate" type="application/rss+xml" href="{url}rss-log" title="RSS feed for {repo|escape}"/>
-</head>
-
-<body>
-<div id="container">
- <div class="page-header">
- <h1><a href="{url}summary{sessionvars%urlparameter}">{repo|escape}</a> / files</h1>
-
- <form action="{url}log">
- {sessionvars%hiddenformentry}
- <dl class="search">
- <dt><label>Search: </label></dt>
- <dd><input type="text" name="rev" /></dd>
- </dl>
- </form>
-
- <ul class="page-nav">
- <li><a href="{url}summary{sessionvars%urlparameter}">summary</a></li>
- <li><a href="{url}shortlog{sessionvars%urlparameter}">shortlog</a></li>
- <li><a href="{url}changelog{sessionvars%urlparameter}">changelog</a></li>
- <li><a href="{url}graph/{node|short}{sessionvars%urlparameter}">graph</a></li>
- <li><a href="{url}tags{sessionvars%urlparameter}">tags</a></li>
- <li><a href="{url}branches{sessionvars%urlparameter}">branches</a></li>
- <li><a href="{url}file/{node|short}{sessionvars%urlparameter}">files</a></li>
- </ul>
- </div>
-
- <ul class="submenu">
- <li class="current">changeset</li>
- <li><a href="{url}raw-rev/{node|short}">raw</a> {archives%archiveentry}</li>
- </ul>
-
- <h2 class="no-link no-border">changeset</h2>
-
- <h3 class="changeset"><a href="{url}raw-rev/{node|short}">{desc|strip|escape|firstline|nonempty} <span class="logtags">{inbranch%inbranchtag}{branches%branchtag}{tags%tagtag}</span></a></h3>
- <p class="changeset-age"><span>{date|age} ago</span></p>
-
- <dl class="overview">
- <dt>author</dt>
- <dd>{author|obfuscate}</dd>
- <dt>date</dt>
- <dd>{date|date}</dd>
- {branch%changesetbranch}
- <dt>changeset {rev}</dt>
- <dd>{node|short}</dd>
- {parent%changesetparent}
- {child%changesetchild}
- </dl>
-
- <p class="description">{desc|strip|escape|addbreaks|nonempty}</p>
-
- <table>
- {files}
- </table>
-
- <div class="diff">
- {diff}
- </div>
-
-{footer}
--- a/sys/src/cmd/hg/templates/monoblue/error.tmpl
+++ /dev/null
@@ -1,34 +1,0 @@
-{header}
- <title>{repo|escape}: Error</title>
- <link rel="alternate" type="application/atom+xml" href="{url}atom-log" title="Atom feed for {repo|escape}"/>
- <link rel="alternate" type="application/rss+xml" href="{url}rss-log" title="RSS feed for {repo|escape}"/>
-</head>
-
-<body>
-<div id="container">
- <div class="page-header">
- <h1><a href="{url}summary{sessionvars%urlparameter}">{repo|escape}</a> / Not found: {repo|escape}</h1>
-
- <form action="{url}log">
- {sessionvars%hiddenformentry}
- <dl class="search">
- <dt><label>Search: </label></dt>
- <dd><input type="text" name="rev" /></dd>
- </dl>
- </form>
-
- <ul class="page-nav">
- <li class="current">summary</li>
- <li><a href="{url}shortlog{sessionvars%urlparameter}">shortlog</a></li>
- <li><a href="{url}log{sessionvars%urlparameter}">changelog</a></li>
- <li><a href="{url}graph/{node|short}{sessionvars%urlparameter}">graph</a></li>
- <li><a href="{url}tags{sessionvars%urlparameter}">tags</a></li>
- <li><a href="{url}branches{sessionvars%urlparameter}">branches</a></li>
- <li><a href="{url}file/{node|short}{sessionvars%urlparameter}">files</a></li>
- </ul>
- </div>
-
- <h2 class="no-link no-border">An error occurred while processing your request</h2>
- <p class="normal">{error|escape}</p>
-
-{footer}
--- a/sys/src/cmd/hg/templates/monoblue/fileannotate.tmpl
+++ /dev/null
@@ -1,63 +1,0 @@
-{header}
-<title>{repo|escape}: {file|escape}@{node|short} (annotated)</title>
- <link rel="alternate" type="application/atom+xml" href="{url}atom-log" title="Atom feed for {repo|escape}"/>
- <link rel="alternate" type="application/rss+xml" href="{url}rss-log" title="RSS feed for {repo|escape}"/>
-</head>
-
-<body>
-<div id="container">
- <div class="page-header">
- <h1><a href="{url}summary{sessionvars%urlparameter}">{repo|escape}</a> / annotate</h1>
-
- <form action="{url}log">
- {sessionvars%hiddenformentry}
- <dl class="search">
- <dt><label>Search: </label></dt>
- <dd><input type="text" name="rev" /></dd>
- </dl>
- </form>
-
- <ul class="page-nav">
- <li><a href="{url}summary{sessionvars%urlparameter}">summary</a></li>
- <li><a href="{url}shortlog{sessionvars%urlparameter}">shortlog</a></li>
- <li><a href="{url}log{sessionvars%urlparameter}">changelog</a></li>
- <li><a href="{url}graph/{node|short}{sessionvars%urlparameter}">graph</a></li>
- <li><a href="{url}tags{sessionvars%urlparameter}">tags</a></li>
- <li><a href="{url}branches{sessionvars%urlparameter}">branches</a></li>
- <li><a href="{url}file/{node|short}{path|urlescape}{sessionvars%urlparameter}">files</a></li>
- </ul>
- </div>
-
- <ul class="submenu">
- <li><a href="{url}file/{node|short}/{file|urlescape}{sessionvars%urlparameter}">file</a></li>
- <li><a href="{url}log/{node|short}/{file|urlescape}{sessionvars%urlparameter}">revisions</a></li>
- <li class="current">annotate</li>
- <li><a href="{url}diff/{node|short}/{file|urlescape}{sessionvars%urlparameter}">diff</a></li>
- <li><a href="{url}raw-annotate/{node|short}/{file|urlescape}">raw</a></li>
- </ul>
-
- <h2 class="no-link no-border">{file|escape}@{node|short} (annotated)</h2>
- <h3 class="changeset">{file|escape}</h3>
- <p class="changeset-age"><span>{date|age} ago</span></p>
-
- <dl class="overview">
- <dt>author</dt>
- <dd>{author|obfuscate}</dd>
- <dt>date</dt>
- <dd>{date|date}</dd>
- {branch%filerevbranch}
- <dt>changeset {rev}</dt>
- <dd><a href="{url}rev/{node|short}{sessionvars%urlparameter}">{node|short}</a></dd>
- {parent%fileannotateparent}
- {child%fileannotatechild}
- <dt>permissions</dt>
- <dd>{permissions|permissions}</dd>
- </dl>
-
- <p class="description">{desc|strip|escape|addbreaks|nonempty}</p>
-
- <table class="annotated">
- {annotate%annotateline}
- </table>
-
-{footer}
--- a/sys/src/cmd/hg/templates/monoblue/filediff.tmpl
+++ /dev/null
@@ -1,54 +1,0 @@
-{header}
-<title>{repo|escape}: diff {file|escape}</title>
- <link rel="alternate" type="application/atom+xml" href="{url}atom-log" title="Atom feed for {repo|escape}"/>
- <link rel="alternate" type="application/rss+xml" href="{url}rss-log" title="RSS feed for {repo|escape}"/>
-</head>
-
-<body>
-<div id="container">
- <div class="page-header">
- <h1><a href="{url}summary{sessionvars%urlparameter}">{repo|escape}</a> / file diff</h1>
-
- <form action="{url}log">
- {sessionvars%hiddenformentry}
- <dl class="search">
- <dt><label>Search: </label></dt>
- <dd><input type="text" name="rev" /></dd>
- </dl>
- </form>
-
- <ul class="page-nav">
- <li><a href="{url}summary{sessionvars%urlparameter}">summary</a></li>
- <li><a href="{url}shortlog{sessionvars%urlparameter}">shortlog</a></li>
- <li><a href="{url}log{sessionvars%urlparameter}">changelog</a></li>
- <li><a href="{url}graph/{node|short}{sessionvars%urlparameter}">graph</a></li>
- <li><a href="{url}tags{sessionvars%urlparameter}">tags</a></li>
- <li><a href="{url}branches{sessionvars%urlparameter}">branches</a></li>
- <li><a href="{url}file/{node|short}{path|urlescape}{sessionvars%urlparameter}">files</a></li>
- </ul>
- </div>
-
- <ul class="submenu">
- <li><a href="{url}file/{node|short}/{file|urlescape}{sessionvars%urlparameter}">file</a></li>
- <li><a href="{url}log/{node|short}/{file|urlescape}{sessionvars%urlparameter}">revisions</a></li>
- <li><a href="{url}annotate/{node|short}/{file|urlescape}{sessionvars%urlparameter}">annotate</a></li>
- <li class="current">diff</li>
- <li><a href="{url}raw-diff/{node|short}/{file|urlescape}">raw</a></li>
- </ul>
-
- <h2 class="no-link no-border">diff: {file|escape}</h2>
- <h3 class="changeset">{file|escape}</h3>
-
- <dl class="overview">
- {branch%filerevbranch}
- <dt>changeset {rev}</dt>
- <dd><a href="{url}rev/{node|short}{sessionvars%urlparameter}">{node|short}</a></dd>
- {parent%filediffparent}
- {child%filediffchild}
- </dl>
-
- <div class="diff">
- {diff}
- </div>
-
-{footer}
--- a/sys/src/cmd/hg/templates/monoblue/filelog.tmpl
+++ /dev/null
@@ -1,49 +1,0 @@
-{header}
-<title>{repo|escape}: File revisions</title>
- <link rel="alternate" type="application/atom+xml" href="{url}atom-log" title="Atom feed for {repo|escape}"/>
- <link rel="alternate" type="application/rss+xml" href="{url}rss-log" title="RSS feed for {repo|escape}"/>
-</head>
-
-<body>
-<div id="container">
- <div class="page-header">
- <h1><a href="{url}summary{sessionvars%urlparameter}">{repo|escape}</a> / file revisions</h1>
-
- <form action="{url}log">
- {sessionvars%hiddenformentry}
- <dl class="search">
- <dt><label>Search: </label></dt>
- <dd><input type="text" name="rev" /></dd>
- </dl>
- </form>
-
- <ul class="page-nav">
- <li><a href="{url}summary{sessionvars%urlparameter}">summary</a></li>
- <li><a href="{url}shortlog{sessionvars%urlparameter}">shortlog</a></li>
- <li><a href="{url}log{sessionvars%urlparameter}">changelog</a></li>
- <li><a href="{url}graph/{node|short}{sessionvars%urlparameter}">graph</a></li>
- <li><a href="{url}tags{sessionvars%urlparameter}">tags</a></li>
- <li><a href="{url}branches{sessionvars%urlparameter}">branches</a></li>
- <li><a href="{url}file/{node|short}{path|urlescape}{sessionvars%urlparameter}">files</a></li>
- </ul>
- </div>
-
- <ul class="submenu">
- <li><a href="{url}file/{node|short}/{file|urlescape}{sessionvars%urlparameter}">file</a></li>
- <li class="current">revisions</li>
- <li><a href="{url}annotate/{node|short}/{file|urlescape}{sessionvars%urlparameter}">annotate</a></li>
- <li><a href="{url}diff/{node|short}/{file|urlescape}{sessionvars%urlparameter}">diff</a></li>
- <li><a href="{url}rss-log/{node|short}/{file|urlescape}">rss</a></li>
- </ul>
-
- <h2 class="no-link no-border">{file|urlescape}</h2>
-
- <table>
- {entries%filelogentry}
- </table>
-
- <div class="page-path">
- {nav%filenaventry}
- </div>
-
-{footer}
--- a/sys/src/cmd/hg/templates/monoblue/filerevision.tmpl
+++ /dev/null
@@ -1,63 +1,0 @@
-{header}
-<title>{repo|escape}: {file|escape}@{node|short}</title>
- <link rel="alternate" type="application/atom+xml" href="{url}atom-log" title="Atom feed for {repo|escape}"/>
- <link rel="alternate" type="application/rss+xml" href="{url}rss-log" title="RSS feed for {repo|escape}"/>
-</head>
-
-<body>
-<div id="container">
- <div class="page-header">
- <h1><a href="{url}summary{sessionvars%urlparameter}">{repo|escape}</a> / file revision</h1>
-
- <form action="{url}log">
- {sessionvars%hiddenformentry}
- <dl class="search">
- <dt><label>Search: </label></dt>
- <dd><input type="text" name="rev" /></dd>
- </dl>
- </form>
-
- <ul class="page-nav">
- <li><a href="{url}summary{sessionvars%urlparameter}">summary</a></li>
- <li><a href="{url}shortlog{sessionvars%urlparameter}">shortlog</a></li>
- <li><a href="{url}changelog{sessionvars%urlparameter}">changelog</a></li>
- <li><a href="{url}graph/{node|short}{sessionvars%urlparameter}">graph</a></li>
- <li><a href="{url}tags{sessionvars%urlparameter}">tags</a></li>
- <li><a href="{url}branches{sessionvars%urlparameter}">branches</a></li>
- <li><a href="{url}file/{node|short}{path|urlescape}{sessionvars%urlparameter}">files</a></li>
- </ul>
- </div>
-
- <ul class="submenu">
- <li class="current">file</li>
- <li><a href="{url}log/{node|short}/{file|urlescape}{sessionvars%urlparameter}">revisions</a></li>
- <li><a href="{url}annotate/{node|short}/{file|urlescape}{sessionvars%urlparameter}">annotate</a></li>
- <li><a href="{url}diff/{node|short}/{file|urlescape}{sessionvars%urlparameter}">diff</a></li>
- <li><a href="{url}raw-file/{node|short}/{file|urlescape}">raw</a></li>
- </ul>
-
- <h2 class="no-link no-border">{file|escape}@{node|short}</h2>
- <h3 class="changeset">{file|escape}</h3>
- <p class="changeset-age"><span>{date|age} ago</span></p>
-
- <dl class="overview">
- <dt>author</dt>
- <dd>{author|obfuscate}</dd>
- <dt>date</dt>
- <dd>{date|date}</dd>
- {branch%filerevbranch}
- <dt>changeset {rev}</dt>
- <dd><a class="list" href="{url}rev/{node|short}{sessionvars%urlparameter}">{node|short}</a></dd>
- {parent%filerevparent}
- {child%filerevchild}
- <dt>permissions</dt>
- <dd>{permissions|permissions}</dd>
- </dl>
-
- <p class="description">{desc|strip|escape|addbreaks|nonempty}</p>
-
- <div class="source">
- {text%fileline}
- </div>
-
-{footer}
--- a/sys/src/cmd/hg/templates/monoblue/footer.tmpl
+++ /dev/null
@@ -1,22 +1,0 @@
- <div class="page-footer">
- <p>Mercurial Repository: {repo|escape}</p>
- <ul class="rss-logo">
- <li><a href="{url}rss-log">RSS</a></li>
- <li><a href="{url}atom-log">Atom</a></li>
- </ul>
- {motd}
- </div>
-
- <div id="powered-by">
- <p><a href="http://mercurial.selenic.com/" title="Mercurial"><img src="{staticurl}hglogo.png" width=75 height=90 border=0 alt="mercurial"></a></p>
- </div>
-
- <div id="corner-top-left"></div>
- <div id="corner-top-right"></div>
- <div id="corner-bottom-left"></div>
- <div id="corner-bottom-right"></div>
-
-</div>
-
-</body>
-</html>
--- a/sys/src/cmd/hg/templates/monoblue/graph.tmpl
+++ /dev/null
@@ -1,118 +1,0 @@
-{header}
- <title>{repo|escape}: graph</title>
- <link rel="alternate" type="application/atom+xml" href="{url}atom-log" title="Atom feed for {repo|escape}"/>
- <link rel="alternate" type="application/rss+xml" href="{url}rss-log" title="RSS feed for {repo|escape}"/>
- <!--[if IE]><script type="text/javascript" src="{staticurl}excanvas.js"></script><![endif]-->
-</head>
-
-<body>
-<div id="container">
- <div class="page-header">
- <h1><a href="{url}summary{sessionvars%urlparameter}">{repo|escape}</a> / graph</h1>
-
- <form action="{url}log">
- {sessionvars%hiddenformentry}
- <dl class="search">
- <dt><label>Search: </label></dt>
- <dd><input type="text" name="rev" /></dd>
- </dl>
- </form>
-
- <ul class="page-nav">
- <li><a href="{url}summary{sessionvars%urlparameter}">summary</a></li>
- <li><a href="{url}shortlog{sessionvars%urlparameter}">shortlog</a></li>
- <li><a href="{url}changelog{sessionvars%urlparameter}">changelog</a></li>
- <li class="current">graph</li>
- <li><a href="{url}tags{sessionvars%urlparameter}">tags</a></li>
- <li><a href="{url}branches{sessionvars%urlparameter}">branches</a></li>
- <li><a href="{url}file/{node|short}{sessionvars%urlparameter}">files</a></li>
- </ul>
- </div>
-
- <h2 class="no-link no-border">graph</h2>
-
- <div id="noscript">The revision graph only works with JavaScript-enabled browsers.</div>
- <div id="wrapper">
- <ul id="nodebgs"></ul>
- <canvas id="graph" width="224" height="{canvasheight}"></canvas>
- <ul id="graphnodes"></ul>
- </div>
-
- <script type="text/javascript" src="{staticurl}graph.js"></script>
- <script>
- <!-- hide script content
-
- document.getElementById('noscript').style.display = 'none';
-
- var data = {jsdata|json};
- var graph = new Graph();
- graph.scale({bg_height});
-
- graph.edge = function(x0, y0, x1, y1, color) {
-
- this.setColor(color, 0.0, 0.65);
- this.ctx.beginPath();
- this.ctx.moveTo(x0, y0);
- this.ctx.lineTo(x1, y1);
- this.ctx.stroke();
-
- }
-
- var revlink = '<li style="_STYLE"><span class="desc">';
- revlink += '<a href="{url}rev/_NODEID{sessionvars%urlparameter}" title="_NODEID">_DESC</a>';
- revlink += '</span>_TAGS<span class="info">_DATE ago, by _USER</span></li>';
-
- graph.vertex = function(x, y, color, parity, cur) {
-
- this.ctx.beginPath();
- color = this.setColor(color, 0.25, 0.75);
- this.ctx.arc(x, y, radius, 0, Math.PI * 2, true);
- this.ctx.fill();
-
- var bg = '<li class="bg parity' + parity + '"></li>';
- var left = (this.columns + 1) * this.bg_height;
- var nstyle = 'padding-left: ' + left + 'px;';
- var item = revlink.replace(/_STYLE/, nstyle);
- item = item.replace(/_PARITY/, 'parity' + parity);
- item = item.replace(/_NODEID/, cur[0]);
- item = item.replace(/_NODEID/, cur[0]);
- item = item.replace(/_DESC/, cur[3]);
- item = item.replace(/_USER/, cur[4]);
- item = item.replace(/_DATE/, cur[5]);
-
- var tagspan = '';
- if (cur[7].length || (cur[6][0] != 'default' || cur[6][1])) {
- tagspan = '<span class="logtags">';
- if (cur[6][1]) {
- tagspan += '<span class="branchtag" title="' + cur[6][0] + '">';
- tagspan += cur[6][0] + '</span> ';
- } else if (!cur[6][1] && cur[6][0] != 'default') {
- tagspan += '<span class="inbranchtag" title="' + cur[6][0] + '">';
- tagspan += cur[6][0] + '</span> ';
- }
- if (cur[7].length) {
- for (var t in cur[7]) {
- var tag = cur[7][t];
- tagspan += '<span class="tagtag">' + tag + '</span> ';
- }
- }
- tagspan += '</span>';
- }
-
- item = item.replace(/_TAGS/, tagspan);
- return [bg, item];
-
- }
-
- graph.render(data);
-
- // stop hiding script -->
- </script>
-
- <div class="page-path">
- <a href="{url}graph/{rev}{lessvars%urlparameter}">less</a>
- <a href="{url}graph/{rev}{morevars%urlparameter}">more</a>
- | {changenav%navgraphentry}
- </div>
-
-{footer}
--- a/sys/src/cmd/hg/templates/monoblue/header.tmpl
+++ /dev/null
@@ -1,6 +1,0 @@
-<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
-<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
-<head>
- <link rel="icon" href="{staticurl}hgicon.png" type="image/png" />
- <meta name="robots" content="index, nofollow"/>
- <link rel="stylesheet" href="{staticurl}style-monoblue.css" type="text/css" />
--- a/sys/src/cmd/hg/templates/monoblue/index.tmpl
+++ /dev/null
@@ -1,39 +1,0 @@
-{header}
- <title>{repo|escape}: Mercurial repositories index</title>
-</head>
-
-<body>
-<div id="container">
- <div class="page-header">
- <h1>Mercurial Repositories</h1>
- <ul class="page-nav">
- </ul>
- </div>
-
- <table cellspacing="0">
- <tr>
- <td><a href="?sort={sort_name}">Name</a></td>
- <td><a href="?sort={sort_description}">Description</a></td>
- <td><a href="?sort={sort_contact}">Contact</a></td>
- <td><a href="?sort={sort_lastchange}">Last change</a></td>
- <td> </td>
- <td> </td>
- </tr>
- {entries%indexentry}
- </table>
- <div class="page-footer">
- {motd}
- </div>
-
- <div id="powered-by">
- <p><a href="http://mercurial.selenic.com/" title="Mercurial"><img src="{staticurl}hglogo.png" width=75 height=90 border=0 alt="mercurial"></a></p>
- </div>
-
- <div id="corner-top-left"></div>
- <div id="corner-top-right"></div>
- <div id="corner-bottom-left"></div>
- <div id="corner-bottom-right"></div>
-
-</div>
-</body>
-</html>
--- a/sys/src/cmd/hg/templates/monoblue/manifest.tmpl
+++ /dev/null
@@ -1,51 +1,0 @@
-{header}
-<title>{repo|escape}: files</title>
- <link rel="alternate" type="application/atom+xml" href="{url}atom-log" title="Atom feed for {repo|escape}"/>
- <link rel="alternate" type="application/rss+xml" href="{url}rss-log" title="RSS feed for {repo|escape}"/>
-</head>
-
-<body>
-<div id="container">
- <div class="page-header">
- <h1><a href="{url}summary{sessionvars%urlparameter}">{repo|escape}</a> / files</h1>
-
- <form action="{url}log">
- {sessionvars%hiddenformentry}
- <dl class="search">
- <dt><label>Search: </label></dt>
- <dd><input type="text" name="rev" /></dd>
- </dl>
- </form>
-
- <ul class="page-nav">
- <li><a href="{url}summary{sessionvars%urlparameter}">summary</a></li>
- <li><a href="{url}shortlog{sessionvars%urlparameter}">shortlog</a></li>
- <li><a href="{url}changelog{sessionvars%urlparameter}">changelog</a></li>
- <li><a href="{url}graph/{node|short}{sessionvars%urlparameter}">graph</a></li>
- <li><a href="{url}tags{sessionvars%urlparameter}">tags</a></li>
- <li><a href="{url}branches{sessionvars%urlparameter}">branches</a></li>
- <li class="current">files</li>
- </ul>
- </div>
-
- <ul class="submenu">
- <li><a href="{url}rev/{node|short}{sessionvars%urlparameter}">changeset</a> {archives%archiveentry}</li>
- {archives%archiveentry}
- </ul>
-
- <h2 class="no-link no-border">files</h2>
- <p class="files">{path|escape} <span class="logtags">{inbranch%inbranchtag}{branches%branchtag}{tags%tagtag}</span></p>
-
- <table>
- <tr class="parity{upparity}">
- <td>drwxr-xr-x</td>
- <td></td>
- <td></td>
- <td><a href="{url}file/{node|short}{up|urlescape}{sessionvars%urlparameter}">[up]</a></td>
- <td class="link"> </td>
- </tr>
- {dentries%direntry}
- {fentries%fileentry}
- </table>
-
-{footer}
--- a/sys/src/cmd/hg/templates/monoblue/map
+++ /dev/null
@@ -1,214 +1,0 @@
-default = 'summary'
-mimetype = 'text/html; charset={encoding}'
-header = header.tmpl
-footer = footer.tmpl
-search = search.tmpl
-changelog = changelog.tmpl
-summary = summary.tmpl
-error = error.tmpl
-notfound = notfound.tmpl
-naventry = '<a href="{url}log/{node|short}{sessionvars%urlparameter}">{label|escape}</a> '
-navshortentry = '<a href="{url}shortlog/{node|short}{sessionvars%urlparameter}">{label|escape}</a> '
-navgraphentry = '<a href="{url}graph/{node|short}{sessionvars%urlparameter}">{label|escape}</a> '
-filenaventry = '<a href="{url}log/{node|short}/{file|urlescape}{sessionvars%urlparameter}">{label|escape}</a>'
-filedifflink = '<a href="{url}diff/{node|short}/{file|urlescape}{sessionvars%urlparameter}">{file|escape}</a> '
-filenodelink = '
- <tr class="parity{parity}">
- <td><a href="{url}diff/{node|short}/{file|urlescape}{sessionvars%urlparameter}">{file|escape}</a></td>
- <td></td>
- <td>
- <a href="{url}file/{node|short}/{file|urlescape}{sessionvars%urlparameter}">file</a> |
- <a href="{url}annotate/{node|short}/{file|urlescape}{sessionvars%urlparameter}">annotate</a> |
- <a href="{url}diff/{node|short}/{file|urlescape}{sessionvars%urlparameter}">diff</a> |
- <a href="{url}log/{node|short}/{file|urlescape}{sessionvars%urlparameter}">revisions</a>
- </td>
- </tr>'
-filenolink = '
- <tr class="parity{parity}">
- <td>
- <a href="{url}diff/{node|short}/{file|urlescape}{sessionvars%urlparameter}">{file|escape}</a></td><td></td><td>file |
- annotate |
- <a href="{url}diff/{node|short}/{file|urlescape}{sessionvars%urlparameter}">diff</a> |
- <a href="{url}log/{node|short}/{file|urlescape}{sessionvars%urlparameter}">revisions</a>
- </td>
- </tr>'
-fileellipses = '...'
-changelogentry = changelogentry.tmpl
-searchentry = changelogentry.tmpl
-changeset = changeset.tmpl
-manifest = manifest.tmpl
-direntry = '
- <tr class="parity{parity}">
- <td>drwxr-xr-x</td>
- <td></td>
- <td></td>
- <td><a href="{url}file/{node|short}{path|urlescape}{sessionvars%urlparameter}">{basename|escape}</a></td>
- <td><a href="{url}file/{node|short}{path|urlescape}{sessionvars%urlparameter}">files</a></td>
- </tr>'
-fileentry = '
- <tr class="parity{parity}">
- <td>{permissions|permissions}</td>
- <td>{date|isodate}</td>
- <td>{size}</td>
- <td><a href="{url}file/{node|short}/{file|urlescape}{sessionvars%urlparameter}">{basename|escape}</a></td>
- <td>
- <a href="{url}file/{node|short}/{file|urlescape}{sessionvars%urlparameter}">file</a> |
- <a href="{url}log/{node|short}/{file|urlescape}{sessionvars%urlparameter}">revisions</a> |
- <a href="{url}annotate/{node|short}/{file|urlescape}{sessionvars%urlparameter}">annotate</a>
- </td>
- </tr>'
-filerevision = filerevision.tmpl
-fileannotate = fileannotate.tmpl
-filediff = filediff.tmpl
-filelog = filelog.tmpl
-fileline = '
- <div style="font-family:monospace" class="parity{parity}">
- <pre><a class="linenr" href="#{lineid}" id="{lineid}">{linenumber}</a> {line|escape}</pre>
- </div>'
-annotateline = '
- <tr class="parity{parity}">
- <td class="linenr">
- <a href="{url}annotate/{node|short}/{file|urlescape}{sessionvars%urlparameter}#{targetline}"
- title="{node|short}: {desc|escape|firstline}">{author|user}@{rev}</a>
- </td>
- <td class="lineno">
- <a href="#{lineid}" id="{lineid}">{linenumber}</a>
- </td>
- <td class="source">{line|escape}</td>
- </tr>'
-difflineplus = '<span style="color:#008800;"><a class="linenr" href="#{lineid}" id="{lineid}">{linenumber}</a> {line|escape}</span>'
-difflineminus = '<span style="color:#cc0000;"><a class="linenr" href="#{lineid}" id="{lineid}">{linenumber}</a> {line|escape}</span>'
-difflineat = '<span style="color:#990099;"><a class="linenr" href="#{lineid}" id="{lineid}">{linenumber}</a> {line|escape}</span>'
-diffline = '<span><a class="linenr" href="#{lineid}" id="{lineid}">{linenumber}</a> {line|escape}</span>'
-changelogparent = '
- <tr>
- <th class="parent">parent {rev}:</th>
- <td class="parent">
- <a href="{url}rev/{node|short}{sessionvars%urlparameter}">{node|short}</a>
- </td>
- </tr>'
-changesetbranch = '<dt>branch</dt><dd>{name}</dd>'
-changesetparent = '
- <dt>parent {rev}</dt>
- <dd><a href="{url}rev/{node|short}{sessionvars%urlparameter}">{node|short}</a></dd>'
-filerevbranch = '<dt>branch</dt><dd>{name}</dd>'
-filerevparent = '
- <dt>parent {rev}</dt>
- <dd>
- <a href="{url}file/{node|short}/{file|urlescape}{sessionvars%urlparameter}">
- {rename%filerename}{node|short}
- </a>
- </dd>'
-filerename = '{file|escape}@'
-filelogrename = '| <a href="{url}file/{node|short}/{file|urlescape}{sessionvars%urlparameter}">base</a>'
-fileannotateparent = '
- <dt>parent {rev}</dt>
- <dd>
- <a href="{url}annotate/{node|short}/{file|urlescape}{sessionvars%urlparameter}">
- {rename%filerename}{node|short}
- </a>
- </dd>'
-changelogchild = '
- <dt>child {rev}:</dt>
- <dd><a href="{url}rev/{node|short}{sessionvars%urlparameter}">{node|short}</a></dd>'
-changesetchild = '
- <dt>child {rev}</dt>
- <dd><a href="{url}rev/{node|short}{sessionvars%urlparameter}">{node|short}</a></dd>'
-filerevchild = '
- <dt>child {rev}</dt>
- <dd>
- <a href="{url}file/{node|short}/{file|urlescape}{sessionvars%urlparameter}">{node|short}</a>
- </dd>'
-fileannotatechild = '
- <dt>child {rev}</dt>
- <dd>
- <a href="{url}annotate/{node|short}/{file|urlescape}{sessionvars%urlparameter}">{node|short}</a>
- </dd>'
-tags = tags.tmpl
-tagentry = '
- <tr class="parity{parity}">
- <td class="nowrap">{date|age} ago</td>
- <td><a href="{url}rev/{node|short}{sessionvars%urlparameter}">{tag|escape}</a></td>
- <td class="nowrap">
- <a href="{url}rev/{node|short}{sessionvars%urlparameter}">changeset</a> |
- <a href="{url}log/{node|short}{sessionvars%urlparameter}">changelog</a> |
- <a href="{url}file/{node|short}{sessionvars%urlparameter}">files</a>
- </td>
- </tr>'
-branches = branches.tmpl
-branchentry = '
- <tr class="parity{parity}">
- <td class="nowrap">{date|age} ago</td>
- <td><a href="{url}shortlog/{node|short}{sessionvars%urlparameter}">{node|short}</a></td>
- <td class="{status}">{branch|escape}</td>
- <td class="nowrap">
- <a href="{url}rev/{node|short}{sessionvars%urlparameter}">changeset</a> |
- <a href="{url}log/{node|short}{sessionvars%urlparameter}">changelog</a> |
- <a href="{url}file/{node|short}{sessionvars%urlparameter}">files</a>
- </td>
- </tr>'
-diffblock = '<pre>{lines}</pre>'
-filediffparent = '
- <dt>parent {rev}</dt>
- <dd><a href="{url}diff/{node|short}/{file|urlescape}{sessionvars%urlparameter}">{node|short}</a></dd>'
-filelogparent = '
- <tr>
- <td align="right">parent {rev}: </td>
- <td><a href="{url}file/{node|short}/{file|urlescape}{sessionvars%urlparameter}">{node|short}</a></td>
- </tr>'
-filediffchild = '
- <dt>child {rev}</dt>
- <dd><a href="{url}diff/{node|short}/{file|urlescape}{sessionvars%urlparameter}">{node|short}</a></dd>'
-filelogchild = '
- <tr>
- <td align="right">child {rev}: </td>
- <td><a href="{url}file{node|short}/{file|urlescape}{sessionvars%urlparameter}">{node|short}</a></td>
- </tr>'
-shortlog = shortlog.tmpl
-tagtag = '<span class="tagtag" title="{name}">{name}</span> '
-branchtag = '<span class="branchtag" title="{name}">{name}</span> '
-inbranchtag = '<span class="inbranchtag" title="{name}">{name}</span> '
-shortlogentry = '
- <tr class="parity{parity}">
- <td class="nowrap">{date|age} ago</td>
- <td>{author|person}</td>
- <td>
- <a href="{url}rev/{node|short}{sessionvars%urlparameter}">
- {desc|strip|firstline|escape|nonempty}
- <span class="logtags">{inbranch%inbranchtag}{branches%branchtag}{tags%tagtag}</span>
- </a>
- </td>
- <td class="nowrap">
- <a href="{url}rev/{node|short}{sessionvars%urlparameter}">changeset</a> |
- <a href="{url}file/{node|short}{sessionvars%urlparameter}">files</a>
- </td>
- </tr>'
-filelogentry = '
- <tr class="parity{parity}">
- <td class="nowrap">{date|age} ago</td>
- <td><a href="{url}rev/{node|short}{sessionvars%urlparameter}">{desc|strip|firstline|escape|nonempty}</a></td>
- <td class="nowrap">
- <a href="{url}file/{node|short}/{file|urlescape}{sessionvars%urlparameter}">file</a> | <a href="{url}diff/{node|short}/{file|urlescape}{sessionvars%urlparameter}">diff</a> | <a href="{url}annotate/{node|short}/{file|urlescape}{sessionvars%urlparameter}">annotate</a>
- {rename%filelogrename}
- </td>
- </tr>'
-archiveentry = '<li><a href="{url}archive/{node|short}{extension}">{type|escape}</a></li>'
-indexentry = '
- <tr class="parity{parity}">
- <td><a href="{url}{sessionvars%urlparameter}">{name|escape}</a></td>
- <td>{description}</td>
- <td>{contact|obfuscate}</td>
- <td>{lastchange|age} ago</td>
- <td class="indexlinks">{archives%indexarchiveentry}</td>
- <td>
- <div class="rss_logo">
- <a href="{url}rss-log">RSS</a>
- <a href="{url}atom-log">Atom</a>
- </div>
- </td>
- </tr>\n'
-indexarchiveentry = '<a href="{url}archive/{node|short}{extension}">{type|escape}</a> '
-index = index.tmpl
-urlparameter = '{separator}{name}={value|urlescape}'
-hiddenformentry = '<input type="hidden" name="{name}" value="{value|escape}" />'
-graph = graph.tmpl
--- a/sys/src/cmd/hg/templates/monoblue/notfound.tmpl
+++ /dev/null
@@ -1,35 +1,0 @@
-{header}
- <title>{repo|escape}: Mercurial repository not found</title>
- <link rel="alternate" type="application/atom+xml" href="{url}atom-log" title="Atom feed for {repo|escape}"/>
- <link rel="alternate" type="application/rss+xml" href="{url}rss-log" title="RSS feed for {repo|escape}"/>
-</head>
-
-<body>
-<div id="container">
- <div class="page-header">
- <h1><a href="{url}summary{sessionvars%urlparameter}">{repo|escape}</a> / Not found: {repo|escape}</h1>
-
- <form action="{url}log">
- {sessionvars%hiddenformentry}
- <dl class="search">
- <dt><label>Search: </label></dt>
- <dd><input type="text" name="rev" /></dd>
- </dl>
- </form>
-
- <ul class="page-nav">
- <li class="current">summary</li>
- <li><a href="{url}shortlog{sessionvars%urlparameter}">shortlog</a></li>
- <li><a href="{url}log{sessionvars%urlparameter}">changelog</a></li>
- <li><a href="{url}graph/{node|short}{sessionvars%urlparameter}">graph</a></li>
- <li><a href="{url}tags{sessionvars%urlparameter}">tags</a></li>
- <li><a href="{url}branches{sessionvars%urlparameter}">branches</a></li>
- <li><a href="{url}file/{node|short}{sessionvars%urlparameter}">files</a>{archives%archiveentry}</li>
- </ul>
- </div>
-
- <h2 class="no-link no-border">Not Found</h2>
- <p class="normal">The specified repository "{repo|escape}" is unknown, sorry.</p>
- <p class="normal">Please go back to the <a href="{url}">main repository list page</a>.</p>
-
-{footer}
--- a/sys/src/cmd/hg/templates/monoblue/search.tmpl
+++ /dev/null
@@ -1,34 +1,0 @@
-{header}
- <title>{repo|escape}: Search</title>
- <link rel="alternate" type="application/atom+xml" href="{url}atom-log" title="Atom feed for {repo|escape}"/>
- <link rel="alternate" type="application/rss+xml" href="{url}rss-log" title="RSS feed for {repo|escape}"/>
-</head>
-
-<body>
-<div id="container">
- <div class="page-header">
- <h1><a href="{url}summary{sessionvars%urlparameter}">{repo|escape}</a> / search</h1>
-
- <form action="{url}log">
- {sessionvars%hiddenformentry}
- <dl class="search">
- <dt><label>Search: </label></dt>
- <dd><input type="text" name="rev" value="{query|escape}" /></dd>
- </dl>
- </form>
-
- <ul class="page-nav">
- <li><a href="{url}summary{sessionvars%urlparameter}">summary</a></li>
- <li><a href="{url}shortlog{sessionvars%urlparameter}">shortlog</a></li>
- <li><a href="{url}log{sessionvars%urlparameter}">changelog</a></li>
- <li><a href="{url}graph/{node|short}{sessionvars%urlparameter}">graph</a></li>
- <li><a href="{url}tags{sessionvars%urlparameter}">tags</a></li>
- <li><a href="{url}branches{sessionvars%urlparameter}">branches</a></li>
- <li><a href="{url}file/{node|short}{sessionvars%urlparameter}">files</a>{archives%archiveentry}
- </ul>
- </div>
-
- <h2 class="no-link no-border">searching for {query|escape}</h2>
- {entries}
-
-{footer}
--- a/sys/src/cmd/hg/templates/monoblue/shortlog.tmpl
+++ /dev/null
@@ -1,41 +1,0 @@
-{header}
- <title>{repo|escape}: shortlog</title>
- <link rel="alternate" type="application/atom+xml" href="{url}atom-log" title="Atom feed for {repo|escape}"/>
- <link rel="alternate" type="application/rss+xml" href="{url}rss-log" title="RSS feed for {repo|escape}"/>
-</head>
-
-<body>
-<div id="container">
- <div class="page-header">
- <h1><a href="{url}summary{sessionvars%urlparameter}">{repo|escape}</a> / shortlog</h1>
-
- <form action="{url}log">
- {sessionvars%hiddenformentry}
- <dl class="search">
- <dt><label>Search: </label></dt>
- <dd><input type="text" name="rev" /></dd>
- </dl>
- </form>
-
- <ul class="page-nav">
- <li><a href="{url}summary{sessionvars%urlparameter}">summary</a></li>
- <li class="current">shortlog</li>
- <li><a href="{url}log{sessionvars%urlparameter}">changelog</a></li>
- <li><a href="{url}graph/{node|short}{sessionvars%urlparameter}">graph</a></li>
- <li><a href="{url}tags{sessionvars%urlparameter}">tags</a></li>
- <li><a href="{url}branches{sessionvars%urlparameter}">branches</a></li>
- <li><a href="{url}file/{node|short}{sessionvars%urlparameter}">files</a>{archives%archiveentry}</li>
- </ul>
- </div>
-
- <h2 class="no-link no-border">shortlog</h2>
-
- <table>
-{entries%shortlogentry}
- </table>
-
- <div class="page-path">
-{changenav%navshortentry}
- </div>
-
-{footer}
--- a/sys/src/cmd/hg/templates/monoblue/summary.tmpl
+++ /dev/null
@@ -1,66 +1,0 @@
-{header}
- <title>{repo|escape}: Summary</title>
- <link rel="alternate" type="application/atom+xml" href="{url}atom-log" title="Atom feed for {repo|escape}"/>
- <link rel="alternate" type="application/rss+xml" href="{url}rss-log" title="RSS feed for {repo|escape}"/>
-</head>
-
-<body>
-<div id="container">
- <div class="page-header">
- <h1><a href="{url}summary{sessionvars%urlparameter}">{repo|escape}</a> / summary</h1>
-
- <form action="{url}log">
- {sessionvars%hiddenformentry}
- <dl class="search">
- <dt><label>Search: </label></dt>
- <dd><input type="text" name="rev" /></dd>
- </dl>
- </form>
-
- <ul class="page-nav">
- <li class="current">summary</li>
- <li><a href="{url}shortlog{sessionvars%urlparameter}">shortlog</a></li>
- <li><a href="{url}log{sessionvars%urlparameter}">changelog</a></li>
- <li><a href="{url}graph/{node|short}{sessionvars%urlparameter}">graph</a></li>
- <li><a href="{url}tags{sessionvars%urlparameter}">tags</a></li>
- <li><a href="{url}branches{sessionvars%urlparameter}">branches</a></li>
- <li><a href="{url}file/{node|short}{sessionvars%urlparameter}">files</a></li>
- </ul>
- </div>
-
- <h2 class="no-link no-border">Mercurial Repository Overview</h2>
- <dl class="overview">
- <dt>name</dt>
- <dd>{repo|escape}</dd>
- <dt>description</dt>
- <dd>{desc}</dd>
- <dt>owner</dt>
- <dd>{owner|obfuscate}</dd>
- <dt>last change</dt>
- <dd>{lastchange|rfc822date}</dd>
- </dl>
-
- <h2><a href="{url}shortlog{sessionvars%urlparameter}">Changes</a></h2>
- <table>
-{shortlog}
- <tr class="light">
- <td colspan="4"><a class="list" href="{url}shortlog{sessionvars%urlparameter}">...</a></td>
- </tr>
- </table>
-
- <h2><a href="{url}tags{sessionvars%urlparameter}">Tags</a></h2>
- <table>
-{tags}
- <tr class="light">
- <td colspan="3"><a class="list" href="{url}tags{sessionvars%urlparameter}">...</a></td>
- </tr>
- </table>
-
- <h2 class="no-link">Branches</h2>
- <table>
- {branches%branchentry}
- <tr class="light">
- <td colspan="4"><a class="list" href="#">...</a></td>
- </tr>
- </table>
-{footer}
--- a/sys/src/cmd/hg/templates/monoblue/tags.tmpl
+++ /dev/null
@@ -1,36 +1,0 @@
-{header}
- <title>{repo|escape}: Tags</title>
- <link rel="alternate" type="application/atom+xml" href="{url}atom-log" title="Atom feed for {repo|escape}"/>
- <link rel="alternate" type="application/rss+xml" href="{url}rss-log" title="RSS feed for {repo|escape}"/>
-</head>
-
-<body>
-<div id="container">
- <div class="page-header">
- <h1><a href="{url}summary{sessionvars%urlparameter}">{repo|escape}</a> / Tags</h1>
-
- <form action="{url}log">
- {sessionvars%hiddenformentry}
- <dl class="search">
- <dt><label>Search: </label></dt>
- <dd><input type="text" name="rev" /></dd>
- </dl>
- </form>
-
- <ul class="page-nav">
- <li><a href="{url}summary{sessionvars%urlparameter}">summary</a></li>
- <li><a href="{url}shortlog{sessionvars%urlparameter}">shortlog</a></li>
- <li><a href="{url}changelog{sessionvars%urlparameter}">changelog</a></li>
- <li><a href="{url}graph/{node|short}{sessionvars%urlparameter}">graph</a></li>
- <li class="current">tags</li>
- <li><a href="{url}branches{sessionvars%urlparameter}">branches</a></li>
- <li><a href="{url}file/{node|short}{sessionvars%urlparameter}">files</a></li>
- </ul>
- </div>
-
- <h2 class="no-link no-border">tags</h2>
- <table cellspacing="0">
-{entries%tagentry}
- </table>
-
-{footer}
--- a/sys/src/cmd/hg/templates/paper/branches.tmpl
+++ /dev/null
@@ -1,45 +1,0 @@
-{header}
-<title>{repo|escape}: branches</title>
-<link rel="alternate" type="application/atom+xml"
- href="{url}atom-tags" title="Atom feed for {repo|escape}: branches" />
-<link rel="alternate" type="application/rss+xml"
- href="{url}rss-tags" title="RSS feed for {repo|escape}: branches" />
-</head>
-<body>
-
-<div class="container">
-<div class="menu">
-<div class="logo">
-<a href="http://mercurial.selenic.com/">
-<img src="{staticurl}hglogo.png" alt="mercurial" /></a>
-</div>
-<ul>
-<li><a href="{url}shortlog{sessionvars%urlparameter}">log</a></li>
-<li><a href="{url}graph{sessionvars%urlparameter}">graph</a></li>
-<li><a href="{url}tags{sessionvars%urlparameter}">tags</a></li>
-<li class="active">branches</li>
-</ul>
-</div>
-
-<div class="main">
-<h2><a href="{url}{sessionvars%urlparameter}">{repo|escape}</a></h2>
-<h3>branches</h3>
-
-<form class="search" action="{url}log">
-{sessionvars%hiddenformentry}
-<p><input name="rev" id="search1" type="text" size="30" /></p>
-<div id="hint">find changesets by author, revision,
-files, or words in the commit message</div>
-</form>
-
-<table class="bigtable">
-<tr>
- <th>branch</th>
- <th>node</th>
-</tr>
-{entries%branchentry}
-</table>
-</div>
-</div>
-
-{footer}
--- a/sys/src/cmd/hg/templates/paper/changeset.tmpl
+++ /dev/null
@@ -1,71 +1,0 @@
-{header}
-<title>{repo|escape}: {node|short}</title>
-</head>
-<body>
-<div class="container">
-<div class="menu">
-<div class="logo">
-<a href="http://mercurial.selenic.com/">
-<img src="{staticurl}hglogo.png" alt="mercurial" /></a>
-</div>
-<ul>
- <li><a href="{url}shortlog/{node|short}{sessionvars%urlparameter}">log</a></li>
- <li><a href="{url}graph/{node|short}{sessionvars%urlparameter}">graph</a></li>
- <li><a href="{url}tags{sessionvars%urlparameter}">tags</a></li>
- <li><a href="{url}branches{sessionvars%urlparameter}">branches</a></li>
-</ul>
-<ul>
- <li class="active">changeset</li>
- <li><a href="{url}raw-rev/{node|short}{sessionvars%urlparameter}">raw</a></li>
- <li><a href="{url}file/{node|short}{sessionvars%urlparameter}">browse</a></li>
-</ul>
-<ul>
- {archives%archiveentry}
-</ul>
-</div>
-
-<div class="main">
-
-<h2><a href="{url}{sessionvars%urlparameter}">{repo|escape}</a></h2>
-<h3>changeset {rev}:{node|short} {changesetbranch%changelogbranchname} {changesettag}</h3>
-
-<form class="search" action="{url}log">
-{sessionvars%hiddenformentry}
-<p><input name="rev" id="search1" type="text" size="30" /></p>
-<div id="hint">find changesets by author, revision,
-files, or words in the commit message</div>
-</form>
-
-<div class="description">{desc|strip|escape|addbreaks|nonempty}</div>
-
-<table id="changesetEntry">
-<tr>
- <th class="author">author</th>
- <td class="author">{author|obfuscate}</td>
-</tr>
-<tr>
- <th class="date">date</th>
- <td class="date">{date|date} ({date|age} ago)</td></tr>
-<tr>
- <th class="author">parents</th>
- <td class="author">{parent%changesetparent}</td>
-</tr>
-<tr>
- <th class="author">children</th>
- <td class="author">{child%changesetchild}</td>
-</tr>
-<tr>
- <th class="files">files</th>
- <td class="files">{files}</td>
-</tr>
-</table>
-
-<div class="overflow">
-<div class="sourcefirst"> line diff</div>
-
-{diff}
-</div>
-
-</div>
-</div>
-{footer}
--- a/sys/src/cmd/hg/templates/paper/error.tmpl
+++ /dev/null
@@ -1,43 +1,0 @@
-{header}
-<title>{repo|escape}: error</title>
-</head>
-<body>
-
-<div class="container">
-<div class="menu">
-<div class="logo">
-<a href="http://mercurial.selenic.com/">
-<img src="{staticurl}hglogo.png" width=75 height=90 border=0 alt="mercurial" /></a>
-</div>
-<ul>
-<li><a href="{url}shortlog{sessionvars%urlparameter}">log</a></li>
-<li><a href="{url}graph{sessionvars%urlparameter}">graph</a></li>
-<li><a href="{url}tags{sessionvars%urlparameter}">tags</a></li>
-<li><a href="{url}branches{sessionvars%urlparameter}">branches</a></li>
-</ul>
-</div>
-
-<div class="main">
-
-<h2><a href="{url}{sessionvars%urlparameter}">{repo|escape}</a></h2>
-<h3>error</h3>
-
-<form class="search" action="{url}log">
-{sessionvars%hiddenformentry}
-<p><input name="rev" id="search1" type="text" size="30"></p>
-<div id="hint">find changesets by author, revision,
-files, or words in the commit message</div>
-</form>
-
-<div class="description">
-<p>
-An error occurred while processing your request:
-</p>
-<p>
-{error|escape}
-</p>
-</div>
-</div>
-</div>
-
-{footer}
--- a/sys/src/cmd/hg/templates/paper/fileannotate.tmpl
+++ /dev/null
@@ -1,77 +1,0 @@
-{header}
-<title>{repo|escape}: {file|escape} annotate</title>
-</head>
-<body>
-
-<div class="container">
-<div class="menu">
-<div class="logo">
-<a href="http://mercurial.selenic.com/">
-<img src="{staticurl}hglogo.png" alt="mercurial" /></a>
-</div>
-<ul>
-<li><a href="{url}shortlog/{node|short}{sessionvars%urlparameter}">log</a></li>
-<li><a href="{url}graph/{node|short}{sessionvars%urlparameter}">graph</a></li>
-<li><a href="{url}tags{sessionvars%urlparameter}">tags</a></li>
-<li><a href="{url}branches{sessionvars%urlparameter}">branches</a></li>
-</ul>
-
-<ul>
-<li><a href="{url}rev/{node|short}{sessionvars%urlparameter}">changeset</a></li>
-<li><a href="{url}file/{node|short}{path|urlescape}{sessionvars%urlparameter}">browse</a></li>
-</ul>
-<ul>
-<li><a href="{url}file/{node|short}/{file|urlescape}{sessionvars%urlparameter}">file</a></li>
-<li><a href="{url}diff/{node|short}/{file|urlescape}{sessionvars%urlparameter}">diff</a></li>
-<li class="active">annotate</li>
-<li><a href="{url}log/{node|short}/{file|urlescape}{sessionvars%urlparameter}">file log</a></li>
-<li><a href="{url}raw-annotate/{node|short}/{file|urlescape}">raw</a></li>
-</ul>
-</div>
-
-<div class="main">
-<h2><a href="{url}{sessionvars%urlparameter}">{repo|escape}</a></h2>
-<h3>annotate {file|escape} @ {rev}:{node|short}</h3>
-
-<form class="search" action="{url}log">
-{sessionvars%hiddenformentry}
-<p><input name="rev" id="search1" type="text" size="30" /></p>
-<div id="hint">find changesets by author, revision,
-files, or words in the commit message</div>
-</form>
-
-<div class="description">{desc|strip|escape|addbreaks|nonempty}</div>
-
-<table id="changesetEntry">
-<tr>
- <th class="author">author</th>
- <td class="author">{author|obfuscate}</td>
-</tr>
-<tr>
- <th class="date">date</th>
- <td class="date">{date|date} ({date|age} ago)</td>
-</tr>
-<tr>
- <th class="author">parents</th>
- <td class="author">{parent%filerevparent}</td>
-</tr>
-<tr>
- <th class="author">children</th>
- <td class="author">{child%filerevchild}</td>
-</tr>
-{changesettag}
-</table>
-
-<div class="overflow">
-<table class="bigtable">
-<tr>
- <th class="annotate">rev</th>
- <th class="line"> line source</th>
-</tr>
-{annotate%annotateline}
-</table>
-</div>
-</div>
-</div>
-
-{footer}
--- a/sys/src/cmd/hg/templates/paper/filediff.tmpl
+++ /dev/null
@@ -1,72 +1,0 @@
-{header}
-<title>{repo|escape}: {file|escape} diff</title>
-</head>
-<body>
-
-<div class="container">
-<div class="menu">
-<div class="logo">
-<a href="http://mercurial.selenic.com/">
-<img src="{staticurl}hglogo.png" alt="mercurial" /></a>
-</div>
-<ul>
-<li><a href="{url}shortlog/{node|short}{sessionvars%urlparameter}">log</a></li>
-<li><a href="{url}graph/{node|short}{sessionvars%urlparameter}">graph</a></li>
-<li><a href="{url}tags{sessionvars%urlparameter}">tags</a></li>
-<li><a href="{url}branches{sessionvars%urlparameter}">branches</a></li>
-</ul>
-<ul>
-<li><a href="{url}rev/{node|short}{sessionvars%urlparameter}">changeset</a></li>
-<li><a href="{url}file/{node|short}{path|urlescape}{sessionvars%urlparameter}">browse</a></li>
-</ul>
-<ul>
-<li><a href="{url}file/{node|short}/{file|urlescape}{sessionvars%urlparameter}">file</a></li>
-<li class="active">diff</li>
-<li><a href="{url}annotate/{node|short}/{file|urlescape}{sessionvars%urlparameter}">annotate</a></li>
-<li><a href="{url}log/{node|short}/{file|urlescape}{sessionvars%urlparameter}">file log</a></li>
-<li><a href="{url}raw-file/{node|short}/{file|urlescape}">raw</a></li>
-</ul>
-</div>
-
-<div class="main">
-<h2><a href="{url}{sessionvars%urlparameter}">{repo|escape}</a></h2>
-<h3>diff {file|escape} @ {rev}:{node|short}</h3>
-
-<form class="search" action="{url}log">
-<p>{sessionvars%hiddenformentry}</p>
-<p><input name="rev" id="search1" type="text" size="30" /></p>
-<div id="hint">find changesets by author, revision,
-files, or words in the commit message</div>
-</form>
-
-<div class="description">{desc|strip|escape|addbreaks|nonempty}</div>
-
-<table id="changesetEntry">
-<tr>
- <th>author</th>
- <td>{author|obfuscate}</td>
-</tr>
-<tr>
- <th>date</th>
- <td>{date|date} ({date|age} ago)</td>
-</tr>
-<tr>
- <th>parents</th>
- <td>{parent%filerevparent}</td>
-</tr>
-<tr>
- <th>children</th>
- <td>{child%filerevchild}</td>
-</tr>
-{changesettag}
-</table>
-
-<div class="overflow">
-<div class="sourcefirst"> line diff</div>
-
-{diff}
-</div>
-</div>
-</div>
-
-{footer}
--- a/sys/src/cmd/hg/templates/paper/filelog.tmpl
+++ /dev/null
@@ -1,60 +1,0 @@
-{header}
-<title>{repo|escape}: {file|escape} history</title>
-<link rel="alternate" type="application/atom+xml"
- href="{url}atom-log/tip/{file|urlescape}" title="Atom feed for {repo|escape}:{file}" />
-<link rel="alternate" type="application/rss+xml"
- href="{url}rss-log/tip/{file|urlescape}" title="RSS feed for {repo|escape}:{file}" />
-</head>
-<body>
-
-<div class="container">
-<div class="menu">
-<div class="logo">
-<a href="http://mercurial.selenic.com/">
-<img src="{staticurl}hglogo.png" alt="mercurial" /></a>
-</div>
-<ul>
-<li><a href="{url}shortlog/{node|short}{sessionvars%urlparameter}">log</a></li>
-<li><a href="{url}graph/{node|short}{sessionvars%urlparameter}">graph</a></li>
-<li><a href="{url}tags{sessionvars%urlparameter}">tags</a></li>
-<li><a href="{url}branches{sessionvars%urlparameter}">branches</a></li>
-</ul>
-<ul>
-<li><a href="{url}rev/{node|short}{sessionvars%urlparameter}">changeset</a></li>
-<li><a href="{url}file/{node|short}{path|urlescape}{sessionvars%urlparameter}">browse</a></li>
-</ul>
-<ul>
-<li><a href="{url}file/{node|short}/{file|urlescape}{sessionvars%urlparameter}">file</a></li>
-<li><a href="{url}diff/{node|short}/{file|urlescape}{sessionvars%urlparameter}">diff</a></li>
-<li><a href="{url}annotate/{node|short}/{file|urlescape}{sessionvars%urlparameter}">annotate</a></li>
-<li class="active">file log</li>
-<li><a href="{url}raw-file/{node|short}/{file|urlescape}">raw</a></li>
-</ul>
-</div>
-
-<div class="main">
-<h2><a href="{url}{sessionvars%urlparameter}">{repo|escape}</a></h2>
-<h3>log {file|escape}</h3>
-
-<form class="search" action="{url}log">
-{sessionvars%hiddenformentry}
-<p><input name="rev" id="search1" type="text" size="30" /></p>
-<div id="hint">find changesets by author, revision,
-files, or words in the commit message</div>
-</form>
-
-<div class="navigate">{nav%filenaventry}</div>
-
-<table class="bigtable">
- <tr>
- <th class="age">age</th>
- <th class="author">author</th>
- <th class="description">description</th>
- </tr>
-{entries%filelogentry}
-</table>
-
-</div>
-</div>
-
-{footer}
--- a/sys/src/cmd/hg/templates/paper/filelogentry.tmpl
+++ /dev/null
@@ -1,5 +1,0 @@
- <tr class="parity{parity}">
- <td class="age">{date|age}</td>
- <td class="author">{author|person}</td>
- <td class="description"><a href="{url}rev/{node|short}{sessionvars%urlparameter}">{desc|strip|firstline|escape|nonempty}</a>{inbranch%changelogbranchname}{branches%changelogbranchhead}{tags%changelogtag}</td>
- </tr>
--- a/sys/src/cmd/hg/templates/paper/filerevision.tmpl
+++ /dev/null
@@ -1,72 +1,0 @@
-{header}
-<title>{repo|escape}: {node|short} {file|escape}</title>
-</head>
-<body>
-
-<div class="container">
-<div class="menu">
-<div class="logo">
-<a href="http://mercurial.selenic.com/">
-<img src="{staticurl}hglogo.png" alt="mercurial" /></a>
-</div>
-<ul>
-<li><a href="{url}shortlog/{node|short}{sessionvars%urlparameter}">log</a></li>
-<li><a href="{url}graph/{node|short}{sessionvars%urlparameter}">graph</a></li>
-<li><a href="{url}tags{sessionvars%urlparameter}">tags</a></li>
-<li><a href="{url}branches{sessionvars%urlparameter}">branches</a></li>
-</ul>
-<ul>
-<li><a href="{url}rev/{node|short}{sessionvars%urlparameter}">changeset</a></li>
-<li><a href="{url}file/{node|short}{path|urlescape}{sessionvars%urlparameter}">browse</a></li>
-</ul>
-<ul>
-<li class="active">file</li>
-<li><a href="{url}diff/{node|short}/{file|urlescape}{sessionvars%urlparameter}">diff</a></li>
-<li><a href="{url}annotate/{node|short}/{file|urlescape}{sessionvars%urlparameter}">annotate</a></li>
-<li><a href="{url}log/{node|short}/{file|urlescape}{sessionvars%urlparameter}">file log</a></li>
-<li><a href="{url}raw-file/{node|short}/{file|urlescape}">raw</a></li>
-</ul>
-</div>
-
-<div class="main">
-<h2><a href="{url}{sessionvars%urlparameter}">{repo|escape}</a></h2>
-<h3>view {file|escape} @ {rev}:{node|short}</h3>
-
-<form class="search" action="{url}log">
-{sessionvars%hiddenformentry}
-<p><input name="rev" id="search1" type="text" size="30" /></p>
-<div id="hint">find changesets by author, revision,
-files, or words in the commit message</div>
-</form>
-
-<div class="description">{desc|strip|escape|addbreaks|nonempty}</div>
-
-<table id="changesetEntry">
-<tr>
- <th class="author">author</th>
- <td class="author">{author|obfuscate}</td>
-</tr>
-<tr>
- <th class="date">date</th>
- <td class="date">{date|date} ({date|age} ago)</td>
-</tr>
-<tr>
- <th class="author">parents</th>
- <td class="author">{parent%filerevparent}</td>
-</tr>
-<tr>
- <th class="author">children</th>
- <td class="author">{child%filerevchild}</td>
-</tr>
-{changesettag}
-</table>
-
-<div class="overflow">
-<div class="sourcefirst"> line source</div>
-{text%fileline}
-<div class="sourcelast"></div>
-</div>
-</div>
-</div>
-
-{footer}
--- a/sys/src/cmd/hg/templates/paper/footer.tmpl
+++ /dev/null
@@ -1,4 +1,0 @@
-{motd}
-
-</body>
-</html>
--- a/sys/src/cmd/hg/templates/paper/graph.tmpl
+++ /dev/null
@@ -1,132 +1,0 @@
-{header}
-<title>{repo|escape}: revision graph</title>
-<link rel="alternate" type="application/atom+xml"
- href="{url}atom-log" title="Atom feed for {repo|escape}: log" />
-<link rel="alternate" type="application/rss+xml"
- href="{url}rss-log" title="RSS feed for {repo|escape}: log" />
-<!--[if IE]><script type="text/javascript" src="{staticurl}excanvas.js"></script><![endif]-->
-</head>
-<body>
-
-<div class="container">
-<div class="menu">
-<div class="logo">
-<a href="http://mercurial.selenic.com/">
-<img src="{staticurl}hglogo.png" alt="mercurial" /></a>
-</div>
-<ul>
-<li><a href="{url}shortlog/{node|short}{sessionvars%urlparameter}">log</a></li>
-<li class="active">graph</li>
-<li><a href="{url}tags{sessionvars%urlparameter}">tags</a></li>
-<li><a href="{url}branches{sessionvars%urlparameter}">branches</a></li>
-</ul>
-<ul>
-<li><a href="{url}rev/{node|short}{sessionvars%urlparameter}">changeset</a></li>
-<li><a href="{url}file/{node|short}{path|urlescape}{sessionvars%urlparameter}">browse</a></li>
-</ul>
-</div>
-
-<div class="main">
-<h2><a href="{url}{sessionvars%urlparameter}">{repo|escape}</a></h2>
-<h3>graph</h3>
-
-<form class="search" action="{url}log">
-{sessionvars%hiddenformentry}
-<p><input name="rev" id="search1" type="text" size="30" /></p>
-<div id="hint">find changesets by author, revision,
-files, or words in the commit message</div>
-</form>
-
-<div class="navigate">
-<a href="{url}graph/{rev}{lessvars%urlparameter}">less</a>
-<a href="{url}graph/{rev}{morevars%urlparameter}">more</a>
-| rev {rev}: {changenav%navgraphentry}
-</div>
-
-<noscript><p>The revision graph only works with JavaScript-enabled browsers.</p></noscript>
-
-<div id="wrapper">
-<ul id="nodebgs"></ul>
-<canvas id="graph" width="224" height="{canvasheight}"></canvas>
-<ul id="graphnodes"></ul>
-</div>
-
-<script type="text/javascript" src="{staticurl}graph.js"></script>
-<script type="text/javascript">
-<!-- hide script content
-
-var data = {jsdata|json};
-var graph = new Graph();
-graph.scale({bg_height});
-
-graph.edge = function(x0, y0, x1, y1, color) {
-
- this.setColor(color, 0.0, 0.65);
- this.ctx.beginPath();
- this.ctx.moveTo(x0, y0);
- this.ctx.lineTo(x1, y1);
- this.ctx.stroke();
-
-}
-
-var revlink = '<li style="_STYLE"><span class="desc">';
-revlink += '<a href="{url}rev/_NODEID{sessionvars%urlparameter}" title="_NODEID">_DESC</a>';
-revlink += '</span>_TAGS<span class="info">_DATE ago, by _USER</span></li>';
-
-graph.vertex = function(x, y, color, parity, cur) {
-
- this.ctx.beginPath();
- color = this.setColor(color, 0.25, 0.75);
- this.ctx.arc(x, y, radius, 0, Math.PI * 2, true);
- this.ctx.fill();
-
- var bg = '<li class="bg parity' + parity + '"></li>';
- var left = (this.columns + 1) * this.bg_height;
- var nstyle = 'padding-left: ' + left + 'px;';
- var item = revlink.replace(/_STYLE/, nstyle);
- item = item.replace(/_PARITY/, 'parity' + parity);
- item = item.replace(/_NODEID/, cur[0]);
- item = item.replace(/_NODEID/, cur[0]);
- item = item.replace(/_DESC/, cur[3]);
- item = item.replace(/_USER/, cur[4]);
- item = item.replace(/_DATE/, cur[5]);
-
- var tagspan = '';
- if (cur[7].length || (cur[6][0] != 'default' || cur[6][1])) {
- tagspan = '<span class="logtags">';
- if (cur[6][1]) {
- tagspan += '<span class="branchhead" title="' + cur[6][0] + '">';
- tagspan += cur[6][0] + '</span> ';
- } else if (!cur[6][1] && cur[6][0] != 'default') {
- tagspan += '<span class="branchname" title="' + cur[6][0] + '">';
- tagspan += cur[6][0] + '</span> ';
- }
- if (cur[7].length) {
- for (var t in cur[7]) {
- var tag = cur[7][t];
- tagspan += '<span class="tag">' + tag + '</span> ';
- }
- }
- tagspan += '</span>';
- }
-
- item = item.replace(/_TAGS/, tagspan);
- return [bg, item];
-
-}
-
-graph.render(data);
-
-// stop hiding script -->
-</script>
-
-<div class="navigate">
-<a href="{url}graph/{rev}{lessvars%urlparameter}">less</a>
-<a href="{url}graph/{rev}{morevars%urlparameter}">more</a>
-| rev {rev}: {changenav%navgraphentry}
-</div>
-
-</div>
-</div>
-
-{footer}
--- a/sys/src/cmd/hg/templates/paper/header.tmpl
+++ /dev/null
@@ -1,6 +1,0 @@
-<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
-<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en-US">
-<head>
-<link rel="icon" href="{staticurl}hgicon.png" type="image/png" />
-<meta name="robots" content="index, nofollow" />
-<link rel="stylesheet" href="{staticurl}style-paper.css" type="text/css" />
--- a/sys/src/cmd/hg/templates/paper/index.tmpl
+++ /dev/null
@@ -1,26 +1,0 @@
-{header}
-<title>Mercurial repositories index</title>
-</head>
-<body>
-
-<div class="container">
-<div class="menu">
-<a href="http://mercurial.selenic.com/">
-<img src="{staticurl}hglogo.png" width=75 height=90 border=0 alt="mercurial" /></a>
-</div>
-<div class="main">
-<h2>Mercurial Repositories</h2>
-
-<table class="bigtable">
- <tr>
- <th><a href="?sort={sort_name}">Name</a></th>
- <th><a href="?sort={sort_description}">Description</a></th>
- <th><a href="?sort={sort_contact}">Contact</a></th>
- <th><a href="?sort={sort_lastchange}">Last change</a></th>
- <th> </th>
- </tr>
- {entries%indexentry}
-</table>
-</div>
-</div>
-{footer}
--- a/sys/src/cmd/hg/templates/paper/manifest.tmpl
+++ /dev/null
@@ -1,54 +1,0 @@
-{header}
-<title>{repo|escape}: {node|short} {path|escape}</title>
-</head>
-<body>
-
-<div class="container">
-<div class="menu">
-<div class="logo">
-<a href="http://mercurial.selenic.com/">
-<img src="{staticurl}hglogo.png" alt="mercurial" /></a>
-</div>
-<ul>
-<li><a href="{url}shortlog/{node|short}{sessionvars%urlparameter}">log</a></li>
-<li><a href="{url}graph/{node|short}{sessionvars%urlparameter}">graph</a></li>
-<li><a href="{url}tags{sessionvars%urlparameter}">tags</a></li>
-<li><a href="{url}branches{sessionvars%urlparameter}">branches</a></li>
-</ul>
-<ul>
-<li><a href="{url}rev/{node|short}{sessionvars%urlparameter}">changeset</a></li>
-<li class="active">browse</li>
-</ul>
-<ul>
-{archives%archiveentry}
-</ul>
-</div>
-
-<div class="main">
-<h2><a href="{url}{sessionvars%urlparameter}">{repo|escape}</a></h2>
-<h3>directory {path|escape} @ {rev}:{node|short} {tags%changelogtag}</h3>
-
-<form class="search" action="{url}log">
-{sessionvars%hiddenformentry}
-<p><input name="rev" id="search1" type="text" size="30" /></p>
-<div id="hint">find changesets by author, revision,
-files, or words in the commit message</div>
-</form>
-
-<table class="bigtable">
-<tr>
- <th class="name">name</th>
- <th class="size">size</th>
- <th class="permissions">permissions</th>
-</tr>
-<tr class="fileline parity{upparity}">
- <td class="name"><a href="{url}file/{node|short}{up|urlescape}{sessionvars%urlparameter}">[up]</a></td>
- <td class="size"></td>
- <td class="permissions">drwxr-xr-x</td>
-</tr>
-{dentries%direntry}
-{fentries%fileentry}
-</table>
-</div>
-</div>
-{footer}
--- a/sys/src/cmd/hg/templates/paper/map
+++ /dev/null
@@ -1,191 +1,0 @@
-default = 'shortlog'
-
-mimetype = 'text/html; charset={encoding}'
-header = header.tmpl
-footer = footer.tmpl
-search = search.tmpl
-
-changelog = shortlog.tmpl
-shortlog = shortlog.tmpl
-shortlogentry = shortlogentry.tmpl
-graph = graph.tmpl
-
-naventry = '<a href="{url}log/{node|short}{sessionvars%urlparameter}">{label|escape}</a> '
-navshortentry = '<a href="{url}shortlog/{node|short}{sessionvars%urlparameter}">{label|escape}</a> '
-navgraphentry = '<a href="{url}graph/{node|short}{sessionvars%urlparameter}">{label|escape}</a> '
-filenaventry = '<a href="{url}log/{node|short}/{file|urlescape}{sessionvars%urlparameter}">{label|escape}</a> '
-filedifflink = '<a href="{url}diff/{node|short}/{file|urlescape}{sessionvars%urlparameter}">{file|escape}</a> '
-filenodelink = '<a href="{url}file/{node|short}/{file|urlescape}{sessionvars%urlparameter}">{file|escape}</a> '
-filenolink = '{file|escape} '
-fileellipses = '...'
-changelogentry = shortlogentry.tmpl
-searchentry = shortlogentry.tmpl
-changeset = changeset.tmpl
-manifest = manifest.tmpl
-
-direntry = '
- <tr class="fileline parity{parity}">
- <td class="name">
- <a href="{url}file/{node|short}{path|urlescape}{sessionvars%urlparameter}">
- <img src="{staticurl}coal-folder.png" alt="dir."/> {basename|escape}/
- </a>
- <a href="{url}file/{node|short}{path|urlescape}/{emptydirs|urlescape}{sessionvars%urlparameter}">
- {emptydirs|escape}
- </a>
- </td>
- <td class="size"></td>
- <td class="permissions">drwxr-xr-x</td>
- </tr>'
-
-fileentry = '
- <tr class="fileline parity{parity}">
- <td class="filename">
- <a href="{url}file/{node|short}/{file|urlescape}{sessionvars%urlparameter}">
- <img src="{staticurl}coal-file.png" alt="file"/> {basename|escape}
- </a>
- </td>
- <td class="size">{size}</td>
- <td class="permissions">{permissions|permissions}</td>
- </tr>'
-
-filerevision = filerevision.tmpl
-fileannotate = fileannotate.tmpl
-filediff = filediff.tmpl
-filelog = filelog.tmpl
-fileline = '
- <div class="parity{parity} source"><a href="#{lineid}" id="{lineid}">{linenumber}</a> {line|escape}</div>'
-filelogentry = filelogentry.tmpl
-
-annotateline = '
- <tr class="parity{parity}">
- <td class="annotate">
- <a href="{url}annotate/{node|short}/{file|urlescape}{sessionvars%urlparameter}#{targetline}"
- title="{node|short}: {desc|escape|firstline}">{author|user}@{rev}</a>
- </td>
- <td class="source"><a href="#{lineid}" id="{lineid}">{linenumber}</a> {line|escape}</td>
- </tr>'
-
-diffblock = '<div class="source bottomline parity{parity}"><pre>{lines}</pre></div>'
-difflineplus = '<a href="#{lineid}" id="{lineid}">{linenumber}</a> <span class="plusline">{line|escape}</span>'
-difflineminus = '<a href="#{lineid}" id="{lineid}">{linenumber}</a> <span class="minusline">{line|escape}</span>'
-difflineat = '<a href="#{lineid}" id="{lineid}">{linenumber}</a> <span class="atline">{line|escape}</span>'
-diffline = '<a href="#{lineid}" id="{lineid}">{linenumber}</a> {line|escape}'
-
-changelogparent = '
- <tr>
- <th class="parent">parent {rev}:</th>
- <td class="parent"><a href="{url}rev/{node|short}{sessionvars%urlparameter}">{node|short}</a></td>
- </tr>'
-
-changesetparent = '<a href="{url}rev/{node|short}{sessionvars%urlparameter}">{node|short}</a> '
-
-filerevparent = '<a href="{url}file/{node|short}/{file|urlescape}{sessionvars%urlparameter}">{rename%filerename}{node|short}</a> '
-filerevchild = '<a href="{url}file/{node|short}/{file|urlescape}{sessionvars%urlparameter}">{node|short}</a> '
-
-filerename = '{file|escape}@'
-filelogrename = '
- <tr>
- <th>base:</th>
- <td>
- <a href="{url}file/{node|short}/{file|urlescape}{sessionvars%urlparameter}">
- {file|escape}@{node|short}
- </a>
- </td>
- </tr>'
-fileannotateparent = '
- <tr>
- <td class="metatag">parent:</td>
- <td>
- <a href="{url}annotate/{node|short}/{file|urlescape}{sessionvars%urlparameter}">
- {rename%filerename}{node|short}
- </a>
- </td>
- </tr>'
-changesetchild = ' <a href="{url}rev/{node|short}{sessionvars%urlparameter}">{node|short}</a>'
-changelogchild = '
- <tr>
- <th class="child">child</th>
- <td class="child">
- <a href="{url}rev/{node|short}{sessionvars%urlparameter}">
- {node|short}
- </a>
- </td>
- </tr>'
-fileannotatechild = '
- <tr>
- <td class="metatag">child:</td>
- <td>
- <a href="{url}annotate/{node|short}/{file|urlescape}{sessionvars%urlparameter}">
- {node|short}
- </a>
- </td>
- </tr>'
-tags = tags.tmpl
-tagentry = '
- <tr class="tagEntry parity{parity}">
- <td>
- <a href="{url}rev/{node|short}{sessionvars%urlparameter}">
- {tag|escape}
- </a>
- </td>
- <td class="node">
- {node|short}
- </td>
- </tr>'
-branches = branches.tmpl
-branchentry = '
- <tr class="tagEntry parity{parity}">
- <td>
- <a href="{url}shortlog/{node|short}{sessionvars%urlparameter}" class="{status}">
- {branch|escape}
- </a>
- </td>
- <td class="node">
- {node|short}
- </td>
- </tr>'
-changelogtag = '<span class="tag">{name|escape}</span> '
-changesettag = '<span class="tag">{tag|escape}</span> '
-changelogbranchhead = '<span class="branchhead">{name|escape}</span> '
-changelogbranchname = '<span class="branchname">{name|escape}</span> '
-
-filediffparent = '
- <tr>
- <th class="parent">parent {rev}:</th>
- <td class="parent"><a href="{url}rev/{node|short}{sessionvars%urlparameter}">{node|short}</a></td>
- </tr>'
-filelogparent = '
- <tr>
- <th>parent {rev}:</th>
- <td><a href="{url}file/{node|short}/{file|urlescape}{sessionvars%urlparameter}">{node|short}</a></td>
- </tr>'
-filediffchild = '
- <tr>
- <th class="child">child {rev}:</th>
- <td class="child"><a href="{url}rev/{node|short}{sessionvars%urlparameter}">{node|short}</a>
- </td>
- </tr>'
-filelogchild = '
- <tr>
- <th>child {rev}:</th>
- <td><a href="{url}file/{node|short}/{file|urlescape}{sessionvars%urlparameter}">{node|short}</a></td>
- </tr>'
-
-indexentry = '
- <tr class="parity{parity}">
- <td><a href="{url}{sessionvars%urlparameter}">{name|escape}</a></td>
- <td>{description}</td>
- <td>{contact|obfuscate}</td>
- <td class="age">{lastchange|age} ago</td>
- <td class="indexlinks">{archives%indexarchiveentry}</td>
- </tr>\n'
-indexarchiveentry = '<a href="{url}archive/{node|short}{extension|urlescape}"> ↓{type|escape}</a>'
-index = index.tmpl
-archiveentry = '
- <li>
- <a href="{url}archive/{node|short}{extension|urlescape}">{type|escape}</a>
- </li>'
-notfound = notfound.tmpl
-error = error.tmpl
-urlparameter = '{separator}{name}={value|urlescape}'
-hiddenformentry = '<input type="hidden" name="{name}" value="{value|escape}" />'
--- a/sys/src/cmd/hg/templates/paper/notfound.tmpl
+++ /dev/null
@@ -1,12 +1,0 @@
-{header}
-<title>Mercurial repository not found</title>
-</head>
-<body>
-
-<h2>Mercurial repository not found</h2>
-
-The specified repository "{repo|escape}" is unknown, sorry.
-
-Please go back to the <a href="{url}">main repository list page</a>.
-
-{footer}
--- a/sys/src/cmd/hg/templates/paper/search.tmpl
+++ /dev/null
@@ -1,43 +1,0 @@
-{header}
-<title>{repo|escape}: searching for {query|escape}</title>
-</head>
-<body>
-
-<div class="container">
-<div class="menu">
-<div class="logo">
-<a href="http://mercurial.selenic.com/">
-<img src="{staticurl}hglogo.png" width=75 height=90 border=0 alt="mercurial"></a>
-</div>
-<ul>
-<li><a href="{url}shortlog{sessionvars%urlparameter}">log</a></li>
-<li><a href="{url}graph{sessionvars%urlparameter}">graph</a></li>
-<li><a href="{url}tags{sessionvars%urlparameter}">tags</a></li>
-<li><a href="{url}branches{sessionvars%urlparameter}">branches</a></li>
-</ul>
-</div>
-
-<div class="main">
-<h2><a href="{url}{sessionvars%urlparameter}">{repo|escape}</a></h2>
-<h3>searching for '{query|escape}'</h3>
-
-<form class="search" action="{url}log">
-{sessionvars%hiddenformentry}
-<p><input name="rev" id="search1" type="text" size="30"></p>
-<div id="hint">find changesets by author, revision,
-files, or words in the commit message</div>
-</form>
-
-<table class="bigtable">
- <tr>
- <th class="age">age</th>
- <th class="author">author</th>
- <th class="description">description</th>
- </tr>
-{entries}
-</table>
-
-</div>
-</div>
-
-{footer}
--- a/sys/src/cmd/hg/templates/paper/shortlog.tmpl
+++ /dev/null
@@ -1,57 +1,0 @@
-{header}
-<title>{repo|escape}: log</title>
-<link rel="alternate" type="application/atom+xml"
- href="{url}atom-log" title="Atom feed for {repo|escape}" />
-<link rel="alternate" type="application/rss+xml"
- href="{url}rss-log" title="RSS feed for {repo|escape}" />
-</head>
-<body>
-
-<div class="container">
-<div class="menu">
-<div class="logo">
-<a href="http://mercurial.selenic.com/">
-<img src="{staticurl}hglogo.png" alt="mercurial" /></a>
-</div>
-<ul>
-<li class="active">log</li>
-<li><a href="{url}graph/{node|short}{sessionvars%urlparameter}">graph</a></li>
-<li><a href="{url}tags{sessionvars%urlparameter}">tags</a></li>
-<li><a href="{url}branches{sessionvars%urlparameter}">branches</a></li>
-</ul>
-<ul>
-<li><a href="{url}rev/{node|short}{sessionvars%urlparameter}">changeset</a></li>
-<li><a href="{url}file/{node|short}{path|urlescape}{sessionvars%urlparameter}">browse</a></li>
-</ul>
-<ul>
-{archives%archiveentry}
-</ul>
-</div>
-
-<div class="main">
-<h2><a href="{url}{sessionvars%urlparameter}">{repo|escape}</a></h2>
-<h3>log</h3>
-
-<form class="search" action="{url}log">
-{sessionvars%hiddenformentry}
-<p><input name="rev" id="search1" type="text" size="30" /></p>
-<div id="hint">find changesets by author, revision,
-files, or words in the commit message</div>
-</form>
-
-<div class="navigate">rev {rev}: {changenav%navshortentry}</div>
-
-<table class="bigtable">
- <tr>
- <th class="age">age</th>
- <th class="author">author</th>
- <th class="description">description</th>
- </tr>
-{entries%shortlogentry}
-</table>
-
-<div class="navigate">rev {rev}: {changenav%navshortentry}</div>
-</div>
-</div>
-
-{footer}
--- a/sys/src/cmd/hg/templates/paper/shortlogentry.tmpl
+++ /dev/null
@@ -1,5 +1,0 @@
- <tr class="parity{parity}">
- <td class="age">{date|age}</td>
- <td class="author">{author|person}</td>
- <td class="description"><a href="{url}rev/{node|short}{sessionvars%urlparameter}">{desc|strip|firstline|escape|nonempty}</a>{inbranch%changelogbranchname}{branches%changelogbranchhead}{tags%changelogtag}</td>
- </tr>
--- a/sys/src/cmd/hg/templates/paper/tags.tmpl
+++ /dev/null
@@ -1,45 +1,0 @@
-{header}
-<title>{repo|escape}: tags</title>
-<link rel="alternate" type="application/atom+xml"
- href="{url}atom-tags" title="Atom feed for {repo|escape}: tags" />
-<link rel="alternate" type="application/rss+xml"
- href="{url}rss-tags" title="RSS feed for {repo|escape}: tags" />
-</head>
-<body>
-
-<div class="container">
-<div class="menu">
-<div class="logo">
-<a href="http://mercurial.selenic.com/">
-<img src="{staticurl}hglogo.png" alt="mercurial" /></a>
-</div>
-<ul>
-<li><a href="{url}shortlog{sessionvars%urlparameter}">log</a></li>
-<li><a href="{url}graph{sessionvars%urlparameter}">graph</a></li>
-<li class="active">tags</li>
-<li><a href="{url}branches{sessionvars%urlparameter}">branches</a></li>
-</ul>
-</div>
-
-<div class="main">
-<h2><a href="{url}{sessionvars%urlparameter}">{repo|escape}</a></h2>
-<h3>tags</h3>
-
-<form class="search" action="{url}log">
-{sessionvars%hiddenformentry}
-<p><input name="rev" id="search1" type="text" size="30" /></p>
-<div id="hint">find changesets by author, revision,
-files, or words in the commit message</div>
-</form>
-
-<table class="bigtable">
-<tr>
- <th>tag</th>
- <th>node</th>
-</tr>
-{entries%tagentry}
-</table>
-</div>
-</div>
-
-{footer}
--- a/sys/src/cmd/hg/templates/raw/changeset.tmpl
+++ /dev/null
@@ -1,9 +1,0 @@
-{header}
-# HG changeset patch
-# User {author}
-# Date {date|hgdate}
-# Node ID {node}
-{parent%changesetparent}
-{desc}
-
-{diff}
--- a/sys/src/cmd/hg/templates/raw/error.tmpl
+++ /dev/null
@@ -1,2 +1,0 @@
-{header}
-error: {error}
--- a/sys/src/cmd/hg/templates/raw/fileannotate.tmpl
+++ /dev/null
@@ -1,5 +1,0 @@
-{header}
-{annotate%annotateline}
-{footer}
-
-
--- a/sys/src/cmd/hg/templates/raw/filediff.tmpl
+++ /dev/null
@@ -1,5 +1,0 @@
-{header}
-{diff}
-{footer}
-
-
--- a/sys/src/cmd/hg/templates/raw/index.tmpl
+++ /dev/null
@@ -1,2 +1,0 @@
-{header}
-{entries%indexentry}
--- a/sys/src/cmd/hg/templates/raw/manifest.tmpl
+++ /dev/null
@@ -1,3 +1,0 @@
-{header}
-{dentries%direntry}{fentries%fileentry}
-{footer}
--- a/sys/src/cmd/hg/templates/raw/map
+++ /dev/null
@@ -1,23 +1,0 @@
-mimetype = 'text/plain; charset={encoding}'
-header = ''
-footer = ''
-changeset = changeset.tmpl
-difflineplus = '{line}'
-difflineminus = '{line}'
-difflineat = '{line}'
-diffline = '{line}'
-changesetparent = '# Parent {node}'
-changesetchild = '# Child {node}'
-filenodelink = ''
-fileline = '{line}'
-diffblock = '{lines}'
-filediff = filediff.tmpl
-fileannotate = fileannotate.tmpl
-annotateline = '{author|user}@{rev}: {line}'
-manifest = manifest.tmpl
-direntry = 'drwxr-xr-x {basename}\n'
-fileentry = '{permissions|permissions} {size} {basename}\n'
-index = index.tmpl
-notfound = notfound.tmpl
-error = error.tmpl
-indexentry = '{url}\n'
--- a/sys/src/cmd/hg/templates/raw/notfound.tmpl
+++ /dev/null
@@ -1,2 +1,0 @@
-{header}
-error: repository {repo} not found
--- a/sys/src/cmd/hg/templates/rss/changelog.tmpl
+++ /dev/null
@@ -1,6 +1,0 @@
-{header}
- <title>{repo|escape} Changelog</title>
- <description>{repo|escape} Changelog</description>
- {entries%changelogentry}
- </channel>
-</rss>
\ No newline at end of file
--- a/sys/src/cmd/hg/templates/rss/changelogentry.tmpl
+++ /dev/null
@@ -1,7 +1,0 @@
-<item>
- <title>{desc|strip|firstline|strip|escape}</title>
- <guid isPermaLink="true">{urlbase}{url}rev/{node|short}</guid>
- <description><![CDATA[{desc|strip|escape|addbreaks|nonempty}]]></description>
- <author>{author|obfuscate}</author>
- <pubDate>{date|rfc822date}</pubDate>
-</item>
--- a/sys/src/cmd/hg/templates/rss/error.tmpl
+++ /dev/null
@@ -1,10 +1,0 @@
-{header}
- <title>Error</title>
- <description>Error</description>
- <item>
- <title>Error</title>
- <description>{error|escape}</description>
- <guid>http://mercurial.selenic.com/#error</guid>
- </item>
- </channel>
-</rss>
--- a/sys/src/cmd/hg/templates/rss/filelog.tmpl
+++ /dev/null
@@ -1,6 +1,0 @@
-{header}
- <title>{repo|escape}: {file|escape} history</title>
- <description>{file|escape} revision history</description>
- {entries%filelogentry}
- </channel>
-</rss>
--- a/sys/src/cmd/hg/templates/rss/filelogentry.tmpl
+++ /dev/null
@@ -1,7 +1,0 @@
-<item>
- <title>{desc|strip|firstline|strip|escape}</title>
- <link>{urlbase}{url}log{{node|short}}/{file|urlescape}</link>
- <description><![CDATA[{desc|strip|escape|addbreaks|nonempty}]]></description>
- <author>{author|obfuscate}</author>
- <pubDate>{date|rfc822date}</pubDate>
-</item>
--- a/sys/src/cmd/hg/templates/rss/header.tmpl
+++ /dev/null
@@ -1,5 +1,0 @@
-<?xml version="1.0" encoding="{encoding}"?>
-<rss version="2.0">
- <channel>
- <link>{urlbase}{url}</link>
- <language>en-us</language>
--- a/sys/src/cmd/hg/templates/rss/map
+++ /dev/null
@@ -1,10 +1,0 @@
-default = 'changelog'
-mimetype = 'text/xml; charset={encoding}'
-header = header.tmpl
-changelog = changelog.tmpl
-changelogentry = changelogentry.tmpl
-filelog = filelog.tmpl
-filelogentry = filelogentry.tmpl
-tags = tags.tmpl
-tagentry = tagentry.tmpl
-error = error.tmpl
--- a/sys/src/cmd/hg/templates/rss/tagentry.tmpl
+++ /dev/null
@@ -1,6 +1,0 @@
-<item>
- <title>{tag|escape}</title>
- <link>{urlbase}{url}rev/{node|short}</link>
- <description><![CDATA[{tag|strip|escape|addbreaks}]]></description>
- <pubDate>{date|rfc822date}</pubDate>
-</item>
--- a/sys/src/cmd/hg/templates/rss/tags.tmpl
+++ /dev/null
@@ -1,6 +1,0 @@
-{header}
- <title>{repo|escape}: tags </title>
- <description>{repo|escape} tag history</description>
- {entriesnotip%tagentry}
- </channel>
-</rss>
--- a/sys/src/cmd/hg/templates/spartan/branches.tmpl
+++ /dev/null
@@ -1,26 +1,0 @@
-{header}
-<title>{repo|escape}: branches</title>
-<link rel="alternate" type="application/atom+xml"
- href="{url}atom-branches" title="Atom feed for {repo|escape}: branches">
-<link rel="alternate" type="application/rss+xml"
- href="{url}rss-branches" title="RSS feed for {repo|escape}: branches">
-</head>
-<body>
-
-<div class="buttons">
-<a href="{url}log{sessionvars%urlparameter}">changelog</a>
-<a href="{url}shortlog{sessionvars%urlparameter}">shortlog</a>
-<a href="{url}graph{sessionvars%urlparameter}">graph</a>
-<a href="{url}tags{sessionvars%urlparameter}">tags</a>
-<a href="{url}file/{node|short}/{sessionvars%urlparameter}">files</a>
-<a type="application/rss+xml" href="{url}rss-branches">rss</a>
-<a type="application/atom+xml" href="{url}atom-branches">atom</a>
-</div>
-
-<h2>branches:</h2>
-
-<ul id="tagEntries">
-{entries%branchentry}
-</ul>
-
-{footer}
--- a/sys/src/cmd/hg/templates/spartan/changelog.tmpl
+++ /dev/null
@@ -1,43 +1,0 @@
-{header}
-<title>{repo|escape}: changelog</title>
-<link rel="alternate" type="application/atom+xml"
- href="{url}atom-log" title="Atom feed for {repo|escape}">
-<link rel="alternate" type="application/rss+xml"
- href="{url}rss-log" title="RSS feed for {repo|escape}">
-</head>
-<body>
-
-<div class="buttons">
-<a href="{url}shortlog/{rev}{sessionvars%urlparameter}">shortlog</a>
-<a href="{url}graph{sessionvars%urlparameter}">graph</a>
-<a href="{url}tags{sessionvars%urlparameter}">tags</a>
-<a href="{url}branches{sessionvars%urlparameter}">branches</a>
-<a href="{url}file/{node|short}{sessionvars%urlparameter}">files</a>
-{archives%archiveentry}
-<a type="application/rss+xml" href="{url}rss-log">rss</a>
-<a type="application/atom+xml" href="{url}atom-log" title="Atom feed for {repo|escape}">atom</a>
-</div>
-
-<h2>changelog for {repo|escape}</h2>
-
-<form action="{url}log">
-{sessionvars%hiddenformentry}
-<p>
-<label for="search1">search:</label>
-<input name="rev" id="search1" type="text" size="30">
-navigate: <small class="navigate">{changenav%naventry}</small>
-</p>
-</form>
-
-{entries%changelogentry}
-
-<form action="{url}log">
-{sessionvars%hiddenformentry}
-<p>
-<label for="search2">search:</label>
-<input name="rev" id="search2" type="text" size="30">
-navigate: <small class="navigate">{changenav%naventry}</small>
-</p>
-</form>
-
-{footer}
--- a/sys/src/cmd/hg/templates/spartan/changelogentry.tmpl
+++ /dev/null
@@ -1,25 +1,0 @@
-<table class="logEntry parity{parity}">
- <tr>
- <th class="age">{date|age} ago:</th>
- <th class="firstline">{desc|strip|firstline|escape|nonempty}</th>
- </tr>
- <tr>
- <th class="revision">changeset {rev}:</th>
- <td class="node"><a href="{url}rev/{node|short}{sessionvars%urlparameter}">{node|short}</a></td>
- </tr>
- {parent%changelogparent}
- {child%changelogchild}
- {changelogtag}
- <tr>
- <th class="author">author:</th>
- <td class="author">{author|obfuscate}</td>
- </tr>
- <tr>
- <th class="date">date:</th>
- <td class="date">{date|date}</td>
- </tr>
- <tr>
- <th class="files"><a href="{url}file/{node|short}{sessionvars%urlparameter}">files</a>:</th>
- <td class="files">{files}</td>
- </tr>
-</table>
--- a/sys/src/cmd/hg/templates/spartan/changeset.tmpl
+++ /dev/null
@@ -1,51 +1,0 @@
-{header}
-<title>{repo|escape}: changeset {node|short}</title>
-</head>
-<body>
-
-<div class="buttons">
-<a href="{url}log/{rev}{sessionvars%urlparameter}">changelog</a>
-<a href="{url}shortlog/{rev}{sessionvars%urlparameter}">shortlog</a>
-<a href="{url}graph{sessionvars%urlparameter}">graph</a>
-<a href="{url}tags{sessionvars%urlparameter}">tags</a>
-<a href="{url}branches{sessionvars%urlparameter}">branches</a>
-<a href="{url}file/{node|short}{sessionvars%urlparameter}">files</a>
-<a href="{url}raw-rev/{node|short}">raw</a>
-{archives%archiveentry}
-</div>
-
-<h2>changeset: {desc|strip|escape|firstline|nonempty}</h2>
-
-<table id="changesetEntry">
-<tr>
- <th class="changeset">changeset {rev}:</th>
- <td class="changeset"><a href="{url}rev/{node|short}{sessionvars%urlparameter}">{node|short}</a></td>
-</tr>
-{parent%changesetparent}
-{child%changesetchild}
-{changesettag}
-<tr>
- <th class="author">author:</th>
- <td class="author">{author|obfuscate}</td>
-</tr>
-<tr>
- <th class="date">date:</th>
- <td class="date">{date|date} ({date|age} ago)</td>
-</tr>
-<tr>
- <th class="files">files:</th>
- <td class="files">{files}</td>
-</tr>
-<tr>
- <th class="description">description:</th>
- <td class="description">{desc|strip|escape|addbreaks|nonempty}</td>
-</tr>
-</table>
-
-<div id="changesetDiff">
-{diff}
-</div>
-
-{footer}
-
-
--- a/sys/src/cmd/hg/templates/spartan/error.tmpl
+++ /dev/null
@@ -1,15 +1,0 @@
-{header}
-<title>Mercurial Error</title>
-</head>
-<body>
-
-<h2>Mercurial Error</h2>
-
-<p>
-An error occurred while processing your request:
-</p>
-<p>
-{error|escape}
-</p>
-
-{footer}
--- a/sys/src/cmd/hg/templates/spartan/fileannotate.tmpl
+++ /dev/null
@@ -1,48 +1,0 @@
-{header}
-<title>{repo|escape}: {file|escape} annotate</title>
-</head>
-<body>
-
-<div class="buttons">
-<a href="{url}log/{rev}{sessionvars%urlparameter}">changelog</a>
-<a href="{url}shortlog/{rev}{sessionvars%urlparameter}">shortlog</a>
-<a href="{url}graph{sessionvars%urlparameter}">graph</a>
-<a href="{url}tags{sessionvars%urlparameter}">tags</a>
-<a href="{url}branches{sessionvars%urlparameter}">branches</a>
-<a href="{url}rev/{node|short}{sessionvars%urlparameter}">changeset</a>
-<a href="{url}file/{node|short}{path|urlescape}{sessionvars%urlparameter}">files</a>
-<a href="{url}file/{node|short}/{file|urlescape}{sessionvars%urlparameter}">file</a>
-<a href="{url}log/{node|short}/{file|urlescape}{sessionvars%urlparameter}">revisions</a>
-<a href="{url}raw-annotate/{node|short}/{file|urlescape}">raw</a>
-</div>
-
-<h2>Annotate {file|escape}</h2>
-
-<table>
-<tr>
- <td class="metatag">changeset {rev}:</td>
- <td><a href="{url}rev/{node|short}{sessionvars%urlparameter}">{node|short}</a></td></tr>
-{parent%fileannotateparent}
-{child%fileannotatechild}
-<tr>
- <td class="metatag">author:</td>
- <td>{author|obfuscate}</td></tr>
-<tr>
- <td class="metatag">date:</td>
- <td>{date|date} ({date|age} ago)</td>
-</tr>
-<tr>
- <td class="metatag">permissions:</td>
- <td>{permissions|permissions}</td>
-</tr>
-<tr>
- <td class="metatag">description:</td>
- <td>{desc|strip|escape|addbreaks|nonempty}</td>
-</tr>
-</table>
-
-<table cellspacing="0" cellpadding="0">
-{annotate%annotateline}
-</table>
-
-{footer}
--- a/sys/src/cmd/hg/templates/spartan/filediff.tmpl
+++ /dev/null
@@ -1,36 +1,0 @@
-{header}
-<title>{repo|escape}: {file|escape} diff</title>
-</head>
-<body>
-
-<div class="buttons">
-<a href="{url}log/{rev}{sessionvars%urlparameter}">changelog</a>
-<a href="{url}shortlog/{rev}{sessionvars%urlparameter}">shortlog</a>
-<a href="{url}graph{sessionvars%urlparameter}">graph</a>
-<a href="{url}tags{sessionvars%urlparameter}">tags</a>
-<a href="{url}branches{sessionvars%urlparameter}">branches</a>
-<a href="{url}rev/{node|short}{sessionvars%urlparameter}">changeset</a>
-<a href="{url}file/{node|short}/{file|urlescape}{sessionvars%urlparameter}">file</a>
-<a href="{url}log/{node|short}/{file|urlescape}{sessionvars%urlparameter}">revisions</a>
-<a href="{url}annotate/{node|short}/{file|urlescape}{sessionvars%urlparameter}">annotate</a>
-<a href="{url}raw-diff/{node|short}/{file|urlescape}">raw</a>
-</div>
-
-<h2>{file|escape}</h2>
-
-<table id="filediffEntry">
-<tr>
- <th class="revision">revision {rev}:</th>
- <td class="revision"><a href="{url}rev/{node|short}{sessionvars%urlparameter}">{node|short}</a></td>
-</tr>
-{parent%filediffparent}
-{child%filediffchild}
-</table>
-
-<div id="fileDiff">
-{diff}
-</div>
-
-{footer}
-
-
--- a/sys/src/cmd/hg/templates/spartan/filelog.tmpl
+++ /dev/null
@@ -1,28 +1,0 @@
-{header}
-<title>{repo|escape}: {file|escape} history</title>
-<link rel="alternate" type="application/atom+xml"
- href="{url}atom-log/tip/{file|urlescape}" title="Atom feed for {repo|escape}:{file}">
-<link rel="alternate" type="application/rss+xml"
- href="{url}rss-log/tip/{file|urlescape}" title="RSS feed for {repo|escape}:{file}">
-</head>
-<body>
-
-<div class="buttons">
-<a href="{url}log{sessionvars%urlparameter}">changelog</a>
-<a href="{url}shortlog{sessionvars%urlparameter}">shortlog</a>
-<a href="{url}graph{sessionvars%urlparameter}">graph</a>
-<a href="{url}tags{sessionvars%urlparameter}">tags</a>
-<a href="{url}branches{sessionvars%urlparameter}">branches</a>
-<a href="{url}file/{node|short}/{file|urlescape}{sessionvars%urlparameter}">file</a>
-<a href="{url}annotate/{node|short}/{file|urlescape}{sessionvars%urlparameter}">annotate</a>
-<a type="application/rss+xml" href="{url}rss-log/tip/{file|urlescape}">rss</a>
-<a type="application/atom+xml" href="{url}atom-log/tip/{file|urlescape}" title="Atom feed for {repo|escape}:{file}">atom</a>
-</div>
-
-<h2>{file|escape} revision history</h2>
-
-<p>navigate: <small class="navigate">{nav%filenaventry}</small></p>
-
-{entries%filelogentry}
-
-{footer}
--- a/sys/src/cmd/hg/templates/spartan/filelogentry.tmpl
+++ /dev/null
@@ -1,25 +1,0 @@
-<table class="logEntry parity{parity}">
- <tr>
- <th class="age">{date|age} ago:</th>
- <th class="firstline"><a href="{url}rev/{node|short}{sessionvars%urlparameter}">{desc|strip|firstline|escape|nonempty}</a></th>
- </tr>
- <tr>
- <th class="revision">revision {filerev}:</td>
- <td class="node">
- <a href="{url}file/{node|short}/{file|urlescape}{sessionvars%urlparameter}">{node|short}</a>
- <a href="{url}diff/{node|short}/{file|urlescape}{sessionvars%urlparameter}">(diff)</a>
- <a href="{url}annotate/{node|short}/{file|urlescape}{sessionvars%urlparameter}">(annotate)</a>
- </td>
- </tr>
- {rename%filelogrename}
- <tr>
- <th class="author">author:</th>
- <td class="author">{author|obfuscate}</td>
- </tr>
- <tr>
- <th class="date">date:</th>
- <td class="date">{date|date}</td>
- </tr>
-</table>
-
-
--- a/sys/src/cmd/hg/templates/spartan/filerevision.tmpl
+++ /dev/null
@@ -1,46 +1,0 @@
-{header}
-<title>{repo|escape}:{file|escape}</title>
-</head>
-<body>
-
-<div class="buttons">
-<a href="{url}log/{rev}{sessionvars%urlparameter}">changelog</a>
-<a href="{url}shortlog/{rev}{sessionvars%urlparameter}">shortlog</a>
-<a href="{url}graph{sessionvars%urlparameter}">graph</a>
-<a href="{url}tags{sessionvars%urlparameter}">tags</a>
-<a href="{url}branches{sessionvars%urlparameter}">branches</a>
-<a href="{url}rev/{node|short}{sessionvars%urlparameter}">changeset</a>
-<a href="{url}file/{node|short}{path|urlescape}{sessionvars%urlparameter}">files</a>
-<a href="{url}log/{node|short}/{file|urlescape}{sessionvars%urlparameter}">revisions</a>
-<a href="{url}annotate/{node|short}/{file|urlescape}{sessionvars%urlparameter}">annotate</a>
-<a href="{url}raw-file/{node|short}/{file|urlescape}">raw</a>
-</div>
-
-<h2>{file|escape}</h2>
-
-<table>
-<tr>
- <td class="metatag">changeset {rev}:</td>
- <td><a href="{url}rev/{node|short}{sessionvars%urlparameter}">{node|short}</a></td></tr>
-{parent%filerevparent}
-{child%filerevchild}
-<tr>
- <td class="metatag">author:</td>
- <td>{author|obfuscate}</td></tr>
-<tr>
- <td class="metatag">date:</td>
- <td>{date|date} ({date|age} ago)</td></tr>
-<tr>
- <td class="metatag">permissions:</td>
- <td>{permissions|permissions}</td></tr>
-<tr>
- <td class="metatag">description:</td>
- <td>{desc|strip|escape|addbreaks|nonempty}</td>
-</tr>
-</table>
-
-<pre>
-{text%fileline}
-</pre>
-
-{footer}
--- a/sys/src/cmd/hg/templates/spartan/footer.tmpl
+++ /dev/null
@@ -1,8 +1,0 @@
-{motd}
-<div class="logo">
-<a href="http://mercurial.selenic.com/">
-<img src="{staticurl}hglogo.png" width=75 height=90 border=0 alt="mercurial"></a>
-</div>
-
-</body>
-</html>
--- a/sys/src/cmd/hg/templates/spartan/graph.tmpl
+++ /dev/null
@@ -1,96 +1,0 @@
-{header}
-<title>{repo|escape}: graph</title>
-<link rel="alternate" type="application/atom+xml"
- href="{url}atom-tags" title="Atom feed for {repo|escape}: tags">
-<link rel="alternate" type="application/rss+xml"
- href="{url}rss-tags" title="RSS feed for {repo|escape}: tags">
-<!--[if IE]><script type="text/javascript" src="{staticurl}excanvas.js"></script><![endif]-->
-</head>
-<body>
-
-<div class="buttons">
-<a href="{url}log{sessionvars%urlparameter}">changelog</a>
-<a href="{url}shortlog{sessionvars%urlparameter}">shortlog</a>
-<a href="{url}tags{sessionvars%urlparameter}">tags</a>
-<a href="{url}branches{sessionvars%urlparameter}">branches</a>
-<a href="{url}file/{node|short}/{sessionvars%urlparameter}">files</a>
-</div>
-
-<h2>graph</h2>
-
-<form action="{url}log">
-{sessionvars%hiddenformentry}
-<p>
-<label for="search1">search:</label>
-<input name="rev" id="search1" type="text" size="30">
-navigate: <small class="navigate">{changenav%navgraphentry}</small>
-</p>
-</form>
-
-<noscript>The revision graph only works with JavaScript-enabled browsers.</noscript>
-
-<div id="wrapper">
-<ul id="nodebgs"></ul>
-<canvas id="graph" width="224" height="{canvasheight}"></canvas>
-<ul id="graphnodes"></ul>
-</div>
-
-<script type="text/javascript" src="{staticurl}graph.js"></script>
-<script type="text/javascript">
-<!-- hide script content
-
-var data = {jsdata|json};
-var graph = new Graph();
-graph.scale({bg_height});
-
-graph.edge = function(x0, y0, x1, y1, color) {
-
- this.setColor(color, 0.0, 0.65);
- this.ctx.beginPath();
- this.ctx.moveTo(x0, y0);
- this.ctx.lineTo(x1, y1);
- this.ctx.stroke();
-
-}
-
-var revlink = '<li style="_STYLE"><span class="desc">';
-revlink += '<a href="{url}rev/_NODEID{sessionvars%urlparameter}" title="_NODEID">_DESC</a>';
-revlink += '</span><span class="info">_DATE ago, by _USER</span></li>';
-
-graph.vertex = function(x, y, color, parity, cur) {
-
- this.ctx.beginPath();
- color = this.setColor(color, 0.25, 0.75);
- this.ctx.arc(x, y, radius, 0, Math.PI * 2, true);
- this.ctx.fill();
-
- var bg = '<li class="bg parity' + parity + '"></li>';
- var left = (this.columns + 1) * this.bg_height;
- var nstyle = 'padding-left: ' + left + 'px;';
- var item = revlink.replace(/_STYLE/, nstyle);
- item = item.replace(/_PARITY/, 'parity' + parity);
- item = item.replace(/_NODEID/, cur[0]);
- item = item.replace(/_NODEID/, cur[0]);
- item = item.replace(/_DESC/, cur[3]);
- item = item.replace(/_USER/, cur[4]);
- item = item.replace(/_DATE/, cur[5]);
-
- return [bg, item];
-
-}
-
-graph.render(data);
-
-// stop hiding script -->
-</script>
-
-<form action="{url}log">
-{sessionvars%hiddenformentry}
-<p>
-<label for="search1">search:</label>
-<input name="rev" id="search1" type="text" size="30">
-navigate: <small class="navigate">{changenav%navgraphentry}</small>
-</p>
-</form>
-
-{footer}
--- a/sys/src/cmd/hg/templates/spartan/header.tmpl
+++ /dev/null
@@ -1,6 +1,0 @@
-<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
-<html>
-<head>
-<link rel="icon" href="{staticurl}hgicon.png" type="image/png">
-<meta name="robots" content="index, nofollow" />
-<link rel="stylesheet" href="{staticurl}style.css" type="text/css" />
--- a/sys/src/cmd/hg/templates/spartan/index.tmpl
+++ /dev/null
@@ -1,19 +1,0 @@
-{header}
-<title>Mercurial repositories index</title>
-</head>
-<body>
-
-<h2>Mercurial Repositories</h2>
-
-<table>
- <tr>
- <td><a href="?sort={sort_name}">Name</a></td>
- <td><a href="?sort={sort_description}">Description</a></td>
- <td><a href="?sort={sort_contact}">Contact</a></td>
- <td><a href="?sort={sort_lastchange}">Last change</a></td>
- <td> </td>
- </tr>
- {entries%indexentry}
-</table>
-
-{footer}
--- a/sys/src/cmd/hg/templates/spartan/manifest.tmpl
+++ /dev/null
@@ -1,28 +1,0 @@
-{header}
-<title>{repo|escape}: files for changeset {node|short}</title>
-</head>
-<body>
-
-<div class="buttons">
-<a href="{url}log/{rev}{sessionvars%urlparameter}">changelog</a>
-<a href="{url}shortlog/{rev}{sessionvars%urlparameter}">shortlog</a>
-<a href="{url}graph{sessionvars%urlparameter}">graph</a>
-<a href="{url}tags{sessionvars%urlparameter}">tags</a>
-<a href="{url}branches{sessionvars%urlparameter}">branches</a>
-<a href="{url}rev/{node|short}{sessionvars%urlparameter}">changeset</a>
-{archives%archiveentry}
-</div>
-
-<h2>files for changeset {node|short}: {path|escape}</h2>
-
-<table cellpadding="0" cellspacing="0">
-<tr class="parity{upparity}">
- <td><tt>drwxr-xr-x</tt>
- <td>
- <td>
- <td><a href="{url}file/{node|short}{up|urlescape}{sessionvars%urlparameter}">[up]</a>
-</tr>
-{dentries%direntry}
-{fentries%fileentry}
-</table>
-{footer}
--- a/sys/src/cmd/hg/templates/spartan/map
+++ /dev/null
@@ -1,178 +1,0 @@
-default = 'shortlog'
-mimetype = 'text/html; charset={encoding}'
-header = header.tmpl
-footer = footer.tmpl
-search = search.tmpl
-changelog = changelog.tmpl
-shortlog = shortlog.tmpl
-shortlogentry = shortlogentry.tmpl
-graph = graph.tmpl
-naventry = '<a href="{url}log/{node|short}{sessionvars%urlparameter}">{label|escape}</a> '
-navshortentry = '<a href="{url}shortlog/{node|short}{sessionvars%urlparameter}">{label|escape}</a> '
-navgraphentry = '<a href="{url}graph/{node|short}{sessionvars%urlparameter}">{label|escape}</a> '
-filenaventry = '<a href="{url}log/{node|short}/{file|urlescape}{sessionvars%urlparameter}">{label|escape}</a> '
-filedifflink = '<a href="{url}diff/{node|short}/{file|urlescape}{sessionvars%urlparameter}">{file|escape}</a> '
-filenodelink = '<a href="{url}file/{node|short}/{file|urlescape}{sessionvars%urlparameter}">{file|escape}</a> '
-filenolink = '{file|escape} '
-fileellipses = '...'
-changelogentry = changelogentry.tmpl
-searchentry = changelogentry.tmpl
-changeset = changeset.tmpl
-manifest = manifest.tmpl
-
-direntry = '
- <tr class="parity{parity}">
- <td><tt>drwxr-xr-x</tt>
- <td>
- <td>
- <td>
- <a href="{url}file/{node|short}{path|urlescape}{sessionvars%urlparameter}">{basename|escape}/</a>
- <a href="{url}file/{node|short}{path|urlescape}/{emptydirs|urlescape}{sessionvars%urlparameter}">
- {emptydirs|urlescape}
- </a>'
-
-fileentry = '
- <tr class="parity{parity}">
- <td><tt>{permissions|permissions}</tt>
- <td align=right><tt class="date">{date|isodate}</tt>
- <td align=right><tt>{size}</tt>
- <td><a href="{url}file/{node|short}/{file|urlescape}{sessionvars%urlparameter}">{basename|escape}</a>'
-
-filerevision = filerevision.tmpl
-fileannotate = fileannotate.tmpl
-filediff = filediff.tmpl
-filelog = filelog.tmpl
-fileline = '<div class="parity{parity}"><a class="lineno" href="#{lineid}" id="{lineid}">{linenumber}</a> {line|escape}</div>'
-filelogentry = filelogentry.tmpl
-
-# The ensures that all table cells have content (even if there
-# is an empty line in the annotated file), which in turn ensures that
-# all table rows have equal height.
-annotateline = '
- <tr class="parity{parity}">
- <td class="annotate">
- <a href="{url}annotate/{node|short}/{file|urlescape}{sessionvars%urlparameter}#l{targetline}"
- title="{node|short}: {desc|escape|firstline}">{author|user}@{rev}</a>
- </td>
- <td>
- <a class="lineno" href="#{lineid}" id="{lineid}">{linenumber}</a>
- </td>
- <td><pre> {line|escape}</pre></td>
- </tr>'
-difflineplus = '<span class="plusline"><a class="lineno" href="#{lineid}" id="{lineid}">{linenumber}</a>{line|escape}</span>'
-difflineminus = '<span class="minusline"><a class="lineno" href="#{lineid}" id="{lineid}">{linenumber}</a>{line|escape}</span>'
-difflineat = '<span class="atline"><a class="lineno" href="#{lineid}" id="{lineid}">{linenumber}</a>{line|escape}</span>'
-diffline = '<a class="lineno" href="#{lineid}" id="{lineid}">{linenumber}</a>{line|escape}'
-changelogparent = '
- <tr>
- <th class="parent">parent {rev}:</th>
- <td class="parent">
- <a href="{url}rev/{node|short}{sessionvars%urlparameter}">{node|short}</a>
- </td>
- </tr>'
-changesetparent = '
- <tr>
- <th class="parent">parent {rev}:</th>
- <td class="parent"><a href="{url}rev/{node|short}{sessionvars%urlparameter}">{node|short}</a></td>
- </tr>'
-filerevparent = '
- <tr>
- <td class="metatag">parent:</td>
- <td>
- <a href="{url}file/{node|short}/{file|urlescape}{sessionvars%urlparameter}">
- {rename%filerename}{node|short}
- </a>
- </td>
- </tr>'
-filerename = '{file|escape}@'
-filelogrename = '
- <tr>
- <th>base:</th>
- <td>
- <a href="{url}file/{node|short}/{file|urlescape}{sessionvars%urlparameter}">
- {file|escape}@{node|short}
- </a>
- </td>
- </tr>'
-fileannotateparent = '
- <tr>
- <td class="metatag">parent:</td>
- <td>
- <a href="{url}annotate/{node|short}/{file|urlescape}{sessionvars%urlparameter}">
- {rename%filerename}{node|short}
- </a>
- </td>
- </tr>'
-changesetchild = '
- <tr>
- <th class="child">child {rev}:</th>
- <td class="child"><a href="{url}rev/{node|short}{sessionvars%urlparameter}">{node|short}</a></td>
- </tr>'
-changelogchild = '
- <tr>
- <th class="child">child {rev}:</th>
- <td class="child"><a href="{url}rev/{node|short}{sessionvars%urlparameter}">{node|short}</a></td>
- </tr>'
-filerevchild = '
- <tr>
- <td class="metatag">child:</td>
- <td><a href="{url}file/{node|short}/{file|urlescape}{sessionvars%urlparameter}">{node|short}</a></td>
- </tr>'
-fileannotatechild = '
- <tr>
- <td class="metatag">child:</td>
- <td><a href="{url}annotate/{node|short}/{file|urlescape}{sessionvars%urlparameter}">{node|short}</a></td>
- </tr>'
-tags = tags.tmpl
-tagentry = '
- <li class="tagEntry parity{parity}">
- <tt class="node">{node}</tt>
- <a href="{url}rev/{node|short}{sessionvars%urlparameter}">{tag|escape}</a>
- </li>'
-branches = branches.tmpl
-branchentry = '
- <li class="tagEntry parity{parity}">
- <tt class="node">{node}</tt>
- <a href="{url}shortlog/{node|short}{sessionvars%urlparameter}" class="{status}">{branch|escape}</a>
- </li>'
-diffblock = '<pre class="parity{parity}">{lines}</pre>'
-changelogtag = '<tr><th class="tag">tag:</th><td class="tag">{tag|escape}</td></tr>'
-changesettag = '<tr><th class="tag">tag:</th><td class="tag">{tag|escape}</td></tr>'
-filediffparent = '
- <tr>
- <th class="parent">parent {rev}:</th>
- <td class="parent"><a href="{url}rev/{node|short}{sessionvars%urlparameter}">{node|short}</a></td>
- </tr>'
-filelogparent = '
- <tr>
- <th>parent {rev}:</th>
- <td><a href="{url}file/{node|short}/{file|urlescape}{sessionvars%urlparameter}">{node|short}</a></td>
- </tr>'
-filediffchild = '
- <tr>
- <th class="child">child {rev}:</th>
- <td class="child"><a href="{url}rev/{node|short}{sessionvars%urlparameter}">{node|short}</a></td>
- </tr>'
-filelogchild = '
- <tr>
- <th>child {rev}:</th>
- <td><a href="{url}file/{node|short}/{file|urlescape}{sessionvars%urlparameter}">{node|short}</a></td>
- </tr>'
-indexentry = '
- <tr class="parity{parity}">
- <td><a href="{url}{sessionvars%urlparameter}">{name|escape}</a></td>
- <td>{description}</td>
- <td>{contact|obfuscate}</td>
- <td class="age">{lastchange|age} ago</td>
- <td class="indexlinks">
- <a href="{url}rss-log">RSS</a>
- <a href="{url}atom-log">Atom</a>
- {archives%archiveentry}
- </td>
- </tr>'
-index = index.tmpl
-archiveentry = '<a href="{url}archive/{node|short}{extension|urlescape}">{type|escape}</a> '
-notfound = notfound.tmpl
-error = error.tmpl
-urlparameter = '{separator}{name}={value|urlescape}'
-hiddenformentry = '<input type="hidden" name="{name}" value="{value|escape}" />'
--- a/sys/src/cmd/hg/templates/spartan/notfound.tmpl
+++ /dev/null
@@ -1,12 +1,0 @@
-{header}
-<title>Mercurial repository not found</title>
-</head>
-<body>
-
-<h2>Mercurial repository not found</h2>
-
-The specified repository "{repo|escape}" is unknown, sorry.
-
-Please go back to the <a href="{url}">main repository list page</a>.
-
-{footer}
--- a/sys/src/cmd/hg/templates/spartan/search.tmpl
+++ /dev/null
@@ -1,36 +1,0 @@
-{header}
-<title>{repo|escape}: searching for {query|escape}</title>
-</head>
-<body>
-
-<div class="buttons">
-<a href="{url}log{sessionvars%urlparameter}">changelog</a>
-<a href="{url}shortlog{sessionvars%urlparameter}">shortlog</a>
-<a href="{url}graph{sessionvars%urlparameter}">graph</a>
-<a href="{url}tags{sessionvars%urlparameter}">tags</a>
-<a href="{url}branches{sessionvars%urlparameter}">branches</a>
-<a href="{url}file/{node|short}{sessionvars%urlparameter}">files</a>
-{archives%archiveentry}
-</div>
-
-<h2>searching for {query|escape}</h2>
-
-<form>
-{sessionvars%hiddenformentry}
-<p>
-search:
-<input name="rev" type="text" width="30" value="{query|escape}">
-</p>
-</form>
-
-{entries}
-
-<form>
-{sessionvars%hiddenformentry}
-<p>
-search:
-<input name="rev" type="text" width="30" value="{query|escape}">
-</p>
-</form>
-
-{footer}
--- a/sys/src/cmd/hg/templates/spartan/shortlog.tmpl
+++ /dev/null
@@ -1,43 +1,0 @@
-{header}
-<title>{repo|escape}: shortlog</title>
-<link rel="alternate" type="application/atom+xml"
- href="{url}atom-log" title="Atom feed for {repo|escape}">
-<link rel="alternate" type="application/rss+xml"
- href="{url}rss-log" title="RSS feed for {repo|escape}">
-</head>
-<body>
-
-<div class="buttons">
-<a href="{url}log/{rev}{sessionvars%urlparameter}">changelog</a>
-<a href="{url}graph{sessionvars%urlparameter}">graph</a>
-<a href="{url}tags{sessionvars%urlparameter}">tags</a>
-<a href="{url}branches{sessionvars%urlparameter}">branches</a>
-<a href="{url}file/{node|short}/{sessionvars%urlparameter}">files</a>
-{archives%archiveentry}
-<a type="application/rss+xml" href="{url}rss-log">rss</a>
-<a type="application/rss+xml" href="{url}atom-log" title="Atom feed for {repo|escape}">atom</a>
-</div>
-
-<h2>shortlog for {repo|escape}</h2>
-
-<form action="{url}log">
-{sessionvars%hiddenformentry}
-<p>
-<label for="search1">search:</label>
-<input name="rev" id="search1" type="text" size="30">
-navigate: <small class="navigate">{changenav%navshortentry}</small>
-</p>
-</form>
-
-{entries%shortlogentry}
-
-<form action="{url}log">
-{sessionvars%hiddenformentry}
-<p>
-<label for="search2">search:</label>
-<input name="rev" id="search2" type="text" size="30">
-navigate: <small class="navigate">{changenav%navshortentry}</small>
-</p>
-</form>
-
-{footer}
--- a/sys/src/cmd/hg/templates/spartan/shortlogentry.tmpl
+++ /dev/null
@@ -1,7 +1,0 @@
-<table class="slogEntry parity{parity}">
- <tr>
- <td class="age">{date|age}</td>
- <td class="author">{author|person}</td>
- <td class="node"><a href="{url}rev/{node|short}{sessionvars%urlparameter}">{desc|strip|firstline|escape|nonempty}</a></td>
- </tr>
-</table>
--- a/sys/src/cmd/hg/templates/spartan/tags.tmpl
+++ /dev/null
@@ -1,26 +1,0 @@
-{header}
-<title>{repo|escape}: tags</title>
-<link rel="alternate" type="application/atom+xml"
- href="{url}atom-tags" title="Atom feed for {repo|escape}: tags">
-<link rel="alternate" type="application/rss+xml"
- href="{url}rss-tags" title="RSS feed for {repo|escape}: tags">
-</head>
-<body>
-
-<div class="buttons">
-<a href="{url}log{sessionvars%urlparameter}">changelog</a>
-<a href="{url}shortlog{sessionvars%urlparameter}">shortlog</a>
-<a href="{url}graph{sessionvars%urlparameter}">graph</a>
-<a href="{url}branches{sessionvars%urlparameter}">branches</a>
-<a href="{url}file/{node|short}/{sessionvars%urlparameter}">files</a>
-<a type="application/rss+xml" href="{url}rss-tags">rss</a>
-<a type="application/atom+xml" href="{url}atom-tags">atom</a>
-</div>
-
-<h2>tags:</h2>
-
-<ul id="tagEntries">
-{entries%tagentry}
-</ul>
-
-{footer}
binary files a/sys/src/cmd/hg/templates/static/background.png /dev/null differ
binary files a/sys/src/cmd/hg/templates/static/coal-file.png /dev/null differ
binary files a/sys/src/cmd/hg/templates/static/coal-folder.png /dev/null differ
--- a/sys/src/cmd/hg/templates/static/excanvas.js
+++ /dev/null
@@ -1,19 +1,0 @@
-if(!window.CanvasRenderingContext2D){(function(){var I=Math,i=I.round,L=I.sin,M=I.cos,m=10,A=m/2,Q={init:function(a){var b=a||document;if(/MSIE/.test(navigator.userAgent)&&!window.opera){var c=this;b.attachEvent("onreadystatechange",function(){c.r(b)})}},r:function(a){if(a.readyState=="complete"){if(!a.namespaces["s"]){a.namespaces.add("g_vml_","urn:schemas-microsoft-com:vml")}var b=a.createStyleSheet();b.cssText="canvas{display:inline-block;overflow:hidden;text-align:left;width:300px;height:150px}g_vml_\\:*{behavior:url(#default#VML)}";
-var c=a.getElementsByTagName("canvas");for(var d=0;d<c.length;d++){if(!c[d].getContext){this.initElement(c[d])}}}},q:function(a){var b=a.outerHTML,c=a.ownerDocument.createElement(b);if(b.slice(-2)!="/>"){var d="/"+a.tagName,e;while((e=a.nextSibling)&&e.tagName!=d){e.removeNode()}if(e){e.removeNode()}}a.parentNode.replaceChild(c,a);return c},initElement:function(a){a=this.q(a);a.getContext=function(){if(this.l){return this.l}return this.l=new K(this)};a.attachEvent("onpropertychange",V);a.attachEvent("onresize",
-W);var b=a.attributes;if(b.width&&b.width.specified){a.style.width=b.width.nodeValue+"px"}else{a.width=a.clientWidth}if(b.height&&b.height.specified){a.style.height=b.height.nodeValue+"px"}else{a.height=a.clientHeight}return a}};function V(a){var b=a.srcElement;switch(a.propertyName){case "width":b.style.width=b.attributes.width.nodeValue+"px";b.getContext().clearRect();break;case "height":b.style.height=b.attributes.height.nodeValue+"px";b.getContext().clearRect();break}}function W(a){var b=a.srcElement;
-if(b.firstChild){b.firstChild.style.width=b.clientWidth+"px";b.firstChild.style.height=b.clientHeight+"px"}}Q.init();var R=[];for(var E=0;E<16;E++){for(var F=0;F<16;F++){R[E*16+F]=E.toString(16)+F.toString(16)}}function J(){return[[1,0,0],[0,1,0],[0,0,1]]}function G(a,b){var c=J();for(var d=0;d<3;d++){for(var e=0;e<3;e++){var g=0;for(var h=0;h<3;h++){g+=a[d][h]*b[h][e]}c[d][e]=g}}return c}function N(a,b){b.fillStyle=a.fillStyle;b.lineCap=a.lineCap;b.lineJoin=a.lineJoin;b.lineWidth=a.lineWidth;b.miterLimit=
-a.miterLimit;b.shadowBlur=a.shadowBlur;b.shadowColor=a.shadowColor;b.shadowOffsetX=a.shadowOffsetX;b.shadowOffsetY=a.shadowOffsetY;b.strokeStyle=a.strokeStyle;b.d=a.d;b.e=a.e}function O(a){var b,c=1;a=String(a);if(a.substring(0,3)=="rgb"){var d=a.indexOf("(",3),e=a.indexOf(")",d+1),g=a.substring(d+1,e).split(",");b="#";for(var h=0;h<3;h++){b+=R[Number(g[h])]}if(g.length==4&&a.substr(3,1)=="a"){c=g[3]}}else{b=a}return[b,c]}function S(a){switch(a){case "butt":return"flat";case "round":return"round";
-case "square":default:return"square"}}function K(a){this.a=J();this.m=[];this.k=[];this.c=[];this.strokeStyle="#000";this.fillStyle="#000";this.lineWidth=1;this.lineJoin="miter";this.lineCap="butt";this.miterLimit=m*1;this.globalAlpha=1;this.canvas=a;var b=a.ownerDocument.createElement("div");b.style.width=a.clientWidth+"px";b.style.height=a.clientHeight+"px";b.style.overflow="hidden";b.style.position="absolute";a.appendChild(b);this.j=b;this.d=1;this.e=1}var j=K.prototype;j.clearRect=function(){this.j.innerHTML=
-"";this.c=[]};j.beginPath=function(){this.c=[]};j.moveTo=function(a,b){this.c.push({type:"moveTo",x:a,y:b});this.f=a;this.g=b};j.lineTo=function(a,b){this.c.push({type:"lineTo",x:a,y:b});this.f=a;this.g=b};j.bezierCurveTo=function(a,b,c,d,e,g){this.c.push({type:"bezierCurveTo",cp1x:a,cp1y:b,cp2x:c,cp2y:d,x:e,y:g});this.f=e;this.g=g};j.quadraticCurveTo=function(a,b,c,d){var e=this.f+0.6666666666666666*(a-this.f),g=this.g+0.6666666666666666*(b-this.g),h=e+(c-this.f)/3,l=g+(d-this.g)/3;this.bezierCurveTo(e,
-g,h,l,c,d)};j.arc=function(a,b,c,d,e,g){c*=m;var h=g?"at":"wa",l=a+M(d)*c-A,n=b+L(d)*c-A,o=a+M(e)*c-A,f=b+L(e)*c-A;if(l==o&&!g){l+=0.125}this.c.push({type:h,x:a,y:b,radius:c,xStart:l,yStart:n,xEnd:o,yEnd:f})};j.rect=function(a,b,c,d){this.moveTo(a,b);this.lineTo(a+c,b);this.lineTo(a+c,b+d);this.lineTo(a,b+d);this.closePath()};j.strokeRect=function(a,b,c,d){this.beginPath();this.moveTo(a,b);this.lineTo(a+c,b);this.lineTo(a+c,b+d);this.lineTo(a,b+d);this.closePath();this.stroke()};j.fillRect=function(a,
-b,c,d){this.beginPath();this.moveTo(a,b);this.lineTo(a+c,b);this.lineTo(a+c,b+d);this.lineTo(a,b+d);this.closePath();this.fill()};j.createLinearGradient=function(a,b,c,d){var e=new H("gradient");return e};j.createRadialGradient=function(a,b,c,d,e,g){var h=new H("gradientradial");h.n=c;h.o=g;h.i.x=a;h.i.y=b;return h};j.drawImage=function(a,b){var c,d,e,g,h,l,n,o,f=a.runtimeStyle.width,k=a.runtimeStyle.height;a.runtimeStyle.width="auto";a.runtimeStyle.height="auto";var q=a.width,r=a.height;a.runtimeStyle.width=
-f;a.runtimeStyle.height=k;if(arguments.length==3){c=arguments[1];d=arguments[2];h=(l=0);n=(e=q);o=(g=r)}else if(arguments.length==5){c=arguments[1];d=arguments[2];e=arguments[3];g=arguments[4];h=(l=0);n=q;o=r}else if(arguments.length==9){h=arguments[1];l=arguments[2];n=arguments[3];o=arguments[4];c=arguments[5];d=arguments[6];e=arguments[7];g=arguments[8]}else{throw"Invalid number of arguments";}var s=this.b(c,d),t=[],v=10,w=10;t.push(" <g_vml_:group",' coordsize="',m*v,",",m*w,'"',' coordorigin="0,0"',
-' style="width:',v,";height:",w,";position:absolute;");if(this.a[0][0]!=1||this.a[0][1]){var x=[];x.push("M11='",this.a[0][0],"',","M12='",this.a[1][0],"',","M21='",this.a[0][1],"',","M22='",this.a[1][1],"',","Dx='",i(s.x/m),"',","Dy='",i(s.y/m),"'");var p=s,y=this.b(c+e,d),z=this.b(c,d+g),B=this.b(c+e,d+g);p.x=Math.max(p.x,y.x,z.x,B.x);p.y=Math.max(p.y,y.y,z.y,B.y);t.push("padding:0 ",i(p.x/m),"px ",i(p.y/m),"px 0;filter:progid:DXImageTransform.Microsoft.Matrix(",x.join(""),", sizingmethod='clip');")}else{t.push("top:",
-i(s.y/m),"px;left:",i(s.x/m),"px;")}t.push(' ">','<g_vml_:image src="',a.src,'"',' style="width:',m*e,";"," height:",m*g,';"',' cropleft="',h/q,'"',' croptop="',l/r,'"',' cropright="',(q-h-n)/q,'"',' cropbottom="',(r-l-o)/r,'"'," />","</g_vml_:group>");this.j.insertAdjacentHTML("BeforeEnd",t.join(""))};j.stroke=function(a){var b=[],c=O(a?this.fillStyle:this.strokeStyle),d=c[0],e=c[1]*this.globalAlpha,g=10,h=10;b.push("<g_vml_:shape",' fillcolor="',d,'"',' filled="',Boolean(a),'"',' style="position:absolute;width:',
-g,";height:",h,';"',' coordorigin="0 0" coordsize="',m*g," ",m*h,'"',' stroked="',!a,'"',' strokeweight="',this.lineWidth,'"',' strokecolor="',d,'"',' path="');var l={x:null,y:null},n={x:null,y:null};for(var o=0;o<this.c.length;o++){var f=this.c[o];if(f.type=="moveTo"){b.push(" m ");var k=this.b(f.x,f.y);b.push(i(k.x),",",i(k.y))}else if(f.type=="lineTo"){b.push(" l ");var k=this.b(f.x,f.y);b.push(i(k.x),",",i(k.y))}else if(f.type=="close"){b.push(" x ")}else if(f.type=="bezierCurveTo"){b.push(" c ");
-var k=this.b(f.x,f.y),q=this.b(f.cp1x,f.cp1y),r=this.b(f.cp2x,f.cp2y);b.push(i(q.x),",",i(q.y),",",i(r.x),",",i(r.y),",",i(k.x),",",i(k.y))}else if(f.type=="at"||f.type=="wa"){b.push(" ",f.type," ");var k=this.b(f.x,f.y),s=this.b(f.xStart,f.yStart),t=this.b(f.xEnd,f.yEnd);b.push(i(k.x-this.d*f.radius),",",i(k.y-this.e*f.radius)," ",i(k.x+this.d*f.radius),",",i(k.y+this.e*f.radius)," ",i(s.x),",",i(s.y)," ",i(t.x),",",i(t.y))}if(k){if(l.x==null||k.x<l.x){l.x=k.x}if(n.x==null||k.x>n.x){n.x=k.x}if(l.y==
-null||k.y<l.y){l.y=k.y}if(n.y==null||k.y>n.y){n.y=k.y}}}b.push(' ">');if(typeof this.fillStyle=="object"){var v={x:"50%",y:"50%"},w=n.x-l.x,x=n.y-l.y,p=w>x?w:x;v.x=i(this.fillStyle.i.x/w*100+50)+"%";v.y=i(this.fillStyle.i.y/x*100+50)+"%";var y=[];if(this.fillStyle.p=="gradientradial"){var z=this.fillStyle.n/p*100,B=this.fillStyle.o/p*100-z}else{var z=0,B=100}var C={offset:null,color:null},D={offset:null,color:null};this.fillStyle.h.sort(function(T,U){return T.offset-U.offset});for(var o=0;o<this.fillStyle.h.length;o++){var u=
-this.fillStyle.h[o];y.push(u.offset*B+z,"% ",u.color,",");if(u.offset>C.offset||C.offset==null){C.offset=u.offset;C.color=u.color}if(u.offset<D.offset||D.offset==null){D.offset=u.offset;D.color=u.color}}y.pop();b.push("<g_vml_:fill",' color="',D.color,'"',' color2="',C.color,'"',' type="',this.fillStyle.p,'"',' focusposition="',v.x,", ",v.y,'"',' colors="',y.join(""),'"',' opacity="',e,'" />')}else if(a){b.push('<g_vml_:fill color="',d,'" opacity="',e,'" />')}else{b.push("<g_vml_:stroke",' opacity="',
-e,'"',' joinstyle="',this.lineJoin,'"',' miterlimit="',this.miterLimit,'"',' endcap="',S(this.lineCap),'"',' weight="',this.lineWidth,'px"',' color="',d,'" />')}b.push("</g_vml_:shape>");this.j.insertAdjacentHTML("beforeEnd",b.join(""));this.c=[]};j.fill=function(){this.stroke(true)};j.closePath=function(){this.c.push({type:"close"})};j.b=function(a,b){return{x:m*(a*this.a[0][0]+b*this.a[1][0]+this.a[2][0])-A,y:m*(a*this.a[0][1]+b*this.a[1][1]+this.a[2][1])-A}};j.save=function(){var a={};N(this,a);
-this.k.push(a);this.m.push(this.a);this.a=G(J(),this.a)};j.restore=function(){N(this.k.pop(),this);this.a=this.m.pop()};j.translate=function(a,b){var c=[[1,0,0],[0,1,0],[a,b,1]];this.a=G(c,this.a)};j.rotate=function(a){var b=M(a),c=L(a),d=[[b,c,0],[-c,b,0],[0,0,1]];this.a=G(d,this.a)};j.scale=function(a,b){this.d*=a;this.e*=b;var c=[[a,0,0],[0,b,0],[0,0,1]];this.a=G(c,this.a)};j.clip=function(){};j.arcTo=function(){};j.createPattern=function(){return new P};function H(a){this.p=a;this.n=0;this.o=
-0;this.h=[];this.i={x:0,y:0}}H.prototype.addColorStop=function(a,b){b=O(b);this.h.push({offset:1-a,color:b})};function P(){}G_vmlCanvasManager=Q;CanvasRenderingContext2D=K;CanvasGradient=H;CanvasPattern=P})()};
--- a/sys/src/cmd/hg/templates/static/graph.js
+++ /dev/null
@@ -1,137 +1,0 @@
-// branch_renderer.js - Rendering of branch DAGs on the client side
-//
-// Copyright 2008 Dirkjan Ochtman <dirkjan AT ochtman DOT nl>
-// Copyright 2006 Alexander Schremmer <alex AT alexanderweb DOT de>
-//
-// derived from code written by Scott James Remnant <scott@ubuntu.com>
-// Copyright 2005 Canonical Ltd.
-//
-// This software may be used and distributed according to the terms
-// of the GNU General Public License, incorporated herein by reference.
-
-var colors = [
- [ 1.0, 0.0, 0.0 ],
- [ 1.0, 1.0, 0.0 ],
- [ 0.0, 1.0, 0.0 ],
- [ 0.0, 1.0, 1.0 ],
- [ 0.0, 0.0, 1.0 ],
- [ 1.0, 0.0, 1.0 ]
-];
-
-function Graph() {
-
- this.canvas = document.getElementById('graph');
- if (navigator.userAgent.indexOf('MSIE') >= 0) this.canvas = window.G_vmlCanvasManager.initElement(this.canvas);
- this.ctx = this.canvas.getContext('2d');
- this.ctx.strokeStyle = 'rgb(0, 0, 0)';
- this.ctx.fillStyle = 'rgb(0, 0, 0)';
- this.cur = [0, 0];
- this.line_width = 3;
- this.bg = [0, 4];
- this.cell = [2, 0];
- this.columns = 0;
- this.revlink = '';
-
- this.scale = function(height) {
- this.bg_height = height;
- this.box_size = Math.floor(this.bg_height / 1.2);
- this.cell_height = this.box_size;
- }
-
- function colorPart(num) {
- num *= 255
- num = num < 0 ? 0 : num;
- num = num > 255 ? 255 : num;
- var digits = Math.round(num).toString(16);
- if (num < 16) {
- return '0' + digits;
- } else {
- return digits;
- }
- }
-
- this.setColor = function(color, bg, fg) {
-
- // Set the colour.
- //
- // Picks a distinct colour based on an internal wheel; the bg
- // parameter provides the value that should be assigned to the 'zero'
- // colours and the fg parameter provides the multiplier that should be
- // applied to the foreground colours.
-
- color %= colors.length;
- var red = (colors[color][0] * fg) || bg;
- var green = (colors[color][1] * fg) || bg;
- var blue = (colors[color][2] * fg) || bg;
- red = Math.round(red * 255);
- green = Math.round(green * 255);
- blue = Math.round(blue * 255);
- var s = 'rgb(' + red + ', ' + green + ', ' + blue + ')';
- this.ctx.strokeStyle = s;
- this.ctx.fillStyle = s;
- return s;
-
- }
-
- this.render = function(data) {
-
- var backgrounds = '';
- var nodedata = '';
-
- for (var i in data) {
-
- var parity = i % 2;
- this.cell[1] += this.bg_height;
- this.bg[1] += this.bg_height;
-
- var cur = data[i];
- var node = cur[1];
- var edges = cur[2];
- var fold = false;
-
- for (var j in edges) {
-
- line = edges[j];
- start = line[0];
- end = line[1];
- color = line[2];
-
- if (end > this.columns || start > this.columns) {
- this.columns += 1;
- }
-
- if (start == this.columns && start > end) {
- var fold = true;
- }
-
- x0 = this.cell[0] + this.box_size * start + this.box_size / 2;
- y0 = this.bg[1] - this.bg_height / 2;
- x1 = this.cell[0] + this.box_size * end + this.box_size / 2;
- y1 = this.bg[1] + this.bg_height / 2;
-
- this.edge(x0, y0, x1, y1, color);
-
- }
-
- // Draw the revision node in the right column
-
- column = node[0]
- color = node[1]
-
- radius = this.box_size / 8;
- x = this.cell[0] + this.box_size * column + this.box_size / 2;
- y = this.bg[1] - this.bg_height / 2;
- var add = this.vertex(x, y, color, parity, cur);
- backgrounds += add[0];
- nodedata += add[1];
-
- if (fold) this.columns -= 1;
-
- }
-
- document.getElementById('nodebgs').innerHTML += backgrounds;
- document.getElementById('graphnodes').innerHTML += nodedata;
-
- }
-
-}
binary files a/sys/src/cmd/hg/templates/static/hgicon.png /dev/null differ
binary files a/sys/src/cmd/hg/templates/static/hglogo.png /dev/null differ
--- a/sys/src/cmd/hg/templates/static/style-coal.css
+++ /dev/null
@@ -1,265 +1,0 @@
-body {
- margin: 0;
- padding: 0;
- background: black url(background.png) repeat-x;
- font-family: sans-serif;
-}
-
-.container {
- padding-right: 150px;
-}
-
-.main {
- position: relative;
- background: white;
- padding: 2em;
- border-right: 15px solid black;
- border-bottom: 15px solid black;
-}
-
-#.main {
- width: 98%;
-}
-
-.overflow {
- width: 100%;
- overflow: auto;
-}
-
-.menu {
- background: #999;
- padding: 10px;
- width: 75px;
- margin: 0;
- font-size: 80%;
- text-align: left;
- position: fixed;
- top: 27px;
- left: auto;
- right: 27px;
-}
-
-#.menu {
- position: absolute !important;
- top:expression(eval(document.body.scrollTop + 27));
-}
-
-.menu ul {
- list-style: none;
- padding: 0;
- margin: 10px 0 0 0;
-}
-
-.menu li {
- margin-bottom: 3px;
- padding: 2px 4px;
- background: white;
- color: black;
- font-weight: normal;
-}
-
-.menu li.active {
- background: black;
- color: white;
-}
-
-.menu img {
- width: 75px;
- height: 90px;
- border: 0;
-}
-
-.menu a { color: black; display: block; }
-
-.search {
- position: absolute;
- top: .7em;
- right: 2em;
-}
-
-form.search div#hint {
- display: none;
- position: absolute;
- top: 40px;
- right: 0px;
- width: 190px;
- padding: 5px;
- background: #ffc;
- font-size: 70%;
- border: 1px solid yellow;
- -moz-border-radius: 5px; /* this works only in camino/firefox */
- -webkit-border-radius: 5px; /* this is just for Safari */
-}
-
-form.search:hover div#hint { display: block; }
-
-a { text-decoration:none; }
-.age { white-space:nowrap; }
-.date { white-space:nowrap; }
-.indexlinks { white-space:nowrap; }
-.parity0 { background-color: #f0f0f0; }
-.parity1 { background-color: white; }
-.plusline { color: green; }
-.minusline { color: #dc143c; } /* crimson */
-.atline { color: purple; }
-
-.navigate {
- text-align: right;
- font-size: 60%;
- margin: 1em 0;
-}
-
-.tag {
- color: #999;
- font-size: 70%;
- font-weight: normal;
- margin-left: .5em;
- vertical-align: baseline;
-}
-
-.branchhead {
- color: #000;
- font-size: 80%;
- font-weight: normal;
- margin-left: .5em;
- vertical-align: baseline;
-}
-
-ul#graphnodes .branchhead {
- font-size: 75%;
-}
-
-.branchname {
- color: #000;
- font-size: 60%;
- font-weight: normal;
- margin-left: .5em;
- vertical-align: baseline;
-}
-
-h3 .branchname {
- font-size: 80%;
-}
-
-/* Common */
-pre { margin: 0; }
-
-h2 { font-size: 120%; border-bottom: 1px solid #999; }
-h2 a { color: #000; }
-h3 {
- margin-top: -.7em;
- font-size: 100%;
-}
-
-/* log and tags tables */
-.bigtable {
- border-bottom: 1px solid #999;
- border-collapse: collapse;
- font-size: 90%;
- width: 100%;
- font-weight: normal;
- text-align: left;
-}
-
-.bigtable td {
- vertical-align: top;
-}
-
-.bigtable th {
- padding: 1px 4px;
- border-bottom: 1px solid #999;
-}
-.bigtable tr { border: none; }
-.bigtable .age { width: 6em; }
-.bigtable .author { width: 12em; }
-.bigtable .description { }
-.bigtable .node { width: 5em; font-family: monospace;}
-.bigtable .lineno { width: 2em; text-align: right;}
-.bigtable .lineno a { color: #999; font-size: smaller; font-family: monospace;}
-.bigtable .permissions { width: 8em; text-align: left;}
-.bigtable .size { width: 5em; text-align: right; }
-.bigtable .annotate { text-align: right; }
-.bigtable td.annotate { font-size: smaller; }
-.bigtable td.source { font-size: inherit; }
-
-.source, .sourcefirst, .sourcelast {
- font-family: monospace;
- white-space: pre;
- padding: 1px 4px;
- font-size: 90%;
-}
-.sourcefirst { border-bottom: 1px solid #999; font-weight: bold; }
-.sourcelast { border-top: 1px solid #999; }
-.source a { color: #999; font-size: smaller; font-family: monospace;}
-.bottomline { border-bottom: 1px solid #999; }
-
-.fileline { font-family: monospace; }
-.fileline img { border: 0; }
-
-.tagEntry .closed { color: #99f; }
-
-/* Changeset entry */
-#changesetEntry {
- border-collapse: collapse;
- font-size: 90%;
- width: 100%;
- margin-bottom: 1em;
-}
-
-#changesetEntry th {
- padding: 1px 4px;
- width: 4em;
- text-align: right;
- font-weight: normal;
- color: #999;
- margin-right: .5em;
- vertical-align: top;
-}
-
-div.description {
- border-left: 3px solid #999;
- margin: 1em 0 1em 0;
- padding: .3em;
-}
-
-/* Graph */
-div#wrapper {
- position: relative;
- border-top: 1px solid black;
- border-bottom: 1px solid black;
- margin: 0;
- padding: 0;
-}
-
-canvas {
- position: absolute;
- z-index: 5;
- top: -0.7em;
- margin: 0;
-}
-
-ul#graphnodes {
- position: absolute;
- z-index: 10;
- top: -1.0em;
- list-style: none inside none;
- padding: 0;
-}
-
-ul#nodebgs {
- list-style: none inside none;
- padding: 0;
- margin: 0;
- top: -0.7em;
-}
-
-ul#graphnodes li, ul#nodebgs li {
- height: 39px;
-}
-
-ul#graphnodes li .info {
- display: block;
- font-size: 70%;
- position: relative;
- top: -3px;
-}
--- a/sys/src/cmd/hg/templates/static/style-gitweb.css
+++ /dev/null
@@ -1,123 +1,0 @@
-body { font-family: sans-serif; font-size: 12px; margin:0px; border:solid #d9d8d1; border-width:1px; margin:10px; }
-a { color:#0000cc; }
-a:hover, a:visited, a:active { color:#880000; }
-div.page_header { height:25px; padding:8px; font-size:18px; font-weight:bold; background-color:#d9d8d1; }
-div.page_header a:visited { color:#0000cc; }
-div.page_header a:hover { color:#880000; }
-div.page_nav { padding:8px; }
-div.page_nav a:visited { color:#0000cc; }
-div.page_path { padding:8px; border:solid #d9d8d1; border-width:0px 0px 1px}
-div.page_footer { padding:4px 8px; background-color: #d9d8d1; }
-div.page_footer_text { float:left; color:#555555; font-style:italic; }
-div.page_body { padding:8px; }
-div.title, a.title {
- display:block; padding:6px 8px;
- font-weight:bold; background-color:#edece6; text-decoration:none; color:#000000;
-}
-a.title:hover { background-color: #d9d8d1; }
-div.title_text { padding:6px 0px; border: solid #d9d8d1; border-width:0px 0px 1px; }
-div.log_body { padding:8px 8px 8px 150px; }
-.age { white-space:nowrap; }
-span.age { position:relative; float:left; width:142px; font-style:italic; }
-div.log_link {
- padding:0px 8px;
- font-size:10px; font-family:sans-serif; font-style:normal;
- position:relative; float:left; width:136px;
-}
-div.list_head { padding:6px 8px 4px; border:solid #d9d8d1; border-width:1px 0px 0px; font-style:italic; }
-a.list { text-decoration:none; color:#000000; }
-a.list:hover { text-decoration:underline; color:#880000; }
-table { padding:8px 4px; }
-th { padding:2px 5px; font-size:12px; text-align:left; }
-tr.light:hover, .parity0:hover { background-color:#edece6; }
-tr.dark, .parity1 { background-color:#f6f6f0; }
-tr.dark:hover, .parity1:hover { background-color:#edece6; }
-td { padding:2px 5px; font-size:12px; vertical-align:top; }
-td.link { padding:2px 5px; font-family:sans-serif; font-size:10px; }
-td.indexlinks { white-space: nowrap; }
-td.indexlinks a {
- padding: 2px 5px; line-height: 10px;
- border: 1px solid;
- color: #ffffff; background-color: #7777bb;
- border-color: #aaaadd #333366 #333366 #aaaadd;
- font-weight: bold; text-align: center; text-decoration: none;
- font-size: 10px;
-}
-td.indexlinks a:hover { background-color: #6666aa; }
-div.pre { font-family:monospace; font-size:12px; white-space:pre; }
-div.diff_info { font-family:monospace; color:#000099; background-color:#edece6; font-style:italic; }
-div.index_include { border:solid #d9d8d1; border-width:0px 0px 1px; padding:12px 8px; }
-div.search { margin:4px 8px; position:absolute; top:56px; right:12px }
-.linenr { color:#999999; text-decoration:none }
-div.rss_logo { float: right; white-space: nowrap; }
-div.rss_logo a {
- padding:3px 6px; line-height:10px;
- border:1px solid; border-color:#fcc7a5 #7d3302 #3e1a01 #ff954e;
- color:#ffffff; background-color:#ff6600;
- font-weight:bold; font-family:sans-serif; font-size:10px;
- text-align:center; text-decoration:none;
-}
-div.rss_logo a:hover { background-color:#ee5500; }
-pre { margin: 0; }
-span.logtags span {
- padding: 0px 4px;
- font-size: 10px;
- font-weight: normal;
- border: 1px solid;
- background-color: #ffaaff;
- border-color: #ffccff #ff00ee #ff00ee #ffccff;
-}
-span.logtags span.tagtag {
- background-color: #ffffaa;
- border-color: #ffffcc #ffee00 #ffee00 #ffffcc;
-}
-span.logtags span.branchtag {
- background-color: #aaffaa;
- border-color: #ccffcc #00cc33 #00cc33 #ccffcc;
-}
-span.logtags span.inbranchtag {
- background-color: #d5dde6;
- border-color: #e3ecf4 #9398f4 #9398f4 #e3ecf4;
-}
-
-/* Graph */
-div#wrapper {
- position: relative;
- margin: 0;
- padding: 0;
- margin-top: 3px;
-}
-
-canvas {
- position: absolute;
- z-index: 5;
- top: -0.9em;
- margin: 0;
-}
-
-ul#nodebgs {
- list-style: none inside none;
- padding: 0;
- margin: 0;
- top: -0.7em;
-}
-
-ul#graphnodes li, ul#nodebgs li {
- height: 39px;
-}
-
-ul#graphnodes {
- position: absolute;
- z-index: 10;
- top: -0.8em;
- list-style: none inside none;
- padding: 0;
-}
-
-ul#graphnodes li .info {
- display: block;
- font-size: 100%;
- position: relative;
- top: -3px;
- font-style: italic;
-}
--- a/sys/src/cmd/hg/templates/static/style-monoblue.css
+++ /dev/null
@@ -1,472 +1,0 @@
-/*** Initial Settings ***/
-* {
- margin: 0;
- padding: 0;
- font-weight: normal;
- font-style: normal;
-}
-
-html {
- font-size: 100%;
- font-family: sans-serif;
-}
-
-body {
- font-size: 77%;
- margin: 15px 50px;
- background: #4B4B4C;
-}
-
-a {
- color:#0000cc;
- text-decoration: none;
-}
-/*** end of Initial Settings ***/
-
-
-/** common settings **/
-div#container {
- background: #FFFFFF;
- position: relative;
- color: #666;
-}
-
-div.page-header {
- padding: 50px 20px 0;
- background: #006699 top left repeat-x;
- position: relative;
-}
- div.page-header h1 {
- margin: 10px 0 30px;
- font-size: 1.8em;
- font-weight: bold;
- font-family: osaka,'MS P Gothic', Georgia, serif;
- letter-spacing: 1px;
- color: #DDD;
- }
- div.page-header h1 a {
- font-weight: bold;
- color: #FFF;
- }
- div.page-header a {
- text-decoration: none;
- }
-
- div.page-header form {
- position: absolute;
- margin-bottom: 2px;
- bottom: 0;
- right: 20px;
- }
- div.page-header form label {
- color: #DDD;
- }
- div.page-header form input {
- padding: 2px;
- border: solid 1px #DDD;
- }
- div.page-header form dl {
- overflow: hidden;
- }
- div.page-header form dl dt {
- font-size: 1.2em;
- }
- div.page-header form dl dt,
- div.page-header form dl dd {
- margin: 0 0 0 5px;
- float: left;
- height: 24px;
- line-height: 20px;
- }
-
- ul.page-nav {
- margin: 10px 0 0 0;
- list-style-type: none;
- overflow: hidden;
- width: 800px;
- }
- ul.page-nav li {
- margin: 0 2px 0 0;
- float: left;
- width: 80px;
- height: 24px;
- font-size: 1.1em;
- line-height: 24px;
- text-align: center;
- }
- ul.page-nav li.current {
- background: #FFF;
- }
- ul.page-nav li a {
- height: 24px;
- color: #666;
- background: #DDD;
- display: block;
- text-decoration: none;
- }
- ul.page-nav li a:hover {
- color:#333;
- background: #FFF;
- }
-
-ul.submenu {
- margin: 10px 0 -10px 20px;
- list-style-type: none;
-}
-ul.submenu li {
- margin: 0 10px 0 0;
- font-size: 1.2em;
- display: inline;
-}
-
-h2 {
- margin: 20px 0 10px;
- height: 30px;
- line-height: 30px;
- text-indent: 20px;
- background: #FFF;
- font-size: 1.2em;
- border-top: dotted 1px #D5E1E6;
- font-weight: bold;
-}
-h2.no-link {
- color:#006699;
-}
-h2.no-border {
- color: #FFF;
- background: #006699;
- border: 0;
-}
-h2 a {
- font-weight:bold;
- color:#006699;
-}
-
-div.page-path {
- text-align: right;
- padding: 20px 30px 10px 0;
- border:solid #d9d8d1;
- border-width:0px 0px 1px;
- font-size: 1.2em;
-}
-
-div.page-footer {
- margin: 50px 0 0;
- position: relative;
-}
- div.page-footer p {
- position: relative;
- left: 20px;
- bottom: 5px;
- font-size: 1.2em;
- }
-
- ul.rss-logo {
- position: absolute;
- top: -10px;
- right: 20px;
- height: 20px;
- list-style-type: none;
- }
- ul.rss-logo li {
- display: inline;
- }
- ul.rss-logo li a {
- padding: 3px 6px;
- line-height: 10px;
- border:1px solid;
- border-color:#fcc7a5 #7d3302 #3e1a01 #ff954e;
- color:#ffffff;
- background-color:#ff6600;
- font-weight:bold;
- font-family:sans-serif;
- font-size:10px;
- text-align:center;
- text-decoration:none;
- }
- div.rss-logo li a:hover {
- background-color:#ee5500;
- }
-
-p.normal {
- margin: 20px 0 20px 30px;
- font-size: 1.2em;
-}
-
-table {
- margin: 10px 0 0 20px;
- width: 95%;
- border-collapse: collapse;
-}
-table tr td {
- font-size: 1.1em;
-}
-table tr td.nowrap {
- white-space: nowrap;
-}
-/*
-table tr.parity0:hover,
-table tr.parity1:hover {
- background: #D5E1E6;
-}
-*/
-table tr.parity0 {
- background: #F1F6F7;
-}
-table tr.parity1 {
- background: #FFFFFF;
-}
-table tr td {
- padding: 5px 5px;
-}
-table.annotated tr td {
- padding: 0px 5px;
-}
-
-span.logtags span {
- padding: 2px 6px;
- font-weight: normal;
- font-size: 11px;
- border: 1px solid;
- background-color: #ffaaff;
- border-color: #ffccff #ff00ee #ff00ee #ffccff;
-}
-span.logtags span.tagtag {
- background-color: #ffffaa;
- border-color: #ffffcc #ffee00 #ffee00 #ffffcc;
-}
-span.logtags span.branchtag {
- background-color: #aaffaa;
- border-color: #ccffcc #00cc33 #00cc33 #ccffcc;
-}
-span.logtags span.inbranchtag {
- background-color: #d5dde6;
- border-color: #e3ecf4 #9398f4 #9398f4 #e3ecf4;
-}
-
-div.diff pre {
- margin: 10px 0 0 0;
-}
-div.diff pre span {
- font-family: monospace;
- white-space: pre;
- font-size: 1.2em;
- padding: 3px 0;
-}
-td.source {
- white-space: pre;
- font-family: monospace;
- margin: 10px 30px 0;
- font-size: 1.2em;
- font-family: monospace;
-}
- div.source div.parity0,
- div.source div.parity1 {
- padding: 1px;
- font-size: 1.2em;
- }
- div.source div.parity0 {
- background: #F1F6F7;
- }
- div.source div.parity1 {
- background: #FFFFFF;
- }
-div.parity0:hover,
-div.parity1:hover {
- background: #D5E1E6;
-}
-.linenr {
- color: #999;
- text-align: right;
-}
-.lineno {
- text-align: right;
-}
-.lineno a {
- color: #999;
-}
-td.linenr {
- width: 60px;
-}
-
-div#powered-by {
- position: absolute;
- width: 75px;
- top: 15px;
- right: 20px;
- font-size: 1.2em;
-}
-div#powered-by a {
- color: #EEE;
- text-decoration: none;
-}
-div#powered-by a:hover {
- text-decoration: underline;
-}
-/*
-div#monoblue-corner-top-left {
- position: absolute;
- top: 0;
- left: 0;
- width: 10px;
- height: 10px;
- background: url(./monoblue-corner.png) top left no-repeat !important;
- background: none;
-}
-div#monoblue-corner-top-right {
- position: absolute;
- top: 0;
- right: 0;
- width: 10px;
- height: 10px;
- background: url(./monoblue-corner.png) top right no-repeat !important;
- background: none;
-}
-div#monoblue-corner-bottom-left {
- position: absolute;
- bottom: 0;
- left: 0;
- width: 10px;
- height: 10px;
- background: url(./monoblue-corner.png) bottom left no-repeat !important;
- background: none;
-}
-div#monoblue-corner-bottom-right {
- position: absolute;
- bottom: 0;
- right: 0;
- width: 10px;
- height: 10px;
- background: url(./monoblue-corner.png) bottom right no-repeat !important;
- background: none;
-}
-*/
-/** end of common settings **/
-
-/** summary **/
-dl.overview {
- margin: 0 0 0 30px;
- font-size: 1.1em;
- overflow: hidden;
-}
- dl.overview dt,
- dl.overview dd {
- margin: 5px 0;
- float: left;
- }
- dl.overview dt {
- clear: left;
- font-weight: bold;
- width: 150px;
- }
-/** end of summary **/
-
-/** chagelog **/
-h3.changelog {
- margin: 20px 0 5px 30px;
- padding: 0 0 2px;
- font-size: 1.4em;
- border-bottom: dotted 1px #D5E1E6;
-}
-ul.changelog-entry {
- margin: 0 0 10px 30px;
- list-style-type: none;
- position: relative;
-}
-ul.changelog-entry li span.revdate {
- font-size: 1.1em;
-}
-ul.changelog-entry li.age {
- position: absolute;
- top: -25px;
- right: 10px;
- font-size: 1.4em;
- color: #CCC;
- font-weight: bold;
- font-style: italic;
-}
-ul.changelog-entry li span.name {
- font-size: 1.2em;
- font-weight: bold;
-}
-ul.changelog-entry li.description {
- margin: 10px 0 0;
- font-size: 1.1em;
-}
-/** end of changelog **/
-
-/** file **/
-p.files {
- margin: 0 0 0 20px;
- font-size: 2.0em;
- font-weight: bold;
-}
-/** end of file **/
-
-/** changeset **/
-h3.changeset {
- margin: 20px 0 5px 20px;
- padding: 0 0 2px;
- font-size: 1.6em;
- border-bottom: dotted 1px #D5E1E6;
-}
-p.changeset-age {
- position: relative;
-}
-p.changeset-age span {
- position: absolute;
- top: -25px;
- right: 10px;
- font-size: 1.4em;
- color: #CCC;
- font-weight: bold;
- font-style: italic;
-}
-p.description {
- margin: 10px 30px 0 30px;
- padding: 10px;
- border: solid 1px #CCC;
- font-size: 1.2em;
-}
-/** end of changeset **/
-
-/** canvas **/
-div#wrapper {
- position: relative;
- font-size: 1.2em;
-}
-
-canvas {
- position: absolute;
- z-index: 5;
- top: -0.7em;
-}
-
-ul#nodebgs li.parity0 {
- background: #F1F6F7;
-}
-
-ul#nodebgs li.parity1 {
- background: #FFFFFF;
-}
-
-ul#graphnodes {
- position: absolute;
- z-index: 10;
- top: 7px;
- list-style: none inside none;
-}
-
-ul#nodebgs {
- list-style: none inside none;
-}
-
-ul#graphnodes li, ul#nodebgs li {
- height: 39px;
-}
-
-ul#graphnodes li .info {
- display: block;
- position: relative;
-}
-/** end of canvas **/
--- a/sys/src/cmd/hg/templates/static/style-paper.css
+++ /dev/null
@@ -1,254 +1,0 @@
-body {
- margin: 0;
- padding: 0;
- background: white;
- font-family: sans-serif;
-}
-
-.container {
- padding-left: 115px;
-}
-
-.main {
- position: relative;
- background: white;
- padding: 2em 2em 2em 0;
-}
-
-#.main {
- width: 98%;
-}
-
-.overflow {
- width: 100%;
- overflow: auto;
-}
-
-.menu {
- width: 90px;
- margin: 0;
- font-size: 80%;
- text-align: left;
- position: absolute;
- top: 20px;
- left: 20px;
- right: auto;
-}
-
-.menu ul {
- list-style: none;
- padding: 0;
- margin: 10px 0 0 0;
- border-left: 2px solid #999;
-}
-
-.menu li {
- margin-bottom: 3px;
- padding: 2px 4px;
- background: white;
- color: black;
- font-weight: normal;
-}
-
-.menu li.active {
- font-weight: bold;
-}
-
-.menu img {
- width: 75px;
- height: 90px;
- border: 0;
-}
-
-.menu a { color: black; display: block; }
-
-.search {
- position: absolute;
- top: .7em;
- right: 2em;
-}
-
-form.search div#hint {
- display: none;
- position: absolute;
- top: 40px;
- right: 0px;
- width: 190px;
- padding: 5px;
- background: #ffc;
- font-size: 70%;
- border: 1px solid yellow;
- -moz-border-radius: 5px; /* this works only in camino/firefox */
- -webkit-border-radius: 5px; /* this is just for Safari */
-}
-
-form.search:hover div#hint { display: block; }
-
-a { text-decoration:none; }
-.age { white-space:nowrap; }
-.date { white-space:nowrap; }
-.indexlinks { white-space:nowrap; }
-.parity0 { background-color: #f0f0f0; }
-.parity1 { background-color: white; }
-.plusline { color: green; }
-.minusline { color: #dc143c; } /* crimson */
-.atline { color: purple; }
-
-.navigate {
- text-align: right;
- font-size: 60%;
- margin: 1em 0;
-}
-
-.tag {
- color: #999;
- font-size: 70%;
- font-weight: normal;
- margin-left: .5em;
- vertical-align: baseline;
-}
-
-.branchhead {
- color: #000;
- font-size: 80%;
- font-weight: normal;
- margin-left: .5em;
- vertical-align: baseline;
-}
-
-ul#graphnodes .branchhead {
- font-size: 75%;
-}
-
-.branchname {
- color: #000;
- font-size: 60%;
- font-weight: normal;
- margin-left: .5em;
- vertical-align: baseline;
-}
-
-h3 .branchname {
- font-size: 80%;
-}
-
-/* Common */
-pre { margin: 0; }
-
-h2 { font-size: 120%; border-bottom: 1px solid #999; }
-h2 a { color: #000; }
-h3 {
- margin-top: -.7em;
- font-size: 100%;
-}
-
-/* log and tags tables */
-.bigtable {
- border-bottom: 1px solid #999;
- border-collapse: collapse;
- font-size: 90%;
- width: 100%;
- font-weight: normal;
- text-align: left;
-}
-
-.bigtable td {
- vertical-align: top;
-}
-
-.bigtable th {
- padding: 1px 4px;
- border-bottom: 1px solid #999;
-}
-.bigtable tr { border: none; }
-.bigtable .age { width: 6em; }
-.bigtable .author { width: 12em; }
-.bigtable .description { }
-.bigtable .node { width: 5em; font-family: monospace;}
-.bigtable .permissions { width: 8em; text-align: left;}
-.bigtable .size { width: 5em; text-align: right; }
-.bigtable .annotate { text-align: right; }
-.bigtable td.annotate { font-size: smaller; }
-.bigtable td.source { font-size: inherit; }
-
-.source, .sourcefirst, .sourcelast {
- font-family: monospace;
- white-space: pre;
- padding: 1px 4px;
- font-size: 90%;
-}
-.sourcefirst { border-bottom: 1px solid #999; font-weight: bold; }
-.sourcelast { border-top: 1px solid #999; }
-.source a { color: #999; font-size: smaller; font-family: monospace;}
-.bottomline { border-bottom: 1px solid #999; }
-
-.fileline { font-family: monospace; }
-.fileline img { border: 0; }
-
-.tagEntry .closed { color: #99f; }
-
-/* Changeset entry */
-#changesetEntry {
- border-collapse: collapse;
- font-size: 90%;
- width: 100%;
- margin-bottom: 1em;
-}
-
-#changesetEntry th {
- padding: 1px 4px;
- width: 4em;
- text-align: right;
- font-weight: normal;
- color: #999;
- margin-right: .5em;
- vertical-align: top;
-}
-
-div.description {
- border-left: 2px solid #999;
- margin: 1em 0 1em 0;
- padding: .3em;
-}
-
-/* Graph */
-div#wrapper {
- position: relative;
- border-top: 1px solid black;
- border-bottom: 1px solid black;
- margin: 0;
- padding: 0;
-}
-
-canvas {
- position: absolute;
- z-index: 5;
- top: -0.7em;
- margin: 0;
-}
-
-ul#graphnodes {
- position: absolute;
- z-index: 10;
- top: -1.0em;
- list-style: none inside none;
- padding: 0;
-}
-
-ul#nodebgs {
- list-style: none inside none;
- padding: 0;
- margin: 0;
- top: -0.7em;
-}
-
-ul#graphnodes li, ul#nodebgs li {
- height: 39px;
-}
-
-ul#graphnodes li .info {
- display: block;
- font-size: 70%;
- position: relative;
- top: -3px;
-}
--- a/sys/src/cmd/hg/templates/static/style.css
+++ /dev/null
@@ -1,105 +1,0 @@
-a { text-decoration:none; }
-.age { white-space:nowrap; }
-.date { white-space:nowrap; }
-.indexlinks { white-space:nowrap; }
-.parity0 { background-color: #ddd; }
-.parity1 { background-color: #eee; }
-.lineno { width: 60px; color: #aaa; font-size: smaller;
- text-align: right; }
-.plusline { color: green; }
-.minusline { color: red; }
-.atline { color: purple; }
-.annotate { font-size: smaller; text-align: right; padding-right: 1em; }
-.buttons a {
- background-color: #666;
- padding: 2pt;
- color: white;
- font-family: sans;
- font-weight: bold;
-}
-.navigate a {
- background-color: #ccc;
- padding: 2pt;
- font-family: sans;
- color: black;
-}
-
-.metatag {
- background-color: #888;
- color: white;
- text-align: right;
-}
-
-/* Common */
-pre { margin: 0; }
-
-.logo {
- float: right;
- clear: right;
-}
-
-/* Changelog/Filelog entries */
-.logEntry { width: 100%; }
-.logEntry .age { width: 15%; }
-.logEntry th { font-weight: normal; text-align: right; vertical-align: top; }
-.logEntry th.age, .logEntry th.firstline { font-weight: bold; }
-.logEntry th.firstline { text-align: left; width: inherit; }
-
-/* Shortlog entries */
-.slogEntry { width: 100%; }
-.slogEntry .age { width: 8em; }
-.slogEntry td { font-weight: normal; text-align: left; vertical-align: top; }
-.slogEntry td.author { width: 15em; }
-
-/* Tag entries */
-#tagEntries { list-style: none; margin: 0; padding: 0; }
-#tagEntries .tagEntry { list-style: none; margin: 0; padding: 0; }
-
-/* Changeset entry */
-#changesetEntry { }
-#changesetEntry th { font-weight: normal; background-color: #888; color: #fff; text-align: right; }
-#changesetEntry th.files, #changesetEntry th.description { vertical-align: top; }
-
-/* File diff view */
-#filediffEntry { }
-#filediffEntry th { font-weight: normal; background-color: #888; color: #fff; text-align: right; }
-
-/* Graph */
-div#wrapper {
- position: relative;
- margin: 0;
- padding: 0;
-}
-
-canvas {
- position: absolute;
- z-index: 5;
- top: -0.6em;
- margin: 0;
-}
-
-ul#nodebgs {
- list-style: none inside none;
- padding: 0;
- margin: 0;
- top: -0.7em;
-}
-
-ul#graphnodes li, ul#nodebgs li {
- height: 39px;
-}
-
-ul#graphnodes {
- position: absolute;
- z-index: 10;
- top: -0.85em;
- list-style: none inside none;
- padding: 0;
-}
-
-ul#graphnodes li .info {
- display: block;
- font-size: 70%;
- position: relative;
- top: -1px;
-}