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