##// END OF EJS Templates
chmod: create a new file when flags are set on a hardlinked file...
Koen Van Hoof -
r32721:c2cb0de2 default
parent child Browse files
Show More
@@ -1,659 +1,668
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 pycompat,
26 pycompat,
27 )
27 )
28
28
29 posixfile = open
29 posixfile = open
30 normpath = os.path.normpath
30 normpath = os.path.normpath
31 samestat = os.path.samestat
31 samestat = os.path.samestat
32 try:
32 try:
33 oslink = os.link
33 oslink = os.link
34 except AttributeError:
34 except AttributeError:
35 # Some platforms build Python without os.link on systems that are
35 # Some platforms build Python without os.link on systems that are
36 # vaguely unix-like but don't have hardlink support. For those
36 # vaguely unix-like but don't have hardlink support. For those
37 # poor souls, just say we tried and that it failed so we fall back
37 # poor souls, just say we tried and that it failed so we fall back
38 # to copies.
38 # to copies.
39 def oslink(src, dst):
39 def oslink(src, dst):
40 raise OSError(errno.EINVAL,
40 raise OSError(errno.EINVAL,
41 'hardlinks not supported: %s to %s' % (src, dst))
41 'hardlinks not supported: %s to %s' % (src, dst))
42 unlink = os.unlink
42 unlink = os.unlink
43 rename = os.rename
43 rename = os.rename
44 removedirs = os.removedirs
44 removedirs = os.removedirs
45 expandglobs = False
45 expandglobs = False
46
46
47 umask = os.umask(0)
47 umask = os.umask(0)
48 os.umask(umask)
48 os.umask(umask)
49
49
50 def split(p):
50 def split(p):
51 '''Same as posixpath.split, but faster
51 '''Same as posixpath.split, but faster
52
52
53 >>> import posixpath
53 >>> import posixpath
54 >>> for f in ['/absolute/path/to/file',
54 >>> for f in ['/absolute/path/to/file',
55 ... 'relative/path/to/file',
55 ... 'relative/path/to/file',
56 ... 'file_alone',
56 ... 'file_alone',
57 ... 'path/to/directory/',
57 ... 'path/to/directory/',
58 ... '/multiple/path//separators',
58 ... '/multiple/path//separators',
59 ... '/file_at_root',
59 ... '/file_at_root',
60 ... '///multiple_leading_separators_at_root',
60 ... '///multiple_leading_separators_at_root',
61 ... '']:
61 ... '']:
62 ... assert split(f) == posixpath.split(f), f
62 ... assert split(f) == posixpath.split(f), f
63 '''
63 '''
64 ht = p.rsplit('/', 1)
64 ht = p.rsplit('/', 1)
65 if len(ht) == 1:
65 if len(ht) == 1:
66 return '', p
66 return '', p
67 nh = ht[0].rstrip('/')
67 nh = ht[0].rstrip('/')
68 if nh:
68 if nh:
69 return nh, ht[1]
69 return nh, ht[1]
70 return ht[0] + '/', ht[1]
70 return ht[0] + '/', ht[1]
71
71
72 def openhardlinks():
72 def openhardlinks():
73 '''return true if it is safe to hold open file handles to hardlinks'''
73 '''return true if it is safe to hold open file handles to hardlinks'''
74 return True
74 return True
75
75
76 def nlinks(name):
76 def nlinks(name):
77 '''return number of hardlinks for the given file'''
77 '''return number of hardlinks for the given file'''
78 return os.lstat(name).st_nlink
78 return os.lstat(name).st_nlink
79
79
80 def parsepatchoutput(output_line):
80 def parsepatchoutput(output_line):
81 """parses the output produced by patch and returns the filename"""
81 """parses the output produced by patch and returns the filename"""
82 pf = output_line[14:]
82 pf = output_line[14:]
83 if pycompat.sysplatform == 'OpenVMS':
83 if pycompat.sysplatform == 'OpenVMS':
84 if pf[0] == '`':
84 if pf[0] == '`':
85 pf = pf[1:-1] # Remove the quotes
85 pf = pf[1:-1] # Remove the quotes
86 else:
86 else:
87 if pf.startswith("'") and pf.endswith("'") and " " in pf:
87 if pf.startswith("'") and pf.endswith("'") and " " in pf:
88 pf = pf[1:-1] # Remove the quotes
88 pf = pf[1:-1] # Remove the quotes
89 return pf
89 return pf
90
90
91 def sshargs(sshcmd, host, user, port):
91 def sshargs(sshcmd, host, user, port):
92 '''Build argument list for ssh'''
92 '''Build argument list for ssh'''
93 args = user and ("%s@%s" % (user, host)) or host
93 args = user and ("%s@%s" % (user, host)) or host
94 return port and ("%s -p %s" % (args, port)) or args
94 return port and ("%s -p %s" % (args, port)) or args
95
95
96 def isexec(f):
96 def isexec(f):
97 """check whether a file is executable"""
97 """check whether a file is executable"""
98 return (os.lstat(f).st_mode & 0o100 != 0)
98 return (os.lstat(f).st_mode & 0o100 != 0)
99
99
100 def setflags(f, l, x):
100 def setflags(f, l, x):
101 s = os.lstat(f).st_mode
101 st = os.lstat(f)
102 s = st.st_mode
102 if l:
103 if l:
103 if not stat.S_ISLNK(s):
104 if not stat.S_ISLNK(s):
104 # switch file to link
105 # switch file to link
105 fp = open(f)
106 fp = open(f)
106 data = fp.read()
107 data = fp.read()
107 fp.close()
108 fp.close()
108 unlink(f)
109 unlink(f)
109 try:
110 try:
110 os.symlink(data, f)
111 os.symlink(data, f)
111 except OSError:
112 except OSError:
112 # failed to make a link, rewrite file
113 # failed to make a link, rewrite file
113 fp = open(f, "w")
114 fp = open(f, "w")
114 fp.write(data)
115 fp.write(data)
115 fp.close()
116 fp.close()
116 # no chmod needed at this point
117 # no chmod needed at this point
117 return
118 return
118 if stat.S_ISLNK(s):
119 if stat.S_ISLNK(s):
119 # switch link to file
120 # switch link to file
120 data = os.readlink(f)
121 data = os.readlink(f)
121 unlink(f)
122 unlink(f)
122 fp = open(f, "w")
123 fp = open(f, "w")
123 fp.write(data)
124 fp.write(data)
124 fp.close()
125 fp.close()
125 s = 0o666 & ~umask # avoid restatting for chmod
126 s = 0o666 & ~umask # avoid restatting for chmod
126
127
127 sx = s & 0o100
128 sx = s & 0o100
129 if st.st_nlink > 1 and bool(x) != bool(sx):
130 # the file is a hardlink, break it
131 with open(f, "rb") as fp:
132 data = fp.read()
133 unlink(f)
134 with open(f, "wb") as fp:
135 fp.write(data)
136
128 if x and not sx:
137 if x and not sx:
129 # Turn on +x for every +r bit when making a file executable
138 # Turn on +x for every +r bit when making a file executable
130 # and obey umask.
139 # and obey umask.
131 os.chmod(f, s | (s & 0o444) >> 2 & ~umask)
140 os.chmod(f, s | (s & 0o444) >> 2 & ~umask)
132 elif not x and sx:
141 elif not x and sx:
133 # Turn off all +x bits
142 # Turn off all +x bits
134 os.chmod(f, s & 0o666)
143 os.chmod(f, s & 0o666)
135
144
136 def copymode(src, dst, mode=None):
145 def copymode(src, dst, mode=None):
137 '''Copy the file mode from the file at path src to dst.
146 '''Copy the file mode from the file at path src to dst.
138 If src doesn't exist, we're using mode instead. If mode is None, we're
147 If src doesn't exist, we're using mode instead. If mode is None, we're
139 using umask.'''
148 using umask.'''
140 try:
149 try:
141 st_mode = os.lstat(src).st_mode & 0o777
150 st_mode = os.lstat(src).st_mode & 0o777
142 except OSError as inst:
151 except OSError as inst:
143 if inst.errno != errno.ENOENT:
152 if inst.errno != errno.ENOENT:
144 raise
153 raise
145 st_mode = mode
154 st_mode = mode
146 if st_mode is None:
155 if st_mode is None:
147 st_mode = ~umask
156 st_mode = ~umask
148 st_mode &= 0o666
157 st_mode &= 0o666
149 os.chmod(dst, st_mode)
158 os.chmod(dst, st_mode)
150
159
151 def checkexec(path):
160 def checkexec(path):
152 """
161 """
153 Check whether the given path is on a filesystem with UNIX-like exec flags
162 Check whether the given path is on a filesystem with UNIX-like exec flags
154
163
155 Requires a directory (like /foo/.hg)
164 Requires a directory (like /foo/.hg)
156 """
165 """
157
166
158 # VFAT on some Linux versions can flip mode but it doesn't persist
167 # VFAT on some Linux versions can flip mode but it doesn't persist
159 # a FS remount. Frequently we can detect it if files are created
168 # a FS remount. Frequently we can detect it if files are created
160 # with exec bit on.
169 # with exec bit on.
161
170
162 try:
171 try:
163 EXECFLAGS = stat.S_IXUSR | stat.S_IXGRP | stat.S_IXOTH
172 EXECFLAGS = stat.S_IXUSR | stat.S_IXGRP | stat.S_IXOTH
164 cachedir = os.path.join(path, '.hg', 'cache')
173 cachedir = os.path.join(path, '.hg', 'cache')
165 if os.path.isdir(cachedir):
174 if os.path.isdir(cachedir):
166 checkisexec = os.path.join(cachedir, 'checkisexec')
175 checkisexec = os.path.join(cachedir, 'checkisexec')
167 checknoexec = os.path.join(cachedir, 'checknoexec')
176 checknoexec = os.path.join(cachedir, 'checknoexec')
168
177
169 try:
178 try:
170 m = os.stat(checkisexec).st_mode
179 m = os.stat(checkisexec).st_mode
171 except OSError as e:
180 except OSError as e:
172 if e.errno != errno.ENOENT:
181 if e.errno != errno.ENOENT:
173 raise
182 raise
174 # checkisexec does not exist - fall through ...
183 # checkisexec does not exist - fall through ...
175 else:
184 else:
176 # checkisexec exists, check if it actually is exec
185 # checkisexec exists, check if it actually is exec
177 if m & EXECFLAGS != 0:
186 if m & EXECFLAGS != 0:
178 # ensure checkisexec exists, check it isn't exec
187 # ensure checkisexec exists, check it isn't exec
179 try:
188 try:
180 m = os.stat(checknoexec).st_mode
189 m = os.stat(checknoexec).st_mode
181 except OSError as e:
190 except OSError as e:
182 if e.errno != errno.ENOENT:
191 if e.errno != errno.ENOENT:
183 raise
192 raise
184 open(checknoexec, 'w').close() # might fail
193 open(checknoexec, 'w').close() # might fail
185 m = os.stat(checknoexec).st_mode
194 m = os.stat(checknoexec).st_mode
186 if m & EXECFLAGS == 0:
195 if m & EXECFLAGS == 0:
187 # check-exec is exec and check-no-exec is not exec
196 # check-exec is exec and check-no-exec is not exec
188 return True
197 return True
189 # checknoexec exists but is exec - delete it
198 # checknoexec exists but is exec - delete it
190 unlink(checknoexec)
199 unlink(checknoexec)
191 # checkisexec exists but is not exec - delete it
200 # checkisexec exists but is not exec - delete it
192 unlink(checkisexec)
201 unlink(checkisexec)
193
202
194 # check using one file, leave it as checkisexec
203 # check using one file, leave it as checkisexec
195 checkdir = cachedir
204 checkdir = cachedir
196 else:
205 else:
197 # check directly in path and don't leave checkisexec behind
206 # check directly in path and don't leave checkisexec behind
198 checkdir = path
207 checkdir = path
199 checkisexec = None
208 checkisexec = None
200 fh, fn = tempfile.mkstemp(dir=checkdir, prefix='hg-checkexec-')
209 fh, fn = tempfile.mkstemp(dir=checkdir, prefix='hg-checkexec-')
201 try:
210 try:
202 os.close(fh)
211 os.close(fh)
203 m = os.stat(fn).st_mode
212 m = os.stat(fn).st_mode
204 if m & EXECFLAGS == 0:
213 if m & EXECFLAGS == 0:
205 os.chmod(fn, m & 0o777 | EXECFLAGS)
214 os.chmod(fn, m & 0o777 | EXECFLAGS)
206 if os.stat(fn).st_mode & EXECFLAGS != 0:
215 if os.stat(fn).st_mode & EXECFLAGS != 0:
207 if checkisexec is not None:
216 if checkisexec is not None:
208 os.rename(fn, checkisexec)
217 os.rename(fn, checkisexec)
209 fn = None
218 fn = None
210 return True
219 return True
211 finally:
220 finally:
212 if fn is not None:
221 if fn is not None:
213 unlink(fn)
222 unlink(fn)
214 except (IOError, OSError):
223 except (IOError, OSError):
215 # we don't care, the user probably won't be able to commit anyway
224 # we don't care, the user probably won't be able to commit anyway
216 return False
225 return False
217
226
218 def checklink(path):
227 def checklink(path):
219 """check whether the given path is on a symlink-capable filesystem"""
228 """check whether the given path is on a symlink-capable filesystem"""
220 # mktemp is not racy because symlink creation will fail if the
229 # mktemp is not racy because symlink creation will fail if the
221 # file already exists
230 # file already exists
222 while True:
231 while True:
223 cachedir = os.path.join(path, '.hg', 'cache')
232 cachedir = os.path.join(path, '.hg', 'cache')
224 checklink = os.path.join(cachedir, 'checklink')
233 checklink = os.path.join(cachedir, 'checklink')
225 # try fast path, read only
234 # try fast path, read only
226 if os.path.islink(checklink):
235 if os.path.islink(checklink):
227 return True
236 return True
228 if os.path.isdir(cachedir):
237 if os.path.isdir(cachedir):
229 checkdir = cachedir
238 checkdir = cachedir
230 else:
239 else:
231 checkdir = path
240 checkdir = path
232 cachedir = None
241 cachedir = None
233 fscheckdir = pycompat.fsdecode(checkdir)
242 fscheckdir = pycompat.fsdecode(checkdir)
234 name = tempfile.mktemp(dir=fscheckdir,
243 name = tempfile.mktemp(dir=fscheckdir,
235 prefix=r'checklink-')
244 prefix=r'checklink-')
236 name = pycompat.fsencode(name)
245 name = pycompat.fsencode(name)
237 try:
246 try:
238 fd = None
247 fd = None
239 if cachedir is None:
248 if cachedir is None:
240 fd = tempfile.NamedTemporaryFile(dir=fscheckdir,
249 fd = tempfile.NamedTemporaryFile(dir=fscheckdir,
241 prefix=r'hg-checklink-')
250 prefix=r'hg-checklink-')
242 target = pycompat.fsencode(os.path.basename(fd.name))
251 target = pycompat.fsencode(os.path.basename(fd.name))
243 else:
252 else:
244 # create a fixed file to link to; doesn't matter if it
253 # create a fixed file to link to; doesn't matter if it
245 # already exists.
254 # already exists.
246 target = 'checklink-target'
255 target = 'checklink-target'
247 try:
256 try:
248 open(os.path.join(cachedir, target), 'w').close()
257 open(os.path.join(cachedir, target), 'w').close()
249 except IOError as inst:
258 except IOError as inst:
250 if inst[0] == errno.EACCES:
259 if inst[0] == errno.EACCES:
251 # If we can't write to cachedir, just pretend
260 # If we can't write to cachedir, just pretend
252 # that the fs is readonly and by association
261 # that the fs is readonly and by association
253 # that the fs won't support symlinks. This
262 # that the fs won't support symlinks. This
254 # seems like the least dangerous way to avoid
263 # seems like the least dangerous way to avoid
255 # data loss.
264 # data loss.
256 return False
265 return False
257 raise
266 raise
258 try:
267 try:
259 os.symlink(target, name)
268 os.symlink(target, name)
260 if cachedir is None:
269 if cachedir is None:
261 unlink(name)
270 unlink(name)
262 else:
271 else:
263 try:
272 try:
264 os.rename(name, checklink)
273 os.rename(name, checklink)
265 except OSError:
274 except OSError:
266 unlink(name)
275 unlink(name)
267 return True
276 return True
268 except OSError as inst:
277 except OSError as inst:
269 # link creation might race, try again
278 # link creation might race, try again
270 if inst[0] == errno.EEXIST:
279 if inst[0] == errno.EEXIST:
271 continue
280 continue
272 raise
281 raise
273 finally:
282 finally:
274 if fd is not None:
283 if fd is not None:
275 fd.close()
284 fd.close()
276 except AttributeError:
285 except AttributeError:
277 return False
286 return False
278 except OSError as inst:
287 except OSError as inst:
279 # sshfs might report failure while successfully creating the link
288 # sshfs might report failure while successfully creating the link
280 if inst[0] == errno.EIO and os.path.exists(name):
289 if inst[0] == errno.EIO and os.path.exists(name):
281 unlink(name)
290 unlink(name)
282 return False
291 return False
283
292
284 def checkosfilename(path):
293 def checkosfilename(path):
285 '''Check that the base-relative path is a valid filename on this platform.
294 '''Check that the base-relative path is a valid filename on this platform.
286 Returns None if the path is ok, or a UI string describing the problem.'''
295 Returns None if the path is ok, or a UI string describing the problem.'''
287 pass # on posix platforms, every path is ok
296 pass # on posix platforms, every path is ok
288
297
289 def setbinary(fd):
298 def setbinary(fd):
290 pass
299 pass
291
300
292 def pconvert(path):
301 def pconvert(path):
293 return path
302 return path
294
303
295 def localpath(path):
304 def localpath(path):
296 return path
305 return path
297
306
298 def samefile(fpath1, fpath2):
307 def samefile(fpath1, fpath2):
299 """Returns whether path1 and path2 refer to the same file. This is only
308 """Returns whether path1 and path2 refer to the same file. This is only
300 guaranteed to work for files, not directories."""
309 guaranteed to work for files, not directories."""
301 return os.path.samefile(fpath1, fpath2)
310 return os.path.samefile(fpath1, fpath2)
302
311
303 def samedevice(fpath1, fpath2):
312 def samedevice(fpath1, fpath2):
304 """Returns whether fpath1 and fpath2 are on the same device. This is only
313 """Returns whether fpath1 and fpath2 are on the same device. This is only
305 guaranteed to work for files, not directories."""
314 guaranteed to work for files, not directories."""
306 st1 = os.lstat(fpath1)
315 st1 = os.lstat(fpath1)
307 st2 = os.lstat(fpath2)
316 st2 = os.lstat(fpath2)
308 return st1.st_dev == st2.st_dev
317 return st1.st_dev == st2.st_dev
309
318
310 # os.path.normcase is a no-op, which doesn't help us on non-native filesystems
319 # os.path.normcase is a no-op, which doesn't help us on non-native filesystems
311 def normcase(path):
320 def normcase(path):
312 return path.lower()
321 return path.lower()
313
322
314 # what normcase does to ASCII strings
323 # what normcase does to ASCII strings
315 normcasespec = encoding.normcasespecs.lower
324 normcasespec = encoding.normcasespecs.lower
316 # fallback normcase function for non-ASCII strings
325 # fallback normcase function for non-ASCII strings
317 normcasefallback = normcase
326 normcasefallback = normcase
318
327
319 if pycompat.sysplatform == 'darwin':
328 if pycompat.sysplatform == 'darwin':
320
329
321 def normcase(path):
330 def normcase(path):
322 '''
331 '''
323 Normalize a filename for OS X-compatible comparison:
332 Normalize a filename for OS X-compatible comparison:
324 - escape-encode invalid characters
333 - escape-encode invalid characters
325 - decompose to NFD
334 - decompose to NFD
326 - lowercase
335 - lowercase
327 - omit ignored characters [200c-200f, 202a-202e, 206a-206f,feff]
336 - omit ignored characters [200c-200f, 202a-202e, 206a-206f,feff]
328
337
329 >>> normcase('UPPER')
338 >>> normcase('UPPER')
330 'upper'
339 'upper'
331 >>> normcase('Caf\xc3\xa9')
340 >>> normcase('Caf\xc3\xa9')
332 'cafe\\xcc\\x81'
341 'cafe\\xcc\\x81'
333 >>> normcase('\xc3\x89')
342 >>> normcase('\xc3\x89')
334 'e\\xcc\\x81'
343 'e\\xcc\\x81'
335 >>> normcase('\xb8\xca\xc3\xca\xbe\xc8.JPG') # issue3918
344 >>> normcase('\xb8\xca\xc3\xca\xbe\xc8.JPG') # issue3918
336 '%b8%ca%c3\\xca\\xbe%c8.jpg'
345 '%b8%ca%c3\\xca\\xbe%c8.jpg'
337 '''
346 '''
338
347
339 try:
348 try:
340 return encoding.asciilower(path) # exception for non-ASCII
349 return encoding.asciilower(path) # exception for non-ASCII
341 except UnicodeDecodeError:
350 except UnicodeDecodeError:
342 return normcasefallback(path)
351 return normcasefallback(path)
343
352
344 normcasespec = encoding.normcasespecs.lower
353 normcasespec = encoding.normcasespecs.lower
345
354
346 def normcasefallback(path):
355 def normcasefallback(path):
347 try:
356 try:
348 u = path.decode('utf-8')
357 u = path.decode('utf-8')
349 except UnicodeDecodeError:
358 except UnicodeDecodeError:
350 # OS X percent-encodes any bytes that aren't valid utf-8
359 # OS X percent-encodes any bytes that aren't valid utf-8
351 s = ''
360 s = ''
352 pos = 0
361 pos = 0
353 l = len(path)
362 l = len(path)
354 while pos < l:
363 while pos < l:
355 try:
364 try:
356 c = encoding.getutf8char(path, pos)
365 c = encoding.getutf8char(path, pos)
357 pos += len(c)
366 pos += len(c)
358 except ValueError:
367 except ValueError:
359 c = '%%%02X' % ord(path[pos])
368 c = '%%%02X' % ord(path[pos])
360 pos += 1
369 pos += 1
361 s += c
370 s += c
362
371
363 u = s.decode('utf-8')
372 u = s.decode('utf-8')
364
373
365 # Decompose then lowercase (HFS+ technote specifies lower)
374 # Decompose then lowercase (HFS+ technote specifies lower)
366 enc = unicodedata.normalize('NFD', u).lower().encode('utf-8')
375 enc = unicodedata.normalize('NFD', u).lower().encode('utf-8')
367 # drop HFS+ ignored characters
376 # drop HFS+ ignored characters
368 return encoding.hfsignoreclean(enc)
377 return encoding.hfsignoreclean(enc)
369
378
370 if pycompat.sysplatform == 'cygwin':
379 if pycompat.sysplatform == 'cygwin':
371 # workaround for cygwin, in which mount point part of path is
380 # workaround for cygwin, in which mount point part of path is
372 # treated as case sensitive, even though underlying NTFS is case
381 # treated as case sensitive, even though underlying NTFS is case
373 # insensitive.
382 # insensitive.
374
383
375 # default mount points
384 # default mount points
376 cygwinmountpoints = sorted([
385 cygwinmountpoints = sorted([
377 "/usr/bin",
386 "/usr/bin",
378 "/usr/lib",
387 "/usr/lib",
379 "/cygdrive",
388 "/cygdrive",
380 ], reverse=True)
389 ], reverse=True)
381
390
382 # use upper-ing as normcase as same as NTFS workaround
391 # use upper-ing as normcase as same as NTFS workaround
383 def normcase(path):
392 def normcase(path):
384 pathlen = len(path)
393 pathlen = len(path)
385 if (pathlen == 0) or (path[0] != pycompat.ossep):
394 if (pathlen == 0) or (path[0] != pycompat.ossep):
386 # treat as relative
395 # treat as relative
387 return encoding.upper(path)
396 return encoding.upper(path)
388
397
389 # to preserve case of mountpoint part
398 # to preserve case of mountpoint part
390 for mp in cygwinmountpoints:
399 for mp in cygwinmountpoints:
391 if not path.startswith(mp):
400 if not path.startswith(mp):
392 continue
401 continue
393
402
394 mplen = len(mp)
403 mplen = len(mp)
395 if mplen == pathlen: # mount point itself
404 if mplen == pathlen: # mount point itself
396 return mp
405 return mp
397 if path[mplen] == pycompat.ossep:
406 if path[mplen] == pycompat.ossep:
398 return mp + encoding.upper(path[mplen:])
407 return mp + encoding.upper(path[mplen:])
399
408
400 return encoding.upper(path)
409 return encoding.upper(path)
401
410
402 normcasespec = encoding.normcasespecs.other
411 normcasespec = encoding.normcasespecs.other
403 normcasefallback = normcase
412 normcasefallback = normcase
404
413
405 # Cygwin translates native ACLs to POSIX permissions,
414 # Cygwin translates native ACLs to POSIX permissions,
406 # but these translations are not supported by native
415 # but these translations are not supported by native
407 # tools, so the exec bit tends to be set erroneously.
416 # tools, so the exec bit tends to be set erroneously.
408 # Therefore, disable executable bit access on Cygwin.
417 # Therefore, disable executable bit access on Cygwin.
409 def checkexec(path):
418 def checkexec(path):
410 return False
419 return False
411
420
412 # Similarly, Cygwin's symlink emulation is likely to create
421 # Similarly, Cygwin's symlink emulation is likely to create
413 # problems when Mercurial is used from both Cygwin and native
422 # problems when Mercurial is used from both Cygwin and native
414 # Windows, with other native tools, or on shared volumes
423 # Windows, with other native tools, or on shared volumes
415 def checklink(path):
424 def checklink(path):
416 return False
425 return False
417
426
418 _needsshellquote = None
427 _needsshellquote = None
419 def shellquote(s):
428 def shellquote(s):
420 if pycompat.sysplatform == 'OpenVMS':
429 if pycompat.sysplatform == 'OpenVMS':
421 return '"%s"' % s
430 return '"%s"' % s
422 global _needsshellquote
431 global _needsshellquote
423 if _needsshellquote is None:
432 if _needsshellquote is None:
424 _needsshellquote = re.compile(br'[^a-zA-Z0-9._/+-]').search
433 _needsshellquote = re.compile(br'[^a-zA-Z0-9._/+-]').search
425 if s and not _needsshellquote(s):
434 if s and not _needsshellquote(s):
426 # "s" shouldn't have to be quoted
435 # "s" shouldn't have to be quoted
427 return s
436 return s
428 else:
437 else:
429 return "'%s'" % s.replace("'", "'\\''")
438 return "'%s'" % s.replace("'", "'\\''")
430
439
431 def quotecommand(cmd):
440 def quotecommand(cmd):
432 return cmd
441 return cmd
433
442
434 def popen(command, mode='r'):
443 def popen(command, mode='r'):
435 return os.popen(command, mode)
444 return os.popen(command, mode)
436
445
437 def testpid(pid):
446 def testpid(pid):
438 '''return False if pid dead, True if running or not sure'''
447 '''return False if pid dead, True if running or not sure'''
439 if pycompat.sysplatform == 'OpenVMS':
448 if pycompat.sysplatform == 'OpenVMS':
440 return True
449 return True
441 try:
450 try:
442 os.kill(pid, 0)
451 os.kill(pid, 0)
443 return True
452 return True
444 except OSError as inst:
453 except OSError as inst:
445 return inst.errno != errno.ESRCH
454 return inst.errno != errno.ESRCH
446
455
447 def explainexit(code):
456 def explainexit(code):
448 """return a 2-tuple (desc, code) describing a subprocess status
457 """return a 2-tuple (desc, code) describing a subprocess status
449 (codes from kill are negative - not os.system/wait encoding)"""
458 (codes from kill are negative - not os.system/wait encoding)"""
450 if code >= 0:
459 if code >= 0:
451 return _("exited with status %d") % code, code
460 return _("exited with status %d") % code, code
452 return _("killed by signal %d") % -code, -code
461 return _("killed by signal %d") % -code, -code
453
462
454 def isowner(st):
463 def isowner(st):
455 """Return True if the stat object st is from the current user."""
464 """Return True if the stat object st is from the current user."""
456 return st.st_uid == os.getuid()
465 return st.st_uid == os.getuid()
457
466
458 def findexe(command):
467 def findexe(command):
459 '''Find executable for command searching like which does.
468 '''Find executable for command searching like which does.
460 If command is a basename then PATH is searched for command.
469 If command is a basename then PATH is searched for command.
461 PATH isn't searched if command is an absolute or relative path.
470 PATH isn't searched if command is an absolute or relative path.
462 If command isn't found None is returned.'''
471 If command isn't found None is returned.'''
463 if pycompat.sysplatform == 'OpenVMS':
472 if pycompat.sysplatform == 'OpenVMS':
464 return command
473 return command
465
474
466 def findexisting(executable):
475 def findexisting(executable):
467 'Will return executable if existing file'
476 'Will return executable if existing file'
468 if os.path.isfile(executable) and os.access(executable, os.X_OK):
477 if os.path.isfile(executable) and os.access(executable, os.X_OK):
469 return executable
478 return executable
470 return None
479 return None
471
480
472 if pycompat.ossep in command:
481 if pycompat.ossep in command:
473 return findexisting(command)
482 return findexisting(command)
474
483
475 if pycompat.sysplatform == 'plan9':
484 if pycompat.sysplatform == 'plan9':
476 return findexisting(os.path.join('/bin', command))
485 return findexisting(os.path.join('/bin', command))
477
486
478 for path in encoding.environ.get('PATH', '').split(pycompat.ospathsep):
487 for path in encoding.environ.get('PATH', '').split(pycompat.ospathsep):
479 executable = findexisting(os.path.join(path, command))
488 executable = findexisting(os.path.join(path, command))
480 if executable is not None:
489 if executable is not None:
481 return executable
490 return executable
482 return None
491 return None
483
492
484 def setsignalhandler():
493 def setsignalhandler():
485 pass
494 pass
486
495
487 _wantedkinds = {stat.S_IFREG, stat.S_IFLNK}
496 _wantedkinds = {stat.S_IFREG, stat.S_IFLNK}
488
497
489 def statfiles(files):
498 def statfiles(files):
490 '''Stat each file in files. Yield each stat, or None if a file does not
499 '''Stat each file in files. Yield each stat, or None if a file does not
491 exist or has a type we don't care about.'''
500 exist or has a type we don't care about.'''
492 lstat = os.lstat
501 lstat = os.lstat
493 getkind = stat.S_IFMT
502 getkind = stat.S_IFMT
494 for nf in files:
503 for nf in files:
495 try:
504 try:
496 st = lstat(nf)
505 st = lstat(nf)
497 if getkind(st.st_mode) not in _wantedkinds:
506 if getkind(st.st_mode) not in _wantedkinds:
498 st = None
507 st = None
499 except OSError as err:
508 except OSError as err:
500 if err.errno not in (errno.ENOENT, errno.ENOTDIR):
509 if err.errno not in (errno.ENOENT, errno.ENOTDIR):
501 raise
510 raise
502 st = None
511 st = None
503 yield st
512 yield st
504
513
505 def getuser():
514 def getuser():
506 '''return name of current user'''
515 '''return name of current user'''
507 return pycompat.fsencode(getpass.getuser())
516 return pycompat.fsencode(getpass.getuser())
508
517
509 def username(uid=None):
518 def username(uid=None):
510 """Return the name of the user with the given uid.
519 """Return the name of the user with the given uid.
511
520
512 If uid is None, return the name of the current user."""
521 If uid is None, return the name of the current user."""
513
522
514 if uid is None:
523 if uid is None:
515 uid = os.getuid()
524 uid = os.getuid()
516 try:
525 try:
517 return pwd.getpwuid(uid)[0]
526 return pwd.getpwuid(uid)[0]
518 except KeyError:
527 except KeyError:
519 return str(uid)
528 return str(uid)
520
529
521 def groupname(gid=None):
530 def groupname(gid=None):
522 """Return the name of the group with the given gid.
531 """Return the name of the group with the given gid.
523
532
524 If gid is None, return the name of the current group."""
533 If gid is None, return the name of the current group."""
525
534
526 if gid is None:
535 if gid is None:
527 gid = os.getgid()
536 gid = os.getgid()
528 try:
537 try:
529 return grp.getgrgid(gid)[0]
538 return grp.getgrgid(gid)[0]
530 except KeyError:
539 except KeyError:
531 return str(gid)
540 return str(gid)
532
541
533 def groupmembers(name):
542 def groupmembers(name):
534 """Return the list of members of the group with the given
543 """Return the list of members of the group with the given
535 name, KeyError if the group does not exist.
544 name, KeyError if the group does not exist.
536 """
545 """
537 return list(grp.getgrnam(name).gr_mem)
546 return list(grp.getgrnam(name).gr_mem)
538
547
539 def spawndetached(args):
548 def spawndetached(args):
540 return os.spawnvp(os.P_NOWAIT | getattr(os, 'P_DETACH', 0),
549 return os.spawnvp(os.P_NOWAIT | getattr(os, 'P_DETACH', 0),
541 args[0], args)
550 args[0], args)
542
551
543 def gethgcmd():
552 def gethgcmd():
544 return sys.argv[:1]
553 return sys.argv[:1]
545
554
546 def makedir(path, notindexed):
555 def makedir(path, notindexed):
547 os.mkdir(path)
556 os.mkdir(path)
548
557
549 def lookupreg(key, name=None, scope=None):
558 def lookupreg(key, name=None, scope=None):
550 return None
559 return None
551
560
552 def hidewindow():
561 def hidewindow():
553 """Hide current shell window.
562 """Hide current shell window.
554
563
555 Used to hide the window opened when starting asynchronous
564 Used to hide the window opened when starting asynchronous
556 child process under Windows, unneeded on other systems.
565 child process under Windows, unneeded on other systems.
557 """
566 """
558 pass
567 pass
559
568
560 class cachestat(object):
569 class cachestat(object):
561 def __init__(self, path):
570 def __init__(self, path):
562 self.stat = os.stat(path)
571 self.stat = os.stat(path)
563
572
564 def cacheable(self):
573 def cacheable(self):
565 return bool(self.stat.st_ino)
574 return bool(self.stat.st_ino)
566
575
567 __hash__ = object.__hash__
576 __hash__ = object.__hash__
568
577
569 def __eq__(self, other):
578 def __eq__(self, other):
570 try:
579 try:
571 # Only dev, ino, size, mtime and atime are likely to change. Out
580 # Only dev, ino, size, mtime and atime are likely to change. Out
572 # of these, we shouldn't compare atime but should compare the
581 # of these, we shouldn't compare atime but should compare the
573 # rest. However, one of the other fields changing indicates
582 # rest. However, one of the other fields changing indicates
574 # something fishy going on, so return False if anything but atime
583 # something fishy going on, so return False if anything but atime
575 # changes.
584 # changes.
576 return (self.stat.st_mode == other.stat.st_mode and
585 return (self.stat.st_mode == other.stat.st_mode and
577 self.stat.st_ino == other.stat.st_ino and
586 self.stat.st_ino == other.stat.st_ino and
578 self.stat.st_dev == other.stat.st_dev and
587 self.stat.st_dev == other.stat.st_dev and
579 self.stat.st_nlink == other.stat.st_nlink and
588 self.stat.st_nlink == other.stat.st_nlink and
580 self.stat.st_uid == other.stat.st_uid and
589 self.stat.st_uid == other.stat.st_uid and
581 self.stat.st_gid == other.stat.st_gid and
590 self.stat.st_gid == other.stat.st_gid and
582 self.stat.st_size == other.stat.st_size and
591 self.stat.st_size == other.stat.st_size and
583 self.stat.st_mtime == other.stat.st_mtime and
592 self.stat.st_mtime == other.stat.st_mtime and
584 self.stat.st_ctime == other.stat.st_ctime)
593 self.stat.st_ctime == other.stat.st_ctime)
585 except AttributeError:
594 except AttributeError:
586 return False
595 return False
587
596
588 def __ne__(self, other):
597 def __ne__(self, other):
589 return not self == other
598 return not self == other
590
599
591 def executablepath():
600 def executablepath():
592 return None # available on Windows only
601 return None # available on Windows only
593
602
594 def statislink(st):
603 def statislink(st):
595 '''check whether a stat result is a symlink'''
604 '''check whether a stat result is a symlink'''
596 return st and stat.S_ISLNK(st.st_mode)
605 return st and stat.S_ISLNK(st.st_mode)
597
606
598 def statisexec(st):
607 def statisexec(st):
599 '''check whether a stat result is an executable file'''
608 '''check whether a stat result is an executable file'''
600 return st and (st.st_mode & 0o100 != 0)
609 return st and (st.st_mode & 0o100 != 0)
601
610
602 def poll(fds):
611 def poll(fds):
603 """block until something happens on any file descriptor
612 """block until something happens on any file descriptor
604
613
605 This is a generic helper that will check for any activity
614 This is a generic helper that will check for any activity
606 (read, write. exception) and return the list of touched files.
615 (read, write. exception) and return the list of touched files.
607
616
608 In unsupported cases, it will raise a NotImplementedError"""
617 In unsupported cases, it will raise a NotImplementedError"""
609 try:
618 try:
610 while True:
619 while True:
611 try:
620 try:
612 res = select.select(fds, fds, fds)
621 res = select.select(fds, fds, fds)
613 break
622 break
614 except select.error as inst:
623 except select.error as inst:
615 if inst.args[0] == errno.EINTR:
624 if inst.args[0] == errno.EINTR:
616 continue
625 continue
617 raise
626 raise
618 except ValueError: # out of range file descriptor
627 except ValueError: # out of range file descriptor
619 raise NotImplementedError()
628 raise NotImplementedError()
620 return sorted(list(set(sum(res, []))))
629 return sorted(list(set(sum(res, []))))
621
630
622 def readpipe(pipe):
631 def readpipe(pipe):
623 """Read all available data from a pipe."""
632 """Read all available data from a pipe."""
624 # We can't fstat() a pipe because Linux will always report 0.
633 # We can't fstat() a pipe because Linux will always report 0.
625 # So, we set the pipe to non-blocking mode and read everything
634 # So, we set the pipe to non-blocking mode and read everything
626 # that's available.
635 # that's available.
627 flags = fcntl.fcntl(pipe, fcntl.F_GETFL)
636 flags = fcntl.fcntl(pipe, fcntl.F_GETFL)
628 flags |= os.O_NONBLOCK
637 flags |= os.O_NONBLOCK
629 oldflags = fcntl.fcntl(pipe, fcntl.F_SETFL, flags)
638 oldflags = fcntl.fcntl(pipe, fcntl.F_SETFL, flags)
630
639
631 try:
640 try:
632 chunks = []
641 chunks = []
633 while True:
642 while True:
634 try:
643 try:
635 s = pipe.read()
644 s = pipe.read()
636 if not s:
645 if not s:
637 break
646 break
638 chunks.append(s)
647 chunks.append(s)
639 except IOError:
648 except IOError:
640 break
649 break
641
650
642 return ''.join(chunks)
651 return ''.join(chunks)
643 finally:
652 finally:
644 fcntl.fcntl(pipe, fcntl.F_SETFL, oldflags)
653 fcntl.fcntl(pipe, fcntl.F_SETFL, oldflags)
645
654
646 def bindunixsocket(sock, path):
655 def bindunixsocket(sock, path):
647 """Bind the UNIX domain socket to the specified path"""
656 """Bind the UNIX domain socket to the specified path"""
648 # use relative path instead of full path at bind() if possible, since
657 # use relative path instead of full path at bind() if possible, since
649 # AF_UNIX path has very small length limit (107 chars) on common
658 # AF_UNIX path has very small length limit (107 chars) on common
650 # platforms (see sys/un.h)
659 # platforms (see sys/un.h)
651 dirname, basename = os.path.split(path)
660 dirname, basename = os.path.split(path)
652 bakwdfd = None
661 bakwdfd = None
653 if dirname:
662 if dirname:
654 bakwdfd = os.open('.', os.O_DIRECTORY)
663 bakwdfd = os.open('.', os.O_DIRECTORY)
655 os.chdir(dirname)
664 os.chdir(dirname)
656 sock.bind(basename)
665 sock.bind(basename)
657 if bakwdfd:
666 if bakwdfd:
658 os.fchdir(bakwdfd)
667 os.fchdir(bakwdfd)
659 os.close(bakwdfd)
668 os.close(bakwdfd)
@@ -1,410 +1,422
1 #require hardlink
1 #require hardlink
2
2
3 $ cat > nlinks.py <<EOF
3 $ cat > nlinks.py <<EOF
4 > import sys
4 > import sys
5 > from mercurial import util
5 > from mercurial import util
6 > for f in sorted(sys.stdin.readlines()):
6 > for f in sorted(sys.stdin.readlines()):
7 > f = f[:-1]
7 > f = f[:-1]
8 > print util.nlinks(f), f
8 > print util.nlinks(f), f
9 > EOF
9 > EOF
10
10
11 $ nlinksdir()
11 $ nlinksdir()
12 > {
12 > {
13 > find "$@" -type f | python $TESTTMP/nlinks.py
13 > find "$@" -type f | python $TESTTMP/nlinks.py
14 > }
14 > }
15
15
16 Some implementations of cp can't create hardlinks (replaces 'cp -al' on Linux):
16 Some implementations of cp can't create hardlinks (replaces 'cp -al' on Linux):
17
17
18 $ cat > linkcp.py <<EOF
18 $ cat > linkcp.py <<EOF
19 > from mercurial import util
19 > from mercurial import util
20 > import sys
20 > import sys
21 > util.copyfiles(sys.argv[1], sys.argv[2], hardlink=True)
21 > util.copyfiles(sys.argv[1], sys.argv[2], hardlink=True)
22 > EOF
22 > EOF
23
23
24 $ linkcp()
24 $ linkcp()
25 > {
25 > {
26 > python $TESTTMP/linkcp.py $1 $2
26 > python $TESTTMP/linkcp.py $1 $2
27 > }
27 > }
28
28
29 Prepare repo r1:
29 Prepare repo r1:
30
30
31 $ hg init r1
31 $ hg init r1
32 $ cd r1
32 $ cd r1
33
33
34 $ echo c1 > f1
34 $ echo c1 > f1
35 $ hg add f1
35 $ hg add f1
36 $ hg ci -m0
36 $ hg ci -m0
37
37
38 $ mkdir d1
38 $ mkdir d1
39 $ cd d1
39 $ cd d1
40 $ echo c2 > f2
40 $ echo c2 > f2
41 $ hg add f2
41 $ hg add f2
42 $ hg ci -m1
42 $ hg ci -m1
43 $ cd ../..
43 $ cd ../..
44
44
45 $ nlinksdir r1/.hg/store
45 $ nlinksdir r1/.hg/store
46 1 r1/.hg/store/00changelog.i
46 1 r1/.hg/store/00changelog.i
47 1 r1/.hg/store/00manifest.i
47 1 r1/.hg/store/00manifest.i
48 1 r1/.hg/store/data/d1/f2.i
48 1 r1/.hg/store/data/d1/f2.i
49 1 r1/.hg/store/data/f1.i
49 1 r1/.hg/store/data/f1.i
50 1 r1/.hg/store/fncache
50 1 r1/.hg/store/fncache
51 1 r1/.hg/store/phaseroots
51 1 r1/.hg/store/phaseroots
52 1 r1/.hg/store/undo
52 1 r1/.hg/store/undo
53 1 r1/.hg/store/undo.backup.fncache
53 1 r1/.hg/store/undo.backup.fncache
54 1 r1/.hg/store/undo.backupfiles
54 1 r1/.hg/store/undo.backupfiles
55 1 r1/.hg/store/undo.phaseroots
55 1 r1/.hg/store/undo.phaseroots
56
56
57
57
58 Create hardlinked clone r2:
58 Create hardlinked clone r2:
59
59
60 $ hg clone -U --debug r1 r2 --config progress.debug=true
60 $ hg clone -U --debug r1 r2 --config progress.debug=true
61 linking: 1
61 linking: 1
62 linking: 2
62 linking: 2
63 linking: 3
63 linking: 3
64 linking: 4
64 linking: 4
65 linking: 5
65 linking: 5
66 linking: 6
66 linking: 6
67 linking: 7
67 linking: 7
68 linked 7 files
68 linked 7 files
69
69
70 Create non-hardlinked clone r3:
70 Create non-hardlinked clone r3:
71
71
72 $ hg clone --pull r1 r3
72 $ hg clone --pull r1 r3
73 requesting all changes
73 requesting all changes
74 adding changesets
74 adding changesets
75 adding manifests
75 adding manifests
76 adding file changes
76 adding file changes
77 added 2 changesets with 2 changes to 2 files
77 added 2 changesets with 2 changes to 2 files
78 updating to branch default
78 updating to branch default
79 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
79 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
80
80
81
81
82 Repos r1 and r2 should now contain hardlinked files:
82 Repos r1 and r2 should now contain hardlinked files:
83
83
84 $ nlinksdir r1/.hg/store
84 $ nlinksdir r1/.hg/store
85 2 r1/.hg/store/00changelog.i
85 2 r1/.hg/store/00changelog.i
86 2 r1/.hg/store/00manifest.i
86 2 r1/.hg/store/00manifest.i
87 2 r1/.hg/store/data/d1/f2.i
87 2 r1/.hg/store/data/d1/f2.i
88 2 r1/.hg/store/data/f1.i
88 2 r1/.hg/store/data/f1.i
89 2 r1/.hg/store/fncache
89 2 r1/.hg/store/fncache
90 1 r1/.hg/store/phaseroots
90 1 r1/.hg/store/phaseroots
91 1 r1/.hg/store/undo
91 1 r1/.hg/store/undo
92 1 r1/.hg/store/undo.backup.fncache
92 1 r1/.hg/store/undo.backup.fncache
93 1 r1/.hg/store/undo.backupfiles
93 1 r1/.hg/store/undo.backupfiles
94 1 r1/.hg/store/undo.phaseroots
94 1 r1/.hg/store/undo.phaseroots
95
95
96 $ nlinksdir r2/.hg/store
96 $ nlinksdir r2/.hg/store
97 2 r2/.hg/store/00changelog.i
97 2 r2/.hg/store/00changelog.i
98 2 r2/.hg/store/00manifest.i
98 2 r2/.hg/store/00manifest.i
99 2 r2/.hg/store/data/d1/f2.i
99 2 r2/.hg/store/data/d1/f2.i
100 2 r2/.hg/store/data/f1.i
100 2 r2/.hg/store/data/f1.i
101 2 r2/.hg/store/fncache
101 2 r2/.hg/store/fncache
102
102
103 Repo r3 should not be hardlinked:
103 Repo r3 should not be hardlinked:
104
104
105 $ nlinksdir r3/.hg/store
105 $ nlinksdir r3/.hg/store
106 1 r3/.hg/store/00changelog.i
106 1 r3/.hg/store/00changelog.i
107 1 r3/.hg/store/00manifest.i
107 1 r3/.hg/store/00manifest.i
108 1 r3/.hg/store/data/d1/f2.i
108 1 r3/.hg/store/data/d1/f2.i
109 1 r3/.hg/store/data/f1.i
109 1 r3/.hg/store/data/f1.i
110 1 r3/.hg/store/fncache
110 1 r3/.hg/store/fncache
111 1 r3/.hg/store/phaseroots
111 1 r3/.hg/store/phaseroots
112 1 r3/.hg/store/undo
112 1 r3/.hg/store/undo
113 1 r3/.hg/store/undo.backupfiles
113 1 r3/.hg/store/undo.backupfiles
114 1 r3/.hg/store/undo.phaseroots
114 1 r3/.hg/store/undo.phaseroots
115
115
116
116
117 Create a non-inlined filelog in r3:
117 Create a non-inlined filelog in r3:
118
118
119 $ cd r3/d1
119 $ cd r3/d1
120 >>> f = open('data1', 'wb')
120 >>> f = open('data1', 'wb')
121 >>> for x in range(10000):
121 >>> for x in range(10000):
122 ... f.write("%s\n" % str(x))
122 ... f.write("%s\n" % str(x))
123 >>> f.close()
123 >>> f.close()
124 $ for j in 0 1 2 3 4 5 6 7 8 9; do
124 $ for j in 0 1 2 3 4 5 6 7 8 9; do
125 > cat data1 >> f2
125 > cat data1 >> f2
126 > hg commit -m$j
126 > hg commit -m$j
127 > done
127 > done
128 $ cd ../..
128 $ cd ../..
129
129
130 $ nlinksdir r3/.hg/store
130 $ nlinksdir r3/.hg/store
131 1 r3/.hg/store/00changelog.i
131 1 r3/.hg/store/00changelog.i
132 1 r3/.hg/store/00manifest.i
132 1 r3/.hg/store/00manifest.i
133 1 r3/.hg/store/data/d1/f2.d
133 1 r3/.hg/store/data/d1/f2.d
134 1 r3/.hg/store/data/d1/f2.i
134 1 r3/.hg/store/data/d1/f2.i
135 1 r3/.hg/store/data/f1.i
135 1 r3/.hg/store/data/f1.i
136 1 r3/.hg/store/fncache
136 1 r3/.hg/store/fncache
137 1 r3/.hg/store/phaseroots
137 1 r3/.hg/store/phaseroots
138 1 r3/.hg/store/undo
138 1 r3/.hg/store/undo
139 1 r3/.hg/store/undo.backup.fncache
139 1 r3/.hg/store/undo.backup.fncache
140 1 r3/.hg/store/undo.backup.phaseroots
140 1 r3/.hg/store/undo.backup.phaseroots
141 1 r3/.hg/store/undo.backupfiles
141 1 r3/.hg/store/undo.backupfiles
142 1 r3/.hg/store/undo.phaseroots
142 1 r3/.hg/store/undo.phaseroots
143
143
144 Push to repo r1 should break up most hardlinks in r2:
144 Push to repo r1 should break up most hardlinks in r2:
145
145
146 $ hg -R r2 verify
146 $ hg -R r2 verify
147 checking changesets
147 checking changesets
148 checking manifests
148 checking manifests
149 crosschecking files in changesets and manifests
149 crosschecking files in changesets and manifests
150 checking files
150 checking files
151 2 files, 2 changesets, 2 total revisions
151 2 files, 2 changesets, 2 total revisions
152
152
153 $ cd r3
153 $ cd r3
154 $ hg push
154 $ hg push
155 pushing to $TESTTMP/r1 (glob)
155 pushing to $TESTTMP/r1 (glob)
156 searching for changes
156 searching for changes
157 adding changesets
157 adding changesets
158 adding manifests
158 adding manifests
159 adding file changes
159 adding file changes
160 added 10 changesets with 10 changes to 1 files
160 added 10 changesets with 10 changes to 1 files
161
161
162 $ cd ..
162 $ cd ..
163
163
164 $ nlinksdir r2/.hg/store
164 $ nlinksdir r2/.hg/store
165 1 r2/.hg/store/00changelog.i
165 1 r2/.hg/store/00changelog.i
166 1 r2/.hg/store/00manifest.i
166 1 r2/.hg/store/00manifest.i
167 1 r2/.hg/store/data/d1/f2.i
167 1 r2/.hg/store/data/d1/f2.i
168 2 r2/.hg/store/data/f1.i
168 2 r2/.hg/store/data/f1.i
169 [12] r2/\.hg/store/fncache (re)
169 [12] r2/\.hg/store/fncache (re)
170
170
171 #if hardlink-whitelisted
171 #if hardlink-whitelisted
172 $ nlinksdir r2/.hg/store/fncache
172 $ nlinksdir r2/.hg/store/fncache
173 2 r2/.hg/store/fncache
173 2 r2/.hg/store/fncache
174 #endif
174 #endif
175
175
176 $ hg -R r2 verify
176 $ hg -R r2 verify
177 checking changesets
177 checking changesets
178 checking manifests
178 checking manifests
179 crosschecking files in changesets and manifests
179 crosschecking files in changesets and manifests
180 checking files
180 checking files
181 2 files, 2 changesets, 2 total revisions
181 2 files, 2 changesets, 2 total revisions
182
182
183
183
184 $ cd r1
184 $ cd r1
185 $ hg up
185 $ hg up
186 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
186 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
187
187
188 Committing a change to f1 in r1 must break up hardlink f1.i in r2:
188 Committing a change to f1 in r1 must break up hardlink f1.i in r2:
189
189
190 $ echo c1c1 >> f1
190 $ echo c1c1 >> f1
191 $ hg ci -m00
191 $ hg ci -m00
192 $ cd ..
192 $ cd ..
193
193
194 $ nlinksdir r2/.hg/store
194 $ nlinksdir r2/.hg/store
195 1 r2/.hg/store/00changelog.i
195 1 r2/.hg/store/00changelog.i
196 1 r2/.hg/store/00manifest.i
196 1 r2/.hg/store/00manifest.i
197 1 r2/.hg/store/data/d1/f2.i
197 1 r2/.hg/store/data/d1/f2.i
198 1 r2/.hg/store/data/f1.i
198 1 r2/.hg/store/data/f1.i
199 [12] r2/\.hg/store/fncache (re)
199 [12] r2/\.hg/store/fncache (re)
200
200
201 #if hardlink-whitelisted
201 #if hardlink-whitelisted
202 $ nlinksdir r2/.hg/store/fncache
202 $ nlinksdir r2/.hg/store/fncache
203 2 r2/.hg/store/fncache
203 2 r2/.hg/store/fncache
204 #endif
204 #endif
205
205
206 Create a file which exec permissions we will change
207 $ cd r3
208 $ echo "echo hello world" > f3
209 $ hg add f3
210 $ hg ci -mf3
211 $ cd ..
212
206 $ cd r3
213 $ cd r3
207 $ hg tip --template '{rev}:{node|short}\n'
214 $ hg tip --template '{rev}:{node|short}\n'
208 11:a6451b6bc41f
215 12:d3b77733a28a
209 $ echo bla > f1
216 $ echo bla > f1
217 $ chmod +x f3
210 $ hg ci -m1
218 $ hg ci -m1
211 $ cd ..
219 $ cd ..
212
220
213 Create hardlinked copy r4 of r3 (on Linux, we would call 'cp -al'):
221 Create hardlinked copy r4 of r3 (on Linux, we would call 'cp -al'):
214
222
215 $ linkcp r3 r4
223 $ linkcp r3 r4
216
224
217 'checklink' is produced by hardlinking a symlink, which is undefined whether
225 'checklink' is produced by hardlinking a symlink, which is undefined whether
218 the symlink should be followed or not. It does behave differently on Linux and
226 the symlink should be followed or not. It does behave differently on Linux and
219 BSD. Just remove it so the test pass on both platforms.
227 BSD. Just remove it so the test pass on both platforms.
220
228
221 $ rm -f r4/.hg/cache/checklink
229 $ rm -f r4/.hg/cache/checklink
222
230
223 r4 has hardlinks in the working dir (not just inside .hg):
231 r4 has hardlinks in the working dir (not just inside .hg):
224
232
225 $ nlinksdir r4
233 $ nlinksdir r4
226 2 r4/.hg/00changelog.i
234 2 r4/.hg/00changelog.i
227 2 r4/.hg/branch
235 2 r4/.hg/branch
228 2 r4/.hg/cache/branch2-base
236 2 r4/.hg/cache/branch2-base
229 2 r4/.hg/cache/branch2-served
237 2 r4/.hg/cache/branch2-served
230 2 r4/.hg/cache/checkisexec (execbit !)
238 2 r4/.hg/cache/checkisexec (execbit !)
231 ? r4/.hg/cache/checklink-target (glob) (symlink !)
239 ? r4/.hg/cache/checklink-target (glob) (symlink !)
232 2 r4/.hg/cache/checknoexec (execbit !)
240 2 r4/.hg/cache/checknoexec (execbit !)
233 2 r4/.hg/cache/rbc-names-v1
241 2 r4/.hg/cache/rbc-names-v1
234 2 r4/.hg/cache/rbc-revs-v1
242 2 r4/.hg/cache/rbc-revs-v1
235 2 r4/.hg/dirstate
243 2 r4/.hg/dirstate
236 2 r4/.hg/hgrc
244 2 r4/.hg/hgrc
237 2 r4/.hg/last-message.txt
245 2 r4/.hg/last-message.txt
238 2 r4/.hg/requires
246 2 r4/.hg/requires
239 2 r4/.hg/store/00changelog.i
247 2 r4/.hg/store/00changelog.i
240 2 r4/.hg/store/00manifest.i
248 2 r4/.hg/store/00manifest.i
241 2 r4/.hg/store/data/d1/f2.d
249 2 r4/.hg/store/data/d1/f2.d
242 2 r4/.hg/store/data/d1/f2.i
250 2 r4/.hg/store/data/d1/f2.i
243 2 r4/.hg/store/data/f1.i
251 2 r4/.hg/store/data/f1.i
252 2 r4/.hg/store/data/f3.i
244 2 r4/.hg/store/fncache
253 2 r4/.hg/store/fncache
245 2 r4/.hg/store/phaseroots
254 2 r4/.hg/store/phaseroots
246 2 r4/.hg/store/undo
255 2 r4/.hg/store/undo
247 2 r4/.hg/store/undo.backup.fncache
256 2 r4/.hg/store/undo.backup.fncache
248 2 r4/.hg/store/undo.backup.phaseroots
257 2 r4/.hg/store/undo.backup.phaseroots
249 2 r4/.hg/store/undo.backupfiles
258 2 r4/.hg/store/undo.backupfiles
250 2 r4/.hg/store/undo.phaseroots
259 2 r4/.hg/store/undo.phaseroots
251 [24] r4/\.hg/undo\.backup\.dirstate (re)
260 [24] r4/\.hg/undo\.backup\.dirstate (re)
252 2 r4/.hg/undo.bookmarks
261 2 r4/.hg/undo.bookmarks
253 2 r4/.hg/undo.branch
262 2 r4/.hg/undo.branch
254 2 r4/.hg/undo.desc
263 2 r4/.hg/undo.desc
255 [24] r4/\.hg/undo\.dirstate (re)
264 [24] r4/\.hg/undo\.dirstate (re)
256 2 r4/d1/data1
265 2 r4/d1/data1
257 2 r4/d1/f2
266 2 r4/d1/f2
258 2 r4/f1
267 2 r4/f1
268 2 r4/f3
259
269
270 Update back to revision 12 in r4 should break hardlink of file f1 and f3:
260 #if hardlink-whitelisted
271 #if hardlink-whitelisted
261 $ nlinksdir r4/.hg/undo.backup.dirstate r4/.hg/undo.dirstate
272 $ nlinksdir r4/.hg/undo.backup.dirstate r4/.hg/undo.dirstate
262 4 r4/.hg/undo.backup.dirstate
273 4 r4/.hg/undo.backup.dirstate
263 4 r4/.hg/undo.dirstate
274 4 r4/.hg/undo.dirstate
264 #endif
275 #endif
265
276
266 Update back to revision 11 in r4 should break hardlink of file f1:
267
277
268 $ hg -R r4 up 11
278 $ hg -R r4 up 12
269 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
279 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
270
280
271 $ nlinksdir r4
281 $ nlinksdir r4
272 2 r4/.hg/00changelog.i
282 2 r4/.hg/00changelog.i
273 1 r4/.hg/branch
283 1 r4/.hg/branch
274 2 r4/.hg/cache/branch2-base
284 2 r4/.hg/cache/branch2-base
275 2 r4/.hg/cache/branch2-served
285 2 r4/.hg/cache/branch2-served
276 2 r4/.hg/cache/checkisexec (execbit !)
286 2 r4/.hg/cache/checkisexec (execbit !)
277 2 r4/.hg/cache/checklink-target (symlink !)
287 2 r4/.hg/cache/checklink-target (symlink !)
278 2 r4/.hg/cache/checknoexec (execbit !)
288 2 r4/.hg/cache/checknoexec (execbit !)
279 2 r4/.hg/cache/rbc-names-v1
289 2 r4/.hg/cache/rbc-names-v1
280 2 r4/.hg/cache/rbc-revs-v1
290 2 r4/.hg/cache/rbc-revs-v1
281 1 r4/.hg/dirstate
291 1 r4/.hg/dirstate
282 2 r4/.hg/hgrc
292 2 r4/.hg/hgrc
283 2 r4/.hg/last-message.txt
293 2 r4/.hg/last-message.txt
284 2 r4/.hg/requires
294 2 r4/.hg/requires
285 2 r4/.hg/store/00changelog.i
295 2 r4/.hg/store/00changelog.i
286 2 r4/.hg/store/00manifest.i
296 2 r4/.hg/store/00manifest.i
287 2 r4/.hg/store/data/d1/f2.d
297 2 r4/.hg/store/data/d1/f2.d
288 2 r4/.hg/store/data/d1/f2.i
298 2 r4/.hg/store/data/d1/f2.i
289 2 r4/.hg/store/data/f1.i
299 2 r4/.hg/store/data/f1.i
300 2 r4/.hg/store/data/f3.i
290 2 r4/.hg/store/fncache
301 2 r4/.hg/store/fncache
291 2 r4/.hg/store/phaseroots
302 2 r4/.hg/store/phaseroots
292 2 r4/.hg/store/undo
303 2 r4/.hg/store/undo
293 2 r4/.hg/store/undo.backup.fncache
304 2 r4/.hg/store/undo.backup.fncache
294 2 r4/.hg/store/undo.backup.phaseroots
305 2 r4/.hg/store/undo.backup.phaseroots
295 2 r4/.hg/store/undo.backupfiles
306 2 r4/.hg/store/undo.backupfiles
296 2 r4/.hg/store/undo.phaseroots
307 2 r4/.hg/store/undo.phaseroots
297 [24] r4/\.hg/undo\.backup\.dirstate (re)
308 [24] r4/\.hg/undo\.backup\.dirstate (re)
298 2 r4/.hg/undo.bookmarks
309 2 r4/.hg/undo.bookmarks
299 2 r4/.hg/undo.branch
310 2 r4/.hg/undo.branch
300 2 r4/.hg/undo.desc
311 2 r4/.hg/undo.desc
301 [24] r4/\.hg/undo\.dirstate (re)
312 [24] r4/\.hg/undo\.dirstate (re)
302 2 r4/d1/data1
313 2 r4/d1/data1
303 2 r4/d1/f2
314 2 r4/d1/f2
304 1 r4/f1
315 1 r4/f1
316 1 r4/f3
305
317
306 #if hardlink-whitelisted
318 #if hardlink-whitelisted
307 $ nlinksdir r4/.hg/undo.backup.dirstate r4/.hg/undo.dirstate
319 $ nlinksdir r4/.hg/undo.backup.dirstate r4/.hg/undo.dirstate
308 4 r4/.hg/undo.backup.dirstate
320 4 r4/.hg/undo.backup.dirstate
309 4 r4/.hg/undo.dirstate
321 4 r4/.hg/undo.dirstate
310 #endif
322 #endif
311
323
312 Test hardlinking outside hg:
324 Test hardlinking outside hg:
313
325
314 $ mkdir x
326 $ mkdir x
315 $ echo foo > x/a
327 $ echo foo > x/a
316
328
317 $ linkcp x y
329 $ linkcp x y
318 $ echo bar >> y/a
330 $ echo bar >> y/a
319
331
320 No diff if hardlink:
332 No diff if hardlink:
321
333
322 $ diff x/a y/a
334 $ diff x/a y/a
323
335
324 Test mq hardlinking:
336 Test mq hardlinking:
325
337
326 $ echo "[extensions]" >> $HGRCPATH
338 $ echo "[extensions]" >> $HGRCPATH
327 $ echo "mq=" >> $HGRCPATH
339 $ echo "mq=" >> $HGRCPATH
328
340
329 $ hg init a
341 $ hg init a
330 $ cd a
342 $ cd a
331
343
332 $ hg qimport -n foo - << EOF
344 $ hg qimport -n foo - << EOF
333 > # HG changeset patch
345 > # HG changeset patch
334 > # Date 1 0
346 > # Date 1 0
335 > diff -r 2588a8b53d66 a
347 > diff -r 2588a8b53d66 a
336 > --- /dev/null Thu Jan 01 00:00:00 1970 +0000
348 > --- /dev/null Thu Jan 01 00:00:00 1970 +0000
337 > +++ b/a Wed Jul 23 15:54:29 2008 +0200
349 > +++ b/a Wed Jul 23 15:54:29 2008 +0200
338 > @@ -0,0 +1,1 @@
350 > @@ -0,0 +1,1 @@
339 > +a
351 > +a
340 > EOF
352 > EOF
341 adding foo to series file
353 adding foo to series file
342
354
343 $ hg qpush
355 $ hg qpush
344 applying foo
356 applying foo
345 now at: foo
357 now at: foo
346
358
347 $ cd ..
359 $ cd ..
348 $ linkcp a b
360 $ linkcp a b
349 $ cd b
361 $ cd b
350
362
351 $ hg qimport -n bar - << EOF
363 $ hg qimport -n bar - << EOF
352 > # HG changeset patch
364 > # HG changeset patch
353 > # Date 2 0
365 > # Date 2 0
354 > diff -r 2588a8b53d66 a
366 > diff -r 2588a8b53d66 a
355 > --- /dev/null Thu Jan 01 00:00:00 1970 +0000
367 > --- /dev/null Thu Jan 01 00:00:00 1970 +0000
356 > +++ b/b Wed Jul 23 15:54:29 2008 +0200
368 > +++ b/b Wed Jul 23 15:54:29 2008 +0200
357 > @@ -0,0 +1,1 @@
369 > @@ -0,0 +1,1 @@
358 > +b
370 > +b
359 > EOF
371 > EOF
360 adding bar to series file
372 adding bar to series file
361
373
362 $ hg qpush
374 $ hg qpush
363 applying bar
375 applying bar
364 now at: bar
376 now at: bar
365
377
366 $ cat .hg/patches/status
378 $ cat .hg/patches/status
367 430ed4828a74fa4047bc816a25500f7472ab4bfe:foo
379 430ed4828a74fa4047bc816a25500f7472ab4bfe:foo
368 4e7abb4840c46a910f6d7b4d3c3fc7e5209e684c:bar
380 4e7abb4840c46a910f6d7b4d3c3fc7e5209e684c:bar
369
381
370 $ cat .hg/patches/series
382 $ cat .hg/patches/series
371 foo
383 foo
372 bar
384 bar
373
385
374 $ cat ../a/.hg/patches/status
386 $ cat ../a/.hg/patches/status
375 430ed4828a74fa4047bc816a25500f7472ab4bfe:foo
387 430ed4828a74fa4047bc816a25500f7472ab4bfe:foo
376
388
377 $ cat ../a/.hg/patches/series
389 $ cat ../a/.hg/patches/series
378 foo
390 foo
379
391
380 Test tags hardlinking:
392 Test tags hardlinking:
381
393
382 $ hg qdel -r qbase:qtip
394 $ hg qdel -r qbase:qtip
383 patch foo finalized without changeset message
395 patch foo finalized without changeset message
384 patch bar finalized without changeset message
396 patch bar finalized without changeset message
385
397
386 $ hg tag -l lfoo
398 $ hg tag -l lfoo
387 $ hg tag foo
399 $ hg tag foo
388
400
389 $ cd ..
401 $ cd ..
390 $ linkcp b c
402 $ linkcp b c
391 $ cd c
403 $ cd c
392
404
393 $ hg tag -l -r 0 lbar
405 $ hg tag -l -r 0 lbar
394 $ hg tag -r 0 bar
406 $ hg tag -r 0 bar
395
407
396 $ cat .hgtags
408 $ cat .hgtags
397 4e7abb4840c46a910f6d7b4d3c3fc7e5209e684c foo
409 4e7abb4840c46a910f6d7b4d3c3fc7e5209e684c foo
398 430ed4828a74fa4047bc816a25500f7472ab4bfe bar
410 430ed4828a74fa4047bc816a25500f7472ab4bfe bar
399
411
400 $ cat .hg/localtags
412 $ cat .hg/localtags
401 4e7abb4840c46a910f6d7b4d3c3fc7e5209e684c lfoo
413 4e7abb4840c46a910f6d7b4d3c3fc7e5209e684c lfoo
402 430ed4828a74fa4047bc816a25500f7472ab4bfe lbar
414 430ed4828a74fa4047bc816a25500f7472ab4bfe lbar
403
415
404 $ cat ../b/.hgtags
416 $ cat ../b/.hgtags
405 4e7abb4840c46a910f6d7b4d3c3fc7e5209e684c foo
417 4e7abb4840c46a910f6d7b4d3c3fc7e5209e684c foo
406
418
407 $ cat ../b/.hg/localtags
419 $ cat ../b/.hg/localtags
408 4e7abb4840c46a910f6d7b4d3c3fc7e5209e684c lfoo
420 4e7abb4840c46a910f6d7b4d3c3fc7e5209e684c lfoo
409
421
410 $ cd ..
422 $ cd ..
General Comments 0
You need to be logged in to leave comments. Login now