--- a/CONTRIBUTORS Sun Dec 02 19:39:27 2007 +0100
+++ b/CONTRIBUTORS Sun Dec 02 19:45:38 2007 +0100
@@ -1,4 +1,7 @@
-Andrea Arcangeli <andrea at suse.de>
+[This file is here for historical purposes, all recent contributors
+should appear in the changelog directly]
+
+Andrea Arcangeli <andrea at suse.de>
Thomas Arendsen Hein <thomas at intevation.de>
Goffredo Baroncelli <kreijack at libero.it>
Muli Ben-Yehuda <mulix at mulix.org>
@@ -36,5 +39,3 @@
Rafael Villar Burke <pachi at mmn-arquitectos.com>
Tristan Wibberley <tristan at wibberley.org>
Mark Williamson <mark.williamson at cl.cam.ac.uk>
-
-If you are a contributor and don't see your name here, please let me know.
--- a/contrib/bash_completion Sun Dec 02 19:39:27 2007 +0100
+++ b/contrib/bash_completion Sun Dec 02 19:45:38 2007 +0100
@@ -305,6 +305,15 @@
_hg_ext_mq_patchlist qunapplied
}
+_hg_cmd_qgoto()
+{
+ if [[ "$prev" = @(-n|--name) ]]; then
+ _hg_ext_mq_queues
+ return
+ fi
+ _hg_ext_mq_patchlist qseries
+}
+
_hg_cmd_qdelete()
{
local qcmd=qunapplied
--- a/contrib/hgk Sun Dec 02 19:39:27 2007 +0100
+++ b/contrib/hgk Sun Dec 02 19:45:38 2007 +0100
@@ -649,7 +649,7 @@
if {$stuffsaved} return
if {![winfo viewable .]} return
catch {
- set f [open "~/.gitk-new" w]
+ set f [open "~/.hgk-new" w]
puts $f [list set mainfont $mainfont]
puts $f [list set curidfont $curidfont]
puts $f [list set textfont $textfont]
@@ -687,7 +687,7 @@
puts $f "#"
puts $f "set authorcolors {$authorcolors}"
close $f
- file rename -force "~/.gitk-new" "~/.gitk"
+ file rename -force "~/.hgk-new" "~/.hgk"
}
set stuffsaved 1
}
@@ -3847,7 +3847,7 @@
deeppink mediumorchid blue burlywood4 goldenrod slateblue red2 navy dimgrey
}
-catch {source ~/.gitk}
+catch {source ~/.hgk}
if {$curidfont == ""} { # initialize late based on current mainfont
set curidfont "$mainfont bold italic underline"
--- a/doc/hgrc.5.txt Sun Dec 02 19:39:27 2007 +0100
+++ b/doc/hgrc.5.txt Sun Dec 02 19:45:38 2007 +0100
@@ -17,7 +17,9 @@
Mercurial reads configuration data from several files, if they exist.
The names of these files depend on the system on which Mercurial is
-installed.
+installed. Windows registry keys contain PATH-like strings, every
+part must reference a Mercurial.ini file or be a directory where *.rc
+files will be read.
(Unix) <install-root>/etc/mercurial/hgrc.d/*.rc::
(Unix) <install-root>/etc/mercurial/hgrc::
@@ -29,6 +31,8 @@
(Unix) /etc/mercurial/hgrc.d/*.rc::
(Unix) /etc/mercurial/hgrc::
+(Windows) HKEY_LOCAL_MACHINE\SOFTWARE\Mercurial::
+ or::
(Windows) C:\Mercurial\Mercurial.ini::
Per-system configuration files, for the system on which Mercurial
is running. Options in these files apply to all Mercurial
--- a/hg Sun Dec 02 19:39:27 2007 +0100
+++ b/hg Sun Dec 02 19:45:38 2007 +0100
@@ -10,5 +10,11 @@
# enable importing on demand to reduce startup time
from mercurial import demandimport; demandimport.enable()
+import sys
+import mercurial.util
import mercurial.dispatch
+
+for fp in (sys.stdin, sys.stdout, sys.stderr):
+ mercurial.util.set_binary(fp)
+
mercurial.dispatch.run()
--- a/hgext/convert/__init__.py Sun Dec 02 19:39:27 2007 +0100
+++ b/hgext/convert/__init__.py Sun Dec 02 19:45:38 2007 +0100
@@ -5,12 +5,12 @@
# This software may be used and distributed according to the terms
# of the GNU General Public License, incorporated herein by reference.
-from common import NoRepo, SKIPREV, converter_source, converter_sink
+from common import NoRepo, SKIPREV, converter_source, converter_sink, 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, debugsvnlog
+from subversion import debugsvnlog, svn_source, svn_sink
import filemap
import os, shutil
@@ -29,6 +29,7 @@
sink_converters = [
('hg', mercurial_sink),
+ ('svn', svn_sink),
]
def convertsource(ui, path, type, rev):
@@ -61,23 +62,10 @@
self.ui = ui
self.opts = opts
self.commitcache = {}
- self.revmapfile = revmapfile
- self.revmapfilefd = None
self.authors = {}
self.authorfile = None
- self.maporder = []
- self.map = {}
- try:
- origrevmapfile = open(self.revmapfile, 'r')
- for l in origrevmapfile:
- sv, dv = l[:-1].split()
- if sv not in self.map:
- self.maporder.append(sv)
- self.map[sv] = dv
- origrevmapfile.close()
- except IOError:
- pass
+ self.map = mapfile(ui, revmapfile)
# Read first the dst author map if any
authorfile = self.dest.authorfile()
@@ -165,16 +153,6 @@
return s
- def mapentry(self, src, dst):
- if self.revmapfilefd is None:
- try:
- self.revmapfilefd = open(self.revmapfile, "a")
- except IOError, (errno, strerror):
- raise util.Abort("Could not open map file %s: %s, %s\n" % (self.revmapfile, errno, strerror))
- self.map[src] = dst
- self.revmapfilefd.write("%s %s\n" % (src, dst))
- self.revmapfilefd.flush()
-
def writeauthormap(self):
authorfile = self.authorfile
if authorfile:
@@ -221,7 +199,7 @@
dest = SKIPREV
else:
dest = self.map[changes]
- self.mapentry(rev, dest)
+ self.map[rev] = dest
return
files, copies = changes
parents = [self.map[r] for r in commit.parents]
@@ -249,13 +227,14 @@
self.dest.copyfile(copyf, f)
newnode = self.dest.putcommit(filenames, parents, commit)
- self.mapentry(rev, newnode)
+ self.source.converted(rev, newnode)
+ self.map[rev] = newnode
def convert(self):
try:
self.source.before()
self.dest.before()
- self.source.setrevmap(self.map, self.maporder)
+ self.source.setrevmap(self.map)
self.ui.status("scanning source...\n")
heads = self.source.getheads()
parents = self.walktree(heads)
@@ -285,7 +264,7 @@
# write another hash correspondence to override the previous
# one so we don't end up with extra tag heads
if nrev:
- self.mapentry(c, nrev)
+ self.map[c] = nrev
self.writeauthormap()
finally:
@@ -296,8 +275,7 @@
self.dest.after()
finally:
self.source.after()
- if self.revmapfilefd:
- self.revmapfilefd.close()
+ self.map.close()
def convert(ui, src, dest=None, revmapfile=None, **opts):
"""Convert a foreign SCM repository to a Mercurial one.
@@ -311,6 +289,7 @@
Accepted destination formats:
- Mercurial
+ - Subversion (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
@@ -353,6 +332,24 @@
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.
+
+ Back end options:
+
+ --config convert.hg.clonebranches=False (boolean)
+ hg target: XXX not documented
+ --config convert.hg.saverev=True (boolean)
+ hg source: allow target to preserve source revision ID
+ --config convert.hg.tagsbranch=default (branch name)
+ hg target: XXX not documented
+ --config convert.hg.usebranchnames=True (boolean)
+ hg target: preserve branch names
+
+ --config convert.svn.branches=branches (directory name)
+ svn source: specify the directory containing branches
+ --config convert.svn.tags=tags (directory name)
+ svn source: specify the directory containing tags
+ --config convert.svn.trunk=trunk (directory name)
+ svn source: specify the name of the trunk branch
"""
util._encoding = 'UTF-8'
--- a/hgext/convert/common.py Sun Dec 02 19:39:27 2007 +0100
+++ b/hgext/convert/common.py Sun Dec 02 19:45:38 2007 +0100
@@ -1,6 +1,8 @@
# common code for the convert extension
-import base64
+import base64, errno
import cPickle as pickle
+from mercurial import util
+from mercurial.i18n import _
def encodeargs(args):
def encodearg(s):
@@ -15,6 +17,11 @@
s = base64.decodestring(s)
return pickle.loads(s)
+def checktool(exe, name=None):
+ name = name or exe
+ if not util.find_exe(exe):
+ raise util.Abort('cannot find required "%s" tool' % name)
+
class NoRepo(Exception): pass
SKIPREV = 'SKIP'
@@ -33,7 +40,7 @@
class converter_source(object):
"""Conversion source interface"""
- def __init__(self, ui, path, rev=None):
+ 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
@@ -48,11 +55,8 @@
def after(self):
pass
- def setrevmap(self, revmap, order):
- """set the map of already-converted revisions
-
- order is a list with the keys from revmap in the order they
- appear in the revision map file."""
+ def setrevmap(self, revmap):
+ """set the map of already-converted revisions"""
pass
def getheads(self):
@@ -111,6 +115,11 @@
"""
raise NotImplementedError()
+ def converted(self, rev, sinkrev):
+ '''Notify the source that a revision has been converted.'''
+ pass
+
+
class converter_sink(object):
"""Conversion sink (target) interface"""
@@ -184,3 +193,105 @@
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 _run(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]
+ cmdline += ['<', util.nulldev]
+ cmdline = ' '.join(cmdline)
+ self.ui.debug(cmdline, '\n')
+
+ 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 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
+
+
+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):
+ try:
+ fp = open(self.path, 'r')
+ except IOError, err:
+ if err.errno != errno.ENOENT:
+ raise
+ return
+ for line in fp:
+ key, value = line[:-1].split(' ', 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/hgext/convert/cvs.py Sun Dec 02 19:39:27 2007 +0100
+++ b/hgext/convert/cvs.py Sun Dec 02 19:45:38 2007 +0100
@@ -1,9 +1,10 @@
# CVS conversion code inspired by hg-cvs-import and git-cvsimport
import os, locale, re, socket
+from cStringIO import StringIO
from mercurial import util
-from common import NoRepo, commit, converter_source
+from common import NoRepo, commit, converter_source, checktool
class convert_cvs(converter_source):
def __init__(self, ui, path, rev=None):
@@ -13,6 +14,9 @@
if not os.path.exists(cvs):
raise NoRepo("%s does not look like a CVS checkout" % path)
+ for tool in ('cvsps', 'cvs'):
+ checktool(tool)
+
self.changeset = {}
self.files = {}
self.tags = {}
@@ -206,6 +210,20 @@
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
@@ -224,14 +242,14 @@
self.readp.readline() # entries
mode = self.readp.readline()[:-1]
count = int(self.readp.readline()[:-1])
- data = self.readp.read(count)
+ 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 = self.readp.read(count)
+ data = chunkedread(self.readp, count)
else:
if line == "ok\n":
return (data, "x" in mode and "x" or "")
--- a/hgext/convert/darcs.py Sun Dec 02 19:39:27 2007 +0100
+++ b/hgext/convert/darcs.py Sun Dec 02 19:45:38 2007 +0100
@@ -1,6 +1,6 @@
# darcs support for the convert extension
-from common import NoRepo, commit, converter_source
+from common import NoRepo, checktool, commandline, commit, converter_source
from mercurial.i18n import _
from mercurial import util
import os, shutil, tempfile
@@ -17,15 +17,18 @@
except ImportError: ElementTree = None
-class darcs_source(converter_source):
+class darcs_source(converter_source, commandline):
def __init__(self, ui, path, rev=None):
- super(darcs_source, self).__init__(ui, path, rev=rev)
+ 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')):
raise NoRepo("%s does not look like a darcs repo" % path)
+ checktool('darcs')
+
if ElementTree is None:
raise util.Abort(_("Python ElementTree module is not available"))
@@ -45,7 +48,8 @@
output, status = self.run('init', repodir=self.tmppath)
self.checkexit(status)
- tree = self.xml('changes', '--xml-output', '--summary')
+ tree = self.xml('changes', xml_output=True, summary=True,
+ repodir=self.path)
tagname = None
child = None
for elt in tree.findall('patch'):
@@ -65,31 +69,9 @@
self.ui.debug('cleaning up %s\n' % self.tmppath)
shutil.rmtree(self.tmppath, ignore_errors=True)
- def _run(self, cmd, *args, **kwargs):
- cmdline = ['darcs', cmd, '--repodir', kwargs.get('repodir', self.path)]
- cmdline += args
- cmdline = [util.shellquote(arg) for arg in cmdline]
- cmdline += ['<', util.nulldev]
- cmdline = ' '.join(cmdline)
- self.ui.debug(cmdline, '\n')
- return util.popen(cmdline)
-
- def run(self, cmd, *args, **kwargs):
- fp = self._run(cmd, *args, **kwargs)
- output = fp.read()
- return output, fp.close()
-
- def checkexit(self, status, output=''):
- if status:
- if output:
- self.ui.warn(_('darcs error:\n'))
- self.ui.warn(output)
- msg = util.explain_exit(status)[0]
- raise util.Abort(_('darcs %s') % msg)
-
- def xml(self, cmd, *opts):
+ def xml(self, cmd, **kwargs):
etree = ElementTree()
- fp = self._run(cmd, *opts)
+ fp = self._run(cmd, **kwargs)
etree.parse(fp)
self.checkexit(fp.close())
return etree.getroot()
@@ -105,15 +87,15 @@
desc=desc.strip(), parents=self.parents[rev])
def pull(self, rev):
- output, status = self.run('pull', self.path, '--all',
- '--match', 'hash %s' % rev,
- '--no-test', '--no-posthook',
- '--external-merge', '/bin/false',
+ 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', repodir=self.tmppath)
+ output, status = self.run('revert', all=True, repodir=self.tmppath)
self.checkexit(status, output)
def getchanges(self, rev):
--- a/hgext/convert/filemap.py Sun Dec 02 19:39:27 2007 +0100
+++ b/hgext/convert/filemap.py Sun Dec 02 19:45:38 2007 +0100
@@ -7,7 +7,7 @@
import shlex
from mercurial.i18n import _
from mercurial import util
-from common import SKIPREV
+from common import SKIPREV, converter_source
def rpairs(name):
e = len(name)
@@ -110,9 +110,9 @@
# touch files we're interested in, but also merges that merge two
# or more interesting revisions.
-class filemap_source(object):
+class filemap_source(converter_source):
def __init__(self, ui, baseconverter, filemap):
- self.ui = ui
+ super(filemap_source, self).__init__(ui)
self.base = baseconverter
self.filemapper = filemapper(ui, filemap)
self.commits = {}
@@ -128,7 +128,7 @@
self.children = {}
self.seenchildren = {}
- def setrevmap(self, revmap, order):
+ def setrevmap(self, revmap):
# rebuild our state to make things restartable
#
# To avoid calling getcommit for every revision that has already
@@ -143,7 +143,7 @@
seen = {SKIPREV: SKIPREV}
dummyset = util.set()
converted = []
- for rev in order:
+ for rev in revmap.order:
mapped = revmap[rev]
wanted = mapped not in seen
if wanted:
@@ -157,7 +157,7 @@
arg = None
converted.append((rev, wanted, arg))
self.convertedorder = converted
- return self.base.setrevmap(revmap, order)
+ return self.base.setrevmap(revmap)
def rebuild(self):
if self._rebuilt:
@@ -344,9 +344,3 @@
def gettags(self):
return self.base.gettags()
-
- def before(self):
- pass
-
- def after(self):
- pass
--- a/hgext/convert/git.py Sun Dec 02 19:39:27 2007 +0100
+++ b/hgext/convert/git.py Sun Dec 02 19:45:38 2007 +0100
@@ -3,7 +3,7 @@
import os
from mercurial import util
-from common import NoRepo, commit, converter_source
+from common import NoRepo, commit, converter_source, checktool
class convert_git(converter_source):
# Windows does not support GIT_DIR= construct while other systems
@@ -31,6 +31,9 @@
path += "/.git"
if not os.path.exists(path + "/objects"):
raise NoRepo("%s does not look like a Git repo" % path)
+
+ checktool('git-rev-parse', 'git')
+
self.path = path
def getheads(self):
--- a/hgext/convert/hg.py Sun Dec 02 19:39:27 2007 +0100
+++ b/hgext/convert/hg.py Sun Dec 02 19:45:38 2007 +0100
@@ -1,10 +1,16 @@
# hg backend for convert extension
-# Note 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.
+# 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.
+#
+# * By default, the source revision is stored in the converted
+# revision. This will cause the converted revision to have a
+# different identity than the source. To avoid this, use the
+# following option: "--config convert.hg.saverev=false"
import os, time
@@ -24,8 +30,6 @@
if os.path.isdir(path) and len(os.listdir(path)) > 0:
try:
self.repo = hg.repository(self.ui, path)
- ui.status(_('destination %s is a Mercurial repository\n') %
- path)
except hg.RepoError, err:
ui.print_exc()
raise NoRepo(err.args[0])
@@ -183,6 +187,7 @@
class mercurial_source(converter_source):
def __init__(self, ui, path, rev=None):
converter_source.__init__(self, ui, path, rev)
+ self.saverev = ui.configbool('convert', 'hg.saverev', True)
try:
self.repo = hg.repository(self.ui, path)
# try to provoke an exception if this isn't really a hg
@@ -195,6 +200,7 @@
self.lastrev = None
self.lastctx = None
self._changescache = None
+ self.convertfp = None
def changectx(self, rev):
if self.lastrev != rev:
@@ -240,8 +246,12 @@
def getcommit(self, rev):
ctx = self.changectx(rev)
parents = [hex(p.node()) for p in ctx.parents() if p.node() != nullid]
+ if self.saverev:
+ crev = rev
+ else:
+ crev = None
return commit(author=ctx.user(), date=util.datestr(ctx.date()),
- desc=ctx.description(), parents=parents,
+ desc=ctx.description(), rev=crev, parents=parents,
branch=ctx.branch(), extra=ctx.extra())
def gettags(self):
@@ -258,3 +268,9 @@
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()
--- a/hgext/convert/subversion.py Sun Dec 02 19:39:27 2007 +0100
+++ b/hgext/convert/subversion.py Sun Dec 02 19:45:38 2007 +0100
@@ -17,9 +17,13 @@
import locale
import os
+import re
import sys
import cPickle as pickle
-from mercurial import util
+import tempfile
+
+from mercurial import strutil, util
+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
@@ -28,6 +32,7 @@
from cStringIO import StringIO
from common import NoRepo, commit, converter_source, encodeargs, decodeargs
+from common import commandline, converter_sink, mapfile
try:
from svn.core import SubversionException, Pool
@@ -149,9 +154,15 @@
self.head = self.revid(self.last_changed)
self._changescache = None
- def setrevmap(self, revmap, order):
+ 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.keys():
+ for revid in revmap.iterkeys():
uuid, module, revnum = self.revsplit(revid)
lastrevnum = lastrevs.setdefault(module, revnum)
if revnum > lastrevnum:
@@ -293,6 +304,15 @@
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()
+
# -- helper functions --
def revid(self, revnum, module=None):
@@ -664,3 +684,219 @@
pool = Pool()
rpath = '/'.join([self.base, path]).strip('/')
return ['%s/%s' % (path, x) for x in svn.client.ls(rpath, optrev(revnum), True, self.ctx, pool).keys()]
+
+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.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 = path.replace('\\', '/')
+ 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_exec(hook, 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_exec(self.wjoin(filename), 'x' in flags)
+ if was_exec:
+ if 'x' not in flags:
+ self.run0('propdel', 'svn:executable', filename)
+ else:
+ if 'x' in flags:
+ self.run0('propset', 'svn:executable', '*', filename)
+
+ def delfile(self, name):
+ self.delete.append(name)
+
+ 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_files(self, files):
+ add_dirs = [d for d in self.dirs_of(files)
+ if not os.path.exists(self.wjoin(d, '.svn', 'entries'))]
+ if add_dirs:
+ add_dirs.sort()
+ self.run('add', non_recursive=True, quiet=True, *add_dirs)
+ if files:
+ self.run('add', quiet=True, *files)
+ return files.union(add_dirs)
+
+ def tidy_dirs(self, names):
+ dirs = list(self.dirs_of(names))
+ dirs.sort(reverse=True)
+ deleted = []
+ for d in dirs:
+ 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, parents, commit):
+ for parent in parents:
+ try:
+ return self.revid(self.childmap[parent])
+ except KeyError:
+ pass
+ entries = set(self.delete)
+ if self.delete:
+ self.run0('delete', *self.delete)
+ self.delete = []
+ files = util.frozenset(files)
+ entries.update(self.add_files(files.difference(entries)))
+ entries.update(self.tidy_dirs(entries))
+ 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,
+ *list(entries))
+ 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/hgext/gpg.py Sun Dec 02 19:39:27 2007 +0100
+++ b/hgext/gpg.py Sun Dec 02 19:45:38 2007 +0100
@@ -249,7 +249,7 @@
message = opts['message']
if not message:
message = "\n".join([_("Added signature for changeset %s")
- % hgnode.hex(n)
+ % hgnode.short(n)
for n in nodes])
try:
repo.commit([".hgsigs"], message, opts['user'], opts['date'])
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/hgext/highlight.py Sun Dec 02 19:45:38 2007 +0100
@@ -0,0 +1,145 @@
+"""
+This is Mercurial extension for syntax highlighting in the file
+revision view of hgweb.
+
+It depends on the pygments syntax highlighting library:
+http://pygments.org/
+
+To enable the extension add this to hgrc:
+
+[extensions]
+hgext.highlight =
+
+There is a single configuration option:
+
+[web]
+pygments_style = <style>
+
+The default is 'colorful'. If this is changed the corresponding CSS
+file should be re-generated by running
+
+# pygmentize -f html -S <newstyle>
+
+
+-- Adam Hupp <adam@hupp.org>
+
+
+"""
+
+from mercurial import demandimport
+demandimport.ignore.extend(['pkgutil',
+ 'pkg_resources',
+ '__main__',])
+
+import mimetypes
+
+from mercurial.hgweb import hgweb_mod
+from mercurial.hgweb.hgweb_mod import hgweb
+from mercurial import util
+from mercurial.hgweb.common import paritygen
+from mercurial.node import hex
+
+from pygments import highlight
+from pygments.util import ClassNotFound
+from pygments.lexers import guess_lexer_for_filename, TextLexer
+from pygments.formatters import HtmlFormatter
+
+SYNTAX_CSS = ('\n<link rel="stylesheet" href="#staticurl#highlight.css" '
+ 'type="text/css" />')
+
+class StripedHtmlFormatter(HtmlFormatter):
+ def __init__(self, stripecount, *args, **kwargs):
+ super(StripedHtmlFormatter, self).__init__(*args, **kwargs)
+ self.stripecount = stripecount
+
+ def wrap(self, source, outfile):
+ yield 0, "<div class='highlight'>"
+ yield 0, "<pre>"
+ parity = paritygen(self.stripecount)
+
+ for n, i in source:
+ if n == 1:
+ i = "<div class='parity%s'>%s</div>" % (parity.next(), i)
+ yield n, i
+
+ yield 0, "</pre>"
+ yield 0, "</div>"
+
+
+def pygments_format(filename, rawtext, forcetext=False, stripecount=1,
+ style='colorful'):
+ if not forcetext:
+ try:
+ lexer = guess_lexer_for_filename(filename, rawtext)
+ except ClassNotFound:
+ lexer = TextLexer()
+ else:
+ lexer = TextLexer()
+
+ formatter = StripedHtmlFormatter(stripecount, style=style,
+ linenos='inline')
+
+ return highlight(rawtext, lexer, formatter)
+
+
+def filerevision_pygments(self, fctx):
+ """Reimplement hgweb.filerevision to use syntax highlighting"""
+ filename = fctx.path()
+
+ rawtext = fctx.data()
+ text = rawtext
+
+ mt = mimetypes.guess_type(filename)[0]
+
+ if util.binary(text):
+ mt = mt or 'application/octet-stream'
+ text = "(binary:%s)" % mt
+
+ # don't parse (binary:...) as anything
+ forcetext = True
+ else:
+ mt = mt or 'text/plain'
+ forcetext = False
+
+ def lines(text):
+ for line in text.splitlines(True):
+ yield {"line": line}
+
+ style = self.config("web", "pygments_style", "colorful")
+
+ text_formatted = lines(pygments_format(filename, text,
+ forcetext=forcetext,
+ stripecount=self.stripecount,
+ style=style))
+
+ # override per-line template
+ self.t.cache['fileline'] = '#line#'
+
+ # append a <link ...> to the syntax highlighting css
+ old_header = ''.join(self.t('header'))
+ if SYNTAX_CSS not in old_header:
+ new_header = old_header + SYNTAX_CSS
+ self.t.cache['header'] = new_header
+
+ yield self.t("filerevision",
+ file=filename,
+ path=hgweb_mod._up(filename), # fixme: make public
+ text=text_formatted,
+ raw=rawtext,
+ mimetype=mt,
+ rev=fctx.rev(),
+ node=hex(fctx.node()),
+ author=fctx.user(),
+ date=fctx.date(),
+ desc=fctx.description(),
+ parent=self.siblings(fctx.parents()),
+ child=self.siblings(fctx.children()),
+ rename=self.renamelink(fctx.filelog(),
+ fctx.filenode()),
+ permissions=fctx.manifest().flags(filename))
+
+
+# monkeypatch in the new version
+# should be safer than overriding the method in a derived class
+# and then patching the class
+hgweb.filerevision = filerevision_pygments
--- a/hgweb.cgi Sun Dec 02 19:39:27 2007 +0100
+++ b/hgweb.cgi Sun Dec 02 19:45:38 2007 +0100
@@ -22,10 +22,7 @@
#os.environ["HGENCODING"] = "UTF-8"
from mercurial.hgweb.hgweb_mod import hgweb
-from mercurial.hgweb.request import wsgiapplication
import mercurial.hgweb.wsgicgi as wsgicgi
-def make_web_app():
- return hgweb("/path/to/repo", "repository name")
-
-wsgicgi.launch(wsgiapplication(make_web_app))
+application = hgweb("/path/to/repo", "repository name")
+wsgicgi.launch(application)
--- a/hgwebdir.cgi Sun Dec 02 19:39:27 2007 +0100
+++ b/hgwebdir.cgi Sun Dec 02 19:45:38 2007 +0100
@@ -22,7 +22,6 @@
#os.environ["HGENCODING"] = "UTF-8"
from mercurial.hgweb.hgwebdir_mod import hgwebdir
-from mercurial.hgweb.request import wsgiapplication
import mercurial.hgweb.wsgicgi as wsgicgi
# The config file looks like this. You can have paths to individual
@@ -44,7 +43,5 @@
# Alternatively you can pass a list of ('virtual/path', '/real/path') tuples
# or use a dictionary with entries like 'virtual/path': '/real/path'
-def make_web_app():
- return hgwebdir("hgweb.config")
-
-wsgicgi.launch(wsgiapplication(make_web_app))
+application = hgwebdir('hgweb.config')
+wsgicgi.launch(application)
--- a/mercurial/bundlerepo.py Sun Dec 02 19:39:27 2007 +0100
+++ b/mercurial/bundlerepo.py Sun Dec 02 19:45:38 2007 +0100
@@ -48,7 +48,7 @@
continue
for p in (p1, p2):
if not p in self.nodemap:
- raise revlog.LookupError(_("unknown parent %s") % short(p1))
+ raise revlog.LookupError(hex(p1), _("unknown parent %s") % short(p1))
if linkmapper is None:
link = n
else:
--- a/mercurial/cmdutil.py Sun Dec 02 19:39:27 2007 +0100
+++ b/mercurial/cmdutil.py Sun Dec 02 19:45:38 2007 +0100
@@ -571,26 +571,26 @@
def showcopies(**args):
c = [{'name': x[0], 'source': x[1]} for x in copies]
return showlist('file_copy', c, plural='file_copies', **args)
-
- if self.ui.debugflag:
- files = self.repo.status(log.parents(changenode)[0], changenode)[:3]
- def showfiles(**args):
- return showlist('file', files[0], **args)
- def showadds(**args):
- return showlist('file_add', files[1], **args)
- def showdels(**args):
- return showlist('file_del', files[2], **args)
- def showmanifest(**args):
- args = args.copy()
- args.update(dict(rev=self.repo.manifest.rev(changes[0]),
- node=hex(changes[0])))
- return self.t('manifest', **args)
- else:
- def showfiles(**args):
- return showlist('file', changes[3], **args)
- showadds = ''
- showdels = ''
- showmanifest = ''
+
+ files = []
+ def getfiles():
+ if not files:
+ files[:] = self.repo.status(
+ log.parents(changenode)[0], changenode)[:3]
+ return files
+ def showfiles(**args):
+ return showlist('file', changes[3], **args)
+ def showmods(**args):
+ return showlist('file_mod', getfiles()[0], **args)
+ def showadds(**args):
+ return showlist('file_add', getfiles()[1], **args)
+ def showdels(**args):
+ return showlist('file_del', getfiles()[2], **args)
+ def showmanifest(**args):
+ args = args.copy()
+ args.update(dict(rev=self.repo.manifest.rev(changes[0]),
+ node=hex(changes[0])))
+ return self.t('manifest', **args)
defprops = {
'author': changes[1],
@@ -599,6 +599,7 @@
'desc': changes[4].strip(),
'file_adds': showadds,
'file_dels': showdels,
+ 'file_mods': showmods,
'files': showfiles,
'file_copies': showcopies,
'manifest': showmanifest,
--- a/mercurial/commands.py Sun Dec 02 19:39:27 2007 +0100
+++ b/mercurial/commands.py Sun Dec 02 19:45:38 2007 +0100
@@ -1629,7 +1629,7 @@
if opts.get('exact'):
if hex(n) != nodeid:
repo.rollback()
- raise util.Abort(_('patch is damaged' +
+ raise util.Abort(_('patch is damaged'
' or loses information'))
finally:
os.unlink(tmpname)
@@ -1937,7 +1937,7 @@
if len(heads) == 1:
msg = _('there is nothing to merge')
if parent != repo.lookup(repo.workingctx().branch()):
- msg = _('%s - use "hg update" instead' % msg)
+ msg = _('%s - use "hg update" instead') % msg
raise util.Abort(msg)
if parent not in heads:
--- a/mercurial/context.py Sun Dec 02 19:39:27 2007 +0100
+++ b/mercurial/context.py Sun Dec 02 19:45:38 2007 +0100
@@ -100,13 +100,13 @@
try:
return self._manifest[path], self._manifest.flags(path)
except KeyError:
- raise revlog.LookupError(_("'%s' not found in manifest") % path)
+ raise revlog.LookupError(path, _("'%s' not found in manifest") % path)
if '_manifestdelta' in self.__dict__ or path in self.files():
if path in self._manifestdelta:
return self._manifestdelta[path], self._manifestdelta.flags(path)
node, flag = self._repo.manifest.find(self._changeset[0], path)
if not node:
- raise revlog.LookupError(_("'%s' not found in manifest") % path)
+ raise revlog.LookupError(path, _("'%s' not found in manifest") % path)
return node, flag
--- a/mercurial/dispatch.py Sun Dec 02 19:39:27 2007 +0100
+++ b/mercurial/dispatch.py Sun Dec 02 19:45:38 2007 +0100
@@ -125,7 +125,7 @@
ui.warn("\n%r\n" % util.ellipsis(inst[1]))
except ImportError, inst:
m = str(inst).split()[-1]
- ui.warn(_("abort: could not import module %s!\n" % m))
+ ui.warn(_("abort: could not import module %s!\n") % m)
if m in "mpatch bdiff".split():
ui.warn(_("(did you forget to compile extensions?)\n"))
elif m in "zlib".split():
--- a/mercurial/hgweb/common.py Sun Dec 02 19:39:27 2007 +0100
+++ b/mercurial/hgweb/common.py Sun Dec 02 19:45:38 2007 +0100
@@ -6,7 +6,24 @@
# This software may be used and distributed according to the terms
# of the GNU General Public License, incorporated herein by reference.
-import os, mimetypes
+import errno, mimetypes, os
+
+class ErrorResponse(Exception):
+ def __init__(self, code, message=None):
+ Exception.__init__(self)
+ self.code = code
+ if message:
+ 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")
@@ -40,9 +57,13 @@
req.header([('Content-type', ct),
('Content-length', str(os.path.getsize(path)))])
return file(path, 'rb').read()
- except (TypeError, OSError):
- # illegal fname or unreadable file
- return ""
+ except TypeError:
+ raise ErrorResponse(500, 'illegal file name')
+ except OSError, err:
+ if err.errno == errno.ENOENT:
+ raise ErrorResponse(404)
+ else:
+ raise ErrorResponse(500, err.strerror)
def style_map(templatepath, style):
"""Return path to mapfile for a given style.
--- a/mercurial/hgweb/hgweb_mod.py Sun Dec 02 19:39:27 2007 +0100
+++ b/mercurial/hgweb/hgweb_mod.py Sun Dec 02 19:45:38 2007 +0100
@@ -6,13 +6,14 @@
# This software may be used and distributed according to the terms
# of the GNU General Public License, incorporated herein by reference.
-import os, mimetypes, re, zlib, mimetools, cStringIO, sys
+import errno, os, mimetypes, re, zlib, mimetools, cStringIO, sys
import tempfile, urllib, bz2
from mercurial.node import *
from mercurial.i18n import gettext as _
from mercurial import mdiff, ui, hg, util, archival, streamclone, patch
from mercurial import revlog, templater
-from common import get_mtime, staticfile, style_map, paritygen
+from common import ErrorResponse, get_mtime, staticfile, style_map, paritygen
+from request import wsgirequest
def _up(p):
if p[0] != "/":
@@ -478,6 +479,9 @@
short = os.path.basename(remain)
files[short] = (f, n)
+ if not files:
+ raise ErrorResponse(404, 'Path not found: ' + path)
+
def filelist(**map):
fl = files.keys()
fl.sort()
@@ -668,10 +672,12 @@
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
- from request import wsgiapplication
- def make_web_app():
- return self
- wsgicgi.launch(wsgiapplication(make_web_app))
+ wsgicgi.launch(self)
+
+ def __call__(self, env, respond):
+ req = wsgirequest(env, respond)
+ self.run_wsgi(req)
+ return req
def run_wsgi(self, req):
def header(**map):
@@ -720,37 +726,21 @@
def rewrite_request(req):
'''translate new web interface to traditional format'''
- def spliturl(req):
- def firstitem(query):
- return query.split('&', 1)[0].split(';', 1)[0]
-
- def normurl(url):
- inner = '/'.join([x for x in url.split('/') if x])
- tl = len(url) > 1 and url.endswith('/') and '/' or ''
-
- return '%s%s%s' % (url.startswith('/') and '/' or '',
- inner, tl)
-
- root = normurl(urllib.unquote(req.env.get('REQUEST_URI', '').split('?', 1)[0]))
- pi = normurl(req.env.get('PATH_INFO', ''))
- if pi:
- # strip leading /
- pi = pi[1:]
- if pi:
- root = root[:root.rfind(pi)]
- if req.env.has_key('REPO_NAME'):
- rn = req.env['REPO_NAME'] + '/'
- root += rn
- query = pi[len(rn):]
- else:
- query = pi
- else:
- root += '?'
- query = firstitem(req.env['QUERY_STRING'])
-
- return (root, query)
-
- req.url, query = spliturl(req)
+ req.url = req.env['SCRIPT_NAME']
+ if not req.url.endswith('/'):
+ req.url += '/'
+ if req.env.has_key('REPO_NAME'):
+ req.url += req.env['REPO_NAME'] + '/'
+
+ if req.env.get('PATH_INFO'):
+ parts = req.env.get('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]
if req.form.has_key('cmd'):
# old style
@@ -845,14 +835,20 @@
cmd = req.form['cmd'][0]
- method = getattr(self, 'do_' + cmd, None)
- if method:
- try:
- method(req)
- except (hg.RepoError, revlog.RevlogError), inst:
- req.write(self.t("error", error=str(inst)))
- else:
- req.write(self.t("error", error='No such method: ' + cmd))
+ try:
+ method = getattr(self, 'do_' + cmd)
+ method(req)
+ except revlog.LookupError, err:
+ req.respond(404, self.t(
+ 'error', error='revision not found: %s' % err.name))
+ except (hg.RepoError, revlog.RevlogError), inst:
+ req.respond('500 Internal Server Error',
+ self.t('error', error=str(inst)))
+ except ErrorResponse, inst:
+ req.respond(inst.code, self.t('error', error=inst.message))
+ except AttributeError:
+ req.respond(400,
+ self.t('error', error='No such method: ' + cmd))
finally:
self.t = None
@@ -1038,7 +1034,8 @@
self.archive(req, req.form['node'][0], type_)
return
- req.write(self.t("error"))
+ req.respond(400, self.t('error',
+ error='Unsupported archive type: %s' % type_))
def do_static(self, req):
fname = req.form['file'][0]
@@ -1047,8 +1044,7 @@
static = self.config("web", "static",
os.path.join(self.templatepath, "static"),
untrusted=False)
- req.write(staticfile(static, fname, req)
- or self.t("error", error="%r not found" % fname))
+ req.write(staticfile(static, fname, req))
def do_capabilities(self, req):
caps = ['lookup', 'changegroupsubset']
@@ -1198,7 +1194,11 @@
else:
filename = ''
error = getattr(inst, 'strerror', 'Unknown error')
- req.write('%s: %s\n' % (error, filename))
+ if inst.errno == errno.ENOENT:
+ code = 404
+ else:
+ code = 500
+ req.respond(code, '%s: %s\n' % (error, filename))
finally:
fp.close()
os.unlink(tempname)
--- a/mercurial/hgweb/hgwebdir_mod.py Sun Dec 02 19:39:27 2007 +0100
+++ b/mercurial/hgweb/hgwebdir_mod.py Sun Dec 02 19:45:38 2007 +0100
@@ -9,8 +9,9 @@
import os, mimetools, cStringIO
from mercurial.i18n import gettext as _
from mercurial import ui, hg, util, templater
-from common import get_mtime, staticfile, style_map, paritygen
+from common import ErrorResponse, get_mtime, staticfile, style_map, paritygen
from hgweb_mod import hgweb
+from request import wsgirequest
# This is a stopgap
class hgwebdir(object):
@@ -60,10 +61,12 @@
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
- from request import wsgiapplication
- def make_web_app():
- return self
- wsgicgi.launch(wsgiapplication(make_web_app))
+ wsgicgi.launch(self)
+
+ def __call__(self, env, respond):
+ req = wsgirequest(env, respond)
+ self.run_wsgi(req)
+ return req
def run_wsgi(self, req):
def header(**map):
@@ -88,15 +91,11 @@
def config(section, name, default=None, untrusted=True):
return parentui.config(section, name, default, untrusted)
- url = req.env['REQUEST_URI'].split('?')[0]
+ url = req.env.get('SCRIPT_NAME', '')
if not url.endswith('/'):
url += '/'
- pathinfo = req.env.get('PATH_INFO', '').strip('/') + '/'
- base = url[:len(url) - len(pathinfo)]
- if not base.endswith('/'):
- base += '/'
- staticurl = config('web', 'staticurl') or base + 'static/'
+ staticurl = config('web', 'staticurl') or url + 'static/'
if not staticurl.endswith('/'):
staticurl += '/'
@@ -155,8 +154,10 @@
if u.configbool("web", "hidden", untrusted=True):
continue
- url = ('/'.join([req.env["REQUEST_URI"].split('?')[0], name])
- .replace("//", "/")) + '/'
+ parts = [req.env['PATH_INFO'], name]
+ if req.env['SCRIPT_NAME']:
+ parts.insert(0, req.env['SCRIPT_NAME'])
+ url = ('/'.join(parts).replace("//", "/")) + '/'
# update time with local timezone
try:
@@ -215,46 +216,47 @@
**dict(sort)))
try:
- virtual = req.env.get("PATH_INFO", "").strip('/')
- if virtual.startswith('static/'):
- static = os.path.join(templater.templatepath(), 'static')
- fname = virtual[7:]
- req.write(staticfile(static, fname, req) or
- tmpl('error', error='%r not found' % fname))
- elif virtual:
- repos = dict(self.repos)
- while virtual:
- real = repos.get(virtual)
- if real:
- req.env['REPO_NAME'] = virtual
- try:
- repo = hg.repository(parentui, real)
- hgweb(repo).run_wsgi(req)
- except IOError, inst:
- req.write(tmpl("error", error=inst.strerror))
- except hg.RepoError, inst:
- req.write(tmpl("error", error=str(inst)))
- return
+ try:
+ virtual = req.env.get("PATH_INFO", "").strip('/')
+ if virtual.startswith('static/'):
+ static = os.path.join(templater.templatepath(), 'static')
+ fname = virtual[7:]
+ req.write(staticfile(static, fname, req))
+ elif virtual:
+ repos = dict(self.repos)
+ while virtual:
+ real = repos.get(virtual)
+ if real:
+ req.env['REPO_NAME'] = virtual
+ try:
+ repo = hg.repository(parentui, real)
+ hgweb(repo).run_wsgi(req)
+ return
+ except IOError, inst:
+ raise ErrorResponse(500, inst.strerror)
+ except hg.RepoError, inst:
+ raise ErrorResponse(500, str(inst))
- # browse subdirectories
- subdir = virtual + '/'
- if [r for r in repos if r.startswith(subdir)]:
- makeindex(req, subdir)
- return
+ # browse subdirectories
+ subdir = virtual + '/'
+ if [r for r in repos if r.startswith(subdir)]:
+ makeindex(req, subdir)
+ return
+
+ up = virtual.rfind('/')
+ if up < 0:
+ break
+ virtual = virtual[:up]
- up = virtual.rfind('/')
- if up < 0:
- break
- virtual = virtual[:up]
-
- req.write(tmpl("notfound", repo=virtual))
- else:
- if req.form.has_key('static'):
- static = os.path.join(templater.templatepath(), "static")
- fname = req.form['static'][0]
- req.write(staticfile(static, fname, req)
- or tmpl("error", error="%r not found" % fname))
+ req.respond(404, tmpl("notfound", repo=virtual))
else:
- makeindex(req)
+ if req.form.has_key('static'):
+ static = os.path.join(templater.templatepath(), "static")
+ fname = req.form['static'][0]
+ req.write(staticfile(static, fname, req))
+ else:
+ makeindex(req)
+ except ErrorResponse, err:
+ req.respond(err.code, tmpl('error', error=err.message or ''))
finally:
tmpl = None
--- a/mercurial/hgweb/request.py Sun Dec 02 19:39:27 2007 +0100
+++ b/mercurial/hgweb/request.py Sun Dec 02 19:45:38 2007 +0100
@@ -8,16 +8,10 @@
import socket, cgi, errno
from mercurial.i18n import gettext as _
-
-class wsgiapplication(object):
- def __init__(self, destmaker):
- self.destmaker = destmaker
+from common import ErrorResponse, statusmessage
- def __call__(self, wsgienv, start_response):
- return _wsgirequest(self.destmaker(), wsgienv, start_response)
-
-class _wsgirequest(object):
- def __init__(self, destination, wsgienv, start_response):
+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"
@@ -32,7 +26,6 @@
self.form = cgi.parse(self.inp, self.env, keep_blank_values=1)
self.start_response = start_response
self.headers = []
- destination.run_wsgi(self)
out = property(lambda self: self)
@@ -42,25 +35,32 @@
def read(self, count=-1):
return self.inp.read(count)
- def write(self, *things):
+ def respond(self, status, *things):
for thing in things:
if hasattr(thing, "__iter__"):
for part in thing:
- self.write(part)
+ self.respond(status, part)
else:
thing = str(thing)
if self.server_write is None:
if not self.headers:
raise RuntimeError("request.write called before headers sent (%s)." % thing)
- self.server_write = self.start_response('200 Script output follows',
+ if isinstance(status, ErrorResponse):
+ status = statusmessage(status.code)
+ elif isinstance(status, int):
+ status = statusmessage(status)
+ self.server_write = self.start_response(status,
self.headers)
self.start_response = None
- self.headers = None
+ self.headers = []
try:
self.server_write(thing)
except socket.error, inst:
if inst[0] != errno.ECONNRESET:
raise
+
+ def write(self, *things):
+ self.respond('200 Script output follows', *things)
def writelines(self, lines):
for line in lines:
@@ -84,3 +84,9 @@
if length:
headers.append(('Content-length', str(length)))
self.header(headers)
+
+def wsgiapplication(app_maker):
+ application = app_maker()
+ def run_wsgi(env, respond):
+ application(env, respond)
+ return run_wsgi
--- a/mercurial/hgweb/server.py Sun Dec 02 19:39:27 2007 +0100
+++ b/mercurial/hgweb/server.py Sun Dec 02 19:45:38 2007 +0100
@@ -10,7 +10,6 @@
from mercurial import ui, hg, util, templater
from hgweb_mod import hgweb
from hgwebdir_mod import hgwebdir
-from request import wsgiapplication
from mercurial.i18n import gettext as _
def _splitURI(uri):
@@ -85,6 +84,7 @@
env['SERVER_NAME'] = self.server.server_name
env['SERVER_PORT'] = str(self.server.server_port)
env['REQUEST_URI'] = self.path
+ env['SCRIPT_NAME'] = ''
env['PATH_INFO'] = path_info
env['REMOTE_HOST'] = self.client_address[0]
env['REMOTE_ADDR'] = self.client_address[0]
@@ -121,10 +121,7 @@
self.saved_headers = []
self.sent_headers = False
self.length = None
- req = self.server.reqmaker(env, self._start_response)
- for data in req:
- if data:
- self._write(data)
+ self.server.application(env, self._start_response)
def send_headers(self):
if not self.saved_status:
@@ -250,7 +247,7 @@
raise hg.RepoError(_("There is no Mercurial repository here"
" (.hg not found)"))
return hgwebobj
- self.reqmaker = wsgiapplication(make_handler)
+ self.application = make_handler()
addr = address
if addr in ('', '::'):
--- a/mercurial/hgweb/wsgicgi.py Sun Dec 02 19:39:27 2007 +0100
+++ b/mercurial/hgweb/wsgicgi.py Sun Dec 02 19:45:38 2007 +0100
@@ -16,6 +16,7 @@
util.set_binary(sys.stdout)
environ = dict(os.environ.items())
+ environ.setdefault('PATH_INFO', '')
environ['wsgi.input'] = sys.stdin
environ['wsgi.errors'] = sys.stderr
environ['wsgi.version'] = (1, 0)
@@ -61,13 +62,4 @@
headers_set[:] = [status, response_headers]
return write
- result = application(environ, start_response)
- try:
- for data in result:
- if data: # don't send headers until body appears
- write(data)
- if not headers_sent:
- write('') # send headers now if body was empty
- finally:
- if hasattr(result,'close'):
- result.close()
+ application(environ, start_response)
--- a/mercurial/localrepo.py Sun Dec 02 19:39:27 2007 +0100
+++ b/mercurial/localrepo.py Sun Dec 02 19:45:38 2007 +0100
@@ -1710,6 +1710,8 @@
# Go through all our files in order sorted by name.
for fname in changedfiles:
filerevlog = self.file(fname)
+ if filerevlog.count() == 0:
+ raise util.abort(_("empty or missing revlog for %s") % fname)
# Toss out the filenodes that the recipient isn't really
# missing.
if msng_filenode_set.has_key(fname):
@@ -1794,6 +1796,8 @@
for fname in changedfiles:
filerevlog = self.file(fname)
+ if filerevlog.count() == 0:
+ raise util.abort(_("empty or missing revlog for %s") % fname)
nodeiter = gennodelst(filerevlog)
nodeiter = list(nodeiter)
if nodeiter:
--- a/mercurial/revlog.py Sun Dec 02 19:39:27 2007 +0100
+++ b/mercurial/revlog.py Sun Dec 02 19:45:38 2007 +0100
@@ -31,8 +31,13 @@
class RevlogError(Exception):
pass
+
class LookupError(RevlogError):
- pass
+ def __init__(self, name, message=None):
+ if message is None:
+ message = _('not found: %s') % name
+ RevlogError.__init__(self, message)
+ self.name = name
def getoffset(q):
return int(q >> 16)
@@ -321,7 +326,7 @@
e = _unpack(indexformatv0, cur)
# transform to revlogv1 format
e2 = (offset_type(e[0], 0), e[1], -1, e[2], e[3],
- nodemap[e[4]], nodemap[e[5]], e[6])
+ nodemap.get(e[4], nullrev), nodemap.get(e[5], nullrev), e[6])
index.append(e2)
nodemap[e[6]] = n
n += 1
@@ -516,7 +521,7 @@
try:
return self.nodemap[node]
except KeyError:
- raise LookupError(_('%s: no node %s') % (self.indexfile, hex(node)))
+ raise LookupError(hex(node), _('%s: no node %s') % (self.indexfile, hex(node)))
def node(self, rev):
return self.index[rev][7]
def linkrev(self, node):
@@ -836,7 +841,8 @@
for n in self.nodemap:
if n.startswith(bin_id) and hex(n).startswith(id):
if node is not None:
- raise LookupError(_("Ambiguous identifier"))
+ raise LookupError(hex(node),
+ _("Ambiguous identifier"))
node = n
if node is not None:
return node
@@ -855,7 +861,7 @@
if n:
return n
- raise LookupError(_("No match found"))
+ raise LookupError(id, _("No match found"))
def cmp(self, node, text):
"""compare text with a given file revision"""
@@ -1166,13 +1172,13 @@
for p in (p1, p2):
if not p in self.nodemap:
- raise LookupError(_("unknown parent %s") % short(p))
+ raise LookupError(hex(p), _("unknown parent %s") % short(p))
if not chain:
# retrieve the parent revision of the delta chain
chain = p1
if not chain in self.nodemap:
- raise LookupError(_("unknown base %s") % short(chain[:4]))
+ raise LookupError(hex(chain), _("unknown base %s") % short(chain[:4]))
# full versions are inserted when the needed deltas become
# comparable to the uncompressed text or when the previous
--- a/mercurial/util_win32.py Sun Dec 02 19:39:27 2007 +0100
+++ b/mercurial/util_win32.py Sun Dec 02 19:45:38 2007 +0100
@@ -16,6 +16,7 @@
from i18n import _
import errno, os, pywintypes, win32con, win32file, win32process
import cStringIO, winerror
+import osutil
from win32com.shell import shell,shellcon
class WinError:
@@ -179,6 +180,20 @@
def system_rcpath_win32():
'''return default os-specific hgrc search path'''
+ try:
+ value = win32api.RegQueryValue(
+ win32con.HKEY_LOCAL_MACHINE, 'SOFTWARE\\Mercurial')
+ rcpath = []
+ for p in value.split(os.pathsep):
+ if p.lower().endswith('mercurial.ini'):
+ rcpath.append(p)
+ elif os.path.isdir(p):
+ for f, kind in osutil.listdir(p):
+ if f.endswith('.rc'):
+ rcpath.append(os.path.join(p, f))
+ return rcpath
+ except pywintypes.error:
+ pass
proc = win32api.GetCurrentProcess()
try:
# This will fail on windows < NT
--- a/mercurial/verify.py Sun Dec 02 19:39:27 2007 +0100
+++ b/mercurial/verify.py Sun Dec 02 19:45:38 2007 +0100
@@ -62,9 +62,14 @@
repo.ui.status(_("repository uses revlog format %d\n") %
(revlogv1 and 1 or 0))
+ havecl = havemf = 1
seen = {}
repo.ui.status(_("checking changesets\n"))
- checksize(repo.changelog, "changelog")
+ if repo.changelog.count() == 0 and repo.manifest.count() > 1:
+ havecl = 0
+ err(0, _("empty or missing 00changelog.i"))
+ else:
+ checksize(repo.changelog, "changelog")
for i in xrange(repo.changelog.count()):
changesets += 1
@@ -96,14 +101,18 @@
seen = {}
repo.ui.status(_("checking manifests\n"))
- checkversion(repo.manifest, "manifest")
- checksize(repo.manifest, "manifest")
+ if repo.changelog.count() > 0 and repo.manifest.count() == 0:
+ havemf = 0
+ err(0, _("empty or missing 00manifest.i"))
+ else:
+ checkversion(repo.manifest, "manifest")
+ checksize(repo.manifest, "manifest")
for i in xrange(repo.manifest.count()):
n = repo.manifest.node(i)
l = repo.manifest.linkrev(n)
- if l < 0 or l >= repo.changelog.count():
+ if l < 0 or (havecl and l >= repo.changelog.count()):
err(None, _("bad link (%d) at manifest revision %d") % (l, i))
if n in neededmanifests:
@@ -132,38 +141,51 @@
repo.ui.status(_("crosschecking files in changesets and manifests\n"))
- nm = neededmanifests.items()
- nm.sort()
- for m, c in nm:
- err(m, _("changeset refers to unknown manifest %s") % short(c))
- del neededmanifests, nm
+ if havemf > 0:
+ nm = [(c, m) for m, c in neededmanifests.items()]
+ nm.sort()
+ for c, m in nm:
+ err(c, _("changeset refers to unknown manifest %s") % short(m))
+ del neededmanifests, nm
- for f in filenodes:
- if f not in filelinkrevs:
- lrs = [repo.manifest.linkrev(n) for n in filenodes[f]]
- lrs.sort()
- err(lrs[0], _("in manifest but not in changeset"), f)
+ if havecl:
+ fl = filenodes.keys()
+ fl.sort()
+ for f in fl:
+ if f not in filelinkrevs:
+ lrs = [repo.manifest.linkrev(n) for n in filenodes[f]]
+ lrs.sort()
+ err(lrs[0], _("in manifest but not in changeset"), f)
+ del fl
- for f in filelinkrevs:
- if f not in filenodes:
- lr = filelinkrevs[f][0]
- err(lr, _("in changeset but not in manifest"), f)
+ if havemf:
+ fl = filelinkrevs.keys()
+ fl.sort()
+ for f in fl:
+ if f not in filenodes:
+ lr = filelinkrevs[f][0]
+ err(lr, _("in changeset but not in manifest"), f)
+ del fl
repo.ui.status(_("checking files\n"))
- ff = filenodes.keys()
+ ff = dict.fromkeys(filenodes.keys() + filelinkrevs.keys()).keys()
ff.sort()
for f in ff:
if f == "/dev/null":
continue
files += 1
if not f:
- lr = repo.manifest.linkrev(filenodes[f][0])
- err(lr, _("file without name in manifest %s") % short(ff[n]))
+ lr = filelinkrevs[f][0]
+ err(lr, _("file without name in manifest"))
continue
fl = repo.file(f)
checkversion(fl, f)
checksize(fl, f)
+ if fl.count() == 0:
+ err(filelinkrevs[f][0], _("empty or missing revlog"), f)
+ continue
+
seen = {}
nodes = {nullid: 1}
for i in xrange(fl.count()):
@@ -171,7 +193,7 @@
n = fl.node(i)
flr = fl.linkrev(n)
- if flr not in filelinkrevs.get(f, []):
+ if flr < 0 or (havecl and flr not in filelinkrevs.get(f, [])):
if flr < 0 or flr >= repo.changelog.count():
err(None, _("rev %d point to nonexistent changeset %d")
% (i, flr), f)
@@ -182,14 +204,16 @@
warn(_(" (expected %s)") % filelinkrevs[f][0])
flr = None # can't be trusted
else:
- filelinkrevs[f].remove(flr)
+ if havecl:
+ filelinkrevs[f].remove(flr)
if n in seen:
err(flr, _("duplicate revision %d") % i, f)
- if n not in filenodes[f]:
- err(flr, _("%s not in manifests") % (short(n)), f)
- else:
- del filenodes[f][n]
+ if f in filenodes:
+ if havemf and n not in filenodes[f]:
+ err(flr, _("%s not in manifests") % (short(n)), f)
+ else:
+ del filenodes[f][n]
# verify contents
try:
@@ -230,11 +254,12 @@
(short(n), inst), f)
# cross-check
- fns = [(repo.manifest.linkrev(filenodes[f][n]), n)
- for n in filenodes[f]]
- fns.sort()
- for lr, node in fns:
- err(lr, _("%s in manifests not found") % short(node), f)
+ if f in filenodes:
+ fns = [(repo.manifest.linkrev(filenodes[f][n]), n)
+ for n in filenodes[f]]
+ fns.sort()
+ for lr, node in fns:
+ err(lr, _("%s in manifests not found") % short(node), f)
repo.ui.status(_("%d files, %d changesets, %d total revisions\n") %
(files, changesets, revisions))
@@ -247,4 +272,3 @@
repo.ui.warn(_("(first damaged changeset appears to be %d)\n")
% firstbad[0])
return 1
-
--- a/templates/gitweb/notfound.tmpl Sun Dec 02 19:39:27 2007 +0100
+++ b/templates/gitweb/notfound.tmpl Sun Dec 02 19:45:38 2007 +0100
@@ -1,5 +1,5 @@
{header}
-<title>Mercurial repositories index</title>
+<title>Mercurial repository not found</title>
</head>
<body>
--- a/templates/map-cmdline.default Sun Dec 02 19:39:27 2007 +0100
+++ b/templates/map-cmdline.default Sun Dec 02 19:45:38 2007 +0100
@@ -1,10 +1,13 @@
changeset = 'changeset: {rev}:{node|short}\n{branches}{tags}{parents}user: {author}\ndate: {date|date}\nsummary: {desc|firstline}\n\n'
changeset_quiet = '{rev}:{node|short}\n'
-changeset_verbose = 'changeset: {rev}:{node|short}\n{branches}{tags}{parents}{manifest}user: {author}\ndate: {date|date}\n{files}{file_adds}{file_dels}{file_copies}description:\n{desc|strip}\n\n\n'
-changeset_debug = 'changeset: {rev}:{node}\n{branches}{tags}{parents}{manifest}user: {author}\ndate: {date|date}\n{files}{file_adds}{file_dels}{file_copies}{extras}description:\n{desc|strip}\n\n\n'
+changeset_verbose = 'changeset: {rev}:{node|short}\n{branches}{tags}{parents}user: {author}\ndate: {date|date}\n{files}{file_copies}description:\n{desc|strip}\n\n\n'
+changeset_debug = 'changeset: {rev}:{node}\n{branches}{tags}{parents}{manifest}user: {author}\ndate: {date|date}\n{file_mods}{file_adds}{file_dels}{file_copies}{extras}description:\n{desc|strip}\n\n\n'
start_files = 'files: '
file = ' {file}'
end_files = '\n'
+start_file_mods = 'files: '
+file_mod = ' {file_mod}'
+end_file_mods = '\n'
start_file_adds = 'files+: '
file_add = ' {file_add}'
end_file_adds = '\n'
--- a/templates/notfound.tmpl Sun Dec 02 19:39:27 2007 +0100
+++ b/templates/notfound.tmpl Sun Dec 02 19:45:38 2007 +0100
@@ -1,9 +1,9 @@
#header#
-<title>Mercurial repositories index</title>
+<title>Mercurial repository not found</title>
</head>
<body>
-<h2>Mercurial Repositories</h2>
+<h2>Mercurial repository not found</h2>
The specified repository "#repo|escape#" is unknown, sorry.
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/templates/raw/error.tmpl Sun Dec 02 19:45:38 2007 +0100
@@ -0,0 +1,2 @@
+#header#
+error: #error#
--- a/templates/raw/map Sun Dec 02 19:39:27 2007 +0100
+++ b/templates/raw/map Sun Dec 02 19:45:38 2007 +0100
@@ -18,4 +18,6 @@
manifestdirentry = 'drwxr-xr-x {basename}\n'
manifestfileentry = '{permissions|permissions} {size} {basename}\n'
index = index.tmpl
+notfound = notfound.tmpl
+error = error.tmpl
indexentry = '#url#\n'
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/templates/raw/notfound.tmpl Sun Dec 02 19:45:38 2007 +0100
@@ -0,0 +1,2 @@
+#header#
+error: repository #repo# not found
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/templates/static/highlight.css Sun Dec 02 19:45:38 2007 +0100
@@ -0,0 +1,59 @@
+.c { color: #808080 } /* Comment */
+.err { color: #F00000; background-color: #F0A0A0 } /* Error */
+.k { color: #008000; font-weight: bold } /* Keyword */
+.o { color: #303030 } /* Operator */
+.cm { color: #808080 } /* Comment.Multiline */
+.cp { color: #507090 } /* Comment.Preproc */
+.c1 { color: #808080 } /* Comment.Single */
+.cs { color: #cc0000; font-weight: bold } /* Comment.Special */
+.gd { color: #A00000 } /* Generic.Deleted */
+.ge { font-style: italic } /* Generic.Emph */
+.gr { color: #FF0000 } /* Generic.Error */
+.gh { color: #000080; font-weight: bold } /* Generic.Heading */
+.gi { color: #00A000 } /* Generic.Inserted */
+.go { color: #808080 } /* Generic.Output */
+.gp { color: #c65d09; font-weight: bold } /* Generic.Prompt */
+.gs { font-weight: bold } /* Generic.Strong */
+.gu { color: #800080; font-weight: bold } /* Generic.Subheading */
+.gt { color: #0040D0 } /* Generic.Traceback */
+.kc { color: #008000; font-weight: bold } /* Keyword.Constant */
+.kd { color: #008000; font-weight: bold } /* Keyword.Declaration */
+.kp { color: #003080; font-weight: bold } /* Keyword.Pseudo */
+.kr { color: #008000; font-weight: bold } /* Keyword.Reserved */
+.kt { color: #303090; font-weight: bold } /* Keyword.Type */
+.m { color: #6000E0; font-weight: bold } /* Literal.Number */
+.s { background-color: #fff0f0 } /* Literal.String */
+.na { color: #0000C0 } /* Name.Attribute */
+.nb { color: #007020 } /* Name.Builtin */
+.nc { color: #B00060; font-weight: bold } /* Name.Class */
+.no { color: #003060; font-weight: bold } /* Name.Constant */
+.nd { color: #505050; font-weight: bold } /* Name.Decorator */
+.ni { color: #800000; font-weight: bold } /* Name.Entity */
+.ne { color: #F00000; font-weight: bold } /* Name.Exception */
+.nf { color: #0060B0; font-weight: bold } /* Name.Function */
+.nl { color: #907000; font-weight: bold } /* Name.Label */
+.nn { color: #0e84b5; font-weight: bold } /* Name.Namespace */
+.nt { color: #007000 } /* Name.Tag */
+.nv { color: #906030 } /* Name.Variable */
+.ow { color: #000000; font-weight: bold } /* Operator.Word */
+.w { color: #bbbbbb } /* Text.Whitespace */
+.mf { color: #6000E0; font-weight: bold } /* Literal.Number.Float */
+.mh { color: #005080; font-weight: bold } /* Literal.Number.Hex */
+.mi { color: #0000D0; font-weight: bold } /* Literal.Number.Integer */
+.mo { color: #4000E0; font-weight: bold } /* Literal.Number.Oct */
+.sb { background-color: #fff0f0 } /* Literal.String.Backtick */
+.sc { color: #0040D0 } /* Literal.String.Char */
+.sd { color: #D04020 } /* Literal.String.Doc */
+.s2 { background-color: #fff0f0 } /* Literal.String.Double */
+.se { color: #606060; font-weight: bold; background-color: #fff0f0 } /* Literal.String.Escape */
+.sh { background-color: #fff0f0 } /* Literal.String.Heredoc */
+.si { background-color: #e0e0e0 } /* Literal.String.Interpol */
+.sx { color: #D02000; background-color: #fff0f0 } /* Literal.String.Other */
+.sr { color: #000000; background-color: #fff0ff } /* Literal.String.Regex */
+.s1 { background-color: #fff0f0 } /* Literal.String.Single */
+.ss { color: #A06000 } /* Literal.String.Symbol */
+.bp { color: #007020 } /* Name.Builtin.Pseudo */
+.vc { color: #306090 } /* Name.Variable.Class */
+.vg { color: #d07000; font-weight: bold } /* Name.Variable.Global */
+.vi { color: #3030B0 } /* Name.Variable.Instance */
+.il { color: #0000D0; font-weight: bold } /* Literal.Number.Integer.Long */
--- a/tests/get-with-headers.py Sun Dec 02 19:39:27 2007 +0100
+++ b/tests/get-with-headers.py Sun Dec 02 19:45:38 2007 +0100
@@ -14,3 +14,7 @@
print "%s: %s" % (h, response.getheader(h))
print
sys.stdout.write(response.read())
+
+if 200 <= response.status <= 299:
+ sys.exit(0)
+sys.exit(1)
--- a/tests/test-command-template Sun Dec 02 19:39:27 2007 +0100
+++ b/tests/test-command-template Sun Dec 02 19:45:38 2007 +0100
@@ -89,8 +89,8 @@
cat changelog
echo "# keys work"
-for key in author branches date desc file_adds file_dels files \
- manifest node parents rev tags; do
+for key in author branches date desc file_adds file_dels file_mods \
+ files manifest node parents rev tags; do
for mode in '' --verbose --debug; do
hg log $mode --template "$key$mode: {$key}\n"
done
--- a/tests/test-command-template.out Sun Dec 02 19:39:27 2007 +0100
+++ b/tests/test-command-template.out Sun Dec 02 19:45:38 2007 +0100
@@ -260,22 +260,22 @@
other 3
desc--debug: line 1
line 2
-file_adds:
-file_adds:
+file_adds: second
file_adds:
-file_adds:
-file_adds:
-file_adds:
+file_adds: d
file_adds:
file_adds:
+file_adds: c
+file_adds: b
+file_adds: a
+file_adds--verbose: second
file_adds--verbose:
-file_adds--verbose:
+file_adds--verbose: d
file_adds--verbose:
file_adds--verbose:
-file_adds--verbose:
-file_adds--verbose:
-file_adds--verbose:
-file_adds--verbose:
+file_adds--verbose: c
+file_adds--verbose: b
+file_adds--verbose: a
file_adds--debug: second
file_adds--debug:
file_adds--debug: d
@@ -308,6 +308,30 @@
file_dels--debug:
file_dels--debug:
file_dels--debug:
+file_mods:
+file_mods:
+file_mods:
+file_mods:
+file_mods: c
+file_mods:
+file_mods:
+file_mods:
+file_mods--verbose:
+file_mods--verbose:
+file_mods--verbose:
+file_mods--verbose:
+file_mods--verbose: c
+file_mods--verbose:
+file_mods--verbose:
+file_mods--verbose:
+file_mods--debug:
+file_mods--debug:
+file_mods--debug:
+file_mods--debug:
+file_mods--debug: c
+file_mods--debug:
+file_mods--debug:
+file_mods--debug:
files: second
files:
files: d
@@ -324,30 +348,30 @@
files--verbose: c
files--verbose: b
files--verbose: a
-files--debug:
+files--debug: second
files--debug:
-files--debug:
+files--debug: d
files--debug:
files--debug: c
-files--debug:
-files--debug:
-files--debug:
-manifest:
-manifest:
-manifest:
-manifest:
-manifest:
-manifest:
-manifest:
-manifest:
-manifest--verbose:
-manifest--verbose:
-manifest--verbose:
-manifest--verbose:
-manifest--verbose:
-manifest--verbose:
-manifest--verbose:
-manifest--verbose:
+files--debug: c
+files--debug: b
+files--debug: a
+manifest: 7:f2dbc354b94e
+manifest: 6:91015e9dbdd7
+manifest: 5:4dc3def4f9b4
+manifest: 4:90ae8dda64e1
+manifest: 3:cb5a1327723b
+manifest: 2:6e0e82995c35
+manifest: 1:4e8d705b1e53
+manifest: 0:a0c8bcbbb45c
+manifest--verbose: 7:f2dbc354b94e
+manifest--verbose: 6:91015e9dbdd7
+manifest--verbose: 5:4dc3def4f9b4
+manifest--verbose: 4:90ae8dda64e1
+manifest--verbose: 3:cb5a1327723b
+manifest--verbose: 2:6e0e82995c35
+manifest--verbose: 1:4e8d705b1e53
+manifest--verbose: 0:a0c8bcbbb45c
manifest--debug: 7:f2dbc354b94e5ec0b4f10680ee0cee816101d0bf
manifest--debug: 6:91015e9dbdd76a6791085d12b0a0ec7fcd22ffbf
manifest--debug: 5:4dc3def4f9b4c6e8de820f6ee74737f91e96a216
--- a/tests/test-convert Sun Dec 02 19:39:27 2007 +0100
+++ b/tests/test-convert Sun Dec 02 19:45:38 2007 +0100
@@ -1,7 +1,11 @@
#!/bin/sh
-echo "[extensions]" >> $HGRCPATH
-echo "convert=" >> $HGRCPATH
+cat >> $HGRCPATH <<EOF
+[extensions]
+convert=
+[convert]
+hg.saverev=False
+EOF
hg help convert
--- a/tests/test-convert-cvs.out Sun Dec 02 19:39:27 2007 +0100
+++ b/tests/test-convert-cvs.out Sun Dec 02 19:45:38 2007 +0100
@@ -44,7 +44,6 @@
checking in src/a,v
checking in src/b/c,v
% convert again
-destination src-hg is a Mercurial repository
connecting to cvsrepo
scanning source...
sorting...
@@ -56,7 +55,6 @@
c
c
% convert again with --filemap
-destination src-filemap is a Mercurial repository
connecting to cvsrepo
scanning source...
sorting...
--- a/tests/test-convert-hg-sink Sun Dec 02 19:39:27 2007 +0100
+++ b/tests/test-convert-hg-sink Sun Dec 02 19:45:38 2007 +0100
@@ -1,7 +1,11 @@
#!/bin/sh
-echo "[extensions]" >> $HGRCPATH
-echo "hgext.convert=" >> $HGRCPATH
+cat >> $HGRCPATH <<EOF
+[extensions]
+convert=
+[convert]
+hg.saverev=False
+EOF
hg init orig
cd orig
--- a/tests/test-convert-hg-sink.out Sun Dec 02 19:39:27 2007 +0100
+++ b/tests/test-convert-hg-sink.out Sun Dec 02 19:45:38 2007 +0100
@@ -37,7 +37,6 @@
a 0 -1 unset baz
copy: bar -> baz
% add a new revision in the original repo
-destination new is a Mercurial repository
scanning source...
sorting...
converting...
--- a/tests/test-convert-hg-source Sun Dec 02 19:39:27 2007 +0100
+++ b/tests/test-convert-hg-source Sun Dec 02 19:45:38 2007 +0100
@@ -1,7 +1,11 @@
#!/bin/sh
-echo "[extensions]" >> $HGRCPATH
-echo "hgext.convert=" >> $HGRCPATH
+cat >> $HGRCPATH <<EOF
+[extensions]
+convert=
+[convert]
+hg.saverev=False
+EOF
hg init orig
cd orig
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/tests/test-convert-hg-svn Sun Dec 02 19:45:38 2007 +0100
@@ -0,0 +1,66 @@
+#!/bin/sh
+
+"$TESTDIR/hghave" svn svn-bindings || exit 80
+
+fix_path()
+{
+ tr '\\' /
+}
+
+echo "[extensions]" >> $HGRCPATH
+echo "convert = " >> $HGRCPATH
+
+svnpath=`pwd`/svn-repo
+svnadmin create $svnpath
+
+cat > $svnpath/hooks/pre-revprop-change <<'EOF'
+#!/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
+EOF
+chmod +x $svnpath/hooks/pre-revprop-change
+
+svnurl=file://$svnpath
+svn co $svnurl $svnpath-wc
+
+cd $svnpath-wc
+echo a > a
+svn add a
+svn ci -m'added a' a
+
+cd ..
+
+echo % initial roundtrip
+hg convert -s svn -d hg $svnpath-wc $svnpath-hg | grep -v initializing
+hg convert -s hg -d svn $svnpath-hg $svnpath-wc
+
+echo % second roundtrip should do nothing
+hg convert -s svn -d hg $svnpath-wc $svnpath-hg
+hg convert -s hg -d svn $svnpath-hg $svnpath-wc
+
+echo % new hg rev
+
+hg clone $svnpath-hg $svnpath-work
+echo b > $svnpath-work/b
+hg --cwd $svnpath-work add b
+hg --cwd $svnpath-work ci -mb
+
+echo % echo hg to svn
+hg --cwd $svnpath-hg pull -q $svnpath-work
+hg convert -s hg -d svn $svnpath-hg $svnpath-wc
+
+echo % svn back to hg should do nothing
+hg convert -s svn -d hg $svnpath-wc $svnpath-hg
+echo % hg back to svn should do nothing
+hg convert -s hg -d svn $svnpath-hg $svnpath-wc
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/tests/test-convert-hg-svn.out Sun Dec 02 19:45:38 2007 +0100
@@ -0,0 +1,35 @@
+Checked out revision 0.
+A a
+Adding a
+Transmitting file data .
+Committed revision 1.
+% initial roundtrip
+scanning source...
+sorting...
+converting...
+0 added a
+scanning source...
+sorting...
+converting...
+% second roundtrip should do nothing
+scanning source...
+sorting...
+converting...
+scanning source...
+sorting...
+converting...
+% new hg rev
+1 files updated, 0 files merged, 0 files removed, 0 files unresolved
+% echo hg to svn
+scanning source...
+sorting...
+converting...
+0 b
+% svn back to hg should do nothing
+scanning source...
+sorting...
+converting...
+% hg back to svn should do nothing
+scanning source...
+sorting...
+converting...
--- a/tests/test-convert-svn Sun Dec 02 19:39:27 2007 +0100
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,115 +0,0 @@
-#!/bin/sh
-
-"$TESTDIR/hghave" svn svn-bindings || exit 80
-
-fix_path()
-{
- tr '\\' /
-}
-
-echo "[extensions]" >> $HGRCPATH
-echo "convert = " >> $HGRCPATH
-
-svnadmin create svn-repo
-
-echo % initial svn import
-mkdir t
-cd t
-echo a > a
-cd ..
-
-svnpath=`pwd | fix_path`
-# SVN wants all paths to start with a slash. Unfortunately,
-# Windows ones don't. Handle that.
-expr $svnpath : "\/" > /dev/null
-if [ $? -ne 0 ]; then
- svnpath='/'$svnpath
-fi
-
-svnurl=file://$svnpath/svn-repo/trunk
-svn import -m init t $svnurl | fix_path
-
-echo % update svn repository
-svn co $svnurl t2 | fix_path
-cd t2
-echo b >> a
-echo b > b
-svn add b
-svn ci -m changea
-cd ..
-
-echo % convert to hg once
-hg convert $svnurl
-
-echo % update svn repository again
-cd t2
-echo c >> a
-echo c >> b
-svn ci -m changeb
-cd ..
-
-echo % test incremental conversion
-hg convert $svnurl
-
-echo % test filemap
-echo 'include b' > filemap
-hg convert --filemap filemap $svnurl fmap
-echo '[extensions]' >> $HGRCPATH
-echo 'hgext.graphlog =' >> $HGRCPATH
-hg glog -R fmap --template '#rev# #desc|firstline# files: #files#\n'
-
-########################################
-
-echo "# now tests that it works with trunk/branches/tags layout"
-echo
-echo % initial svn import
-mkdir projA
-cd projA
-mkdir trunk
-mkdir branches
-mkdir tags
-cd ..
-
-svnurl=file://$svnpath/svn-repo/projA
-svn import -m "init projA" projA $svnurl | fix_path
-
-
-echo % update svn repository
-svn co $svnurl/trunk A | fix_path
-cd A
-echo hello > letter.txt
-svn add letter.txt
-svn ci -m hello
-
-echo world >> letter.txt
-svn ci -m world
-
-svn copy -m "tag v0.1" $svnurl/trunk $svnurl/tags/v0.1
-
-echo 'nice day today!' >> letter.txt
-svn ci -m "nice day"
-cd ..
-
-echo % convert to hg once
-hg convert $svnurl A-hg
-
-echo % update svn repository again
-cd A
-echo "see second letter" >> letter.txt
-echo "nice to meet you" > letter2.txt
-svn add letter2.txt
-svn ci -m "second letter"
-
-svn copy -m "tag v0.2" $svnurl/trunk $svnurl/tags/v0.2
-
-echo "blah-blah-blah" >> letter2.txt
-svn ci -m "work in progress"
-cd ..
-
-echo % test incremental conversion
-hg convert $svnurl A-hg
-
-cd A-hg
-hg glog --template '#rev# #desc|firstline# files: #files#\n'
-hg tags -q
-cd ..
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/tests/test-convert-svn-sink Sun Dec 02 19:45:38 2007 +0100
@@ -0,0 +1,93 @@
+#!/bin/sh
+
+"$TESTDIR/hghave" svn svn-bindings || exit 80
+
+echo "[extensions]" >> $HGRCPATH
+echo "convert = " >> $HGRCPATH
+
+hg init a
+
+echo a > a/a
+mkdir -p a/d1/d2
+echo b > a/d1/d2/b
+echo % add
+hg --cwd a ci -d '0 0' -A -m 'add a file'
+
+echo a >> a/a
+echo % modify
+hg --cwd a ci -d '1 0' -m 'modify a file'
+hg --cwd a tip -q
+
+hg convert -d svn a
+(cd a-hg-wc; svn up; svn st -v; svn log --xml -v --limit=2 | sed 's,<date>.*,<date/>,')
+ls a a-hg-wc
+cmp a/a a-hg-wc/a && echo same || echo different
+
+hg --cwd a mv a b
+echo % rename
+hg --cwd a ci -d '2 0' -m 'rename a file'
+hg --cwd a tip -q
+
+hg convert -d svn a
+(cd a-hg-wc; svn up; svn st -v; svn log --xml -v --limit=1 | sed 's,<date>.*,<date/>,')
+ls a a-hg-wc
+
+hg --cwd a cp b c
+echo % copy
+hg --cwd a ci -d '3 0' -m 'copy a file'
+hg --cwd a tip -q
+
+hg convert -d svn a
+(cd a-hg-wc; svn up; svn st -v; svn log --xml -v --limit=1 | sed 's,<date>.*,<date/>,')
+ls a a-hg-wc
+
+hg --cwd a rm b
+echo % remove
+hg --cwd a ci -d '4 0' -m 'remove a file'
+hg --cwd a tip -q
+
+hg convert -d svn a
+(cd a-hg-wc; svn up; svn st -v; svn log --xml -v --limit=1 | sed 's,<date>.*,<date/>,')
+ls a a-hg-wc
+
+chmod +x a/c
+echo % executable
+hg --cwd a ci -d '5 0' -m 'make a file executable'
+hg --cwd a tip -q
+
+hg convert -d svn a
+(cd a-hg-wc; svn up; svn st -v; svn log --xml -v --limit=1 | sed 's,<date>.*,<date/>,')
+test -x a-hg-wc/c && echo executable || echo not executable
+
+echo % branchy history
+
+hg init b
+echo base > b/b
+hg --cwd b ci -d '0 0' -Ambase
+
+echo left-1 >> b/b
+echo left-1 > b/left-1
+hg --cwd b ci -d '1 0' -Amleft-1
+
+echo left-2 >> b/b
+echo left-2 > b/left-2
+hg --cwd b ci -d '2 0' -Amleft-2
+
+hg --cwd b up 0
+
+echo right-1 >> b/b
+echo right-1 > b/right-1
+hg --cwd b ci -d '3 0' -Amright-1
+
+echo right-2 >> b/b
+echo right-2 > b/right-2
+hg --cwd b ci -d '4 0' -Amright-2
+
+hg --cwd b up -C 2
+hg --cwd b merge
+hg --cwd b revert -r 2 b
+hg --cwd b ci -d '5 0' -m 'merge'
+
+hg convert -d svn b
+echo % expect 4 changes
+(cd b-hg-wc; svn up; svn st -v; svn log --xml -v | sed 's,<date>.*,<date/>,')
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/tests/test-convert-svn-sink.out Sun Dec 02 19:45:38 2007 +0100
@@ -0,0 +1,281 @@
+% add
+adding a
+adding d1/d2/b
+% modify
+1:e0e2b8a9156b
+assuming destination a-hg
+initializing svn repo 'a-hg'
+initializing svn wc 'a-hg-wc'
+scanning source...
+sorting...
+converting...
+1 add a file
+0 modify a file
+At revision 2.
+ 2 2 test .
+ 2 2 test a
+ 2 1 test d1
+ 2 1 test d1/d2
+ 2 1 test d1/d2/b
+<?xml version="1.0"?>
+<log>
+<logentry
+ revision="2">
+<author>test</author>
+<date/>
+<paths>
+<path
+ action="M">/a</path>
+</paths>
+<msg>modify a file</msg>
+</logentry>
+<logentry
+ revision="1">
+<author>test</author>
+<date/>
+<paths>
+<path
+ action="A">/a</path>
+<path
+ action="A">/d1</path>
+<path
+ action="A">/d1/d2</path>
+<path
+ action="A">/d1/d2/b</path>
+</paths>
+<msg>add a file</msg>
+</logentry>
+</log>
+a:
+a
+d1
+
+a-hg-wc:
+a
+d1
+same
+% rename
+2:7009fc4efb34
+assuming destination a-hg
+initializing svn wc 'a-hg-wc'
+scanning source...
+sorting...
+converting...
+0 rename a file
+At revision 3.
+ 3 3 test .
+ 3 3 test b
+ 3 1 test d1
+ 3 1 test d1/d2
+ 3 1 test d1/d2/b
+<?xml version="1.0"?>
+<log>
+<logentry
+ revision="3">
+<author>test</author>
+<date/>
+<paths>
+<path
+ action="D">/a</path>
+<path
+ copyfrom-path="/a"
+ copyfrom-rev="2"
+ action="A">/b</path>
+</paths>
+<msg>rename a file</msg>
+</logentry>
+</log>
+a:
+b
+d1
+
+a-hg-wc:
+b
+d1
+% copy
+3:56c519973ce6
+assuming destination a-hg
+initializing svn wc 'a-hg-wc'
+scanning source...
+sorting...
+converting...
+0 copy a file
+At revision 4.
+ 4 4 test .
+ 4 3 test b
+ 4 4 test c
+ 4 1 test d1
+ 4 1 test d1/d2
+ 4 1 test d1/d2/b
+<?xml version="1.0"?>
+<log>
+<logentry
+ revision="4">
+<author>test</author>
+<date/>
+<paths>
+<path
+ copyfrom-path="/b"
+ copyfrom-rev="3"
+ action="A">/c</path>
+</paths>
+<msg>copy a file</msg>
+</logentry>
+</log>
+a:
+b
+c
+d1
+
+a-hg-wc:
+b
+c
+d1
+% remove
+4:ed4dc9a6f585
+assuming destination a-hg
+initializing svn wc 'a-hg-wc'
+scanning source...
+sorting...
+converting...
+0 remove a file
+At revision 5.
+ 5 5 test .
+ 5 4 test c
+ 5 1 test d1
+ 5 1 test d1/d2
+ 5 1 test d1/d2/b
+<?xml version="1.0"?>
+<log>
+<logentry
+ revision="5">
+<author>test</author>
+<date/>
+<paths>
+<path
+ action="D">/b</path>
+</paths>
+<msg>remove a file</msg>
+</logentry>
+</log>
+a:
+c
+d1
+
+a-hg-wc:
+c
+d1
+% executable
+5:f205b3636d77
+svn: Path 'b' does not exist
+assuming destination a-hg
+initializing svn wc 'a-hg-wc'
+scanning source...
+sorting...
+converting...
+0 make a file executable
+abort: svn exited with status 1
+At revision 5.
+ 5 5 test .
+ M 5 4 test c
+ 5 1 test d1
+ 5 1 test d1/d2
+ 5 1 test d1/d2/b
+<?xml version="1.0"?>
+<log>
+<logentry
+ revision="5">
+<author>test</author>
+<date/>
+<paths>
+<path
+ action="D">/b</path>
+</paths>
+<msg>remove a file</msg>
+</logentry>
+</log>
+executable
+% branchy history
+adding b
+adding left-1
+adding left-2
+1 files updated, 0 files merged, 2 files removed, 0 files unresolved
+adding right-1
+adding right-2
+3 files updated, 0 files merged, 2 files removed, 0 files unresolved
+warning: conflicts during merge.
+merging b
+merging b failed!
+2 files updated, 0 files merged, 0 files removed, 1 files unresolved
+There are unresolved merges, you can redo the full merge using:
+ hg update -C 2
+ hg merge 4
+assuming destination b-hg
+initializing svn repo 'b-hg'
+initializing svn wc 'b-hg-wc'
+scanning source...
+sorting...
+converting...
+5 base
+4 left-1
+3 left-2
+2 right-1
+1 right-2
+0 merge
+% expect 4 changes
+At revision 4.
+ 4 4 test .
+ 4 3 test b
+ 4 2 test left-1
+ 4 3 test left-2
+ 4 4 test right-1
+ 4 4 test right-2
+<?xml version="1.0"?>
+<log>
+<logentry
+ revision="4">
+<author>test</author>
+<date/>
+<paths>
+<path
+ action="A">/right-1</path>
+<path
+ action="A">/right-2</path>
+</paths>
+<msg>merge</msg>
+</logentry>
+<logentry
+ revision="3">
+<author>test</author>
+<date/>
+<paths>
+<path
+ action="M">/b</path>
+<path
+ action="A">/left-2</path>
+</paths>
+<msg>left-2</msg>
+</logentry>
+<logentry
+ revision="2">
+<author>test</author>
+<date/>
+<paths>
+<path
+ action="M">/b</path>
+<path
+ action="A">/left-1</path>
+</paths>
+<msg>left-1</msg>
+</logentry>
+<logentry
+ revision="1">
+<author>test</author>
+<date/>
+<paths>
+<path
+ action="A">/b</path>
+</paths>
+<msg>base</msg>
+</logentry>
+</log>
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/tests/test-convert-svn-source Sun Dec 02 19:45:38 2007 +0100
@@ -0,0 +1,115 @@
+#!/bin/sh
+
+"$TESTDIR/hghave" svn svn-bindings || exit 80
+
+fix_path()
+{
+ tr '\\' /
+}
+
+echo "[extensions]" >> $HGRCPATH
+echo "convert = " >> $HGRCPATH
+
+svnadmin create svn-repo
+
+echo % initial svn import
+mkdir t
+cd t
+echo a > a
+cd ..
+
+svnpath=`pwd | fix_path`
+# SVN wants all paths to start with a slash. Unfortunately,
+# Windows ones don't. Handle that.
+expr $svnpath : "\/" > /dev/null
+if [ $? -ne 0 ]; then
+ svnpath='/'$svnpath
+fi
+
+svnurl=file://$svnpath/svn-repo/trunk
+svn import -m init t $svnurl | fix_path
+
+echo % update svn repository
+svn co $svnurl t2 | fix_path
+cd t2
+echo b >> a
+echo b > b
+svn add b
+svn ci -m changea
+cd ..
+
+echo % convert to hg once
+hg convert $svnurl
+
+echo % update svn repository again
+cd t2
+echo c >> a
+echo c >> b
+svn ci -m changeb
+cd ..
+
+echo % test incremental conversion
+hg convert $svnurl
+
+echo % test filemap
+echo 'include b' > filemap
+hg convert --filemap filemap $svnurl fmap
+echo '[extensions]' >> $HGRCPATH
+echo 'hgext.graphlog =' >> $HGRCPATH
+hg glog -R fmap --template '#rev# #desc|firstline# files: #files#\n'
+
+########################################
+
+echo "# now tests that it works with trunk/branches/tags layout"
+echo
+echo % initial svn import
+mkdir projA
+cd projA
+mkdir trunk
+mkdir branches
+mkdir tags
+cd ..
+
+svnurl=file://$svnpath/svn-repo/projA
+svn import -m "init projA" projA $svnurl | fix_path
+
+
+echo % update svn repository
+svn co $svnurl/trunk A | fix_path
+cd A
+echo hello > letter.txt
+svn add letter.txt
+svn ci -m hello
+
+echo world >> letter.txt
+svn ci -m world
+
+svn copy -m "tag v0.1" $svnurl/trunk $svnurl/tags/v0.1
+
+echo 'nice day today!' >> letter.txt
+svn ci -m "nice day"
+cd ..
+
+echo % convert to hg once
+hg convert $svnurl A-hg
+
+echo % update svn repository again
+cd A
+echo "see second letter" >> letter.txt
+echo "nice to meet you" > letter2.txt
+svn add letter2.txt
+svn ci -m "second letter"
+
+svn copy -m "tag v0.2" $svnurl/trunk $svnurl/tags/v0.2
+
+echo "blah-blah-blah" >> letter2.txt
+svn ci -m "work in progress"
+cd ..
+
+echo % test incremental conversion
+hg convert $svnurl A-hg
+
+cd A-hg
+hg glog --template '#rev# #desc|firstline# files: #files#\n'
+hg tags -q
+cd ..
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/tests/test-convert-svn-source.out Sun Dec 02 19:45:38 2007 +0100
@@ -0,0 +1,112 @@
+% initial svn import
+Adding t/a
+
+Committed revision 1.
+% update svn repository
+A t2/a
+Checked out revision 1.
+A b
+Sending a
+Adding b
+Transmitting file data ..
+Committed revision 2.
+% convert to hg once
+assuming destination trunk-hg
+initializing destination trunk-hg repository
+scanning source...
+sorting...
+converting...
+1 init
+0 changea
+% update svn repository again
+Sending a
+Sending b
+Transmitting file data ..
+Committed revision 3.
+% test incremental conversion
+assuming destination trunk-hg
+scanning source...
+sorting...
+converting...
+0 changeb
+% test filemap
+initializing destination fmap repository
+scanning source...
+sorting...
+converting...
+2 init
+1 changea
+0 changeb
+o 1 changeb files: b
+|
+o 0 changea files: b
+
+# now tests that it works with trunk/branches/tags layout
+
+% initial svn import
+Adding projA/trunk
+Adding projA/branches
+Adding projA/tags
+
+Committed revision 4.
+% update svn repository
+Checked out revision 4.
+A letter.txt
+Adding letter.txt
+Transmitting file data .
+Committed revision 5.
+Sending letter.txt
+Transmitting file data .
+Committed revision 6.
+
+Committed revision 7.
+Sending letter.txt
+Transmitting file data .
+Committed revision 8.
+% convert to hg once
+initializing destination A-hg repository
+scanning source...
+sorting...
+converting...
+3 init projA
+2 hello
+1 world
+0 nice day
+updating tags
+% update svn repository again
+A letter2.txt
+Sending letter.txt
+Adding letter2.txt
+Transmitting file data ..
+Committed revision 9.
+
+Committed revision 10.
+Sending letter2.txt
+Transmitting file data .
+Committed revision 11.
+% test incremental conversion
+scanning source...
+sorting...
+converting...
+1 second letter
+0 work in progress
+updating tags
+o 7 update tags files: .hgtags
+|
+o 6 work in progress files: letter2.txt
+|
+o 5 second letter files: letter.txt letter2.txt
+|
+o 4 update tags files: .hgtags
+|
+o 3 nice day files: letter.txt
+|
+o 2 world files: letter.txt
+|
+o 1 hello files: letter.txt
+|
+o 0 init projA files:
+
+tip
+v0.2
+v0.1
--- a/tests/test-convert-svn.out Sun Dec 02 19:39:27 2007 +0100
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,114 +0,0 @@
-% initial svn import
-Adding t/a
-
-Committed revision 1.
-% update svn repository
-A t2/a
-Checked out revision 1.
-A b
-Sending a
-Adding b
-Transmitting file data ..
-Committed revision 2.
-% convert to hg once
-assuming destination trunk-hg
-initializing destination trunk-hg repository
-scanning source...
-sorting...
-converting...
-1 init
-0 changea
-% update svn repository again
-Sending a
-Sending b
-Transmitting file data ..
-Committed revision 3.
-% test incremental conversion
-assuming destination trunk-hg
-destination trunk-hg is a Mercurial repository
-scanning source...
-sorting...
-converting...
-0 changeb
-% test filemap
-initializing destination fmap repository
-scanning source...
-sorting...
-converting...
-2 init
-1 changea
-0 changeb
-o 1 changeb files: b
-|
-o 0 changea files: b
-
-# now tests that it works with trunk/branches/tags layout
-
-% initial svn import
-Adding projA/trunk
-Adding projA/branches
-Adding projA/tags
-
-Committed revision 4.
-% update svn repository
-Checked out revision 4.
-A letter.txt
-Adding letter.txt
-Transmitting file data .
-Committed revision 5.
-Sending letter.txt
-Transmitting file data .
-Committed revision 6.
-
-Committed revision 7.
-Sending letter.txt
-Transmitting file data .
-Committed revision 8.
-% convert to hg once
-initializing destination A-hg repository
-scanning source...
-sorting...
-converting...
-3 init projA
-2 hello
-1 world
-0 nice day
-updating tags
-% update svn repository again
-A letter2.txt
-Sending letter.txt
-Adding letter2.txt
-Transmitting file data ..
-Committed revision 9.
-
-Committed revision 10.
-Sending letter2.txt
-Transmitting file data .
-Committed revision 11.
-% test incremental conversion
-destination A-hg is a Mercurial repository
-scanning source...
-sorting...
-converting...
-1 second letter
-0 work in progress
-updating tags
-o 7 update tags files: .hgtags
-|
-o 6 work in progress files: letter2.txt
-|
-o 5 second letter files: letter.txt letter2.txt
-|
-o 4 update tags files: .hgtags
-|
-o 3 nice day files: letter.txt
-|
-o 2 world files: letter.txt
-|
-o 1 hello files: letter.txt
-|
-o 0 init projA files:
-
-tip
-v0.2
-v0.1
--- a/tests/test-convert.out Sun Dec 02 19:39:27 2007 +0100
+++ b/tests/test-convert.out Sun Dec 02 19:45:38 2007 +0100
@@ -11,6 +11,7 @@
Accepted destination formats:
- Mercurial
+ - Subversion (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
@@ -54,6 +55,24 @@
subdirectory into the root of the repository, use '.' as the path to
rename to.
+ Back end options:
+
+ --config convert.hg.clonebranches=False (boolean)
+ hg target: XXX not documented
+ --config convert.hg.saverev=True (boolean)
+ hg source: allow target to preserve source revision ID
+ --config convert.hg.tagsbranch=default (branch name)
+ hg target: XXX not documented
+ --config convert.hg.usebranchnames=True (boolean)
+ hg target: preserve branch names
+
+ --config convert.svn.branches=branches (directory name)
+ svn source: specify the directory containing branches
+ --config convert.svn.tags=tags (directory name)
+ svn source: specify the directory containing tags
+ --config convert.svn.trunk=trunk (directory name)
+ svn source: specify the name of the trunk branch
+
options:
-A --authors username mapping filename
--- a/tests/test-execute-bit Sun Dec 02 19:39:27 2007 +0100
+++ b/tests/test-execute-bit Sun Dec 02 19:45:38 2007 +0100
@@ -1,5 +1,7 @@
#!/bin/sh
+"$TESTDIR/hghave" execbit || exit 80
+
hg init
echo a > a
hg ci -d'0 0' -Am'not executable'
--- a/tests/test-hgweb Sun Dec 02 19:39:27 2007 +0100
+++ b/tests/test-hgweb Sun Dec 02 19:45:38 2007 +0100
@@ -1,4 +1,5 @@
#!/bin/sh
+# Some tests for hgweb. Tests static files, plain files and different 404's.
hg init test
cd test
@@ -7,7 +8,25 @@
echo foo > foo
hg ci -Ambase -d '0 0'
hg serve -p $HGPORT -d --pid-file=hg.pid
+cat hg.pid >> $DAEMON_PIDS
echo % manifest
("$TESTDIR/get-with-headers.py" localhost:$HGPORT '/file/tip/?style=raw')
("$TESTDIR/get-with-headers.py" localhost:$HGPORT '/file/tip/da?style=raw')
-kill `cat hg.pid`
+
+echo % plain file
+"$TESTDIR/get-with-headers.py" localhost:$HGPORT '/file/tip/foo?style=raw'
+
+echo % should give a 404 - static file that does not exist
+"$TESTDIR/get-with-headers.py" localhost:$HGPORT '/static/bogus'
+
+echo % should give a 404 - bad revision
+"$TESTDIR/get-with-headers.py" localhost:$HGPORT '/file/spam/foo?style=raw'
+
+echo % should give a 400 - bad command
+"$TESTDIR/get-with-headers.py" localhost:$HGPORT '/file/tip/foo?cmd=spam&style=raw' | sed 's/400.*/400/'
+
+echo % should give a 404 - file does not exist
+"$TESTDIR/get-with-headers.py" localhost:$HGPORT '/file/tip/bork?style=raw'
+
+echo % static file
+"$TESTDIR/get-with-headers.py" localhost:$HGPORT '/static/style-gitweb.css'
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/tests/test-hgweb-no-request-uri Sun Dec 02 19:45:38 2007 +0100
@@ -0,0 +1,77 @@
+#!/bin/sh
+# This tests if hgweb and hgwebdir still work if the REQUEST_URI variable is
+# no longer passed with the request. Instead, SCRIPT_NAME and PATH_INFO
+# should be used from d74fc8dec2b4 onward to route the request.
+
+mkdir repo
+cd repo
+hg init
+echo foo > bar
+hg add bar
+hg commit -m "test" -d "0 0" -u "Testing"
+hg tip
+
+cat > request.py <<EOF
+from mercurial.hgweb import hgweb, hgwebdir
+from StringIO import StringIO
+import os, sys
+
+errors = StringIO()
+input = StringIO()
+
+def startrsp(headers, data):
+ print '---- HEADERS'
+ print headers
+ print '---- DATA'
+ print data
+ return output.write
+
+env = {
+ 'wsgi.version': (1, 0),
+ 'wsgi.url_scheme': 'http',
+ 'wsgi.errors': errors,
+ 'wsgi.input': input,
+ 'wsgi.multithread': False,
+ 'wsgi.multiprocess': False,
+ 'wsgi.run_once': False,
+ 'REQUEST_METHOD': 'GET',
+ 'SCRIPT_NAME': '',
+ 'SERVER_NAME': '127.0.0.1',
+ 'SERVER_PORT': os.environ['HGPORT'],
+ 'SERVER_PROTOCOL': 'HTTP/1.0'
+}
+
+output = StringIO()
+env['PATH_INFO'] = '/'
+env['QUERY_STRING'] = 'style=atom'
+hgweb('.', name = 'repo')(env, startrsp)
+print output.getvalue()
+print '---- ERRORS'
+print errors.getvalue()
+
+output = StringIO()
+env['PATH_INFO'] = '/file/tip/'
+env['QUERY_STRING'] = 'style=raw'
+hgweb('.', name = 'repo')(env, startrsp)
+print output.getvalue()
+print '---- ERRORS'
+print errors.getvalue()
+
+output = StringIO()
+env['PATH_INFO'] = '/'
+env['QUERY_STRING'] = 'style=raw'
+hgwebdir({'repo': '.'})(env, startrsp)
+print output.getvalue()
+print '---- ERRORS'
+print errors.getvalue()
+
+output = StringIO()
+env['PATH_INFO'] = '/repo/file/tip/'
+env['QUERY_STRING'] = 'style=raw'
+hgwebdir({'repo': '.'})(env, startrsp)
+print output.getvalue()
+print '---- ERRORS'
+print errors.getvalue()
+EOF
+
+python request.py | sed "s/http:\/\/127\.0\.0\.1:[0-9]*\//http:\/\/127.0.0.1\//"
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/tests/test-hgweb-no-request-uri.out Sun Dec 02 19:45:38 2007 +0100
@@ -0,0 +1,72 @@
+changeset: 0:4cbec7e6f8c4
+tag: tip
+user: Testing
+date: Thu Jan 01 00:00:00 1970 +0000
+summary: test
+
+---- HEADERS
+200 Script output follows
+---- DATA
+[('content-type', 'application/atom+xml; charset=ascii')]
+<?xml version="1.0" encoding="ascii"?>
+<feed xmlns="http://www.w3.org/2005/Atom">
+ <!-- Changelog -->
+ <id>http://127.0.0.1/</id>
+ <link rel="self" href="http://127.0.0.1/atom-log"/>
+ <link rel="alternate" href="http://127.0.0.1/"/>
+ <title>repo Changelog</title>
+ <updated>1970-01-01T00:00:00+00:00</updated>
+
+ <entry>
+ <title>test</title>
+ <id>http://www.selenic.com/mercurial/#changeset-4cbec7e6f8c42eb52b6b52670e1f7560ae9a101e</id>
+ <link href="http://127.0.0.1/rev/4cbec7e6f8c42eb52b6b52670e1f7560ae9a101e"/>
+ <author>
+ <name>Testing</name>
+ <email>Testing</email>
+ </author>
+ <updated>1970-01-01T00:00:00+00:00</updated>
+ <published>1970-01-01T00:00:00+00:00</published>
+ <content type="xhtml">
+ <div xmlns="http://www.w3.org/1999/xhtml">
+ <pre xml:space="preserve">test</pre>
+ </div>
+ </content>
+ </entry>
+
+</feed>
+
+---- ERRORS
+
+---- HEADERS
+200 Script output follows
+---- DATA
+[('content-type', 'text/plain; charset=ascii')]
+
+-rw-r--r-- 4 bar
+
+
+
+---- ERRORS
+
+---- HEADERS
+200 Script output follows
+---- DATA
+[('content-type', 'text/plain; charset=ascii')]
+
+/repo/
+
+
+---- ERRORS
+
+---- HEADERS
+200 Script output follows
+---- DATA
+[('content-type', 'text/plain; charset=ascii')]
+
+-rw-r--r-- 4 bar
+
+
+
+---- ERRORS
+
--- a/tests/test-hgweb.out Sun Dec 02 19:39:27 2007 +0100
+++ b/tests/test-hgweb.out Sun Dec 02 19:45:38 2007 +0100
@@ -14,3 +14,123 @@
-rw-r--r-- 4 foo
+% plain file
+200 Script output follows
+
+foo
+% should give a 404 - static file that does not exist
+404 Not Found
+
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
+<html>
+<head>
+<link rel="icon" href="/static/hgicon.png" type="image/png">
+<meta name="robots" content="index, nofollow" />
+<link rel="stylesheet" href="/static/style.css" type="text/css" />
+
+<title>Mercurial Error</title>
+</head>
+<body>
+
+<h2>Mercurial Error</h2>
+
+<p>
+An error occured while processing your request:
+</p>
+<p>
+Not Found
+</p>
+
+
+<div class="logo">
+powered by<br/>
+<a href="http://www.selenic.com/mercurial/">mercurial</a>
+</div>
+
+</body>
+</html>
+
+% should give a 404 - bad revision
+404 Not Found
+
+
+error: revision not found: spam
+% should give a 400 - bad command
+400
+
+
+error: No such method: spam
+% should give a 404 - file does not exist
+404 Not Found
+
+
+error: Path not found: bork/
+% static file
+200 Script output follows
+
+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; }
+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 }
+a.rss_logo {
+ float:right; padding:3px 0px; width:35px; 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;
+}
+a.rss_logo: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;
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/tests/test-hgwebdir Sun Dec 02 19:45:38 2007 +0100
@@ -0,0 +1,77 @@
+#!/bin/sh
+# Tests some basic hgwebdir functionality. Tests setting up paths and
+# collection, different forms of 404s and the subdirectory support.
+
+mkdir webdir
+cd webdir
+
+hg init a
+echo a > a/a
+hg --cwd a ci -Ama -d'1 0'
+
+hg init b
+echo b > b/b
+hg --cwd b ci -Amb -d'2 0'
+
+hg init c
+echo c > c/c
+hg --cwd c ci -Amc -d'3 0'
+root=`pwd`
+
+cd ..
+
+cat > paths.conf <<EOF
+[paths]
+a=$root/a
+b=$root/b
+EOF
+
+hg serve -p $HGPORT -d --pid-file=hg.pid --webdir-conf paths.conf \
+ -A access-paths.log -E error-paths.log
+cat hg.pid >> $DAEMON_PIDS
+
+echo % should give a 404 - file does not exist
+"$TESTDIR/get-with-headers.py" localhost:$HGPORT '/a/file/tip/bork?style=raw'
+
+echo % should succeed
+"$TESTDIR/get-with-headers.py" localhost:$HGPORT '/?style=raw'
+"$TESTDIR/get-with-headers.py" localhost:$HGPORT '/a/file/tip/a?style=raw'
+"$TESTDIR/get-with-headers.py" localhost:$HGPORT '/b/file/tip/b?style=raw'
+
+echo % should give a 404 - repo is not published
+"$TESTDIR/get-with-headers.py" localhost:$HGPORT '/c/file/tip/c?style=raw'
+
+cat > paths.conf <<EOF
+[paths]
+t/a=$root/a
+b=$root/b
+EOF
+
+hg serve -p $HGPORT1 -d --pid-file=hg.pid --webdir-conf paths.conf \
+ -A access-paths.log -E error-paths.log
+cat hg.pid >> $DAEMON_PIDS
+
+echo % should succeed, slashy names
+"$TESTDIR/get-with-headers.py" localhost:$HGPORT1 '/?style=raw'
+"$TESTDIR/get-with-headers.py" localhost:$HGPORT1 '/t?style=raw'
+"$TESTDIR/get-with-headers.py" localhost:$HGPORT1 '/t/?style=raw'
+"$TESTDIR/get-with-headers.py" localhost:$HGPORT1 '/t/a?style=atom' \
+ | sed "s/http:\/\/[^/]*\//http:\/\/127.0.0.1\//"
+"$TESTDIR/get-with-headers.py" localhost:$HGPORT1 '/t/a/?style=atom' \
+ | sed "s/http:\/\/[^/]*\//http:\/\/127.0.0.1\//"
+"$TESTDIR/get-with-headers.py" localhost:$HGPORT1 '/t/a/file/tip/a?style=raw'
+
+cat > collections.conf <<EOF
+[collections]
+$root=$root
+EOF
+
+hg serve -p $HGPORT2 -d --pid-file=hg.pid --webdir-conf collections.conf \
+ -A access-collections.log -E error-collections.log
+cat hg.pid >> $DAEMON_PIDS
+
+echo % should succeed
+"$TESTDIR/get-with-headers.py" localhost:$HGPORT2 '/?style=raw'
+"$TESTDIR/get-with-headers.py" localhost:$HGPORT2 '/a/file/tip/a?style=raw'
+"$TESTDIR/get-with-headers.py" localhost:$HGPORT2 '/b/file/tip/b?style=raw'
+"$TESTDIR/get-with-headers.py" localhost:$HGPORT2 '/c/file/tip/c?style=raw'
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/tests/test-hgwebdir.out Sun Dec 02 19:45:38 2007 +0100
@@ -0,0 +1,121 @@
+adding a
+adding b
+adding c
+% should give a 404 - file does not exist
+404 Not Found
+
+
+error: Path not found: bork/
+% should succeed
+200 Script output follows
+
+
+/a/
+/b/
+
+200 Script output follows
+
+a
+200 Script output follows
+
+b
+% should give a 404 - repo is not published
+404 Not Found
+
+
+error: repository c not found
+% should succeed, slashy names
+200 Script output follows
+
+
+/b/
+/t/a/
+
+200 Script output follows
+
+
+/t/a/
+
+200 Script output follows
+
+
+/t/a/
+
+200 Script output follows
+
+<?xml version="1.0" encoding="ascii"?>
+<feed xmlns="http://127.0.0.1/2005/Atom">
+ <!-- Changelog -->
+ <id>http://127.0.0.1/t/a/</id>
+ <link rel="self" href="http://127.0.0.1/t/a/atom-log"/>
+ <link rel="alternate" href="http://127.0.0.1/t/a/"/>
+ <title>t/a Changelog</title>
+ <updated>1970-01-01T00:00:01+00:00</updated>
+
+ <entry>
+ <title>a</title>
+ <id>http://127.0.0.1/mercurial/#changeset-8580ff50825a50c8f716709acdf8de0deddcd6ab</id>
+ <link href="http://127.0.0.1/t/a/rev/8580ff50825a50c8f716709acdf8de0deddcd6ab"/>
+ <author>
+ <name>test</name>
+ <email>test</email>
+ </author>
+ <updated>1970-01-01T00:00:01+00:00</updated>
+ <published>1970-01-01T00:00:01+00:00</published>
+ <content type="xhtml">
+ <div xmlns="http://127.0.0.1/1999/xhtml">
+ <pre xml:space="preserve">a</pre>
+ </div>
+ </content>
+ </entry>
+
+</feed>
+200 Script output follows
+
+<?xml version="1.0" encoding="ascii"?>
+<feed xmlns="http://127.0.0.1/2005/Atom">
+ <!-- Changelog -->
+ <id>http://127.0.0.1/t/a/</id>
+ <link rel="self" href="http://127.0.0.1/t/a/atom-log"/>
+ <link rel="alternate" href="http://127.0.0.1/t/a/"/>
+ <title>t/a Changelog</title>
+ <updated>1970-01-01T00:00:01+00:00</updated>
+
+ <entry>
+ <title>a</title>
+ <id>http://127.0.0.1/mercurial/#changeset-8580ff50825a50c8f716709acdf8de0deddcd6ab</id>
+ <link href="http://127.0.0.1/t/a/rev/8580ff50825a50c8f716709acdf8de0deddcd6ab"/>
+ <author>
+ <name>test</name>
+ <email>test</email>
+ </author>
+ <updated>1970-01-01T00:00:01+00:00</updated>
+ <published>1970-01-01T00:00:01+00:00</published>
+ <content type="xhtml">
+ <div xmlns="http://127.0.0.1/1999/xhtml">
+ <pre xml:space="preserve">a</pre>
+ </div>
+ </content>
+ </entry>
+
+</feed>
+200 Script output follows
+
+a
+% should succeed
+200 Script output follows
+
+
+/a/
+/b/
+/c/
+
+200 Script output follows
+
+a
+200 Script output follows
+
+b
+200 Script output follows
+
+c
--- a/tests/test-import Sun Dec 02 19:39:27 2007 +0100
+++ b/tests/test-import Sun Dec 02 19:45:38 2007 +0100
@@ -57,7 +57,7 @@
cat > mkmsg.py <<EOF
import email.Message, sys
msg = email.Message.Message()
-msg.set_payload('email commit message\n' + open('tip.patch').read())
+msg.set_payload('email commit message\n' + open('tip.patch', 'rb').read())
msg['Subject'] = 'email patch'
msg['From'] = 'email patcher'
sys.stdout.write(msg.as_string())
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/tests/test-merge-types Sun Dec 02 19:45:38 2007 +0100
@@ -0,0 +1,36 @@
+#!/bin/sh
+
+hg init
+echo a > a
+hg ci -Amadd
+
+chmod +x a
+hg ci -mexecutable
+
+hg up 0
+rm a
+ln -s symlink a
+hg ci -msymlink
+
+hg merge
+
+echo % symlink is left parent, executable is right
+
+if [ -L a ]; then
+ echo a is a symlink
+ readlink a
+elif [ -x a ]; then
+ echo a is executable
+fi
+
+hg update -C 1
+hg merge
+
+echo % symlink is right parent, executable is left
+
+if [ -L a ]; then
+ echo a is a symlink
+ readlink a
+elif [ -x a ]; then
+ echo a is executable
+fi
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/tests/test-merge-types.out Sun Dec 02 19:45:38 2007 +0100
@@ -0,0 +1,1 @@
+### This test is for a known, unfixed bug ###
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/tests/test-newcgi Sun Dec 02 19:45:38 2007 +0100
@@ -0,0 +1,91 @@
+#!/bin/sh
+# This tests if CGI files from after d0db3462d568 but
+# before d74fc8dec2b4 still work.
+
+hg init test
+
+cat >hgweb.cgi <<HGWEB
+#!/usr/bin/env python
+#
+# An example CGI script to use hgweb, edit as necessary
+
+import cgitb
+cgitb.enable()
+
+from mercurial import demandimport; demandimport.enable()
+from mercurial.hgweb import hgweb
+from mercurial.hgweb import wsgicgi
+from mercurial.hgweb.request import wsgiapplication
+
+def make_web_app():
+ return hgweb("test", "Empty test repository")
+
+wsgicgi.launch(wsgiapplication(make_web_app))
+HGWEB
+chmod 755 hgweb.cgi
+
+cat >hgweb.config <<HGWEBDIRCONF
+[paths]
+test = test
+HGWEBDIRCONF
+
+cat >hgwebdir.cgi <<HGWEBDIR
+#!/usr/bin/env python
+#
+# An example CGI script to export multiple hgweb repos, edit as necessary
+
+import cgitb
+cgitb.enable()
+
+from mercurial import demandimport; demandimport.enable()
+from mercurial.hgweb import hgwebdir
+from mercurial.hgweb import wsgicgi
+from mercurial.hgweb.request import wsgiapplication
+
+def make_web_app():
+ return hgwebdir("hgweb.config")
+
+wsgicgi.launch(wsgiapplication(make_web_app))
+HGWEBDIR
+chmod 755 hgwebdir.cgi
+
+DOCUMENT_ROOT="/var/www/hg"; export DOCUMENT_ROOT
+GATEWAY_INTERFACE="CGI/1.1"; export GATEWAY_INTERFACE
+HTTP_ACCEPT="text/xml,application/xml,application/xhtml+xml,text/html;q=0.9,text/plain;q=0.8,image/png,*/*;q=0.5"; export HTTP_ACCEPT
+HTTP_ACCEPT_CHARSET="ISO-8859-1,utf-8;q=0.7,*;q=0.7"; export HTTP_ACCEPT_CHARSET
+HTTP_ACCEPT_ENCODING="gzip,deflate"; export HTTP_ACCEPT_ENCODING
+HTTP_ACCEPT_LANGUAGE="en-us,en;q=0.5"; export HTTP_ACCEPT_LANGUAGE
+HTTP_CACHE_CONTROL="max-age=0"; export HTTP_CACHE_CONTROL
+HTTP_CONNECTION="keep-alive"; export HTTP_CONNECTION
+HTTP_HOST="hg.omnifarious.org"; export HTTP_HOST
+HTTP_KEEP_ALIVE="300"; export HTTP_KEEP_ALIVE
+HTTP_USER_AGENT="Mozilla/5.0 (X11; U; Linux x86_64; en-US; rv:1.8.0.4) Gecko/20060608 Ubuntu/dapper-security Firefox/1.5.0.4"; export HTTP_USER_AGENT
+PATH_INFO="/"; export PATH_INFO
+PATH_TRANSLATED="/var/www/hg/index.html"; export PATH_TRANSLATED
+QUERY_STRING=""; export QUERY_STRING
+REMOTE_ADDR="127.0.0.2"; export REMOTE_ADDR
+REMOTE_PORT="44703"; export REMOTE_PORT
+REQUEST_METHOD="GET"; export REQUEST_METHOD
+REQUEST_URI="/test/"; export REQUEST_URI
+SCRIPT_FILENAME="/home/hopper/hg_public/test.cgi"; export SCRIPT_FILENAME
+SCRIPT_NAME="/test"; export SCRIPT_NAME
+SCRIPT_URI="http://hg.omnifarious.org/test/"; export SCRIPT_URI
+SCRIPT_URL="/test/"; export SCRIPT_URL
+SERVER_ADDR="127.0.0.1"; export SERVER_ADDR
+SERVER_ADMIN="eric@localhost"; export SERVER_ADMIN
+SERVER_NAME="hg.omnifarious.org"; export SERVER_NAME
+SERVER_PORT="80"; export SERVER_PORT
+SERVER_PROTOCOL="HTTP/1.1"; export SERVER_PROTOCOL
+SERVER_SIGNATURE="<address>Apache/2.0.53 (Fedora) Server at hg.omnifarious.org Port 80</address>\; export SERVER_SIGNATURE
+"
+SERVER_SOFTWARE="Apache/2.0.53 (Fedora)"; export SERVER_SOFTWARE
+python hgweb.cgi >page1 2>&1 ; echo $?
+python hgwebdir.cgi >page2 2>&1 ; echo $?
+PATH_INFO="/test/"
+PATH_TRANSLATED="/var/something/test.cgi"
+REQUEST_URI="/test/test/"
+SCRIPT_URI="http://hg.omnifarious.org/test/test/"
+SCRIPT_URL="/test/test/"
+python hgwebdir.cgi >page3 2>&1 ; echo $?
+fgrep -i error page1 page2 page3 && exit 1
+exit 0
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/tests/test-newcgi.out Sun Dec 02 19:45:38 2007 +0100
@@ -0,0 +1,3 @@
+0
+0
+0
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/tests/test-newercgi Sun Dec 02 19:45:38 2007 +0100
@@ -0,0 +1,84 @@
+#!/bin/sh
+# This is a rudimentary test of the CGI files as of d74fc8dec2b4.
+
+hg init test
+
+cat >hgweb.cgi <<HGWEB
+#!/usr/bin/env python
+#
+# An example CGI script to use hgweb, edit as necessary
+
+import cgitb
+cgitb.enable()
+
+from mercurial import demandimport; demandimport.enable()
+from mercurial.hgweb import hgweb
+from mercurial.hgweb import wsgicgi
+
+application = hgweb("test", "Empty test repository")
+wsgicgi.launch(application)
+HGWEB
+chmod 755 hgweb.cgi
+
+cat >hgweb.config <<HGWEBDIRCONF
+[paths]
+test = test
+HGWEBDIRCONF
+
+cat >hgwebdir.cgi <<HGWEBDIR
+#!/usr/bin/env python
+#
+# An example CGI script to export multiple hgweb repos, edit as necessary
+
+import cgitb
+cgitb.enable()
+
+from mercurial import demandimport; demandimport.enable()
+from mercurial.hgweb import hgwebdir
+from mercurial.hgweb import wsgicgi
+
+application = hgwebdir("hgweb.config")
+wsgicgi.launch(application)
+HGWEBDIR
+chmod 755 hgwebdir.cgi
+
+DOCUMENT_ROOT="/var/www/hg"; export DOCUMENT_ROOT
+GATEWAY_INTERFACE="CGI/1.1"; export GATEWAY_INTERFACE
+HTTP_ACCEPT="text/xml,application/xml,application/xhtml+xml,text/html;q=0.9,text/plain;q=0.8,image/png,*/*;q=0.5"; export HTTP_ACCEPT
+HTTP_ACCEPT_CHARSET="ISO-8859-1,utf-8;q=0.7,*;q=0.7"; export HTTP_ACCEPT_CHARSET
+HTTP_ACCEPT_ENCODING="gzip,deflate"; export HTTP_ACCEPT_ENCODING
+HTTP_ACCEPT_LANGUAGE="en-us,en;q=0.5"; export HTTP_ACCEPT_LANGUAGE
+HTTP_CACHE_CONTROL="max-age=0"; export HTTP_CACHE_CONTROL
+HTTP_CONNECTION="keep-alive"; export HTTP_CONNECTION
+HTTP_HOST="hg.omnifarious.org"; export HTTP_HOST
+HTTP_KEEP_ALIVE="300"; export HTTP_KEEP_ALIVE
+HTTP_USER_AGENT="Mozilla/5.0 (X11; U; Linux x86_64; en-US; rv:1.8.0.4) Gecko/20060608 Ubuntu/dapper-security Firefox/1.5.0.4"; export HTTP_USER_AGENT
+PATH_INFO="/"; export PATH_INFO
+PATH_TRANSLATED="/var/www/hg/index.html"; export PATH_TRANSLATED
+QUERY_STRING=""; export QUERY_STRING
+REMOTE_ADDR="127.0.0.2"; export REMOTE_ADDR
+REMOTE_PORT="44703"; export REMOTE_PORT
+REQUEST_METHOD="GET"; export REQUEST_METHOD
+REQUEST_URI="/test/"; export REQUEST_URI
+SCRIPT_FILENAME="/home/hopper/hg_public/test.cgi"; export SCRIPT_FILENAME
+SCRIPT_NAME="/test"; export SCRIPT_NAME
+SCRIPT_URI="http://hg.omnifarious.org/test/"; export SCRIPT_URI
+SCRIPT_URL="/test/"; export SCRIPT_URL
+SERVER_ADDR="127.0.0.1"; export SERVER_ADDR
+SERVER_ADMIN="eric@localhost"; export SERVER_ADMIN
+SERVER_NAME="hg.omnifarious.org"; export SERVER_NAME
+SERVER_PORT="80"; export SERVER_PORT
+SERVER_PROTOCOL="HTTP/1.1"; export SERVER_PROTOCOL
+SERVER_SIGNATURE="<address>Apache/2.0.53 (Fedora) Server at hg.omnifarious.org Port 80</address>\; export SERVER_SIGNATURE
+"
+SERVER_SOFTWARE="Apache/2.0.53 (Fedora)"; export SERVER_SOFTWARE
+python hgweb.cgi >page1 2>&1 ; echo $?
+python hgwebdir.cgi >page2 2>&1 ; echo $?
+PATH_INFO="/test/"
+PATH_TRANSLATED="/var/something/test.cgi"
+REQUEST_URI="/test/test/"
+SCRIPT_URI="http://hg.omnifarious.org/test/test/"
+SCRIPT_URL="/test/test/"
+python hgwebdir.cgi >page3 2>&1 ; echo $?
+fgrep -i error page1 page2 page3 && exit 1
+exit 0
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/tests/test-newercgi.out Sun Dec 02 19:45:38 2007 +0100
@@ -0,0 +1,3 @@
+0
+0
+0
--- a/tests/test-non-interactive-wsgi Sun Dec 02 19:39:27 2007 +0100
+++ b/tests/test-non-interactive-wsgi Sun Dec 02 19:45:38 2007 +0100
@@ -1,4 +1,6 @@
#!/bin/sh
+# Tests if hgweb can run without touching sys.stdin, as is required
+# by the WSGI standard and strictly implemented by mod_wsgi.
mkdir repo
cd repo
@@ -11,7 +13,6 @@
cat > request.py <<EOF
from mercurial import dispatch
from mercurial.hgweb.hgweb_mod import hgweb
-from mercurial.hgweb.request import _wsgirequest
from mercurial.ui import ui
from mercurial import hg
from StringIO import StringIO
@@ -62,7 +63,7 @@
'SERVER_PROTOCOL': 'HTTP/1.0'
}
-_wsgirequest(hgweb('.'), env, startrsp)
+hgweb('.')(env, startrsp)
print '---- ERRORS'
print errors.getvalue()
EOF
--- a/tests/test-oldcgi Sun Dec 02 19:39:27 2007 +0100
+++ b/tests/test-oldcgi Sun Dec 02 19:45:38 2007 +0100
@@ -1,4 +1,5 @@
#!/bin/sh
+# This tests if CGI files from before d0db3462d568 still work.
hg init test