changeset 1076:01db658cc78a

tarball support v0.3 Hello, I'm slowly improving support for tarballs in Mercurial. Attached patch is made against current tip in Matt's repository - f859e9cba1b9, and contains everything done so far. Changes: - gzip and bzip2 tarballs are sent immediately without writing to temporary files (I was wrong Matt, it can be done very easy) - hgrc customization, you can choose which type (if any) you will support There's no easy way to support compression levels, since TarFile open() assume that it is 9. I tried to use gzopen(), and bz2open() methods instead, but it seems that headers of generated archives, are missing or wrong. We could eventually try to rewrite tarfile.py and include our own version into Mercurial, but I don't know if it's good idea... Wojtek
author Wojciech Milkowski <wmilkowski@interia.pl>
date Fri, 26 Aug 2005 20:51:34 -0700
parents e254bcbfe636
children b87aeccf73d9
files mercurial/hgweb.py templates/changeset.tmpl
diffstat 2 files changed, 88 insertions(+), 3 deletions(-) [+]
line wrap: on
line diff
--- a/mercurial/hgweb.py
+++ b/mercurial/hgweb.py
@@ -58,8 +58,14 @@ def up(p):
         return "/"
     return up + "/"
 
-def httphdr(type):
-    sys.stdout.write('Content-type: %s\n\n' % type)
+def httphdr(type, file="", size=0):
+    sys.stdout.write('Content-type: %s\n' % type)
+    if file:
+        sys.stdout.write('Content-disposition: attachment; filename=%s\n'
+            % file)
+    if size > 0:
+        sys.stdout.write('Content-length: %d\n' % size)
+    sys.stdout.write('\n')
 
 def write(*things):
     for thing in things:
@@ -161,6 +167,9 @@ class hgweb:
             self.maxchanges = self.repo.ui.config("web", "maxchanges", 10)
             self.maxfiles = self.repo.ui.config("web", "maxchanges", 10)
             self.allowpull = self.repo.ui.configbool("web", "allowpull", True)
+            self.allowzip = self.repo.ui.configbool("web", "zip", True)
+            self.allowgz = self.repo.ui.configbool("web", "gz", True)
+            self.allowbz2 = self.repo.ui.configbool("web", "bz2", True)
 
     def date(self, cs):
         return time.asctime(time.gmtime(float(cs[2].split(' ')[0])))
@@ -188,6 +197,16 @@ class hgweb:
         for t in self.repo.nodetags(node):
              yield self.t(t1, tag=t, **args)
 
+    def tarballbuttons(self, m):
+        s = ''
+        if self.allowzip:
+            s += '<a href="?cmd=tarball;manifest=%s;type=zip">zip</a>\n' % m
+        if self.allowgz:
+            s += '<a href="?cmd=tarball;manifest=%s;type=gz">gz</a>\n' % m
+        if self.allowbz2:
+            s += '<a href="?cmd=tarball;manifest=%s;type=bz2">bz2</a>\n' % m
+        return s
+
     def diff(self, node1, node2, files):
         def filterfiles(list, files):
             l = [x for x in list if x in files]
@@ -395,7 +414,8 @@ class hgweb:
                      author=changes[1],
                      desc=changes[4],
                      date=t,
-                     files=files)
+                     files=files,
+                     tarballbuttons=self.tarballbuttons(hex(changes[0])))
 
     def filelog(self, f, filenode):
         cl = self.repo.changelog
@@ -623,6 +643,58 @@ class hgweb:
                                          cl.parents(n), cl.rev),
                      diff=diff)
 
+    def ziparchive(self, mnode):
+        import zipfile
+
+        tmp = tempfile.mkstemp()[1]
+        zf = zipfile.ZipFile(tmp, "w", zipfile.ZIP_DEFLATED)
+        mf = self.repo.manifest.read(bin(mnode))
+        rev = self.repo.manifest.rev(bin(mnode))
+        cnode = short(self.repo.changelog.node(rev))
+        name = os.path.basename(self.repo.path[:-4]) # without '/.hg' suffix
+        name += '-' + str(rev) + '-' + cnode + '/'
+
+        for fname in mf.keys():
+            r = self.repo.file(fname)
+            zf.writestr(name + fname, r.read(mf[fname]))
+        zf.close()
+
+        f = open(tmp, 'r')
+        httphdr('application/zip', name[:-1] + '.zip', os.path.getsize(tmp))
+        sys.stdout.write(f.read())
+        f.close()
+        os.unlink(tmp)
+
+    def tararchive(self, mnode, type):
+        import StringIO
+        import time
+        import tarfile
+
+        #if type == "gz":
+        #    tf = tarfile.TarFile.gzopen('', 'w', sys.stdout, compressionlevel)
+        #else:
+        #    tf = tarfile.TarFile.bz2open('', 'w', sys.stdout, compressionlevel)
+        tf = tarfile.TarFile.open(mode='w|' + type, fileobj=sys.stdout)
+
+        mf = self.repo.manifest.read(bin(mnode))
+        rev = self.repo.manifest.rev(bin(mnode))
+        cnode = short(self.repo.changelog.node(rev))
+        mff = self.repo.manifest.readflags(bin(mnode))
+        mtime = int(time.time())
+        name = os.path.basename(self.repo.path[:-4]) # without '/.hg' suffix
+        name += '-' + str(rev) + '-' + cnode  + '/'
+
+        httphdr('application/octet-stream', name[:-1] + '.tar.' + type)
+        for fname in mf.keys():
+            r = self.repo.file(fname)
+            rcont = r.read(mf[fname])
+            finfo = tarfile.TarInfo(name + fname)
+            finfo.mtime = mtime
+            finfo.size = len(rcont)
+            finfo.mode = mff[fname] and 0755 or 0644
+            tf.addfile(finfo, StringIO.StringIO(rcont))
+        tf.close()
+
     # add tags to things
     # tags -> list of changesets corresponding to tags
     # find tag, changeset, file
@@ -740,6 +812,18 @@ class hgweb:
 
             sys.stdout.write(z.flush())
 
+        elif args['cmd'][0] == 'tarball':
+            manifest = args['manifest'][0]
+            type = args['type'][0]
+            if type == 'zip' and self.allowzip:
+                self.ziparchive(manifest)
+            elif type == 'gz' and self.allowgz:
+                self.tararchive(manifest, 'gz')
+            elif type == 'bz2' and self.allowbz2:
+                self.tararchive(manifest, 'bz2')
+            else:
+                write(self.t("error"))
+
         else:
             write(self.t("error"))
 
--- a/templates/changeset.tmpl
+++ b/templates/changeset.tmpl
@@ -8,6 +8,7 @@
 <a href="?cmd=tags">tags</a>
 <a href="?cmd=manifest;manifest=#manifest#;path=/">manifest</a>
 <a href="?cmd=changeset;node=#node#;style=raw">raw</a>
+#tarballbuttons#
 </div>
 
 <h2>changeset: #desc|escape|firstline#</h2>