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