comparison mercurial/archival.py @ 43076:2372284d9457

formatting: blacken the codebase This is using my patch to black (https://github.com/psf/black/pull/826) so we don't un-wrap collection literals. Done with: hg files 'set:**.py - mercurial/thirdparty/** - "contrib/python-zstandard/**"' | xargs black -S # skip-blame mass-reformatting only # no-check-commit reformats foo_bar functions Differential Revision: https://phab.mercurial-scm.org/D6971
author Augie Fackler <augie@google.com>
date Sun, 06 Oct 2019 09:45:02 -0400
parents c04e0836f039
children 687b865b95ad
comparison
equal deleted inserted replaced
43075:57875cf423c9 43076:2372284d9457
14 import time 14 import time
15 import zipfile 15 import zipfile
16 import zlib 16 import zlib
17 17
18 from .i18n import _ 18 from .i18n import _
19 from .node import ( 19 from .node import nullrev
20 nullrev,
21 )
22 20
23 from . import ( 21 from . import (
24 error, 22 error,
25 formatter, 23 formatter,
26 match as matchmod, 24 match as matchmod,
27 pycompat, 25 pycompat,
28 scmutil, 26 scmutil,
29 util, 27 util,
30 vfs as vfsmod, 28 vfs as vfsmod,
31 ) 29 )
30
32 stringio = util.stringio 31 stringio = util.stringio
33 32
34 # from unzip source code: 33 # from unzip source code:
35 _UNX_IFREG = 0x8000 34 _UNX_IFREG = 0x8000
36 _UNX_IFLNK = 0xa000 35 _UNX_IFLNK = 0xA000
36
37 37
38 def tidyprefix(dest, kind, prefix): 38 def tidyprefix(dest, kind, prefix):
39 '''choose prefix to use for names in archive. make sure prefix is 39 '''choose prefix to use for names in archive. make sure prefix is
40 safe for consumers.''' 40 safe for consumers.'''
41 41
46 raise ValueError('dest must be string if no prefix') 46 raise ValueError('dest must be string if no prefix')
47 prefix = os.path.basename(dest) 47 prefix = os.path.basename(dest)
48 lower = prefix.lower() 48 lower = prefix.lower()
49 for sfx in exts.get(kind, []): 49 for sfx in exts.get(kind, []):
50 if lower.endswith(sfx): 50 if lower.endswith(sfx):
51 prefix = prefix[:-len(sfx)] 51 prefix = prefix[: -len(sfx)]
52 break 52 break
53 lpfx = os.path.normpath(util.localpath(prefix)) 53 lpfx = os.path.normpath(util.localpath(prefix))
54 prefix = util.pconvert(lpfx) 54 prefix = util.pconvert(lpfx)
55 if not prefix.endswith('/'): 55 if not prefix.endswith('/'):
56 prefix += '/' 56 prefix += '/'
60 prefix = prefix[2:] 60 prefix = prefix[2:]
61 if prefix.startswith('../') or os.path.isabs(lpfx) or '/../' in prefix: 61 if prefix.startswith('../') or os.path.isabs(lpfx) or '/../' in prefix:
62 raise error.Abort(_('archive prefix contains illegal components')) 62 raise error.Abort(_('archive prefix contains illegal components'))
63 return prefix 63 return prefix
64 64
65
65 exts = { 66 exts = {
66 'tar': ['.tar'], 67 'tar': ['.tar'],
67 'tbz2': ['.tbz2', '.tar.bz2'], 68 'tbz2': ['.tbz2', '.tar.bz2'],
68 'tgz': ['.tgz', '.tar.gz'], 69 'tgz': ['.tgz', '.tar.gz'],
69 'zip': ['.zip'], 70 'zip': ['.zip'],
70 'txz': ['.txz', '.tar.xz'] 71 'txz': ['.txz', '.tar.xz'],
71 } 72 }
73
72 74
73 def guesskind(dest): 75 def guesskind(dest):
74 for kind, extensions in exts.iteritems(): 76 for kind, extensions in exts.iteritems():
75 if any(dest.endswith(ext) for ext in extensions): 77 if any(dest.endswith(ext) for ext in extensions):
76 return kind 78 return kind
77 return None 79 return None
78 80
81
79 def _rootctx(repo): 82 def _rootctx(repo):
80 # repo[0] may be hidden 83 # repo[0] may be hidden
81 for rev in repo: 84 for rev in repo:
82 return repo[rev] 85 return repo[rev]
83 return repo[nullrev] 86 return repo[nullrev]
87
84 88
85 # {tags} on ctx includes local tags and 'tip', with no current way to limit 89 # {tags} on ctx includes local tags and 'tip', with no current way to limit
86 # that to global tags. Therefore, use {latesttag} as a substitute when 90 # that to global tags. Therefore, use {latesttag} as a substitute when
87 # the distance is 0, since that will be the list of global tags on ctx. 91 # the distance is 0, since that will be the list of global tags on ctx.
88 _defaultmetatemplate = br''' 92 _defaultmetatemplate = br'''
92 {ifeq(latesttagdistance, 0, join(latesttag % "tag: {tag}", "\n"), 96 {ifeq(latesttagdistance, 0, join(latesttag % "tag: {tag}", "\n"),
93 separate("\n", 97 separate("\n",
94 join(latesttag % "latesttag: {tag}", "\n"), 98 join(latesttag % "latesttag: {tag}", "\n"),
95 "latesttagdistance: {latesttagdistance}", 99 "latesttagdistance: {latesttagdistance}",
96 "changessincelatesttag: {changessincelatesttag}"))} 100 "changessincelatesttag: {changessincelatesttag}"))}
97 '''[1:] # drop leading '\n' 101 '''[
102 1:
103 ] # drop leading '\n'
104
98 105
99 def buildmetadata(ctx): 106 def buildmetadata(ctx):
100 '''build content of .hg_archival.txt''' 107 '''build content of .hg_archival.txt'''
101 repo = ctx.repo() 108 repo = ctx.repo()
102 109
103 opts = { 110 opts = {
104 'template': repo.ui.config('experimental', 'archivemetatemplate', 111 'template': repo.ui.config(
105 _defaultmetatemplate) 112 'experimental', 'archivemetatemplate', _defaultmetatemplate
113 )
106 } 114 }
107 115
108 out = util.stringio() 116 out = util.stringio()
109 117
110 fm = formatter.formatter(repo.ui, out, 'archive', opts) 118 fm = formatter.formatter(repo.ui, out, 'archive', opts)
119 fm.data(dirty=dirty) 127 fm.data(dirty=dirty)
120 fm.end() 128 fm.end()
121 129
122 return out.getvalue() 130 return out.getvalue()
123 131
132
124 class tarit(object): 133 class tarit(object):
125 '''write archive to tar file or stream. can write uncompressed, 134 '''write archive to tar file or stream. can write uncompressed,
126 or compress with gzip or bzip2.''' 135 or compress with gzip or bzip2.'''
127 136
128 class GzipFileWithTime(gzip.GzipFile): 137 class GzipFileWithTime(gzip.GzipFile):
129
130 def __init__(self, *args, **kw): 138 def __init__(self, *args, **kw):
131 timestamp = None 139 timestamp = None
132 if r'timestamp' in kw: 140 if r'timestamp' in kw:
133 timestamp = kw.pop(r'timestamp') 141 timestamp = kw.pop(r'timestamp')
134 if timestamp is None: 142 if timestamp is None:
136 else: 144 else:
137 self.timestamp = timestamp 145 self.timestamp = timestamp
138 gzip.GzipFile.__init__(self, *args, **kw) 146 gzip.GzipFile.__init__(self, *args, **kw)
139 147
140 def _write_gzip_header(self): 148 def _write_gzip_header(self):
141 self.fileobj.write('\037\213') # magic header 149 self.fileobj.write('\037\213') # magic header
142 self.fileobj.write('\010') # compression method 150 self.fileobj.write('\010') # compression method
143 fname = self.name 151 fname = self.name
144 if fname and fname.endswith('.gz'): 152 if fname and fname.endswith('.gz'):
145 fname = fname[:-3] 153 fname = fname[:-3]
146 flags = 0 154 flags = 0
147 if fname: 155 if fname:
160 def taropen(mode, name='', fileobj=None): 168 def taropen(mode, name='', fileobj=None):
161 if kind == 'gz': 169 if kind == 'gz':
162 mode = mode[0:1] 170 mode = mode[0:1]
163 if not fileobj: 171 if not fileobj:
164 fileobj = open(name, mode + 'b') 172 fileobj = open(name, mode + 'b')
165 gzfileobj = self.GzipFileWithTime(name, 173 gzfileobj = self.GzipFileWithTime(
166 pycompat.sysstr(mode + 'b'), 174 name,
167 zlib.Z_BEST_COMPRESSION, 175 pycompat.sysstr(mode + 'b'),
168 fileobj, timestamp=mtime) 176 zlib.Z_BEST_COMPRESSION,
177 fileobj,
178 timestamp=mtime,
179 )
169 self.fileobj = gzfileobj 180 self.fileobj = gzfileobj
170 return tarfile.TarFile.taropen( 181 return tarfile.TarFile.taropen(
171 name, pycompat.sysstr(mode), gzfileobj) 182 name, pycompat.sysstr(mode), gzfileobj
183 )
172 else: 184 else:
173 return tarfile.open( 185 return tarfile.open(name, pycompat.sysstr(mode + kind), fileobj)
174 name, pycompat.sysstr(mode + kind), fileobj)
175 186
176 if isinstance(dest, bytes): 187 if isinstance(dest, bytes):
177 self.z = taropen('w:', name=dest) 188 self.z = taropen('w:', name=dest)
178 else: 189 else:
179 self.z = taropen('w|', fileobj=dest) 190 self.z = taropen('w|', fileobj=dest)
197 def done(self): 208 def done(self):
198 self.z.close() 209 self.z.close()
199 if self.fileobj: 210 if self.fileobj:
200 self.fileobj.close() 211 self.fileobj.close()
201 212
213
202 class zipit(object): 214 class zipit(object):
203 '''write archive to zip file or stream. can write uncompressed, 215 '''write archive to zip file or stream. can write uncompressed,
204 or compressed with deflate.''' 216 or compressed with deflate.'''
205 217
206 def __init__(self, dest, mtime, compress=True): 218 def __init__(self, dest, mtime, compress=True):
207 if isinstance(dest, bytes): 219 if isinstance(dest, bytes):
208 dest = pycompat.fsdecode(dest) 220 dest = pycompat.fsdecode(dest)
209 self.z = zipfile.ZipFile(dest, r'w', 221 self.z = zipfile.ZipFile(
210 compress and zipfile.ZIP_DEFLATED or 222 dest, r'w', compress and zipfile.ZIP_DEFLATED or zipfile.ZIP_STORED
211 zipfile.ZIP_STORED) 223 )
212 224
213 # Python's zipfile module emits deprecation warnings if we try 225 # Python's zipfile module emits deprecation warnings if we try
214 # to store files with a date before 1980. 226 # to store files with a date before 1980.
215 epoch = 315532800 # calendar.timegm((1980, 1, 1, 0, 0, 0, 1, 1, 0)) 227 epoch = 315532800 # calendar.timegm((1980, 1, 1, 0, 0, 0, 1, 1, 0))
216 if mtime < epoch: 228 if mtime < epoch:
217 mtime = epoch 229 mtime = epoch
218 230
219 self.mtime = mtime 231 self.mtime = mtime
220 self.date_time = time.gmtime(mtime)[:6] 232 self.date_time = time.gmtime(mtime)[:6]
231 ftype = _UNX_IFLNK 243 ftype = _UNX_IFLNK
232 i.external_attr = (mode | ftype) << 16 244 i.external_attr = (mode | ftype) << 16
233 # add "extended-timestamp" extra block, because zip archives 245 # add "extended-timestamp" extra block, because zip archives
234 # without this will be extracted with unexpected timestamp, 246 # without this will be extracted with unexpected timestamp,
235 # if TZ is not configured as GMT 247 # if TZ is not configured as GMT
236 i.extra += struct.pack('<hhBl', 248 i.extra += struct.pack(
237 0x5455, # block type: "extended-timestamp" 249 '<hhBl',
238 1 + 4, # size of this block 250 0x5455, # block type: "extended-timestamp"
239 1, # "modification time is present" 251 1 + 4, # size of this block
240 int(self.mtime)) # last modification (UTC) 252 1, # "modification time is present"
253 int(self.mtime),
254 ) # last modification (UTC)
241 self.z.writestr(i, data) 255 self.z.writestr(i, data)
242 256
243 def done(self): 257 def done(self):
244 self.z.close() 258 self.z.close()
259
245 260
246 class fileit(object): 261 class fileit(object):
247 '''write archive as files in directory.''' 262 '''write archive as files in directory.'''
248 263
249 def __init__(self, name, mtime): 264 def __init__(self, name, mtime):
264 os.utime(destfile, (self.mtime, self.mtime)) 279 os.utime(destfile, (self.mtime, self.mtime))
265 280
266 def done(self): 281 def done(self):
267 pass 282 pass
268 283
284
269 archivers = { 285 archivers = {
270 'files': fileit, 286 'files': fileit,
271 'tar': tarit, 287 'tar': tarit,
272 'tbz2': lambda name, mtime: tarit(name, mtime, 'bz2'), 288 'tbz2': lambda name, mtime: tarit(name, mtime, 'bz2'),
273 'tgz': lambda name, mtime: tarit(name, mtime, 'gz'), 289 'tgz': lambda name, mtime: tarit(name, mtime, 'gz'),
274 'txz': lambda name, mtime: tarit(name, mtime, 'xz'), 290 'txz': lambda name, mtime: tarit(name, mtime, 'xz'),
275 'uzip': lambda name, mtime: zipit(name, mtime, False), 291 'uzip': lambda name, mtime: zipit(name, mtime, False),
276 'zip': zipit, 292 'zip': zipit,
277 } 293 }
278 294
279 def archive(repo, dest, node, kind, decode=True, match=None, 295
280 prefix='', mtime=None, subrepos=False): 296 def archive(
297 repo,
298 dest,
299 node,
300 kind,
301 decode=True,
302 match=None,
303 prefix='',
304 mtime=None,
305 subrepos=False,
306 ):
281 '''create archive of repo as it was at node. 307 '''create archive of repo as it was at node.
282 308
283 dest can be name of directory, name of archive file, or file 309 dest can be name of directory, name of archive file, or file
284 object to write archive to. 310 object to write archive to.
285 311
328 354
329 files = [f for f in ctx.manifest().matches(match)] 355 files = [f for f in ctx.manifest().matches(match)]
330 total = len(files) 356 total = len(files)
331 if total: 357 if total:
332 files.sort() 358 files.sort()
333 scmutil.prefetchfiles(repo, [ctx.rev()], 359 scmutil.prefetchfiles(
334 scmutil.matchfiles(repo, files)) 360 repo, [ctx.rev()], scmutil.matchfiles(repo, files)
335 progress = repo.ui.makeprogress(_('archiving'), unit=_('files'), 361 )
336 total=total) 362 progress = repo.ui.makeprogress(
363 _('archiving'), unit=_('files'), total=total
364 )
337 progress.update(0) 365 progress.update(0)
338 for f in files: 366 for f in files:
339 ff = ctx.flags(f) 367 ff = ctx.flags(f)
340 write(f, 'x' in ff and 0o755 or 0o644, 'l' in ff, ctx[f].data) 368 write(f, 'x' in ff and 0o755 or 0o644, 'l' in ff, ctx[f].data)
341 progress.increment(item=f) 369 progress.increment(item=f)