##// END OF EJS Templates
treemanifests: fix local clone...
Martin von Zweigbergk -
r28006:bb45190a default
parent child Browse files
Show More
@@ -1,552 +1,552 b''
1 # store.py - repository store handling for Mercurial
1 # store.py - repository store handling for Mercurial
2 #
2 #
3 # Copyright 2008 Matt Mackall <mpm@selenic.com>
3 # Copyright 2008 Matt Mackall <mpm@selenic.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 __future__ import absolute_import
8 from __future__ import absolute_import
9
9
10 import errno
10 import errno
11 import os
11 import os
12 import stat
12 import stat
13
13
14 from .i18n import _
14 from .i18n import _
15 from . import (
15 from . import (
16 error,
16 error,
17 parsers,
17 parsers,
18 scmutil,
18 scmutil,
19 util,
19 util,
20 )
20 )
21
21
22 _sha = util.sha1
22 _sha = util.sha1
23
23
24 # This avoids a collision between a file named foo and a dir named
24 # This avoids a collision between a file named foo and a dir named
25 # foo.i or foo.d
25 # foo.i or foo.d
26 def _encodedir(path):
26 def _encodedir(path):
27 '''
27 '''
28 >>> _encodedir('data/foo.i')
28 >>> _encodedir('data/foo.i')
29 'data/foo.i'
29 'data/foo.i'
30 >>> _encodedir('data/foo.i/bla.i')
30 >>> _encodedir('data/foo.i/bla.i')
31 'data/foo.i.hg/bla.i'
31 'data/foo.i.hg/bla.i'
32 >>> _encodedir('data/foo.i.hg/bla.i')
32 >>> _encodedir('data/foo.i.hg/bla.i')
33 'data/foo.i.hg.hg/bla.i'
33 'data/foo.i.hg.hg/bla.i'
34 >>> _encodedir('data/foo.i\\ndata/foo.i/bla.i\\ndata/foo.i.hg/bla.i\\n')
34 >>> _encodedir('data/foo.i\\ndata/foo.i/bla.i\\ndata/foo.i.hg/bla.i\\n')
35 'data/foo.i\\ndata/foo.i.hg/bla.i\\ndata/foo.i.hg.hg/bla.i\\n'
35 'data/foo.i\\ndata/foo.i.hg/bla.i\\ndata/foo.i.hg.hg/bla.i\\n'
36 '''
36 '''
37 return (path
37 return (path
38 .replace(".hg/", ".hg.hg/")
38 .replace(".hg/", ".hg.hg/")
39 .replace(".i/", ".i.hg/")
39 .replace(".i/", ".i.hg/")
40 .replace(".d/", ".d.hg/"))
40 .replace(".d/", ".d.hg/"))
41
41
42 encodedir = getattr(parsers, 'encodedir', _encodedir)
42 encodedir = getattr(parsers, 'encodedir', _encodedir)
43
43
44 def decodedir(path):
44 def decodedir(path):
45 '''
45 '''
46 >>> decodedir('data/foo.i')
46 >>> decodedir('data/foo.i')
47 'data/foo.i'
47 'data/foo.i'
48 >>> decodedir('data/foo.i.hg/bla.i')
48 >>> decodedir('data/foo.i.hg/bla.i')
49 'data/foo.i/bla.i'
49 'data/foo.i/bla.i'
50 >>> decodedir('data/foo.i.hg.hg/bla.i')
50 >>> decodedir('data/foo.i.hg.hg/bla.i')
51 'data/foo.i.hg/bla.i'
51 'data/foo.i.hg/bla.i'
52 '''
52 '''
53 if ".hg/" not in path:
53 if ".hg/" not in path:
54 return path
54 return path
55 return (path
55 return (path
56 .replace(".d.hg/", ".d/")
56 .replace(".d.hg/", ".d/")
57 .replace(".i.hg/", ".i/")
57 .replace(".i.hg/", ".i/")
58 .replace(".hg.hg/", ".hg/"))
58 .replace(".hg.hg/", ".hg/"))
59
59
60 def _buildencodefun():
60 def _buildencodefun():
61 '''
61 '''
62 >>> enc, dec = _buildencodefun()
62 >>> enc, dec = _buildencodefun()
63
63
64 >>> enc('nothing/special.txt')
64 >>> enc('nothing/special.txt')
65 'nothing/special.txt'
65 'nothing/special.txt'
66 >>> dec('nothing/special.txt')
66 >>> dec('nothing/special.txt')
67 'nothing/special.txt'
67 'nothing/special.txt'
68
68
69 >>> enc('HELLO')
69 >>> enc('HELLO')
70 '_h_e_l_l_o'
70 '_h_e_l_l_o'
71 >>> dec('_h_e_l_l_o')
71 >>> dec('_h_e_l_l_o')
72 'HELLO'
72 'HELLO'
73
73
74 >>> enc('hello:world?')
74 >>> enc('hello:world?')
75 'hello~3aworld~3f'
75 'hello~3aworld~3f'
76 >>> dec('hello~3aworld~3f')
76 >>> dec('hello~3aworld~3f')
77 'hello:world?'
77 'hello:world?'
78
78
79 >>> enc('the\x07quick\xADshot')
79 >>> enc('the\x07quick\xADshot')
80 'the~07quick~adshot'
80 'the~07quick~adshot'
81 >>> dec('the~07quick~adshot')
81 >>> dec('the~07quick~adshot')
82 'the\\x07quick\\xadshot'
82 'the\\x07quick\\xadshot'
83 '''
83 '''
84 e = '_'
84 e = '_'
85 winreserved = [ord(x) for x in '\\:*?"<>|']
85 winreserved = [ord(x) for x in '\\:*?"<>|']
86 cmap = dict([(chr(x), chr(x)) for x in xrange(127)])
86 cmap = dict([(chr(x), chr(x)) for x in xrange(127)])
87 for x in (range(32) + range(126, 256) + winreserved):
87 for x in (range(32) + range(126, 256) + winreserved):
88 cmap[chr(x)] = "~%02x" % x
88 cmap[chr(x)] = "~%02x" % x
89 for x in range(ord("A"), ord("Z") + 1) + [ord(e)]:
89 for x in range(ord("A"), ord("Z") + 1) + [ord(e)]:
90 cmap[chr(x)] = e + chr(x).lower()
90 cmap[chr(x)] = e + chr(x).lower()
91 dmap = {}
91 dmap = {}
92 for k, v in cmap.iteritems():
92 for k, v in cmap.iteritems():
93 dmap[v] = k
93 dmap[v] = k
94 def decode(s):
94 def decode(s):
95 i = 0
95 i = 0
96 while i < len(s):
96 while i < len(s):
97 for l in xrange(1, 4):
97 for l in xrange(1, 4):
98 try:
98 try:
99 yield dmap[s[i:i + l]]
99 yield dmap[s[i:i + l]]
100 i += l
100 i += l
101 break
101 break
102 except KeyError:
102 except KeyError:
103 pass
103 pass
104 else:
104 else:
105 raise KeyError
105 raise KeyError
106 return (lambda s: ''.join([cmap[c] for c in s]),
106 return (lambda s: ''.join([cmap[c] for c in s]),
107 lambda s: ''.join(list(decode(s))))
107 lambda s: ''.join(list(decode(s))))
108
108
109 _encodefname, _decodefname = _buildencodefun()
109 _encodefname, _decodefname = _buildencodefun()
110
110
111 def encodefilename(s):
111 def encodefilename(s):
112 '''
112 '''
113 >>> encodefilename('foo.i/bar.d/bla.hg/hi:world?/HELLO')
113 >>> encodefilename('foo.i/bar.d/bla.hg/hi:world?/HELLO')
114 'foo.i.hg/bar.d.hg/bla.hg.hg/hi~3aworld~3f/_h_e_l_l_o'
114 'foo.i.hg/bar.d.hg/bla.hg.hg/hi~3aworld~3f/_h_e_l_l_o'
115 '''
115 '''
116 return _encodefname(encodedir(s))
116 return _encodefname(encodedir(s))
117
117
118 def decodefilename(s):
118 def decodefilename(s):
119 '''
119 '''
120 >>> decodefilename('foo.i.hg/bar.d.hg/bla.hg.hg/hi~3aworld~3f/_h_e_l_l_o')
120 >>> decodefilename('foo.i.hg/bar.d.hg/bla.hg.hg/hi~3aworld~3f/_h_e_l_l_o')
121 'foo.i/bar.d/bla.hg/hi:world?/HELLO'
121 'foo.i/bar.d/bla.hg/hi:world?/HELLO'
122 '''
122 '''
123 return decodedir(_decodefname(s))
123 return decodedir(_decodefname(s))
124
124
125 def _buildlowerencodefun():
125 def _buildlowerencodefun():
126 '''
126 '''
127 >>> f = _buildlowerencodefun()
127 >>> f = _buildlowerencodefun()
128 >>> f('nothing/special.txt')
128 >>> f('nothing/special.txt')
129 'nothing/special.txt'
129 'nothing/special.txt'
130 >>> f('HELLO')
130 >>> f('HELLO')
131 'hello'
131 'hello'
132 >>> f('hello:world?')
132 >>> f('hello:world?')
133 'hello~3aworld~3f'
133 'hello~3aworld~3f'
134 >>> f('the\x07quick\xADshot')
134 >>> f('the\x07quick\xADshot')
135 'the~07quick~adshot'
135 'the~07quick~adshot'
136 '''
136 '''
137 winreserved = [ord(x) for x in '\\:*?"<>|']
137 winreserved = [ord(x) for x in '\\:*?"<>|']
138 cmap = dict([(chr(x), chr(x)) for x in xrange(127)])
138 cmap = dict([(chr(x), chr(x)) for x in xrange(127)])
139 for x in (range(32) + range(126, 256) + winreserved):
139 for x in (range(32) + range(126, 256) + winreserved):
140 cmap[chr(x)] = "~%02x" % x
140 cmap[chr(x)] = "~%02x" % x
141 for x in range(ord("A"), ord("Z") + 1):
141 for x in range(ord("A"), ord("Z") + 1):
142 cmap[chr(x)] = chr(x).lower()
142 cmap[chr(x)] = chr(x).lower()
143 return lambda s: "".join([cmap[c] for c in s])
143 return lambda s: "".join([cmap[c] for c in s])
144
144
145 lowerencode = getattr(parsers, 'lowerencode', None) or _buildlowerencodefun()
145 lowerencode = getattr(parsers, 'lowerencode', None) or _buildlowerencodefun()
146
146
147 # Windows reserved names: con, prn, aux, nul, com1..com9, lpt1..lpt9
147 # Windows reserved names: con, prn, aux, nul, com1..com9, lpt1..lpt9
148 _winres3 = ('aux', 'con', 'prn', 'nul') # length 3
148 _winres3 = ('aux', 'con', 'prn', 'nul') # length 3
149 _winres4 = ('com', 'lpt') # length 4 (with trailing 1..9)
149 _winres4 = ('com', 'lpt') # length 4 (with trailing 1..9)
150 def _auxencode(path, dotencode):
150 def _auxencode(path, dotencode):
151 '''
151 '''
152 Encodes filenames containing names reserved by Windows or which end in
152 Encodes filenames containing names reserved by Windows or which end in
153 period or space. Does not touch other single reserved characters c.
153 period or space. Does not touch other single reserved characters c.
154 Specifically, c in '\\:*?"<>|' or ord(c) <= 31 are *not* encoded here.
154 Specifically, c in '\\:*?"<>|' or ord(c) <= 31 are *not* encoded here.
155 Additionally encodes space or period at the beginning, if dotencode is
155 Additionally encodes space or period at the beginning, if dotencode is
156 True. Parameter path is assumed to be all lowercase.
156 True. Parameter path is assumed to be all lowercase.
157 A segment only needs encoding if a reserved name appears as a
157 A segment only needs encoding if a reserved name appears as a
158 basename (e.g. "aux", "aux.foo"). A directory or file named "foo.aux"
158 basename (e.g. "aux", "aux.foo"). A directory or file named "foo.aux"
159 doesn't need encoding.
159 doesn't need encoding.
160
160
161 >>> s = '.foo/aux.txt/txt.aux/con/prn/nul/foo.'
161 >>> s = '.foo/aux.txt/txt.aux/con/prn/nul/foo.'
162 >>> _auxencode(s.split('/'), True)
162 >>> _auxencode(s.split('/'), True)
163 ['~2efoo', 'au~78.txt', 'txt.aux', 'co~6e', 'pr~6e', 'nu~6c', 'foo~2e']
163 ['~2efoo', 'au~78.txt', 'txt.aux', 'co~6e', 'pr~6e', 'nu~6c', 'foo~2e']
164 >>> s = '.com1com2/lpt9.lpt4.lpt1/conprn/com0/lpt0/foo.'
164 >>> s = '.com1com2/lpt9.lpt4.lpt1/conprn/com0/lpt0/foo.'
165 >>> _auxencode(s.split('/'), False)
165 >>> _auxencode(s.split('/'), False)
166 ['.com1com2', 'lp~749.lpt4.lpt1', 'conprn', 'com0', 'lpt0', 'foo~2e']
166 ['.com1com2', 'lp~749.lpt4.lpt1', 'conprn', 'com0', 'lpt0', 'foo~2e']
167 >>> _auxencode(['foo. '], True)
167 >>> _auxencode(['foo. '], True)
168 ['foo.~20']
168 ['foo.~20']
169 >>> _auxencode([' .foo'], True)
169 >>> _auxencode([' .foo'], True)
170 ['~20.foo']
170 ['~20.foo']
171 '''
171 '''
172 for i, n in enumerate(path):
172 for i, n in enumerate(path):
173 if not n:
173 if not n:
174 continue
174 continue
175 if dotencode and n[0] in '. ':
175 if dotencode and n[0] in '. ':
176 n = "~%02x" % ord(n[0]) + n[1:]
176 n = "~%02x" % ord(n[0]) + n[1:]
177 path[i] = n
177 path[i] = n
178 else:
178 else:
179 l = n.find('.')
179 l = n.find('.')
180 if l == -1:
180 if l == -1:
181 l = len(n)
181 l = len(n)
182 if ((l == 3 and n[:3] in _winres3) or
182 if ((l == 3 and n[:3] in _winres3) or
183 (l == 4 and n[3] <= '9' and n[3] >= '1'
183 (l == 4 and n[3] <= '9' and n[3] >= '1'
184 and n[:3] in _winres4)):
184 and n[:3] in _winres4)):
185 # encode third letter ('aux' -> 'au~78')
185 # encode third letter ('aux' -> 'au~78')
186 ec = "~%02x" % ord(n[2])
186 ec = "~%02x" % ord(n[2])
187 n = n[0:2] + ec + n[3:]
187 n = n[0:2] + ec + n[3:]
188 path[i] = n
188 path[i] = n
189 if n[-1] in '. ':
189 if n[-1] in '. ':
190 # encode last period or space ('foo...' -> 'foo..~2e')
190 # encode last period or space ('foo...' -> 'foo..~2e')
191 path[i] = n[:-1] + "~%02x" % ord(n[-1])
191 path[i] = n[:-1] + "~%02x" % ord(n[-1])
192 return path
192 return path
193
193
194 _maxstorepathlen = 120
194 _maxstorepathlen = 120
195 _dirprefixlen = 8
195 _dirprefixlen = 8
196 _maxshortdirslen = 8 * (_dirprefixlen + 1) - 4
196 _maxshortdirslen = 8 * (_dirprefixlen + 1) - 4
197
197
198 def _hashencode(path, dotencode):
198 def _hashencode(path, dotencode):
199 digest = _sha(path).hexdigest()
199 digest = _sha(path).hexdigest()
200 le = lowerencode(path[5:]).split('/') # skips prefix 'data/' or 'meta/'
200 le = lowerencode(path[5:]).split('/') # skips prefix 'data/' or 'meta/'
201 parts = _auxencode(le, dotencode)
201 parts = _auxencode(le, dotencode)
202 basename = parts[-1]
202 basename = parts[-1]
203 _root, ext = os.path.splitext(basename)
203 _root, ext = os.path.splitext(basename)
204 sdirs = []
204 sdirs = []
205 sdirslen = 0
205 sdirslen = 0
206 for p in parts[:-1]:
206 for p in parts[:-1]:
207 d = p[:_dirprefixlen]
207 d = p[:_dirprefixlen]
208 if d[-1] in '. ':
208 if d[-1] in '. ':
209 # Windows can't access dirs ending in period or space
209 # Windows can't access dirs ending in period or space
210 d = d[:-1] + '_'
210 d = d[:-1] + '_'
211 if sdirslen == 0:
211 if sdirslen == 0:
212 t = len(d)
212 t = len(d)
213 else:
213 else:
214 t = sdirslen + 1 + len(d)
214 t = sdirslen + 1 + len(d)
215 if t > _maxshortdirslen:
215 if t > _maxshortdirslen:
216 break
216 break
217 sdirs.append(d)
217 sdirs.append(d)
218 sdirslen = t
218 sdirslen = t
219 dirs = '/'.join(sdirs)
219 dirs = '/'.join(sdirs)
220 if len(dirs) > 0:
220 if len(dirs) > 0:
221 dirs += '/'
221 dirs += '/'
222 res = 'dh/' + dirs + digest + ext
222 res = 'dh/' + dirs + digest + ext
223 spaceleft = _maxstorepathlen - len(res)
223 spaceleft = _maxstorepathlen - len(res)
224 if spaceleft > 0:
224 if spaceleft > 0:
225 filler = basename[:spaceleft]
225 filler = basename[:spaceleft]
226 res = 'dh/' + dirs + filler + digest + ext
226 res = 'dh/' + dirs + filler + digest + ext
227 return res
227 return res
228
228
229 def _hybridencode(path, dotencode):
229 def _hybridencode(path, dotencode):
230 '''encodes path with a length limit
230 '''encodes path with a length limit
231
231
232 Encodes all paths that begin with 'data/', according to the following.
232 Encodes all paths that begin with 'data/', according to the following.
233
233
234 Default encoding (reversible):
234 Default encoding (reversible):
235
235
236 Encodes all uppercase letters 'X' as '_x'. All reserved or illegal
236 Encodes all uppercase letters 'X' as '_x'. All reserved or illegal
237 characters are encoded as '~xx', where xx is the two digit hex code
237 characters are encoded as '~xx', where xx is the two digit hex code
238 of the character (see encodefilename).
238 of the character (see encodefilename).
239 Relevant path components consisting of Windows reserved filenames are
239 Relevant path components consisting of Windows reserved filenames are
240 masked by encoding the third character ('aux' -> 'au~78', see _auxencode).
240 masked by encoding the third character ('aux' -> 'au~78', see _auxencode).
241
241
242 Hashed encoding (not reversible):
242 Hashed encoding (not reversible):
243
243
244 If the default-encoded path is longer than _maxstorepathlen, a
244 If the default-encoded path is longer than _maxstorepathlen, a
245 non-reversible hybrid hashing of the path is done instead.
245 non-reversible hybrid hashing of the path is done instead.
246 This encoding uses up to _dirprefixlen characters of all directory
246 This encoding uses up to _dirprefixlen characters of all directory
247 levels of the lowerencoded path, but not more levels than can fit into
247 levels of the lowerencoded path, but not more levels than can fit into
248 _maxshortdirslen.
248 _maxshortdirslen.
249 Then follows the filler followed by the sha digest of the full path.
249 Then follows the filler followed by the sha digest of the full path.
250 The filler is the beginning of the basename of the lowerencoded path
250 The filler is the beginning of the basename of the lowerencoded path
251 (the basename is everything after the last path separator). The filler
251 (the basename is everything after the last path separator). The filler
252 is as long as possible, filling in characters from the basename until
252 is as long as possible, filling in characters from the basename until
253 the encoded path has _maxstorepathlen characters (or all chars of the
253 the encoded path has _maxstorepathlen characters (or all chars of the
254 basename have been taken).
254 basename have been taken).
255 The extension (e.g. '.i' or '.d') is preserved.
255 The extension (e.g. '.i' or '.d') is preserved.
256
256
257 The string 'data/' at the beginning is replaced with 'dh/', if the hashed
257 The string 'data/' at the beginning is replaced with 'dh/', if the hashed
258 encoding was used.
258 encoding was used.
259 '''
259 '''
260 path = encodedir(path)
260 path = encodedir(path)
261 ef = _encodefname(path).split('/')
261 ef = _encodefname(path).split('/')
262 res = '/'.join(_auxencode(ef, dotencode))
262 res = '/'.join(_auxencode(ef, dotencode))
263 if len(res) > _maxstorepathlen:
263 if len(res) > _maxstorepathlen:
264 res = _hashencode(path, dotencode)
264 res = _hashencode(path, dotencode)
265 return res
265 return res
266
266
267 def _pathencode(path):
267 def _pathencode(path):
268 de = encodedir(path)
268 de = encodedir(path)
269 if len(path) > _maxstorepathlen:
269 if len(path) > _maxstorepathlen:
270 return _hashencode(de, True)
270 return _hashencode(de, True)
271 ef = _encodefname(de).split('/')
271 ef = _encodefname(de).split('/')
272 res = '/'.join(_auxencode(ef, True))
272 res = '/'.join(_auxencode(ef, True))
273 if len(res) > _maxstorepathlen:
273 if len(res) > _maxstorepathlen:
274 return _hashencode(de, True)
274 return _hashencode(de, True)
275 return res
275 return res
276
276
277 _pathencode = getattr(parsers, 'pathencode', _pathencode)
277 _pathencode = getattr(parsers, 'pathencode', _pathencode)
278
278
279 def _plainhybridencode(f):
279 def _plainhybridencode(f):
280 return _hybridencode(f, False)
280 return _hybridencode(f, False)
281
281
282 def _calcmode(vfs):
282 def _calcmode(vfs):
283 try:
283 try:
284 # files in .hg/ will be created using this mode
284 # files in .hg/ will be created using this mode
285 mode = vfs.stat().st_mode
285 mode = vfs.stat().st_mode
286 # avoid some useless chmods
286 # avoid some useless chmods
287 if (0o777 & ~util.umask) == (0o777 & mode):
287 if (0o777 & ~util.umask) == (0o777 & mode):
288 mode = None
288 mode = None
289 except OSError:
289 except OSError:
290 mode = None
290 mode = None
291 return mode
291 return mode
292
292
293 _data = ('data 00manifest.d 00manifest.i 00changelog.d 00changelog.i'
293 _data = ('data meta 00manifest.d 00manifest.i 00changelog.d 00changelog.i'
294 ' phaseroots obsstore')
294 ' phaseroots obsstore')
295
295
296 class basicstore(object):
296 class basicstore(object):
297 '''base class for local repository stores'''
297 '''base class for local repository stores'''
298 def __init__(self, path, vfstype):
298 def __init__(self, path, vfstype):
299 vfs = vfstype(path)
299 vfs = vfstype(path)
300 self.path = vfs.base
300 self.path = vfs.base
301 self.createmode = _calcmode(vfs)
301 self.createmode = _calcmode(vfs)
302 vfs.createmode = self.createmode
302 vfs.createmode = self.createmode
303 self.rawvfs = vfs
303 self.rawvfs = vfs
304 self.vfs = scmutil.filtervfs(vfs, encodedir)
304 self.vfs = scmutil.filtervfs(vfs, encodedir)
305 self.opener = self.vfs
305 self.opener = self.vfs
306
306
307 def join(self, f):
307 def join(self, f):
308 return self.path + '/' + encodedir(f)
308 return self.path + '/' + encodedir(f)
309
309
310 def _walk(self, relpath, recurse):
310 def _walk(self, relpath, recurse):
311 '''yields (unencoded, encoded, size)'''
311 '''yields (unencoded, encoded, size)'''
312 path = self.path
312 path = self.path
313 if relpath:
313 if relpath:
314 path += '/' + relpath
314 path += '/' + relpath
315 striplen = len(self.path) + 1
315 striplen = len(self.path) + 1
316 l = []
316 l = []
317 if self.rawvfs.isdir(path):
317 if self.rawvfs.isdir(path):
318 visit = [path]
318 visit = [path]
319 readdir = self.rawvfs.readdir
319 readdir = self.rawvfs.readdir
320 while visit:
320 while visit:
321 p = visit.pop()
321 p = visit.pop()
322 for f, kind, st in readdir(p, stat=True):
322 for f, kind, st in readdir(p, stat=True):
323 fp = p + '/' + f
323 fp = p + '/' + f
324 if kind == stat.S_IFREG and f[-2:] in ('.d', '.i'):
324 if kind == stat.S_IFREG and f[-2:] in ('.d', '.i'):
325 n = util.pconvert(fp[striplen:])
325 n = util.pconvert(fp[striplen:])
326 l.append((decodedir(n), n, st.st_size))
326 l.append((decodedir(n), n, st.st_size))
327 elif kind == stat.S_IFDIR and recurse:
327 elif kind == stat.S_IFDIR and recurse:
328 visit.append(fp)
328 visit.append(fp)
329 l.sort()
329 l.sort()
330 return l
330 return l
331
331
332 def datafiles(self):
332 def datafiles(self):
333 return self._walk('data', True)
333 return self._walk('data', True)
334
334
335 def topfiles(self):
335 def topfiles(self):
336 # yield manifest before changelog
336 # yield manifest before changelog
337 return reversed(self._walk('', False))
337 return reversed(self._walk('', False))
338
338
339 def walk(self):
339 def walk(self):
340 '''yields (unencoded, encoded, size)'''
340 '''yields (unencoded, encoded, size)'''
341 # yield data files first
341 # yield data files first
342 for x in self.datafiles():
342 for x in self.datafiles():
343 yield x
343 yield x
344 for x in self.topfiles():
344 for x in self.topfiles():
345 yield x
345 yield x
346
346
347 def copylist(self):
347 def copylist(self):
348 return ['requires'] + _data.split()
348 return ['requires'] + _data.split()
349
349
350 def write(self, tr):
350 def write(self, tr):
351 pass
351 pass
352
352
353 def invalidatecaches(self):
353 def invalidatecaches(self):
354 pass
354 pass
355
355
356 def markremoved(self, fn):
356 def markremoved(self, fn):
357 pass
357 pass
358
358
359 def __contains__(self, path):
359 def __contains__(self, path):
360 '''Checks if the store contains path'''
360 '''Checks if the store contains path'''
361 path = "/".join(("data", path))
361 path = "/".join(("data", path))
362 # file?
362 # file?
363 if self.vfs.exists(path + ".i"):
363 if self.vfs.exists(path + ".i"):
364 return True
364 return True
365 # dir?
365 # dir?
366 if not path.endswith("/"):
366 if not path.endswith("/"):
367 path = path + "/"
367 path = path + "/"
368 return self.vfs.exists(path)
368 return self.vfs.exists(path)
369
369
370 class encodedstore(basicstore):
370 class encodedstore(basicstore):
371 def __init__(self, path, vfstype):
371 def __init__(self, path, vfstype):
372 vfs = vfstype(path + '/store')
372 vfs = vfstype(path + '/store')
373 self.path = vfs.base
373 self.path = vfs.base
374 self.createmode = _calcmode(vfs)
374 self.createmode = _calcmode(vfs)
375 vfs.createmode = self.createmode
375 vfs.createmode = self.createmode
376 self.rawvfs = vfs
376 self.rawvfs = vfs
377 self.vfs = scmutil.filtervfs(vfs, encodefilename)
377 self.vfs = scmutil.filtervfs(vfs, encodefilename)
378 self.opener = self.vfs
378 self.opener = self.vfs
379
379
380 def datafiles(self):
380 def datafiles(self):
381 for a, b, size in self._walk('data', True):
381 for a, b, size in self._walk('data', True):
382 try:
382 try:
383 a = decodefilename(a)
383 a = decodefilename(a)
384 except KeyError:
384 except KeyError:
385 a = None
385 a = None
386 yield a, b, size
386 yield a, b, size
387
387
388 def join(self, f):
388 def join(self, f):
389 return self.path + '/' + encodefilename(f)
389 return self.path + '/' + encodefilename(f)
390
390
391 def copylist(self):
391 def copylist(self):
392 return (['requires', '00changelog.i'] +
392 return (['requires', '00changelog.i'] +
393 ['store/' + f for f in _data.split()])
393 ['store/' + f for f in _data.split()])
394
394
395 class fncache(object):
395 class fncache(object):
396 # the filename used to be partially encoded
396 # the filename used to be partially encoded
397 # hence the encodedir/decodedir dance
397 # hence the encodedir/decodedir dance
398 def __init__(self, vfs):
398 def __init__(self, vfs):
399 self.vfs = vfs
399 self.vfs = vfs
400 self.entries = None
400 self.entries = None
401 self._dirty = False
401 self._dirty = False
402
402
403 def _load(self):
403 def _load(self):
404 '''fill the entries from the fncache file'''
404 '''fill the entries from the fncache file'''
405 self._dirty = False
405 self._dirty = False
406 try:
406 try:
407 fp = self.vfs('fncache', mode='rb')
407 fp = self.vfs('fncache', mode='rb')
408 except IOError:
408 except IOError:
409 # skip nonexistent file
409 # skip nonexistent file
410 self.entries = set()
410 self.entries = set()
411 return
411 return
412 self.entries = set(decodedir(fp.read()).splitlines())
412 self.entries = set(decodedir(fp.read()).splitlines())
413 if '' in self.entries:
413 if '' in self.entries:
414 fp.seek(0)
414 fp.seek(0)
415 for n, line in enumerate(fp):
415 for n, line in enumerate(fp):
416 if not line.rstrip('\n'):
416 if not line.rstrip('\n'):
417 t = _('invalid entry in fncache, line %d') % (n + 1)
417 t = _('invalid entry in fncache, line %d') % (n + 1)
418 raise error.Abort(t)
418 raise error.Abort(t)
419 fp.close()
419 fp.close()
420
420
421 def write(self, tr):
421 def write(self, tr):
422 if self._dirty:
422 if self._dirty:
423 tr.addbackup('fncache')
423 tr.addbackup('fncache')
424 fp = self.vfs('fncache', mode='wb', atomictemp=True)
424 fp = self.vfs('fncache', mode='wb', atomictemp=True)
425 if self.entries:
425 if self.entries:
426 fp.write(encodedir('\n'.join(self.entries) + '\n'))
426 fp.write(encodedir('\n'.join(self.entries) + '\n'))
427 fp.close()
427 fp.close()
428 self._dirty = False
428 self._dirty = False
429
429
430 def add(self, fn):
430 def add(self, fn):
431 if self.entries is None:
431 if self.entries is None:
432 self._load()
432 self._load()
433 if fn not in self.entries:
433 if fn not in self.entries:
434 self._dirty = True
434 self._dirty = True
435 self.entries.add(fn)
435 self.entries.add(fn)
436
436
437 def remove(self, fn):
437 def remove(self, fn):
438 if self.entries is None:
438 if self.entries is None:
439 self._load()
439 self._load()
440 try:
440 try:
441 self.entries.remove(fn)
441 self.entries.remove(fn)
442 self._dirty = True
442 self._dirty = True
443 except KeyError:
443 except KeyError:
444 pass
444 pass
445
445
446 def __contains__(self, fn):
446 def __contains__(self, fn):
447 if self.entries is None:
447 if self.entries is None:
448 self._load()
448 self._load()
449 return fn in self.entries
449 return fn in self.entries
450
450
451 def __iter__(self):
451 def __iter__(self):
452 if self.entries is None:
452 if self.entries is None:
453 self._load()
453 self._load()
454 return iter(self.entries)
454 return iter(self.entries)
455
455
456 class _fncachevfs(scmutil.abstractvfs, scmutil.auditvfs):
456 class _fncachevfs(scmutil.abstractvfs, scmutil.auditvfs):
457 def __init__(self, vfs, fnc, encode):
457 def __init__(self, vfs, fnc, encode):
458 scmutil.auditvfs.__init__(self, vfs)
458 scmutil.auditvfs.__init__(self, vfs)
459 self.fncache = fnc
459 self.fncache = fnc
460 self.encode = encode
460 self.encode = encode
461
461
462 def __call__(self, path, mode='r', *args, **kw):
462 def __call__(self, path, mode='r', *args, **kw):
463 if mode not in ('r', 'rb') and path.startswith('data/'):
463 if mode not in ('r', 'rb') and path.startswith('data/'):
464 self.fncache.add(path)
464 self.fncache.add(path)
465 return self.vfs(self.encode(path), mode, *args, **kw)
465 return self.vfs(self.encode(path), mode, *args, **kw)
466
466
467 def join(self, path):
467 def join(self, path):
468 if path:
468 if path:
469 return self.vfs.join(self.encode(path))
469 return self.vfs.join(self.encode(path))
470 else:
470 else:
471 return self.vfs.join(path)
471 return self.vfs.join(path)
472
472
473 class fncachestore(basicstore):
473 class fncachestore(basicstore):
474 def __init__(self, path, vfstype, dotencode):
474 def __init__(self, path, vfstype, dotencode):
475 if dotencode:
475 if dotencode:
476 encode = _pathencode
476 encode = _pathencode
477 else:
477 else:
478 encode = _plainhybridencode
478 encode = _plainhybridencode
479 self.encode = encode
479 self.encode = encode
480 vfs = vfstype(path + '/store')
480 vfs = vfstype(path + '/store')
481 self.path = vfs.base
481 self.path = vfs.base
482 self.pathsep = self.path + '/'
482 self.pathsep = self.path + '/'
483 self.createmode = _calcmode(vfs)
483 self.createmode = _calcmode(vfs)
484 vfs.createmode = self.createmode
484 vfs.createmode = self.createmode
485 self.rawvfs = vfs
485 self.rawvfs = vfs
486 fnc = fncache(vfs)
486 fnc = fncache(vfs)
487 self.fncache = fnc
487 self.fncache = fnc
488 self.vfs = _fncachevfs(vfs, fnc, encode)
488 self.vfs = _fncachevfs(vfs, fnc, encode)
489 self.opener = self.vfs
489 self.opener = self.vfs
490
490
491 def join(self, f):
491 def join(self, f):
492 return self.pathsep + self.encode(f)
492 return self.pathsep + self.encode(f)
493
493
494 def getsize(self, path):
494 def getsize(self, path):
495 return self.rawvfs.stat(path).st_size
495 return self.rawvfs.stat(path).st_size
496
496
497 def datafiles(self):
497 def datafiles(self):
498 for f in sorted(self.fncache):
498 for f in sorted(self.fncache):
499 ef = self.encode(f)
499 ef = self.encode(f)
500 try:
500 try:
501 yield f, ef, self.getsize(ef)
501 yield f, ef, self.getsize(ef)
502 except OSError as err:
502 except OSError as err:
503 if err.errno != errno.ENOENT:
503 if err.errno != errno.ENOENT:
504 raise
504 raise
505
505
506 def copylist(self):
506 def copylist(self):
507 d = ('data dh fncache phaseroots obsstore'
507 d = ('data meta dh fncache phaseroots obsstore'
508 ' 00manifest.d 00manifest.i 00changelog.d 00changelog.i')
508 ' 00manifest.d 00manifest.i 00changelog.d 00changelog.i')
509 return (['requires', '00changelog.i'] +
509 return (['requires', '00changelog.i'] +
510 ['store/' + f for f in d.split()])
510 ['store/' + f for f in d.split()])
511
511
512 def write(self, tr):
512 def write(self, tr):
513 self.fncache.write(tr)
513 self.fncache.write(tr)
514
514
515 def invalidatecaches(self):
515 def invalidatecaches(self):
516 self.fncache.entries = None
516 self.fncache.entries = None
517
517
518 def markremoved(self, fn):
518 def markremoved(self, fn):
519 self.fncache.remove(fn)
519 self.fncache.remove(fn)
520
520
521 def _exists(self, f):
521 def _exists(self, f):
522 ef = self.encode(f)
522 ef = self.encode(f)
523 try:
523 try:
524 self.getsize(ef)
524 self.getsize(ef)
525 return True
525 return True
526 except OSError as err:
526 except OSError as err:
527 if err.errno != errno.ENOENT:
527 if err.errno != errno.ENOENT:
528 raise
528 raise
529 # nonexistent entry
529 # nonexistent entry
530 return False
530 return False
531
531
532 def __contains__(self, path):
532 def __contains__(self, path):
533 '''Checks if the store contains path'''
533 '''Checks if the store contains path'''
534 path = "/".join(("data", path))
534 path = "/".join(("data", path))
535 # check for files (exact match)
535 # check for files (exact match)
536 e = path + '.i'
536 e = path + '.i'
537 if e in self.fncache and self._exists(e):
537 if e in self.fncache and self._exists(e):
538 return True
538 return True
539 # now check for directories (prefix match)
539 # now check for directories (prefix match)
540 if not path.endswith('/'):
540 if not path.endswith('/'):
541 path += '/'
541 path += '/'
542 for e in self.fncache:
542 for e in self.fncache:
543 if e.startswith(path) and self._exists(e):
543 if e.startswith(path) and self._exists(e):
544 return True
544 return True
545 return False
545 return False
546
546
547 def store(requirements, path, vfstype):
547 def store(requirements, path, vfstype):
548 if 'store' in requirements:
548 if 'store' in requirements:
549 if 'fncache' in requirements:
549 if 'fncache' in requirements:
550 return fncachestore(path, vfstype, 'dotencode' in requirements)
550 return fncachestore(path, vfstype, 'dotencode' in requirements)
551 return encodedstore(path, vfstype)
551 return encodedstore(path, vfstype)
552 return basicstore(path, vfstype)
552 return basicstore(path, vfstype)
@@ -1,515 +1,568 b''
1 $ cat << EOF >> $HGRCPATH
1 $ cat << EOF >> $HGRCPATH
2 > [format]
2 > [format]
3 > usegeneraldelta=yes
3 > usegeneraldelta=yes
4 > [ui]
4 > [ui]
5 > ssh=python "$TESTDIR/dummyssh"
5 > ssh=python "$TESTDIR/dummyssh"
6 > EOF
6 > EOF
7
7
8 Set up repo
8 Set up repo
9
9
10 $ hg --config experimental.treemanifest=True init repo
10 $ hg --config experimental.treemanifest=True init repo
11 $ cd repo
11 $ cd repo
12
12
13 Requirements get set on init
13 Requirements get set on init
14
14
15 $ grep treemanifest .hg/requires
15 $ grep treemanifest .hg/requires
16 treemanifest
16 treemanifest
17
17
18 Without directories, looks like any other repo
18 Without directories, looks like any other repo
19
19
20 $ echo 0 > a
20 $ echo 0 > a
21 $ echo 0 > b
21 $ echo 0 > b
22 $ hg ci -Aqm initial
22 $ hg ci -Aqm initial
23 $ hg debugdata -m 0
23 $ hg debugdata -m 0
24 a\x00362fef284ce2ca02aecc8de6d5e8a1c3af0556fe (esc)
24 a\x00362fef284ce2ca02aecc8de6d5e8a1c3af0556fe (esc)
25 b\x00362fef284ce2ca02aecc8de6d5e8a1c3af0556fe (esc)
25 b\x00362fef284ce2ca02aecc8de6d5e8a1c3af0556fe (esc)
26
26
27 Submanifest is stored in separate revlog
27 Submanifest is stored in separate revlog
28
28
29 $ mkdir dir1
29 $ mkdir dir1
30 $ echo 1 > dir1/a
30 $ echo 1 > dir1/a
31 $ echo 1 > dir1/b
31 $ echo 1 > dir1/b
32 $ echo 1 > e
32 $ echo 1 > e
33 $ hg ci -Aqm 'add dir1'
33 $ hg ci -Aqm 'add dir1'
34 $ hg debugdata -m 1
34 $ hg debugdata -m 1
35 a\x00362fef284ce2ca02aecc8de6d5e8a1c3af0556fe (esc)
35 a\x00362fef284ce2ca02aecc8de6d5e8a1c3af0556fe (esc)
36 b\x00362fef284ce2ca02aecc8de6d5e8a1c3af0556fe (esc)
36 b\x00362fef284ce2ca02aecc8de6d5e8a1c3af0556fe (esc)
37 dir1\x008b3ffd73f901e83304c83d33132c8e774ceac44et (esc)
37 dir1\x008b3ffd73f901e83304c83d33132c8e774ceac44et (esc)
38 e\x00b8e02f6433738021a065f94175c7cd23db5f05be (esc)
38 e\x00b8e02f6433738021a065f94175c7cd23db5f05be (esc)
39 $ hg debugdata --dir dir1 0
39 $ hg debugdata --dir dir1 0
40 a\x00b8e02f6433738021a065f94175c7cd23db5f05be (esc)
40 a\x00b8e02f6433738021a065f94175c7cd23db5f05be (esc)
41 b\x00b8e02f6433738021a065f94175c7cd23db5f05be (esc)
41 b\x00b8e02f6433738021a065f94175c7cd23db5f05be (esc)
42
42
43 Can add nested directories
43 Can add nested directories
44
44
45 $ mkdir dir1/dir1
45 $ mkdir dir1/dir1
46 $ echo 2 > dir1/dir1/a
46 $ echo 2 > dir1/dir1/a
47 $ echo 2 > dir1/dir1/b
47 $ echo 2 > dir1/dir1/b
48 $ mkdir dir1/dir2
48 $ mkdir dir1/dir2
49 $ echo 2 > dir1/dir2/a
49 $ echo 2 > dir1/dir2/a
50 $ echo 2 > dir1/dir2/b
50 $ echo 2 > dir1/dir2/b
51 $ hg ci -Aqm 'add dir1/dir1'
51 $ hg ci -Aqm 'add dir1/dir1'
52 $ hg files -r .
52 $ hg files -r .
53 a
53 a
54 b
54 b
55 dir1/a (glob)
55 dir1/a (glob)
56 dir1/b (glob)
56 dir1/b (glob)
57 dir1/dir1/a (glob)
57 dir1/dir1/a (glob)
58 dir1/dir1/b (glob)
58 dir1/dir1/b (glob)
59 dir1/dir2/a (glob)
59 dir1/dir2/a (glob)
60 dir1/dir2/b (glob)
60 dir1/dir2/b (glob)
61 e
61 e
62
62
63 Revision is not created for unchanged directory
63 Revision is not created for unchanged directory
64
64
65 $ mkdir dir2
65 $ mkdir dir2
66 $ echo 3 > dir2/a
66 $ echo 3 > dir2/a
67 $ hg add dir2
67 $ hg add dir2
68 adding dir2/a (glob)
68 adding dir2/a (glob)
69 $ hg debugindex --dir dir1 > before
69 $ hg debugindex --dir dir1 > before
70 $ hg ci -qm 'add dir2'
70 $ hg ci -qm 'add dir2'
71 $ hg debugindex --dir dir1 > after
71 $ hg debugindex --dir dir1 > after
72 $ diff before after
72 $ diff before after
73 $ rm before after
73 $ rm before after
74
74
75 Removing directory does not create an revlog entry
75 Removing directory does not create an revlog entry
76
76
77 $ hg rm dir1/dir1
77 $ hg rm dir1/dir1
78 removing dir1/dir1/a (glob)
78 removing dir1/dir1/a (glob)
79 removing dir1/dir1/b (glob)
79 removing dir1/dir1/b (glob)
80 $ hg debugindex --dir dir1/dir1 > before
80 $ hg debugindex --dir dir1/dir1 > before
81 $ hg ci -qm 'remove dir1/dir1'
81 $ hg ci -qm 'remove dir1/dir1'
82 $ hg debugindex --dir dir1/dir1 > after
82 $ hg debugindex --dir dir1/dir1 > after
83 $ diff before after
83 $ diff before after
84 $ rm before after
84 $ rm before after
85
85
86 Check that hg files (calls treemanifest.walk()) works
86 Check that hg files (calls treemanifest.walk()) works
87 without loading all directory revlogs
87 without loading all directory revlogs
88
88
89 $ hg co 'desc("add dir2")'
89 $ hg co 'desc("add dir2")'
90 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
90 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
91 $ mv .hg/store/meta/dir2 .hg/store/meta/dir2-backup
91 $ mv .hg/store/meta/dir2 .hg/store/meta/dir2-backup
92 $ hg files -r . dir1
92 $ hg files -r . dir1
93 dir1/a (glob)
93 dir1/a (glob)
94 dir1/b (glob)
94 dir1/b (glob)
95 dir1/dir1/a (glob)
95 dir1/dir1/a (glob)
96 dir1/dir1/b (glob)
96 dir1/dir1/b (glob)
97 dir1/dir2/a (glob)
97 dir1/dir2/a (glob)
98 dir1/dir2/b (glob)
98 dir1/dir2/b (glob)
99
99
100 Check that status between revisions works (calls treemanifest.matches())
100 Check that status between revisions works (calls treemanifest.matches())
101 without loading all directory revlogs
101 without loading all directory revlogs
102
102
103 $ hg status --rev 'desc("add dir1")' --rev . dir1
103 $ hg status --rev 'desc("add dir1")' --rev . dir1
104 A dir1/dir1/a
104 A dir1/dir1/a
105 A dir1/dir1/b
105 A dir1/dir1/b
106 A dir1/dir2/a
106 A dir1/dir2/a
107 A dir1/dir2/b
107 A dir1/dir2/b
108 $ mv .hg/store/meta/dir2-backup .hg/store/meta/dir2
108 $ mv .hg/store/meta/dir2-backup .hg/store/meta/dir2
109
109
110 Merge creates 2-parent revision of directory revlog
110 Merge creates 2-parent revision of directory revlog
111
111
112 $ echo 5 > dir1/a
112 $ echo 5 > dir1/a
113 $ hg ci -Aqm 'modify dir1/a'
113 $ hg ci -Aqm 'modify dir1/a'
114 $ hg co '.^'
114 $ hg co '.^'
115 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
115 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
116 $ echo 6 > dir1/b
116 $ echo 6 > dir1/b
117 $ hg ci -Aqm 'modify dir1/b'
117 $ hg ci -Aqm 'modify dir1/b'
118 $ hg merge 'desc("modify dir1/a")'
118 $ hg merge 'desc("modify dir1/a")'
119 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
119 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
120 (branch merge, don't forget to commit)
120 (branch merge, don't forget to commit)
121 $ hg ci -m 'conflict-free merge involving dir1/'
121 $ hg ci -m 'conflict-free merge involving dir1/'
122 $ cat dir1/a
122 $ cat dir1/a
123 5
123 5
124 $ cat dir1/b
124 $ cat dir1/b
125 6
125 6
126 $ hg debugindex --dir dir1
126 $ hg debugindex --dir dir1
127 rev offset length delta linkrev nodeid p1 p2
127 rev offset length delta linkrev nodeid p1 p2
128 0 0 54 -1 1 8b3ffd73f901 000000000000 000000000000
128 0 0 54 -1 1 8b3ffd73f901 000000000000 000000000000
129 1 54 68 0 2 68e9d057c5a8 8b3ffd73f901 000000000000
129 1 54 68 0 2 68e9d057c5a8 8b3ffd73f901 000000000000
130 2 122 12 1 4 4698198d2624 68e9d057c5a8 000000000000
130 2 122 12 1 4 4698198d2624 68e9d057c5a8 000000000000
131 3 134 55 1 5 44844058ccce 68e9d057c5a8 000000000000
131 3 134 55 1 5 44844058ccce 68e9d057c5a8 000000000000
132 4 189 55 1 6 bf3d9b744927 68e9d057c5a8 000000000000
132 4 189 55 1 6 bf3d9b744927 68e9d057c5a8 000000000000
133 5 244 55 4 7 dde7c0af2a03 bf3d9b744927 44844058ccce
133 5 244 55 4 7 dde7c0af2a03 bf3d9b744927 44844058ccce
134
134
135 Merge keeping directory from parent 1 does not create revlog entry. (Note that
135 Merge keeping directory from parent 1 does not create revlog entry. (Note that
136 dir1's manifest does change, but only because dir1/a's filelog changes.)
136 dir1's manifest does change, but only because dir1/a's filelog changes.)
137
137
138 $ hg co 'desc("add dir2")'
138 $ hg co 'desc("add dir2")'
139 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
139 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
140 $ echo 8 > dir2/a
140 $ echo 8 > dir2/a
141 $ hg ci -m 'modify dir2/a'
141 $ hg ci -m 'modify dir2/a'
142 created new head
142 created new head
143
143
144 $ hg debugindex --dir dir2 > before
144 $ hg debugindex --dir dir2 > before
145 $ hg merge 'desc("modify dir1/a")'
145 $ hg merge 'desc("modify dir1/a")'
146 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
146 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
147 (branch merge, don't forget to commit)
147 (branch merge, don't forget to commit)
148 $ hg revert -r 'desc("modify dir2/a")' .
148 $ hg revert -r 'desc("modify dir2/a")' .
149 reverting dir1/a (glob)
149 reverting dir1/a (glob)
150 $ hg ci -m 'merge, keeping parent 1'
150 $ hg ci -m 'merge, keeping parent 1'
151 $ hg debugindex --dir dir2 > after
151 $ hg debugindex --dir dir2 > after
152 $ diff before after
152 $ diff before after
153 $ rm before after
153 $ rm before after
154
154
155 Merge keeping directory from parent 2 does not create revlog entry. (Note that
155 Merge keeping directory from parent 2 does not create revlog entry. (Note that
156 dir2's manifest does change, but only because dir2/a's filelog changes.)
156 dir2's manifest does change, but only because dir2/a's filelog changes.)
157
157
158 $ hg co 'desc("modify dir2/a")'
158 $ hg co 'desc("modify dir2/a")'
159 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
159 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
160 $ hg debugindex --dir dir1 > before
160 $ hg debugindex --dir dir1 > before
161 $ hg merge 'desc("modify dir1/a")'
161 $ hg merge 'desc("modify dir1/a")'
162 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
162 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
163 (branch merge, don't forget to commit)
163 (branch merge, don't forget to commit)
164 $ hg revert -r 'desc("modify dir1/a")' .
164 $ hg revert -r 'desc("modify dir1/a")' .
165 reverting dir2/a (glob)
165 reverting dir2/a (glob)
166 $ hg ci -m 'merge, keeping parent 2'
166 $ hg ci -m 'merge, keeping parent 2'
167 created new head
167 created new head
168 $ hg debugindex --dir dir1 > after
168 $ hg debugindex --dir dir1 > after
169 $ diff before after
169 $ diff before after
170 $ rm before after
170 $ rm before after
171
171
172 Create flat source repo for tests with mixed flat/tree manifests
172 Create flat source repo for tests with mixed flat/tree manifests
173
173
174 $ cd ..
174 $ cd ..
175 $ hg init repo-flat
175 $ hg init repo-flat
176 $ cd repo-flat
176 $ cd repo-flat
177
177
178 Create a few commits with flat manifest
178 Create a few commits with flat manifest
179
179
180 $ echo 0 > a
180 $ echo 0 > a
181 $ echo 0 > b
181 $ echo 0 > b
182 $ echo 0 > e
182 $ echo 0 > e
183 $ for d in dir1 dir1/dir1 dir1/dir2 dir2
183 $ for d in dir1 dir1/dir1 dir1/dir2 dir2
184 > do
184 > do
185 > mkdir $d
185 > mkdir $d
186 > echo 0 > $d/a
186 > echo 0 > $d/a
187 > echo 0 > $d/b
187 > echo 0 > $d/b
188 > done
188 > done
189 $ hg ci -Aqm initial
189 $ hg ci -Aqm initial
190
190
191 $ echo 1 > a
191 $ echo 1 > a
192 $ echo 1 > dir1/a
192 $ echo 1 > dir1/a
193 $ echo 1 > dir1/dir1/a
193 $ echo 1 > dir1/dir1/a
194 $ hg ci -Aqm 'modify on branch 1'
194 $ hg ci -Aqm 'modify on branch 1'
195
195
196 $ hg co 0
196 $ hg co 0
197 3 files updated, 0 files merged, 0 files removed, 0 files unresolved
197 3 files updated, 0 files merged, 0 files removed, 0 files unresolved
198 $ echo 2 > b
198 $ echo 2 > b
199 $ echo 2 > dir1/b
199 $ echo 2 > dir1/b
200 $ echo 2 > dir1/dir1/b
200 $ echo 2 > dir1/dir1/b
201 $ hg ci -Aqm 'modify on branch 2'
201 $ hg ci -Aqm 'modify on branch 2'
202
202
203 $ hg merge 1
203 $ hg merge 1
204 3 files updated, 0 files merged, 0 files removed, 0 files unresolved
204 3 files updated, 0 files merged, 0 files removed, 0 files unresolved
205 (branch merge, don't forget to commit)
205 (branch merge, don't forget to commit)
206 $ hg ci -m 'merge of flat manifests to new flat manifest'
206 $ hg ci -m 'merge of flat manifests to new flat manifest'
207
207
208 $ hg serve -p $HGPORT -d --pid-file=hg.pid --errorlog=errors.log
208 $ hg serve -p $HGPORT -d --pid-file=hg.pid --errorlog=errors.log
209 $ cat hg.pid >> $DAEMON_PIDS
209 $ cat hg.pid >> $DAEMON_PIDS
210
210
211 Create clone with tree manifests enabled
211 Create clone with tree manifests enabled
212
212
213 $ cd ..
213 $ cd ..
214 $ hg clone --config experimental.treemanifest=1 \
214 $ hg clone --config experimental.treemanifest=1 \
215 > http://localhost:$HGPORT repo-mixed -r 1
215 > http://localhost:$HGPORT repo-mixed -r 1
216 adding changesets
216 adding changesets
217 adding manifests
217 adding manifests
218 adding file changes
218 adding file changes
219 added 2 changesets with 14 changes to 11 files
219 added 2 changesets with 14 changes to 11 files
220 updating to branch default
220 updating to branch default
221 11 files updated, 0 files merged, 0 files removed, 0 files unresolved
221 11 files updated, 0 files merged, 0 files removed, 0 files unresolved
222 $ cd repo-mixed
222 $ cd repo-mixed
223 $ test -d .hg/store/meta
223 $ test -d .hg/store/meta
224 [1]
224 [1]
225 $ grep treemanifest .hg/requires
225 $ grep treemanifest .hg/requires
226 treemanifest
226 treemanifest
227
227
228 Should be possible to push updates from flat to tree manifest repo
228 Should be possible to push updates from flat to tree manifest repo
229
229
230 $ hg -R ../repo-flat push ssh://user@dummy/repo-mixed
230 $ hg -R ../repo-flat push ssh://user@dummy/repo-mixed
231 pushing to ssh://user@dummy/repo-mixed
231 pushing to ssh://user@dummy/repo-mixed
232 searching for changes
232 searching for changes
233 remote: adding changesets
233 remote: adding changesets
234 remote: adding manifests
234 remote: adding manifests
235 remote: adding file changes
235 remote: adding file changes
236 remote: added 2 changesets with 3 changes to 3 files
236 remote: added 2 changesets with 3 changes to 3 files
237
237
238 Commit should store revlog per directory
238 Commit should store revlog per directory
239
239
240 $ hg co 1
240 $ hg co 1
241 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
241 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
242 $ echo 3 > a
242 $ echo 3 > a
243 $ echo 3 > dir1/a
243 $ echo 3 > dir1/a
244 $ echo 3 > dir1/dir1/a
244 $ echo 3 > dir1/dir1/a
245 $ hg ci -m 'first tree'
245 $ hg ci -m 'first tree'
246 created new head
246 created new head
247 $ find .hg/store/meta | sort
247 $ find .hg/store/meta | sort
248 .hg/store/meta
248 .hg/store/meta
249 .hg/store/meta/dir1
249 .hg/store/meta/dir1
250 .hg/store/meta/dir1/00manifest.i
250 .hg/store/meta/dir1/00manifest.i
251 .hg/store/meta/dir1/dir1
251 .hg/store/meta/dir1/dir1
252 .hg/store/meta/dir1/dir1/00manifest.i
252 .hg/store/meta/dir1/dir1/00manifest.i
253 .hg/store/meta/dir1/dir2
253 .hg/store/meta/dir1/dir2
254 .hg/store/meta/dir1/dir2/00manifest.i
254 .hg/store/meta/dir1/dir2/00manifest.i
255 .hg/store/meta/dir2
255 .hg/store/meta/dir2
256 .hg/store/meta/dir2/00manifest.i
256 .hg/store/meta/dir2/00manifest.i
257
257
258 Merge of two trees
258 Merge of two trees
259
259
260 $ hg co 2
260 $ hg co 2
261 6 files updated, 0 files merged, 0 files removed, 0 files unresolved
261 6 files updated, 0 files merged, 0 files removed, 0 files unresolved
262 $ hg merge 1
262 $ hg merge 1
263 3 files updated, 0 files merged, 0 files removed, 0 files unresolved
263 3 files updated, 0 files merged, 0 files removed, 0 files unresolved
264 (branch merge, don't forget to commit)
264 (branch merge, don't forget to commit)
265 $ hg ci -m 'merge of flat manifests to new tree manifest'
265 $ hg ci -m 'merge of flat manifests to new tree manifest'
266 created new head
266 created new head
267 $ hg diff -r 3
267 $ hg diff -r 3
268
268
269 Parent of tree root manifest should be flat manifest, and two for merge
269 Parent of tree root manifest should be flat manifest, and two for merge
270
270
271 $ hg debugindex -m
271 $ hg debugindex -m
272 rev offset length delta linkrev nodeid p1 p2
272 rev offset length delta linkrev nodeid p1 p2
273 0 0 80 -1 0 40536115ed9e 000000000000 000000000000
273 0 0 80 -1 0 40536115ed9e 000000000000 000000000000
274 1 80 83 0 1 f3376063c255 40536115ed9e 000000000000
274 1 80 83 0 1 f3376063c255 40536115ed9e 000000000000
275 2 163 89 0 2 5d9b9da231a2 40536115ed9e 000000000000
275 2 163 89 0 2 5d9b9da231a2 40536115ed9e 000000000000
276 3 252 83 2 3 d17d663cbd8a 5d9b9da231a2 f3376063c255
276 3 252 83 2 3 d17d663cbd8a 5d9b9da231a2 f3376063c255
277 4 335 124 1 4 51e32a8c60ee f3376063c255 000000000000
277 4 335 124 1 4 51e32a8c60ee f3376063c255 000000000000
278 5 459 126 2 5 cc5baa78b230 5d9b9da231a2 f3376063c255
278 5 459 126 2 5 cc5baa78b230 5d9b9da231a2 f3376063c255
279
279
280
280
281 Status across flat/tree boundary should work
281 Status across flat/tree boundary should work
282
282
283 $ hg status --rev '.^' --rev .
283 $ hg status --rev '.^' --rev .
284 M a
284 M a
285 M dir1/a
285 M dir1/a
286 M dir1/dir1/a
286 M dir1/dir1/a
287
287
288
288
289 Turning off treemanifest config has no effect
289 Turning off treemanifest config has no effect
290
290
291 $ hg debugindex --dir dir1
291 $ hg debugindex --dir dir1
292 rev offset length delta linkrev nodeid p1 p2
292 rev offset length delta linkrev nodeid p1 p2
293 0 0 127 -1 4 064927a0648a 000000000000 000000000000
293 0 0 127 -1 4 064927a0648a 000000000000 000000000000
294 1 127 111 0 5 25ecb8cb8618 000000000000 000000000000
294 1 127 111 0 5 25ecb8cb8618 000000000000 000000000000
295 $ echo 2 > dir1/a
295 $ echo 2 > dir1/a
296 $ hg --config experimental.treemanifest=False ci -qm 'modify dir1/a'
296 $ hg --config experimental.treemanifest=False ci -qm 'modify dir1/a'
297 $ hg debugindex --dir dir1
297 $ hg debugindex --dir dir1
298 rev offset length delta linkrev nodeid p1 p2
298 rev offset length delta linkrev nodeid p1 p2
299 0 0 127 -1 4 064927a0648a 000000000000 000000000000
299 0 0 127 -1 4 064927a0648a 000000000000 000000000000
300 1 127 111 0 5 25ecb8cb8618 000000000000 000000000000
300 1 127 111 0 5 25ecb8cb8618 000000000000 000000000000
301 2 238 55 1 6 5b16163a30c6 25ecb8cb8618 000000000000
301 2 238 55 1 6 5b16163a30c6 25ecb8cb8618 000000000000
302
302
303 Stripping and recovering changes should work
303 Stripping and recovering changes should work
304
304
305 $ hg st --change tip
305 $ hg st --change tip
306 M dir1/a
306 M dir1/a
307 $ hg --config extensions.strip= strip tip
307 $ hg --config extensions.strip= strip tip
308 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
308 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
309 saved backup bundle to $TESTTMP/repo-mixed/.hg/strip-backup/51cfd7b1e13b-78a2f3ed-backup.hg (glob)
309 saved backup bundle to $TESTTMP/repo-mixed/.hg/strip-backup/51cfd7b1e13b-78a2f3ed-backup.hg (glob)
310 $ hg unbundle -q .hg/strip-backup/*
310 $ hg unbundle -q .hg/strip-backup/*
311 $ hg st --change tip
311 $ hg st --change tip
312 M dir1/a
312 M dir1/a
313
313
314 Shelving and unshelving should work
314 Shelving and unshelving should work
315
315
316 $ echo foo >> dir1/a
316 $ echo foo >> dir1/a
317 $ hg --config extensions.shelve= shelve
317 $ hg --config extensions.shelve= shelve
318 shelved as default
318 shelved as default
319 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
319 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
320 $ hg --config extensions.shelve= unshelve
320 $ hg --config extensions.shelve= unshelve
321 unshelving change 'default'
321 unshelving change 'default'
322 $ hg diff --nodates
322 $ hg diff --nodates
323 diff -r 708a273da119 dir1/a
323 diff -r 708a273da119 dir1/a
324 --- a/dir1/a
324 --- a/dir1/a
325 +++ b/dir1/a
325 +++ b/dir1/a
326 @@ -1,1 +1,2 @@
326 @@ -1,1 +1,2 @@
327 1
327 1
328 +foo
328 +foo
329
329
330 Pushing from treemanifest repo to an empty repo makes that a treemanifest repo
330 Pushing from treemanifest repo to an empty repo makes that a treemanifest repo
331
331
332 $ cd ..
332 $ cd ..
333 $ hg init empty-repo
333 $ hg init empty-repo
334 $ cat << EOF >> empty-repo/.hg/hgrc
334 $ cat << EOF >> empty-repo/.hg/hgrc
335 > [experimental]
335 > [experimental]
336 > changegroup3=yes
336 > changegroup3=yes
337 > EOF
337 > EOF
338 $ grep treemanifest empty-repo/.hg/requires
338 $ grep treemanifest empty-repo/.hg/requires
339 [1]
339 [1]
340 $ hg push -R repo -r 0 empty-repo
340 $ hg push -R repo -r 0 empty-repo
341 pushing to empty-repo
341 pushing to empty-repo
342 searching for changes
342 searching for changes
343 adding changesets
343 adding changesets
344 adding manifests
344 adding manifests
345 adding file changes
345 adding file changes
346 added 1 changesets with 2 changes to 2 files
346 added 1 changesets with 2 changes to 2 files
347 $ grep treemanifest empty-repo/.hg/requires
347 $ grep treemanifest empty-repo/.hg/requires
348 treemanifest
348 treemanifest
349
349
350 Pushing to an empty repo works
350 Pushing to an empty repo works
351
351
352 $ hg --config experimental.treemanifest=1 init clone
352 $ hg --config experimental.treemanifest=1 init clone
353 $ grep treemanifest clone/.hg/requires
353 $ grep treemanifest clone/.hg/requires
354 treemanifest
354 treemanifest
355 $ hg push -R repo clone
355 $ hg push -R repo clone
356 pushing to clone
356 pushing to clone
357 searching for changes
357 searching for changes
358 adding changesets
358 adding changesets
359 adding manifests
359 adding manifests
360 adding file changes
360 adding file changes
361 added 11 changesets with 15 changes to 10 files (+3 heads)
361 added 11 changesets with 15 changes to 10 files (+3 heads)
362 $ grep treemanifest clone/.hg/requires
362 $ grep treemanifest clone/.hg/requires
363 treemanifest
363 treemanifest
364
364
365 Create deeper repo with tree manifests.
365 Create deeper repo with tree manifests.
366
366
367 $ hg --config experimental.treemanifest=True init deeprepo
367 $ hg --config experimental.treemanifest=True init deeprepo
368 $ cd deeprepo
368 $ cd deeprepo
369
369
370 $ mkdir a
370 $ mkdir a
371 $ mkdir b
371 $ mkdir b
372 $ mkdir b/bar
372 $ mkdir b/bar
373 $ mkdir b/bar/orange
373 $ mkdir b/bar/orange
374 $ mkdir b/bar/orange/fly
374 $ mkdir b/bar/orange/fly
375 $ mkdir b/foo
375 $ mkdir b/foo
376 $ mkdir b/foo/apple
376 $ mkdir b/foo/apple
377 $ mkdir b/foo/apple/bees
377 $ mkdir b/foo/apple/bees
378
378
379 $ touch a/one.txt
379 $ touch a/one.txt
380 $ touch a/two.txt
380 $ touch a/two.txt
381 $ touch b/bar/fruits.txt
381 $ touch b/bar/fruits.txt
382 $ touch b/bar/orange/fly/gnat.py
382 $ touch b/bar/orange/fly/gnat.py
383 $ touch b/bar/orange/fly/housefly.txt
383 $ touch b/bar/orange/fly/housefly.txt
384 $ touch b/foo/apple/bees/flower.py
384 $ touch b/foo/apple/bees/flower.py
385 $ touch c.txt
385 $ touch c.txt
386 $ touch d.py
386 $ touch d.py
387
387
388 $ hg ci -Aqm 'initial'
388 $ hg ci -Aqm 'initial'
389
389
390 We'll see that visitdir works by removing some treemanifest revlogs and running
390 We'll see that visitdir works by removing some treemanifest revlogs and running
391 the files command with various parameters.
391 the files command with various parameters.
392
392
393 Test files from the root.
393 Test files from the root.
394
394
395 $ hg files -r .
395 $ hg files -r .
396 a/one.txt (glob)
396 a/one.txt (glob)
397 a/two.txt (glob)
397 a/two.txt (glob)
398 b/bar/fruits.txt (glob)
398 b/bar/fruits.txt (glob)
399 b/bar/orange/fly/gnat.py (glob)
399 b/bar/orange/fly/gnat.py (glob)
400 b/bar/orange/fly/housefly.txt (glob)
400 b/bar/orange/fly/housefly.txt (glob)
401 b/foo/apple/bees/flower.py (glob)
401 b/foo/apple/bees/flower.py (glob)
402 c.txt
402 c.txt
403 d.py
403 d.py
404
404
405 Excludes with a glob should not exclude everything from the glob's root
405 Excludes with a glob should not exclude everything from the glob's root
406
406
407 $ hg files -r . -X 'b/fo?' b
407 $ hg files -r . -X 'b/fo?' b
408 b/bar/fruits.txt (glob)
408 b/bar/fruits.txt (glob)
409 b/bar/orange/fly/gnat.py (glob)
409 b/bar/orange/fly/gnat.py (glob)
410 b/bar/orange/fly/housefly.txt (glob)
410 b/bar/orange/fly/housefly.txt (glob)
411 $ cp -r .hg/store .hg/store-copy
411 $ cp -r .hg/store .hg/store-copy
412
412
413 Test files for a subdirectory.
413 Test files for a subdirectory.
414
414
415 $ rm -r .hg/store/meta/a
415 $ rm -r .hg/store/meta/a
416 $ hg files -r . b
416 $ hg files -r . b
417 b/bar/fruits.txt (glob)
417 b/bar/fruits.txt (glob)
418 b/bar/orange/fly/gnat.py (glob)
418 b/bar/orange/fly/gnat.py (glob)
419 b/bar/orange/fly/housefly.txt (glob)
419 b/bar/orange/fly/housefly.txt (glob)
420 b/foo/apple/bees/flower.py (glob)
420 b/foo/apple/bees/flower.py (glob)
421 $ cp -rT .hg/store-copy .hg/store
421 $ cp -rT .hg/store-copy .hg/store
422
422
423 Test files with just includes and excludes.
423 Test files with just includes and excludes.
424
424
425 $ rm -r .hg/store/meta/a
425 $ rm -r .hg/store/meta/a
426 $ rm -r .hg/store/meta/b/bar/orange/fly
426 $ rm -r .hg/store/meta/b/bar/orange/fly
427 $ rm -r .hg/store/meta/b/foo/apple/bees
427 $ rm -r .hg/store/meta/b/foo/apple/bees
428 $ hg files -r . -I path:b/bar -X path:b/bar/orange/fly -I path:b/foo -X path:b/foo/apple/bees
428 $ hg files -r . -I path:b/bar -X path:b/bar/orange/fly -I path:b/foo -X path:b/foo/apple/bees
429 b/bar/fruits.txt (glob)
429 b/bar/fruits.txt (glob)
430 $ cp -rT .hg/store-copy .hg/store
430 $ cp -rT .hg/store-copy .hg/store
431
431
432 Test files for a subdirectory, excluding a directory within it.
432 Test files for a subdirectory, excluding a directory within it.
433
433
434 $ rm -r .hg/store/meta/a
434 $ rm -r .hg/store/meta/a
435 $ rm -r .hg/store/meta/b/foo
435 $ rm -r .hg/store/meta/b/foo
436 $ hg files -r . -X path:b/foo b
436 $ hg files -r . -X path:b/foo b
437 b/bar/fruits.txt (glob)
437 b/bar/fruits.txt (glob)
438 b/bar/orange/fly/gnat.py (glob)
438 b/bar/orange/fly/gnat.py (glob)
439 b/bar/orange/fly/housefly.txt (glob)
439 b/bar/orange/fly/housefly.txt (glob)
440 $ cp -rT .hg/store-copy .hg/store
440 $ cp -rT .hg/store-copy .hg/store
441
441
442 Test files for a sub directory, including only a directory within it, and
442 Test files for a sub directory, including only a directory within it, and
443 including an unrelated directory.
443 including an unrelated directory.
444
444
445 $ rm -r .hg/store/meta/a
445 $ rm -r .hg/store/meta/a
446 $ rm -r .hg/store/meta/b/foo
446 $ rm -r .hg/store/meta/b/foo
447 $ hg files -r . -I path:b/bar/orange -I path:a b
447 $ hg files -r . -I path:b/bar/orange -I path:a b
448 b/bar/orange/fly/gnat.py (glob)
448 b/bar/orange/fly/gnat.py (glob)
449 b/bar/orange/fly/housefly.txt (glob)
449 b/bar/orange/fly/housefly.txt (glob)
450 $ cp -rT .hg/store-copy .hg/store
450 $ cp -rT .hg/store-copy .hg/store
451
451
452 Test files for a pattern, including a directory, and excluding a directory
452 Test files for a pattern, including a directory, and excluding a directory
453 within that.
453 within that.
454
454
455 $ rm -r .hg/store/meta/a
455 $ rm -r .hg/store/meta/a
456 $ rm -r .hg/store/meta/b/foo
456 $ rm -r .hg/store/meta/b/foo
457 $ rm -r .hg/store/meta/b/bar/orange
457 $ rm -r .hg/store/meta/b/bar/orange
458 $ hg files -r . glob:**.txt -I path:b/bar -X path:b/bar/orange
458 $ hg files -r . glob:**.txt -I path:b/bar -X path:b/bar/orange
459 b/bar/fruits.txt (glob)
459 b/bar/fruits.txt (glob)
460 $ cp -rT .hg/store-copy .hg/store
460 $ cp -rT .hg/store-copy .hg/store
461
461
462 Add some more changes to the deep repo
462 Add some more changes to the deep repo
463 $ echo narf >> b/bar/fruits.txt
463 $ echo narf >> b/bar/fruits.txt
464 $ hg ci -m narf
464 $ hg ci -m narf
465 $ echo troz >> b/bar/orange/fly/gnat.py
465 $ echo troz >> b/bar/orange/fly/gnat.py
466 $ hg ci -m troz
466 $ hg ci -m troz
467
467
468 Test cloning a treemanifest repo over http.
468 Test cloning a treemanifest repo over http.
469 $ hg serve -p $HGPORT2 -d --pid-file=hg.pid --errorlog=errors.log
469 $ hg serve -p $HGPORT2 -d --pid-file=hg.pid --errorlog=errors.log
470 $ cat hg.pid >> $DAEMON_PIDS
470 $ cat hg.pid >> $DAEMON_PIDS
471 $ cd ..
471 $ cd ..
472 We can clone even with the knob turned off and we'll get a treemanifest repo.
472 We can clone even with the knob turned off and we'll get a treemanifest repo.
473 $ hg clone --config experimental.treemanifest=False \
473 $ hg clone --config experimental.treemanifest=False \
474 > --config experimental.changegroup3=True \
474 > --config experimental.changegroup3=True \
475 > http://localhost:$HGPORT2 deepclone
475 > http://localhost:$HGPORT2 deepclone
476 requesting all changes
476 requesting all changes
477 adding changesets
477 adding changesets
478 adding manifests
478 adding manifests
479 adding file changes
479 adding file changes
480 added 3 changesets with 10 changes to 8 files
480 added 3 changesets with 10 changes to 8 files
481 updating to branch default
481 updating to branch default
482 8 files updated, 0 files merged, 0 files removed, 0 files unresolved
482 8 files updated, 0 files merged, 0 files removed, 0 files unresolved
483 No server errors.
483 No server errors.
484 $ cat deeprepo/errors.log
484 $ cat deeprepo/errors.log
485 requires got updated to include treemanifest
485 requires got updated to include treemanifest
486 $ cat deepclone/.hg/requires | grep treemanifest
486 $ cat deepclone/.hg/requires | grep treemanifest
487 treemanifest
487 treemanifest
488 Tree manifest revlogs exist.
488 Tree manifest revlogs exist.
489 $ find deepclone/.hg/store/meta | sort
489 $ find deepclone/.hg/store/meta | sort
490 deepclone/.hg/store/meta
490 deepclone/.hg/store/meta
491 deepclone/.hg/store/meta/a
491 deepclone/.hg/store/meta/a
492 deepclone/.hg/store/meta/a/00manifest.i
492 deepclone/.hg/store/meta/a/00manifest.i
493 deepclone/.hg/store/meta/b
493 deepclone/.hg/store/meta/b
494 deepclone/.hg/store/meta/b/00manifest.i
494 deepclone/.hg/store/meta/b/00manifest.i
495 deepclone/.hg/store/meta/b/bar
495 deepclone/.hg/store/meta/b/bar
496 deepclone/.hg/store/meta/b/bar/00manifest.i
496 deepclone/.hg/store/meta/b/bar/00manifest.i
497 deepclone/.hg/store/meta/b/bar/orange
497 deepclone/.hg/store/meta/b/bar/orange
498 deepclone/.hg/store/meta/b/bar/orange/00manifest.i
498 deepclone/.hg/store/meta/b/bar/orange/00manifest.i
499 deepclone/.hg/store/meta/b/bar/orange/fly
499 deepclone/.hg/store/meta/b/bar/orange/fly
500 deepclone/.hg/store/meta/b/bar/orange/fly/00manifest.i
500 deepclone/.hg/store/meta/b/bar/orange/fly/00manifest.i
501 deepclone/.hg/store/meta/b/foo
501 deepclone/.hg/store/meta/b/foo
502 deepclone/.hg/store/meta/b/foo/00manifest.i
502 deepclone/.hg/store/meta/b/foo/00manifest.i
503 deepclone/.hg/store/meta/b/foo/apple
503 deepclone/.hg/store/meta/b/foo/apple
504 deepclone/.hg/store/meta/b/foo/apple/00manifest.i
504 deepclone/.hg/store/meta/b/foo/apple/00manifest.i
505 deepclone/.hg/store/meta/b/foo/apple/bees
505 deepclone/.hg/store/meta/b/foo/apple/bees
506 deepclone/.hg/store/meta/b/foo/apple/bees/00manifest.i
506 deepclone/.hg/store/meta/b/foo/apple/bees/00manifest.i
507 Verify passes.
507 Verify passes.
508 $ cd deepclone
508 $ cd deepclone
509 $ hg verify
509 $ hg verify
510 checking changesets
510 checking changesets
511 checking manifests
511 checking manifests
512 crosschecking files in changesets and manifests
512 crosschecking files in changesets and manifests
513 checking files
513 checking files
514 8 files, 3 changesets, 10 total revisions
514 8 files, 3 changesets, 10 total revisions
515 $ cd ..
515 $ cd ..
516
517 Create clones using old repo formats to use in later tests
518 $ hg clone --config format.usestore=False \
519 > --config experimental.changegroup3=True \
520 > http://localhost:$HGPORT2 deeprepo-basicstore
521 requesting all changes
522 adding changesets
523 adding manifests
524 adding file changes
525 added 3 changesets with 10 changes to 8 files
526 updating to branch default
527 8 files updated, 0 files merged, 0 files removed, 0 files unresolved
528 $ grep store deeprepo-basicstore/.hg/requires
529 [1]
530 $ hg clone --config format.usefncache=False \
531 > --config experimental.changegroup3=True \
532 > http://localhost:$HGPORT2 deeprepo-encodedstore
533 requesting all changes
534 adding changesets
535 adding manifests
536 adding file changes
537 added 3 changesets with 10 changes to 8 files
538 updating to branch default
539 8 files updated, 0 files merged, 0 files removed, 0 files unresolved
540 $ grep fncache deeprepo-encodedstore/.hg/requires
541 [1]
542
543 Local clone with basicstore
544 $ hg clone -U deeprepo-basicstore local-clone-basicstore
545 $ hg -R local-clone-basicstore verify
546 checking changesets
547 checking manifests
548 crosschecking files in changesets and manifests
549 checking files
550 8 files, 3 changesets, 10 total revisions
551
552 Local clone with encodedstore
553 $ hg clone -U deeprepo-encodedstore local-clone-encodedstore
554 $ hg -R local-clone-encodedstore verify
555 checking changesets
556 checking manifests
557 crosschecking files in changesets and manifests
558 checking files
559 8 files, 3 changesets, 10 total revisions
560
561 Local clone with fncachestore
562 $ hg clone -U deeprepo local-clone-fncachestore
563 $ hg -R local-clone-fncachestore verify
564 checking changesets
565 checking manifests
566 crosschecking files in changesets and manifests
567 checking files
568 8 files, 3 changesets, 10 total revisions
General Comments 0
You need to be logged in to leave comments. Login now