tarball support v0.3
authorWojciech Milkowski <wmilkowski@interia.pl>
Fri, 26 Aug 2005 20:51:34 -0700
changeset 1076 01db658cc78a
parent 1075 e254bcbfe636
child 1077 b87aeccf73d9
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
mercurial/hgweb.py
templates/changeset.tmpl
--- a/mercurial/hgweb.py	Fri Aug 26 19:20:04 2005 -0700
+++ b/mercurial/hgweb.py	Fri Aug 26 20:51:34 2005 -0700
@@ -58,8 +58,14 @@
         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 @@
             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 @@
         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 @@
                      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 @@
                                          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 @@
 
             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	Fri Aug 26 19:20:04 2005 -0700
+++ b/templates/changeset.tmpl	Fri Aug 26 20:51:34 2005 -0700
@@ -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>