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