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