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