##// END OF EJS Templates
Merge with crew-stable
Bryan O'Sullivan -
r17629:331d6118 merge default
parent child Browse files
Show More
@@ -1,295 +1,305 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 the
5 # This software may be used and distributed according to the terms of the
6 # GNU General Public License version 2 or any later version.
6 # GNU General Public License version 2 or any later version.
7
7
8 from i18n import _
8 from i18n import _
9 from node import hex
9 from node import hex
10 import match as matchmod
10 import match as matchmod
11 import cmdutil
11 import cmdutil
12 import scmutil, util, encoding
12 import scmutil, util, encoding
13 import cStringIO, os, tarfile, time, zipfile
13 import cStringIO, os, tarfile, time, zipfile
14 import zlib, gzip
14 import zlib, gzip
15 import struct
15
16
16 # from unzip source code:
17 # from unzip source code:
17 _UNX_IFREG = 0x8000
18 _UNX_IFREG = 0x8000
18 _UNX_IFLNK = 0xa000
19 _UNX_IFLNK = 0xa000
19
20
20 def tidyprefix(dest, kind, prefix):
21 def tidyprefix(dest, kind, prefix):
21 '''choose prefix to use for names in archive. make sure prefix is
22 '''choose prefix to use for names in archive. make sure prefix is
22 safe for consumers.'''
23 safe for consumers.'''
23
24
24 if prefix:
25 if prefix:
25 prefix = util.normpath(prefix)
26 prefix = util.normpath(prefix)
26 else:
27 else:
27 if not isinstance(dest, str):
28 if not isinstance(dest, str):
28 raise ValueError('dest must be string if no prefix')
29 raise ValueError('dest must be string if no prefix')
29 prefix = os.path.basename(dest)
30 prefix = os.path.basename(dest)
30 lower = prefix.lower()
31 lower = prefix.lower()
31 for sfx in exts.get(kind, []):
32 for sfx in exts.get(kind, []):
32 if lower.endswith(sfx):
33 if lower.endswith(sfx):
33 prefix = prefix[:-len(sfx)]
34 prefix = prefix[:-len(sfx)]
34 break
35 break
35 lpfx = os.path.normpath(util.localpath(prefix))
36 lpfx = os.path.normpath(util.localpath(prefix))
36 prefix = util.pconvert(lpfx)
37 prefix = util.pconvert(lpfx)
37 if not prefix.endswith('/'):
38 if not prefix.endswith('/'):
38 prefix += '/'
39 prefix += '/'
39 if prefix.startswith('../') or os.path.isabs(lpfx) or '/../' in prefix:
40 if prefix.startswith('../') or os.path.isabs(lpfx) or '/../' in prefix:
40 raise util.Abort(_('archive prefix contains illegal components'))
41 raise util.Abort(_('archive prefix contains illegal components'))
41 return prefix
42 return prefix
42
43
43 exts = {
44 exts = {
44 'tar': ['.tar'],
45 'tar': ['.tar'],
45 'tbz2': ['.tbz2', '.tar.bz2'],
46 'tbz2': ['.tbz2', '.tar.bz2'],
46 'tgz': ['.tgz', '.tar.gz'],
47 'tgz': ['.tgz', '.tar.gz'],
47 'zip': ['.zip'],
48 'zip': ['.zip'],
48 }
49 }
49
50
50 def guesskind(dest):
51 def guesskind(dest):
51 for kind, extensions in exts.iteritems():
52 for kind, extensions in exts.iteritems():
52 if util.any(dest.endswith(ext) for ext in extensions):
53 if util.any(dest.endswith(ext) for ext in extensions):
53 return kind
54 return kind
54 return None
55 return None
55
56
56
57
57 class tarit(object):
58 class tarit(object):
58 '''write archive to tar file or stream. can write uncompressed,
59 '''write archive to tar file or stream. can write uncompressed,
59 or compress with gzip or bzip2.'''
60 or compress with gzip or bzip2.'''
60
61
61 class GzipFileWithTime(gzip.GzipFile):
62 class GzipFileWithTime(gzip.GzipFile):
62
63
63 def __init__(self, *args, **kw):
64 def __init__(self, *args, **kw):
64 timestamp = None
65 timestamp = None
65 if 'timestamp' in kw:
66 if 'timestamp' in kw:
66 timestamp = kw.pop('timestamp')
67 timestamp = kw.pop('timestamp')
67 if timestamp is None:
68 if timestamp is None:
68 self.timestamp = time.time()
69 self.timestamp = time.time()
69 else:
70 else:
70 self.timestamp = timestamp
71 self.timestamp = timestamp
71 gzip.GzipFile.__init__(self, *args, **kw)
72 gzip.GzipFile.__init__(self, *args, **kw)
72
73
73 def _write_gzip_header(self):
74 def _write_gzip_header(self):
74 self.fileobj.write('\037\213') # magic header
75 self.fileobj.write('\037\213') # magic header
75 self.fileobj.write('\010') # compression method
76 self.fileobj.write('\010') # compression method
76 # Python 2.6 deprecates self.filename
77 # Python 2.6 deprecates self.filename
77 fname = getattr(self, 'name', None) or self.filename
78 fname = getattr(self, 'name', None) or self.filename
78 if fname and fname.endswith('.gz'):
79 if fname and fname.endswith('.gz'):
79 fname = fname[:-3]
80 fname = fname[:-3]
80 flags = 0
81 flags = 0
81 if fname:
82 if fname:
82 flags = gzip.FNAME
83 flags = gzip.FNAME
83 self.fileobj.write(chr(flags))
84 self.fileobj.write(chr(flags))
84 gzip.write32u(self.fileobj, long(self.timestamp))
85 gzip.write32u(self.fileobj, long(self.timestamp))
85 self.fileobj.write('\002')
86 self.fileobj.write('\002')
86 self.fileobj.write('\377')
87 self.fileobj.write('\377')
87 if fname:
88 if fname:
88 self.fileobj.write(fname + '\000')
89 self.fileobj.write(fname + '\000')
89
90
90 def __init__(self, dest, mtime, kind=''):
91 def __init__(self, dest, mtime, kind=''):
91 self.mtime = mtime
92 self.mtime = mtime
92 self.fileobj = None
93 self.fileobj = None
93
94
94 def taropen(name, mode, fileobj=None):
95 def taropen(name, mode, fileobj=None):
95 if kind == 'gz':
96 if kind == 'gz':
96 mode = mode[0]
97 mode = mode[0]
97 if not fileobj:
98 if not fileobj:
98 fileobj = open(name, mode + 'b')
99 fileobj = open(name, mode + 'b')
99 gzfileobj = self.GzipFileWithTime(name, mode + 'b',
100 gzfileobj = self.GzipFileWithTime(name, mode + 'b',
100 zlib.Z_BEST_COMPRESSION,
101 zlib.Z_BEST_COMPRESSION,
101 fileobj, timestamp=mtime)
102 fileobj, timestamp=mtime)
102 self.fileobj = gzfileobj
103 self.fileobj = gzfileobj
103 return tarfile.TarFile.taropen(name, mode, gzfileobj)
104 return tarfile.TarFile.taropen(name, mode, gzfileobj)
104 else:
105 else:
105 self.fileobj = fileobj
106 self.fileobj = fileobj
106 return tarfile.open(name, mode + kind, fileobj)
107 return tarfile.open(name, mode + kind, fileobj)
107
108
108 if isinstance(dest, str):
109 if isinstance(dest, str):
109 self.z = taropen(dest, mode='w:')
110 self.z = taropen(dest, mode='w:')
110 else:
111 else:
111 # Python 2.5-2.5.1 have a regression that requires a name arg
112 # Python 2.5-2.5.1 have a regression that requires a name arg
112 self.z = taropen(name='', mode='w|', fileobj=dest)
113 self.z = taropen(name='', mode='w|', fileobj=dest)
113
114
114 def addfile(self, name, mode, islink, data):
115 def addfile(self, name, mode, islink, data):
115 i = tarfile.TarInfo(name)
116 i = tarfile.TarInfo(name)
116 i.mtime = self.mtime
117 i.mtime = self.mtime
117 i.size = len(data)
118 i.size = len(data)
118 if islink:
119 if islink:
119 i.type = tarfile.SYMTYPE
120 i.type = tarfile.SYMTYPE
120 i.mode = 0777
121 i.mode = 0777
121 i.linkname = data
122 i.linkname = data
122 data = None
123 data = None
123 i.size = 0
124 i.size = 0
124 else:
125 else:
125 i.mode = mode
126 i.mode = mode
126 data = cStringIO.StringIO(data)
127 data = cStringIO.StringIO(data)
127 self.z.addfile(i, data)
128 self.z.addfile(i, data)
128
129
129 def done(self):
130 def done(self):
130 self.z.close()
131 self.z.close()
131 if self.fileobj:
132 if self.fileobj:
132 self.fileobj.close()
133 self.fileobj.close()
133
134
134 class tellable(object):
135 class tellable(object):
135 '''provide tell method for zipfile.ZipFile when writing to http
136 '''provide tell method for zipfile.ZipFile when writing to http
136 response file object.'''
137 response file object.'''
137
138
138 def __init__(self, fp):
139 def __init__(self, fp):
139 self.fp = fp
140 self.fp = fp
140 self.offset = 0
141 self.offset = 0
141
142
142 def __getattr__(self, key):
143 def __getattr__(self, key):
143 return getattr(self.fp, key)
144 return getattr(self.fp, key)
144
145
145 def write(self, s):
146 def write(self, s):
146 self.fp.write(s)
147 self.fp.write(s)
147 self.offset += len(s)
148 self.offset += len(s)
148
149
149 def tell(self):
150 def tell(self):
150 return self.offset
151 return self.offset
151
152
152 class zipit(object):
153 class zipit(object):
153 '''write archive to zip file or stream. can write uncompressed,
154 '''write archive to zip file or stream. can write uncompressed,
154 or compressed with deflate.'''
155 or compressed with deflate.'''
155
156
156 def __init__(self, dest, mtime, compress=True):
157 def __init__(self, dest, mtime, compress=True):
157 if not isinstance(dest, str):
158 if not isinstance(dest, str):
158 try:
159 try:
159 dest.tell()
160 dest.tell()
160 except (AttributeError, IOError):
161 except (AttributeError, IOError):
161 dest = tellable(dest)
162 dest = tellable(dest)
162 self.z = zipfile.ZipFile(dest, 'w',
163 self.z = zipfile.ZipFile(dest, 'w',
163 compress and zipfile.ZIP_DEFLATED or
164 compress and zipfile.ZIP_DEFLATED or
164 zipfile.ZIP_STORED)
165 zipfile.ZIP_STORED)
165
166
166 # Python's zipfile module emits deprecation warnings if we try
167 # Python's zipfile module emits deprecation warnings if we try
167 # to store files with a date before 1980.
168 # to store files with a date before 1980.
168 epoch = 315532800 # calendar.timegm((1980, 1, 1, 0, 0, 0, 1, 1, 0))
169 epoch = 315532800 # calendar.timegm((1980, 1, 1, 0, 0, 0, 1, 1, 0))
169 if mtime < epoch:
170 if mtime < epoch:
170 mtime = epoch
171 mtime = epoch
171
172
173 self.mtime = mtime
172 self.date_time = time.gmtime(mtime)[:6]
174 self.date_time = time.gmtime(mtime)[:6]
173
175
174 def addfile(self, name, mode, islink, data):
176 def addfile(self, name, mode, islink, data):
175 i = zipfile.ZipInfo(name, self.date_time)
177 i = zipfile.ZipInfo(name, self.date_time)
176 i.compress_type = self.z.compression
178 i.compress_type = self.z.compression
177 # unzip will not honor unix file modes unless file creator is
179 # unzip will not honor unix file modes unless file creator is
178 # set to unix (id 3).
180 # set to unix (id 3).
179 i.create_system = 3
181 i.create_system = 3
180 ftype = _UNX_IFREG
182 ftype = _UNX_IFREG
181 if islink:
183 if islink:
182 mode = 0777
184 mode = 0777
183 ftype = _UNX_IFLNK
185 ftype = _UNX_IFLNK
184 i.external_attr = (mode | ftype) << 16L
186 i.external_attr = (mode | ftype) << 16L
187 # add "extended-timestamp" extra block, because zip archives
188 # without this will be extracted with unexpected timestamp,
189 # if TZ is not configured as GMT
190 i.extra += struct.pack('<hhBl',
191 0x5455, # block type: "extended-timestamp"
192 1 + 4, # size of this block
193 1, # "modification time is present"
194 self.mtime) # time of last modification (UTC)
185 self.z.writestr(i, data)
195 self.z.writestr(i, data)
186
196
187 def done(self):
197 def done(self):
188 self.z.close()
198 self.z.close()
189
199
190 class fileit(object):
200 class fileit(object):
191 '''write archive as files in directory.'''
201 '''write archive as files in directory.'''
192
202
193 def __init__(self, name, mtime):
203 def __init__(self, name, mtime):
194 self.basedir = name
204 self.basedir = name
195 self.opener = scmutil.opener(self.basedir)
205 self.opener = scmutil.opener(self.basedir)
196
206
197 def addfile(self, name, mode, islink, data):
207 def addfile(self, name, mode, islink, data):
198 if islink:
208 if islink:
199 self.opener.symlink(data, name)
209 self.opener.symlink(data, name)
200 return
210 return
201 f = self.opener(name, "w", atomictemp=True)
211 f = self.opener(name, "w", atomictemp=True)
202 f.write(data)
212 f.write(data)
203 f.close()
213 f.close()
204 destfile = os.path.join(self.basedir, name)
214 destfile = os.path.join(self.basedir, name)
205 os.chmod(destfile, mode)
215 os.chmod(destfile, mode)
206
216
207 def done(self):
217 def done(self):
208 pass
218 pass
209
219
210 archivers = {
220 archivers = {
211 'files': fileit,
221 'files': fileit,
212 'tar': tarit,
222 'tar': tarit,
213 'tbz2': lambda name, mtime: tarit(name, mtime, 'bz2'),
223 'tbz2': lambda name, mtime: tarit(name, mtime, 'bz2'),
214 'tgz': lambda name, mtime: tarit(name, mtime, 'gz'),
224 'tgz': lambda name, mtime: tarit(name, mtime, 'gz'),
215 'uzip': lambda name, mtime: zipit(name, mtime, False),
225 'uzip': lambda name, mtime: zipit(name, mtime, False),
216 'zip': zipit,
226 'zip': zipit,
217 }
227 }
218
228
219 def archive(repo, dest, node, kind, decode=True, matchfn=None,
229 def archive(repo, dest, node, kind, decode=True, matchfn=None,
220 prefix=None, mtime=None, subrepos=False):
230 prefix=None, mtime=None, subrepos=False):
221 '''create archive of repo as it was at node.
231 '''create archive of repo as it was at node.
222
232
223 dest can be name of directory, name of archive file, or file
233 dest can be name of directory, name of archive file, or file
224 object to write archive to.
234 object to write archive to.
225
235
226 kind is type of archive to create.
236 kind is type of archive to create.
227
237
228 decode tells whether to put files through decode filters from
238 decode tells whether to put files through decode filters from
229 hgrc.
239 hgrc.
230
240
231 matchfn is function to filter names of files to write to archive.
241 matchfn is function to filter names of files to write to archive.
232
242
233 prefix is name of path to put before every archive member.'''
243 prefix is name of path to put before every archive member.'''
234
244
235 if kind == 'files':
245 if kind == 'files':
236 if prefix:
246 if prefix:
237 raise util.Abort(_('cannot give prefix when archiving to files'))
247 raise util.Abort(_('cannot give prefix when archiving to files'))
238 else:
248 else:
239 prefix = tidyprefix(dest, kind, prefix)
249 prefix = tidyprefix(dest, kind, prefix)
240
250
241 def write(name, mode, islink, getdata):
251 def write(name, mode, islink, getdata):
242 data = getdata()
252 data = getdata()
243 if decode:
253 if decode:
244 data = repo.wwritedata(name, data)
254 data = repo.wwritedata(name, data)
245 archiver.addfile(prefix + name, mode, islink, data)
255 archiver.addfile(prefix + name, mode, islink, data)
246
256
247 if kind not in archivers:
257 if kind not in archivers:
248 raise util.Abort(_("unknown archive type '%s'") % kind)
258 raise util.Abort(_("unknown archive type '%s'") % kind)
249
259
250 ctx = repo[node]
260 ctx = repo[node]
251 archiver = archivers[kind](dest, mtime or ctx.date()[0])
261 archiver = archivers[kind](dest, mtime or ctx.date()[0])
252
262
253 if repo.ui.configbool("ui", "archivemeta", True):
263 if repo.ui.configbool("ui", "archivemeta", True):
254 def metadata():
264 def metadata():
255 base = 'repo: %s\nnode: %s\nbranch: %s\n' % (
265 base = 'repo: %s\nnode: %s\nbranch: %s\n' % (
256 repo[0].hex(), hex(node), encoding.fromlocal(ctx.branch()))
266 repo[0].hex(), hex(node), encoding.fromlocal(ctx.branch()))
257
267
258 tags = ''.join('tag: %s\n' % t for t in ctx.tags()
268 tags = ''.join('tag: %s\n' % t for t in ctx.tags()
259 if repo.tagtype(t) == 'global')
269 if repo.tagtype(t) == 'global')
260 if not tags:
270 if not tags:
261 repo.ui.pushbuffer()
271 repo.ui.pushbuffer()
262 opts = {'template': '{latesttag}\n{latesttagdistance}',
272 opts = {'template': '{latesttag}\n{latesttagdistance}',
263 'style': '', 'patch': None, 'git': None}
273 'style': '', 'patch': None, 'git': None}
264 cmdutil.show_changeset(repo.ui, repo, opts).show(ctx)
274 cmdutil.show_changeset(repo.ui, repo, opts).show(ctx)
265 ltags, dist = repo.ui.popbuffer().split('\n')
275 ltags, dist = repo.ui.popbuffer().split('\n')
266 tags = ''.join('latesttag: %s\n' % t for t in ltags.split(':'))
276 tags = ''.join('latesttag: %s\n' % t for t in ltags.split(':'))
267 tags += 'latesttagdistance: %s\n' % dist
277 tags += 'latesttagdistance: %s\n' % dist
268
278
269 return base + tags
279 return base + tags
270
280
271 name = '.hg_archival.txt'
281 name = '.hg_archival.txt'
272 if not matchfn or matchfn(name):
282 if not matchfn or matchfn(name):
273 write(name, 0644, False, metadata)
283 write(name, 0644, False, metadata)
274
284
275 if matchfn:
285 if matchfn:
276 files = [f for f in ctx.manifest().keys() if matchfn(f)]
286 files = [f for f in ctx.manifest().keys() if matchfn(f)]
277 else:
287 else:
278 files = ctx.manifest().keys()
288 files = ctx.manifest().keys()
279 files.sort()
289 files.sort()
280 total = len(files)
290 total = len(files)
281 repo.ui.progress(_('archiving'), 0, unit=_('files'), total=total)
291 repo.ui.progress(_('archiving'), 0, unit=_('files'), total=total)
282 for i, f in enumerate(files):
292 for i, f in enumerate(files):
283 ff = ctx.flags(f)
293 ff = ctx.flags(f)
284 write(f, 'x' in ff and 0755 or 0644, 'l' in ff, ctx[f].data)
294 write(f, 'x' in ff and 0755 or 0644, 'l' in ff, ctx[f].data)
285 repo.ui.progress(_('archiving'), i + 1, item=f,
295 repo.ui.progress(_('archiving'), i + 1, item=f,
286 unit=_('files'), total=total)
296 unit=_('files'), total=total)
287 repo.ui.progress(_('archiving'), None)
297 repo.ui.progress(_('archiving'), None)
288
298
289 if subrepos:
299 if subrepos:
290 for subpath in ctx.substate:
300 for subpath in ctx.substate:
291 sub = ctx.sub(subpath)
301 sub = ctx.sub(subpath)
292 submatch = matchmod.narrowmatcher(subpath, matchfn)
302 submatch = matchmod.narrowmatcher(subpath, matchfn)
293 sub.archive(repo.ui, archiver, prefix, submatch)
303 sub.archive(repo.ui, archiver, prefix, submatch)
294
304
295 archiver.done()
305 archiver.done()
@@ -1,272 +1,300 b''
1 $ "$TESTDIR/hghave" serve || exit 80
1 $ "$TESTDIR/hghave" serve || exit 80
2
2
3 $ hg init test
3 $ hg init test
4 $ cd test
4 $ cd test
5 $ echo foo>foo
5 $ echo foo>foo
6 $ hg commit -Am 1 -d '1 0'
6 $ hg commit -Am 1 -d '1 0'
7 adding foo
7 adding foo
8 $ echo bar>bar
8 $ echo bar>bar
9 $ hg commit -Am 2 -d '2 0'
9 $ hg commit -Am 2 -d '2 0'
10 adding bar
10 adding bar
11 $ mkdir baz
11 $ mkdir baz
12 $ echo bletch>baz/bletch
12 $ echo bletch>baz/bletch
13 $ hg commit -Am 3 -d '1000000000 0'
13 $ hg commit -Am 3 -d '1000000000 0'
14 adding baz/bletch
14 adding baz/bletch
15 $ echo "[web]" >> .hg/hgrc
15 $ echo "[web]" >> .hg/hgrc
16 $ echo "name = test-archive" >> .hg/hgrc
16 $ echo "name = test-archive" >> .hg/hgrc
17 $ cp .hg/hgrc .hg/hgrc-base
17 $ cp .hg/hgrc .hg/hgrc-base
18 > test_archtype() {
18 > test_archtype() {
19 > echo "allow_archive = $1" >> .hg/hgrc
19 > echo "allow_archive = $1" >> .hg/hgrc
20 > hg serve -p $HGPORT -d --pid-file=hg.pid -E errors.log
20 > hg serve -p $HGPORT -d --pid-file=hg.pid -E errors.log
21 > cat hg.pid >> $DAEMON_PIDS
21 > cat hg.pid >> $DAEMON_PIDS
22 > echo % $1 allowed should give 200
22 > echo % $1 allowed should give 200
23 > "$TESTDIR/get-with-headers.py" localhost:$HGPORT "archive/tip.$2" | head -n 1
23 > "$TESTDIR/get-with-headers.py" localhost:$HGPORT "archive/tip.$2" | head -n 1
24 > echo % $3 and $4 disallowed should both give 403
24 > echo % $3 and $4 disallowed should both give 403
25 > "$TESTDIR/get-with-headers.py" localhost:$HGPORT "archive/tip.$3" | head -n 1
25 > "$TESTDIR/get-with-headers.py" localhost:$HGPORT "archive/tip.$3" | head -n 1
26 > "$TESTDIR/get-with-headers.py" localhost:$HGPORT "archive/tip.$4" | head -n 1
26 > "$TESTDIR/get-with-headers.py" localhost:$HGPORT "archive/tip.$4" | head -n 1
27 > "$TESTDIR/killdaemons.py" $DAEMON_PIDS
27 > "$TESTDIR/killdaemons.py" $DAEMON_PIDS
28 > cat errors.log
28 > cat errors.log
29 > cp .hg/hgrc-base .hg/hgrc
29 > cp .hg/hgrc-base .hg/hgrc
30 > }
30 > }
31
31
32 check http return codes
32 check http return codes
33
33
34 $ test_archtype gz tar.gz tar.bz2 zip
34 $ test_archtype gz tar.gz tar.bz2 zip
35 % gz allowed should give 200
35 % gz allowed should give 200
36 200 Script output follows
36 200 Script output follows
37 % tar.bz2 and zip disallowed should both give 403
37 % tar.bz2 and zip disallowed should both give 403
38 403 Archive type not allowed: bz2
38 403 Archive type not allowed: bz2
39 403 Archive type not allowed: zip
39 403 Archive type not allowed: zip
40 $ test_archtype bz2 tar.bz2 zip tar.gz
40 $ test_archtype bz2 tar.bz2 zip tar.gz
41 % bz2 allowed should give 200
41 % bz2 allowed should give 200
42 200 Script output follows
42 200 Script output follows
43 % zip and tar.gz disallowed should both give 403
43 % zip and tar.gz disallowed should both give 403
44 403 Archive type not allowed: zip
44 403 Archive type not allowed: zip
45 403 Archive type not allowed: gz
45 403 Archive type not allowed: gz
46 $ test_archtype zip zip tar.gz tar.bz2
46 $ test_archtype zip zip tar.gz tar.bz2
47 % zip allowed should give 200
47 % zip allowed should give 200
48 200 Script output follows
48 200 Script output follows
49 % tar.gz and tar.bz2 disallowed should both give 403
49 % tar.gz and tar.bz2 disallowed should both give 403
50 403 Archive type not allowed: gz
50 403 Archive type not allowed: gz
51 403 Archive type not allowed: bz2
51 403 Archive type not allowed: bz2
52
52
53 $ echo "allow_archive = gz bz2 zip" >> .hg/hgrc
53 $ echo "allow_archive = gz bz2 zip" >> .hg/hgrc
54 $ hg serve -p $HGPORT -d --pid-file=hg.pid -E errors.log
54 $ hg serve -p $HGPORT -d --pid-file=hg.pid -E errors.log
55 $ cat hg.pid >> $DAEMON_PIDS
55 $ cat hg.pid >> $DAEMON_PIDS
56
56
57 invalid arch type should give 404
57 invalid arch type should give 404
58
58
59 $ "$TESTDIR/get-with-headers.py" localhost:$HGPORT "archive/tip.invalid" | head -n 1
59 $ "$TESTDIR/get-with-headers.py" localhost:$HGPORT "archive/tip.invalid" | head -n 1
60 404 Unsupported archive type: None
60 404 Unsupported archive type: None
61
61
62 $ TIP=`hg id -v | cut -f1 -d' '`
62 $ TIP=`hg id -v | cut -f1 -d' '`
63 $ QTIP=`hg id -q`
63 $ QTIP=`hg id -q`
64 $ cat > getarchive.py <<EOF
64 $ cat > getarchive.py <<EOF
65 > import os, sys, urllib2
65 > import os, sys, urllib2
66 > try:
66 > try:
67 > # Set stdout to binary mode for win32 platforms
67 > # Set stdout to binary mode for win32 platforms
68 > import msvcrt
68 > import msvcrt
69 > msvcrt.setmode(sys.stdout.fileno(), os.O_BINARY)
69 > msvcrt.setmode(sys.stdout.fileno(), os.O_BINARY)
70 > except ImportError:
70 > except ImportError:
71 > pass
71 > pass
72 > node, archive = sys.argv[1:]
72 > node, archive = sys.argv[1:]
73 > f = urllib2.urlopen('http://127.0.0.1:%s/?cmd=archive;node=%s;type=%s'
73 > f = urllib2.urlopen('http://127.0.0.1:%s/?cmd=archive;node=%s;type=%s'
74 > % (os.environ['HGPORT'], node, archive))
74 > % (os.environ['HGPORT'], node, archive))
75 > sys.stdout.write(f.read())
75 > sys.stdout.write(f.read())
76 > EOF
76 > EOF
77 $ python getarchive.py "$TIP" gz | gunzip | tar tf - 2>/dev/null
77 $ python getarchive.py "$TIP" gz | gunzip | tar tf - 2>/dev/null
78 test-archive-2c0277f05ed4/.hg_archival.txt
78 test-archive-2c0277f05ed4/.hg_archival.txt
79 test-archive-2c0277f05ed4/bar
79 test-archive-2c0277f05ed4/bar
80 test-archive-2c0277f05ed4/baz/bletch
80 test-archive-2c0277f05ed4/baz/bletch
81 test-archive-2c0277f05ed4/foo
81 test-archive-2c0277f05ed4/foo
82 $ python getarchive.py "$TIP" bz2 | bunzip2 | tar tf - 2>/dev/null
82 $ python getarchive.py "$TIP" bz2 | bunzip2 | tar tf - 2>/dev/null
83 test-archive-2c0277f05ed4/.hg_archival.txt
83 test-archive-2c0277f05ed4/.hg_archival.txt
84 test-archive-2c0277f05ed4/bar
84 test-archive-2c0277f05ed4/bar
85 test-archive-2c0277f05ed4/baz/bletch
85 test-archive-2c0277f05ed4/baz/bletch
86 test-archive-2c0277f05ed4/foo
86 test-archive-2c0277f05ed4/foo
87 $ python getarchive.py "$TIP" zip > archive.zip
87 $ python getarchive.py "$TIP" zip > archive.zip
88 $ unzip -t archive.zip
88 $ unzip -t archive.zip
89 Archive: archive.zip
89 Archive: archive.zip
90 testing: test-archive-2c0277f05ed4/.hg_archival.txt OK
90 testing: test-archive-2c0277f05ed4/.hg_archival.txt OK
91 testing: test-archive-2c0277f05ed4/bar OK
91 testing: test-archive-2c0277f05ed4/bar OK
92 testing: test-archive-2c0277f05ed4/baz/bletch OK
92 testing: test-archive-2c0277f05ed4/baz/bletch OK
93 testing: test-archive-2c0277f05ed4/foo OK
93 testing: test-archive-2c0277f05ed4/foo OK
94 No errors detected in compressed data of archive.zip.
94 No errors detected in compressed data of archive.zip.
95
95
96 $ "$TESTDIR/killdaemons.py" $DAEMON_PIDS
96 $ "$TESTDIR/killdaemons.py" $DAEMON_PIDS
97
97
98 $ hg archive -t tar test.tar
98 $ hg archive -t tar test.tar
99 $ tar tf test.tar
99 $ tar tf test.tar
100 test/.hg_archival.txt
100 test/.hg_archival.txt
101 test/bar
101 test/bar
102 test/baz/bletch
102 test/baz/bletch
103 test/foo
103 test/foo
104
104
105 $ hg archive --debug -t tbz2 -X baz test.tar.bz2
105 $ hg archive --debug -t tbz2 -X baz test.tar.bz2
106 archiving: 0/2 files (0.00%)
106 archiving: 0/2 files (0.00%)
107 archiving: bar 1/2 files (50.00%)
107 archiving: bar 1/2 files (50.00%)
108 archiving: foo 2/2 files (100.00%)
108 archiving: foo 2/2 files (100.00%)
109 $ bunzip2 -dc test.tar.bz2 | tar tf - 2>/dev/null
109 $ bunzip2 -dc test.tar.bz2 | tar tf - 2>/dev/null
110 test/.hg_archival.txt
110 test/.hg_archival.txt
111 test/bar
111 test/bar
112 test/foo
112 test/foo
113
113
114 $ hg archive -t tgz -p %b-%h test-%h.tar.gz
114 $ hg archive -t tgz -p %b-%h test-%h.tar.gz
115 $ gzip -dc test-$QTIP.tar.gz | tar tf - 2>/dev/null
115 $ gzip -dc test-$QTIP.tar.gz | tar tf - 2>/dev/null
116 test-2c0277f05ed4/.hg_archival.txt
116 test-2c0277f05ed4/.hg_archival.txt
117 test-2c0277f05ed4/bar
117 test-2c0277f05ed4/bar
118 test-2c0277f05ed4/baz/bletch
118 test-2c0277f05ed4/baz/bletch
119 test-2c0277f05ed4/foo
119 test-2c0277f05ed4/foo
120
120
121 $ hg archive autodetected_test.tar
121 $ hg archive autodetected_test.tar
122 $ tar tf autodetected_test.tar
122 $ tar tf autodetected_test.tar
123 autodetected_test/.hg_archival.txt
123 autodetected_test/.hg_archival.txt
124 autodetected_test/bar
124 autodetected_test/bar
125 autodetected_test/baz/bletch
125 autodetected_test/baz/bletch
126 autodetected_test/foo
126 autodetected_test/foo
127
127
128 The '-t' should override autodetection
128 The '-t' should override autodetection
129
129
130 $ hg archive -t tar autodetect_override_test.zip
130 $ hg archive -t tar autodetect_override_test.zip
131 $ tar tf autodetect_override_test.zip
131 $ tar tf autodetect_override_test.zip
132 autodetect_override_test.zip/.hg_archival.txt
132 autodetect_override_test.zip/.hg_archival.txt
133 autodetect_override_test.zip/bar
133 autodetect_override_test.zip/bar
134 autodetect_override_test.zip/baz/bletch
134 autodetect_override_test.zip/baz/bletch
135 autodetect_override_test.zip/foo
135 autodetect_override_test.zip/foo
136
136
137 $ for ext in tar tar.gz tgz tar.bz2 tbz2 zip; do
137 $ for ext in tar tar.gz tgz tar.bz2 tbz2 zip; do
138 > hg archive auto_test.$ext
138 > hg archive auto_test.$ext
139 > if [ -d auto_test.$ext ]; then
139 > if [ -d auto_test.$ext ]; then
140 > echo "extension $ext was not autodetected."
140 > echo "extension $ext was not autodetected."
141 > fi
141 > fi
142 > done
142 > done
143
143
144 $ cat > md5comp.py <<EOF
144 $ cat > md5comp.py <<EOF
145 > try:
145 > try:
146 > from hashlib import md5
146 > from hashlib import md5
147 > except ImportError:
147 > except ImportError:
148 > from md5 import md5
148 > from md5 import md5
149 > import sys
149 > import sys
150 > f1, f2 = sys.argv[1:3]
150 > f1, f2 = sys.argv[1:3]
151 > h1 = md5(file(f1, 'rb').read()).hexdigest()
151 > h1 = md5(file(f1, 'rb').read()).hexdigest()
152 > h2 = md5(file(f2, 'rb').read()).hexdigest()
152 > h2 = md5(file(f2, 'rb').read()).hexdigest()
153 > print h1 == h2 or "md5 differ: " + repr((h1, h2))
153 > print h1 == h2 or "md5 differ: " + repr((h1, h2))
154 > EOF
154 > EOF
155
155
156 archive name is stored in the archive, so create similar archives and
156 archive name is stored in the archive, so create similar archives and
157 rename them afterwards.
157 rename them afterwards.
158
158
159 $ hg archive -t tgz tip.tar.gz
159 $ hg archive -t tgz tip.tar.gz
160 $ mv tip.tar.gz tip1.tar.gz
160 $ mv tip.tar.gz tip1.tar.gz
161 $ sleep 1
161 $ sleep 1
162 $ hg archive -t tgz tip.tar.gz
162 $ hg archive -t tgz tip.tar.gz
163 $ mv tip.tar.gz tip2.tar.gz
163 $ mv tip.tar.gz tip2.tar.gz
164 $ python md5comp.py tip1.tar.gz tip2.tar.gz
164 $ python md5comp.py tip1.tar.gz tip2.tar.gz
165 True
165 True
166
166
167 $ hg archive -t zip -p /illegal test.zip
167 $ hg archive -t zip -p /illegal test.zip
168 abort: archive prefix contains illegal components
168 abort: archive prefix contains illegal components
169 [255]
169 [255]
170 $ hg archive -t zip -p very/../bad test.zip
170 $ hg archive -t zip -p very/../bad test.zip
171
171
172 $ hg archive --config ui.archivemeta=false -t zip -r 2 test.zip
172 $ hg archive --config ui.archivemeta=false -t zip -r 2 test.zip
173 $ unzip -t test.zip
173 $ unzip -t test.zip
174 Archive: test.zip
174 Archive: test.zip
175 testing: test/bar OK
175 testing: test/bar OK
176 testing: test/baz/bletch OK
176 testing: test/baz/bletch OK
177 testing: test/foo OK
177 testing: test/foo OK
178 No errors detected in compressed data of test.zip.
178 No errors detected in compressed data of test.zip.
179
179
180 $ hg archive -t tar - | tar tf - 2>/dev/null
180 $ hg archive -t tar - | tar tf - 2>/dev/null
181 test-2c0277f05ed4/.hg_archival.txt
181 test-2c0277f05ed4/.hg_archival.txt
182 test-2c0277f05ed4/bar
182 test-2c0277f05ed4/bar
183 test-2c0277f05ed4/baz/bletch
183 test-2c0277f05ed4/baz/bletch
184 test-2c0277f05ed4/foo
184 test-2c0277f05ed4/foo
185
185
186 $ hg archive -r 0 -t tar rev-%r.tar
186 $ hg archive -r 0 -t tar rev-%r.tar
187 $ if [ -f rev-0.tar ]; then
187 $ if [ -f rev-0.tar ]; then
188 $ fi
188 $ fi
189
189
190 test .hg_archival.txt
190 test .hg_archival.txt
191
191
192 $ hg archive ../test-tags
192 $ hg archive ../test-tags
193 $ cat ../test-tags/.hg_archival.txt
193 $ cat ../test-tags/.hg_archival.txt
194 repo: daa7f7c60e0a224faa4ff77ca41b2760562af264
194 repo: daa7f7c60e0a224faa4ff77ca41b2760562af264
195 node: 2c0277f05ed49d1c8328fb9ba92fba7a5ebcb33e
195 node: 2c0277f05ed49d1c8328fb9ba92fba7a5ebcb33e
196 branch: default
196 branch: default
197 latesttag: null
197 latesttag: null
198 latesttagdistance: 3
198 latesttagdistance: 3
199 $ hg tag -r 2 mytag
199 $ hg tag -r 2 mytag
200 $ hg tag -r 2 anothertag
200 $ hg tag -r 2 anothertag
201 $ hg archive -r 2 ../test-lasttag
201 $ hg archive -r 2 ../test-lasttag
202 $ cat ../test-lasttag/.hg_archival.txt
202 $ cat ../test-lasttag/.hg_archival.txt
203 repo: daa7f7c60e0a224faa4ff77ca41b2760562af264
203 repo: daa7f7c60e0a224faa4ff77ca41b2760562af264
204 node: 2c0277f05ed49d1c8328fb9ba92fba7a5ebcb33e
204 node: 2c0277f05ed49d1c8328fb9ba92fba7a5ebcb33e
205 branch: default
205 branch: default
206 tag: anothertag
206 tag: anothertag
207 tag: mytag
207 tag: mytag
208
208
209 $ hg archive -t bogus test.bogus
209 $ hg archive -t bogus test.bogus
210 abort: unknown archive type 'bogus'
210 abort: unknown archive type 'bogus'
211 [255]
211 [255]
212
212
213 enable progress extension:
213 enable progress extension:
214
214
215 $ cp $HGRCPATH $HGRCPATH.no-progress
215 $ cp $HGRCPATH $HGRCPATH.no-progress
216 $ cat >> $HGRCPATH <<EOF
216 $ cat >> $HGRCPATH <<EOF
217 > [extensions]
217 > [extensions]
218 > progress =
218 > progress =
219 > [progress]
219 > [progress]
220 > assume-tty = 1
220 > assume-tty = 1
221 > format = topic bar number
221 > format = topic bar number
222 > delay = 0
222 > delay = 0
223 > refresh = 0
223 > refresh = 0
224 > width = 60
224 > width = 60
225 > EOF
225 > EOF
226
226
227 $ hg archive ../with-progress 2>&1 | "$TESTDIR/filtercr.py"
227 $ hg archive ../with-progress 2>&1 | "$TESTDIR/filtercr.py"
228
228
229 archiving [ ] 0/4
229 archiving [ ] 0/4
230 archiving [ ] 0/4
230 archiving [ ] 0/4
231 archiving [=========> ] 1/4
231 archiving [=========> ] 1/4
232 archiving [=========> ] 1/4
232 archiving [=========> ] 1/4
233 archiving [====================> ] 2/4
233 archiving [====================> ] 2/4
234 archiving [====================> ] 2/4
234 archiving [====================> ] 2/4
235 archiving [===============================> ] 3/4
235 archiving [===============================> ] 3/4
236 archiving [===============================> ] 3/4
236 archiving [===============================> ] 3/4
237 archiving [==========================================>] 4/4
237 archiving [==========================================>] 4/4
238 archiving [==========================================>] 4/4
238 archiving [==========================================>] 4/4
239 \r (esc)
239 \r (esc)
240
240
241 cleanup after progress extension test:
241 cleanup after progress extension test:
242
242
243 $ cp $HGRCPATH.no-progress $HGRCPATH
243 $ cp $HGRCPATH.no-progress $HGRCPATH
244
244
245 server errors
245 server errors
246
246
247 $ cat errors.log
247 $ cat errors.log
248
248
249 empty repo
249 empty repo
250
250
251 $ hg init ../empty
251 $ hg init ../empty
252 $ cd ../empty
252 $ cd ../empty
253 $ hg archive ../test-empty
253 $ hg archive ../test-empty
254 abort: no working directory: please specify a revision
254 abort: no working directory: please specify a revision
255 [255]
255 [255]
256
256
257 old file -- date clamped to 1980
257 old file -- date clamped to 1980
258
258
259 $ touch -t 197501010000 old
259 $ touch -t 197501010000 old
260 $ hg add old
260 $ hg add old
261 $ hg commit -m old
261 $ hg commit -m old
262 $ hg archive ../old.zip
262 $ hg archive ../old.zip
263 $ unzip -l ../old.zip
263 $ unzip -l ../old.zip
264 Archive: ../old.zip
264 Archive: ../old.zip
265 \s*Length.* (re)
265 \s*Length.* (re)
266 *-----* (glob)
266 *-----* (glob)
267 *147*80*00:00*old/.hg_archival.txt (glob)
267 *147*80*00:00*old/.hg_archival.txt (glob)
268 *0*80*00:00*old/old (glob)
268 *0*80*00:00*old/old (glob)
269 *-----* (glob)
269 *-----* (glob)
270 \s*147\s+2 files (re)
270 \s*147\s+2 files (re)
271
271
272 $ cd ..
272 $ cd ..
273
274 issue3600: check whether "hg archive" can create archive files which
275 are extracted with expected timestamp, even though TZ is not
276 configured as GMT.
277
278 $ mkdir issue3600
279 $ cd issue3600
280
281 $ hg init repo
282 $ echo a > repo/a
283 $ hg -R repo add repo/a
284 $ hg -R repo commit -m '#0' -d '456789012 21600'
285 $ cat > show_mtime.py <<EOF
286 > import sys, os
287 > print int(os.stat(sys.argv[1]).st_mtime)
288 > EOF
289
290 $ hg -R repo archive --prefix tar-extracted archive.tar
291 $ (TZ=UTC-3; export TZ; tar xf archive.tar)
292 $ python show_mtime.py tar-extracted/a
293 456789012
294
295 $ hg -R repo archive --prefix zip-extracted archive.zip
296 $ (TZ=UTC-3; export TZ; unzip -q archive.zip)
297 $ python show_mtime.py zip-extracted/a
298 456789012
299
300 $ cd ..
General Comments 0
You need to be logged in to leave comments. Login now