##// END OF EJS Templates
timestamp of gzip archives taken from changeset context
csaba.henk@creo.hu -
r4652:06de6567 default
parent child Browse files
Show More
@@ -1,171 +1,211
1 1 # archival.py - revision archival for mercurial
2 2 #
3 3 # Copyright 2006 Vadim Gelfer <vadim.gelfer@gmail.com>
4 4 #
5 5 # This software may be used and distributed according to the terms of
6 6 # the GNU General Public License, incorporated herein by reference.
7 7
8 8 from i18n import _
9 9 from node import *
10 10 import cStringIO, os, stat, tarfile, time, util, zipfile
11 import zlib, gzip
11 12
12 13 def tidyprefix(dest, prefix, suffixes):
13 14 '''choose prefix to use for names in archive. make sure prefix is
14 15 safe for consumers.'''
15 16
16 17 if prefix:
17 18 prefix = prefix.replace('\\', '/')
18 19 else:
19 20 if not isinstance(dest, str):
20 21 raise ValueError('dest must be string if no prefix')
21 22 prefix = os.path.basename(dest)
22 23 lower = prefix.lower()
23 24 for sfx in suffixes:
24 25 if lower.endswith(sfx):
25 26 prefix = prefix[:-len(sfx)]
26 27 break
27 28 lpfx = os.path.normpath(util.localpath(prefix))
28 29 prefix = util.pconvert(lpfx)
29 30 if not prefix.endswith('/'):
30 31 prefix += '/'
31 32 if prefix.startswith('../') or os.path.isabs(lpfx) or '/../' in prefix:
32 33 raise util.Abort(_('archive prefix contains illegal components'))
33 34 return prefix
34 35
35 36 class tarit:
36 37 '''write archive to tar file or stream. can write uncompressed,
37 38 or compress with gzip or bzip2.'''
38 39
40 class GzipFileWithTime(gzip.GzipFile):
41
42 def __init__(self, *args, **kw):
43 timestamp = None
44 if 'timestamp' in kw:
45 timestamp = kw.pop('timestamp')
46 if timestamp == None:
47 self.timestamp = time.time()
48 else:
49 self.timestamp = timestamp
50 gzip.GzipFile.__init__(self, *args, **kw)
51
52 def _write_gzip_header(self):
53 self.fileobj.write('\037\213') # magic header
54 self.fileobj.write('\010') # compression method
55 fname = self.filename[:-3]
56 flags = 0
57 if fname:
58 flags = gzip.FNAME
59 self.fileobj.write(chr(flags))
60 gzip.write32u(self.fileobj, long(self.timestamp))
61 self.fileobj.write('\002')
62 self.fileobj.write('\377')
63 if fname:
64 self.fileobj.write(fname + '\000')
65
39 66 def __init__(self, dest, prefix, mtime, kind=''):
40 67 self.prefix = tidyprefix(dest, prefix, ['.tar', '.tar.bz2', '.tar.gz',
41 68 '.tgz', '.tbz2'])
42 69 self.mtime = mtime
70
71 def taropen(name, mode, fileobj=None):
72 if kind == 'gz':
73 mode = mode[0]
74 if not fileobj:
75 fileobj = open(name, mode)
76 gzfileobj = self.GzipFileWithTime(name, mode + 'b',
77 zlib.Z_BEST_COMPRESSION,
78 fileobj, timestamp=mtime)
79 return tarfile.TarFile.taropen(name, mode, gzfileobj)
80 else:
81 return tarfile.open(name, mode + kind, fileobj)
82
43 83 if isinstance(dest, str):
44 self.z = tarfile.open(dest, mode='w:'+kind)
84 self.z = taropen(dest, mode='w:')
45 85 else:
46 86 # Python 2.5-2.5.1 have a regression that requires a name arg
47 self.z = tarfile.open(name='', mode='w|'+kind, fileobj=dest)
87 self.z = taropen(name='', mode='w|', fileobj=dest)
48 88
49 89 def addfile(self, name, mode, data):
50 90 i = tarfile.TarInfo(self.prefix + name)
51 91 i.mtime = self.mtime
52 92 i.size = len(data)
53 93 i.mode = mode
54 94 self.z.addfile(i, cStringIO.StringIO(data))
55 95
56 96 def done(self):
57 97 self.z.close()
58 98
59 99 class tellable:
60 100 '''provide tell method for zipfile.ZipFile when writing to http
61 101 response file object.'''
62 102
63 103 def __init__(self, fp):
64 104 self.fp = fp
65 105 self.offset = 0
66 106
67 107 def __getattr__(self, key):
68 108 return getattr(self.fp, key)
69 109
70 110 def write(self, s):
71 111 self.fp.write(s)
72 112 self.offset += len(s)
73 113
74 114 def tell(self):
75 115 return self.offset
76 116
77 117 class zipit:
78 118 '''write archive to zip file or stream. can write uncompressed,
79 119 or compressed with deflate.'''
80 120
81 121 def __init__(self, dest, prefix, mtime, compress=True):
82 122 self.prefix = tidyprefix(dest, prefix, ('.zip',))
83 123 if not isinstance(dest, str):
84 124 try:
85 125 dest.tell()
86 126 except (AttributeError, IOError):
87 127 dest = tellable(dest)
88 128 self.z = zipfile.ZipFile(dest, 'w',
89 129 compress and zipfile.ZIP_DEFLATED or
90 130 zipfile.ZIP_STORED)
91 131 self.date_time = time.gmtime(mtime)[:6]
92 132
93 133 def addfile(self, name, mode, data):
94 134 i = zipfile.ZipInfo(self.prefix + name, self.date_time)
95 135 i.compress_type = self.z.compression
96 136 i.flag_bits = 0x08
97 137 # unzip will not honor unix file modes unless file creator is
98 138 # set to unix (id 3).
99 139 i.create_system = 3
100 140 i.external_attr = (mode | stat.S_IFREG) << 16L
101 141 self.z.writestr(i, data)
102 142
103 143 def done(self):
104 144 self.z.close()
105 145
106 146 class fileit:
107 147 '''write archive as files in directory.'''
108 148
109 149 def __init__(self, name, prefix, mtime):
110 150 if prefix:
111 151 raise util.Abort(_('cannot give prefix when archiving to files'))
112 152 self.basedir = name
113 153 self.dirs = {}
114 154 self.oflags = (os.O_CREAT | os.O_EXCL | os.O_WRONLY |
115 155 getattr(os, 'O_BINARY', 0) |
116 156 getattr(os, 'O_NOFOLLOW', 0))
117 157
118 158 def addfile(self, name, mode, data):
119 159 destfile = os.path.join(self.basedir, name)
120 160 destdir = os.path.dirname(destfile)
121 161 if destdir not in self.dirs:
122 162 if not os.path.isdir(destdir):
123 163 os.makedirs(destdir)
124 164 self.dirs[destdir] = 1
125 165 os.fdopen(os.open(destfile, self.oflags, mode), 'wb').write(data)
126 166
127 167 def done(self):
128 168 pass
129 169
130 170 archivers = {
131 171 'files': fileit,
132 172 'tar': tarit,
133 173 'tbz2': lambda name, prefix, mtime: tarit(name, prefix, mtime, 'bz2'),
134 174 'tgz': lambda name, prefix, mtime: tarit(name, prefix, mtime, 'gz'),
135 175 'uzip': lambda name, prefix, mtime: zipit(name, prefix, mtime, False),
136 176 'zip': zipit,
137 177 }
138 178
139 179 def archive(repo, dest, node, kind, decode=True, matchfn=None,
140 180 prefix=None, mtime=None):
141 181 '''create archive of repo as it was at node.
142 182
143 183 dest can be name of directory, name of archive file, or file
144 184 object to write archive to.
145 185
146 186 kind is type of archive to create.
147 187
148 188 decode tells whether to put files through decode filters from
149 189 hgrc.
150 190
151 191 matchfn is function to filter names of files to write to archive.
152 192
153 193 prefix is name of path to put before every archive member.'''
154 194
155 195 def write(name, mode, data):
156 196 if matchfn and not matchfn(name): return
157 197 if decode:
158 198 data = repo.wwritedata(name, data)
159 199 archiver.addfile(name, mode, data)
160 200
161 201 ctx = repo.changectx(node)
162 202 archiver = archivers[kind](dest, prefix, mtime or ctx.date()[0])
163 203 m = ctx.manifest()
164 204 items = m.items()
165 205 items.sort()
166 206 write('.hg_archival.txt', 0644,
167 207 'repo: %s\nnode: %s\n' % (hex(repo.changelog.node(0)), hex(node)))
168 208 for filename, filenode in items:
169 209 write(filename, m.execf(filename) and 0755 or 0644,
170 210 repo.file(filename).read(filenode))
171 211 archiver.done()
General Comments 0
You need to be logged in to leave comments. Login now