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