##// END OF EJS Templates
Fix tgz archival on Windows....
csaba.henk@creo.hu -
r4731:1d5a2ee6 default
parent child Browse files
Show More
@@ -1,210 +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 + '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.dirs = {}
153 self.oflags = (os.O_CREAT | os.O_EXCL | os.O_WRONLY |
153 self.oflags = (os.O_CREAT | os.O_EXCL | os.O_WRONLY |
154 getattr(os, 'O_BINARY', 0) |
154 getattr(os, 'O_BINARY', 0) |
155 getattr(os, 'O_NOFOLLOW', 0))
155 getattr(os, 'O_NOFOLLOW', 0))
156
156
157 def addfile(self, name, mode, data):
157 def addfile(self, name, mode, data):
158 destfile = os.path.join(self.basedir, name)
158 destfile = os.path.join(self.basedir, name)
159 destdir = os.path.dirname(destfile)
159 destdir = os.path.dirname(destfile)
160 if destdir not in self.dirs:
160 if destdir not in self.dirs:
161 if not os.path.isdir(destdir):
161 if not os.path.isdir(destdir):
162 os.makedirs(destdir)
162 os.makedirs(destdir)
163 self.dirs[destdir] = 1
163 self.dirs[destdir] = 1
164 os.fdopen(os.open(destfile, self.oflags, mode), 'wb').write(data)
164 os.fdopen(os.open(destfile, self.oflags, mode), 'wb').write(data)
165
165
166 def done(self):
166 def done(self):
167 pass
167 pass
168
168
169 archivers = {
169 archivers = {
170 'files': fileit,
170 'files': fileit,
171 'tar': tarit,
171 'tar': tarit,
172 'tbz2': lambda name, prefix, mtime: tarit(name, prefix, mtime, 'bz2'),
172 'tbz2': lambda name, prefix, mtime: tarit(name, prefix, mtime, 'bz2'),
173 'tgz': lambda name, prefix, mtime: tarit(name, prefix, mtime, 'gz'),
173 'tgz': lambda name, prefix, mtime: tarit(name, prefix, mtime, 'gz'),
174 'uzip': lambda name, prefix, mtime: zipit(name, prefix, mtime, False),
174 'uzip': lambda name, prefix, mtime: zipit(name, prefix, mtime, False),
175 'zip': zipit,
175 'zip': zipit,
176 }
176 }
177
177
178 def archive(repo, dest, node, kind, decode=True, matchfn=None,
178 def archive(repo, dest, node, kind, decode=True, matchfn=None,
179 prefix=None, mtime=None):
179 prefix=None, mtime=None):
180 '''create archive of repo as it was at node.
180 '''create archive of repo as it was at node.
181
181
182 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
183 object to write archive to.
183 object to write archive to.
184
184
185 kind is type of archive to create.
185 kind is type of archive to create.
186
186
187 decode tells whether to put files through decode filters from
187 decode tells whether to put files through decode filters from
188 hgrc.
188 hgrc.
189
189
190 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.
191
191
192 prefix is name of path to put before every archive member.'''
192 prefix is name of path to put before every archive member.'''
193
193
194 def write(name, mode, data):
194 def write(name, mode, data):
195 if matchfn and not matchfn(name): return
195 if matchfn and not matchfn(name): return
196 if decode:
196 if decode:
197 data = repo.wwritedata(name, data)
197 data = repo.wwritedata(name, data)
198 archiver.addfile(name, mode, data)
198 archiver.addfile(name, mode, data)
199
199
200 ctx = repo.changectx(node)
200 ctx = repo.changectx(node)
201 archiver = archivers[kind](dest, prefix, mtime or ctx.date()[0])
201 archiver = archivers[kind](dest, prefix, mtime or ctx.date()[0])
202 m = ctx.manifest()
202 m = ctx.manifest()
203 items = m.items()
203 items = m.items()
204 items.sort()
204 items.sort()
205 write('.hg_archival.txt', 0644,
205 write('.hg_archival.txt', 0644,
206 '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)))
207 for filename, filenode in items:
207 for filename, filenode in items:
208 write(filename, m.execf(filename) and 0755 or 0644,
208 write(filename, m.execf(filename) and 0755 or 0644,
209 repo.file(filename).read(filenode))
209 repo.file(filename).read(filenode))
210 archiver.done()
210 archiver.done()
General Comments 0
You need to be logged in to leave comments. Login now