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