--- a/mercurial/archival.py Tue Jul 10 10:54:54 2007 -0600
+++ b/mercurial/archival.py Wed Jul 11 20:15:03 2007 -0300
@@ -86,12 +86,19 @@
# Python 2.5-2.5.1 have a regression that requires a name arg
self.z = taropen(name='', mode='w|', fileobj=dest)
- def addfile(self, name, mode, data):
+ def addfile(self, name, mode, islink, data):
i = tarfile.TarInfo(self.prefix + name)
i.mtime = self.mtime
i.size = len(data)
- i.mode = mode
- self.z.addfile(i, cStringIO.StringIO(data))
+ if islink:
+ i.type = tarfile.SYMTYPE
+ i.mode = 0777
+ i.linkname = data
+ data = None
+ else:
+ i.mode = mode
+ data = cStringIO.StringIO(data)
+ self.z.addfile(i, data)
def done(self):
self.z.close()
@@ -130,13 +137,17 @@
zipfile.ZIP_STORED)
self.date_time = time.gmtime(mtime)[:6]
- def addfile(self, name, mode, data):
+ def addfile(self, name, mode, islink, data):
i = zipfile.ZipInfo(self.prefix + name, self.date_time)
i.compress_type = self.z.compression
# unzip will not honor unix file modes unless file creator is
# set to unix (id 3).
i.create_system = 3
- i.external_attr = (mode | stat.S_IFREG) << 16L
+ ftype = stat.S_IFREG
+ if islink:
+ mode = 0777
+ ftype = stat.S_IFLNK
+ i.external_attr = (mode | ftype) << 16L
self.z.writestr(i, data)
def done(self):
@@ -149,19 +160,17 @@
if prefix:
raise util.Abort(_('cannot give prefix when archiving to files'))
self.basedir = name
- self.dirs = {}
- self.oflags = (os.O_CREAT | os.O_EXCL | os.O_WRONLY |
- getattr(os, 'O_BINARY', 0) |
- getattr(os, 'O_NOFOLLOW', 0))
+ self.opener = util.opener(self.basedir)
- def addfile(self, name, mode, data):
+ def addfile(self, name, mode, islink, data):
+ if islink:
+ self.opener.symlink(data, name)
+ return
+ f = self.opener(name, "w", atomictemp=True)
+ f.write(data)
+ f.rename()
destfile = os.path.join(self.basedir, name)
- destdir = os.path.dirname(destfile)
- if destdir not in self.dirs:
- if not os.path.isdir(destdir):
- os.makedirs(destdir)
- self.dirs[destdir] = 1
- os.fdopen(os.open(destfile, self.oflags, mode), 'wb').write(data)
+ os.chmod(destfile, mode)
def done(self):
pass
@@ -191,20 +200,20 @@
prefix is name of path to put before every archive member.'''
- def write(name, mode, data):
+ def write(name, mode, islink, data):
if matchfn and not matchfn(name): return
if decode:
data = repo.wwritedata(name, data)
- archiver.addfile(name, mode, data)
+ archiver.addfile(name, mode, islink, data)
ctx = repo.changectx(node)
archiver = archivers[kind](dest, prefix, mtime or ctx.date()[0])
m = ctx.manifest()
items = m.items()
items.sort()
- write('.hg_archival.txt', 0644,
+ write('.hg_archival.txt', 0644, False,
'repo: %s\nnode: %s\n' % (hex(repo.changelog.node(0)), hex(node)))
for filename, filenode in items:
- write(filename, m.execf(filename) and 0755 or 0644,
+ write(filename, m.execf(filename) and 0755 or 0644, m.linkf(filename),
repo.file(filename).read(filenode))
archiver.done()
--- a/mercurial/cmdutil.py Tue Jul 10 10:54:54 2007 -0600
+++ b/mercurial/cmdutil.py Wed Jul 11 20:15:03 2007 -0300
@@ -542,9 +542,9 @@
try:
if node:
expander.update(node_expander)
- if node and revwidth is not None:
+ if node:
expander['r'] = (lambda:
- str(repo.changelog.rev(node)).zfill(revwidth))
+ str(repo.changelog.rev(node)).zfill(revwidth or 0))
if total is not None:
expander['N'] = lambda: str(total)
if seqno is not None:
--- a/mercurial/commands.py Tue Jul 10 10:54:54 2007 -0600
+++ b/mercurial/commands.py Wed Jul 11 20:15:03 2007 -0300
@@ -548,9 +548,10 @@
targets[abstarget] = abssrc
if abstarget != origsrc:
if repo.dirstate.state(origsrc) == 'a':
- ui.warn(_("%s was marked for addition. "
- "%s will not be committed as a copy.\n")
- % (repo.pathto(origsrc, cwd), reltarget))
+ if not ui.quiet:
+ ui.warn(_("%s has not been committed yet, so no copy "
+ "data will be stored for %s.\n")
+ % (repo.pathto(origsrc, cwd), reltarget))
if abstarget not in repo.dirstate and not opts.get('dry_run'):
repo.add([abstarget], wlock)
elif not opts.get('dry_run'):
@@ -2471,6 +2472,8 @@
for o in optlist.split():
if opts[o]:
parentui.setconfig("web", o, str(opts[o]))
+ if repo.ui != parentui:
+ repo.ui.setconfig("web", o, str(opts[o]))
if repo is None and not ui.config("web", "webdir_conf"):
raise hg.RepoError(_("There is no Mercurial repository here"
--- a/mercurial/hgweb/hgweb_mod.py Tue Jul 10 10:54:54 2007 -0600
+++ b/mercurial/hgweb/hgweb_mod.py Wed Jul 11 20:15:03 2007 -0300
@@ -1184,4 +1184,4 @@
def do_stream_out(self, req):
req.httphdr("application/mercurial-0.1")
- streamclone.stream_out(self.repo, req)
+ streamclone.stream_out(self.repo, req, untrusted=True)
--- a/mercurial/hgweb/server.py Tue Jul 10 10:54:54 2007 -0600
+++ b/mercurial/hgweb/server.py Wed Jul 11 20:15:03 2007 -0300
@@ -197,13 +197,13 @@
return open(opt, 'w')
return default
- address = ui.config("web", "address", "")
- port = int(ui.config("web", "port", 8000))
- use_ipv6 = ui.configbool("web", "ipv6")
- webdir_conf = ui.config("web", "webdir_conf")
- ssl_cert = ui.config("web", "certificate")
- accesslog = openlog(ui.config("web", "accesslog", "-"), sys.stdout)
- errorlog = openlog(ui.config("web", "errorlog", "-"), sys.stderr)
+ address = repo.ui.config("web", "address", "")
+ port = int(repo.ui.config("web", "port", 8000))
+ use_ipv6 = repo.ui.configbool("web", "ipv6")
+ webdir_conf = repo.ui.config("web", "webdir_conf")
+ ssl_cert = repo.ui.config("web", "certificate")
+ accesslog = openlog(repo.ui.config("web", "accesslog", "-"), sys.stdout)
+ errorlog = openlog(repo.ui.config("web", "errorlog", "-"), sys.stderr)
if use_threads:
try:
--- a/mercurial/localrepo.py Tue Jul 10 10:54:54 2007 -0600
+++ b/mercurial/localrepo.py Wed Jul 11 20:15:03 2007 -0300
@@ -460,15 +460,7 @@
def wwrite(self, filename, data, flags):
data = self._filter("decode", filename, data)
if "l" in flags:
- f = self.wjoin(filename)
- try:
- os.unlink(f)
- except OSError:
- pass
- d = os.path.dirname(f)
- if not os.path.exists(d):
- os.makedirs(d)
- os.symlink(data, f)
+ self.wopener.symlink(data, filename)
else:
try:
if self._link(filename):
--- a/mercurial/streamclone.py Tue Jul 10 10:54:54 2007 -0600
+++ b/mercurial/streamclone.py Wed Jul 11 20:15:03 2007 -0300
@@ -56,11 +56,11 @@
#
# server writes out raw file data.
-def stream_out(repo, fileobj):
+def stream_out(repo, fileobj, untrusted=False):
'''stream out all metadata files in repository.
writes to file-like object, must support write() and optional flush().'''
- if not repo.ui.configbool('server', 'uncompressed'):
+ if not repo.ui.configbool('server', 'uncompressed', untrusted=untrusted):
fileobj.write('1\n')
return
--- a/mercurial/util.py Tue Jul 10 10:54:54 2007 -0600
+++ b/mercurial/util.py Wed Jul 11 20:15:03 2007 -0300
@@ -540,7 +540,7 @@
return (roots, match, (inc or exc or anypats) and True)
-_hgexecutable = None
+_hgexecutable = 'hg'
def set_hgexecutable(path):
"""remember location of the 'hg' executable if easily possible
@@ -549,7 +549,8 @@
fall back to 'hg' in this case.
"""
global _hgexecutable
- _hgexecutable = path and os.path.abspath(path) or 'hg'
+ if path:
+ _hgexecutable = os.path.abspath(path)
def system(cmd, environ={}, cwd=None, onerr=None, errprefix=None):
'''enhanced shell command execution.
@@ -1181,70 +1182,95 @@
return openerfn(fn(path), *args, **kw)
return o
-def opener(base, audit=True):
+def mktempcopy(name, emptyok=False):
+ """Create a temporary file with the same contents from name
+
+ The permission bits are copied from the original file.
+
+ If the temporary file is going to be truncated immediately, you
+ can use emptyok=True as an optimization.
+
+ Returns the name of the temporary file.
"""
- return a function that opens files relative to base
+ d, fn = os.path.split(name)
+ fd, temp = tempfile.mkstemp(prefix='.%s-' % fn, dir=d)
+ os.close(fd)
+ # Temporary files are created with mode 0600, which is usually not
+ # what we want. If the original file already exists, just copy
+ # its mode. Otherwise, manually obey umask.
+ try:
+ st_mode = os.lstat(name).st_mode
+ except OSError, inst:
+ if inst.errno != errno.ENOENT:
+ raise
+ st_mode = 0666 & ~_umask
+ os.chmod(temp, st_mode)
+ if emptyok:
+ return temp
+ try:
+ try:
+ ifp = posixfile(name, "rb")
+ except IOError, inst:
+ if inst.errno == errno.ENOENT:
+ return temp
+ if not getattr(inst, 'filename', None):
+ inst.filename = name
+ raise
+ ofp = posixfile(temp, "wb")
+ for chunk in filechunkiter(ifp):
+ ofp.write(chunk)
+ ifp.close()
+ ofp.close()
+ except:
+ try: os.unlink(temp)
+ except: pass
+ raise
+ return temp
- this function is used to hide the details of COW semantics and
+class atomictempfile(posixfile):
+ """file-like object that atomically updates a file
+
+ All writes will be redirected to a temporary copy of the original
+ file. When rename is called, the copy is renamed to the original
+ name, making the changes visible.
+ """
+ def __init__(self, name, mode):
+ self.__name = name
+ self.temp = mktempcopy(name, emptyok=('w' in mode))
+ posixfile.__init__(self, self.temp, mode)
+
+ def rename(self):
+ if not self.closed:
+ posixfile.close(self)
+ rename(self.temp, localpath(self.__name))
+
+ def __del__(self):
+ if not self.closed:
+ try:
+ os.unlink(self.temp)
+ except: pass
+ posixfile.close(self)
+
+class opener(object):
+ """Open files relative to a base directory
+
+ This class is used to hide the details of COW semantics and
remote file access from higher level code.
"""
- def mktempcopy(name, emptyok=False):
- d, fn = os.path.split(name)
- fd, temp = tempfile.mkstemp(prefix='.%s-' % fn, dir=d)
- os.close(fd)
- # Temporary files are created with mode 0600, which is usually not
- # what we want. If the original file already exists, just copy
- # its mode. Otherwise, manually obey umask.
- try:
- st_mode = os.lstat(name).st_mode
- except OSError, inst:
- if inst.errno != errno.ENOENT:
- raise
- st_mode = 0666 & ~_umask
- os.chmod(temp, st_mode)
- if emptyok:
- return temp
- try:
- try:
- ifp = posixfile(name, "rb")
- except IOError, inst:
- if inst.errno == errno.ENOENT:
- return temp
- if not getattr(inst, 'filename', None):
- inst.filename = name
- raise
- ofp = posixfile(temp, "wb")
- for chunk in filechunkiter(ifp):
- ofp.write(chunk)
- ifp.close()
- ofp.close()
- except:
- try: os.unlink(temp)
- except: pass
- raise
- return temp
+ def __init__(self, base, audit=True):
+ self.base = base
+ self.audit = audit
- class atomictempfile(posixfile):
- """the file will only be copied when rename is called"""
- def __init__(self, name, mode):
- self.__name = name
- self.temp = mktempcopy(name, emptyok=('w' in mode))
- posixfile.__init__(self, self.temp, mode)
- def rename(self):
- if not self.closed:
- posixfile.close(self)
- rename(self.temp, localpath(self.__name))
- def __del__(self):
- if not self.closed:
- try:
- os.unlink(self.temp)
- except: pass
- posixfile.close(self)
+ def __getattr__(self, name):
+ if name == '_can_symlink':
+ self._can_symlink = checklink(self.base)
+ return self._can_symlink
+ raise AttributeError(name)
- def o(path, mode="r", text=False, atomictemp=False):
- if audit:
+ def __call__(self, path, mode="r", text=False, atomictemp=False):
+ if self.audit:
audit_path(path)
- f = os.path.join(base, path)
+ f = os.path.join(self.base, path)
if not text and "b" not in mode:
mode += "b" # for that other OS
@@ -1263,7 +1289,25 @@
rename(mktempcopy(f), f)
return posixfile(f, mode)
- return o
+ def symlink(self, src, dst):
+ if self.audit:
+ audit_path(dst)
+ linkname = os.path.join(self.base, dst)
+ try:
+ os.unlink(linkname)
+ except OSError:
+ pass
+
+ dirname = os.path.dirname(linkname)
+ if not os.path.exists(dirname):
+ os.makedirs(dirname)
+
+ if self._can_symlink:
+ os.symlink(src, linkname)
+ else:
+ f = self(self, dst, "w")
+ f.write(src)
+ f.close()
class chunkbuffer(object):
"""Allow arbitrary sized chunks of data to be efficiently read from an
--- a/tests/test-archive Tue Jul 10 10:54:54 2007 -0600
+++ b/tests/test-archive Wed Jul 11 20:15:03 2007 -0300
@@ -64,8 +64,13 @@
hg archive -t tar - | tar tf - | sed "s/$QTIP/TIP/"
+hg archive -r 0 -t tar rev-%r.tar
+if [ -f rev-0.tar ]; then
+ echo 'rev-0.tar created'
+fi
+
echo '% empty repo'
hg init ../empty
cd ../empty
hg archive ../test-empty
-exit 0
\ No newline at end of file
+exit 0
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/tests/test-archive-symlinks Wed Jul 11 20:15:03 2007 -0300
@@ -0,0 +1,37 @@
+#!/bin/sh
+
+origdir=`pwd`
+
+cat >> readlink.py <<EOF
+import os
+import sys
+
+for f in sys.argv[1:]:
+ print f, '->', os.readlink(f)
+EOF
+
+hg init repo
+cd repo
+ln -s nothing dangling
+hg ci -qAm 'add symlink'
+
+hg archive -t files ../archive
+hg archive -t tar -p tar ../archive.tar
+hg archive -t zip -p zip ../archive.zip
+
+echo '% files'
+cd "$origdir"
+cd archive
+python ../readlink.py dangling
+
+echo '% tar'
+cd "$origdir"
+tar xf archive.tar
+cd tar
+python ../readlink.py dangling
+
+echo '% zip'
+cd "$origdir"
+unzip archive.zip > /dev/null
+cd zip
+python ../readlink.py dangling
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/tests/test-archive-symlinks.out Wed Jul 11 20:15:03 2007 -0300
@@ -0,0 +1,6 @@
+% files
+dangling -> nothing
+% tar
+dangling -> nothing
+% zip
+dangling -> nothing
--- a/tests/test-archive.out Tue Jul 10 10:54:54 2007 -0600
+++ b/tests/test-archive.out Wed Jul 11 20:15:03 2007 -0300
@@ -38,5 +38,6 @@
test-TIP/bar
test-TIP/baz/bletch
test-TIP/foo
+rev-0.tar created
% empty repo
abort: repository has no revisions
--- a/tests/test-copy2.out Tue Jul 10 10:54:54 2007 -0600
+++ b/tests/test-copy2.out Wed Jul 11 20:15:03 2007 -0300
@@ -3,13 +3,13 @@
abort: no files to copy
? foo
# dry-run; print a warning that this is not a real copy; foo is added
-foo was marked for addition. bar will not be committed as a copy.
+foo has not been committed yet, so no copy data will be stored for bar.
A foo
# should print a warning that this is not a real copy; bar is added
-foo was marked for addition. bar will not be committed as a copy.
+foo has not been committed yet, so no copy data will be stored for bar.
A bar
# should print a warning that this is not a real copy; foo is added
-bar was marked for addition. foo will not be committed as a copy.
+bar has not been committed yet, so no copy data will be stored for foo.
A foo
# dry-run; should show that foo is clean
C foo
--- a/tests/test-serve Tue Jul 10 10:54:54 2007 -0600
+++ b/tests/test-serve Wed Jul 11 20:15:03 2007 -0300
@@ -3,9 +3,15 @@
hg init test
cd test
+echo '[web]' > .hg/hgrc
+echo 'accesslog = access.log' >> .hg/hgrc
+
echo % Without -v
hg serve -a localhost -p 20063 -d --pid-file=hg.pid
cat hg.pid >> "$DAEMON_PIDS"
+if [ -f access.log ]; then
+ echo 'access log created - .hg/hgrc respected'
+fi
echo % With -v
hg serve -a localhost -p 20064 -d --pid-file=hg.pid -v
--- a/tests/test-serve.out Tue Jul 10 10:54:54 2007 -0600
+++ b/tests/test-serve.out Wed Jul 11 20:15:03 2007 -0300
@@ -1,3 +1,4 @@
% Without -v
+access log created - .hg/hgrc respected
% With -v
listening at http://localhost:20064/