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