##// END OF EJS Templates
checkexec: create destination directory if necessary...
Boris Feld -
r40703:bd087497 stable
parent child Browse files
Show More
@@ -1,684 +1,699
1 # posix.py - Posix utility function implementations for Mercurial
1 # posix.py - Posix utility function implementations for Mercurial
2 #
2 #
3 # Copyright 2005-2009 Matt Mackall <mpm@selenic.com> and others
3 # Copyright 2005-2009 Matt Mackall <mpm@selenic.com> and others
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 fcntl
11 import fcntl
12 import getpass
12 import getpass
13 import grp
13 import grp
14 import os
14 import os
15 import pwd
15 import pwd
16 import re
16 import re
17 import select
17 import select
18 import stat
18 import stat
19 import sys
19 import sys
20 import tempfile
20 import tempfile
21 import unicodedata
21 import unicodedata
22
22
23 from .i18n import _
23 from .i18n import _
24 from . import (
24 from . import (
25 encoding,
25 encoding,
26 error,
26 error,
27 policy,
27 policy,
28 pycompat,
28 pycompat,
29 )
29 )
30
30
31 osutil = policy.importmod(r'osutil')
31 osutil = policy.importmod(r'osutil')
32
32
33 posixfile = open
33 posixfile = open
34 normpath = os.path.normpath
34 normpath = os.path.normpath
35 samestat = os.path.samestat
35 samestat = os.path.samestat
36 try:
36 try:
37 oslink = os.link
37 oslink = os.link
38 except AttributeError:
38 except AttributeError:
39 # Some platforms build Python without os.link on systems that are
39 # Some platforms build Python without os.link on systems that are
40 # vaguely unix-like but don't have hardlink support. For those
40 # vaguely unix-like but don't have hardlink support. For those
41 # poor souls, just say we tried and that it failed so we fall back
41 # poor souls, just say we tried and that it failed so we fall back
42 # to copies.
42 # to copies.
43 def oslink(src, dst):
43 def oslink(src, dst):
44 raise OSError(errno.EINVAL,
44 raise OSError(errno.EINVAL,
45 'hardlinks not supported: %s to %s' % (src, dst))
45 'hardlinks not supported: %s to %s' % (src, dst))
46 readlink = os.readlink
46 readlink = os.readlink
47 unlink = os.unlink
47 unlink = os.unlink
48 rename = os.rename
48 rename = os.rename
49 removedirs = os.removedirs
49 removedirs = os.removedirs
50 expandglobs = False
50 expandglobs = False
51
51
52 umask = os.umask(0)
52 umask = os.umask(0)
53 os.umask(umask)
53 os.umask(umask)
54
54
55 def split(p):
55 def split(p):
56 '''Same as posixpath.split, but faster
56 '''Same as posixpath.split, but faster
57
57
58 >>> import posixpath
58 >>> import posixpath
59 >>> for f in [b'/absolute/path/to/file',
59 >>> for f in [b'/absolute/path/to/file',
60 ... b'relative/path/to/file',
60 ... b'relative/path/to/file',
61 ... b'file_alone',
61 ... b'file_alone',
62 ... b'path/to/directory/',
62 ... b'path/to/directory/',
63 ... b'/multiple/path//separators',
63 ... b'/multiple/path//separators',
64 ... b'/file_at_root',
64 ... b'/file_at_root',
65 ... b'///multiple_leading_separators_at_root',
65 ... b'///multiple_leading_separators_at_root',
66 ... b'']:
66 ... b'']:
67 ... assert split(f) == posixpath.split(f), f
67 ... assert split(f) == posixpath.split(f), f
68 '''
68 '''
69 ht = p.rsplit('/', 1)
69 ht = p.rsplit('/', 1)
70 if len(ht) == 1:
70 if len(ht) == 1:
71 return '', p
71 return '', p
72 nh = ht[0].rstrip('/')
72 nh = ht[0].rstrip('/')
73 if nh:
73 if nh:
74 return nh, ht[1]
74 return nh, ht[1]
75 return ht[0] + '/', ht[1]
75 return ht[0] + '/', ht[1]
76
76
77 def openhardlinks():
77 def openhardlinks():
78 '''return true if it is safe to hold open file handles to hardlinks'''
78 '''return true if it is safe to hold open file handles to hardlinks'''
79 return True
79 return True
80
80
81 def nlinks(name):
81 def nlinks(name):
82 '''return number of hardlinks for the given file'''
82 '''return number of hardlinks for the given file'''
83 return os.lstat(name).st_nlink
83 return os.lstat(name).st_nlink
84
84
85 def parsepatchoutput(output_line):
85 def parsepatchoutput(output_line):
86 """parses the output produced by patch and returns the filename"""
86 """parses the output produced by patch and returns the filename"""
87 pf = output_line[14:]
87 pf = output_line[14:]
88 if pycompat.sysplatform == 'OpenVMS':
88 if pycompat.sysplatform == 'OpenVMS':
89 if pf[0] == '`':
89 if pf[0] == '`':
90 pf = pf[1:-1] # Remove the quotes
90 pf = pf[1:-1] # Remove the quotes
91 else:
91 else:
92 if pf.startswith("'") and pf.endswith("'") and " " in pf:
92 if pf.startswith("'") and pf.endswith("'") and " " in pf:
93 pf = pf[1:-1] # Remove the quotes
93 pf = pf[1:-1] # Remove the quotes
94 return pf
94 return pf
95
95
96 def sshargs(sshcmd, host, user, port):
96 def sshargs(sshcmd, host, user, port):
97 '''Build argument list for ssh'''
97 '''Build argument list for ssh'''
98 args = user and ("%s@%s" % (user, host)) or host
98 args = user and ("%s@%s" % (user, host)) or host
99 if '-' in args[:1]:
99 if '-' in args[:1]:
100 raise error.Abort(
100 raise error.Abort(
101 _('illegal ssh hostname or username starting with -: %s') % args)
101 _('illegal ssh hostname or username starting with -: %s') % args)
102 args = shellquote(args)
102 args = shellquote(args)
103 if port:
103 if port:
104 args = '-p %s %s' % (shellquote(port), args)
104 args = '-p %s %s' % (shellquote(port), args)
105 return args
105 return args
106
106
107 def isexec(f):
107 def isexec(f):
108 """check whether a file is executable"""
108 """check whether a file is executable"""
109 return (os.lstat(f).st_mode & 0o100 != 0)
109 return (os.lstat(f).st_mode & 0o100 != 0)
110
110
111 def setflags(f, l, x):
111 def setflags(f, l, x):
112 st = os.lstat(f)
112 st = os.lstat(f)
113 s = st.st_mode
113 s = st.st_mode
114 if l:
114 if l:
115 if not stat.S_ISLNK(s):
115 if not stat.S_ISLNK(s):
116 # switch file to link
116 # switch file to link
117 fp = open(f, 'rb')
117 fp = open(f, 'rb')
118 data = fp.read()
118 data = fp.read()
119 fp.close()
119 fp.close()
120 unlink(f)
120 unlink(f)
121 try:
121 try:
122 os.symlink(data, f)
122 os.symlink(data, f)
123 except OSError:
123 except OSError:
124 # failed to make a link, rewrite file
124 # failed to make a link, rewrite file
125 fp = open(f, "wb")
125 fp = open(f, "wb")
126 fp.write(data)
126 fp.write(data)
127 fp.close()
127 fp.close()
128 # no chmod needed at this point
128 # no chmod needed at this point
129 return
129 return
130 if stat.S_ISLNK(s):
130 if stat.S_ISLNK(s):
131 # switch link to file
131 # switch link to file
132 data = os.readlink(f)
132 data = os.readlink(f)
133 unlink(f)
133 unlink(f)
134 fp = open(f, "wb")
134 fp = open(f, "wb")
135 fp.write(data)
135 fp.write(data)
136 fp.close()
136 fp.close()
137 s = 0o666 & ~umask # avoid restatting for chmod
137 s = 0o666 & ~umask # avoid restatting for chmod
138
138
139 sx = s & 0o100
139 sx = s & 0o100
140 if st.st_nlink > 1 and bool(x) != bool(sx):
140 if st.st_nlink > 1 and bool(x) != bool(sx):
141 # the file is a hardlink, break it
141 # the file is a hardlink, break it
142 with open(f, "rb") as fp:
142 with open(f, "rb") as fp:
143 data = fp.read()
143 data = fp.read()
144 unlink(f)
144 unlink(f)
145 with open(f, "wb") as fp:
145 with open(f, "wb") as fp:
146 fp.write(data)
146 fp.write(data)
147
147
148 if x and not sx:
148 if x and not sx:
149 # Turn on +x for every +r bit when making a file executable
149 # Turn on +x for every +r bit when making a file executable
150 # and obey umask.
150 # and obey umask.
151 os.chmod(f, s | (s & 0o444) >> 2 & ~umask)
151 os.chmod(f, s | (s & 0o444) >> 2 & ~umask)
152 elif not x and sx:
152 elif not x and sx:
153 # Turn off all +x bits
153 # Turn off all +x bits
154 os.chmod(f, s & 0o666)
154 os.chmod(f, s & 0o666)
155
155
156 def copymode(src, dst, mode=None):
156 def copymode(src, dst, mode=None):
157 '''Copy the file mode from the file at path src to dst.
157 '''Copy the file mode from the file at path src to dst.
158 If src doesn't exist, we're using mode instead. If mode is None, we're
158 If src doesn't exist, we're using mode instead. If mode is None, we're
159 using umask.'''
159 using umask.'''
160 try:
160 try:
161 st_mode = os.lstat(src).st_mode & 0o777
161 st_mode = os.lstat(src).st_mode & 0o777
162 except OSError as inst:
162 except OSError as inst:
163 if inst.errno != errno.ENOENT:
163 if inst.errno != errno.ENOENT:
164 raise
164 raise
165 st_mode = mode
165 st_mode = mode
166 if st_mode is None:
166 if st_mode is None:
167 st_mode = ~umask
167 st_mode = ~umask
168 st_mode &= 0o666
168 st_mode &= 0o666
169 os.chmod(dst, st_mode)
169 os.chmod(dst, st_mode)
170
170
171 def checkexec(path):
171 def checkexec(path):
172 """
172 """
173 Check whether the given path is on a filesystem with UNIX-like exec flags
173 Check whether the given path is on a filesystem with UNIX-like exec flags
174
174
175 Requires a directory (like /foo/.hg)
175 Requires a directory (like /foo/.hg)
176 """
176 """
177
177
178 # VFAT on some Linux versions can flip mode but it doesn't persist
178 # VFAT on some Linux versions can flip mode but it doesn't persist
179 # a FS remount. Frequently we can detect it if files are created
179 # a FS remount. Frequently we can detect it if files are created
180 # with exec bit on.
180 # with exec bit on.
181
181
182 try:
182 try:
183 EXECFLAGS = stat.S_IXUSR | stat.S_IXGRP | stat.S_IXOTH
183 EXECFLAGS = stat.S_IXUSR | stat.S_IXGRP | stat.S_IXOTH
184 cachedir = os.path.join(path, '.hg', 'cache')
184 basedir = os.path.join(path, '.hg')
185 cachedir = os.path.join(basedir, 'cache')
186 storedir = os.path.join(basedir, 'store')
187 if not os.path.exists(cachedir):
188 try:
189 # we want to create the 'cache' directory, not the '.hg' one.
190 # Automatically creating '.hg' directory could silently spawn
191 # invalid Mercurial repositories. That seems like a bad idea.
192 os.mkdir(cachedir)
193 if os.path.exists(storedir):
194 copymode(storedir, cachedir)
195 else:
196 copymode(basedir, cachedir)
197 except (IOError, OSError):
198 # we other fallback logic triggers
199 pass
185 if os.path.isdir(cachedir):
200 if os.path.isdir(cachedir):
186 checkisexec = os.path.join(cachedir, 'checkisexec')
201 checkisexec = os.path.join(cachedir, 'checkisexec')
187 checknoexec = os.path.join(cachedir, 'checknoexec')
202 checknoexec = os.path.join(cachedir, 'checknoexec')
188
203
189 try:
204 try:
190 m = os.stat(checkisexec).st_mode
205 m = os.stat(checkisexec).st_mode
191 except OSError as e:
206 except OSError as e:
192 if e.errno != errno.ENOENT:
207 if e.errno != errno.ENOENT:
193 raise
208 raise
194 # checkisexec does not exist - fall through ...
209 # checkisexec does not exist - fall through ...
195 else:
210 else:
196 # checkisexec exists, check if it actually is exec
211 # checkisexec exists, check if it actually is exec
197 if m & EXECFLAGS != 0:
212 if m & EXECFLAGS != 0:
198 # ensure checkisexec exists, check it isn't exec
213 # ensure checkisexec exists, check it isn't exec
199 try:
214 try:
200 m = os.stat(checknoexec).st_mode
215 m = os.stat(checknoexec).st_mode
201 except OSError as e:
216 except OSError as e:
202 if e.errno != errno.ENOENT:
217 if e.errno != errno.ENOENT:
203 raise
218 raise
204 open(checknoexec, 'w').close() # might fail
219 open(checknoexec, 'w').close() # might fail
205 m = os.stat(checknoexec).st_mode
220 m = os.stat(checknoexec).st_mode
206 if m & EXECFLAGS == 0:
221 if m & EXECFLAGS == 0:
207 # check-exec is exec and check-no-exec is not exec
222 # check-exec is exec and check-no-exec is not exec
208 return True
223 return True
209 # checknoexec exists but is exec - delete it
224 # checknoexec exists but is exec - delete it
210 unlink(checknoexec)
225 unlink(checknoexec)
211 # checkisexec exists but is not exec - delete it
226 # checkisexec exists but is not exec - delete it
212 unlink(checkisexec)
227 unlink(checkisexec)
213
228
214 # check using one file, leave it as checkisexec
229 # check using one file, leave it as checkisexec
215 checkdir = cachedir
230 checkdir = cachedir
216 else:
231 else:
217 # check directly in path and don't leave checkisexec behind
232 # check directly in path and don't leave checkisexec behind
218 checkdir = path
233 checkdir = path
219 checkisexec = None
234 checkisexec = None
220 fh, fn = pycompat.mkstemp(dir=checkdir, prefix='hg-checkexec-')
235 fh, fn = pycompat.mkstemp(dir=checkdir, prefix='hg-checkexec-')
221 try:
236 try:
222 os.close(fh)
237 os.close(fh)
223 m = os.stat(fn).st_mode
238 m = os.stat(fn).st_mode
224 if m & EXECFLAGS == 0:
239 if m & EXECFLAGS == 0:
225 os.chmod(fn, m & 0o777 | EXECFLAGS)
240 os.chmod(fn, m & 0o777 | EXECFLAGS)
226 if os.stat(fn).st_mode & EXECFLAGS != 0:
241 if os.stat(fn).st_mode & EXECFLAGS != 0:
227 if checkisexec is not None:
242 if checkisexec is not None:
228 os.rename(fn, checkisexec)
243 os.rename(fn, checkisexec)
229 fn = None
244 fn = None
230 return True
245 return True
231 finally:
246 finally:
232 if fn is not None:
247 if fn is not None:
233 unlink(fn)
248 unlink(fn)
234 except (IOError, OSError):
249 except (IOError, OSError):
235 # we don't care, the user probably won't be able to commit anyway
250 # we don't care, the user probably won't be able to commit anyway
236 return False
251 return False
237
252
238 def checklink(path):
253 def checklink(path):
239 """check whether the given path is on a symlink-capable filesystem"""
254 """check whether the given path is on a symlink-capable filesystem"""
240 # mktemp is not racy because symlink creation will fail if the
255 # mktemp is not racy because symlink creation will fail if the
241 # file already exists
256 # file already exists
242 while True:
257 while True:
243 cachedir = os.path.join(path, '.hg', 'cache')
258 cachedir = os.path.join(path, '.hg', 'cache')
244 checklink = os.path.join(cachedir, 'checklink')
259 checklink = os.path.join(cachedir, 'checklink')
245 # try fast path, read only
260 # try fast path, read only
246 if os.path.islink(checklink):
261 if os.path.islink(checklink):
247 return True
262 return True
248 if os.path.isdir(cachedir):
263 if os.path.isdir(cachedir):
249 checkdir = cachedir
264 checkdir = cachedir
250 else:
265 else:
251 checkdir = path
266 checkdir = path
252 cachedir = None
267 cachedir = None
253 name = tempfile.mktemp(dir=pycompat.fsdecode(checkdir),
268 name = tempfile.mktemp(dir=pycompat.fsdecode(checkdir),
254 prefix=r'checklink-')
269 prefix=r'checklink-')
255 name = pycompat.fsencode(name)
270 name = pycompat.fsencode(name)
256 try:
271 try:
257 fd = None
272 fd = None
258 if cachedir is None:
273 if cachedir is None:
259 fd = pycompat.namedtempfile(dir=checkdir,
274 fd = pycompat.namedtempfile(dir=checkdir,
260 prefix='hg-checklink-')
275 prefix='hg-checklink-')
261 target = os.path.basename(fd.name)
276 target = os.path.basename(fd.name)
262 else:
277 else:
263 # create a fixed file to link to; doesn't matter if it
278 # create a fixed file to link to; doesn't matter if it
264 # already exists.
279 # already exists.
265 target = 'checklink-target'
280 target = 'checklink-target'
266 try:
281 try:
267 fullpath = os.path.join(cachedir, target)
282 fullpath = os.path.join(cachedir, target)
268 open(fullpath, 'w').close()
283 open(fullpath, 'w').close()
269 except IOError as inst:
284 except IOError as inst:
270 if inst[0] == errno.EACCES:
285 if inst[0] == errno.EACCES:
271 # If we can't write to cachedir, just pretend
286 # If we can't write to cachedir, just pretend
272 # that the fs is readonly and by association
287 # that the fs is readonly and by association
273 # that the fs won't support symlinks. This
288 # that the fs won't support symlinks. This
274 # seems like the least dangerous way to avoid
289 # seems like the least dangerous way to avoid
275 # data loss.
290 # data loss.
276 return False
291 return False
277 raise
292 raise
278 try:
293 try:
279 os.symlink(target, name)
294 os.symlink(target, name)
280 if cachedir is None:
295 if cachedir is None:
281 unlink(name)
296 unlink(name)
282 else:
297 else:
283 try:
298 try:
284 os.rename(name, checklink)
299 os.rename(name, checklink)
285 except OSError:
300 except OSError:
286 unlink(name)
301 unlink(name)
287 return True
302 return True
288 except OSError as inst:
303 except OSError as inst:
289 # link creation might race, try again
304 # link creation might race, try again
290 if inst.errno == errno.EEXIST:
305 if inst.errno == errno.EEXIST:
291 continue
306 continue
292 raise
307 raise
293 finally:
308 finally:
294 if fd is not None:
309 if fd is not None:
295 fd.close()
310 fd.close()
296 except AttributeError:
311 except AttributeError:
297 return False
312 return False
298 except OSError as inst:
313 except OSError as inst:
299 # sshfs might report failure while successfully creating the link
314 # sshfs might report failure while successfully creating the link
300 if inst.errno == errno.EIO and os.path.exists(name):
315 if inst.errno == errno.EIO and os.path.exists(name):
301 unlink(name)
316 unlink(name)
302 return False
317 return False
303
318
304 def checkosfilename(path):
319 def checkosfilename(path):
305 '''Check that the base-relative path is a valid filename on this platform.
320 '''Check that the base-relative path is a valid filename on this platform.
306 Returns None if the path is ok, or a UI string describing the problem.'''
321 Returns None if the path is ok, or a UI string describing the problem.'''
307 return None # on posix platforms, every path is ok
322 return None # on posix platforms, every path is ok
308
323
309 def getfsmountpoint(dirpath):
324 def getfsmountpoint(dirpath):
310 '''Get the filesystem mount point from a directory (best-effort)
325 '''Get the filesystem mount point from a directory (best-effort)
311
326
312 Returns None if we are unsure. Raises OSError on ENOENT, EPERM, etc.
327 Returns None if we are unsure. Raises OSError on ENOENT, EPERM, etc.
313 '''
328 '''
314 return getattr(osutil, 'getfsmountpoint', lambda x: None)(dirpath)
329 return getattr(osutil, 'getfsmountpoint', lambda x: None)(dirpath)
315
330
316 def getfstype(dirpath):
331 def getfstype(dirpath):
317 '''Get the filesystem type name from a directory (best-effort)
332 '''Get the filesystem type name from a directory (best-effort)
318
333
319 Returns None if we are unsure. Raises OSError on ENOENT, EPERM, etc.
334 Returns None if we are unsure. Raises OSError on ENOENT, EPERM, etc.
320 '''
335 '''
321 return getattr(osutil, 'getfstype', lambda x: None)(dirpath)
336 return getattr(osutil, 'getfstype', lambda x: None)(dirpath)
322
337
323 def setbinary(fd):
338 def setbinary(fd):
324 pass
339 pass
325
340
326 def pconvert(path):
341 def pconvert(path):
327 return path
342 return path
328
343
329 def localpath(path):
344 def localpath(path):
330 return path
345 return path
331
346
332 def samefile(fpath1, fpath2):
347 def samefile(fpath1, fpath2):
333 """Returns whether path1 and path2 refer to the same file. This is only
348 """Returns whether path1 and path2 refer to the same file. This is only
334 guaranteed to work for files, not directories."""
349 guaranteed to work for files, not directories."""
335 return os.path.samefile(fpath1, fpath2)
350 return os.path.samefile(fpath1, fpath2)
336
351
337 def samedevice(fpath1, fpath2):
352 def samedevice(fpath1, fpath2):
338 """Returns whether fpath1 and fpath2 are on the same device. This is only
353 """Returns whether fpath1 and fpath2 are on the same device. This is only
339 guaranteed to work for files, not directories."""
354 guaranteed to work for files, not directories."""
340 st1 = os.lstat(fpath1)
355 st1 = os.lstat(fpath1)
341 st2 = os.lstat(fpath2)
356 st2 = os.lstat(fpath2)
342 return st1.st_dev == st2.st_dev
357 return st1.st_dev == st2.st_dev
343
358
344 # os.path.normcase is a no-op, which doesn't help us on non-native filesystems
359 # os.path.normcase is a no-op, which doesn't help us on non-native filesystems
345 def normcase(path):
360 def normcase(path):
346 return path.lower()
361 return path.lower()
347
362
348 # what normcase does to ASCII strings
363 # what normcase does to ASCII strings
349 normcasespec = encoding.normcasespecs.lower
364 normcasespec = encoding.normcasespecs.lower
350 # fallback normcase function for non-ASCII strings
365 # fallback normcase function for non-ASCII strings
351 normcasefallback = normcase
366 normcasefallback = normcase
352
367
353 if pycompat.isdarwin:
368 if pycompat.isdarwin:
354
369
355 def normcase(path):
370 def normcase(path):
356 '''
371 '''
357 Normalize a filename for OS X-compatible comparison:
372 Normalize a filename for OS X-compatible comparison:
358 - escape-encode invalid characters
373 - escape-encode invalid characters
359 - decompose to NFD
374 - decompose to NFD
360 - lowercase
375 - lowercase
361 - omit ignored characters [200c-200f, 202a-202e, 206a-206f,feff]
376 - omit ignored characters [200c-200f, 202a-202e, 206a-206f,feff]
362
377
363 >>> normcase(b'UPPER')
378 >>> normcase(b'UPPER')
364 'upper'
379 'upper'
365 >>> normcase(b'Caf\\xc3\\xa9')
380 >>> normcase(b'Caf\\xc3\\xa9')
366 'cafe\\xcc\\x81'
381 'cafe\\xcc\\x81'
367 >>> normcase(b'\\xc3\\x89')
382 >>> normcase(b'\\xc3\\x89')
368 'e\\xcc\\x81'
383 'e\\xcc\\x81'
369 >>> normcase(b'\\xb8\\xca\\xc3\\xca\\xbe\\xc8.JPG') # issue3918
384 >>> normcase(b'\\xb8\\xca\\xc3\\xca\\xbe\\xc8.JPG') # issue3918
370 '%b8%ca%c3\\xca\\xbe%c8.jpg'
385 '%b8%ca%c3\\xca\\xbe%c8.jpg'
371 '''
386 '''
372
387
373 try:
388 try:
374 return encoding.asciilower(path) # exception for non-ASCII
389 return encoding.asciilower(path) # exception for non-ASCII
375 except UnicodeDecodeError:
390 except UnicodeDecodeError:
376 return normcasefallback(path)
391 return normcasefallback(path)
377
392
378 normcasespec = encoding.normcasespecs.lower
393 normcasespec = encoding.normcasespecs.lower
379
394
380 def normcasefallback(path):
395 def normcasefallback(path):
381 try:
396 try:
382 u = path.decode('utf-8')
397 u = path.decode('utf-8')
383 except UnicodeDecodeError:
398 except UnicodeDecodeError:
384 # OS X percent-encodes any bytes that aren't valid utf-8
399 # OS X percent-encodes any bytes that aren't valid utf-8
385 s = ''
400 s = ''
386 pos = 0
401 pos = 0
387 l = len(path)
402 l = len(path)
388 while pos < l:
403 while pos < l:
389 try:
404 try:
390 c = encoding.getutf8char(path, pos)
405 c = encoding.getutf8char(path, pos)
391 pos += len(c)
406 pos += len(c)
392 except ValueError:
407 except ValueError:
393 c = '%%%02X' % ord(path[pos:pos + 1])
408 c = '%%%02X' % ord(path[pos:pos + 1])
394 pos += 1
409 pos += 1
395 s += c
410 s += c
396
411
397 u = s.decode('utf-8')
412 u = s.decode('utf-8')
398
413
399 # Decompose then lowercase (HFS+ technote specifies lower)
414 # Decompose then lowercase (HFS+ technote specifies lower)
400 enc = unicodedata.normalize(r'NFD', u).lower().encode('utf-8')
415 enc = unicodedata.normalize(r'NFD', u).lower().encode('utf-8')
401 # drop HFS+ ignored characters
416 # drop HFS+ ignored characters
402 return encoding.hfsignoreclean(enc)
417 return encoding.hfsignoreclean(enc)
403
418
404 if pycompat.sysplatform == 'cygwin':
419 if pycompat.sysplatform == 'cygwin':
405 # workaround for cygwin, in which mount point part of path is
420 # workaround for cygwin, in which mount point part of path is
406 # treated as case sensitive, even though underlying NTFS is case
421 # treated as case sensitive, even though underlying NTFS is case
407 # insensitive.
422 # insensitive.
408
423
409 # default mount points
424 # default mount points
410 cygwinmountpoints = sorted([
425 cygwinmountpoints = sorted([
411 "/usr/bin",
426 "/usr/bin",
412 "/usr/lib",
427 "/usr/lib",
413 "/cygdrive",
428 "/cygdrive",
414 ], reverse=True)
429 ], reverse=True)
415
430
416 # use upper-ing as normcase as same as NTFS workaround
431 # use upper-ing as normcase as same as NTFS workaround
417 def normcase(path):
432 def normcase(path):
418 pathlen = len(path)
433 pathlen = len(path)
419 if (pathlen == 0) or (path[0] != pycompat.ossep):
434 if (pathlen == 0) or (path[0] != pycompat.ossep):
420 # treat as relative
435 # treat as relative
421 return encoding.upper(path)
436 return encoding.upper(path)
422
437
423 # to preserve case of mountpoint part
438 # to preserve case of mountpoint part
424 for mp in cygwinmountpoints:
439 for mp in cygwinmountpoints:
425 if not path.startswith(mp):
440 if not path.startswith(mp):
426 continue
441 continue
427
442
428 mplen = len(mp)
443 mplen = len(mp)
429 if mplen == pathlen: # mount point itself
444 if mplen == pathlen: # mount point itself
430 return mp
445 return mp
431 if path[mplen] == pycompat.ossep:
446 if path[mplen] == pycompat.ossep:
432 return mp + encoding.upper(path[mplen:])
447 return mp + encoding.upper(path[mplen:])
433
448
434 return encoding.upper(path)
449 return encoding.upper(path)
435
450
436 normcasespec = encoding.normcasespecs.other
451 normcasespec = encoding.normcasespecs.other
437 normcasefallback = normcase
452 normcasefallback = normcase
438
453
439 # Cygwin translates native ACLs to POSIX permissions,
454 # Cygwin translates native ACLs to POSIX permissions,
440 # but these translations are not supported by native
455 # but these translations are not supported by native
441 # tools, so the exec bit tends to be set erroneously.
456 # tools, so the exec bit tends to be set erroneously.
442 # Therefore, disable executable bit access on Cygwin.
457 # Therefore, disable executable bit access on Cygwin.
443 def checkexec(path):
458 def checkexec(path):
444 return False
459 return False
445
460
446 # Similarly, Cygwin's symlink emulation is likely to create
461 # Similarly, Cygwin's symlink emulation is likely to create
447 # problems when Mercurial is used from both Cygwin and native
462 # problems when Mercurial is used from both Cygwin and native
448 # Windows, with other native tools, or on shared volumes
463 # Windows, with other native tools, or on shared volumes
449 def checklink(path):
464 def checklink(path):
450 return False
465 return False
451
466
452 _needsshellquote = None
467 _needsshellquote = None
453 def shellquote(s):
468 def shellquote(s):
454 if pycompat.sysplatform == 'OpenVMS':
469 if pycompat.sysplatform == 'OpenVMS':
455 return '"%s"' % s
470 return '"%s"' % s
456 global _needsshellquote
471 global _needsshellquote
457 if _needsshellquote is None:
472 if _needsshellquote is None:
458 _needsshellquote = re.compile(br'[^a-zA-Z0-9._/+-]').search
473 _needsshellquote = re.compile(br'[^a-zA-Z0-9._/+-]').search
459 if s and not _needsshellquote(s):
474 if s and not _needsshellquote(s):
460 # "s" shouldn't have to be quoted
475 # "s" shouldn't have to be quoted
461 return s
476 return s
462 else:
477 else:
463 return "'%s'" % s.replace("'", "'\\''")
478 return "'%s'" % s.replace("'", "'\\''")
464
479
465 def shellsplit(s):
480 def shellsplit(s):
466 """Parse a command string in POSIX shell way (best-effort)"""
481 """Parse a command string in POSIX shell way (best-effort)"""
467 return pycompat.shlexsplit(s, posix=True)
482 return pycompat.shlexsplit(s, posix=True)
468
483
469 def quotecommand(cmd):
484 def quotecommand(cmd):
470 return cmd
485 return cmd
471
486
472 def testpid(pid):
487 def testpid(pid):
473 '''return False if pid dead, True if running or not sure'''
488 '''return False if pid dead, True if running or not sure'''
474 if pycompat.sysplatform == 'OpenVMS':
489 if pycompat.sysplatform == 'OpenVMS':
475 return True
490 return True
476 try:
491 try:
477 os.kill(pid, 0)
492 os.kill(pid, 0)
478 return True
493 return True
479 except OSError as inst:
494 except OSError as inst:
480 return inst.errno != errno.ESRCH
495 return inst.errno != errno.ESRCH
481
496
482 def isowner(st):
497 def isowner(st):
483 """Return True if the stat object st is from the current user."""
498 """Return True if the stat object st is from the current user."""
484 return st.st_uid == os.getuid()
499 return st.st_uid == os.getuid()
485
500
486 def findexe(command):
501 def findexe(command):
487 '''Find executable for command searching like which does.
502 '''Find executable for command searching like which does.
488 If command is a basename then PATH is searched for command.
503 If command is a basename then PATH is searched for command.
489 PATH isn't searched if command is an absolute or relative path.
504 PATH isn't searched if command is an absolute or relative path.
490 If command isn't found None is returned.'''
505 If command isn't found None is returned.'''
491 if pycompat.sysplatform == 'OpenVMS':
506 if pycompat.sysplatform == 'OpenVMS':
492 return command
507 return command
493
508
494 def findexisting(executable):
509 def findexisting(executable):
495 'Will return executable if existing file'
510 'Will return executable if existing file'
496 if os.path.isfile(executable) and os.access(executable, os.X_OK):
511 if os.path.isfile(executable) and os.access(executable, os.X_OK):
497 return executable
512 return executable
498 return None
513 return None
499
514
500 if pycompat.ossep in command:
515 if pycompat.ossep in command:
501 return findexisting(command)
516 return findexisting(command)
502
517
503 if pycompat.sysplatform == 'plan9':
518 if pycompat.sysplatform == 'plan9':
504 return findexisting(os.path.join('/bin', command))
519 return findexisting(os.path.join('/bin', command))
505
520
506 for path in encoding.environ.get('PATH', '').split(pycompat.ospathsep):
521 for path in encoding.environ.get('PATH', '').split(pycompat.ospathsep):
507 executable = findexisting(os.path.join(path, command))
522 executable = findexisting(os.path.join(path, command))
508 if executable is not None:
523 if executable is not None:
509 return executable
524 return executable
510 return None
525 return None
511
526
512 def setsignalhandler():
527 def setsignalhandler():
513 pass
528 pass
514
529
515 _wantedkinds = {stat.S_IFREG, stat.S_IFLNK}
530 _wantedkinds = {stat.S_IFREG, stat.S_IFLNK}
516
531
517 def statfiles(files):
532 def statfiles(files):
518 '''Stat each file in files. Yield each stat, or None if a file does not
533 '''Stat each file in files. Yield each stat, or None if a file does not
519 exist or has a type we don't care about.'''
534 exist or has a type we don't care about.'''
520 lstat = os.lstat
535 lstat = os.lstat
521 getkind = stat.S_IFMT
536 getkind = stat.S_IFMT
522 for nf in files:
537 for nf in files:
523 try:
538 try:
524 st = lstat(nf)
539 st = lstat(nf)
525 if getkind(st.st_mode) not in _wantedkinds:
540 if getkind(st.st_mode) not in _wantedkinds:
526 st = None
541 st = None
527 except OSError as err:
542 except OSError as err:
528 if err.errno not in (errno.ENOENT, errno.ENOTDIR):
543 if err.errno not in (errno.ENOENT, errno.ENOTDIR):
529 raise
544 raise
530 st = None
545 st = None
531 yield st
546 yield st
532
547
533 def getuser():
548 def getuser():
534 '''return name of current user'''
549 '''return name of current user'''
535 return pycompat.fsencode(getpass.getuser())
550 return pycompat.fsencode(getpass.getuser())
536
551
537 def username(uid=None):
552 def username(uid=None):
538 """Return the name of the user with the given uid.
553 """Return the name of the user with the given uid.
539
554
540 If uid is None, return the name of the current user."""
555 If uid is None, return the name of the current user."""
541
556
542 if uid is None:
557 if uid is None:
543 uid = os.getuid()
558 uid = os.getuid()
544 try:
559 try:
545 return pycompat.fsencode(pwd.getpwuid(uid)[0])
560 return pycompat.fsencode(pwd.getpwuid(uid)[0])
546 except KeyError:
561 except KeyError:
547 return b'%d' % uid
562 return b'%d' % uid
548
563
549 def groupname(gid=None):
564 def groupname(gid=None):
550 """Return the name of the group with the given gid.
565 """Return the name of the group with the given gid.
551
566
552 If gid is None, return the name of the current group."""
567 If gid is None, return the name of the current group."""
553
568
554 if gid is None:
569 if gid is None:
555 gid = os.getgid()
570 gid = os.getgid()
556 try:
571 try:
557 return grp.getgrgid(gid)[0]
572 return grp.getgrgid(gid)[0]
558 except KeyError:
573 except KeyError:
559 return str(gid)
574 return str(gid)
560
575
561 def groupmembers(name):
576 def groupmembers(name):
562 """Return the list of members of the group with the given
577 """Return the list of members of the group with the given
563 name, KeyError if the group does not exist.
578 name, KeyError if the group does not exist.
564 """
579 """
565 return list(grp.getgrnam(name).gr_mem)
580 return list(grp.getgrnam(name).gr_mem)
566
581
567 def spawndetached(args):
582 def spawndetached(args):
568 return os.spawnvp(os.P_NOWAIT | getattr(os, 'P_DETACH', 0),
583 return os.spawnvp(os.P_NOWAIT | getattr(os, 'P_DETACH', 0),
569 args[0], args)
584 args[0], args)
570
585
571 def gethgcmd():
586 def gethgcmd():
572 return sys.argv[:1]
587 return sys.argv[:1]
573
588
574 def makedir(path, notindexed):
589 def makedir(path, notindexed):
575 os.mkdir(path)
590 os.mkdir(path)
576
591
577 def lookupreg(key, name=None, scope=None):
592 def lookupreg(key, name=None, scope=None):
578 return None
593 return None
579
594
580 def hidewindow():
595 def hidewindow():
581 """Hide current shell window.
596 """Hide current shell window.
582
597
583 Used to hide the window opened when starting asynchronous
598 Used to hide the window opened when starting asynchronous
584 child process under Windows, unneeded on other systems.
599 child process under Windows, unneeded on other systems.
585 """
600 """
586 pass
601 pass
587
602
588 class cachestat(object):
603 class cachestat(object):
589 def __init__(self, path):
604 def __init__(self, path):
590 self.stat = os.stat(path)
605 self.stat = os.stat(path)
591
606
592 def cacheable(self):
607 def cacheable(self):
593 return bool(self.stat.st_ino)
608 return bool(self.stat.st_ino)
594
609
595 __hash__ = object.__hash__
610 __hash__ = object.__hash__
596
611
597 def __eq__(self, other):
612 def __eq__(self, other):
598 try:
613 try:
599 # Only dev, ino, size, mtime and atime are likely to change. Out
614 # Only dev, ino, size, mtime and atime are likely to change. Out
600 # of these, we shouldn't compare atime but should compare the
615 # of these, we shouldn't compare atime but should compare the
601 # rest. However, one of the other fields changing indicates
616 # rest. However, one of the other fields changing indicates
602 # something fishy going on, so return False if anything but atime
617 # something fishy going on, so return False if anything but atime
603 # changes.
618 # changes.
604 return (self.stat.st_mode == other.stat.st_mode and
619 return (self.stat.st_mode == other.stat.st_mode and
605 self.stat.st_ino == other.stat.st_ino and
620 self.stat.st_ino == other.stat.st_ino and
606 self.stat.st_dev == other.stat.st_dev and
621 self.stat.st_dev == other.stat.st_dev and
607 self.stat.st_nlink == other.stat.st_nlink and
622 self.stat.st_nlink == other.stat.st_nlink and
608 self.stat.st_uid == other.stat.st_uid and
623 self.stat.st_uid == other.stat.st_uid and
609 self.stat.st_gid == other.stat.st_gid and
624 self.stat.st_gid == other.stat.st_gid and
610 self.stat.st_size == other.stat.st_size and
625 self.stat.st_size == other.stat.st_size and
611 self.stat[stat.ST_MTIME] == other.stat[stat.ST_MTIME] and
626 self.stat[stat.ST_MTIME] == other.stat[stat.ST_MTIME] and
612 self.stat[stat.ST_CTIME] == other.stat[stat.ST_CTIME])
627 self.stat[stat.ST_CTIME] == other.stat[stat.ST_CTIME])
613 except AttributeError:
628 except AttributeError:
614 return False
629 return False
615
630
616 def __ne__(self, other):
631 def __ne__(self, other):
617 return not self == other
632 return not self == other
618
633
619 def statislink(st):
634 def statislink(st):
620 '''check whether a stat result is a symlink'''
635 '''check whether a stat result is a symlink'''
621 return st and stat.S_ISLNK(st.st_mode)
636 return st and stat.S_ISLNK(st.st_mode)
622
637
623 def statisexec(st):
638 def statisexec(st):
624 '''check whether a stat result is an executable file'''
639 '''check whether a stat result is an executable file'''
625 return st and (st.st_mode & 0o100 != 0)
640 return st and (st.st_mode & 0o100 != 0)
626
641
627 def poll(fds):
642 def poll(fds):
628 """block until something happens on any file descriptor
643 """block until something happens on any file descriptor
629
644
630 This is a generic helper that will check for any activity
645 This is a generic helper that will check for any activity
631 (read, write. exception) and return the list of touched files.
646 (read, write. exception) and return the list of touched files.
632
647
633 In unsupported cases, it will raise a NotImplementedError"""
648 In unsupported cases, it will raise a NotImplementedError"""
634 try:
649 try:
635 while True:
650 while True:
636 try:
651 try:
637 res = select.select(fds, fds, fds)
652 res = select.select(fds, fds, fds)
638 break
653 break
639 except select.error as inst:
654 except select.error as inst:
640 if inst.args[0] == errno.EINTR:
655 if inst.args[0] == errno.EINTR:
641 continue
656 continue
642 raise
657 raise
643 except ValueError: # out of range file descriptor
658 except ValueError: # out of range file descriptor
644 raise NotImplementedError()
659 raise NotImplementedError()
645 return sorted(list(set(sum(res, []))))
660 return sorted(list(set(sum(res, []))))
646
661
647 def readpipe(pipe):
662 def readpipe(pipe):
648 """Read all available data from a pipe."""
663 """Read all available data from a pipe."""
649 # We can't fstat() a pipe because Linux will always report 0.
664 # We can't fstat() a pipe because Linux will always report 0.
650 # So, we set the pipe to non-blocking mode and read everything
665 # So, we set the pipe to non-blocking mode and read everything
651 # that's available.
666 # that's available.
652 flags = fcntl.fcntl(pipe, fcntl.F_GETFL)
667 flags = fcntl.fcntl(pipe, fcntl.F_GETFL)
653 flags |= os.O_NONBLOCK
668 flags |= os.O_NONBLOCK
654 oldflags = fcntl.fcntl(pipe, fcntl.F_SETFL, flags)
669 oldflags = fcntl.fcntl(pipe, fcntl.F_SETFL, flags)
655
670
656 try:
671 try:
657 chunks = []
672 chunks = []
658 while True:
673 while True:
659 try:
674 try:
660 s = pipe.read()
675 s = pipe.read()
661 if not s:
676 if not s:
662 break
677 break
663 chunks.append(s)
678 chunks.append(s)
664 except IOError:
679 except IOError:
665 break
680 break
666
681
667 return ''.join(chunks)
682 return ''.join(chunks)
668 finally:
683 finally:
669 fcntl.fcntl(pipe, fcntl.F_SETFL, oldflags)
684 fcntl.fcntl(pipe, fcntl.F_SETFL, oldflags)
670
685
671 def bindunixsocket(sock, path):
686 def bindunixsocket(sock, path):
672 """Bind the UNIX domain socket to the specified path"""
687 """Bind the UNIX domain socket to the specified path"""
673 # use relative path instead of full path at bind() if possible, since
688 # use relative path instead of full path at bind() if possible, since
674 # AF_UNIX path has very small length limit (107 chars) on common
689 # AF_UNIX path has very small length limit (107 chars) on common
675 # platforms (see sys/un.h)
690 # platforms (see sys/un.h)
676 dirname, basename = os.path.split(path)
691 dirname, basename = os.path.split(path)
677 bakwdfd = None
692 bakwdfd = None
678 if dirname:
693 if dirname:
679 bakwdfd = os.open('.', os.O_DIRECTORY)
694 bakwdfd = os.open('.', os.O_DIRECTORY)
680 os.chdir(dirname)
695 os.chdir(dirname)
681 sock.bind(basename)
696 sock.bind(basename)
682 if bakwdfd:
697 if bakwdfd:
683 os.fchdir(bakwdfd)
698 os.fchdir(bakwdfd)
684 os.close(bakwdfd)
699 os.close(bakwdfd)
@@ -1,510 +1,516
1 #require repofncache
1 #require repofncache
2
2
3 Init repo1:
3 Init repo1:
4
4
5 $ hg init repo1
5 $ hg init repo1
6 $ cd repo1
6 $ cd repo1
7 $ echo "some text" > a
7 $ echo "some text" > a
8 $ hg add
8 $ hg add
9 adding a
9 adding a
10 $ hg ci -m first
10 $ hg ci -m first
11 $ cat .hg/store/fncache | sort
11 $ cat .hg/store/fncache | sort
12 data/a.i
12 data/a.i
13
13
14 Testing a.i/b:
14 Testing a.i/b:
15
15
16 $ mkdir a.i
16 $ mkdir a.i
17 $ echo "some other text" > a.i/b
17 $ echo "some other text" > a.i/b
18 $ hg add
18 $ hg add
19 adding a.i/b
19 adding a.i/b
20 $ hg ci -m second
20 $ hg ci -m second
21 $ cat .hg/store/fncache | sort
21 $ cat .hg/store/fncache | sort
22 data/a.i
22 data/a.i
23 data/a.i.hg/b.i
23 data/a.i.hg/b.i
24
24
25 Testing a.i.hg/c:
25 Testing a.i.hg/c:
26
26
27 $ mkdir a.i.hg
27 $ mkdir a.i.hg
28 $ echo "yet another text" > a.i.hg/c
28 $ echo "yet another text" > a.i.hg/c
29 $ hg add
29 $ hg add
30 adding a.i.hg/c
30 adding a.i.hg/c
31 $ hg ci -m third
31 $ hg ci -m third
32 $ cat .hg/store/fncache | sort
32 $ cat .hg/store/fncache | sort
33 data/a.i
33 data/a.i
34 data/a.i.hg.hg/c.i
34 data/a.i.hg.hg/c.i
35 data/a.i.hg/b.i
35 data/a.i.hg/b.i
36
36
37 Testing verify:
37 Testing verify:
38
38
39 $ hg verify
39 $ hg verify
40 checking changesets
40 checking changesets
41 checking manifests
41 checking manifests
42 crosschecking files in changesets and manifests
42 crosschecking files in changesets and manifests
43 checking files
43 checking files
44 checked 3 changesets with 3 changes to 3 files
44 checked 3 changesets with 3 changes to 3 files
45
45
46 $ rm .hg/store/fncache
46 $ rm .hg/store/fncache
47
47
48 $ hg verify
48 $ hg verify
49 checking changesets
49 checking changesets
50 checking manifests
50 checking manifests
51 crosschecking files in changesets and manifests
51 crosschecking files in changesets and manifests
52 checking files
52 checking files
53 warning: revlog 'data/a.i' not in fncache!
53 warning: revlog 'data/a.i' not in fncache!
54 warning: revlog 'data/a.i.hg/c.i' not in fncache!
54 warning: revlog 'data/a.i.hg/c.i' not in fncache!
55 warning: revlog 'data/a.i/b.i' not in fncache!
55 warning: revlog 'data/a.i/b.i' not in fncache!
56 checked 3 changesets with 3 changes to 3 files
56 checked 3 changesets with 3 changes to 3 files
57 3 warnings encountered!
57 3 warnings encountered!
58 hint: run "hg debugrebuildfncache" to recover from corrupt fncache
58 hint: run "hg debugrebuildfncache" to recover from corrupt fncache
59
59
60 Follow the hint to make sure it works
60 Follow the hint to make sure it works
61
61
62 $ hg debugrebuildfncache
62 $ hg debugrebuildfncache
63 adding data/a.i
63 adding data/a.i
64 adding data/a.i.hg/c.i
64 adding data/a.i.hg/c.i
65 adding data/a.i/b.i
65 adding data/a.i/b.i
66 3 items added, 0 removed from fncache
66 3 items added, 0 removed from fncache
67
67
68 $ hg verify
68 $ hg verify
69 checking changesets
69 checking changesets
70 checking manifests
70 checking manifests
71 crosschecking files in changesets and manifests
71 crosschecking files in changesets and manifests
72 checking files
72 checking files
73 checked 3 changesets with 3 changes to 3 files
73 checked 3 changesets with 3 changes to 3 files
74
74
75 $ cd ..
75 $ cd ..
76
76
77 Non store repo:
77 Non store repo:
78
78
79 $ hg --config format.usestore=False init foo
79 $ hg --config format.usestore=False init foo
80 $ cd foo
80 $ cd foo
81 $ mkdir tst.d
81 $ mkdir tst.d
82 $ echo foo > tst.d/foo
82 $ echo foo > tst.d/foo
83 $ hg ci -Amfoo
83 $ hg ci -Amfoo
84 adding tst.d/foo
84 adding tst.d/foo
85 $ find .hg | sort
85 $ find .hg | sort
86 .hg
86 .hg
87 .hg/00changelog.i
87 .hg/00changelog.i
88 .hg/00manifest.i
88 .hg/00manifest.i
89 .hg/cache
89 .hg/cache
90 .hg/cache/branch2-served
90 .hg/cache/branch2-served
91 .hg/cache/checkisexec
92 .hg/cache/checklink
93 .hg/cache/checklink-target
91 .hg/cache/manifestfulltextcache (reporevlogstore !)
94 .hg/cache/manifestfulltextcache (reporevlogstore !)
92 .hg/cache/rbc-names-v1
95 .hg/cache/rbc-names-v1
93 .hg/cache/rbc-revs-v1
96 .hg/cache/rbc-revs-v1
94 .hg/data
97 .hg/data
95 .hg/data/tst.d.hg
98 .hg/data/tst.d.hg
96 .hg/data/tst.d.hg/foo.i
99 .hg/data/tst.d.hg/foo.i
97 .hg/dirstate
100 .hg/dirstate
98 .hg/fsmonitor.state (fsmonitor !)
101 .hg/fsmonitor.state (fsmonitor !)
99 .hg/last-message.txt
102 .hg/last-message.txt
100 .hg/phaseroots
103 .hg/phaseroots
101 .hg/requires
104 .hg/requires
102 .hg/undo
105 .hg/undo
103 .hg/undo.backup.dirstate
106 .hg/undo.backup.dirstate
104 .hg/undo.backupfiles
107 .hg/undo.backupfiles
105 .hg/undo.bookmarks
108 .hg/undo.bookmarks
106 .hg/undo.branch
109 .hg/undo.branch
107 .hg/undo.desc
110 .hg/undo.desc
108 .hg/undo.dirstate
111 .hg/undo.dirstate
109 .hg/undo.phaseroots
112 .hg/undo.phaseroots
110 $ cd ..
113 $ cd ..
111
114
112 Non fncache repo:
115 Non fncache repo:
113
116
114 $ hg --config format.usefncache=False init bar
117 $ hg --config format.usefncache=False init bar
115 $ cd bar
118 $ cd bar
116 $ mkdir tst.d
119 $ mkdir tst.d
117 $ echo foo > tst.d/Foo
120 $ echo foo > tst.d/Foo
118 $ hg ci -Amfoo
121 $ hg ci -Amfoo
119 adding tst.d/Foo
122 adding tst.d/Foo
120 $ find .hg | sort
123 $ find .hg | sort
121 .hg
124 .hg
122 .hg/00changelog.i
125 .hg/00changelog.i
123 .hg/cache
126 .hg/cache
124 .hg/cache/branch2-served
127 .hg/cache/branch2-served
128 .hg/cache/checkisexec
129 .hg/cache/checklink
130 .hg/cache/checklink-target
125 .hg/cache/manifestfulltextcache (reporevlogstore !)
131 .hg/cache/manifestfulltextcache (reporevlogstore !)
126 .hg/cache/rbc-names-v1
132 .hg/cache/rbc-names-v1
127 .hg/cache/rbc-revs-v1
133 .hg/cache/rbc-revs-v1
128 .hg/dirstate
134 .hg/dirstate
129 .hg/fsmonitor.state (fsmonitor !)
135 .hg/fsmonitor.state (fsmonitor !)
130 .hg/last-message.txt
136 .hg/last-message.txt
131 .hg/requires
137 .hg/requires
132 .hg/store
138 .hg/store
133 .hg/store/00changelog.i
139 .hg/store/00changelog.i
134 .hg/store/00manifest.i
140 .hg/store/00manifest.i
135 .hg/store/data
141 .hg/store/data
136 .hg/store/data/tst.d.hg
142 .hg/store/data/tst.d.hg
137 .hg/store/data/tst.d.hg/_foo.i
143 .hg/store/data/tst.d.hg/_foo.i
138 .hg/store/phaseroots
144 .hg/store/phaseroots
139 .hg/store/undo
145 .hg/store/undo
140 .hg/store/undo.backupfiles
146 .hg/store/undo.backupfiles
141 .hg/store/undo.phaseroots
147 .hg/store/undo.phaseroots
142 .hg/undo.backup.dirstate
148 .hg/undo.backup.dirstate
143 .hg/undo.bookmarks
149 .hg/undo.bookmarks
144 .hg/undo.branch
150 .hg/undo.branch
145 .hg/undo.desc
151 .hg/undo.desc
146 .hg/undo.dirstate
152 .hg/undo.dirstate
147 $ cd ..
153 $ cd ..
148
154
149 Encoding of reserved / long paths in the store
155 Encoding of reserved / long paths in the store
150
156
151 $ hg init r2
157 $ hg init r2
152 $ cd r2
158 $ cd r2
153 $ cat <<EOF > .hg/hgrc
159 $ cat <<EOF > .hg/hgrc
154 > [ui]
160 > [ui]
155 > portablefilenames = ignore
161 > portablefilenames = ignore
156 > EOF
162 > EOF
157
163
158 $ hg import -q --bypass - <<EOF
164 $ hg import -q --bypass - <<EOF
159 > # HG changeset patch
165 > # HG changeset patch
160 > # User test
166 > # User test
161 > # Date 0 0
167 > # Date 0 0
162 > # Node ID 1c7a2f7cb77be1a0def34e4c7cabc562ad98fbd7
168 > # Node ID 1c7a2f7cb77be1a0def34e4c7cabc562ad98fbd7
163 > # Parent 0000000000000000000000000000000000000000
169 > # Parent 0000000000000000000000000000000000000000
164 > 1
170 > 1
165 >
171 >
166 > diff --git a/12345678/12345678/12345678/12345678/12345678/12345678/12345678/12345/xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-123456789-12.3456789-12345-ABCDEFGHIJKLMNOPRSTUVWXYZ-abcdefghjiklmnopqrstuvwxyz b/12345678/12345678/12345678/12345678/12345678/12345678/12345678/12345/xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-123456789-12.3456789-12345-ABCDEFGHIJKLMNOPRSTUVWXYZ-abcdefghjiklmnopqrstuvwxyz
172 > diff --git a/12345678/12345678/12345678/12345678/12345678/12345678/12345678/12345/xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-123456789-12.3456789-12345-ABCDEFGHIJKLMNOPRSTUVWXYZ-abcdefghjiklmnopqrstuvwxyz b/12345678/12345678/12345678/12345678/12345678/12345678/12345678/12345/xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-123456789-12.3456789-12345-ABCDEFGHIJKLMNOPRSTUVWXYZ-abcdefghjiklmnopqrstuvwxyz
167 > new file mode 100644
173 > new file mode 100644
168 > --- /dev/null
174 > --- /dev/null
169 > +++ b/12345678/12345678/12345678/12345678/12345678/12345678/12345678/12345/xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-123456789-12.3456789-12345-ABCDEFGHIJKLMNOPRSTUVWXYZ-abcdefghjiklmnopqrstuvwxyz
175 > +++ b/12345678/12345678/12345678/12345678/12345678/12345678/12345678/12345/xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-123456789-12.3456789-12345-ABCDEFGHIJKLMNOPRSTUVWXYZ-abcdefghjiklmnopqrstuvwxyz
170 > @@ -0,0 +1,1 @@
176 > @@ -0,0 +1,1 @@
171 > +foo
177 > +foo
172 > diff --git a/AUX/SECOND/X.PRN/FOURTH/FI:FTH/SIXTH/SEVENTH/EIGHTH/NINETH/TENTH/ELEVENTH/LOREMIPSUM.TXT b/AUX/SECOND/X.PRN/FOURTH/FI:FTH/SIXTH/SEVENTH/EIGHTH/NINETH/TENTH/ELEVENTH/LOREMIPSUM.TXT
178 > diff --git a/AUX/SECOND/X.PRN/FOURTH/FI:FTH/SIXTH/SEVENTH/EIGHTH/NINETH/TENTH/ELEVENTH/LOREMIPSUM.TXT b/AUX/SECOND/X.PRN/FOURTH/FI:FTH/SIXTH/SEVENTH/EIGHTH/NINETH/TENTH/ELEVENTH/LOREMIPSUM.TXT
173 > new file mode 100644
179 > new file mode 100644
174 > --- /dev/null
180 > --- /dev/null
175 > +++ b/AUX/SECOND/X.PRN/FOURTH/FI:FTH/SIXTH/SEVENTH/EIGHTH/NINETH/TENTH/ELEVENTH/LOREMIPSUM.TXT
181 > +++ b/AUX/SECOND/X.PRN/FOURTH/FI:FTH/SIXTH/SEVENTH/EIGHTH/NINETH/TENTH/ELEVENTH/LOREMIPSUM.TXT
176 > @@ -0,0 +1,1 @@
182 > @@ -0,0 +1,1 @@
177 > +foo
183 > +foo
178 > diff --git a/Project Planning/Resources/AnotherLongDirectoryName/Followedbyanother/AndAnother/AndThenAnExtremelyLongFileName.txt b/Project Planning/Resources/AnotherLongDirectoryName/Followedbyanother/AndAnother/AndThenAnExtremelyLongFileName.txt
184 > diff --git a/Project Planning/Resources/AnotherLongDirectoryName/Followedbyanother/AndAnother/AndThenAnExtremelyLongFileName.txt b/Project Planning/Resources/AnotherLongDirectoryName/Followedbyanother/AndAnother/AndThenAnExtremelyLongFileName.txt
179 > new file mode 100644
185 > new file mode 100644
180 > --- /dev/null
186 > --- /dev/null
181 > +++ b/Project Planning/Resources/AnotherLongDirectoryName/Followedbyanother/AndAnother/AndThenAnExtremelyLongFileName.txt
187 > +++ b/Project Planning/Resources/AnotherLongDirectoryName/Followedbyanother/AndAnother/AndThenAnExtremelyLongFileName.txt
182 > @@ -0,0 +1,1 @@
188 > @@ -0,0 +1,1 @@
183 > +foo
189 > +foo
184 > diff --git a/bla.aux/prn/PRN/lpt/com3/nul/coma/foo.NUL/normal.c b/bla.aux/prn/PRN/lpt/com3/nul/coma/foo.NUL/normal.c
190 > diff --git a/bla.aux/prn/PRN/lpt/com3/nul/coma/foo.NUL/normal.c b/bla.aux/prn/PRN/lpt/com3/nul/coma/foo.NUL/normal.c
185 > new file mode 100644
191 > new file mode 100644
186 > --- /dev/null
192 > --- /dev/null
187 > +++ b/bla.aux/prn/PRN/lpt/com3/nul/coma/foo.NUL/normal.c
193 > +++ b/bla.aux/prn/PRN/lpt/com3/nul/coma/foo.NUL/normal.c
188 > @@ -0,0 +1,1 @@
194 > @@ -0,0 +1,1 @@
189 > +foo
195 > +foo
190 > diff --git a/enterprise/openesbaddons/contrib-imola/corba-bc/netbeansplugin/wsdlExtension/src/main/java/META-INF/services/org.netbeans.modules.xml.wsdl.bindingsupport.spi.ExtensibilityElementTemplateProvider b/enterprise/openesbaddons/contrib-imola/corba-bc/netbeansplugin/wsdlExtension/src/main/java/META-INF/services/org.netbeans.modules.xml.wsdl.bindingsupport.spi.ExtensibilityElementTemplateProvider
196 > diff --git a/enterprise/openesbaddons/contrib-imola/corba-bc/netbeansplugin/wsdlExtension/src/main/java/META-INF/services/org.netbeans.modules.xml.wsdl.bindingsupport.spi.ExtensibilityElementTemplateProvider b/enterprise/openesbaddons/contrib-imola/corba-bc/netbeansplugin/wsdlExtension/src/main/java/META-INF/services/org.netbeans.modules.xml.wsdl.bindingsupport.spi.ExtensibilityElementTemplateProvider
191 > new file mode 100644
197 > new file mode 100644
192 > --- /dev/null
198 > --- /dev/null
193 > +++ b/enterprise/openesbaddons/contrib-imola/corba-bc/netbeansplugin/wsdlExtension/src/main/java/META-INF/services/org.netbeans.modules.xml.wsdl.bindingsupport.spi.ExtensibilityElementTemplateProvider
199 > +++ b/enterprise/openesbaddons/contrib-imola/corba-bc/netbeansplugin/wsdlExtension/src/main/java/META-INF/services/org.netbeans.modules.xml.wsdl.bindingsupport.spi.ExtensibilityElementTemplateProvider
194 > @@ -0,0 +1,1 @@
200 > @@ -0,0 +1,1 @@
195 > +foo
201 > +foo
196 > EOF
202 > EOF
197
203
198 $ find .hg/store -name *.i | sort
204 $ find .hg/store -name *.i | sort
199 .hg/store/00changelog.i
205 .hg/store/00changelog.i
200 .hg/store/00manifest.i
206 .hg/store/00manifest.i
201 .hg/store/data/bla.aux/pr~6e/_p_r_n/lpt/co~6d3/nu~6c/coma/foo._n_u_l/normal.c.i
207 .hg/store/data/bla.aux/pr~6e/_p_r_n/lpt/co~6d3/nu~6c/coma/foo._n_u_l/normal.c.i
202 .hg/store/dh/12345678/12345678/12345678/12345678/12345678/12345678/12345678/12345/xxxxxx168e07b38e65eff86ab579afaaa8e30bfbe0f35f.i
208 .hg/store/dh/12345678/12345678/12345678/12345678/12345678/12345678/12345678/12345/xxxxxx168e07b38e65eff86ab579afaaa8e30bfbe0f35f.i
203 .hg/store/dh/au~78/second/x.prn/fourth/fi~3afth/sixth/seventh/eighth/nineth/tenth/loremia20419e358ddff1bf8751e38288aff1d7c32ec05.i
209 .hg/store/dh/au~78/second/x.prn/fourth/fi~3afth/sixth/seventh/eighth/nineth/tenth/loremia20419e358ddff1bf8751e38288aff1d7c32ec05.i
204 .hg/store/dh/enterpri/openesba/contrib-/corba-bc/netbeans/wsdlexte/src/main/java/org.net7018f27961fdf338a598a40c4683429e7ffb9743.i
210 .hg/store/dh/enterpri/openesba/contrib-/corba-bc/netbeans/wsdlexte/src/main/java/org.net7018f27961fdf338a598a40c4683429e7ffb9743.i
205 .hg/store/dh/project_/resource/anotherl/followed/andanoth/andthenanextremelylongfilename0d8e1f4187c650e2f1fdca9fd90f786bc0976b6b.i
211 .hg/store/dh/project_/resource/anotherl/followed/andanoth/andthenanextremelylongfilename0d8e1f4187c650e2f1fdca9fd90f786bc0976b6b.i
206
212
207 $ cd ..
213 $ cd ..
208
214
209 Aborting lock does not prevent fncache writes
215 Aborting lock does not prevent fncache writes
210
216
211 $ cat > exceptionext.py <<EOF
217 $ cat > exceptionext.py <<EOF
212 > from __future__ import absolute_import
218 > from __future__ import absolute_import
213 > import os
219 > import os
214 > from mercurial import commands, error, extensions
220 > from mercurial import commands, error, extensions
215 >
221 >
216 > def lockexception(orig, vfs, lockname, wait, releasefn, *args, **kwargs):
222 > def lockexception(orig, vfs, lockname, wait, releasefn, *args, **kwargs):
217 > def releasewrap():
223 > def releasewrap():
218 > l.held = False # ensure __del__ is a noop
224 > l.held = False # ensure __del__ is a noop
219 > raise error.Abort("forced lock failure")
225 > raise error.Abort("forced lock failure")
220 > l = orig(vfs, lockname, wait, releasewrap, *args, **kwargs)
226 > l = orig(vfs, lockname, wait, releasewrap, *args, **kwargs)
221 > return l
227 > return l
222 >
228 >
223 > def reposetup(ui, repo):
229 > def reposetup(ui, repo):
224 > extensions.wrapfunction(repo, '_lock', lockexception)
230 > extensions.wrapfunction(repo, '_lock', lockexception)
225 >
231 >
226 > cmdtable = {}
232 > cmdtable = {}
227 >
233 >
228 > # wrap "commit" command to prevent wlock from being '__del__()'-ed
234 > # wrap "commit" command to prevent wlock from being '__del__()'-ed
229 > # at the end of dispatching (for intentional "forced lcok failure")
235 > # at the end of dispatching (for intentional "forced lcok failure")
230 > def commitwrap(orig, ui, repo, *pats, **opts):
236 > def commitwrap(orig, ui, repo, *pats, **opts):
231 > repo = repo.unfiltered() # to use replaced repo._lock certainly
237 > repo = repo.unfiltered() # to use replaced repo._lock certainly
232 > wlock = repo.wlock()
238 > wlock = repo.wlock()
233 > try:
239 > try:
234 > return orig(ui, repo, *pats, **opts)
240 > return orig(ui, repo, *pats, **opts)
235 > finally:
241 > finally:
236 > # multiple 'relase()' is needed for complete releasing wlock,
242 > # multiple 'relase()' is needed for complete releasing wlock,
237 > # because "forced" abort at last releasing store lock
243 > # because "forced" abort at last releasing store lock
238 > # prevents wlock from being released at same 'lockmod.release()'
244 > # prevents wlock from being released at same 'lockmod.release()'
239 > for i in range(wlock.held):
245 > for i in range(wlock.held):
240 > wlock.release()
246 > wlock.release()
241 >
247 >
242 > def extsetup(ui):
248 > def extsetup(ui):
243 > extensions.wrapcommand(commands.table, b"commit", commitwrap)
249 > extensions.wrapcommand(commands.table, b"commit", commitwrap)
244 > EOF
250 > EOF
245 $ extpath=`pwd`/exceptionext.py
251 $ extpath=`pwd`/exceptionext.py
246 $ hg init fncachetxn
252 $ hg init fncachetxn
247 $ cd fncachetxn
253 $ cd fncachetxn
248 $ printf "[extensions]\nexceptionext=$extpath\n" >> .hg/hgrc
254 $ printf "[extensions]\nexceptionext=$extpath\n" >> .hg/hgrc
249 $ touch y
255 $ touch y
250 $ hg ci -qAm y
256 $ hg ci -qAm y
251 abort: forced lock failure
257 abort: forced lock failure
252 [255]
258 [255]
253 $ cat .hg/store/fncache
259 $ cat .hg/store/fncache
254 data/y.i
260 data/y.i
255
261
256 Aborting transaction prevents fncache change
262 Aborting transaction prevents fncache change
257
263
258 $ cat > ../exceptionext.py <<EOF
264 $ cat > ../exceptionext.py <<EOF
259 > from __future__ import absolute_import
265 > from __future__ import absolute_import
260 > import os
266 > import os
261 > from mercurial import commands, error, extensions, localrepo
267 > from mercurial import commands, error, extensions, localrepo
262 >
268 >
263 > def wrapper(orig, self, *args, **kwargs):
269 > def wrapper(orig, self, *args, **kwargs):
264 > tr = orig(self, *args, **kwargs)
270 > tr = orig(self, *args, **kwargs)
265 > def fail(tr):
271 > def fail(tr):
266 > raise error.Abort(b"forced transaction failure")
272 > raise error.Abort(b"forced transaction failure")
267 > # zzz prefix to ensure it sorted after store.write
273 > # zzz prefix to ensure it sorted after store.write
268 > tr.addfinalize(b'zzz-forcefails', fail)
274 > tr.addfinalize(b'zzz-forcefails', fail)
269 > return tr
275 > return tr
270 >
276 >
271 > def uisetup(ui):
277 > def uisetup(ui):
272 > extensions.wrapfunction(
278 > extensions.wrapfunction(
273 > localrepo.localrepository, b'transaction', wrapper)
279 > localrepo.localrepository, b'transaction', wrapper)
274 >
280 >
275 > cmdtable = {}
281 > cmdtable = {}
276 >
282 >
277 > EOF
283 > EOF
278
284
279 Clean cached version
285 Clean cached version
280 $ rm -f "${extpath}c"
286 $ rm -f "${extpath}c"
281 $ rm -Rf "`dirname $extpath`/__pycache__"
287 $ rm -Rf "`dirname $extpath`/__pycache__"
282
288
283 $ touch z
289 $ touch z
284 $ hg ci -qAm z
290 $ hg ci -qAm z
285 transaction abort!
291 transaction abort!
286 rollback completed
292 rollback completed
287 abort: forced transaction failure
293 abort: forced transaction failure
288 [255]
294 [255]
289 $ cat .hg/store/fncache
295 $ cat .hg/store/fncache
290 data/y.i
296 data/y.i
291
297
292 Aborted transactions can be recovered later
298 Aborted transactions can be recovered later
293
299
294 $ cat > ../exceptionext.py <<EOF
300 $ cat > ../exceptionext.py <<EOF
295 > from __future__ import absolute_import
301 > from __future__ import absolute_import
296 > import os
302 > import os
297 > from mercurial import (
303 > from mercurial import (
298 > commands,
304 > commands,
299 > error,
305 > error,
300 > extensions,
306 > extensions,
301 > localrepo,
307 > localrepo,
302 > transaction,
308 > transaction,
303 > )
309 > )
304 >
310 >
305 > def trwrapper(orig, self, *args, **kwargs):
311 > def trwrapper(orig, self, *args, **kwargs):
306 > tr = orig(self, *args, **kwargs)
312 > tr = orig(self, *args, **kwargs)
307 > def fail(tr):
313 > def fail(tr):
308 > raise error.Abort(b"forced transaction failure")
314 > raise error.Abort(b"forced transaction failure")
309 > # zzz prefix to ensure it sorted after store.write
315 > # zzz prefix to ensure it sorted after store.write
310 > tr.addfinalize(b'zzz-forcefails', fail)
316 > tr.addfinalize(b'zzz-forcefails', fail)
311 > return tr
317 > return tr
312 >
318 >
313 > def abortwrapper(orig, self, *args, **kwargs):
319 > def abortwrapper(orig, self, *args, **kwargs):
314 > raise error.Abort(b"forced transaction failure")
320 > raise error.Abort(b"forced transaction failure")
315 >
321 >
316 > def uisetup(ui):
322 > def uisetup(ui):
317 > extensions.wrapfunction(localrepo.localrepository, 'transaction',
323 > extensions.wrapfunction(localrepo.localrepository, 'transaction',
318 > trwrapper)
324 > trwrapper)
319 > extensions.wrapfunction(transaction.transaction, '_abort',
325 > extensions.wrapfunction(transaction.transaction, '_abort',
320 > abortwrapper)
326 > abortwrapper)
321 >
327 >
322 > cmdtable = {}
328 > cmdtable = {}
323 >
329 >
324 > EOF
330 > EOF
325
331
326 Clean cached versions
332 Clean cached versions
327 $ rm -f "${extpath}c"
333 $ rm -f "${extpath}c"
328 $ rm -Rf "`dirname $extpath`/__pycache__"
334 $ rm -Rf "`dirname $extpath`/__pycache__"
329
335
330 $ hg up -q 1
336 $ hg up -q 1
331 $ touch z
337 $ touch z
332 $ hg ci -qAm z 2>/dev/null
338 $ hg ci -qAm z 2>/dev/null
333 [255]
339 [255]
334 $ cat .hg/store/fncache | sort
340 $ cat .hg/store/fncache | sort
335 data/y.i
341 data/y.i
336 data/z.i
342 data/z.i
337 $ hg recover
343 $ hg recover
338 rolling back interrupted transaction
344 rolling back interrupted transaction
339 checking changesets
345 checking changesets
340 checking manifests
346 checking manifests
341 crosschecking files in changesets and manifests
347 crosschecking files in changesets and manifests
342 checking files
348 checking files
343 checked 1 changesets with 1 changes to 1 files
349 checked 1 changesets with 1 changes to 1 files
344 $ cat .hg/store/fncache
350 $ cat .hg/store/fncache
345 data/y.i
351 data/y.i
346
352
347 $ cd ..
353 $ cd ..
348
354
349 debugrebuildfncache does nothing unless repo has fncache requirement
355 debugrebuildfncache does nothing unless repo has fncache requirement
350
356
351 $ hg --config format.usefncache=false init nofncache
357 $ hg --config format.usefncache=false init nofncache
352 $ cd nofncache
358 $ cd nofncache
353 $ hg debugrebuildfncache
359 $ hg debugrebuildfncache
354 (not rebuilding fncache because repository does not support fncache)
360 (not rebuilding fncache because repository does not support fncache)
355
361
356 $ cd ..
362 $ cd ..
357
363
358 debugrebuildfncache works on empty repository
364 debugrebuildfncache works on empty repository
359
365
360 $ hg init empty
366 $ hg init empty
361 $ cd empty
367 $ cd empty
362 $ hg debugrebuildfncache
368 $ hg debugrebuildfncache
363 fncache already up to date
369 fncache already up to date
364 $ cd ..
370 $ cd ..
365
371
366 debugrebuildfncache on an up to date repository no-ops
372 debugrebuildfncache on an up to date repository no-ops
367
373
368 $ hg init repo
374 $ hg init repo
369 $ cd repo
375 $ cd repo
370 $ echo initial > foo
376 $ echo initial > foo
371 $ echo initial > .bar
377 $ echo initial > .bar
372 $ hg commit -A -m initial
378 $ hg commit -A -m initial
373 adding .bar
379 adding .bar
374 adding foo
380 adding foo
375
381
376 $ cat .hg/store/fncache | sort
382 $ cat .hg/store/fncache | sort
377 data/.bar.i
383 data/.bar.i
378 data/foo.i
384 data/foo.i
379
385
380 $ hg debugrebuildfncache
386 $ hg debugrebuildfncache
381 fncache already up to date
387 fncache already up to date
382
388
383 debugrebuildfncache restores deleted fncache file
389 debugrebuildfncache restores deleted fncache file
384
390
385 $ rm -f .hg/store/fncache
391 $ rm -f .hg/store/fncache
386 $ hg debugrebuildfncache
392 $ hg debugrebuildfncache
387 adding data/.bar.i
393 adding data/.bar.i
388 adding data/foo.i
394 adding data/foo.i
389 2 items added, 0 removed from fncache
395 2 items added, 0 removed from fncache
390
396
391 $ cat .hg/store/fncache | sort
397 $ cat .hg/store/fncache | sort
392 data/.bar.i
398 data/.bar.i
393 data/foo.i
399 data/foo.i
394
400
395 Rebuild after rebuild should no-op
401 Rebuild after rebuild should no-op
396
402
397 $ hg debugrebuildfncache
403 $ hg debugrebuildfncache
398 fncache already up to date
404 fncache already up to date
399
405
400 A single missing file should get restored, an extra file should be removed
406 A single missing file should get restored, an extra file should be removed
401
407
402 $ cat > .hg/store/fncache << EOF
408 $ cat > .hg/store/fncache << EOF
403 > data/foo.i
409 > data/foo.i
404 > data/bad-entry.i
410 > data/bad-entry.i
405 > EOF
411 > EOF
406
412
407 $ hg debugrebuildfncache
413 $ hg debugrebuildfncache
408 removing data/bad-entry.i
414 removing data/bad-entry.i
409 adding data/.bar.i
415 adding data/.bar.i
410 1 items added, 1 removed from fncache
416 1 items added, 1 removed from fncache
411
417
412 $ cat .hg/store/fncache | sort
418 $ cat .hg/store/fncache | sort
413 data/.bar.i
419 data/.bar.i
414 data/foo.i
420 data/foo.i
415
421
416 $ cd ..
422 $ cd ..
417
423
418 Try a simple variation without dotencode to ensure fncache is ignorant of encoding
424 Try a simple variation without dotencode to ensure fncache is ignorant of encoding
419
425
420 $ hg --config format.dotencode=false init nodotencode
426 $ hg --config format.dotencode=false init nodotencode
421 $ cd nodotencode
427 $ cd nodotencode
422 $ echo initial > foo
428 $ echo initial > foo
423 $ echo initial > .bar
429 $ echo initial > .bar
424 $ hg commit -A -m initial
430 $ hg commit -A -m initial
425 adding .bar
431 adding .bar
426 adding foo
432 adding foo
427
433
428 $ cat .hg/store/fncache | sort
434 $ cat .hg/store/fncache | sort
429 data/.bar.i
435 data/.bar.i
430 data/foo.i
436 data/foo.i
431
437
432 $ rm .hg/store/fncache
438 $ rm .hg/store/fncache
433 $ hg debugrebuildfncache
439 $ hg debugrebuildfncache
434 adding data/.bar.i
440 adding data/.bar.i
435 adding data/foo.i
441 adding data/foo.i
436 2 items added, 0 removed from fncache
442 2 items added, 0 removed from fncache
437
443
438 $ cat .hg/store/fncache | sort
444 $ cat .hg/store/fncache | sort
439 data/.bar.i
445 data/.bar.i
440 data/foo.i
446 data/foo.i
441
447
442 $ cd ..
448 $ cd ..
443
449
444 In repositories that have accumulated a large number of files over time, the
450 In repositories that have accumulated a large number of files over time, the
445 fncache file is going to be large. If we possibly can avoid loading it, so much the better.
451 fncache file is going to be large. If we possibly can avoid loading it, so much the better.
446 The cache should not loaded when committing changes to existing files, or when unbundling
452 The cache should not loaded when committing changes to existing files, or when unbundling
447 changesets that only contain changes to existing files:
453 changesets that only contain changes to existing files:
448
454
449 $ cat > fncacheloadwarn.py << EOF
455 $ cat > fncacheloadwarn.py << EOF
450 > from __future__ import absolute_import
456 > from __future__ import absolute_import
451 > from mercurial import extensions, localrepo
457 > from mercurial import extensions, localrepo
452 >
458 >
453 > def extsetup(ui):
459 > def extsetup(ui):
454 > def wrapstore(orig, requirements, *args):
460 > def wrapstore(orig, requirements, *args):
455 > store = orig(requirements, *args)
461 > store = orig(requirements, *args)
456 > if b'store' in requirements and b'fncache' in requirements:
462 > if b'store' in requirements and b'fncache' in requirements:
457 > instrumentfncachestore(store, ui)
463 > instrumentfncachestore(store, ui)
458 > return store
464 > return store
459 > extensions.wrapfunction(localrepo, 'makestore', wrapstore)
465 > extensions.wrapfunction(localrepo, 'makestore', wrapstore)
460 >
466 >
461 > def instrumentfncachestore(fncachestore, ui):
467 > def instrumentfncachestore(fncachestore, ui):
462 > class instrumentedfncache(type(fncachestore.fncache)):
468 > class instrumentedfncache(type(fncachestore.fncache)):
463 > def _load(self):
469 > def _load(self):
464 > ui.warn(b'fncache load triggered!\n')
470 > ui.warn(b'fncache load triggered!\n')
465 > super(instrumentedfncache, self)._load()
471 > super(instrumentedfncache, self)._load()
466 > fncachestore.fncache.__class__ = instrumentedfncache
472 > fncachestore.fncache.__class__ = instrumentedfncache
467 > EOF
473 > EOF
468
474
469 $ fncachextpath=`pwd`/fncacheloadwarn.py
475 $ fncachextpath=`pwd`/fncacheloadwarn.py
470 $ hg init nofncacheload
476 $ hg init nofncacheload
471 $ cd nofncacheload
477 $ cd nofncacheload
472 $ printf "[extensions]\nfncacheloadwarn=$fncachextpath\n" >> .hg/hgrc
478 $ printf "[extensions]\nfncacheloadwarn=$fncachextpath\n" >> .hg/hgrc
473
479
474 A new file should trigger a load, as we'd want to update the fncache set in that case:
480 A new file should trigger a load, as we'd want to update the fncache set in that case:
475
481
476 $ touch foo
482 $ touch foo
477 $ hg ci -qAm foo
483 $ hg ci -qAm foo
478 fncache load triggered!
484 fncache load triggered!
479
485
480 But modifying that file should not:
486 But modifying that file should not:
481
487
482 $ echo bar >> foo
488 $ echo bar >> foo
483 $ hg ci -qm foo
489 $ hg ci -qm foo
484
490
485 If a transaction has been aborted, the zero-size truncated index file will
491 If a transaction has been aborted, the zero-size truncated index file will
486 not prevent the fncache from being loaded; rather than actually abort
492 not prevent the fncache from being loaded; rather than actually abort
487 a transaction, we simulate the situation by creating a zero-size index file:
493 a transaction, we simulate the situation by creating a zero-size index file:
488
494
489 $ touch .hg/store/data/bar.i
495 $ touch .hg/store/data/bar.i
490 $ touch bar
496 $ touch bar
491 $ hg ci -qAm bar
497 $ hg ci -qAm bar
492 fncache load triggered!
498 fncache load triggered!
493
499
494 Unbundling should follow the same rules; existing files should not cause a load:
500 Unbundling should follow the same rules; existing files should not cause a load:
495
501
496 $ hg clone -q . tobundle
502 $ hg clone -q . tobundle
497 $ echo 'new line' > tobundle/bar
503 $ echo 'new line' > tobundle/bar
498 $ hg -R tobundle ci -qm bar
504 $ hg -R tobundle ci -qm bar
499 $ hg -R tobundle bundle -q barupdated.hg
505 $ hg -R tobundle bundle -q barupdated.hg
500 $ hg unbundle -q barupdated.hg
506 $ hg unbundle -q barupdated.hg
501
507
502 but adding new files should:
508 but adding new files should:
503
509
504 $ touch tobundle/newfile
510 $ touch tobundle/newfile
505 $ hg -R tobundle ci -qAm newfile
511 $ hg -R tobundle ci -qAm newfile
506 $ hg -R tobundle bundle -q newfile.hg
512 $ hg -R tobundle bundle -q newfile.hg
507 $ hg unbundle -q newfile.hg
513 $ hg unbundle -q newfile.hg
508 fncache load triggered!
514 fncache load triggered!
509
515
510 $ cd ..
516 $ cd ..
@@ -1,173 +1,176
1 #require unix-permissions
1 #require unix-permissions
2
2
3 test that new files created in .hg inherit the permissions from .hg/store
3 test that new files created in .hg inherit the permissions from .hg/store
4
4
5 $ mkdir dir
5 $ mkdir dir
6
6
7 just in case somebody has a strange $TMPDIR
7 just in case somebody has a strange $TMPDIR
8
8
9 $ chmod g-s dir
9 $ chmod g-s dir
10 $ cd dir
10 $ cd dir
11
11
12 $ cat >printmodes.py <<EOF
12 $ cat >printmodes.py <<EOF
13 > from __future__ import absolute_import, print_function
13 > from __future__ import absolute_import, print_function
14 > import os
14 > import os
15 > import sys
15 > import sys
16 >
16 >
17 > allnames = []
17 > allnames = []
18 > isdir = {}
18 > isdir = {}
19 > for root, dirs, files in os.walk(sys.argv[1]):
19 > for root, dirs, files in os.walk(sys.argv[1]):
20 > for d in dirs:
20 > for d in dirs:
21 > name = os.path.join(root, d)
21 > name = os.path.join(root, d)
22 > isdir[name] = 1
22 > isdir[name] = 1
23 > allnames.append(name)
23 > allnames.append(name)
24 > for f in files:
24 > for f in files:
25 > name = os.path.join(root, f)
25 > name = os.path.join(root, f)
26 > allnames.append(name)
26 > allnames.append(name)
27 > allnames.sort()
27 > allnames.sort()
28 > for name in allnames:
28 > for name in allnames:
29 > suffix = name in isdir and '/' or ''
29 > suffix = name in isdir and '/' or ''
30 > print('%05o %s%s' % (os.lstat(name).st_mode & 0o7777, name, suffix))
30 > print('%05o %s%s' % (os.lstat(name).st_mode & 0o7777, name, suffix))
31 > EOF
31 > EOF
32
32
33 $ cat >mode.py <<EOF
33 $ cat >mode.py <<EOF
34 > from __future__ import absolute_import, print_function
34 > from __future__ import absolute_import, print_function
35 > import os
35 > import os
36 > import sys
36 > import sys
37 > print('%05o' % os.lstat(sys.argv[1]).st_mode)
37 > print('%05o' % os.lstat(sys.argv[1]).st_mode)
38 > EOF
38 > EOF
39
39
40 $ umask 077
40 $ umask 077
41
41
42 $ hg init repo
42 $ hg init repo
43 $ cd repo
43 $ cd repo
44
44
45 $ chmod 0770 .hg/store
45 $ chmod 0770 .hg/store
46
46
47 before commit
47 before commit
48 store can be written by the group, other files cannot
48 store can be written by the group, other files cannot
49 store is setgid
49 store is setgid
50
50
51 $ "$PYTHON" ../printmodes.py .
51 $ "$PYTHON" ../printmodes.py .
52 00700 ./.hg/
52 00700 ./.hg/
53 00600 ./.hg/00changelog.i
53 00600 ./.hg/00changelog.i
54 00600 ./.hg/requires
54 00600 ./.hg/requires
55 00770 ./.hg/store/
55 00770 ./.hg/store/
56
56
57 $ mkdir dir
57 $ mkdir dir
58 $ touch foo dir/bar
58 $ touch foo dir/bar
59 $ hg ci -qAm 'add files'
59 $ hg ci -qAm 'add files'
60
60
61 after commit
61 after commit
62 working dir files can only be written by the owner
62 working dir files can only be written by the owner
63 files created in .hg can be written by the group
63 files created in .hg can be written by the group
64 (in particular, store/**, dirstate, branch cache file, undo files)
64 (in particular, store/**, dirstate, branch cache file, undo files)
65 new directories are setgid
65 new directories are setgid
66
66
67 $ "$PYTHON" ../printmodes.py .
67 $ "$PYTHON" ../printmodes.py .
68 00700 ./.hg/
68 00700 ./.hg/
69 00600 ./.hg/00changelog.i
69 00600 ./.hg/00changelog.i
70 00770 ./.hg/cache/
70 00770 ./.hg/cache/
71 00660 ./.hg/cache/branch2-served
71 00660 ./.hg/cache/branch2-served
72 00711 ./.hg/cache/checkisexec
73 00777 ./.hg/cache/checklink
74 00600 ./.hg/cache/checklink-target
72 00660 ./.hg/cache/manifestfulltextcache (reporevlogstore !)
75 00660 ./.hg/cache/manifestfulltextcache (reporevlogstore !)
73 00660 ./.hg/cache/rbc-names-v1
76 00660 ./.hg/cache/rbc-names-v1
74 00660 ./.hg/cache/rbc-revs-v1
77 00660 ./.hg/cache/rbc-revs-v1
75 00660 ./.hg/dirstate
78 00660 ./.hg/dirstate
76 00660 ./.hg/fsmonitor.state (fsmonitor !)
79 00660 ./.hg/fsmonitor.state (fsmonitor !)
77 00660 ./.hg/last-message.txt
80 00660 ./.hg/last-message.txt
78 00600 ./.hg/requires
81 00600 ./.hg/requires
79 00770 ./.hg/store/
82 00770 ./.hg/store/
80 00660 ./.hg/store/00changelog.i
83 00660 ./.hg/store/00changelog.i
81 00660 ./.hg/store/00manifest.i
84 00660 ./.hg/store/00manifest.i
82 00770 ./.hg/store/data/
85 00770 ./.hg/store/data/
83 00770 ./.hg/store/data/dir/
86 00770 ./.hg/store/data/dir/
84 00660 ./.hg/store/data/dir/bar.i (reporevlogstore !)
87 00660 ./.hg/store/data/dir/bar.i (reporevlogstore !)
85 00660 ./.hg/store/data/foo.i (reporevlogstore !)
88 00660 ./.hg/store/data/foo.i (reporevlogstore !)
86 00770 ./.hg/store/data/dir/bar/ (reposimplestore !)
89 00770 ./.hg/store/data/dir/bar/ (reposimplestore !)
87 00660 ./.hg/store/data/dir/bar/b80de5d138758541c5f05265ad144ab9fa86d1db (reposimplestore !)
90 00660 ./.hg/store/data/dir/bar/b80de5d138758541c5f05265ad144ab9fa86d1db (reposimplestore !)
88 00660 ./.hg/store/data/dir/bar/index (reposimplestore !)
91 00660 ./.hg/store/data/dir/bar/index (reposimplestore !)
89 00770 ./.hg/store/data/foo/ (reposimplestore !)
92 00770 ./.hg/store/data/foo/ (reposimplestore !)
90 00660 ./.hg/store/data/foo/b80de5d138758541c5f05265ad144ab9fa86d1db (reposimplestore !)
93 00660 ./.hg/store/data/foo/b80de5d138758541c5f05265ad144ab9fa86d1db (reposimplestore !)
91 00660 ./.hg/store/data/foo/index (reposimplestore !)
94 00660 ./.hg/store/data/foo/index (reposimplestore !)
92 00660 ./.hg/store/fncache (repofncache !)
95 00660 ./.hg/store/fncache (repofncache !)
93 00660 ./.hg/store/phaseroots
96 00660 ./.hg/store/phaseroots
94 00660 ./.hg/store/undo
97 00660 ./.hg/store/undo
95 00660 ./.hg/store/undo.backupfiles
98 00660 ./.hg/store/undo.backupfiles
96 00660 ./.hg/store/undo.phaseroots
99 00660 ./.hg/store/undo.phaseroots
97 00660 ./.hg/undo.backup.dirstate
100 00660 ./.hg/undo.backup.dirstate
98 00660 ./.hg/undo.bookmarks
101 00660 ./.hg/undo.bookmarks
99 00660 ./.hg/undo.branch
102 00660 ./.hg/undo.branch
100 00660 ./.hg/undo.desc
103 00660 ./.hg/undo.desc
101 00660 ./.hg/undo.dirstate
104 00660 ./.hg/undo.dirstate
102 00700 ./dir/
105 00700 ./dir/
103 00600 ./dir/bar
106 00600 ./dir/bar
104 00600 ./foo
107 00600 ./foo
105
108
106 $ umask 007
109 $ umask 007
107 $ hg init ../push
110 $ hg init ../push
108
111
109 before push
112 before push
110 group can write everything
113 group can write everything
111
114
112 $ "$PYTHON" ../printmodes.py ../push
115 $ "$PYTHON" ../printmodes.py ../push
113 00770 ../push/.hg/
116 00770 ../push/.hg/
114 00660 ../push/.hg/00changelog.i
117 00660 ../push/.hg/00changelog.i
115 00660 ../push/.hg/requires
118 00660 ../push/.hg/requires
116 00770 ../push/.hg/store/
119 00770 ../push/.hg/store/
117
120
118 $ umask 077
121 $ umask 077
119 $ hg -q push ../push
122 $ hg -q push ../push
120
123
121 after push
124 after push
122 group can still write everything
125 group can still write everything
123
126
124 $ "$PYTHON" ../printmodes.py ../push
127 $ "$PYTHON" ../printmodes.py ../push
125 00770 ../push/.hg/
128 00770 ../push/.hg/
126 00660 ../push/.hg/00changelog.i
129 00660 ../push/.hg/00changelog.i
127 00770 ../push/.hg/cache/
130 00770 ../push/.hg/cache/
128 00660 ../push/.hg/cache/branch2-base
131 00660 ../push/.hg/cache/branch2-base
129 00660 ../push/.hg/dirstate
132 00660 ../push/.hg/dirstate
130 00660 ../push/.hg/requires
133 00660 ../push/.hg/requires
131 00770 ../push/.hg/store/
134 00770 ../push/.hg/store/
132 00660 ../push/.hg/store/00changelog.i
135 00660 ../push/.hg/store/00changelog.i
133 00660 ../push/.hg/store/00manifest.i
136 00660 ../push/.hg/store/00manifest.i
134 00770 ../push/.hg/store/data/
137 00770 ../push/.hg/store/data/
135 00770 ../push/.hg/store/data/dir/
138 00770 ../push/.hg/store/data/dir/
136 00660 ../push/.hg/store/data/dir/bar.i (reporevlogstore !)
139 00660 ../push/.hg/store/data/dir/bar.i (reporevlogstore !)
137 00660 ../push/.hg/store/data/foo.i (reporevlogstore !)
140 00660 ../push/.hg/store/data/foo.i (reporevlogstore !)
138 00770 ../push/.hg/store/data/dir/bar/ (reposimplestore !)
141 00770 ../push/.hg/store/data/dir/bar/ (reposimplestore !)
139 00660 ../push/.hg/store/data/dir/bar/b80de5d138758541c5f05265ad144ab9fa86d1db (reposimplestore !)
142 00660 ../push/.hg/store/data/dir/bar/b80de5d138758541c5f05265ad144ab9fa86d1db (reposimplestore !)
140 00660 ../push/.hg/store/data/dir/bar/index (reposimplestore !)
143 00660 ../push/.hg/store/data/dir/bar/index (reposimplestore !)
141 00770 ../push/.hg/store/data/foo/ (reposimplestore !)
144 00770 ../push/.hg/store/data/foo/ (reposimplestore !)
142 00660 ../push/.hg/store/data/foo/b80de5d138758541c5f05265ad144ab9fa86d1db (reposimplestore !)
145 00660 ../push/.hg/store/data/foo/b80de5d138758541c5f05265ad144ab9fa86d1db (reposimplestore !)
143 00660 ../push/.hg/store/data/foo/index (reposimplestore !)
146 00660 ../push/.hg/store/data/foo/index (reposimplestore !)
144 00660 ../push/.hg/store/fncache (repofncache !)
147 00660 ../push/.hg/store/fncache (repofncache !)
145 00660 ../push/.hg/store/undo
148 00660 ../push/.hg/store/undo
146 00660 ../push/.hg/store/undo.backupfiles
149 00660 ../push/.hg/store/undo.backupfiles
147 00660 ../push/.hg/store/undo.phaseroots
150 00660 ../push/.hg/store/undo.phaseroots
148 00660 ../push/.hg/undo.bookmarks
151 00660 ../push/.hg/undo.bookmarks
149 00660 ../push/.hg/undo.branch
152 00660 ../push/.hg/undo.branch
150 00660 ../push/.hg/undo.desc
153 00660 ../push/.hg/undo.desc
151 00660 ../push/.hg/undo.dirstate
154 00660 ../push/.hg/undo.dirstate
152
155
153
156
154 Test that we don't lose the setgid bit when we call chmod.
157 Test that we don't lose the setgid bit when we call chmod.
155 Not all systems support setgid directories (e.g. HFS+), so
158 Not all systems support setgid directories (e.g. HFS+), so
156 just check that directories have the same mode.
159 just check that directories have the same mode.
157
160
158 $ cd ..
161 $ cd ..
159 $ hg init setgid
162 $ hg init setgid
160 $ cd setgid
163 $ cd setgid
161 $ chmod g+rwx .hg/store
164 $ chmod g+rwx .hg/store
162 $ chmod g+s .hg/store 2> /dev/null || true
165 $ chmod g+s .hg/store 2> /dev/null || true
163 $ mkdir dir
166 $ mkdir dir
164 $ touch dir/file
167 $ touch dir/file
165 $ hg ci -qAm 'add dir/file'
168 $ hg ci -qAm 'add dir/file'
166 $ storemode=`"$PYTHON" ../mode.py .hg/store`
169 $ storemode=`"$PYTHON" ../mode.py .hg/store`
167 $ dirmode=`"$PYTHON" ../mode.py .hg/store/data/dir`
170 $ dirmode=`"$PYTHON" ../mode.py .hg/store/data/dir`
168 $ if [ "$storemode" != "$dirmode" ]; then
171 $ if [ "$storemode" != "$dirmode" ]; then
169 > echo "$storemode != $dirmode"
172 > echo "$storemode != $dirmode"
170 > fi
173 > fi
171 $ cd ..
174 $ cd ..
172
175
173 $ cd .. # g-s dir
176 $ cd .. # g-s dir
@@ -1,468 +1,473
1 $ echo "[extensions]" >> $HGRCPATH
1 $ echo "[extensions]" >> $HGRCPATH
2 $ echo "share = " >> $HGRCPATH
2 $ echo "share = " >> $HGRCPATH
3
3
4 prepare repo1
4 prepare repo1
5
5
6 $ hg init repo1
6 $ hg init repo1
7 $ cd repo1
7 $ cd repo1
8 $ echo a > a
8 $ echo a > a
9 $ hg commit -A -m'init'
9 $ hg commit -A -m'init'
10 adding a
10 adding a
11
11
12 share it
12 share it
13
13
14 $ cd ..
14 $ cd ..
15 $ hg share repo1 repo2
15 $ hg share repo1 repo2
16 updating working directory
16 updating working directory
17 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
17 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
18
18
19 share shouldn't have a store dir
19 share shouldn't have a store dir
20
20
21 $ cd repo2
21 $ cd repo2
22 $ test -d .hg/store
22 $ test -d .hg/store
23 [1]
23 [1]
24
24
25 share shouldn't have a cache dir, original repo should
25 share shouldn't have a full cache dir, original repo should
26
26
27 $ hg branches
27 $ hg branches
28 default 0:d3873e73d99e
28 default 0:d3873e73d99e
29 $ hg tags
29 $ hg tags
30 tip 0:d3873e73d99e
30 tip 0:d3873e73d99e
31 $ test -d .hg/cache
31 $ ls -1 .hg/cache
32 [1]
32 checkisexec
33 checklink
34 checklink-target
33 $ ls -1 ../repo1/.hg/cache
35 $ ls -1 ../repo1/.hg/cache
34 branch2-served
36 branch2-served
37 checkisexec
38 checklink
39 checklink-target
35 manifestfulltextcache (reporevlogstore !)
40 manifestfulltextcache (reporevlogstore !)
36 rbc-names-v1
41 rbc-names-v1
37 rbc-revs-v1
42 rbc-revs-v1
38 tags2-visible
43 tags2-visible
39
44
40 Some sed versions appends newline, some don't, and some just fails
45 Some sed versions appends newline, some don't, and some just fails
41
46
42 $ cat .hg/sharedpath; echo
47 $ cat .hg/sharedpath; echo
43 $TESTTMP/repo1/.hg
48 $TESTTMP/repo1/.hg
44
49
45 trailing newline on .hg/sharedpath is ok
50 trailing newline on .hg/sharedpath is ok
46 $ hg tip -q
51 $ hg tip -q
47 0:d3873e73d99e
52 0:d3873e73d99e
48 $ echo '' >> .hg/sharedpath
53 $ echo '' >> .hg/sharedpath
49 $ cat .hg/sharedpath
54 $ cat .hg/sharedpath
50 $TESTTMP/repo1/.hg
55 $TESTTMP/repo1/.hg
51 $ hg tip -q
56 $ hg tip -q
52 0:d3873e73d99e
57 0:d3873e73d99e
53
58
54 commit in shared clone
59 commit in shared clone
55
60
56 $ echo a >> a
61 $ echo a >> a
57 $ hg commit -m'change in shared clone'
62 $ hg commit -m'change in shared clone'
58
63
59 check original
64 check original
60
65
61 $ cd ../repo1
66 $ cd ../repo1
62 $ hg log
67 $ hg log
63 changeset: 1:8af4dc49db9e
68 changeset: 1:8af4dc49db9e
64 tag: tip
69 tag: tip
65 user: test
70 user: test
66 date: Thu Jan 01 00:00:00 1970 +0000
71 date: Thu Jan 01 00:00:00 1970 +0000
67 summary: change in shared clone
72 summary: change in shared clone
68
73
69 changeset: 0:d3873e73d99e
74 changeset: 0:d3873e73d99e
70 user: test
75 user: test
71 date: Thu Jan 01 00:00:00 1970 +0000
76 date: Thu Jan 01 00:00:00 1970 +0000
72 summary: init
77 summary: init
73
78
74 $ hg update
79 $ hg update
75 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
80 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
76 $ cat a # should be two lines of "a"
81 $ cat a # should be two lines of "a"
77 a
82 a
78 a
83 a
79
84
80 commit in original
85 commit in original
81
86
82 $ echo b > b
87 $ echo b > b
83 $ hg commit -A -m'another file'
88 $ hg commit -A -m'another file'
84 adding b
89 adding b
85
90
86 check in shared clone
91 check in shared clone
87
92
88 $ cd ../repo2
93 $ cd ../repo2
89 $ hg log
94 $ hg log
90 changeset: 2:c2e0ac586386
95 changeset: 2:c2e0ac586386
91 tag: tip
96 tag: tip
92 user: test
97 user: test
93 date: Thu Jan 01 00:00:00 1970 +0000
98 date: Thu Jan 01 00:00:00 1970 +0000
94 summary: another file
99 summary: another file
95
100
96 changeset: 1:8af4dc49db9e
101 changeset: 1:8af4dc49db9e
97 user: test
102 user: test
98 date: Thu Jan 01 00:00:00 1970 +0000
103 date: Thu Jan 01 00:00:00 1970 +0000
99 summary: change in shared clone
104 summary: change in shared clone
100
105
101 changeset: 0:d3873e73d99e
106 changeset: 0:d3873e73d99e
102 user: test
107 user: test
103 date: Thu Jan 01 00:00:00 1970 +0000
108 date: Thu Jan 01 00:00:00 1970 +0000
104 summary: init
109 summary: init
105
110
106 $ hg update
111 $ hg update
107 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
112 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
108 $ cat b # should exist with one "b"
113 $ cat b # should exist with one "b"
109 b
114 b
110
115
111 hg serve shared clone
116 hg serve shared clone
112
117
113 $ hg serve -n test -p $HGPORT -d --pid-file=hg.pid
118 $ hg serve -n test -p $HGPORT -d --pid-file=hg.pid
114 $ cat hg.pid >> $DAEMON_PIDS
119 $ cat hg.pid >> $DAEMON_PIDS
115 $ get-with-headers.py localhost:$HGPORT 'raw-file/'
120 $ get-with-headers.py localhost:$HGPORT 'raw-file/'
116 200 Script output follows
121 200 Script output follows
117
122
118
123
119 -rw-r--r-- 4 a
124 -rw-r--r-- 4 a
120 -rw-r--r-- 2 b
125 -rw-r--r-- 2 b
121
126
122
127
123
128
124 test unshare command
129 test unshare command
125
130
126 $ hg unshare
131 $ hg unshare
127 $ test -d .hg/store
132 $ test -d .hg/store
128 $ test -f .hg/sharedpath
133 $ test -f .hg/sharedpath
129 [1]
134 [1]
130 $ grep shared .hg/requires
135 $ grep shared .hg/requires
131 [1]
136 [1]
132 $ hg unshare
137 $ hg unshare
133 abort: this is not a shared repo
138 abort: this is not a shared repo
134 [255]
139 [255]
135
140
136 check that a change does not propagate
141 check that a change does not propagate
137
142
138 $ echo b >> b
143 $ echo b >> b
139 $ hg commit -m'change in unshared'
144 $ hg commit -m'change in unshared'
140 $ cd ../repo1
145 $ cd ../repo1
141 $ hg id -r tip
146 $ hg id -r tip
142 c2e0ac586386 tip
147 c2e0ac586386 tip
143
148
144 $ cd ..
149 $ cd ..
145
150
146
151
147 test sharing bookmarks
152 test sharing bookmarks
148
153
149 $ hg share -B repo1 repo3
154 $ hg share -B repo1 repo3
150 updating working directory
155 updating working directory
151 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
156 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
152 $ cd repo1
157 $ cd repo1
153 $ hg bookmark bm1
158 $ hg bookmark bm1
154 $ hg bookmarks
159 $ hg bookmarks
155 * bm1 2:c2e0ac586386
160 * bm1 2:c2e0ac586386
156 $ cd ../repo2
161 $ cd ../repo2
157 $ hg book bm2
162 $ hg book bm2
158 $ hg bookmarks
163 $ hg bookmarks
159 * bm2 3:0e6e70d1d5f1
164 * bm2 3:0e6e70d1d5f1
160 $ cd ../repo3
165 $ cd ../repo3
161 $ hg bookmarks
166 $ hg bookmarks
162 bm1 2:c2e0ac586386
167 bm1 2:c2e0ac586386
163 $ hg book bm3
168 $ hg book bm3
164 $ hg bookmarks
169 $ hg bookmarks
165 bm1 2:c2e0ac586386
170 bm1 2:c2e0ac586386
166 * bm3 2:c2e0ac586386
171 * bm3 2:c2e0ac586386
167 $ cd ../repo1
172 $ cd ../repo1
168 $ hg bookmarks
173 $ hg bookmarks
169 * bm1 2:c2e0ac586386
174 * bm1 2:c2e0ac586386
170 bm3 2:c2e0ac586386
175 bm3 2:c2e0ac586386
171
176
172 check whether HG_PENDING makes pending changes only in relatd
177 check whether HG_PENDING makes pending changes only in relatd
173 repositories visible to an external hook.
178 repositories visible to an external hook.
174
179
175 In "hg share" case, another transaction can't run in other
180 In "hg share" case, another transaction can't run in other
176 repositories sharing same source repository, because starting
181 repositories sharing same source repository, because starting
177 transaction requires locking store of source repository.
182 transaction requires locking store of source repository.
178
183
179 Therefore, this test scenario ignores checking visibility of
184 Therefore, this test scenario ignores checking visibility of
180 .hg/bookmakrs.pending in repo2, which shares repo1 without bookmarks.
185 .hg/bookmakrs.pending in repo2, which shares repo1 without bookmarks.
181
186
182 $ cat > $TESTTMP/checkbookmarks.sh <<EOF
187 $ cat > $TESTTMP/checkbookmarks.sh <<EOF
183 > echo "@repo1"
188 > echo "@repo1"
184 > hg -R "$TESTTMP/repo1" bookmarks
189 > hg -R "$TESTTMP/repo1" bookmarks
185 > echo "@repo2"
190 > echo "@repo2"
186 > hg -R "$TESTTMP/repo2" bookmarks
191 > hg -R "$TESTTMP/repo2" bookmarks
187 > echo "@repo3"
192 > echo "@repo3"
188 > hg -R "$TESTTMP/repo3" bookmarks
193 > hg -R "$TESTTMP/repo3" bookmarks
189 > exit 1 # to avoid adding new bookmark for subsequent tests
194 > exit 1 # to avoid adding new bookmark for subsequent tests
190 > EOF
195 > EOF
191
196
192 $ cd ../repo1
197 $ cd ../repo1
193 $ hg --config hooks.pretxnclose="sh $TESTTMP/checkbookmarks.sh" -q book bmX
198 $ hg --config hooks.pretxnclose="sh $TESTTMP/checkbookmarks.sh" -q book bmX
194 @repo1
199 @repo1
195 bm1 2:c2e0ac586386
200 bm1 2:c2e0ac586386
196 bm3 2:c2e0ac586386
201 bm3 2:c2e0ac586386
197 * bmX 2:c2e0ac586386
202 * bmX 2:c2e0ac586386
198 @repo2
203 @repo2
199 * bm2 3:0e6e70d1d5f1
204 * bm2 3:0e6e70d1d5f1
200 @repo3
205 @repo3
201 bm1 2:c2e0ac586386
206 bm1 2:c2e0ac586386
202 * bm3 2:c2e0ac586386
207 * bm3 2:c2e0ac586386
203 bmX 2:c2e0ac586386
208 bmX 2:c2e0ac586386
204 transaction abort!
209 transaction abort!
205 rollback completed
210 rollback completed
206 abort: pretxnclose hook exited with status 1
211 abort: pretxnclose hook exited with status 1
207 [255]
212 [255]
208 $ hg book bm1
213 $ hg book bm1
209
214
210 FYI, in contrast to above test, bmX is invisible in repo1 (= shared
215 FYI, in contrast to above test, bmX is invisible in repo1 (= shared
211 src), because (1) HG_PENDING refers only repo3 and (2)
216 src), because (1) HG_PENDING refers only repo3 and (2)
212 "bookmarks.pending" is written only into repo3.
217 "bookmarks.pending" is written only into repo3.
213
218
214 $ cd ../repo3
219 $ cd ../repo3
215 $ hg --config hooks.pretxnclose="sh $TESTTMP/checkbookmarks.sh" -q book bmX
220 $ hg --config hooks.pretxnclose="sh $TESTTMP/checkbookmarks.sh" -q book bmX
216 @repo1
221 @repo1
217 * bm1 2:c2e0ac586386
222 * bm1 2:c2e0ac586386
218 bm3 2:c2e0ac586386
223 bm3 2:c2e0ac586386
219 @repo2
224 @repo2
220 * bm2 3:0e6e70d1d5f1
225 * bm2 3:0e6e70d1d5f1
221 @repo3
226 @repo3
222 bm1 2:c2e0ac586386
227 bm1 2:c2e0ac586386
223 bm3 2:c2e0ac586386
228 bm3 2:c2e0ac586386
224 * bmX 2:c2e0ac586386
229 * bmX 2:c2e0ac586386
225 transaction abort!
230 transaction abort!
226 rollback completed
231 rollback completed
227 abort: pretxnclose hook exited with status 1
232 abort: pretxnclose hook exited with status 1
228 [255]
233 [255]
229 $ hg book bm3
234 $ hg book bm3
230
235
231 $ cd ../repo1
236 $ cd ../repo1
232
237
233 test that commits work
238 test that commits work
234
239
235 $ echo 'shared bookmarks' > a
240 $ echo 'shared bookmarks' > a
236 $ hg commit -m 'testing shared bookmarks'
241 $ hg commit -m 'testing shared bookmarks'
237 $ hg bookmarks
242 $ hg bookmarks
238 * bm1 3:b87954705719
243 * bm1 3:b87954705719
239 bm3 2:c2e0ac586386
244 bm3 2:c2e0ac586386
240 $ cd ../repo3
245 $ cd ../repo3
241 $ hg bookmarks
246 $ hg bookmarks
242 bm1 3:b87954705719
247 bm1 3:b87954705719
243 * bm3 2:c2e0ac586386
248 * bm3 2:c2e0ac586386
244 $ echo 'more shared bookmarks' > a
249 $ echo 'more shared bookmarks' > a
245 $ hg commit -m 'testing shared bookmarks'
250 $ hg commit -m 'testing shared bookmarks'
246 created new head
251 created new head
247 $ hg bookmarks
252 $ hg bookmarks
248 bm1 3:b87954705719
253 bm1 3:b87954705719
249 * bm3 4:62f4ded848e4
254 * bm3 4:62f4ded848e4
250 $ cd ../repo1
255 $ cd ../repo1
251 $ hg bookmarks
256 $ hg bookmarks
252 * bm1 3:b87954705719
257 * bm1 3:b87954705719
253 bm3 4:62f4ded848e4
258 bm3 4:62f4ded848e4
254 $ cd ..
259 $ cd ..
255
260
256 non largefiles repos won't enable largefiles
261 non largefiles repos won't enable largefiles
257
262
258 $ hg share --config extensions.largefiles= repo3 sharedrepo
263 $ hg share --config extensions.largefiles= repo3 sharedrepo
259 The fsmonitor extension is incompatible with the largefiles extension and has been disabled. (fsmonitor !)
264 The fsmonitor extension is incompatible with the largefiles extension and has been disabled. (fsmonitor !)
260 The fsmonitor extension is incompatible with the largefiles extension and has been disabled. (fsmonitor !)
265 The fsmonitor extension is incompatible with the largefiles extension and has been disabled. (fsmonitor !)
261 updating working directory
266 updating working directory
262 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
267 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
263 $ [ -f sharedrepo/.hg/hgrc ]
268 $ [ -f sharedrepo/.hg/hgrc ]
264 [1]
269 [1]
265
270
266 test pushing bookmarks works
271 test pushing bookmarks works
267
272
268 $ hg clone repo3 repo4
273 $ hg clone repo3 repo4
269 updating to branch default
274 updating to branch default
270 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
275 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
271 $ cd repo4
276 $ cd repo4
272 $ hg boo bm4
277 $ hg boo bm4
273 $ echo foo > b
278 $ echo foo > b
274 $ hg commit -m 'foo in b'
279 $ hg commit -m 'foo in b'
275 $ hg boo
280 $ hg boo
276 bm1 3:b87954705719
281 bm1 3:b87954705719
277 bm3 4:62f4ded848e4
282 bm3 4:62f4ded848e4
278 * bm4 5:92793bfc8cad
283 * bm4 5:92793bfc8cad
279 $ hg push -B bm4
284 $ hg push -B bm4
280 pushing to $TESTTMP/repo3
285 pushing to $TESTTMP/repo3
281 searching for changes
286 searching for changes
282 adding changesets
287 adding changesets
283 adding manifests
288 adding manifests
284 adding file changes
289 adding file changes
285 added 1 changesets with 1 changes to 1 files
290 added 1 changesets with 1 changes to 1 files
286 exporting bookmark bm4
291 exporting bookmark bm4
287 $ cd ../repo1
292 $ cd ../repo1
288 $ hg bookmarks
293 $ hg bookmarks
289 * bm1 3:b87954705719
294 * bm1 3:b87954705719
290 bm3 4:62f4ded848e4
295 bm3 4:62f4ded848e4
291 bm4 5:92793bfc8cad
296 bm4 5:92793bfc8cad
292 $ cd ../repo3
297 $ cd ../repo3
293 $ hg bookmarks
298 $ hg bookmarks
294 bm1 3:b87954705719
299 bm1 3:b87954705719
295 * bm3 4:62f4ded848e4
300 * bm3 4:62f4ded848e4
296 bm4 5:92793bfc8cad
301 bm4 5:92793bfc8cad
297 $ cd ..
302 $ cd ..
298
303
299 test behavior when sharing a shared repo
304 test behavior when sharing a shared repo
300
305
301 $ hg share -B repo3 missingdir/repo5
306 $ hg share -B repo3 missingdir/repo5
302 updating working directory
307 updating working directory
303 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
308 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
304 $ cd missingdir/repo5
309 $ cd missingdir/repo5
305 $ hg book
310 $ hg book
306 bm1 3:b87954705719
311 bm1 3:b87954705719
307 bm3 4:62f4ded848e4
312 bm3 4:62f4ded848e4
308 bm4 5:92793bfc8cad
313 bm4 5:92793bfc8cad
309 $ cd ../..
314 $ cd ../..
310
315
311 test what happens when an active bookmark is deleted
316 test what happens when an active bookmark is deleted
312
317
313 $ cd repo1
318 $ cd repo1
314 $ hg boo -d bm3
319 $ hg boo -d bm3
315 $ hg boo
320 $ hg boo
316 * bm1 3:b87954705719
321 * bm1 3:b87954705719
317 bm4 5:92793bfc8cad
322 bm4 5:92793bfc8cad
318 $ cd ../repo3
323 $ cd ../repo3
319 $ hg boo
324 $ hg boo
320 bm1 3:b87954705719
325 bm1 3:b87954705719
321 bm4 5:92793bfc8cad
326 bm4 5:92793bfc8cad
322 $ cd ..
327 $ cd ..
323
328
324 verify that bookmarks are not written on failed transaction
329 verify that bookmarks are not written on failed transaction
325
330
326 $ cat > failpullbookmarks.py << EOF
331 $ cat > failpullbookmarks.py << EOF
327 > """A small extension that makes bookmark pulls fail, for testing"""
332 > """A small extension that makes bookmark pulls fail, for testing"""
328 > from __future__ import absolute_import
333 > from __future__ import absolute_import
329 > from mercurial import (
334 > from mercurial import (
330 > error,
335 > error,
331 > exchange,
336 > exchange,
332 > extensions,
337 > extensions,
333 > )
338 > )
334 > def _pullbookmarks(orig, pullop):
339 > def _pullbookmarks(orig, pullop):
335 > orig(pullop)
340 > orig(pullop)
336 > raise error.HookAbort('forced failure by extension')
341 > raise error.HookAbort('forced failure by extension')
337 > def extsetup(ui):
342 > def extsetup(ui):
338 > extensions.wrapfunction(exchange, '_pullbookmarks', _pullbookmarks)
343 > extensions.wrapfunction(exchange, '_pullbookmarks', _pullbookmarks)
339 > EOF
344 > EOF
340 $ cd repo4
345 $ cd repo4
341 $ hg boo
346 $ hg boo
342 bm1 3:b87954705719
347 bm1 3:b87954705719
343 bm3 4:62f4ded848e4
348 bm3 4:62f4ded848e4
344 * bm4 5:92793bfc8cad
349 * bm4 5:92793bfc8cad
345 $ cd ../repo3
350 $ cd ../repo3
346 $ hg boo
351 $ hg boo
347 bm1 3:b87954705719
352 bm1 3:b87954705719
348 bm4 5:92793bfc8cad
353 bm4 5:92793bfc8cad
349 $ hg --config "extensions.failpullbookmarks=$TESTTMP/failpullbookmarks.py" pull $TESTTMP/repo4
354 $ hg --config "extensions.failpullbookmarks=$TESTTMP/failpullbookmarks.py" pull $TESTTMP/repo4
350 pulling from $TESTTMP/repo4
355 pulling from $TESTTMP/repo4
351 searching for changes
356 searching for changes
352 no changes found
357 no changes found
353 adding remote bookmark bm3
358 adding remote bookmark bm3
354 abort: forced failure by extension
359 abort: forced failure by extension
355 [255]
360 [255]
356 $ hg boo
361 $ hg boo
357 bm1 3:b87954705719
362 bm1 3:b87954705719
358 bm4 5:92793bfc8cad
363 bm4 5:92793bfc8cad
359 $ hg pull $TESTTMP/repo4
364 $ hg pull $TESTTMP/repo4
360 pulling from $TESTTMP/repo4
365 pulling from $TESTTMP/repo4
361 searching for changes
366 searching for changes
362 no changes found
367 no changes found
363 adding remote bookmark bm3
368 adding remote bookmark bm3
364 1 local changesets published
369 1 local changesets published
365 $ hg boo
370 $ hg boo
366 bm1 3:b87954705719
371 bm1 3:b87954705719
367 * bm3 4:62f4ded848e4
372 * bm3 4:62f4ded848e4
368 bm4 5:92793bfc8cad
373 bm4 5:92793bfc8cad
369 $ cd ..
374 $ cd ..
370
375
371 verify bookmark behavior after unshare
376 verify bookmark behavior after unshare
372
377
373 $ cd repo3
378 $ cd repo3
374 $ hg unshare
379 $ hg unshare
375 $ hg boo
380 $ hg boo
376 bm1 3:b87954705719
381 bm1 3:b87954705719
377 * bm3 4:62f4ded848e4
382 * bm3 4:62f4ded848e4
378 bm4 5:92793bfc8cad
383 bm4 5:92793bfc8cad
379 $ hg boo -d bm4
384 $ hg boo -d bm4
380 $ hg boo bm5
385 $ hg boo bm5
381 $ hg boo
386 $ hg boo
382 bm1 3:b87954705719
387 bm1 3:b87954705719
383 bm3 4:62f4ded848e4
388 bm3 4:62f4ded848e4
384 * bm5 4:62f4ded848e4
389 * bm5 4:62f4ded848e4
385 $ cd ../repo1
390 $ cd ../repo1
386 $ hg boo
391 $ hg boo
387 * bm1 3:b87954705719
392 * bm1 3:b87954705719
388 bm3 4:62f4ded848e4
393 bm3 4:62f4ded848e4
389 bm4 5:92793bfc8cad
394 bm4 5:92793bfc8cad
390 $ cd ..
395 $ cd ..
391
396
392 test shared clones using relative paths work
397 test shared clones using relative paths work
393
398
394 $ mkdir thisdir
399 $ mkdir thisdir
395 $ hg init thisdir/orig
400 $ hg init thisdir/orig
396 $ hg share -U thisdir/orig thisdir/abs
401 $ hg share -U thisdir/orig thisdir/abs
397 $ hg share -U --relative thisdir/abs thisdir/rel
402 $ hg share -U --relative thisdir/abs thisdir/rel
398 $ cat thisdir/rel/.hg/sharedpath
403 $ cat thisdir/rel/.hg/sharedpath
399 ../../orig/.hg (no-eol)
404 ../../orig/.hg (no-eol)
400 $ grep shared thisdir/*/.hg/requires
405 $ grep shared thisdir/*/.hg/requires
401 thisdir/abs/.hg/requires:shared
406 thisdir/abs/.hg/requires:shared
402 thisdir/rel/.hg/requires:relshared
407 thisdir/rel/.hg/requires:relshared
403 thisdir/rel/.hg/requires:shared
408 thisdir/rel/.hg/requires:shared
404
409
405 test that relative shared paths aren't relative to $PWD
410 test that relative shared paths aren't relative to $PWD
406
411
407 $ cd thisdir
412 $ cd thisdir
408 $ hg -R rel root
413 $ hg -R rel root
409 $TESTTMP/thisdir/rel
414 $TESTTMP/thisdir/rel
410 $ cd ..
415 $ cd ..
411
416
412 now test that relative paths really are relative, survive across
417 now test that relative paths really are relative, survive across
413 renames and changes of PWD
418 renames and changes of PWD
414
419
415 $ hg -R thisdir/abs root
420 $ hg -R thisdir/abs root
416 $TESTTMP/thisdir/abs
421 $TESTTMP/thisdir/abs
417 $ hg -R thisdir/rel root
422 $ hg -R thisdir/rel root
418 $TESTTMP/thisdir/rel
423 $TESTTMP/thisdir/rel
419 $ mv thisdir thatdir
424 $ mv thisdir thatdir
420 $ hg -R thatdir/abs root
425 $ hg -R thatdir/abs root
421 abort: .hg/sharedpath points to nonexistent directory $TESTTMP/thisdir/orig/.hg!
426 abort: .hg/sharedpath points to nonexistent directory $TESTTMP/thisdir/orig/.hg!
422 [255]
427 [255]
423 $ hg -R thatdir/rel root
428 $ hg -R thatdir/rel root
424 $TESTTMP/thatdir/rel
429 $TESTTMP/thatdir/rel
425
430
426 test unshare relshared repo
431 test unshare relshared repo
427
432
428 $ cd thatdir/rel
433 $ cd thatdir/rel
429 $ hg unshare
434 $ hg unshare
430 $ test -d .hg/store
435 $ test -d .hg/store
431 $ test -f .hg/sharedpath
436 $ test -f .hg/sharedpath
432 [1]
437 [1]
433 $ grep shared .hg/requires
438 $ grep shared .hg/requires
434 [1]
439 [1]
435 $ hg unshare
440 $ hg unshare
436 abort: this is not a shared repo
441 abort: this is not a shared repo
437 [255]
442 [255]
438 $ cd ../..
443 $ cd ../..
439
444
440 $ rm -r thatdir
445 $ rm -r thatdir
441
446
442 Demonstrate buggy behavior around requirements validation
447 Demonstrate buggy behavior around requirements validation
443 See comment in localrepo.py:makelocalrepository() for more.
448 See comment in localrepo.py:makelocalrepository() for more.
444
449
445 $ hg init sharenewrequires
450 $ hg init sharenewrequires
446 $ hg share sharenewrequires shareoldrequires
451 $ hg share sharenewrequires shareoldrequires
447 updating working directory
452 updating working directory
448 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
453 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
449
454
450 $ cat >> sharenewrequires/.hg/requires << EOF
455 $ cat >> sharenewrequires/.hg/requires << EOF
451 > missing-requirement
456 > missing-requirement
452 > EOF
457 > EOF
453
458
454 We cannot open the repo with the unknown requirement
459 We cannot open the repo with the unknown requirement
455
460
456 $ hg -R sharenewrequires status
461 $ hg -R sharenewrequires status
457 abort: repository requires features unknown to this Mercurial: missing-requirement!
462 abort: repository requires features unknown to this Mercurial: missing-requirement!
458 (see https://mercurial-scm.org/wiki/MissingRequirement for more information)
463 (see https://mercurial-scm.org/wiki/MissingRequirement for more information)
459 [255]
464 [255]
460
465
461 BUG: we don't get the same error when opening the shared repo pointing to it
466 BUG: we don't get the same error when opening the shared repo pointing to it
462
467
463 $ hg -R shareoldrequires status
468 $ hg -R shareoldrequires status
464
469
465 Explicitly kill daemons to let the test exit on Windows
470 Explicitly kill daemons to let the test exit on Windows
466
471
467 $ killdaemons.py
472 $ killdaemons.py
468
473
General Comments 0
You need to be logged in to leave comments. Login now