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