Show More
@@ -0,0 +1,142 b'' | |||||
|
1 | #require execbit unix-permissions | |||
|
2 | ||||
|
3 | Checking that experimental.atomic-file works. | |||
|
4 | ||||
|
5 | $ cat > $TESTTMP/show_mode.py <<EOF | |||
|
6 | > from __future__ import print_function | |||
|
7 | > import sys | |||
|
8 | > import os | |||
|
9 | > from stat import ST_MODE | |||
|
10 | > | |||
|
11 | > for file_path in sys.argv[1:]: | |||
|
12 | > file_stat = os.stat(file_path) | |||
|
13 | > octal_mode = oct(file_stat[ST_MODE] & 0o777) | |||
|
14 | > print("%s:%s" % (file_path, octal_mode)) | |||
|
15 | > | |||
|
16 | > EOF | |||
|
17 | ||||
|
18 | $ hg init repo | |||
|
19 | $ cd repo | |||
|
20 | ||||
|
21 | $ cat > .hg/showwrites.py <<EOF | |||
|
22 | > def uisetup(ui): | |||
|
23 | > from mercurial import vfs | |||
|
24 | > class newvfs(vfs.vfs): | |||
|
25 | > def __call__(self, *args, **kwargs): | |||
|
26 | > print('vfs open', args, sorted(list(kwargs.items()))) | |||
|
27 | > return super(newvfs, self).__call__(*args, **kwargs) | |||
|
28 | > vfs.vfs = newvfs | |||
|
29 | > EOF | |||
|
30 | ||||
|
31 | $ for v in a1 a2 b1 b2 c ro; do echo $v > $v; done | |||
|
32 | $ chmod +x b* | |||
|
33 | $ hg commit -Aqm _ | |||
|
34 | ||||
|
35 | # We check that | |||
|
36 | # - the changes are actually atomic | |||
|
37 | # - that permissions are correct (all 4 cases of (executable before) * (executable after)) | |||
|
38 | # - that renames work, though they should be atomic anyway | |||
|
39 | # - that it works when source files are read-only (but directories are read-write still) | |||
|
40 | ||||
|
41 | $ for v in a1 a2 b1 b2 ro; do echo changed-$v > $v; done | |||
|
42 | $ chmod -x *1; chmod +x *2 | |||
|
43 | $ hg rename c d | |||
|
44 | $ hg commit -qm _ | |||
|
45 | ||||
|
46 | Check behavior without update.atomic-file | |||
|
47 | ||||
|
48 | $ hg update -r 0 -q | |||
|
49 | $ hg update -r 1 --config extensions.showwrites=.hg/showwrites.py 2>&1 | grep "a1'.*wb" | |||
|
50 | ('vfs open', ('a1', 'wb'), [('atomictemp', False), ('backgroundclose', True)]) | |||
|
51 | ||||
|
52 | $ python $TESTTMP/show_mode.py * | |||
|
53 | a1:0644 | |||
|
54 | a2:0755 | |||
|
55 | b1:0644 | |||
|
56 | b2:0755 | |||
|
57 | d:0644 | |||
|
58 | ro:0644 | |||
|
59 | ||||
|
60 | Add a second revision for the ro file so we can test update when the file is | |||
|
61 | present or not | |||
|
62 | ||||
|
63 | $ echo "ro" > ro | |||
|
64 | ||||
|
65 | $ hg commit -qm _ | |||
|
66 | ||||
|
67 | Check behavior without update.atomic-file first | |||
|
68 | ||||
|
69 | $ hg update -C -r 0 -q | |||
|
70 | ||||
|
71 | $ hg update -r 1 | |||
|
72 | 6 files updated, 0 files merged, 1 files removed, 0 files unresolved | |||
|
73 | ||||
|
74 | $ python $TESTTMP/show_mode.py * | |||
|
75 | a1:0644 | |||
|
76 | a2:0755 | |||
|
77 | b1:0644 | |||
|
78 | b2:0755 | |||
|
79 | d:0644 | |||
|
80 | ro:0644 | |||
|
81 | ||||
|
82 | Manually reset the mode of the read-only file | |||
|
83 | ||||
|
84 | $ chmod a-w ro | |||
|
85 | ||||
|
86 | $ python $TESTTMP/show_mode.py ro | |||
|
87 | ro:0444 | |||
|
88 | ||||
|
89 | Now the file is present, try to update and check the permissions of the file | |||
|
90 | ||||
|
91 | $ hg up -r 2 | |||
|
92 | 1 files updated, 0 files merged, 0 files removed, 0 files unresolved | |||
|
93 | ||||
|
94 | $ python $TESTTMP/show_mode.py ro | |||
|
95 | ro:0644 | |||
|
96 | ||||
|
97 | # The file which was read-only is now writable in the default behavior | |||
|
98 | ||||
|
99 | Check behavior with update.atomic-files | |||
|
100 | ||||
|
101 | ||||
|
102 | $ cat >> .hg/hgrc <<EOF | |||
|
103 | > [experimental] | |||
|
104 | > update.atomic-file = true | |||
|
105 | > EOF | |||
|
106 | ||||
|
107 | $ hg update -C -r 0 -q | |||
|
108 | $ hg update -r 1 --config extensions.showwrites=.hg/showwrites.py 2>&1 | grep "a1'.*wb" | |||
|
109 | ('vfs open', ('a1', 'wb'), [('atomictemp', True), ('backgroundclose', True)]) | |||
|
110 | $ hg st -A --rev 1 | |||
|
111 | C a1 | |||
|
112 | C a2 | |||
|
113 | C b1 | |||
|
114 | C b2 | |||
|
115 | C d | |||
|
116 | C ro | |||
|
117 | ||||
|
118 | Check the file permission after update | |||
|
119 | $ python $TESTTMP/show_mode.py * | |||
|
120 | a1:0644 | |||
|
121 | a2:0755 | |||
|
122 | b1:0644 | |||
|
123 | b2:0755 | |||
|
124 | d:0644 | |||
|
125 | ro:0644 | |||
|
126 | ||||
|
127 | Manually reset the mode of the read-only file | |||
|
128 | ||||
|
129 | $ chmod a-w ro | |||
|
130 | ||||
|
131 | $ python $TESTTMP/show_mode.py ro | |||
|
132 | ro:0444 | |||
|
133 | ||||
|
134 | Now the file is present, try to update and check the permissions of the file | |||
|
135 | ||||
|
136 | $ hg update -r 2 --traceback | |||
|
137 | 1 files updated, 0 files merged, 0 files removed, 0 files unresolved | |||
|
138 | ||||
|
139 | $ python $TESTTMP/show_mode.py ro | |||
|
140 | ro:0644 | |||
|
141 | ||||
|
142 | # The behavior is the same as without atomic update |
@@ -153,7 +153,7 b' def setflags(f, l, x):' | |||||
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, enforcewritable=False): | |
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.''' | |
@@ -166,7 +166,13 b' def copymode(src, dst, mode=None):' | |||||
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 | ||
|
170 | new_mode = st_mode | |||
|
171 | ||||
|
172 | if enforcewritable: | |||
|
173 | new_mode |= stat.S_IWUSR | |||
|
174 | ||||
|
175 | os.chmod(dst, new_mode) | |||
170 |
|
176 | |||
171 | def checkexec(path): |
|
177 | def checkexec(path): | |
172 | """ |
|
178 | """ |
@@ -2045,7 +2045,7 b' def splitpath(path):' | |||||
2045 | function if need.''' |
|
2045 | function if need.''' | |
2046 | return path.split(pycompat.ossep) |
|
2046 | return path.split(pycompat.ossep) | |
2047 |
|
2047 | |||
2048 | def mktempcopy(name, emptyok=False, createmode=None): |
|
2048 | def mktempcopy(name, emptyok=False, createmode=None, enforcewritable=False): | |
2049 | """Create a temporary file with the same contents from name |
|
2049 | """Create a temporary file with the same contents from name | |
2050 |
|
2050 | |||
2051 | The permission bits are copied from the original file. |
|
2051 | The permission bits are copied from the original file. | |
@@ -2061,7 +2061,8 b' def mktempcopy(name, emptyok=False, crea' | |||||
2061 | # Temporary files are created with mode 0600, which is usually not |
|
2061 | # Temporary files are created with mode 0600, which is usually not | |
2062 | # what we want. If the original file already exists, just copy |
|
2062 | # what we want. If the original file already exists, just copy | |
2063 | # its mode. Otherwise, manually obey umask. |
|
2063 | # its mode. Otherwise, manually obey umask. | |
2064 | copymode(name, temp, createmode) |
|
2064 | copymode(name, temp, createmode, enforcewritable) | |
|
2065 | ||||
2065 | if emptyok: |
|
2066 | if emptyok: | |
2066 | return temp |
|
2067 | return temp | |
2067 | try: |
|
2068 | try: | |
@@ -2204,7 +2205,9 b' class atomictempfile(object):' | |||||
2204 | def __init__(self, name, mode='w+b', createmode=None, checkambig=False): |
|
2205 | def __init__(self, name, mode='w+b', createmode=None, checkambig=False): | |
2205 | self.__name = name # permanent name |
|
2206 | self.__name = name # permanent name | |
2206 | self._tempname = mktempcopy(name, emptyok=('w' in mode), |
|
2207 | self._tempname = mktempcopy(name, emptyok=('w' in mode), | |
2207 |
createmode=createmode |
|
2208 | createmode=createmode, | |
|
2209 | enforcewritable=('w' in mode)) | |||
|
2210 | ||||
2208 | self._fp = posixfile(self._tempname, mode) |
|
2211 | self._fp = posixfile(self._tempname, mode) | |
2209 | self._checkambig = checkambig |
|
2212 | self._checkambig = checkambig | |
2210 |
|
2213 |
@@ -248,7 +248,7 b' def sshargs(sshcmd, host, user, port):' | |||||
248 | def setflags(f, l, x): |
|
248 | def setflags(f, l, x): | |
249 | pass |
|
249 | pass | |
250 |
|
250 | |||
251 | def copymode(src, dst, mode=None): |
|
251 | def copymode(src, dst, mode=None, enforcewritable=False): | |
252 | pass |
|
252 | pass | |
253 |
|
253 | |||
254 | def checkexec(path): |
|
254 | def checkexec(path): |
General Comments 0
You need to be logged in to leave comments.
Login now