##// END OF EJS Templates
archival: pass integer to struct.pack int field instead of float...
Mads Kiilerich -
r18301:49ad7030 default
parent child Browse files
Show More
@@ -1,305 +1,305 b''
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 the
6 6 # GNU General Public License version 2 or any later version.
7 7
8 8 from i18n import _
9 9 from node import hex
10 10 import match as matchmod
11 11 import cmdutil
12 12 import scmutil, util, encoding
13 13 import cStringIO, os, tarfile, time, zipfile
14 14 import zlib, gzip
15 15 import struct
16 16
17 17 # from unzip source code:
18 18 _UNX_IFREG = 0x8000
19 19 _UNX_IFLNK = 0xa000
20 20
21 21 def tidyprefix(dest, kind, prefix):
22 22 '''choose prefix to use for names in archive. make sure prefix is
23 23 safe for consumers.'''
24 24
25 25 if prefix:
26 26 prefix = util.normpath(prefix)
27 27 else:
28 28 if not isinstance(dest, str):
29 29 raise ValueError('dest must be string if no prefix')
30 30 prefix = os.path.basename(dest)
31 31 lower = prefix.lower()
32 32 for sfx in exts.get(kind, []):
33 33 if lower.endswith(sfx):
34 34 prefix = prefix[:-len(sfx)]
35 35 break
36 36 lpfx = os.path.normpath(util.localpath(prefix))
37 37 prefix = util.pconvert(lpfx)
38 38 if not prefix.endswith('/'):
39 39 prefix += '/'
40 40 if prefix.startswith('../') or os.path.isabs(lpfx) or '/../' in prefix:
41 41 raise util.Abort(_('archive prefix contains illegal components'))
42 42 return prefix
43 43
44 44 exts = {
45 45 'tar': ['.tar'],
46 46 'tbz2': ['.tbz2', '.tar.bz2'],
47 47 'tgz': ['.tgz', '.tar.gz'],
48 48 'zip': ['.zip'],
49 49 }
50 50
51 51 def guesskind(dest):
52 52 for kind, extensions in exts.iteritems():
53 53 if util.any(dest.endswith(ext) for ext in extensions):
54 54 return kind
55 55 return None
56 56
57 57
58 58 class tarit(object):
59 59 '''write archive to tar file or stream. can write uncompressed,
60 60 or compress with gzip or bzip2.'''
61 61
62 62 class GzipFileWithTime(gzip.GzipFile):
63 63
64 64 def __init__(self, *args, **kw):
65 65 timestamp = None
66 66 if 'timestamp' in kw:
67 67 timestamp = kw.pop('timestamp')
68 68 if timestamp is None:
69 69 self.timestamp = time.time()
70 70 else:
71 71 self.timestamp = timestamp
72 72 gzip.GzipFile.__init__(self, *args, **kw)
73 73
74 74 def _write_gzip_header(self):
75 75 self.fileobj.write('\037\213') # magic header
76 76 self.fileobj.write('\010') # compression method
77 77 # Python 2.6 deprecates self.filename
78 78 fname = getattr(self, 'name', None) or self.filename
79 79 if fname and fname.endswith('.gz'):
80 80 fname = fname[:-3]
81 81 flags = 0
82 82 if fname:
83 83 flags = gzip.FNAME
84 84 self.fileobj.write(chr(flags))
85 85 gzip.write32u(self.fileobj, long(self.timestamp))
86 86 self.fileobj.write('\002')
87 87 self.fileobj.write('\377')
88 88 if fname:
89 89 self.fileobj.write(fname + '\000')
90 90
91 91 def __init__(self, dest, mtime, kind=''):
92 92 self.mtime = mtime
93 93 self.fileobj = None
94 94
95 95 def taropen(name, mode, fileobj=None):
96 96 if kind == 'gz':
97 97 mode = mode[0]
98 98 if not fileobj:
99 99 fileobj = open(name, mode + 'b')
100 100 gzfileobj = self.GzipFileWithTime(name, mode + 'b',
101 101 zlib.Z_BEST_COMPRESSION,
102 102 fileobj, timestamp=mtime)
103 103 self.fileobj = gzfileobj
104 104 return tarfile.TarFile.taropen(name, mode, gzfileobj)
105 105 else:
106 106 self.fileobj = fileobj
107 107 return tarfile.open(name, mode + kind, fileobj)
108 108
109 109 if isinstance(dest, str):
110 110 self.z = taropen(dest, mode='w:')
111 111 else:
112 112 # Python 2.5-2.5.1 have a regression that requires a name arg
113 113 self.z = taropen(name='', mode='w|', fileobj=dest)
114 114
115 115 def addfile(self, name, mode, islink, data):
116 116 i = tarfile.TarInfo(name)
117 117 i.mtime = self.mtime
118 118 i.size = len(data)
119 119 if islink:
120 120 i.type = tarfile.SYMTYPE
121 121 i.mode = 0777
122 122 i.linkname = data
123 123 data = None
124 124 i.size = 0
125 125 else:
126 126 i.mode = mode
127 127 data = cStringIO.StringIO(data)
128 128 self.z.addfile(i, data)
129 129
130 130 def done(self):
131 131 self.z.close()
132 132 if self.fileobj:
133 133 self.fileobj.close()
134 134
135 135 class tellable(object):
136 136 '''provide tell method for zipfile.ZipFile when writing to http
137 137 response file object.'''
138 138
139 139 def __init__(self, fp):
140 140 self.fp = fp
141 141 self.offset = 0
142 142
143 143 def __getattr__(self, key):
144 144 return getattr(self.fp, key)
145 145
146 146 def write(self, s):
147 147 self.fp.write(s)
148 148 self.offset += len(s)
149 149
150 150 def tell(self):
151 151 return self.offset
152 152
153 153 class zipit(object):
154 154 '''write archive to zip file or stream. can write uncompressed,
155 155 or compressed with deflate.'''
156 156
157 157 def __init__(self, dest, mtime, compress=True):
158 158 if not isinstance(dest, str):
159 159 try:
160 160 dest.tell()
161 161 except (AttributeError, IOError):
162 162 dest = tellable(dest)
163 163 self.z = zipfile.ZipFile(dest, 'w',
164 164 compress and zipfile.ZIP_DEFLATED or
165 165 zipfile.ZIP_STORED)
166 166
167 167 # Python's zipfile module emits deprecation warnings if we try
168 168 # to store files with a date before 1980.
169 169 epoch = 315532800 # calendar.timegm((1980, 1, 1, 0, 0, 0, 1, 1, 0))
170 170 if mtime < epoch:
171 171 mtime = epoch
172 172
173 173 self.mtime = mtime
174 174 self.date_time = time.gmtime(mtime)[:6]
175 175
176 176 def addfile(self, name, mode, islink, data):
177 177 i = zipfile.ZipInfo(name, self.date_time)
178 178 i.compress_type = self.z.compression
179 179 # unzip will not honor unix file modes unless file creator is
180 180 # set to unix (id 3).
181 181 i.create_system = 3
182 182 ftype = _UNX_IFREG
183 183 if islink:
184 184 mode = 0777
185 185 ftype = _UNX_IFLNK
186 186 i.external_attr = (mode | ftype) << 16L
187 187 # add "extended-timestamp" extra block, because zip archives
188 188 # without this will be extracted with unexpected timestamp,
189 189 # if TZ is not configured as GMT
190 190 i.extra += struct.pack('<hhBl',
191 191 0x5455, # block type: "extended-timestamp"
192 192 1 + 4, # size of this block
193 193 1, # "modification time is present"
194 self.mtime) # time of last modification (UTC)
194 int(self.mtime)) # last modification (UTC)
195 195 self.z.writestr(i, data)
196 196
197 197 def done(self):
198 198 self.z.close()
199 199
200 200 class fileit(object):
201 201 '''write archive as files in directory.'''
202 202
203 203 def __init__(self, name, mtime):
204 204 self.basedir = name
205 205 self.opener = scmutil.opener(self.basedir)
206 206
207 207 def addfile(self, name, mode, islink, data):
208 208 if islink:
209 209 self.opener.symlink(data, name)
210 210 return
211 211 f = self.opener(name, "w", atomictemp=True)
212 212 f.write(data)
213 213 f.close()
214 214 destfile = os.path.join(self.basedir, name)
215 215 os.chmod(destfile, mode)
216 216
217 217 def done(self):
218 218 pass
219 219
220 220 archivers = {
221 221 'files': fileit,
222 222 'tar': tarit,
223 223 'tbz2': lambda name, mtime: tarit(name, mtime, 'bz2'),
224 224 'tgz': lambda name, mtime: tarit(name, mtime, 'gz'),
225 225 'uzip': lambda name, mtime: zipit(name, mtime, False),
226 226 'zip': zipit,
227 227 }
228 228
229 229 def archive(repo, dest, node, kind, decode=True, matchfn=None,
230 230 prefix=None, mtime=None, subrepos=False):
231 231 '''create archive of repo as it was at node.
232 232
233 233 dest can be name of directory, name of archive file, or file
234 234 object to write archive to.
235 235
236 236 kind is type of archive to create.
237 237
238 238 decode tells whether to put files through decode filters from
239 239 hgrc.
240 240
241 241 matchfn is function to filter names of files to write to archive.
242 242
243 243 prefix is name of path to put before every archive member.'''
244 244
245 245 if kind == 'files':
246 246 if prefix:
247 247 raise util.Abort(_('cannot give prefix when archiving to files'))
248 248 else:
249 249 prefix = tidyprefix(dest, kind, prefix)
250 250
251 251 def write(name, mode, islink, getdata):
252 252 data = getdata()
253 253 if decode:
254 254 data = repo.wwritedata(name, data)
255 255 archiver.addfile(prefix + name, mode, islink, data)
256 256
257 257 if kind not in archivers:
258 258 raise util.Abort(_("unknown archive type '%s'") % kind)
259 259
260 260 ctx = repo[node]
261 261 archiver = archivers[kind](dest, mtime or ctx.date()[0])
262 262
263 263 if repo.ui.configbool("ui", "archivemeta", True):
264 264 def metadata():
265 265 base = 'repo: %s\nnode: %s\nbranch: %s\n' % (
266 266 repo[0].hex(), hex(node), encoding.fromlocal(ctx.branch()))
267 267
268 268 tags = ''.join('tag: %s\n' % t for t in ctx.tags()
269 269 if repo.tagtype(t) == 'global')
270 270 if not tags:
271 271 repo.ui.pushbuffer()
272 272 opts = {'template': '{latesttag}\n{latesttagdistance}',
273 273 'style': '', 'patch': None, 'git': None}
274 274 cmdutil.show_changeset(repo.ui, repo, opts).show(ctx)
275 275 ltags, dist = repo.ui.popbuffer().split('\n')
276 276 tags = ''.join('latesttag: %s\n' % t for t in ltags.split(':'))
277 277 tags += 'latesttagdistance: %s\n' % dist
278 278
279 279 return base + tags
280 280
281 281 name = '.hg_archival.txt'
282 282 if not matchfn or matchfn(name):
283 283 write(name, 0644, False, metadata)
284 284
285 285 if matchfn:
286 286 files = [f for f in ctx.manifest().keys() if matchfn(f)]
287 287 else:
288 288 files = ctx.manifest().keys()
289 289 files.sort()
290 290 total = len(files)
291 291 repo.ui.progress(_('archiving'), 0, unit=_('files'), total=total)
292 292 for i, f in enumerate(files):
293 293 ff = ctx.flags(f)
294 294 write(f, 'x' in ff and 0755 or 0644, 'l' in ff, ctx[f].data)
295 295 repo.ui.progress(_('archiving'), i + 1, item=f,
296 296 unit=_('files'), total=total)
297 297 repo.ui.progress(_('archiving'), None)
298 298
299 299 if subrepos:
300 300 for subpath in ctx.substate:
301 301 sub = ctx.sub(subpath)
302 302 submatch = matchmod.narrowmatcher(subpath, matchfn)
303 303 sub.archive(repo.ui, archiver, prefix, submatch)
304 304
305 305 archiver.done()
General Comments 0
You need to be logged in to leave comments. Login now