##// END OF EJS Templates
archive: remove spurious flag_bits setting...
Matt Mackall -
r4685:8f91264f default
parent child Browse files
Show More
@@ -1,211 +1,210 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)
75 fileobj = open(name, mode)
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 i.flag_bits = 0x08
137 # unzip will not honor unix file modes unless file creator is
136 # unzip will not honor unix file modes unless file creator is
138 # set to unix (id 3).
137 # set to unix (id 3).
139 i.create_system = 3
138 i.create_system = 3
140 i.external_attr = (mode | stat.S_IFREG) << 16L
139 i.external_attr = (mode | stat.S_IFREG) << 16L
141 self.z.writestr(i, data)
140 self.z.writestr(i, data)
142
141
143 def done(self):
142 def done(self):
144 self.z.close()
143 self.z.close()
145
144
146 class fileit:
145 class fileit:
147 '''write archive as files in directory.'''
146 '''write archive as files in directory.'''
148
147
149 def __init__(self, name, prefix, mtime):
148 def __init__(self, name, prefix, mtime):
150 if prefix:
149 if prefix:
151 raise util.Abort(_('cannot give prefix when archiving to files'))
150 raise util.Abort(_('cannot give prefix when archiving to files'))
152 self.basedir = name
151 self.basedir = name
153 self.dirs = {}
152 self.dirs = {}
154 self.oflags = (os.O_CREAT | os.O_EXCL | os.O_WRONLY |
153 self.oflags = (os.O_CREAT | os.O_EXCL | os.O_WRONLY |
155 getattr(os, 'O_BINARY', 0) |
154 getattr(os, 'O_BINARY', 0) |
156 getattr(os, 'O_NOFOLLOW', 0))
155 getattr(os, 'O_NOFOLLOW', 0))
157
156
158 def addfile(self, name, mode, data):
157 def addfile(self, name, mode, data):
159 destfile = os.path.join(self.basedir, name)
158 destfile = os.path.join(self.basedir, name)
160 destdir = os.path.dirname(destfile)
159 destdir = os.path.dirname(destfile)
161 if destdir not in self.dirs:
160 if destdir not in self.dirs:
162 if not os.path.isdir(destdir):
161 if not os.path.isdir(destdir):
163 os.makedirs(destdir)
162 os.makedirs(destdir)
164 self.dirs[destdir] = 1
163 self.dirs[destdir] = 1
165 os.fdopen(os.open(destfile, self.oflags, mode), 'wb').write(data)
164 os.fdopen(os.open(destfile, self.oflags, mode), 'wb').write(data)
166
165
167 def done(self):
166 def done(self):
168 pass
167 pass
169
168
170 archivers = {
169 archivers = {
171 'files': fileit,
170 'files': fileit,
172 'tar': tarit,
171 'tar': tarit,
173 'tbz2': lambda name, prefix, mtime: tarit(name, prefix, mtime, 'bz2'),
172 'tbz2': lambda name, prefix, mtime: tarit(name, prefix, mtime, 'bz2'),
174 'tgz': lambda name, prefix, mtime: tarit(name, prefix, mtime, 'gz'),
173 'tgz': lambda name, prefix, mtime: tarit(name, prefix, mtime, 'gz'),
175 'uzip': lambda name, prefix, mtime: zipit(name, prefix, mtime, False),
174 'uzip': lambda name, prefix, mtime: zipit(name, prefix, mtime, False),
176 'zip': zipit,
175 'zip': zipit,
177 }
176 }
178
177
179 def archive(repo, dest, node, kind, decode=True, matchfn=None,
178 def archive(repo, dest, node, kind, decode=True, matchfn=None,
180 prefix=None, mtime=None):
179 prefix=None, mtime=None):
181 '''create archive of repo as it was at node.
180 '''create archive of repo as it was at node.
182
181
183 dest can be name of directory, name of archive file, or file
182 dest can be name of directory, name of archive file, or file
184 object to write archive to.
183 object to write archive to.
185
184
186 kind is type of archive to create.
185 kind is type of archive to create.
187
186
188 decode tells whether to put files through decode filters from
187 decode tells whether to put files through decode filters from
189 hgrc.
188 hgrc.
190
189
191 matchfn is function to filter names of files to write to archive.
190 matchfn is function to filter names of files to write to archive.
192
191
193 prefix is name of path to put before every archive member.'''
192 prefix is name of path to put before every archive member.'''
194
193
195 def write(name, mode, data):
194 def write(name, mode, data):
196 if matchfn and not matchfn(name): return
195 if matchfn and not matchfn(name): return
197 if decode:
196 if decode:
198 data = repo.wwritedata(name, data)
197 data = repo.wwritedata(name, data)
199 archiver.addfile(name, mode, data)
198 archiver.addfile(name, mode, data)
200
199
201 ctx = repo.changectx(node)
200 ctx = repo.changectx(node)
202 archiver = archivers[kind](dest, prefix, mtime or ctx.date()[0])
201 archiver = archivers[kind](dest, prefix, mtime or ctx.date()[0])
203 m = ctx.manifest()
202 m = ctx.manifest()
204 items = m.items()
203 items = m.items()
205 items.sort()
204 items.sort()
206 write('.hg_archival.txt', 0644,
205 write('.hg_archival.txt', 0644,
207 'repo: %s\nnode: %s\n' % (hex(repo.changelog.node(0)), hex(node)))
206 'repo: %s\nnode: %s\n' % (hex(repo.changelog.node(0)), hex(node)))
208 for filename, filenode in items:
207 for filename, filenode in items:
209 write(filename, m.execf(filename) and 0755 or 0644,
208 write(filename, m.execf(filename) and 0755 or 0644,
210 repo.file(filename).read(filenode))
209 repo.file(filename).read(filenode))
211 archiver.done()
210 archiver.done()
General Comments 0
You need to be logged in to leave comments. Login now