##// END OF EJS Templates
archive: report the node as "{p1node}+" when archiving a dirty wdir()...
Matt Harbison -
r25615:dc707fb3 default
parent child Browse files
Show More
@@ -1,326 +1,332
1 1 # archival.py - revision archival for mercurial
2 2 #
3 3 # Copyright 2006 Vadim Gelfer <vadim.gelfer@gmail.com>
4 4 #
5 5 # This software may be used and distributed according to the terms of the
6 6 # GNU General Public License version 2 or any later version.
7 7
8 8 from i18n import _
9 9 import match as matchmod
10 10 import cmdutil
11 11 import scmutil, util, encoding
12 12 import cStringIO, os, tarfile, time, zipfile
13 13 import zlib, gzip
14 14 import struct
15 15 import error
16 16
17 17 # from unzip source code:
18 18 _UNX_IFREG = 0x8000
19 19 _UNX_IFLNK = 0xa000
20 20
21 21 def tidyprefix(dest, kind, prefix):
22 22 '''choose prefix to use for names in archive. make sure prefix is
23 23 safe for consumers.'''
24 24
25 25 if prefix:
26 26 prefix = util.normpath(prefix)
27 27 else:
28 28 if not isinstance(dest, str):
29 29 raise ValueError('dest must be string if no prefix')
30 30 prefix = os.path.basename(dest)
31 31 lower = prefix.lower()
32 32 for sfx in exts.get(kind, []):
33 33 if lower.endswith(sfx):
34 34 prefix = prefix[:-len(sfx)]
35 35 break
36 36 lpfx = os.path.normpath(util.localpath(prefix))
37 37 prefix = util.pconvert(lpfx)
38 38 if not prefix.endswith('/'):
39 39 prefix += '/'
40 40 # Drop the leading '.' path component if present, so Windows can read the
41 41 # zip files (issue4634)
42 42 if prefix.startswith('./'):
43 43 prefix = prefix[2:]
44 44 if prefix.startswith('../') or os.path.isabs(lpfx) or '/../' in prefix:
45 45 raise util.Abort(_('archive prefix contains illegal components'))
46 46 return prefix
47 47
48 48 exts = {
49 49 'tar': ['.tar'],
50 50 'tbz2': ['.tbz2', '.tar.bz2'],
51 51 'tgz': ['.tgz', '.tar.gz'],
52 52 'zip': ['.zip'],
53 53 }
54 54
55 55 def guesskind(dest):
56 56 for kind, extensions in exts.iteritems():
57 57 if any(dest.endswith(ext) for ext in extensions):
58 58 return kind
59 59 return None
60 60
61 61 def _rootctx(repo):
62 62 # repo[0] may be hidden
63 63 for rev in repo:
64 64 return repo[rev]
65 65 return repo['null']
66 66
67 67 def buildmetadata(ctx):
68 68 '''build content of .hg_archival.txt'''
69 69 repo = ctx.repo()
70 hex = ctx.hex()
71 if ctx.rev() is None:
72 hex = ctx.p1().hex()
73 if ctx.dirty():
74 hex += '+'
75
70 76 base = 'repo: %s\nnode: %s\nbranch: %s\n' % (
71 _rootctx(repo).hex(), ctx.hex(), encoding.fromlocal(ctx.branch()))
77 _rootctx(repo).hex(), hex, encoding.fromlocal(ctx.branch()))
72 78
73 79 tags = ''.join('tag: %s\n' % t for t in ctx.tags()
74 80 if repo.tagtype(t) == 'global')
75 81 if not tags:
76 82 repo.ui.pushbuffer()
77 83 opts = {'template': '{latesttag}\n{latesttagdistance}',
78 84 'style': '', 'patch': None, 'git': None}
79 85 cmdutil.show_changeset(repo.ui, repo, opts).show(ctx)
80 86 ltags, dist = repo.ui.popbuffer().split('\n')
81 87 ltags = ltags.split(':')
82 88 changessince = len(repo.revs('only(.,%s)', ltags[0]))
83 89 tags = ''.join('latesttag: %s\n' % t for t in ltags)
84 90 tags += 'latesttagdistance: %s\n' % dist
85 91 tags += 'changessincelatesttag: %s\n' % changessince
86 92
87 93 return base + tags
88 94
89 95 class tarit(object):
90 96 '''write archive to tar file or stream. can write uncompressed,
91 97 or compress with gzip or bzip2.'''
92 98
93 99 class GzipFileWithTime(gzip.GzipFile):
94 100
95 101 def __init__(self, *args, **kw):
96 102 timestamp = None
97 103 if 'timestamp' in kw:
98 104 timestamp = kw.pop('timestamp')
99 105 if timestamp is None:
100 106 self.timestamp = time.time()
101 107 else:
102 108 self.timestamp = timestamp
103 109 gzip.GzipFile.__init__(self, *args, **kw)
104 110
105 111 def _write_gzip_header(self):
106 112 self.fileobj.write('\037\213') # magic header
107 113 self.fileobj.write('\010') # compression method
108 114 # Python 2.6 introduced self.name and deprecated self.filename
109 115 try:
110 116 fname = self.name
111 117 except AttributeError:
112 118 fname = self.filename
113 119 if fname and fname.endswith('.gz'):
114 120 fname = fname[:-3]
115 121 flags = 0
116 122 if fname:
117 123 flags = gzip.FNAME
118 124 self.fileobj.write(chr(flags))
119 125 gzip.write32u(self.fileobj, long(self.timestamp))
120 126 self.fileobj.write('\002')
121 127 self.fileobj.write('\377')
122 128 if fname:
123 129 self.fileobj.write(fname + '\000')
124 130
125 131 def __init__(self, dest, mtime, kind=''):
126 132 self.mtime = mtime
127 133 self.fileobj = None
128 134
129 135 def taropen(name, mode, fileobj=None):
130 136 if kind == 'gz':
131 137 mode = mode[0]
132 138 if not fileobj:
133 139 fileobj = open(name, mode + 'b')
134 140 gzfileobj = self.GzipFileWithTime(name, mode + 'b',
135 141 zlib.Z_BEST_COMPRESSION,
136 142 fileobj, timestamp=mtime)
137 143 self.fileobj = gzfileobj
138 144 return tarfile.TarFile.taropen(name, mode, gzfileobj)
139 145 else:
140 146 return tarfile.open(name, mode + kind, fileobj)
141 147
142 148 if isinstance(dest, str):
143 149 self.z = taropen(dest, mode='w:')
144 150 else:
145 151 # Python 2.5-2.5.1 have a regression that requires a name arg
146 152 self.z = taropen(name='', mode='w|', fileobj=dest)
147 153
148 154 def addfile(self, name, mode, islink, data):
149 155 i = tarfile.TarInfo(name)
150 156 i.mtime = self.mtime
151 157 i.size = len(data)
152 158 if islink:
153 159 i.type = tarfile.SYMTYPE
154 160 i.mode = 0777
155 161 i.linkname = data
156 162 data = None
157 163 i.size = 0
158 164 else:
159 165 i.mode = mode
160 166 data = cStringIO.StringIO(data)
161 167 self.z.addfile(i, data)
162 168
163 169 def done(self):
164 170 self.z.close()
165 171 if self.fileobj:
166 172 self.fileobj.close()
167 173
168 174 class tellable(object):
169 175 '''provide tell method for zipfile.ZipFile when writing to http
170 176 response file object.'''
171 177
172 178 def __init__(self, fp):
173 179 self.fp = fp
174 180 self.offset = 0
175 181
176 182 def __getattr__(self, key):
177 183 return getattr(self.fp, key)
178 184
179 185 def write(self, s):
180 186 self.fp.write(s)
181 187 self.offset += len(s)
182 188
183 189 def tell(self):
184 190 return self.offset
185 191
186 192 class zipit(object):
187 193 '''write archive to zip file or stream. can write uncompressed,
188 194 or compressed with deflate.'''
189 195
190 196 def __init__(self, dest, mtime, compress=True):
191 197 if not isinstance(dest, str):
192 198 try:
193 199 dest.tell()
194 200 except (AttributeError, IOError):
195 201 dest = tellable(dest)
196 202 self.z = zipfile.ZipFile(dest, 'w',
197 203 compress and zipfile.ZIP_DEFLATED or
198 204 zipfile.ZIP_STORED)
199 205
200 206 # Python's zipfile module emits deprecation warnings if we try
201 207 # to store files with a date before 1980.
202 208 epoch = 315532800 # calendar.timegm((1980, 1, 1, 0, 0, 0, 1, 1, 0))
203 209 if mtime < epoch:
204 210 mtime = epoch
205 211
206 212 self.mtime = mtime
207 213 self.date_time = time.gmtime(mtime)[:6]
208 214
209 215 def addfile(self, name, mode, islink, data):
210 216 i = zipfile.ZipInfo(name, self.date_time)
211 217 i.compress_type = self.z.compression
212 218 # unzip will not honor unix file modes unless file creator is
213 219 # set to unix (id 3).
214 220 i.create_system = 3
215 221 ftype = _UNX_IFREG
216 222 if islink:
217 223 mode = 0777
218 224 ftype = _UNX_IFLNK
219 225 i.external_attr = (mode | ftype) << 16L
220 226 # add "extended-timestamp" extra block, because zip archives
221 227 # without this will be extracted with unexpected timestamp,
222 228 # if TZ is not configured as GMT
223 229 i.extra += struct.pack('<hhBl',
224 230 0x5455, # block type: "extended-timestamp"
225 231 1 + 4, # size of this block
226 232 1, # "modification time is present"
227 233 int(self.mtime)) # last modification (UTC)
228 234 self.z.writestr(i, data)
229 235
230 236 def done(self):
231 237 self.z.close()
232 238
233 239 class fileit(object):
234 240 '''write archive as files in directory.'''
235 241
236 242 def __init__(self, name, mtime):
237 243 self.basedir = name
238 244 self.opener = scmutil.opener(self.basedir)
239 245
240 246 def addfile(self, name, mode, islink, data):
241 247 if islink:
242 248 self.opener.symlink(data, name)
243 249 return
244 250 f = self.opener(name, "w", atomictemp=True)
245 251 f.write(data)
246 252 f.close()
247 253 destfile = os.path.join(self.basedir, name)
248 254 os.chmod(destfile, mode)
249 255
250 256 def done(self):
251 257 pass
252 258
253 259 archivers = {
254 260 'files': fileit,
255 261 'tar': tarit,
256 262 'tbz2': lambda name, mtime: tarit(name, mtime, 'bz2'),
257 263 'tgz': lambda name, mtime: tarit(name, mtime, 'gz'),
258 264 'uzip': lambda name, mtime: zipit(name, mtime, False),
259 265 'zip': zipit,
260 266 }
261 267
262 268 def archive(repo, dest, node, kind, decode=True, matchfn=None,
263 269 prefix='', mtime=None, subrepos=False):
264 270 '''create archive of repo as it was at node.
265 271
266 272 dest can be name of directory, name of archive file, or file
267 273 object to write archive to.
268 274
269 275 kind is type of archive to create.
270 276
271 277 decode tells whether to put files through decode filters from
272 278 hgrc.
273 279
274 280 matchfn is function to filter names of files to write to archive.
275 281
276 282 prefix is name of path to put before every archive member.'''
277 283
278 284 if kind == 'files':
279 285 if prefix:
280 286 raise util.Abort(_('cannot give prefix when archiving to files'))
281 287 else:
282 288 prefix = tidyprefix(dest, kind, prefix)
283 289
284 290 def write(name, mode, islink, getdata):
285 291 data = getdata()
286 292 if decode:
287 293 data = repo.wwritedata(name, data)
288 294 archiver.addfile(prefix + name, mode, islink, data)
289 295
290 296 if kind not in archivers:
291 297 raise util.Abort(_("unknown archive type '%s'") % kind)
292 298
293 299 ctx = repo[node]
294 300 archiver = archivers[kind](dest, mtime or ctx.date()[0])
295 301
296 302 if repo.ui.configbool("ui", "archivemeta", True):
297 303 name = '.hg_archival.txt'
298 304 if not matchfn or matchfn(name):
299 305 write(name, 0644, False, lambda: buildmetadata(ctx))
300 306
301 307 if matchfn:
302 308 files = [f for f in ctx.manifest().keys() if matchfn(f)]
303 309 else:
304 310 files = ctx.manifest().keys()
305 311 total = len(files)
306 312 if total:
307 313 files.sort()
308 314 repo.ui.progress(_('archiving'), 0, unit=_('files'), total=total)
309 315 for i, f in enumerate(files):
310 316 ff = ctx.flags(f)
311 317 write(f, 'x' in ff and 0755 or 0644, 'l' in ff, ctx[f].data)
312 318 repo.ui.progress(_('archiving'), i + 1, item=f,
313 319 unit=_('files'), total=total)
314 320 repo.ui.progress(_('archiving'), None)
315 321
316 322 if subrepos:
317 323 for subpath in sorted(ctx.substate):
318 324 sub = ctx.workingsub(subpath)
319 325 submatch = matchmod.narrowmatcher(subpath, matchfn)
320 326 total += sub.archive(archiver, prefix, submatch)
321 327
322 328 if total == 0:
323 329 raise error.Abort(_('no files match the archive pattern'))
324 330
325 331 archiver.done()
326 332 return total
@@ -1,694 +1,712
1 1 Preparing the subrepository 'sub2'
2 2
3 3 $ hg init sub2
4 4 $ echo sub2 > sub2/sub2
5 5 $ hg add -R sub2
6 6 adding sub2/sub2 (glob)
7 7 $ hg commit -R sub2 -m "sub2 import"
8 8
9 9 Preparing the 'sub1' repo which depends on the subrepo 'sub2'
10 10
11 11 $ hg init sub1
12 12 $ echo sub1 > sub1/sub1
13 13 $ echo "sub2 = ../sub2" > sub1/.hgsub
14 14 $ hg clone sub2 sub1/sub2
15 15 updating to branch default
16 16 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
17 17 $ hg add -R sub1
18 18 adding sub1/.hgsub (glob)
19 19 adding sub1/sub1 (glob)
20 20 $ hg commit -R sub1 -m "sub1 import"
21 21
22 22 Preparing the 'main' repo which depends on the subrepo 'sub1'
23 23
24 24 $ hg init main
25 25 $ echo main > main/main
26 26 $ echo "sub1 = ../sub1" > main/.hgsub
27 27 $ hg clone sub1 main/sub1
28 28 updating to branch default
29 29 cloning subrepo sub2 from $TESTTMP/sub2
30 30 3 files updated, 0 files merged, 0 files removed, 0 files unresolved
31 31 $ hg add -R main
32 32 adding main/.hgsub (glob)
33 33 adding main/main (glob)
34 34 $ hg commit -R main -m "main import"
35 35
36 36 Cleaning both repositories, just as a clone -U
37 37
38 38 $ hg up -C -R sub2 null
39 39 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
40 40 $ hg up -C -R sub1 null
41 41 0 files updated, 0 files merged, 3 files removed, 0 files unresolved
42 42 $ hg up -C -R main null
43 43 0 files updated, 0 files merged, 3 files removed, 0 files unresolved
44 44 $ rm -rf main/sub1
45 45 $ rm -rf sub1/sub2
46 46
47 47 Clone main
48 48
49 49 $ hg --config extensions.largefiles= clone main cloned
50 50 updating to branch default
51 51 cloning subrepo sub1 from $TESTTMP/sub1
52 52 cloning subrepo sub1/sub2 from $TESTTMP/sub2 (glob)
53 53 3 files updated, 0 files merged, 0 files removed, 0 files unresolved
54 54
55 55 Largefiles is NOT enabled in the clone if the source repo doesn't require it
56 56 $ cat cloned/.hg/hgrc
57 57 # example repository config (see "hg help config" for more info)
58 58 [paths]
59 59 default = $TESTTMP/main (glob)
60 60
61 61 # path aliases to other clones of this repo in URLs or filesystem paths
62 62 # (see "hg help config.paths" for more info)
63 63 #
64 64 # default-push = ssh://jdoe@example.net/hg/jdoes-fork
65 65 # my-fork = ssh://jdoe@example.net/hg/jdoes-fork
66 66 # my-clone = /home/jdoe/jdoes-clone
67 67
68 68 [ui]
69 69 # name and email (local to this repository, optional), e.g.
70 70 # username = Jane Doe <jdoe@example.com>
71 71
72 72 Checking cloned repo ids
73 73
74 74 $ printf "cloned " ; hg id -R cloned
75 75 cloned 7f491f53a367 tip
76 76 $ printf "cloned/sub1 " ; hg id -R cloned/sub1
77 77 cloned/sub1 fc3b4ce2696f tip
78 78 $ printf "cloned/sub1/sub2 " ; hg id -R cloned/sub1/sub2
79 79 cloned/sub1/sub2 c57a0840e3ba tip
80 80
81 81 debugsub output for main and sub1
82 82
83 83 $ hg debugsub -R cloned
84 84 path sub1
85 85 source ../sub1
86 86 revision fc3b4ce2696f7741438c79207583768f2ce6b0dd
87 87 $ hg debugsub -R cloned/sub1
88 88 path sub2
89 89 source ../sub2
90 90 revision c57a0840e3badd667ef3c3ef65471609acb2ba3c
91 91
92 92 Modifying deeply nested 'sub2'
93 93
94 94 $ echo modified > cloned/sub1/sub2/sub2
95 95 $ hg commit --subrepos -m "deep nested modif should trigger a commit" -R cloned
96 96 committing subrepository sub1
97 97 committing subrepository sub1/sub2 (glob)
98 98
99 99 Checking modified node ids
100 100
101 101 $ printf "cloned " ; hg id -R cloned
102 102 cloned ffe6649062fe tip
103 103 $ printf "cloned/sub1 " ; hg id -R cloned/sub1
104 104 cloned/sub1 2ecb03bf44a9 tip
105 105 $ printf "cloned/sub1/sub2 " ; hg id -R cloned/sub1/sub2
106 106 cloned/sub1/sub2 53dd3430bcaf tip
107 107
108 108 debugsub output for main and sub1
109 109
110 110 $ hg debugsub -R cloned
111 111 path sub1
112 112 source ../sub1
113 113 revision 2ecb03bf44a94e749e8669481dd9069526ce7cb9
114 114 $ hg debugsub -R cloned/sub1
115 115 path sub2
116 116 source ../sub2
117 117 revision 53dd3430bcaf5ab4a7c48262bcad6d441f510487
118 118
119 119 Check that deep archiving works
120 120
121 121 $ cd cloned
122 122 $ echo 'test' > sub1/sub2/test.txt
123 123 $ hg --config extensions.largefiles=! add sub1/sub2/test.txt
124 124 $ mkdir sub1/sub2/folder
125 125 $ echo 'subfolder' > sub1/sub2/folder/test.txt
126 126 $ hg ci -ASm "add test.txt"
127 127 adding sub1/sub2/folder/test.txt
128 128 committing subrepository sub1
129 129 committing subrepository sub1/sub2 (glob)
130 130
131 131 .. but first take a detour through some deep removal testing
132 132
133 133 $ hg remove -S -I 're:.*.txt' .
134 134 removing sub1/sub2/folder/test.txt (glob)
135 135 removing sub1/sub2/test.txt (glob)
136 136 $ hg status -S
137 137 R sub1/sub2/folder/test.txt
138 138 R sub1/sub2/test.txt
139 139 $ hg update -Cq
140 140 $ hg remove -I 're:.*.txt' sub1
141 141 $ hg status -S
142 142 $ hg remove sub1/sub2/folder/test.txt
143 143 $ hg remove sub1/.hgsubstate
144 144 $ mv sub1/.hgsub sub1/x.hgsub
145 145 $ hg status -S
146 146 warning: subrepo spec file 'sub1/.hgsub' not found (glob)
147 147 R sub1/.hgsubstate
148 148 R sub1/sub2/folder/test.txt
149 149 ! sub1/.hgsub
150 150 ? sub1/x.hgsub
151 151 $ mv sub1/x.hgsub sub1/.hgsub
152 152 $ hg update -Cq
153 153 $ touch sub1/foo
154 154 $ hg forget sub1/sub2/folder/test.txt
155 155 $ rm sub1/sub2/test.txt
156 156
157 157 Test relative path printing + subrepos
158 158 $ mkdir -p foo/bar
159 159 $ cd foo
160 160 $ touch bar/abc
161 161 $ hg addremove -S ..
162 162 adding ../sub1/sub2/folder/test.txt (glob)
163 163 removing ../sub1/sub2/test.txt (glob)
164 164 adding ../sub1/foo (glob)
165 165 adding bar/abc (glob)
166 166 $ cd ..
167 167 $ hg status -S
168 168 A foo/bar/abc
169 169 A sub1/foo
170 170 R sub1/sub2/test.txt
171 171
172 172 Archive wdir() with subrepos
173 173 $ hg rm main
174 174 $ hg archive -S -r 'wdir()' ../wdir
175 175 $ diff -r . ../wdir | grep -v '\.hg$'
176 176 Only in ../wdir: .hg_archival.txt
177 177
178 178 $ find ../wdir -type f | sort
179 179 ../wdir/.hg_archival.txt
180 180 ../wdir/.hgsub
181 181 ../wdir/.hgsubstate
182 182 ../wdir/foo/bar/abc
183 183 ../wdir/sub1/.hgsub
184 184 ../wdir/sub1/.hgsubstate
185 185 ../wdir/sub1/foo
186 186 ../wdir/sub1/sub1
187 187 ../wdir/sub1/sub2/folder/test.txt
188 188 ../wdir/sub1/sub2/sub2
189 189
190 $ cat ../wdir/.hg_archival.txt
191 repo: 7f491f53a367861f47ee64a80eb997d1f341b77a
192 node: 9bb10eebee29dc0f1201dcf5977b811a540255fd+
193 branch: default
194 latesttag: null
195 latesttagdistance: 4
196 changessincelatesttag: 3
197
190 198 Attempting to archive 'wdir()' with a missing file is handled gracefully
191 199 $ rm sub1/sub1
192 200 $ rm -r ../wdir
193 201 $ hg archive -v -S -r 'wdir()' ../wdir
194 202 $ find ../wdir -type f | sort
195 203 ../wdir/.hg_archival.txt
196 204 ../wdir/.hgsub
197 205 ../wdir/.hgsubstate
198 206 ../wdir/foo/bar/abc
199 207 ../wdir/sub1/.hgsub
200 208 ../wdir/sub1/.hgsubstate
201 209 ../wdir/sub1/foo
202 210 ../wdir/sub1/sub2/folder/test.txt
203 211 ../wdir/sub1/sub2/sub2
204 212
205 213 Continue relative path printing + subrepos
206 214 $ hg update -Cq
215 $ rm -r ../wdir
216 $ hg archive -S -r 'wdir()' ../wdir
217 $ cat ../wdir/.hg_archival.txt
218 repo: 7f491f53a367861f47ee64a80eb997d1f341b77a
219 node: 9bb10eebee29dc0f1201dcf5977b811a540255fd
220 branch: default
221 latesttag: null
222 latesttagdistance: 4
223 changessincelatesttag: 3
224
207 225 $ touch sub1/sub2/folder/bar
208 226 $ hg addremove sub1/sub2
209 227 adding sub1/sub2/folder/bar (glob)
210 228 $ hg status -S
211 229 A sub1/sub2/folder/bar
212 230 ? foo/bar/abc
213 231 ? sub1/foo
214 232 $ hg update -Cq
215 233 $ hg addremove sub1
216 234 adding sub1/sub2/folder/bar (glob)
217 235 adding sub1/foo (glob)
218 236 $ hg update -Cq
219 237 $ rm sub1/sub2/folder/test.txt
220 238 $ rm sub1/sub2/test.txt
221 239 $ hg ci -ASm "remove test.txt"
222 240 adding sub1/sub2/folder/bar
223 241 removing sub1/sub2/folder/test.txt
224 242 removing sub1/sub2/test.txt
225 243 adding sub1/foo
226 244 adding foo/bar/abc
227 245 committing subrepository sub1
228 246 committing subrepository sub1/sub2 (glob)
229 247
230 248 $ hg forget sub1/sub2/sub2
231 249 $ echo x > sub1/sub2/x.txt
232 250 $ hg add sub1/sub2/x.txt
233 251
234 252 Files sees uncommitted adds and removes in subrepos
235 253 $ hg files -S
236 254 .hgsub
237 255 .hgsubstate
238 256 foo/bar/abc (glob)
239 257 main
240 258 sub1/.hgsub (glob)
241 259 sub1/.hgsubstate (glob)
242 260 sub1/foo (glob)
243 261 sub1/sub1 (glob)
244 262 sub1/sub2/folder/bar (glob)
245 263 sub1/sub2/x.txt (glob)
246 264
247 265 $ hg files -S "set:eol('dos') or eol('unix') or size('<= 0')"
248 266 .hgsub
249 267 .hgsubstate
250 268 foo/bar/abc (glob)
251 269 main
252 270 sub1/.hgsub (glob)
253 271 sub1/.hgsubstate (glob)
254 272 sub1/foo (glob)
255 273 sub1/sub1 (glob)
256 274 sub1/sub2/folder/bar (glob)
257 275 sub1/sub2/x.txt (glob)
258 276
259 277 $ hg files -r '.^' -S "set:eol('dos') or eol('unix')"
260 278 .hgsub
261 279 .hgsubstate
262 280 main
263 281 sub1/.hgsub (glob)
264 282 sub1/.hgsubstate (glob)
265 283 sub1/sub1 (glob)
266 284 sub1/sub2/folder/test.txt (glob)
267 285 sub1/sub2/sub2 (glob)
268 286 sub1/sub2/test.txt (glob)
269 287
270 288 $ hg files sub1
271 289 sub1/.hgsub (glob)
272 290 sub1/.hgsubstate (glob)
273 291 sub1/foo (glob)
274 292 sub1/sub1 (glob)
275 293 sub1/sub2/folder/bar (glob)
276 294 sub1/sub2/x.txt (glob)
277 295
278 296 $ hg files sub1/sub2
279 297 sub1/sub2/folder/bar (glob)
280 298 sub1/sub2/x.txt (glob)
281 299
282 300 $ hg files -S -r '.^' sub1/sub2/folder
283 301 sub1/sub2/folder/test.txt (glob)
284 302
285 303 $ hg files -S -r '.^' sub1/sub2/missing
286 304 sub1/sub2/missing: no such file in rev 78026e779ea6 (glob)
287 305 [1]
288 306
289 307 $ hg files -r '.^' sub1/
290 308 sub1/.hgsub (glob)
291 309 sub1/.hgsubstate (glob)
292 310 sub1/sub1 (glob)
293 311 sub1/sub2/folder/test.txt (glob)
294 312 sub1/sub2/sub2 (glob)
295 313 sub1/sub2/test.txt (glob)
296 314
297 315 $ hg files -r '.^' sub1/sub2
298 316 sub1/sub2/folder/test.txt (glob)
299 317 sub1/sub2/sub2 (glob)
300 318 sub1/sub2/test.txt (glob)
301 319
302 320 $ hg rollback -q
303 321 $ hg up -Cq
304 322
305 323 $ hg --config extensions.largefiles=! archive -S ../archive_all
306 324 $ find ../archive_all | sort
307 325 ../archive_all
308 326 ../archive_all/.hg_archival.txt
309 327 ../archive_all/.hgsub
310 328 ../archive_all/.hgsubstate
311 329 ../archive_all/main
312 330 ../archive_all/sub1
313 331 ../archive_all/sub1/.hgsub
314 332 ../archive_all/sub1/.hgsubstate
315 333 ../archive_all/sub1/sub1
316 334 ../archive_all/sub1/sub2
317 335 ../archive_all/sub1/sub2/folder
318 336 ../archive_all/sub1/sub2/folder/test.txt
319 337 ../archive_all/sub1/sub2/sub2
320 338 ../archive_all/sub1/sub2/test.txt
321 339
322 340 Check that archive -X works in deep subrepos
323 341
324 342 $ hg --config extensions.largefiles=! archive -S -X '**test*' ../archive_exclude
325 343 $ find ../archive_exclude | sort
326 344 ../archive_exclude
327 345 ../archive_exclude/.hg_archival.txt
328 346 ../archive_exclude/.hgsub
329 347 ../archive_exclude/.hgsubstate
330 348 ../archive_exclude/main
331 349 ../archive_exclude/sub1
332 350 ../archive_exclude/sub1/.hgsub
333 351 ../archive_exclude/sub1/.hgsubstate
334 352 ../archive_exclude/sub1/sub1
335 353 ../archive_exclude/sub1/sub2
336 354 ../archive_exclude/sub1/sub2/sub2
337 355
338 356 $ hg --config extensions.largefiles=! archive -S -I '**test*' ../archive_include
339 357 $ find ../archive_include | sort
340 358 ../archive_include
341 359 ../archive_include/sub1
342 360 ../archive_include/sub1/sub2
343 361 ../archive_include/sub1/sub2/folder
344 362 ../archive_include/sub1/sub2/folder/test.txt
345 363 ../archive_include/sub1/sub2/test.txt
346 364
347 365 Check that deep archive works with largefiles (which overrides hgsubrepo impl)
348 366 This also tests the repo.ui regression in 43fb170a23bd, and that lf subrepo
349 367 subrepos are archived properly.
350 368 Note that add --large through a subrepo currently adds the file as a normal file
351 369
352 370 $ echo "large" > sub1/sub2/large.bin
353 371 $ hg --config extensions.largefiles= add --large -R sub1/sub2 sub1/sub2/large.bin
354 372 $ echo "large" > large.bin
355 373 $ hg --config extensions.largefiles= add --large large.bin
356 374 $ hg --config extensions.largefiles= ci -S -m "add large files"
357 375 committing subrepository sub1
358 376 committing subrepository sub1/sub2 (glob)
359 377
360 378 $ hg --config extensions.largefiles= archive -S ../archive_lf
361 379 $ find ../archive_lf | sort
362 380 ../archive_lf
363 381 ../archive_lf/.hg_archival.txt
364 382 ../archive_lf/.hgsub
365 383 ../archive_lf/.hgsubstate
366 384 ../archive_lf/large.bin
367 385 ../archive_lf/main
368 386 ../archive_lf/sub1
369 387 ../archive_lf/sub1/.hgsub
370 388 ../archive_lf/sub1/.hgsubstate
371 389 ../archive_lf/sub1/sub1
372 390 ../archive_lf/sub1/sub2
373 391 ../archive_lf/sub1/sub2/folder
374 392 ../archive_lf/sub1/sub2/folder/test.txt
375 393 ../archive_lf/sub1/sub2/large.bin
376 394 ../archive_lf/sub1/sub2/sub2
377 395 ../archive_lf/sub1/sub2/test.txt
378 396 $ rm -rf ../archive_lf
379 397
380 398 Exclude large files from main and sub-sub repo
381 399
382 400 $ hg --config extensions.largefiles= archive -S -X '**.bin' ../archive_lf
383 401 $ find ../archive_lf | sort
384 402 ../archive_lf
385 403 ../archive_lf/.hg_archival.txt
386 404 ../archive_lf/.hgsub
387 405 ../archive_lf/.hgsubstate
388 406 ../archive_lf/main
389 407 ../archive_lf/sub1
390 408 ../archive_lf/sub1/.hgsub
391 409 ../archive_lf/sub1/.hgsubstate
392 410 ../archive_lf/sub1/sub1
393 411 ../archive_lf/sub1/sub2
394 412 ../archive_lf/sub1/sub2/folder
395 413 ../archive_lf/sub1/sub2/folder/test.txt
396 414 ../archive_lf/sub1/sub2/sub2
397 415 ../archive_lf/sub1/sub2/test.txt
398 416 $ rm -rf ../archive_lf
399 417
400 418 Exclude normal files from main and sub-sub repo
401 419
402 420 $ hg --config extensions.largefiles= archive -S -X '**.txt' -p '.' ../archive_lf.tgz
403 421 $ tar -tzf ../archive_lf.tgz | sort
404 422 .hgsub
405 423 .hgsubstate
406 424 large.bin
407 425 main
408 426 sub1/.hgsub
409 427 sub1/.hgsubstate
410 428 sub1/sub1
411 429 sub1/sub2/large.bin
412 430 sub1/sub2/sub2
413 431
414 432 Include normal files from within a largefiles subrepo
415 433
416 434 $ hg --config extensions.largefiles= archive -S -I '**.txt' ../archive_lf
417 435 $ find ../archive_lf | sort
418 436 ../archive_lf
419 437 ../archive_lf/.hg_archival.txt
420 438 ../archive_lf/sub1
421 439 ../archive_lf/sub1/sub2
422 440 ../archive_lf/sub1/sub2/folder
423 441 ../archive_lf/sub1/sub2/folder/test.txt
424 442 ../archive_lf/sub1/sub2/test.txt
425 443 $ rm -rf ../archive_lf
426 444
427 445 Include large files from within a largefiles subrepo
428 446
429 447 $ hg --config extensions.largefiles= archive -S -I '**.bin' ../archive_lf
430 448 $ find ../archive_lf | sort
431 449 ../archive_lf
432 450 ../archive_lf/large.bin
433 451 ../archive_lf/sub1
434 452 ../archive_lf/sub1/sub2
435 453 ../archive_lf/sub1/sub2/large.bin
436 454 $ rm -rf ../archive_lf
437 455
438 456 Find an exact largefile match in a largefiles subrepo
439 457
440 458 $ hg --config extensions.largefiles= archive -S -I 'sub1/sub2/large.bin' ../archive_lf
441 459 $ find ../archive_lf | sort
442 460 ../archive_lf
443 461 ../archive_lf/sub1
444 462 ../archive_lf/sub1/sub2
445 463 ../archive_lf/sub1/sub2/large.bin
446 464 $ rm -rf ../archive_lf
447 465
448 466 The local repo enables largefiles if a largefiles repo is cloned
449 467 $ hg showconfig extensions
450 468 abort: repository requires features unknown to this Mercurial: largefiles!
451 469 (see http://mercurial.selenic.com/wiki/MissingRequirement for more information)
452 470 [255]
453 471 $ hg --config extensions.largefiles= clone -qU . ../lfclone
454 472 $ cat ../lfclone/.hg/hgrc
455 473 # example repository config (see "hg help config" for more info)
456 474 [paths]
457 475 default = $TESTTMP/cloned (glob)
458 476
459 477 # path aliases to other clones of this repo in URLs or filesystem paths
460 478 # (see "hg help config.paths" for more info)
461 479 #
462 480 # default-push = ssh://jdoe@example.net/hg/jdoes-fork
463 481 # my-fork = ssh://jdoe@example.net/hg/jdoes-fork
464 482 # my-clone = /home/jdoe/jdoes-clone
465 483
466 484 [ui]
467 485 # name and email (local to this repository, optional), e.g.
468 486 # username = Jane Doe <jdoe@example.com>
469 487
470 488 [extensions]
471 489 largefiles=
472 490
473 491 Find an exact match to a standin (should archive nothing)
474 492 $ hg --config extensions.largefiles= archive -S -I 'sub/sub2/.hglf/large.bin' ../archive_lf
475 493 $ find ../archive_lf 2> /dev/null | sort
476 494
477 495 $ cat >> $HGRCPATH <<EOF
478 496 > [extensions]
479 497 > largefiles=
480 498 > [largefiles]
481 499 > patterns=glob:**.dat
482 500 > EOF
483 501
484 502 Test forget through a deep subrepo with the largefiles extension, both a
485 503 largefile and a normal file. Then a largefile that hasn't been committed yet.
486 504 $ touch sub1/sub2/untracked.txt
487 505 $ touch sub1/sub2/large.dat
488 506 $ hg forget sub1/sub2/large.bin sub1/sub2/test.txt sub1/sub2/untracked.txt
489 507 not removing sub1/sub2/untracked.txt: file is already untracked (glob)
490 508 [1]
491 509 $ hg add --large --dry-run -v sub1/sub2/untracked.txt
492 510 adding sub1/sub2/untracked.txt as a largefile (glob)
493 511 $ hg add --large -v sub1/sub2/untracked.txt
494 512 adding sub1/sub2/untracked.txt as a largefile (glob)
495 513 $ hg add --normal -v sub1/sub2/large.dat
496 514 adding sub1/sub2/large.dat (glob)
497 515 $ hg forget -v sub1/sub2/untracked.txt
498 516 removing sub1/sub2/untracked.txt (glob)
499 517 $ hg status -S
500 518 A sub1/sub2/large.dat
501 519 R sub1/sub2/large.bin
502 520 R sub1/sub2/test.txt
503 521 ? foo/bar/abc
504 522 ? sub1/sub2/untracked.txt
505 523 ? sub1/sub2/x.txt
506 524 $ hg add sub1/sub2
507 525
508 526 $ hg archive -S -r 'wdir()' ../wdir2
509 527 $ diff -r . ../wdir2 | grep -v '\.hg$'
510 528 Only in ../wdir2: .hg_archival.txt
511 529 Only in .: .hglf
512 530 Only in .: foo
513 531 Only in ./sub1/sub2: large.bin
514 532 Only in ./sub1/sub2: test.txt
515 533 Only in ./sub1/sub2: untracked.txt
516 534 Only in ./sub1/sub2: x.txt
517 535 $ find ../wdir2 -type f | sort
518 536 ../wdir2/.hg_archival.txt
519 537 ../wdir2/.hgsub
520 538 ../wdir2/.hgsubstate
521 539 ../wdir2/large.bin
522 540 ../wdir2/main
523 541 ../wdir2/sub1/.hgsub
524 542 ../wdir2/sub1/.hgsubstate
525 543 ../wdir2/sub1/sub1
526 544 ../wdir2/sub1/sub2/folder/test.txt
527 545 ../wdir2/sub1/sub2/large.dat
528 546 ../wdir2/sub1/sub2/sub2
529 547 $ hg status -S -mac -n | sort
530 548 .hgsub
531 549 .hgsubstate
532 550 large.bin
533 551 main
534 552 sub1/.hgsub
535 553 sub1/.hgsubstate
536 554 sub1/sub1
537 555 sub1/sub2/folder/test.txt
538 556 sub1/sub2/large.dat
539 557 sub1/sub2/sub2
540 558
541 559 $ hg ci -Sqm 'forget testing'
542 560
543 561 Test 'wdir()' modified file archiving with largefiles
544 562 $ echo 'mod' > main
545 563 $ echo 'mod' > large.bin
546 564 $ echo 'mod' > sub1/sub2/large.dat
547 565 $ hg archive -S -r 'wdir()' ../wdir3
548 566 $ diff -r . ../wdir3 | grep -v '\.hg$'
549 567 Only in ../wdir3: .hg_archival.txt
550 568 Only in .: .hglf
551 569 Only in .: foo
552 570 Only in ./sub1/sub2: large.bin
553 571 Only in ./sub1/sub2: test.txt
554 572 Only in ./sub1/sub2: untracked.txt
555 573 Only in ./sub1/sub2: x.txt
556 574 $ find ../wdir3 -type f | sort
557 575 ../wdir3/.hg_archival.txt
558 576 ../wdir3/.hgsub
559 577 ../wdir3/.hgsubstate
560 578 ../wdir3/large.bin
561 579 ../wdir3/main
562 580 ../wdir3/sub1/.hgsub
563 581 ../wdir3/sub1/.hgsubstate
564 582 ../wdir3/sub1/sub1
565 583 ../wdir3/sub1/sub2/folder/test.txt
566 584 ../wdir3/sub1/sub2/large.dat
567 585 ../wdir3/sub1/sub2/sub2
568 586 $ hg up -Cq
569 587
570 588 Test issue4330: commit a directory where only normal files have changed
571 589 $ touch foo/bar/large.dat
572 590 $ hg add --large foo/bar/large.dat
573 591 $ hg ci -m 'add foo/bar/large.dat'
574 592 $ touch a.txt
575 593 $ touch a.dat
576 594 $ hg add -v foo/bar/abc a.txt a.dat
577 595 adding a.dat as a largefile
578 596 adding a.txt
579 597 adding foo/bar/abc (glob)
580 598 $ hg ci -m 'dir commit with only normal file deltas' foo/bar
581 599 $ hg status
582 600 A a.dat
583 601 A a.txt
584 602
585 603 Test a directory commit with a changed largefile and a changed normal file
586 604 $ echo changed > foo/bar/large.dat
587 605 $ echo changed > foo/bar/abc
588 606 $ hg ci -m 'dir commit with normal and lf file deltas' foo
589 607 $ hg status
590 608 A a.dat
591 609 A a.txt
592 610
593 611 $ hg ci -m "add a.*"
594 612 $ hg mv a.dat b.dat
595 613 $ hg mv foo/bar/abc foo/bar/def
596 614 $ hg status -C
597 615 A b.dat
598 616 a.dat
599 617 A foo/bar/def
600 618 foo/bar/abc
601 619 R a.dat
602 620 R foo/bar/abc
603 621
604 622 $ hg ci -m "move large and normal"
605 623 $ hg status -C --rev '.^' --rev .
606 624 A b.dat
607 625 a.dat
608 626 A foo/bar/def
609 627 foo/bar/abc
610 628 R a.dat
611 629 R foo/bar/abc
612 630
613 631
614 632 $ echo foo > main
615 633 $ hg ci -m "mod parent only"
616 634 $ hg init sub3
617 635 $ echo "sub3 = sub3" >> .hgsub
618 636 $ echo xyz > sub3/a.txt
619 637 $ hg add sub3/a.txt
620 638 $ hg ci -Sm "add sub3"
621 639 committing subrepository sub3
622 640 $ cat .hgsub | grep -v sub3 > .hgsub1
623 641 $ mv .hgsub1 .hgsub
624 642 $ hg ci -m "remove sub3"
625 643
626 644 $ hg log -r "subrepo()" --style compact
627 645 0 7f491f53a367 1970-01-01 00:00 +0000 test
628 646 main import
629 647
630 648 1 ffe6649062fe 1970-01-01 00:00 +0000 test
631 649 deep nested modif should trigger a commit
632 650
633 651 2 9bb10eebee29 1970-01-01 00:00 +0000 test
634 652 add test.txt
635 653
636 654 3 7c64f035294f 1970-01-01 00:00 +0000 test
637 655 add large files
638 656
639 657 4 f734a59e2e35 1970-01-01 00:00 +0000 test
640 658 forget testing
641 659
642 660 11 9685a22af5db 1970-01-01 00:00 +0000 test
643 661 add sub3
644 662
645 663 12[tip] 2e0485b475b9 1970-01-01 00:00 +0000 test
646 664 remove sub3
647 665
648 666 $ hg log -r "subrepo('sub3')" --style compact
649 667 11 9685a22af5db 1970-01-01 00:00 +0000 test
650 668 add sub3
651 669
652 670 12[tip] 2e0485b475b9 1970-01-01 00:00 +0000 test
653 671 remove sub3
654 672
655 673 $ hg log -r "subrepo('bogus')" --style compact
656 674
657 675
658 676 Test .hgsubstate in the R state
659 677
660 678 $ hg rm .hgsub .hgsubstate
661 679 $ hg ci -m 'trash subrepo tracking'
662 680
663 681 $ hg log -r "subrepo('re:sub\d+')" --style compact
664 682 0 7f491f53a367 1970-01-01 00:00 +0000 test
665 683 main import
666 684
667 685 1 ffe6649062fe 1970-01-01 00:00 +0000 test
668 686 deep nested modif should trigger a commit
669 687
670 688 2 9bb10eebee29 1970-01-01 00:00 +0000 test
671 689 add test.txt
672 690
673 691 3 7c64f035294f 1970-01-01 00:00 +0000 test
674 692 add large files
675 693
676 694 4 f734a59e2e35 1970-01-01 00:00 +0000 test
677 695 forget testing
678 696
679 697 11 9685a22af5db 1970-01-01 00:00 +0000 test
680 698 add sub3
681 699
682 700 12 2e0485b475b9 1970-01-01 00:00 +0000 test
683 701 remove sub3
684 702
685 703 13[tip] a68b2c361653 1970-01-01 00:00 +0000 test
686 704 trash subrepo tracking
687 705
688 706
689 707 Restore the trashed subrepo tracking
690 708
691 709 $ hg rollback -q
692 710 $ hg update -Cq .
693 711
694 712 $ cd ..
General Comments 0
You need to be logged in to leave comments. Login now