--- a/Makefile Wed Jun 28 17:42:17 2006 -0500
+++ b/Makefile Wed Jul 05 13:28:25 2006 -0500
@@ -37,12 +37,20 @@
rm -f MANIFEST mercurial/__version__.py mercurial/*.so tests/*.err
$(MAKE) -C doc clean
-install: all
+install: install-bin install-doc
+
+install-bin: build
$(PYTHON) setup.py install --prefix="$(PREFIX)" --force
+
+install-doc: doc
cd doc && $(MAKE) $(MFLAGS) install
-install-home: all
+install-home: install-home-bin install-home-doc
+
+install-home-bin: build
$(PYTHON) setup.py install --home="$(HOME)" --force
+
+install-home-doc: doc
cd doc && $(MAKE) $(MFLAGS) PREFIX="$(HOME)" install
dist: tests dist-notests
@@ -57,5 +65,5 @@
cd tests && $(PYTHON) run-tests.py $@
-.PHONY: help all local build doc clean install install-home dist dist-notests tests
-
+.PHONY: help all local build doc clean install install-bin install-doc \
+ install-home install-home-bin install-home-doc dist dist-notests tests
--- a/contrib/macosx/Readme.html Wed Jun 28 17:42:17 2006 -0500
+++ b/contrib/macosx/Readme.html Wed Jul 05 13:28:25 2006 -0500
@@ -18,13 +18,10 @@
<p class="p2"><br></p>
<p class="p3">This is <i>not</i> a stand-alone version of Mercurial.</p>
<p class="p2"><br></p>
-<p class="p3">To use it, you must have the “official unofficial” MacPython 2.4.1 installed.</p>
+<p class="p3">To use it, you must have the Universal MacPython 2.4.3 from <a href="http://www.python.org">www.python.org</a> installed.</p>
<p class="p2"><br></p>
-<p class="p3">You can download MacPython 2.4.1 from here:</p>
-<p class="p4"><span class="s1"><a href="http://python.org/ftp/python/2.4.1/MacPython-OSX-2.4.1-1.dmg">http://python.org/ftp/python/2.4.1/MacPython-OSX-2.4.1-1.dmg</a></span></p>
-<p class="p2"><br></p>
-<p class="p3">For more information on MacPython, go here:</p>
-<p class="p4"><span class="s1"><a href="http://undefined.org/python/">http://undefined.org/python</a></span></p>
+<p class="p3">You can download MacPython 2.4.3 from here:</p>
+<p class="p4"><span class="s1"><a href="http://www.python.org/ftp/python/2.4.3/Universal-MacPython-2.4.3-2006-04-07.dmg">http://www.python.org/ftp/python/2.4.3/Universal-MacPython-2.4.3-2006-04-07.dmg</a></span></p>
<p class="p2"><br></p>
<p class="p1"><b>After you install</b></p>
<p class="p2"><br></p>
--- a/contrib/macosx/Welcome.html Wed Jun 28 17:42:17 2006 -0500
+++ b/contrib/macosx/Welcome.html Wed Jul 05 13:28:25 2006 -0500
@@ -12,6 +12,6 @@
<body>
<p class="p1">This is a prepackaged release of <a href="http://www.selenic.com/mercurial">Mercurial</a> for Mac OS X.</p>
<p class="p2"><br></p>
-<p class="p1">It is based on Mercurial 0.8.</p>
+<p class="p1">It is based on Mercurial 0.9.</p>
</body>
</html>
--- a/contrib/mercurial.el Wed Jun 28 17:42:17 2006 -0500
+++ b/contrib/mercurial.el Wed Jul 05 13:28:25 2006 -0500
@@ -653,7 +653,7 @@
you're already familiar with VC, the same keybindings and functions
will generally work.
-Below is a list of many common SCM tasks. In the list, `G/L'
+Below is a list of many common SCM tasks. In the list, `G/L\'
indicates whether a key binding is global (G) to a repository or local
(L) to a file. Many commands take a prefix argument.
@@ -682,6 +682,8 @@
Update working directory after pull G C-c h u hg-update
See changes that can be pushed G C-c h . hg-outgoing
Push changes G C-c h > hg-push"
+ (unless vc-make-backup-files
+ (set (make-local-variable 'backup-inhibited) t))
(run-hooks 'hg-mode-hook))
(defun hg-find-file-hook ()
@@ -729,6 +731,8 @@
(goto-char 0)
(cd (hg-root path)))
(when update
+ (unless vc-make-backup-files
+ (set (make-local-variable 'backup-inhibited) t))
(with-current-buffer buf
(hg-mode-line)))))
@@ -968,6 +972,7 @@
(cd (hg-root path)))
(when update
(with-current-buffer buf
+ (set (make-local-variable 'backup-inhibited) nil)
(hg-mode-line)))))
(defun hg-incoming (&optional repo)
--- a/contrib/win32/mercurial.ini Wed Jun 28 17:42:17 2006 -0500
+++ b/contrib/win32/mercurial.ini Wed Jul 05 13:28:25 2006 -0500
@@ -18,7 +18,8 @@
[encode]
; Encode files that don't contain NUL characters.
-** = cleverencode:
+
+; ** = cleverencode:
; Alternatively, you can explicitly specify each file extension that
; you want encoded (any you omit will be left untouched), like this:
@@ -28,7 +29,8 @@
[decode]
; Decode files that don't contain NUL characters.
-** = cleverdecode:
+
+; ** = cleverdecode:
; Alternatively, you can explicitly specify each file extension that
; you want decoded (any you omit will be left untouched), like this:
--- a/hgext/hgk.py Wed Jun 28 17:42:17 2006 -0500
+++ b/hgext/hgk.py Wed Jul 05 13:28:25 2006 -0500
@@ -131,7 +131,7 @@
date_ar = changes[2]
date = int(float(date_ar[0]))
lines = changes[4].splitlines()
- if lines[-1].startswith('committer:'):
+ if lines and lines[-1].startswith('committer:'):
committer = lines[-1].split(': ')[1].rstrip()
else:
committer = changes[1]
--- a/hgext/mq.py Wed Jun 28 17:42:17 2006 -0500
+++ b/hgext/mq.py Wed Jul 05 13:28:25 2006 -0500
@@ -5,6 +5,30 @@
# This software may be used and distributed according to the terms
# of the GNU General Public License, incorporated herein by reference.
+'''patch management and development
+
+This extension lets you work with a stack of patches in a Mercurial
+repository. It manages two stacks of patches - all known patches, and
+applied patches (subset of known patches).
+
+Known patches are represented as patch files in the .hg/patches
+directory. Applied patches are both patch files and changesets.
+
+Common tasks (use "hg help command" for more details):
+
+prepare repository to work with patches qinit
+create new patch qnew
+import existing patch qimport
+
+print patch series qseries
+print applied patches qapplied
+print name of top applied patch qtop
+
+add known patch to applied stack qpush
+remove patch from applied stack qpop
+refresh contents of top applied patch qrefresh
+'''
+
from mercurial.demandload import *
demandload(globals(), "os sys re struct traceback errno bz2")
from mercurial.i18n import gettext as _
@@ -214,7 +238,6 @@
return pp[0]
if p1 in arevs:
return pp[1]
- return None
return pp[0]
def mergepatch(self, repo, mergeq, series, wlock):
@@ -386,15 +409,21 @@
self.ui.write("Local changes found, refresh first\n")
sys.exit(1)
def new(self, repo, patch, msg=None, force=None):
- if not force:
- self.check_localchanges(repo)
+ commitfiles = []
+ (c, a, r, d, u) = repo.changes(None, None)
+ if c or a or d or r:
+ if not force:
+ raise util.Abort(_("Local changes found, refresh first"))
+ else:
+ commitfiles = c + a + r
self.check_toppatch(repo)
wlock = repo.wlock()
insert = self.series_end()
if msg:
- n = repo.commit([], "[mq]: %s" % msg, force=True, wlock=wlock)
+ n = repo.commit(commitfiles, "[mq]: %s" % msg, force=True,
+ wlock=wlock)
else:
- n = repo.commit([],
+ n = repo.commit(commitfiles,
"New patch: %s" % patch, force=True, wlock=wlock)
if n == None:
self.ui.warn("repo commit failed\n")
@@ -412,6 +441,8 @@
wlock = None
r = self.qrepo()
if r: r.add([patch])
+ if commitfiles:
+ self.refresh(repo, short=True)
def strip(self, repo, rev, update=True, backup="all", wlock=None):
def limitheads(chlog, stop):
@@ -1076,6 +1107,7 @@
return 0
def commit(ui, repo, *pats, **opts):
+ """commit changes in the queue repository"""
q = repomap[repo]
r = q.qrepo()
if not r: raise util.Abort('no queue repository')
@@ -1257,7 +1289,7 @@
'hg qimport [-e] [-n NAME] [-f] FILE...'),
"^qinit":
(init,
- [('c', 'create-repo', None, 'create patch repository')],
+ [('c', 'create-repo', None, 'create queue repository')],
'hg qinit [-c]'),
"qnew":
(new,
--- a/hgweb.cgi Wed Jun 28 17:42:17 2006 -0500
+++ b/hgweb.cgi Wed Jul 05 13:28:25 2006 -0500
@@ -6,7 +6,11 @@
cgitb.enable()
# sys.path.insert(0, "/path/to/python/lib") # if not a system-wide install
-from mercurial import hgweb
+from mercurial.hgweb.hgweb_mod import hgweb
+from mercurial.hgweb.request import wsgiapplication
+import mercurial.hgweb.wsgicgi as wsgicgi
-h = hgweb.hgweb("/path/to/repo", "repository name")
-h.run()
+def make_web_app():
+ return hgweb("/path/to/repo", "repository name")
+
+wsgicgi.launch(wsgiapplication(make_web_app))
--- a/hgwebdir.cgi Wed Jun 28 17:42:17 2006 -0500
+++ b/hgwebdir.cgi Wed Jul 05 13:28:25 2006 -0500
@@ -6,7 +6,9 @@
cgitb.enable()
# sys.path.insert(0, "/path/to/python/lib") # if not a system-wide install
-from mercurial import hgweb
+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
# repos, collections of repos in a directory tree, or both.
@@ -27,5 +29,7 @@
# Alternatively you can pass a list of ('virtual/path', '/real/path') tuples
# or use a dictionary with entries like 'virtual/path': '/real/path'
-h = hgweb.hgwebdir("hgweb.config")
-h.run()
+def make_web_app():
+ return hgwebdir("hgweb.config")
+
+wsgicgi.launch(wsgiapplication(make_web_app))
--- a/mercurial/bdiff.c Wed Jun 28 17:42:17 2006 -0500
+++ b/mercurial/bdiff.c Wed Jul 05 13:28:25 2006 -0500
@@ -38,7 +38,7 @@
#else
#include <sys/types.h>
#include <arpa/inet.h>
-#include <stdint.h>
+#include <inttypes.h>
#endif
struct line {
--- a/mercurial/changelog.py Wed Jun 28 17:42:17 2006 -0500
+++ b/mercurial/changelog.py Wed Jul 05 13:28:25 2006 -0500
@@ -39,21 +39,10 @@
def add(self, manifest, list, desc, transaction, p1=None, p2=None,
user=None, date=None):
if date:
- # validate explicit (probably user-specified) date and
- # time zone offset. values must fit in signed 32 bits for
- # current 32-bit linux runtimes. timezones go from UTC-12
- # to UTC+14
- try:
- when, offset = map(int, date.split(' '))
- except ValueError:
- raise ValueError(_('invalid date: %r') % date)
- if abs(when) > 0x7fffffff:
- raise ValueError(_('date exceeds 32 bits: %d') % when)
- if offset < -50400 or offset > 43200:
- raise ValueError(_('impossible time zone offset: %d') % offset)
+ parseddate = "%d %d" % util.parsedate(date)
else:
- date = "%d %d" % util.makedate()
+ parseddate = "%d %d" % util.makedate()
list.sort()
- l = [hex(manifest), user, date] + list + ["", desc]
+ l = [hex(manifest), user, parseddate] + list + ["", desc]
text = "\n".join(l)
return self.addrevision(text, transaction, self.count(), p1, p2)
--- a/mercurial/commands.py Wed Jun 28 17:42:17 2006 -0500
+++ b/mercurial/commands.py Wed Jul 05 13:28:25 2006 -0500
@@ -12,7 +12,7 @@
demandload(globals(), "fancyopts ui hg util lock revlog templater bundlerepo")
demandload(globals(), "fnmatch mdiff random signal tempfile time")
demandload(globals(), "traceback errno socket version struct atexit sets bz2")
-demandload(globals(), "archival changegroup")
+demandload(globals(), "archival cStringIO changegroup email.Parser")
demandload(globals(), "hgweb.server sshserver")
class UnknownCommand(Exception):
@@ -534,14 +534,22 @@
"not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n"
))
-def help_(ui, cmd=None, with_version=False):
- """show help for a given command or all commands"""
+def help_(ui, name=None, with_version=False):
+ """show help for a command, extension, or list of commands
+
+ With no arguments, print a list of commands and short help.
+
+ Given a command name, print help for that command.
+
+ Given an extension name, print help for that extension, and the
+ commands it provides."""
option_lists = []
- if cmd and cmd != 'shortlist':
+
+ def helpcmd(name):
if with_version:
show_version(ui)
ui.write('\n')
- aliases, i = find(cmd)
+ aliases, i = findcmd(name)
# synopsis
ui.write("%s\n\n" % i[2])
@@ -561,30 +569,15 @@
# options
if i[1]:
option_lists.append(("options", i[1]))
-
- else:
- # program name
- if ui.verbose or with_version:
- show_version(ui)
- else:
- ui.status(_("Mercurial Distributed SCM\n"))
- ui.status('\n')
-
- # list of commands
- if cmd == "shortlist":
- ui.status(_('basic commands (use "hg help" '
- 'for the full list or option "-v" for details):\n\n'))
- elif ui.verbose:
- ui.status(_('list of commands:\n\n'))
- else:
- ui.status(_('list of commands (use "hg help -v" '
- 'to show aliases and global options):\n\n'))
-
+
+ def helplist(select=None):
h = {}
cmds = {}
for c, e in table.items():
- f = c.split("|")[0]
- if cmd == "shortlist" and not f.startswith("^"):
+ f = c.split("|", 1)[0]
+ if select and not select(f):
+ continue
+ if name == "shortlist" and not f.startswith("^"):
continue
f = f.lstrip("^")
if not ui.debugflag and f.startswith("debug"):
@@ -605,6 +598,53 @@
else:
ui.write(' %-*s %s\n' % (m, f, h[f]))
+ def helpext(name):
+ try:
+ mod = findext(name)
+ except KeyError:
+ raise UnknownCommand(name)
+
+ doc = (mod.__doc__ or _('No help text available')).splitlines(0)
+ ui.write(_('%s extension - %s\n') % (name.split('.')[-1], doc[0]))
+ for d in doc[1:]:
+ ui.write(d, '\n')
+
+ ui.status('\n')
+ if ui.verbose:
+ ui.status(_('list of commands:\n\n'))
+ else:
+ ui.status(_('list of commands (use "hg help -v %s" '
+ 'to show aliases and global options):\n\n') % name)
+
+ modcmds = dict.fromkeys([c.split('|', 1)[0] for c in mod.cmdtable])
+ helplist(modcmds.has_key)
+
+ if name and name != 'shortlist':
+ try:
+ helpcmd(name)
+ except UnknownCommand:
+ helpext(name)
+
+ else:
+ # program name
+ if ui.verbose or with_version:
+ show_version(ui)
+ else:
+ ui.status(_("Mercurial Distributed SCM\n"))
+ ui.status('\n')
+
+ # list of commands
+ if name == "shortlist":
+ ui.status(_('basic commands (use "hg help" '
+ 'for the full list or option "-v" for details):\n\n'))
+ elif ui.verbose:
+ ui.status(_('list of commands:\n\n'))
+ else:
+ ui.status(_('list of commands (use "hg help -v" '
+ 'to show aliases and global options):\n\n'))
+
+ helplist()
+
# global options
if ui.verbose:
option_lists.append(("global options", globalopts))
@@ -650,7 +690,7 @@
elif repo.dirstate.state(abs) == '?':
ui.status(_('adding %s\n') % rel)
names.append(abs)
- if not opts['dry_run']:
+ if not opts.get('dry_run'):
repo.add(names)
def addremove(ui, repo, *pats, **opts):
@@ -908,13 +948,10 @@
if os.path.exists(dest):
raise util.Abort(_("destination '%s' already exists"), dest)
- dest = os.path.realpath(dest)
-
class Dircleanup(object):
def __init__(self, dir_):
self.rmtree = shutil.rmtree
self.dir_ = dir_
- os.mkdir(dir_)
def close(self):
self.dir_ = None
def __del__(self):
@@ -927,13 +964,24 @@
ui.setconfig("ui", "remotecmd", opts['remotecmd'])
source = ui.expandpath(source)
-
- d = Dircleanup(dest)
+ src_repo = hg.repository(ui, source)
+
+ dest_repo = None
+ try:
+ dest_repo = hg.repository(ui, dest)
+ raise util.Abort(_("destination '%s' already exists." % dest))
+ except hg.RepoError:
+ dest_repo = hg.repository(ui, dest, create=1)
+
+ dest_path = None
+ d = None
+ if dest_repo.local():
+ dest_path = os.path.realpath(dest)
+ d = Dircleanup(dest_path)
+
abspath = source
- other = hg.repository(ui, source)
-
copy = False
- if other.dev() != -1:
+ if src_repo.local() and dest_repo.local():
abspath = os.path.abspath(source)
if not opts['pull'] and not opts['rev']:
copy = True
@@ -944,47 +992,57 @@
# can end up with extra data in the cloned revlogs that's
# not pointed to by changesets, thus causing verify to
# fail
- l1 = other.lock()
+ l1 = src_repo.lock()
except lock.LockException:
copy = False
if copy:
# we lock here to avoid premature writing to the target
- os.mkdir(os.path.join(dest, ".hg"))
- l2 = lock.lock(os.path.join(dest, ".hg", "lock"))
-
+ l2 = lock.lock(os.path.join(dest_path, ".hg", "lock"))
+
+ # we need to remove the (empty) data dir in dest so copyfiles can do it's work
+ os.rmdir( os.path.join(dest_path, ".hg", "data") )
files = "data 00manifest.d 00manifest.i 00changelog.d 00changelog.i"
for f in files.split():
src = os.path.join(source, ".hg", f)
- dst = os.path.join(dest, ".hg", f)
+ dst = os.path.join(dest_path, ".hg", f)
try:
util.copyfiles(src, dst)
except OSError, inst:
if inst.errno != errno.ENOENT:
raise
- repo = hg.repository(ui, dest)
+ # we need to re-init the repo after manually copying the data into it
+ dest_repo = hg.repository(ui, dest)
else:
revs = None
if opts['rev']:
- if not other.local():
+ if not src_repo.local():
error = _("clone -r not supported yet for remote repositories.")
raise util.Abort(error)
else:
- revs = [other.lookup(rev) for rev in opts['rev']]
- repo = hg.repository(ui, dest, create=1)
- repo.pull(other, heads = revs)
-
- f = repo.opener("hgrc", "w", text=True)
- f.write("[paths]\n")
- f.write("default = %s\n" % abspath)
- f.close()
-
- if not opts['noupdate']:
- doupdate(repo.ui, repo)
-
- d.close()
+ revs = [src_repo.lookup(rev) for rev in opts['rev']]
+
+ if dest_repo.local():
+ dest_repo.pull(src_repo, heads = revs)
+ elif src_repo.local():
+ src_repo.push(dest_repo, revs = revs)
+ else:
+ error = _("clone from remote to remote not supported.")
+ raise util.Abort(error)
+
+ if dest_repo.local():
+ f = dest_repo.opener("hgrc", "w", text=True)
+ f.write("[paths]\n")
+ f.write("default = %s\n" % abspath)
+ f.close()
+
+ if not opts['noupdate']:
+ doupdate(dest_repo.ui, dest_repo)
+
+ if d:
+ d.close()
def commit(ui, repo, *pats, **opts):
"""commit the specified files or all outstanding changes
@@ -1065,21 +1123,21 @@
ui.warn(_('%s: not overwriting - file exists\n') %
reltarget)
return
- if not opts['after'] and not opts['dry_run']:
+ if not opts['after'] and not opts.get('dry_run'):
os.unlink(reltarget)
if opts['after']:
if not os.path.exists(reltarget):
return
else:
targetdir = os.path.dirname(reltarget) or '.'
- if not os.path.isdir(targetdir) and not opts['dry_run']:
+ if not os.path.isdir(targetdir) and not opts.get('dry_run'):
os.makedirs(targetdir)
try:
restore = repo.dirstate.state(abstarget) == 'r'
- if restore and not opts['dry_run']:
+ if restore and not opts.get('dry_run'):
repo.undelete([abstarget], wlock)
try:
- if not opts['dry_run']:
+ if not opts.get('dry_run'):
shutil.copyfile(relsrc, reltarget)
shutil.copymode(relsrc, reltarget)
restore = False
@@ -1099,7 +1157,7 @@
if ui.verbose or not exact:
ui.status(_('copying %s to %s\n') % (relsrc, reltarget))
targets[abstarget] = abssrc
- if abstarget != origsrc and not opts['dry_run']:
+ if abstarget != origsrc and not opts.get('dry_run'):
repo.copy(origsrc, abstarget, wlock)
copied.append((abssrc, relsrc, exact))
@@ -1225,7 +1283,7 @@
options = []
otables = [globalopts]
if cmd:
- aliases, entry = find(cmd)
+ aliases, entry = findcmd(cmd)
otables.append(entry[1])
for t in otables:
for o in t:
@@ -1707,11 +1765,16 @@
If there are outstanding changes in the working directory, import
will abort unless given the -f flag.
- If a patch looks like a mail message (its first line starts with
- "From " or looks like an RFC822 header), it will not be applied
- unless the -f option is used. The importer neither parses nor
- discards mail headers, so use -f only to override the "mailness"
- safety check, not to import a real mail message.
+ You can import a patch straight from a mail message. Even patches
+ as attachments work (body part must be type text/plain or
+ text/x-patch to be used). From and Subject headers of email
+ message are used as default committer and commit message. All
+ text/plain body parts before first diff are added to commit
+ message.
+
+ If imported patch was generated by hg export, user and description
+ from patch override values from message headers and body. Values
+ given on command line with -m and -u override these.
To read a patch from standard input, use patch name "-".
"""
@@ -1727,79 +1790,98 @@
# attempt to detect the start of a patch
# (this heuristic is borrowed from quilt)
- diffre = re.compile(r'(?:Index:[ \t]|diff[ \t]|RCS file: |' +
+ diffre = re.compile(r'^(?:Index:[ \t]|diff[ \t]|RCS file: |' +
'retrieving revision [0-9]+(\.[0-9]+)*$|' +
- '(---|\*\*\*)[ \t])')
+ '(---|\*\*\*)[ \t])', re.MULTILINE)
for patch in patches:
pf = os.path.join(d, patch)
- message = []
+ message = None
user = None
date = None
hgpatch = False
+
+ p = email.Parser.Parser()
if pf == '-':
- f = sys.stdin
- fd, tmpname = tempfile.mkstemp(prefix='hg-patch-')
- pf = tmpname
- tmpfp = os.fdopen(fd, 'w')
+ msg = p.parse(sys.stdin)
ui.status(_("applying patch from stdin\n"))
else:
- f = open(pf)
- tmpfp, tmpname = None, None
+ msg = p.parse(file(pf))
ui.status(_("applying %s\n") % patch)
+
+ fd, tmpname = tempfile.mkstemp(prefix='hg-patch-')
+ tmpfp = os.fdopen(fd, 'w')
try:
- while True:
- line = f.readline()
- if not line: break
- if tmpfp: tmpfp.write(line)
- line = line.rstrip()
- if (not message and not hgpatch and
- mailre.match(line) and not opts['force']):
- if len(line) > 35:
- line = line[:32] + '...'
- raise util.Abort(_('first line looks like a '
- 'mail header: ') + line)
- if diffre.match(line):
+ message = msg['Subject']
+ if message:
+ message = message.replace('\n\t', ' ')
+ ui.debug('Subject: %s\n' % message)
+ user = msg['From']
+ if user:
+ ui.debug('From: %s\n' % user)
+ diffs_seen = 0
+ ok_types = ('text/plain', 'text/x-patch')
+ for part in msg.walk():
+ content_type = part.get_content_type()
+ ui.debug('Content-Type: %s\n' % content_type)
+ if content_type not in ok_types:
+ continue
+ payload = part.get_payload(decode=True)
+ m = diffre.search(payload)
+ if m:
+ ui.debug(_('found patch at byte %d\n') % m.start(0))
+ diffs_seen += 1
+ hgpatch = False
+ fp = cStringIO.StringIO()
+ if message:
+ fp.write(message)
+ fp.write('\n')
+ for line in payload[:m.start(0)].splitlines():
+ if line.startswith('# HG changeset patch'):
+ ui.debug(_('patch generated by hg export\n'))
+ hgpatch = True
+ # drop earlier commit message content
+ fp.seek(0)
+ fp.truncate()
+ elif hgpatch:
+ if line.startswith('# User '):
+ user = line[7:]
+ ui.debug('From: %s\n' % user)
+ elif line.startswith("# Date "):
+ date = line[7:]
+ if not line.startswith('# '):
+ fp.write(line)
+ fp.write('\n')
+ message = fp.getvalue()
if tmpfp:
- for chunk in util.filechunkiter(f):
- tmpfp.write(chunk)
- break
- elif hgpatch:
- # parse values when importing the result of an hg export
- if line.startswith("# User "):
- user = line[7:]
- ui.debug(_('User: %s\n') % user)
- elif line.startswith("# Date "):
- date = line[7:]
- elif not line.startswith("# ") and line:
- message.append(line)
- hgpatch = False
- elif line == '# HG changeset patch':
- hgpatch = True
- message = [] # We may have collected garbage
- elif message or line:
- message.append(line)
+ tmpfp.write(payload)
+ if not payload.endswith('\n'):
+ tmpfp.write('\n')
+ elif not diffs_seen and message and content_type == 'text/plain':
+ message += '\n' + payload
if opts['message']:
# pickup the cmdline msg
message = opts['message']
elif message:
# pickup the patch msg
- message = '\n'.join(message).rstrip()
+ message = message.strip()
else:
# launch the editor
message = None
ui.debug(_('message:\n%s\n') % message)
- if tmpfp: tmpfp.close()
- files = util.patch(strip, pf, ui)
-
+ tmpfp.close()
+ if not diffs_seen:
+ raise util.Abort(_('no diffs found'))
+
+ files = util.patch(strip, tmpname, ui)
if len(files) > 0:
addremove_lock(ui, repo, files, {})
repo.commit(files, message, user, date)
finally:
- if tmpname: os.unlink(tmpname)
+ os.unlink(tmpname)
def incoming(ui, repo, source="default", **opts):
"""show new changesets found in source
@@ -1839,7 +1921,10 @@
# use the created uncompressed bundlerepo
other = bundlerepo.bundlerepository(ui, repo.root, fname)
- o = other.changelog.nodesbetween(incoming)[0]
+ revs = None
+ if opts['rev']:
+ revs = [other.lookup(rev) for rev in opts['rev']]
+ o = other.changelog.nodesbetween(incoming, revs)[0]
if opts['newest_first']:
o.reverse()
displayer = show_changeset(ui, other, opts)
@@ -1866,8 +1951,6 @@
If no directory is given, the current directory is used.
"""
- if not os.path.exists(dest):
- os.mkdir(dest)
hg.repository(ui, dest, create=1)
def locate(ui, repo, *pats, **opts):
@@ -2049,13 +2132,16 @@
ui.setconfig("ui", "ssh", opts['ssh'])
if opts['remotecmd']:
ui.setconfig("ui", "remotecmd", opts['remotecmd'])
+ revs = None
+ if opts['rev']:
+ revs = [repo.lookup(rev) for rev in opts['rev']]
other = hg.repository(ui, dest)
o = repo.findoutgoing(other, force=opts['force'])
if not o:
ui.status(_("no changes found\n"))
return
- o = repo.changelog.nodesbetween(o)[0]
+ o = repo.changelog.nodesbetween(o, revs)[0]
if opts['newest_first']:
o.reverse()
displayer = show_changeset(ui, repo, opts)
@@ -2322,7 +2408,7 @@
if ui.verbose or not exact:
ui.status(_('removing %s\n') % rel)
names.append(abs)
- if not opts['dry_run']:
+ if not opts.get('dry_run'):
repo.remove(names, True, wlock)
return errs
@@ -2986,11 +3072,13 @@
('n', 'newest-first', None, _('show newest record first')),
('', 'bundle', '', _('file to store the bundles into')),
('p', 'patch', None, _('show patch')),
+ ('r', 'rev', [], _('a specific revision you would like to pull')),
('', 'template', '', _('display with template')),
('e', 'ssh', '', _('specify ssh command to use')),
('', 'remotecmd', '',
_('specify hg command to run on the remote side'))],
- _('hg incoming [-p] [-n] [-M] [--bundle FILENAME] [SOURCE]')),
+ _('hg incoming [-p] [-n] [-M] [-r REV]...'
+ ' [--bundle FILENAME] [SOURCE]')),
"^init": (init, [], _('hg init [DEST]')),
"locate":
(locate,
@@ -3028,12 +3116,13 @@
_('run even when remote repository is unrelated')),
('p', 'patch', None, _('show patch')),
('', 'style', '', _('display using template map file')),
+ ('r', 'rev', [], _('a specific revision you would like to push')),
('n', 'newest-first', None, _('show newest record first')),
('', 'template', '', _('display with template')),
('e', 'ssh', '', _('specify ssh command to use')),
('', 'remotecmd', '',
_('specify hg command to run on the remote side'))],
- _('hg outgoing [-M] [-p] [-n] [DEST]')),
+ _('hg outgoing [-M] [-p] [-n] [-r REV]... [DEST]')),
"^parents":
(parents,
[('b', 'branches', None, _('show branches')),
@@ -3213,7 +3302,7 @@
return choice
-def find(cmd):
+def findcmd(cmd):
"""Return (aliases, command table entry) for command string."""
choice = findpossible(cmd)
@@ -3250,7 +3339,7 @@
if args:
cmd, args = args[0], args[1:]
- aliases, i = find(cmd)
+ aliases, i = findcmd(cmd)
cmd = aliases[0]
defaults = ui.config("defaults", cmd)
if defaults:
@@ -3277,6 +3366,19 @@
return (cmd, cmd and i[0] or None, args, options, cmdoptions)
+external = {}
+
+def findext(name):
+ '''return module with given extension name'''
+ try:
+ return external[name]
+ except KeyError:
+ dotname = '.' + name
+ for k, v in external.iteritems():
+ if k.endswith('.' + name) or v.__name__ == name:
+ return v
+ raise KeyError(name)
+
def dispatch(args):
for name in 'SIGBREAK', 'SIGHUP', 'SIGTERM':
num = getattr(signal, name, None)
@@ -3288,7 +3390,6 @@
sys.stderr.write(_("abort: %s\n") % inst)
return -1
- external = []
for x in u.extensions():
try:
if x[1]:
@@ -3305,10 +3406,12 @@
mod = getattr(mod, comp)
return mod
try:
- mod = importh("hgext." + x[0])
+ name = 'hgext.' + x[0]
+ mod = importh(name)
except ImportError:
- mod = importh(x[0])
- external.append(mod)
+ name = x[0]
+ mod = importh(name)
+ external[name] = mod
except (util.SignalInterrupt, KeyboardInterrupt):
raise
except Exception, inst:
@@ -3316,7 +3419,7 @@
if u.print_exc():
return 1
- for x in external:
+ for x in external.itervalues():
uisetup = getattr(x, 'uisetup', None)
if uisetup:
uisetup(u)
@@ -3372,7 +3475,7 @@
if not repo:
repo = hg.repository(u, path=path)
u = repo.ui
- for x in external:
+ for x in external.itervalues():
if hasattr(x, 'reposetup'):
x.reposetup(u, repo)
except hg.RepoError:
--- a/mercurial/hg.py Wed Jun 28 17:42:17 2006 -0500
+++ b/mercurial/hg.py Wed Jul 05 13:28:25 2006 -0500
@@ -33,6 +33,9 @@
path = path[5:]
return localrepo.localrepository(ui, path, create)
+def ssh_(ui, path, create=0):
+ return sshrepo.sshrepository(ui, path, create)
+
def old_http(ui, path):
ui.warn(_("old-http:// syntax is deprecated, "
"please use static-http:// instead\n"))
@@ -50,7 +53,7 @@
'http': lambda ui, path: httprepo.httprepository(ui, path),
'https': lambda ui, path: httprepo.httpsrepository(ui, path),
'old-http': old_http,
- 'ssh': lambda ui, path: sshrepo.sshrepository(ui, path),
+ 'ssh': ssh_,
'static-http': static_http,
}
@@ -60,11 +63,11 @@
if scheme:
c = scheme.find(':')
scheme = c >= 0 and scheme[:c]
- try:
- ctor = schemes.get(scheme) or schemes['file']
- if create:
+ ctor = schemes.get(scheme) or schemes['file']
+ if create:
+ try:
return ctor(ui, path, create)
- return ctor(ui, path)
- except TypeError:
- raise util.Abort(_('cannot create new repository over "%s" protocol') %
- scheme)
+ except TypeError:
+ raise util.Abort(_('cannot create new repository over "%s" protocol') %
+ scheme)
+ return ctor(ui, path)
--- a/mercurial/hgweb/common.py Wed Jun 28 17:42:17 2006 -0500
+++ b/mercurial/hgweb/common.py Wed Jul 05 13:28:25 2006 -0500
@@ -17,7 +17,7 @@
else:
return os.stat(hg_path).st_mtime
-def staticfile(directory, fname):
+def staticfile(directory, fname, req):
"""return a file inside directory with guessed content-type header
fname always uses '/' as directory separator and isn't allowed to
@@ -36,7 +36,9 @@
try:
os.stat(path)
ct = mimetypes.guess_type(path)[0] or "text/plain"
- return "Content-type: %s\n\n%s" % (ct, file(path).read())
+ req.header([('Content-type', ct),
+ ('Content-length', os.path.getsize(path))])
+ return file(path).read()
except (TypeError, OSError):
# illegal fname or unreadable file
return ""
--- a/mercurial/hgweb/hgweb_mod.py Wed Jun 28 17:42:17 2006 -0500
+++ b/mercurial/hgweb/hgweb_mod.py Wed Jul 05 13:28:25 2006 -0500
@@ -10,9 +10,8 @@
import os.path
import mimetypes
from mercurial.demandload import demandload
-demandload(globals(), "re zlib ConfigParser cStringIO sys tempfile")
+demandload(globals(), "re zlib ConfigParser mimetools cStringIO sys tempfile")
demandload(globals(), "mercurial:mdiff,ui,hg,util,archival,templater")
-demandload(globals(), "mercurial.hgweb.request:hgrequest")
demandload(globals(), "mercurial.hgweb.common:get_mtime,staticfile")
from mercurial.node import *
from mercurial.i18n import gettext as _
@@ -651,9 +650,27 @@
raise Exception("suspicious path")
return p
- def run(self, req=hgrequest()):
+ def run(self):
+ if not os.environ.get('GATEWAY_INTERFACE', '').startswith("CGI/1."):
+ raise RuntimeError("This function is only intended to be called while running as a CGI script.")
+ import mercurial.hgweb.wsgicgi as wsgicgi
+ from request import wsgiapplication
+ def make_web_app():
+ return self
+ wsgicgi.launch(wsgiapplication(make_web_app))
+
+ def run_wsgi(self, req):
def header(**map):
- yield self.t("header", **map)
+ header_file = cStringIO.StringIO(''.join(self.t("header", **map)))
+ msg = mimetools.Message(header_file, 0)
+ req.header(msg.items())
+ yield header_file.read()
+
+ def rawfileheader(**map):
+ req.header([('Content-type', map['mimetype']),
+ ('Content-disposition', 'filename=%s' % map['file']),
+ ('Content-length', str(len(map['raw'])))])
+ yield ''
def footer(**map):
yield self.t("footer",
@@ -712,6 +729,7 @@
"repo": self.reponame,
"header": header,
"footer": footer,
+ "rawfileheader": rawfileheader,
})
if not req.form.has_key('cmd'):
@@ -724,7 +742,6 @@
method(req)
else:
req.write(self.t("error"))
- req.done()
def do_changelog(self, req):
hi = self.repo.changelog.count() - 1
@@ -830,7 +847,7 @@
static = self.repo.ui.config("web", "static",
os.path.join(self.templatepath,
"static"))
- req.write(staticfile(static, fname)
+ req.write(staticfile(static, fname, req)
or self.t("error", error="%r not found" % fname))
def do_capabilities(self, req):
@@ -915,10 +932,11 @@
try:
ret = self.repo.addchangegroup(fp, 'serve')
- req.write('%d\n' % ret)
- req.write(sys.stdout.getvalue())
finally:
+ val = sys.stdout.getvalue()
sys.stdout = old_stdout
+ req.write('%d\n' % ret)
+ req.write(val)
finally:
lock.release()
finally:
--- a/mercurial/hgweb/hgwebdir_mod.py Wed Jun 28 17:42:17 2006 -0500
+++ b/mercurial/hgweb/hgwebdir_mod.py Wed Jul 05 13:28:25 2006 -0500
@@ -8,10 +8,9 @@
import os
from mercurial.demandload import demandload
-demandload(globals(), "ConfigParser")
+demandload(globals(), "ConfigParser mimetools cStringIO")
demandload(globals(), "mercurial:ui,hg,util,templater")
demandload(globals(), "mercurial.hgweb.hgweb_mod:hgweb")
-demandload(globals(), "mercurial.hgweb.request:hgrequest")
demandload(globals(), "mercurial.hgweb.common:get_mtime,staticfile")
from mercurial.i18n import gettext as _
@@ -47,9 +46,21 @@
self.repos.append((name.lstrip(os.sep), repo))
self.repos.sort()
- def run(self, req=hgrequest()):
+ def run(self):
+ if not os.environ.get('GATEWAY_INTERFACE', '').startswith("CGI/1."):
+ raise RuntimeError("This function is only intended to be called while running as a CGI script.")
+ import mercurial.hgweb.wsgicgi as wsgicgi
+ from request import wsgiapplication
+ def make_web_app():
+ return self
+ wsgicgi.launch(wsgiapplication(make_web_app))
+
+ def run_wsgi(self, req):
def header(**map):
- yield tmpl("header", **map)
+ header_file = cStringIO.StringIO(''.join(tmpl("header", **map)))
+ msg = mimetools.Message(header_file, 0)
+ req.header(msg.items())
+ yield header_file.read()
def footer(**map):
yield tmpl("footer", motd=self.motd, **map)
@@ -122,7 +133,7 @@
real = dict(self.repos).get(virtual)
if real:
try:
- hgweb(real).run(req)
+ hgweb(real).run_wsgi(req)
except IOError, inst:
req.write(tmpl("error", error=inst.strerror))
except hg.RepoError, inst:
@@ -133,7 +144,7 @@
if req.form.has_key('static'):
static = os.path.join(templater.templatepath(), "static")
fname = req.form['static'][0]
- req.write(staticfile(static, fname)
+ req.write(staticfile(static, fname, req)
or tmpl("error", error="%r not found" % fname))
else:
sortable = ["name", "description", "contact", "lastchange"]
--- a/mercurial/hgweb/request.py Wed Jun 28 17:42:17 2006 -0500
+++ b/mercurial/hgweb/request.py Wed Jul 05 13:28:25 2006 -0500
@@ -10,13 +10,48 @@
demandload(globals(), "socket sys cgi os errno")
from mercurial.i18n import gettext as _
-class hgrequest(object):
- def __init__(self, inp=None, out=None, env=None):
- self.inp = inp or sys.stdin
- self.out = out or sys.stdout
- self.env = env or os.environ
+class wsgiapplication(object):
+ def __init__(self, destmaker):
+ self.destmaker = destmaker
+
+ def __call__(self, wsgienv, start_response):
+ return _wsgirequest(self.destmaker(), wsgienv, start_response)
+
+class _wsgioutputfile(object):
+ def __init__(self, request):
+ self.request = request
+
+ def write(self, data):
+ self.request.write(data)
+ def writelines(self, lines):
+ for line in lines:
+ self.write(line)
+ def flush(self):
+ return None
+ def close(self):
+ return None
+
+class _wsgirequest(object):
+ def __init__(self, destination, wsgienv, start_response):
+ version = wsgienv['wsgi.version']
+ if (version < (1,0)) or (version >= (2, 0)):
+ raise RuntimeError("Unknown and unsupported WSGI version %d.%d" \
+ % version)
+ self.inp = wsgienv['wsgi.input']
+ self.out = _wsgioutputfile(self)
+ self.server_write = None
+ self.err = wsgienv['wsgi.errors']
+ self.threaded = wsgienv['wsgi.multithread']
+ self.multiprocess = wsgienv['wsgi.multiprocess']
+ self.run_once = wsgienv['wsgi.run_once']
+ self.env = wsgienv
self.form = cgi.parse(self.inp, self.env, keep_blank_values=1)
- self.will_close = True
+ self.start_response = start_response
+ self.headers = []
+ destination.run_wsgi(self)
+
+ def __iter__(self):
+ return iter([])
def read(self, count=-1):
return self.inp.read(count)
@@ -27,23 +62,22 @@
for part in thing:
self.write(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',
+ self.headers)
+ self.start_response = None
+ self.headers = None
try:
- self.out.write(str(thing))
+ self.server_write(thing)
except socket.error, inst:
if inst[0] != errno.ECONNRESET:
raise
- def done(self):
- if self.will_close:
- self.inp.close()
- self.out.close()
- else:
- self.out.flush()
-
def header(self, headers=[('Content-type','text/html')]):
- for header in headers:
- self.out.write("%s: %s\r\n" % header)
- self.out.write("\r\n")
+ self.headers.extend(headers)
def httphdr(self, type, filename=None, length=0, headers={}):
headers = headers.items()
@@ -51,12 +85,6 @@
if filename:
headers.append(('Content-disposition', 'attachment; filename=%s' %
filename))
- # we do not yet support http 1.1 chunked transfer, so we have
- # to force connection to close if content-length not known
if length:
headers.append(('Content-length', str(length)))
- self.will_close = False
- else:
- headers.append(('Connection', 'close'))
- self.will_close = True
self.header(headers)
--- a/mercurial/hgweb/server.py Wed Jun 28 17:42:17 2006 -0500
+++ b/mercurial/hgweb/server.py Wed Jul 05 13:28:25 2006 -0500
@@ -10,7 +10,7 @@
import os, sys, errno
demandload(globals(), "urllib BaseHTTPServer socket SocketServer")
demandload(globals(), "mercurial:ui,hg,util,templater")
-demandload(globals(), "hgweb_mod:hgweb hgwebdir_mod:hgwebdir request:hgrequest")
+demandload(globals(), "hgweb_mod:hgweb hgwebdir_mod:hgwebdir request:wsgiapplication")
from mercurial.i18n import gettext as _
def _splitURI(uri):
@@ -25,6 +25,17 @@
path, query = uri, ''
return urllib.unquote(path), query
+class _error_logger(object):
+ def __init__(self, handler):
+ self.handler = handler
+ def flush(self):
+ pass
+ def write(str):
+ self.writelines(str.split('\n'))
+ def writelines(seq):
+ for msg in seq:
+ self.handler.log_error("HG error: %s", msg)
+
class _hgwebhandler(object, BaseHTTPServer.BaseHTTPRequestHandler):
def __init__(self, *args, **kargs):
self.protocol_version = 'HTTP/1.1'
@@ -76,17 +87,72 @@
length = self.headers.getheader('content-length')
if length:
env['CONTENT_LENGTH'] = length
- accept = []
- for line in self.headers.getallmatchingheaders('accept'):
- if line[:1] in "\t\n\r ":
- accept.append(line.strip())
- else:
- accept = accept + line[7:].split(',')
- env['HTTP_ACCEPT'] = ','.join(accept)
+ for header in [h for h in self.headers.keys() \
+ if h not in ('content-type', 'content-length')]:
+ hkey = 'HTTP_' + header.replace('-', '_').upper()
+ hval = self.headers.getheader(header)
+ hval = hval.replace('\n', '').strip()
+ if hval:
+ env[hkey] = hval
+ env['SERVER_PROTOCOL'] = self.request_version
+ env['wsgi.version'] = (1, 0)
+ env['wsgi.url_scheme'] = 'http'
+ env['wsgi.input'] = self.rfile
+ env['wsgi.errors'] = _error_logger(self)
+ env['wsgi.multithread'] = isinstance(self.server,
+ SocketServer.ThreadingMixIn)
+ env['wsgi.multiprocess'] = isinstance(self.server,
+ SocketServer.ForkingMixIn)
+ env['wsgi.run_once'] = 0
+
+ self.close_connection = True
+ self.saved_status = None
+ self.saved_headers = []
+ self.sent_headers = False
+ self.length = None
+ req = self.server.reqmaker(env, self._start_response)
+ for data in req:
+ if data:
+ self._write(data)
- req = hgrequest(self.rfile, self.wfile, env)
- self.send_response(200, "Script output follows")
- self.close_connection = self.server.make_and_run_handler(req)
+ def send_headers(self):
+ if not self.saved_status:
+ raise AssertionError("Sending headers before start_response() called")
+ saved_status = self.saved_status.split(None, 1)
+ saved_status[0] = int(saved_status[0])
+ self.send_response(*saved_status)
+ should_close = True
+ for h in self.saved_headers:
+ self.send_header(*h)
+ if h[0].lower() == 'content-length':
+ should_close = False
+ self.length = int(h[1])
+ if should_close:
+ self.send_header('Connection', 'close')
+ self.close_connection = should_close
+ self.end_headers()
+ self.sent_headers = True
+
+ def _start_response(self, http_status, headers, exc_info=None):
+ code, msg = http_status.split(None, 1)
+ code = int(code)
+ self.saved_status = http_status
+ bad_headers = ('connection', 'transfer-encoding')
+ self.saved_headers = [ h for h in headers \
+ if h[0].lower() not in bad_headers ]
+ return self._write
+
+ def _write(self, data):
+ if not self.saved_status:
+ raise AssertionError("data written before start_response() called")
+ elif not self.sent_headers:
+ self.send_headers()
+ if self.length is not None:
+ if len(data) > self.length:
+ raise AssertionError("Content-length header sent, but more bytes than specified are being written.")
+ self.length = self.length - len(data)
+ self.wfile.write(data)
+ self.wfile.flush()
def create_server(ui, repo):
use_threads = True
@@ -126,8 +192,9 @@
self.webdir_conf = webdir_conf
self.webdirmaker = hgwebdir
self.repoviewmaker = hgweb
+ self.reqmaker = wsgiapplication(self.make_handler)
- def make_and_run_handler(self, req):
+ def make_handler(self):
if self.webdir_conf:
hgwebobj = self.webdirmaker(self.webdir_conf)
elif self.repo is not None:
@@ -135,8 +202,7 @@
repo.origroot))
else:
raise hg.RepoError(_('no repo found'))
- hgwebobj.run(req)
- return req.will_close
+ return hgwebobj
class IPv6HTTPServer(MercurialHTTPServer):
address_family = getattr(socket, 'AF_INET6', None)
@@ -144,7 +210,7 @@
def __init__(self, *args, **kwargs):
if self.address_family is None:
raise hg.RepoError(_('IPv6 not available on this system'))
- super(IPv6HTTPServer, self).__init__(*args, **kargs)
+ super(IPv6HTTPServer, self).__init__(*args, **kwargs)
if use_ipv6:
return IPv6HTTPServer((address, port), _hgwebhandler)
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/mercurial/hgweb/wsgicgi.py Wed Jul 05 13:28:25 2006 -0500
@@ -0,0 +1,70 @@
+# hgweb/wsgicgi.py - CGI->WSGI translator
+#
+# Copyright 2006 Eric Hopper <hopper@omnifarious.org>
+#
+# This software may be used and distributed according to the terms
+# of the GNU General Public License, incorporated herein by reference.
+#
+# This was originally copied from the public domain code at
+# http://www.python.org/dev/peps/pep-0333/#the-server-gateway-side
+
+import os, sys
+
+def launch(application):
+
+ environ = dict(os.environ.items())
+ environ['wsgi.input'] = sys.stdin
+ environ['wsgi.errors'] = sys.stderr
+ environ['wsgi.version'] = (1,0)
+ environ['wsgi.multithread'] = False
+ environ['wsgi.multiprocess'] = True
+ environ['wsgi.run_once'] = True
+
+ if environ.get('HTTPS','off') in ('on','1'):
+ environ['wsgi.url_scheme'] = 'https'
+ else:
+ environ['wsgi.url_scheme'] = 'http'
+
+ headers_set = []
+ headers_sent = []
+ out = sys.stdout
+
+ def write(data):
+ if not headers_set:
+ raise AssertionError("write() before start_response()")
+
+ elif not headers_sent:
+ # Before the first output, send the stored headers
+ status, response_headers = headers_sent[:] = headers_set
+ out.write('Status: %s\r\n' % status)
+ for header in response_headers:
+ out.write('%s: %s\r\n' % header)
+ out.write('\r\n')
+
+ out.write(data)
+ out.flush()
+
+ def start_response(status,response_headers,exc_info=None):
+ if exc_info:
+ try:
+ if headers_sent:
+ # Re-raise original exception if headers sent
+ raise exc_info[0], exc_info[1], exc_info[2]
+ finally:
+ exc_info = None # avoid dangling circular ref
+ elif headers_set:
+ raise AssertionError("Headers already set!")
+
+ headers_set[:] = [status,response_headers]
+ return write
+
+ 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()
--- a/mercurial/httprepo.py Wed Jun 28 17:42:17 2006 -0500
+++ b/mercurial/httprepo.py Wed Jul 05 13:28:25 2006 -0500
@@ -20,16 +20,22 @@
def find_user_password(self, realm, authuri):
authinfo = urllib2.HTTPPasswordMgrWithDefaultRealm.find_user_password(
self, realm, authuri)
- if authinfo != (None, None):
- return authinfo
+ user, passwd = authinfo
+ if user and passwd:
+ return (user, passwd)
if not self.ui.interactive:
raise util.Abort(_('http authorization required'))
self.ui.write(_("http authorization required\n"))
self.ui.status(_("realm: %s\n") % realm)
- user = self.ui.prompt(_("user:"), default=None)
- passwd = self.ui.getpass()
+ if user:
+ self.ui.status(_("user: %s\n") % user)
+ else:
+ user = self.ui.prompt(_("user:"), default=None)
+
+ if not passwd:
+ passwd = self.ui.getpass()
self.add_password(realm, authuri, user, passwd)
return (user, passwd)
@@ -85,6 +91,22 @@
def http_open(self, req):
return self.do_open(httpconnection, req)
+class httpsconnection(httplib.HTTPSConnection):
+ # must be able to send big bundle as stream.
+
+ def send(self, data):
+ if isinstance(data, str):
+ httplib.HTTPSConnection.send(self, data)
+ else:
+ # if auth required, some data sent twice, so rewind here
+ data.seek(0)
+ for chunk in util.filechunkiter(data):
+ httplib.HTTPSConnection.send(self, chunk)
+
+class httpshandler(urllib2.HTTPSHandler):
+ def https_open(self, req):
+ return self.do_open(httpsconnection, req)
+
class httprepository(remoterepository):
def __init__(self, ui, path):
self.caps = None
@@ -148,12 +170,13 @@
passmgr = passwordmgr(ui)
if user:
- ui.debug(_('will use user %s, password %s for http auth\n') %
- (user, '*' * len(passwd)))
+ ui.debug(_('http auth: user %s, password %s\n') %
+ (user, passwd and '*' * len(passwd) or 'not set'))
passmgr.add_password(None, host, user, passwd or '')
opener = urllib2.build_opener(
handler,
+ httpshandler(),
urllib2.HTTPBasicAuthHandler(passmgr),
urllib2.HTTPDigestAuthHandler(passmgr))
--- a/mercurial/localrepo.py Wed Jun 28 17:42:17 2006 -0500
+++ b/mercurial/localrepo.py Wed Jul 05 13:28:25 2006 -0500
@@ -74,6 +74,8 @@
self.transhandle = None
if create:
+ if not os.path.exists(path):
+ os.mkdir(path)
os.mkdir(self.path)
os.mkdir(self.join("data"))
@@ -1651,8 +1653,8 @@
linear_path = (pa == p1 or pa == p2)
if allow and linear_path:
- raise util.Abort(_("there is nothing to merge, "
- "just use 'hg update'"))
+ raise util.Abort(_("there is nothing to merge, just use "
+ "'hg update' or look at 'hg heads'"))
if allow and not forcemerge:
if modified or added or removed:
raise util.Abort(_("outstanding uncommitted changes"))
--- a/mercurial/mpatch.c Wed Jun 28 17:42:17 2006 -0500
+++ b/mercurial/mpatch.c Wed Jul 05 13:28:25 2006 -0500
@@ -43,7 +43,7 @@
/* not windows */
# include <sys/types.h>
# include <arpa/inet.h>
-# include <stdint.h>
+# include <inttypes.h>
#endif
static char mpatch_doc[] = "Efficient binary patching.";
--- a/mercurial/sshrepo.py Wed Jun 28 17:42:17 2006 -0500
+++ b/mercurial/sshrepo.py Wed Jul 05 13:28:25 2006 -0500
@@ -12,7 +12,7 @@
demandload(globals(), "hg os re stat util")
class sshrepository(remoterepository):
- def __init__(self, ui, path):
+ def __init__(self, ui, path, create=0):
self.url = path
self.ui = ui
@@ -30,6 +30,25 @@
sshcmd = self.ui.config("ui", "ssh", "ssh")
remotecmd = self.ui.config("ui", "remotecmd", "hg")
+
+ if create:
+ try:
+ self.validate_repo(ui, sshcmd, args, remotecmd)
+ return # the repo is good, nothing more to do
+ except hg.RepoError:
+ pass
+
+ cmd = '%s %s "%s init %s"'
+ cmd = cmd % (sshcmd, args, remotecmd, self.path)
+
+ ui.note('running %s\n' % cmd)
+ res = os.system(cmd)
+ if res != 0:
+ raise hg.RepoError(_("could not create remote repo"))
+
+ self.validate_repo(ui, sshcmd, args, remotecmd)
+
+ def validate_repo(self, ui, sshcmd, args, remotecmd):
cmd = '%s %s "%s -R %s serve --stdio"'
cmd = cmd % (sshcmd, args, remotecmd, self.path)
--- a/mercurial/templater.py Wed Jun 28 17:42:17 2006 -0500
+++ b/mercurial/templater.py Wed Jul 05 13:28:25 2006 -0500
@@ -202,7 +202,7 @@
if para_re is None:
para_re = re.compile('(\n\n|\n\\s*[-*]\\s*)', re.M)
space_re = re.compile(r' +')
-
+
def findparas():
start = 0
while True:
@@ -221,10 +221,21 @@
fp.write(rest)
return fp.getvalue()
+def firstline(text):
+ '''return the first line of text'''
+ try:
+ return text.splitlines(1)[0].rstrip('\r\n')
+ except IndexError:
+ return ''
+
def isodate(date):
'''turn a (timestamp, tzoff) tuple into an iso 8631 date and time.'''
return util.datestr(date, format='%Y-%m-%d %H:%M')
+def hgdate(date):
+ '''turn a (timestamp, tzoff) tuple into an hg cset timestamp.'''
+ return "%d %d" % date
+
def nl2br(text):
'''replace raw newlines with xhtml line breaks.'''
return text.replace('\n', '<br/>\n')
@@ -280,8 +291,9 @@
"escape": lambda x: cgi.escape(x, True),
"fill68": lambda x: fill(x, width=68),
"fill76": lambda x: fill(x, width=76),
- "firstline": lambda x: x.splitlines(1)[0].rstrip('\r\n'),
+ "firstline": firstline,
"tabindent": lambda x: indent(x, '\t'),
+ "hgdate": hgdate,
"isodate": isodate,
"obfuscate": obfuscate,
"permissions": lambda x: x and "-rwxr-xr-x" or "-rw-r--r--",
--- a/mercurial/ui.py Wed Jun 28 17:42:17 2006 -0500
+++ b/mercurial/ui.py Wed Jul 05 13:28:25 2006 -0500
@@ -99,10 +99,10 @@
"""Return a list of comma/space separated strings"""
result = self.config(section, name)
if result is None:
- return []
- else:
- return result.replace(",", " ").split()
-
+ result = default or []
+ if isinstance(result, basestring):
+ result = result.replace(",", " ").split()
+ return result
def configbool(self, section, name, default=False):
if self.overlay.has_key((section, name)):
--- a/mercurial/util.py Wed Jun 28 17:42:17 2006 -0500
+++ b/mercurial/util.py Wed Jul 05 13:28:25 2006 -0500
@@ -859,6 +859,49 @@
s += " %+03d%02d" % (-tz / 3600, ((-tz % 3600) / 60))
return s
+def strdate(string, format='%a %b %d %H:%M:%S %Y'):
+ """parse a localized time string and return a (unixtime, offset) tuple.
+ if the string cannot be parsed, ValueError is raised."""
+ def hastimezone(string):
+ return (string[-4:].isdigit() and
+ (string[-5] == '+' or string[-5] == '-') and
+ string[-6].isspace())
+
+ if hastimezone(string):
+ date, tz = string[:-6], string[-5:]
+ tz = int(tz)
+ offset = - 3600 * (tz / 100) - 60 * (tz % 100)
+ else:
+ date, offset = string, 0
+ when = int(time.mktime(time.strptime(date, format))) + offset
+ return when, offset
+
+def parsedate(string, formats=('%Y-%m-%d %H:%M:%S', '%Y-%m-%d %H:%M')):
+ """parse a localized time string and return a (unixtime, offset) tuple.
+ The date may be a "unixtime offset" string or in one of the specified
+ formats."""
+ try:
+ when, offset = map(int, string.split(' '))
+ except ValueError:
+ for format in formats:
+ try:
+ when, offset = strdate(string, format)
+ except ValueError:
+ pass
+ else:
+ break
+ else:
+ raise ValueError(_('invalid date: %r') % string)
+ # validate explicit (probably user-specified) date and
+ # time zone offset. values must fit in signed 32 bits for
+ # current 32-bit linux runtimes. timezones go from UTC-12
+ # to UTC+14
+ if abs(when) > 0x7fffffff:
+ raise ValueError(_('date exceeds 32 bits: %d') % when)
+ if offset < -50400 or offset > 43200:
+ raise ValueError(_('impossible time zone offset: %d') % offset)
+ return when, offset
+
def shortuser(user):
"""Return a short representation of a user name or email address."""
f = user.find('@')
--- a/templates/changeset-raw.tmpl Wed Jun 28 17:42:17 2006 -0500
+++ b/templates/changeset-raw.tmpl Wed Jul 05 13:28:25 2006 -0500
@@ -1,7 +1,7 @@
#header#
# HG changeset patch
# User #author#
-# Date #date|date#
+# Date #date|hgdate#
# Node ID #node#
#parent%changesetparent#
#desc#
--- a/templates/map-raw Wed Jun 28 17:42:17 2006 -0500
+++ b/templates/map-raw Wed Jul 05 13:28:25 2006 -0500
@@ -5,10 +5,10 @@
difflineminus = '#line#'
difflineat = '#line#'
diffline = '#line#'
-changesetparent = '# parent: #node#'
-changesetchild = '# child: #node#'
+changesetparent = '# Parent #node#'
+changesetchild = '# Child #node#'
filenodelink = ''
-filerevision = 'Content-Type: #mimetype#\nContent-Disposition: filename=#file#\n\n#raw#'
+filerevision = '#rawfileheader##raw#'
fileline = '#line#'
diffblock = '#lines#'
filediff = filediff-raw.tmpl
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/tests/get-with-headers.py Wed Jul 05 13:28:25 2006 -0500
@@ -0,0 +1,16 @@
+#!/usr/bin/env python
+
+__doc__ = """This does HTTP get requests given a host:port and path and returns
+a subset of the headers plus the body of the result."""
+
+import httplib, sys
+headers = [h.lower() for h in sys.argv[3:]]
+conn = httplib.HTTPConnection(sys.argv[1])
+conn.request("GET", sys.argv[2])
+response = conn.getresponse()
+print response.status, response.reason
+for h in headers:
+ if response.getheader(h, None) is not None:
+ print "%s: %s" % (h, response.getheader(h))
+print
+sys.stdout.write(response.read())
--- a/tests/test-globalopts Wed Jun 28 17:42:17 2006 -0500
+++ b/tests/test-globalopts Wed Jul 05 13:28:25 2006 -0500
@@ -62,7 +62,7 @@
hg --cwd a --time tip 2>&1 | grep '^Time:' | sed 's/[0-9][0-9]*/x/g'
echo %% --version
-hg --version -q | sed 's/version \([a-f0-9+]*\|unknown\)/version xxx/'
+hg --version -q | sed 's/version [^)]*/version xxx/'
echo %% -h/--help
hg -h
--- a/tests/test-globalopts.out Wed Jun 28 17:42:17 2006 -0500
+++ b/tests/test-globalopts.out Wed Jul 05 13:28:25 2006 -0500
@@ -127,7 +127,7 @@
export dump the header and diffs for one or more changesets
grep search for a pattern in specified files and revisions
heads show current repository heads
- help show help for a given command or all commands
+ help show help for a command, extension, or list of commands
identify print information about the working copy
import import an ordered set of patches
incoming show new changesets found in source
@@ -173,7 +173,7 @@
export dump the header and diffs for one or more changesets
grep search for a pattern in specified files and revisions
heads show current repository heads
- help show help for a given command or all commands
+ help show help for a command, extension, or list of commands
identify print information about the working copy
import import an ordered set of patches
incoming show new changesets found in source
--- a/tests/test-help.out Wed Jun 28 17:42:17 2006 -0500
+++ b/tests/test-help.out Wed Jul 05 13:28:25 2006 -0500
@@ -51,7 +51,7 @@
export dump the header and diffs for one or more changesets
grep search for a pattern in specified files and revisions
heads show current repository heads
- help show help for a given command or all commands
+ help show help for a command, extension, or list of commands
identify print information about the working copy
import import an ordered set of patches
incoming show new changesets found in source
@@ -93,7 +93,7 @@
export dump the header and diffs for one or more changesets
grep search for a pattern in specified files and revisions
heads show current repository heads
- help show help for a given command or all commands
+ help show help for a command, extension, or list of commands
identify print information about the working copy
import import an ordered set of patches
incoming show new changesets found in source
--- a/tests/test-http-proxy Wed Jun 28 17:42:17 2006 -0500
+++ b/tests/test-http-proxy Wed Jul 05 13:28:25 2006 -0500
@@ -26,5 +26,5 @@
echo %% bad host:port for proxy
http_proxy=localhost:20061 hg clone --config http_proxy.always=True http://localhost:20059/ f
-kill $(cat proxy.pid a/hg.pid)
+kill `cat proxy.pid a/hg.pid`
exit 0
--- a/tests/test-hup Wed Jun 28 17:42:17 2006 -0500
+++ b/tests/test-hup Wed Jul 05 13:28:25 2006 -0500
@@ -7,7 +7,7 @@
P=$!
(echo lock; echo addchangegroup; sleep 5) > p &
Q=$!
-sleep 1
+sleep 3
kill -HUP $P
wait
ls .hg
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/tests/test-import Wed Jul 05 13:28:25 2006 -0500
@@ -0,0 +1,81 @@
+#!/bin/sh
+
+hg init a
+echo line 1 > a/a
+hg --cwd a ci -d '0 0' -Ama
+
+echo line 2 >> a/a
+hg --cwd a ci -u someone -d '1 0' -m'second change'
+
+echo % import exported patch
+hg clone -r0 a b
+hg --cwd a export tip > tip.patch
+hg --cwd b import ../tip.patch
+echo % message should be same
+hg --cwd b tip | grep 'second change'
+echo % committer should be same
+hg --cwd b tip | grep someone
+rm -rf b
+
+echo % import of plain diff should fail without message
+hg clone -r0 a b
+hg --cwd a diff -r0:1 > tip.patch
+hg --cwd b import ../tip.patch
+rm -rf b
+
+echo % import of plain diff should be ok with message
+hg clone -r0 a b
+hg --cwd a diff -r0:1 > tip.patch
+hg --cwd b import -mpatch ../tip.patch
+rm -rf b
+
+echo % import from stdin
+hg clone -r0 a b
+hg --cwd a export tip | hg --cwd b import -
+rm -rf b
+
+echo % override commit message
+hg clone -r0 a b
+hg --cwd a export tip | hg --cwd b import -m 'override' -
+hg --cwd b tip | grep override
+rm -rf b
+
+cat > mkmsg.py <<EOF
+import email.Message, sys
+msg = email.Message.Message()
+msg.set_payload('email commit message\n' + open('tip.patch').read())
+msg['Subject'] = 'email patch'
+msg['From'] = 'email patcher'
+sys.stdout.write(msg.as_string())
+EOF
+
+echo % plain diff in email, subject, message body
+hg clone -r0 a b
+hg --cwd a diff -r0:1 > tip.patch
+python mkmsg.py > msg.patch
+hg --cwd b import ../msg.patch
+hg --cwd b tip | grep email
+rm -rf b
+
+echo % plain diff in email, no subject, message body
+hg clone -r0 a b
+grep -v '^Subject:' msg.patch | hg --cwd b import -
+rm -rf b
+
+echo % plain diff in email, subject, no message body
+hg clone -r0 a b
+grep -v '^email ' msg.patch | hg --cwd b import -
+rm -rf b
+
+echo % plain diff in email, no subject, no message body, should fail
+hg clone -r0 a b
+grep -v '^\(Subject\|email\)' msg.patch | hg --cwd b import -
+rm -rf b
+
+echo % hg export in email, should use patch header
+hg clone -r0 a b
+hg --cwd a export tip > tip.patch
+python mkmsg.py | hg --cwd b import -
+hg --cwd b tip | grep second
+rm -rf b
+
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/tests/test-import.out Wed Jul 05 13:28:25 2006 -0500
@@ -0,0 +1,103 @@
+adding a
+% import exported patch
+requesting all changes
+adding changesets
+adding manifests
+adding file changes
+added 1 changesets with 1 changes to 1 files
+1 files updated, 0 files merged, 0 files removed, 0 files unresolved
+applying ../tip.patch
+patching file a
+% message should be same
+summary: second change
+% committer should be same
+user: someone
+% import of plain diff should fail without message
+requesting all changes
+adding changesets
+adding manifests
+adding file changes
+added 1 changesets with 1 changes to 1 files
+1 files updated, 0 files merged, 0 files removed, 0 files unresolved
+applying ../tip.patch
+patching file a
+transaction abort!
+rollback completed
+% import of plain diff should be ok with message
+requesting all changes
+adding changesets
+adding manifests
+adding file changes
+added 1 changesets with 1 changes to 1 files
+1 files updated, 0 files merged, 0 files removed, 0 files unresolved
+applying ../tip.patch
+patching file a
+% import from stdin
+requesting all changes
+adding changesets
+adding manifests
+adding file changes
+added 1 changesets with 1 changes to 1 files
+1 files updated, 0 files merged, 0 files removed, 0 files unresolved
+applying patch from stdin
+patching file a
+% override commit message
+requesting all changes
+adding changesets
+adding manifests
+adding file changes
+added 1 changesets with 1 changes to 1 files
+1 files updated, 0 files merged, 0 files removed, 0 files unresolved
+applying patch from stdin
+patching file a
+summary: override
+% plain diff in email, subject, message body
+requesting all changes
+adding changesets
+adding manifests
+adding file changes
+added 1 changesets with 1 changes to 1 files
+1 files updated, 0 files merged, 0 files removed, 0 files unresolved
+applying ../msg.patch
+patching file a
+user: email patcher
+summary: email patch
+% plain diff in email, no subject, message body
+requesting all changes
+adding changesets
+adding manifests
+adding file changes
+added 1 changesets with 1 changes to 1 files
+1 files updated, 0 files merged, 0 files removed, 0 files unresolved
+applying patch from stdin
+patching file a
+% plain diff in email, subject, no message body
+requesting all changes
+adding changesets
+adding manifests
+adding file changes
+added 1 changesets with 1 changes to 1 files
+1 files updated, 0 files merged, 0 files removed, 0 files unresolved
+applying patch from stdin
+patching file a
+% plain diff in email, no subject, no message body, should fail
+requesting all changes
+adding changesets
+adding manifests
+adding file changes
+added 1 changesets with 1 changes to 1 files
+1 files updated, 0 files merged, 0 files removed, 0 files unresolved
+applying patch from stdin
+patching file a
+transaction abort!
+rollback completed
+% hg export in email, should use patch header
+requesting all changes
+adding changesets
+adding manifests
+adding file changes
+added 1 changesets with 1 changes to 1 files
+1 files updated, 0 files merged, 0 files removed, 0 files unresolved
+applying patch from stdin
+patching file a
+summary: second change
--- a/tests/test-incoming-outgoing Wed Jun 28 17:42:17 2006 -0500
+++ b/tests/test-incoming-outgoing Wed Jul 05 13:28:25 2006 -0500
@@ -14,8 +14,10 @@
hg init new
# http incoming
http_proxy= hg -R new incoming http://localhost:20059/
+http_proxy= hg -R new incoming -r 4 http://localhost:20059/
# local incoming
hg -R new incoming test
+hg -R new incoming -r 4 test
# test with --bundle
http_proxy= hg -R new incoming --bundle test.hg http://localhost:20059/
@@ -42,5 +44,6 @@
cd ..
hg -R test-dev outgoing test
http_proxy= hg -R test-dev outgoing http://localhost:20059/
+http_proxy= hg -R test-dev outgoing -r 11 http://localhost:20059/
kill `cat test/hg.pid`
--- a/tests/test-incoming-outgoing.out Wed Jun 28 17:42:17 2006 -0500
+++ b/tests/test-incoming-outgoing.out Wed Jul 05 13:28:25 2006 -0500
@@ -75,6 +75,31 @@
date: Mon Jan 12 13:46:40 1970 +0000
summary: 4
+changeset: 0:9cb21d99fe27
+user: test
+date: Mon Jan 12 13:46:40 1970 +0000
+summary: 0
+
+changeset: 1:d717f5dfad6a
+user: test
+date: Mon Jan 12 13:46:40 1970 +0000
+summary: 1
+
+changeset: 2:c0d6b86da426
+user: test
+date: Mon Jan 12 13:46:40 1970 +0000
+summary: 2
+
+changeset: 3:dfacbd43b3fe
+user: test
+date: Mon Jan 12 13:46:40 1970 +0000
+summary: 3
+
+changeset: 4:1f3a964b6022
+user: test
+date: Mon Jan 12 13:46:40 1970 +0000
+summary: 4
+
changeset: 5:c028bcc7a28a
user: test
date: Mon Jan 12 13:46:40 1970 +0000
@@ -121,6 +146,31 @@
date: Mon Jan 12 13:46:40 1970 +0000
summary: 4
+changeset: 0:9cb21d99fe27
+user: test
+date: Mon Jan 12 13:46:40 1970 +0000
+summary: 0
+
+changeset: 1:d717f5dfad6a
+user: test
+date: Mon Jan 12 13:46:40 1970 +0000
+summary: 1
+
+changeset: 2:c0d6b86da426
+user: test
+date: Mon Jan 12 13:46:40 1970 +0000
+summary: 2
+
+changeset: 3:dfacbd43b3fe
+user: test
+date: Mon Jan 12 13:46:40 1970 +0000
+summary: 3
+
+changeset: 4:1f3a964b6022
+user: test
+date: Mon Jan 12 13:46:40 1970 +0000
+summary: 4
+
changeset: 5:c028bcc7a28a
user: test
date: Mon Jan 12 13:46:40 1970 +0000
@@ -270,3 +320,19 @@
date: Mon Jan 12 13:46:40 1970 +0000
summary: 13
+searching for changes
+changeset: 9:3741c3ad1096
+user: test
+date: Mon Jan 12 13:46:40 1970 +0000
+summary: 9
+
+changeset: 10:de4143c8d9a5
+user: test
+date: Mon Jan 12 13:46:40 1970 +0000
+summary: 10
+
+changeset: 11:0e1c188b9a7a
+user: test
+date: Mon Jan 12 13:46:40 1970 +0000
+summary: 11
+
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/tests/test-oldcgi Wed Jul 05 13:28:25 2006 -0500
@@ -0,0 +1,100 @@
+#!/bin/sh
+
+hg init test
+
+cat >hgweb.cgi <<HGWEB
+#!/usr/bin/env python
+#
+# An example CGI script to use hgweb, edit as necessary
+
+import cgitb, os, sys
+cgitb.enable()
+
+# sys.path.insert(0, "/path/to/python/lib") # if not a system-wide install
+from mercurial import hgweb
+
+h = hgweb.hgweb("test", "Empty test repository")
+h.run()
+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, sys
+cgitb.enable()
+
+# sys.path.insert(0, "/path/to/python/lib") # if not a system-wide install
+from mercurial import hgweb
+
+# The config file looks like this. You can have paths to individual
+# repos, collections of repos in a directory tree, or both.
+#
+# [paths]
+# virtual/path = /real/path
+# virtual/path = /real/path
+#
+# [collections]
+# /prefix/to/strip/off = /root/of/tree/full/of/repos
+#
+# collections example: say directory tree /foo contains repos /foo/bar,
+# /foo/quux/baz. Give this config section:
+# [collections]
+# /foo = /foo
+# Then repos will list as bar and quux/baz.
+
+# Alternatively you can pass a list of ('virtual/path', '/real/path') tuples
+# or use a dictionary with entries like 'virtual/path': '/real/path'
+
+h = hgweb.hgwebdir("hgweb.config")
+h.run()
+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
+./hgweb.cgi >page1 2>&1 ; echo $?
+./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/"
+./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-oldcgi.out Wed Jul 05 13:28:25 2006 -0500
@@ -0,0 +1,3 @@
+0
+0
+0
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/tests/test-parse-date Wed Jul 05 13:28:25 2006 -0500
@@ -0,0 +1,16 @@
+#!/bin/sh
+
+hg init
+echo "test-parse-date" > a
+hg add a
+hg ci -d "2006-02-01 13:00:30" -m "rev 0"
+echo "hi!" >> a
+hg ci -d "2006-02-01 13:00:30 -0500" -m "rev 1"
+hg tag -d "2006-04-15 13:30" "Hi"
+hg backout --merge -d "2006-04-15 13:30 +0200" -m "rev 3" 1
+hg ci -d "1150000000 14400" -m "rev 4 (merge)"
+echo "fail" >> a
+hg ci -d "should fail" -m "fail"
+hg ci -d "100000000000000000 1400" -m "fail"
+hg ci -d "100000 1400000" -m "fail"
+hg log --template '{date|date}\n'
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/tests/test-parse-date.out Wed Jul 05 13:28:25 2006 -0500
@@ -0,0 +1,19 @@
+reverting a
+changeset 3:107ce1ee2b43 backs out changeset 1:25a1420a55f8
+merging with changeset 2:99a1acecff55
+1 files updated, 0 files merged, 0 files removed, 0 files unresolved
+(branch merge, don't forget to commit)
+abort: invalid date: 'should fail'
+transaction abort!
+rollback completed
+abort: date exceeds 32 bits: 100000000000000000
+transaction abort!
+rollback completed
+abort: impossible time zone offset: 1400000
+transaction abort!
+rollback completed
+Sun Jun 11 00:26:40 2006 -0400
+Sat Apr 15 13:30:00 2006 +0200
+Sat Apr 15 13:30:00 2006 +0000
+Wed Feb 01 13:00:30 2006 -0500
+Wed Feb 01 13:00:30 2006 +0000
--- a/tests/test-pull-pull-corruption2 Wed Jun 28 17:42:17 2006 -0500
+++ b/tests/test-pull-pull-corruption2 Wed Jul 05 13:28:25 2006 -0500
@@ -20,5 +20,6 @@
sleep 1
hg clone --pull -U version2 corrupted
+wait
hg -R corrupted verify
hg -R version2 verify
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/tests/test-ui-config Wed Jul 05 13:28:25 2006 -0500
@@ -0,0 +1,47 @@
+#!/usr/bin/env python
+
+from mercurial import ui
+
+testui = ui.ui()
+testui.updateopts(config=[
+ 'values.string=string value',
+ 'values.bool1=true',
+ 'values.bool2=false',
+ 'lists.list1=foo',
+ 'lists.list2=foo bar baz',
+ 'lists.list3=alice, bob',
+ 'lists.list4=foo bar baz alice, bob',
+])
+
+print repr(testui.configitems('values'))
+print repr(testui.configitems('lists'))
+print "---"
+print repr(testui.config('values', 'string'))
+print repr(testui.config('values', 'bool1'))
+print repr(testui.config('values', 'bool2'))
+print repr(testui.config('values', 'unknown'))
+print "---"
+try:
+ print repr(testui.configbool('values', 'string'))
+except ValueError, why:
+ print why
+print repr(testui.configbool('values', 'bool1'))
+print repr(testui.configbool('values', 'bool2'))
+print repr(testui.configbool('values', 'bool2', True))
+print repr(testui.configbool('values', 'unknown'))
+print repr(testui.configbool('values', 'unknown', True))
+print "---"
+print repr(testui.configlist('lists', 'list1'))
+print repr(testui.configlist('lists', 'list2'))
+print repr(testui.configlist('lists', 'list3'))
+print repr(testui.configlist('lists', 'list4'))
+print repr(testui.configlist('lists', 'list4', ['foo']))
+print repr(testui.configlist('lists', 'unknown'))
+print repr(testui.configlist('lists', 'unknown', ''))
+print repr(testui.configlist('lists', 'unknown', 'foo'))
+print repr(testui.configlist('lists', 'unknown', ['foo']))
+print repr(testui.configlist('lists', 'unknown', 'foo bar'))
+print repr(testui.configlist('lists', 'unknown', 'foo, bar'))
+print repr(testui.configlist('lists', 'unknown', ['foo bar']))
+print repr(testui.configlist('lists', 'unknown', ['foo', 'bar']))
+print "---"
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/tests/test-ui-config.out Wed Jul 05 13:28:25 2006 -0500
@@ -0,0 +1,29 @@
+[('bool1', 'true'), ('bool2', 'false'), ('string', 'string value')]
+[('list1', 'foo'), ('list2', 'foo bar baz'), ('list3', 'alice, bob'), ('list4', 'foo bar baz alice, bob')]
+---
+'string value'
+'true'
+'false'
+None
+---
+Not a boolean: string value
+True
+False
+False
+False
+True
+---
+['foo']
+['foo', 'bar', 'baz']
+['alice', 'bob']
+['foo', 'bar', 'baz', 'alice', 'bob']
+['foo', 'bar', 'baz', 'alice', 'bob']
+[]
+[]
+['foo']
+['foo']
+['foo', 'bar']
+['foo', 'bar']
+['foo bar']
+['foo', 'bar']
+---
--- a/tests/test-up-local-change.out Wed Jun 28 17:42:17 2006 -0500
+++ b/tests/test-up-local-change.out Wed Jul 05 13:28:25 2006 -0500
@@ -43,7 +43,7 @@
date: Mon Jan 12 13:46:40 1970 +0000
summary: 1
-abort: there is nothing to merge, just use 'hg update'
+abort: there is nothing to merge, just use 'hg update' or look at 'hg heads'
failed
changeset: 0:33aaa84a386b
user: test
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/tests/test-webraw Wed Jul 05 13:28:25 2006 -0500
@@ -0,0 +1,21 @@
+#!/bin/sh
+
+hg init test
+cd test
+cat >sometext.txt <<ENDSOME
+This is just some random text
+that will go inside the file and take a few lines.
+It is very boring to read, but computers don't
+care about things like that.
+ENDSOME
+hg add sometext.txt
+hg commit -d "1 0" -m "Just some text"
+hg serve -p 20059 -A access.log -E error.log -d --pid-file=hg.pid
+("$TESTDIR/get-with-headers.py" localhost:20059 '/?f=f165dc289438;file=sometext.txt;style=raw' content-type content-length content-disposition) >getoutput.txt &
+
+sleep 5
+kill `cat hg.pid`
+sleep 1 # wait for server to scream and die
+cat getoutput.txt
+cat access.log error.log | \
+ sed 's/^[^ ]*\( [^[]*\[\)[^]]*\(\].*\)$/host\1date\2/'
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/tests/test-webraw.out Wed Jul 05 13:28:25 2006 -0500
@@ -0,0 +1,10 @@
+200 Script output follows
+content-type: text/plain
+content-length: 157
+content-disposition: filename=sometext.txt
+
+This is just some random text
+that will go inside the file and take a few lines.
+It is very boring to read, but computers don't
+care about things like that.
+host - - [date] "GET /?f=f165dc289438;file=sometext.txt;style=raw HTTP/1.1" 200 -