##// END OF EJS Templates
dirstate: deal with read-race for python code using rust object...
marmoute -
r51133:c9066fc6 stable
parent child Browse files
Show More
@@ -1,733 +1,732 b''
1 # dirstatemap.py
1 # dirstatemap.py
2 #
2 #
3 # This software may be used and distributed according to the terms of the
3 # This software may be used and distributed according to the terms of the
4 # GNU General Public License version 2 or any later version.
4 # GNU General Public License version 2 or any later version.
5
5
6
6
7 from .i18n import _
7 from .i18n import _
8
8
9 from . import (
9 from . import (
10 error,
10 error,
11 pathutil,
11 pathutil,
12 policy,
12 policy,
13 testing,
13 testing,
14 txnutil,
14 txnutil,
15 util,
15 util,
16 )
16 )
17
17
18 from .dirstateutils import (
18 from .dirstateutils import (
19 docket as docketmod,
19 docket as docketmod,
20 v2,
20 v2,
21 )
21 )
22
22
23 parsers = policy.importmod('parsers')
23 parsers = policy.importmod('parsers')
24 rustmod = policy.importrust('dirstate')
24 rustmod = policy.importrust('dirstate')
25
25
26 propertycache = util.propertycache
26 propertycache = util.propertycache
27
27
28 if rustmod is None:
28 if rustmod is None:
29 DirstateItem = parsers.DirstateItem
29 DirstateItem = parsers.DirstateItem
30 else:
30 else:
31 DirstateItem = rustmod.DirstateItem
31 DirstateItem = rustmod.DirstateItem
32
32
33 rangemask = 0x7FFFFFFF
33 rangemask = 0x7FFFFFFF
34
34
35 WRITE_MODE_AUTO = 0
35 WRITE_MODE_AUTO = 0
36 WRITE_MODE_FORCE_NEW = 1
36 WRITE_MODE_FORCE_NEW = 1
37 WRITE_MODE_FORCE_APPEND = 2
37 WRITE_MODE_FORCE_APPEND = 2
38
38
39
39
40 V2_MAX_READ_ATTEMPTS = 5
40 V2_MAX_READ_ATTEMPTS = 5
41
41
42
42
43 class _dirstatemapcommon:
43 class _dirstatemapcommon:
44 """
44 """
45 Methods that are identical for both implementations of the dirstatemap
45 Methods that are identical for both implementations of the dirstatemap
46 class, with and without Rust extensions enabled.
46 class, with and without Rust extensions enabled.
47 """
47 """
48
48
49 # please pytype
49 # please pytype
50
50
51 _map = None
51 _map = None
52 copymap = None
52 copymap = None
53
53
54 def __init__(self, ui, opener, root, nodeconstants, use_dirstate_v2):
54 def __init__(self, ui, opener, root, nodeconstants, use_dirstate_v2):
55 self._use_dirstate_v2 = use_dirstate_v2
55 self._use_dirstate_v2 = use_dirstate_v2
56 self._nodeconstants = nodeconstants
56 self._nodeconstants = nodeconstants
57 self._ui = ui
57 self._ui = ui
58 self._opener = opener
58 self._opener = opener
59 self._root = root
59 self._root = root
60 self._filename = b'dirstate'
60 self._filename = b'dirstate'
61 self._nodelen = 20 # Also update Rust code when changing this!
61 self._nodelen = 20 # Also update Rust code when changing this!
62 self._parents = None
62 self._parents = None
63 self._dirtyparents = False
63 self._dirtyparents = False
64 self._docket = None
64 self._docket = None
65 write_mode = ui.config(b"devel", b"dirstate.v2.data_update_mode")
65 write_mode = ui.config(b"devel", b"dirstate.v2.data_update_mode")
66 if write_mode == b"auto":
66 if write_mode == b"auto":
67 self._write_mode = WRITE_MODE_AUTO
67 self._write_mode = WRITE_MODE_AUTO
68 elif write_mode == b"force-append":
68 elif write_mode == b"force-append":
69 self._write_mode = WRITE_MODE_FORCE_APPEND
69 self._write_mode = WRITE_MODE_FORCE_APPEND
70 elif write_mode == b"force-new":
70 elif write_mode == b"force-new":
71 self._write_mode = WRITE_MODE_FORCE_NEW
71 self._write_mode = WRITE_MODE_FORCE_NEW
72 else:
72 else:
73 # unknown value, fallback to default
73 # unknown value, fallback to default
74 self._write_mode = WRITE_MODE_AUTO
74 self._write_mode = WRITE_MODE_AUTO
75
75
76 # for consistent view between _pl() and _read() invocations
76 # for consistent view between _pl() and _read() invocations
77 self._pendingmode = None
77 self._pendingmode = None
78
78
79 def preload(self):
79 def preload(self):
80 """Loads the underlying data, if it's not already loaded"""
80 """Loads the underlying data, if it's not already loaded"""
81 self._map
81 self._map
82
82
83 def get(self, key, default=None):
83 def get(self, key, default=None):
84 return self._map.get(key, default)
84 return self._map.get(key, default)
85
85
86 def __len__(self):
86 def __len__(self):
87 return len(self._map)
87 return len(self._map)
88
88
89 def __iter__(self):
89 def __iter__(self):
90 return iter(self._map)
90 return iter(self._map)
91
91
92 def __contains__(self, key):
92 def __contains__(self, key):
93 return key in self._map
93 return key in self._map
94
94
95 def __getitem__(self, item):
95 def __getitem__(self, item):
96 return self._map[item]
96 return self._map[item]
97
97
98 ### disk interaction
98 ### disk interaction
99
99
100 def _opendirstatefile(self):
100 def _opendirstatefile(self):
101 fp, mode = txnutil.trypending(self._root, self._opener, self._filename)
101 fp, mode = txnutil.trypending(self._root, self._opener, self._filename)
102 if self._pendingmode is not None and self._pendingmode != mode:
102 if self._pendingmode is not None and self._pendingmode != mode:
103 fp.close()
103 fp.close()
104 raise error.Abort(
104 raise error.Abort(
105 _(b'working directory state may be changed parallelly')
105 _(b'working directory state may be changed parallelly')
106 )
106 )
107 self._pendingmode = mode
107 self._pendingmode = mode
108 return fp
108 return fp
109
109
110 def _readdirstatefile(self, size=-1):
110 def _readdirstatefile(self, size=-1):
111 try:
111 try:
112 with self._opendirstatefile() as fp:
112 with self._opendirstatefile() as fp:
113 return fp.read(size)
113 return fp.read(size)
114 except FileNotFoundError:
114 except FileNotFoundError:
115 # File doesn't exist, so the current state is empty
115 # File doesn't exist, so the current state is empty
116 return b''
116 return b''
117
117
118 @property
118 @property
119 def docket(self):
119 def docket(self):
120 if not self._docket:
120 if not self._docket:
121 if not self._use_dirstate_v2:
121 if not self._use_dirstate_v2:
122 raise error.ProgrammingError(
122 raise error.ProgrammingError(
123 b'dirstate only has a docket in v2 format'
123 b'dirstate only has a docket in v2 format'
124 )
124 )
125 self._docket = docketmod.DirstateDocket.parse(
125 self._docket = docketmod.DirstateDocket.parse(
126 self._readdirstatefile(), self._nodeconstants
126 self._readdirstatefile(), self._nodeconstants
127 )
127 )
128 return self._docket
128 return self._docket
129
129
130 def _read_v2_data(self):
130 def _read_v2_data(self):
131 data = None
131 data = None
132 attempts = 0
132 attempts = 0
133 while attempts < V2_MAX_READ_ATTEMPTS:
133 while attempts < V2_MAX_READ_ATTEMPTS:
134 attempts += 1
134 attempts += 1
135 try:
135 try:
136 # TODO: use mmap when possible
136 data = self._opener.read(self.docket.data_filename())
137 data = self._opener.read(self.docket.data_filename())
137 except FileNotFoundError:
138 except FileNotFoundError:
138 # read race detected between docket and data file
139 # read race detected between docket and data file
139 # reload the docket and retry
140 # reload the docket and retry
140 self._docket = None
141 self._docket = None
141 if data is None:
142 if data is None:
142 assert attempts >= V2_MAX_READ_ATTEMPTS
143 assert attempts >= V2_MAX_READ_ATTEMPTS
143 msg = b"dirstate read race happened %d times in a row"
144 msg = b"dirstate read race happened %d times in a row"
144 msg %= attempts
145 msg %= attempts
145 raise error.Abort(msg)
146 raise error.Abort(msg)
146 return self._opener.read(self.docket.data_filename())
147 return self._opener.read(self.docket.data_filename())
147
148
148 def write_v2_no_append(self, tr, st, meta, packed):
149 def write_v2_no_append(self, tr, st, meta, packed):
149 old_docket = self.docket
150 old_docket = self.docket
150 new_docket = docketmod.DirstateDocket.with_new_uuid(
151 new_docket = docketmod.DirstateDocket.with_new_uuid(
151 self.parents(), len(packed), meta
152 self.parents(), len(packed), meta
152 )
153 )
153 if old_docket.uuid == new_docket.uuid:
154 if old_docket.uuid == new_docket.uuid:
154 raise error.ProgrammingError(b'dirstate docket name collision')
155 raise error.ProgrammingError(b'dirstate docket name collision')
155 data_filename = new_docket.data_filename()
156 data_filename = new_docket.data_filename()
156 self._opener.write(data_filename, packed)
157 self._opener.write(data_filename, packed)
157 # Write the new docket after the new data file has been
158 # Write the new docket after the new data file has been
158 # written. Because `st` was opened with `atomictemp=True`,
159 # written. Because `st` was opened with `atomictemp=True`,
159 # the actual `.hg/dirstate` file is only affected on close.
160 # the actual `.hg/dirstate` file is only affected on close.
160 st.write(new_docket.serialize())
161 st.write(new_docket.serialize())
161 st.close()
162 st.close()
162 # Remove the old data file after the new docket pointing to
163 # Remove the old data file after the new docket pointing to
163 # the new data file was written.
164 # the new data file was written.
164 if old_docket.uuid:
165 if old_docket.uuid:
165 data_filename = old_docket.data_filename()
166 data_filename = old_docket.data_filename()
166 unlink = lambda _tr=None: self._opener.unlink(data_filename)
167 unlink = lambda _tr=None: self._opener.unlink(data_filename)
167 if tr:
168 if tr:
168 category = b"dirstate-v2-clean-" + old_docket.uuid
169 category = b"dirstate-v2-clean-" + old_docket.uuid
169 tr.addpostclose(category, unlink)
170 tr.addpostclose(category, unlink)
170 else:
171 else:
171 unlink()
172 unlink()
172 self._docket = new_docket
173 self._docket = new_docket
173
174
174 ### reading/setting parents
175 ### reading/setting parents
175
176
176 def parents(self):
177 def parents(self):
177 if not self._parents:
178 if not self._parents:
178 if self._use_dirstate_v2:
179 if self._use_dirstate_v2:
179 self._parents = self.docket.parents
180 self._parents = self.docket.parents
180 else:
181 else:
181 read_len = self._nodelen * 2
182 read_len = self._nodelen * 2
182 st = self._readdirstatefile(read_len)
183 st = self._readdirstatefile(read_len)
183 l = len(st)
184 l = len(st)
184 if l == read_len:
185 if l == read_len:
185 self._parents = (
186 self._parents = (
186 st[: self._nodelen],
187 st[: self._nodelen],
187 st[self._nodelen : 2 * self._nodelen],
188 st[self._nodelen : 2 * self._nodelen],
188 )
189 )
189 elif l == 0:
190 elif l == 0:
190 self._parents = (
191 self._parents = (
191 self._nodeconstants.nullid,
192 self._nodeconstants.nullid,
192 self._nodeconstants.nullid,
193 self._nodeconstants.nullid,
193 )
194 )
194 else:
195 else:
195 raise error.Abort(
196 raise error.Abort(
196 _(b'working directory state appears damaged!')
197 _(b'working directory state appears damaged!')
197 )
198 )
198
199
199 return self._parents
200 return self._parents
200
201
201
202
202 class dirstatemap(_dirstatemapcommon):
203 class dirstatemap(_dirstatemapcommon):
203 """Map encapsulating the dirstate's contents.
204 """Map encapsulating the dirstate's contents.
204
205
205 The dirstate contains the following state:
206 The dirstate contains the following state:
206
207
207 - `identity` is the identity of the dirstate file, which can be used to
208 - `identity` is the identity of the dirstate file, which can be used to
208 detect when changes have occurred to the dirstate file.
209 detect when changes have occurred to the dirstate file.
209
210
210 - `parents` is a pair containing the parents of the working copy. The
211 - `parents` is a pair containing the parents of the working copy. The
211 parents are updated by calling `setparents`.
212 parents are updated by calling `setparents`.
212
213
213 - the state map maps filenames to tuples of (state, mode, size, mtime),
214 - the state map maps filenames to tuples of (state, mode, size, mtime),
214 where state is a single character representing 'normal', 'added',
215 where state is a single character representing 'normal', 'added',
215 'removed', or 'merged'. It is read by treating the dirstate as a
216 'removed', or 'merged'. It is read by treating the dirstate as a
216 dict. File state is updated by calling various methods (see each
217 dict. File state is updated by calling various methods (see each
217 documentation for details):
218 documentation for details):
218
219
219 - `reset_state`,
220 - `reset_state`,
220 - `set_tracked`
221 - `set_tracked`
221 - `set_untracked`
222 - `set_untracked`
222 - `set_clean`
223 - `set_clean`
223 - `set_possibly_dirty`
224 - `set_possibly_dirty`
224
225
225 - `copymap` maps destination filenames to their source filename.
226 - `copymap` maps destination filenames to their source filename.
226
227
227 The dirstate also provides the following views onto the state:
228 The dirstate also provides the following views onto the state:
228
229
229 - `filefoldmap` is a dict mapping normalized filenames to the denormalized
230 - `filefoldmap` is a dict mapping normalized filenames to the denormalized
230 form that they appear as in the dirstate.
231 form that they appear as in the dirstate.
231
232
232 - `dirfoldmap` is a dict mapping normalized directory names to the
233 - `dirfoldmap` is a dict mapping normalized directory names to the
233 denormalized form that they appear as in the dirstate.
234 denormalized form that they appear as in the dirstate.
234 """
235 """
235
236
236 ### Core data storage and access
237 ### Core data storage and access
237
238
238 @propertycache
239 @propertycache
239 def _map(self):
240 def _map(self):
240 self._map = {}
241 self._map = {}
241 self.read()
242 self.read()
242 return self._map
243 return self._map
243
244
244 @propertycache
245 @propertycache
245 def copymap(self):
246 def copymap(self):
246 self.copymap = {}
247 self.copymap = {}
247 self._map
248 self._map
248 return self.copymap
249 return self.copymap
249
250
250 def clear(self):
251 def clear(self):
251 self._map.clear()
252 self._map.clear()
252 self.copymap.clear()
253 self.copymap.clear()
253 self.setparents(self._nodeconstants.nullid, self._nodeconstants.nullid)
254 self.setparents(self._nodeconstants.nullid, self._nodeconstants.nullid)
254 util.clearcachedproperty(self, b"_dirs")
255 util.clearcachedproperty(self, b"_dirs")
255 util.clearcachedproperty(self, b"_alldirs")
256 util.clearcachedproperty(self, b"_alldirs")
256 util.clearcachedproperty(self, b"filefoldmap")
257 util.clearcachedproperty(self, b"filefoldmap")
257 util.clearcachedproperty(self, b"dirfoldmap")
258 util.clearcachedproperty(self, b"dirfoldmap")
258
259
259 def items(self):
260 def items(self):
260 return self._map.items()
261 return self._map.items()
261
262
262 # forward for python2,3 compat
263 # forward for python2,3 compat
263 iteritems = items
264 iteritems = items
264
265
265 def debug_iter(self, all):
266 def debug_iter(self, all):
266 """
267 """
267 Return an iterator of (filename, state, mode, size, mtime) tuples
268 Return an iterator of (filename, state, mode, size, mtime) tuples
268
269
269 `all` is unused when Rust is not enabled
270 `all` is unused when Rust is not enabled
270 """
271 """
271 for (filename, item) in self.items():
272 for (filename, item) in self.items():
272 yield (filename, item.state, item.mode, item.size, item.mtime)
273 yield (filename, item.state, item.mode, item.size, item.mtime)
273
274
274 def keys(self):
275 def keys(self):
275 return self._map.keys()
276 return self._map.keys()
276
277
277 ### reading/setting parents
278 ### reading/setting parents
278
279
279 def setparents(self, p1, p2, fold_p2=False):
280 def setparents(self, p1, p2, fold_p2=False):
280 self._parents = (p1, p2)
281 self._parents = (p1, p2)
281 self._dirtyparents = True
282 self._dirtyparents = True
282 copies = {}
283 copies = {}
283 if fold_p2:
284 if fold_p2:
284 for f, s in self._map.items():
285 for f, s in self._map.items():
285 # Discard "merged" markers when moving away from a merge state
286 # Discard "merged" markers when moving away from a merge state
286 if s.p2_info:
287 if s.p2_info:
287 source = self.copymap.pop(f, None)
288 source = self.copymap.pop(f, None)
288 if source:
289 if source:
289 copies[f] = source
290 copies[f] = source
290 s.drop_merge_data()
291 s.drop_merge_data()
291 return copies
292 return copies
292
293
293 ### disk interaction
294 ### disk interaction
294
295
295 def read(self):
296 def read(self):
296 # ignore HG_PENDING because identity is used only for writing
297 # ignore HG_PENDING because identity is used only for writing
297 self.identity = util.filestat.frompath(
298 self.identity = util.filestat.frompath(
298 self._opener.join(self._filename)
299 self._opener.join(self._filename)
299 )
300 )
300
301
301 testing.wait_on_cfg(self._ui, b'dirstate.pre-read-file')
302 testing.wait_on_cfg(self._ui, b'dirstate.pre-read-file')
302 if self._use_dirstate_v2:
303 if self._use_dirstate_v2:
303
304
304 if not self.docket.uuid:
305 if not self.docket.uuid:
305 return
306 return
306 testing.wait_on_cfg(self._ui, b'dirstate.post-docket-read-file')
307 testing.wait_on_cfg(self._ui, b'dirstate.post-docket-read-file')
307 st = self._read_v2_data()
308 st = self._read_v2_data()
308 else:
309 else:
309 st = self._readdirstatefile()
310 st = self._readdirstatefile()
310
311
311 if not st:
312 if not st:
312 return
313 return
313
314
314 # TODO: adjust this estimate for dirstate-v2
315 # TODO: adjust this estimate for dirstate-v2
315 if util.safehasattr(parsers, b'dict_new_presized'):
316 if util.safehasattr(parsers, b'dict_new_presized'):
316 # Make an estimate of the number of files in the dirstate based on
317 # Make an estimate of the number of files in the dirstate based on
317 # its size. This trades wasting some memory for avoiding costly
318 # its size. This trades wasting some memory for avoiding costly
318 # resizes. Each entry have a prefix of 17 bytes followed by one or
319 # resizes. Each entry have a prefix of 17 bytes followed by one or
319 # two path names. Studies on various large-scale real-world repositories
320 # two path names. Studies on various large-scale real-world repositories
320 # found 54 bytes a reasonable upper limit for the average path names.
321 # found 54 bytes a reasonable upper limit for the average path names.
321 # Copy entries are ignored for the sake of this estimate.
322 # Copy entries are ignored for the sake of this estimate.
322 self._map = parsers.dict_new_presized(len(st) // 71)
323 self._map = parsers.dict_new_presized(len(st) // 71)
323
324
324 # Python's garbage collector triggers a GC each time a certain number
325 # Python's garbage collector triggers a GC each time a certain number
325 # of container objects (the number being defined by
326 # of container objects (the number being defined by
326 # gc.get_threshold()) are allocated. parse_dirstate creates a tuple
327 # gc.get_threshold()) are allocated. parse_dirstate creates a tuple
327 # for each file in the dirstate. The C version then immediately marks
328 # for each file in the dirstate. The C version then immediately marks
328 # them as not to be tracked by the collector. However, this has no
329 # them as not to be tracked by the collector. However, this has no
329 # effect on when GCs are triggered, only on what objects the GC looks
330 # effect on when GCs are triggered, only on what objects the GC looks
330 # into. This means that O(number of files) GCs are unavoidable.
331 # into. This means that O(number of files) GCs are unavoidable.
331 # Depending on when in the process's lifetime the dirstate is parsed,
332 # Depending on when in the process's lifetime the dirstate is parsed,
332 # this can get very expensive. As a workaround, disable GC while
333 # this can get very expensive. As a workaround, disable GC while
333 # parsing the dirstate.
334 # parsing the dirstate.
334 #
335 #
335 # (we cannot decorate the function directly since it is in a C module)
336 # (we cannot decorate the function directly since it is in a C module)
336 if self._use_dirstate_v2:
337 if self._use_dirstate_v2:
337 p = self.docket.parents
338 p = self.docket.parents
338 meta = self.docket.tree_metadata
339 meta = self.docket.tree_metadata
339 parse_dirstate = util.nogc(v2.parse_dirstate)
340 parse_dirstate = util.nogc(v2.parse_dirstate)
340 parse_dirstate(self._map, self.copymap, st, meta)
341 parse_dirstate(self._map, self.copymap, st, meta)
341 else:
342 else:
342 parse_dirstate = util.nogc(parsers.parse_dirstate)
343 parse_dirstate = util.nogc(parsers.parse_dirstate)
343 p = parse_dirstate(self._map, self.copymap, st)
344 p = parse_dirstate(self._map, self.copymap, st)
344 if not self._dirtyparents:
345 if not self._dirtyparents:
345 self.setparents(*p)
346 self.setparents(*p)
346
347
347 # Avoid excess attribute lookups by fast pathing certain checks
348 # Avoid excess attribute lookups by fast pathing certain checks
348 self.__contains__ = self._map.__contains__
349 self.__contains__ = self._map.__contains__
349 self.__getitem__ = self._map.__getitem__
350 self.__getitem__ = self._map.__getitem__
350 self.get = self._map.get
351 self.get = self._map.get
351
352
352 def write(self, tr, st):
353 def write(self, tr, st):
353 if self._use_dirstate_v2:
354 if self._use_dirstate_v2:
354 packed, meta = v2.pack_dirstate(self._map, self.copymap)
355 packed, meta = v2.pack_dirstate(self._map, self.copymap)
355 self.write_v2_no_append(tr, st, meta, packed)
356 self.write_v2_no_append(tr, st, meta, packed)
356 else:
357 else:
357 packed = parsers.pack_dirstate(
358 packed = parsers.pack_dirstate(
358 self._map, self.copymap, self.parents()
359 self._map, self.copymap, self.parents()
359 )
360 )
360 st.write(packed)
361 st.write(packed)
361 st.close()
362 st.close()
362 self._dirtyparents = False
363 self._dirtyparents = False
363
364
364 @propertycache
365 @propertycache
365 def identity(self):
366 def identity(self):
366 self._map
367 self._map
367 return self.identity
368 return self.identity
368
369
369 ### code related to maintaining and accessing "extra" property
370 ### code related to maintaining and accessing "extra" property
370 # (e.g. "has_dir")
371 # (e.g. "has_dir")
371
372
372 def _dirs_incr(self, filename, old_entry=None):
373 def _dirs_incr(self, filename, old_entry=None):
373 """increment the dirstate counter if applicable"""
374 """increment the dirstate counter if applicable"""
374 if (
375 if (
375 old_entry is None or old_entry.removed
376 old_entry is None or old_entry.removed
376 ) and "_dirs" in self.__dict__:
377 ) and "_dirs" in self.__dict__:
377 self._dirs.addpath(filename)
378 self._dirs.addpath(filename)
378 if old_entry is None and "_alldirs" in self.__dict__:
379 if old_entry is None and "_alldirs" in self.__dict__:
379 self._alldirs.addpath(filename)
380 self._alldirs.addpath(filename)
380
381
381 def _dirs_decr(self, filename, old_entry=None, remove_variant=False):
382 def _dirs_decr(self, filename, old_entry=None, remove_variant=False):
382 """decrement the dirstate counter if applicable"""
383 """decrement the dirstate counter if applicable"""
383 if old_entry is not None:
384 if old_entry is not None:
384 if "_dirs" in self.__dict__ and not old_entry.removed:
385 if "_dirs" in self.__dict__ and not old_entry.removed:
385 self._dirs.delpath(filename)
386 self._dirs.delpath(filename)
386 if "_alldirs" in self.__dict__ and not remove_variant:
387 if "_alldirs" in self.__dict__ and not remove_variant:
387 self._alldirs.delpath(filename)
388 self._alldirs.delpath(filename)
388 elif remove_variant and "_alldirs" in self.__dict__:
389 elif remove_variant and "_alldirs" in self.__dict__:
389 self._alldirs.addpath(filename)
390 self._alldirs.addpath(filename)
390 if "filefoldmap" in self.__dict__:
391 if "filefoldmap" in self.__dict__:
391 normed = util.normcase(filename)
392 normed = util.normcase(filename)
392 self.filefoldmap.pop(normed, None)
393 self.filefoldmap.pop(normed, None)
393
394
394 @propertycache
395 @propertycache
395 def filefoldmap(self):
396 def filefoldmap(self):
396 """Returns a dictionary mapping normalized case paths to their
397 """Returns a dictionary mapping normalized case paths to their
397 non-normalized versions.
398 non-normalized versions.
398 """
399 """
399 try:
400 try:
400 makefilefoldmap = parsers.make_file_foldmap
401 makefilefoldmap = parsers.make_file_foldmap
401 except AttributeError:
402 except AttributeError:
402 pass
403 pass
403 else:
404 else:
404 return makefilefoldmap(
405 return makefilefoldmap(
405 self._map, util.normcasespec, util.normcasefallback
406 self._map, util.normcasespec, util.normcasefallback
406 )
407 )
407
408
408 f = {}
409 f = {}
409 normcase = util.normcase
410 normcase = util.normcase
410 for name, s in self._map.items():
411 for name, s in self._map.items():
411 if not s.removed:
412 if not s.removed:
412 f[normcase(name)] = name
413 f[normcase(name)] = name
413 f[b'.'] = b'.' # prevents useless util.fspath() invocation
414 f[b'.'] = b'.' # prevents useless util.fspath() invocation
414 return f
415 return f
415
416
416 @propertycache
417 @propertycache
417 def dirfoldmap(self):
418 def dirfoldmap(self):
418 f = {}
419 f = {}
419 normcase = util.normcase
420 normcase = util.normcase
420 for name in self._dirs:
421 for name in self._dirs:
421 f[normcase(name)] = name
422 f[normcase(name)] = name
422 return f
423 return f
423
424
424 def hastrackeddir(self, d):
425 def hastrackeddir(self, d):
425 """
426 """
426 Returns True if the dirstate contains a tracked (not removed) file
427 Returns True if the dirstate contains a tracked (not removed) file
427 in this directory.
428 in this directory.
428 """
429 """
429 return d in self._dirs
430 return d in self._dirs
430
431
431 def hasdir(self, d):
432 def hasdir(self, d):
432 """
433 """
433 Returns True if the dirstate contains a file (tracked or removed)
434 Returns True if the dirstate contains a file (tracked or removed)
434 in this directory.
435 in this directory.
435 """
436 """
436 return d in self._alldirs
437 return d in self._alldirs
437
438
438 @propertycache
439 @propertycache
439 def _dirs(self):
440 def _dirs(self):
440 return pathutil.dirs(self._map, only_tracked=True)
441 return pathutil.dirs(self._map, only_tracked=True)
441
442
442 @propertycache
443 @propertycache
443 def _alldirs(self):
444 def _alldirs(self):
444 return pathutil.dirs(self._map)
445 return pathutil.dirs(self._map)
445
446
446 ### code related to manipulation of entries and copy-sources
447 ### code related to manipulation of entries and copy-sources
447
448
448 def reset_state(
449 def reset_state(
449 self,
450 self,
450 filename,
451 filename,
451 wc_tracked=False,
452 wc_tracked=False,
452 p1_tracked=False,
453 p1_tracked=False,
453 p2_info=False,
454 p2_info=False,
454 has_meaningful_mtime=True,
455 has_meaningful_mtime=True,
455 parentfiledata=None,
456 parentfiledata=None,
456 ):
457 ):
457 """Set a entry to a given state, diregarding all previous state
458 """Set a entry to a given state, diregarding all previous state
458
459
459 This is to be used by the part of the dirstate API dedicated to
460 This is to be used by the part of the dirstate API dedicated to
460 adjusting the dirstate after a update/merge.
461 adjusting the dirstate after a update/merge.
461
462
462 note: calling this might result to no entry existing at all if the
463 note: calling this might result to no entry existing at all if the
463 dirstate map does not see any point at having one for this file
464 dirstate map does not see any point at having one for this file
464 anymore.
465 anymore.
465 """
466 """
466 # copy information are now outdated
467 # copy information are now outdated
467 # (maybe new information should be in directly passed to this function)
468 # (maybe new information should be in directly passed to this function)
468 self.copymap.pop(filename, None)
469 self.copymap.pop(filename, None)
469
470
470 if not (p1_tracked or p2_info or wc_tracked):
471 if not (p1_tracked or p2_info or wc_tracked):
471 old_entry = self._map.get(filename)
472 old_entry = self._map.get(filename)
472 self._drop_entry(filename)
473 self._drop_entry(filename)
473 self._dirs_decr(filename, old_entry=old_entry)
474 self._dirs_decr(filename, old_entry=old_entry)
474 return
475 return
475
476
476 old_entry = self._map.get(filename)
477 old_entry = self._map.get(filename)
477 self._dirs_incr(filename, old_entry)
478 self._dirs_incr(filename, old_entry)
478 entry = DirstateItem(
479 entry = DirstateItem(
479 wc_tracked=wc_tracked,
480 wc_tracked=wc_tracked,
480 p1_tracked=p1_tracked,
481 p1_tracked=p1_tracked,
481 p2_info=p2_info,
482 p2_info=p2_info,
482 has_meaningful_mtime=has_meaningful_mtime,
483 has_meaningful_mtime=has_meaningful_mtime,
483 parentfiledata=parentfiledata,
484 parentfiledata=parentfiledata,
484 )
485 )
485 self._map[filename] = entry
486 self._map[filename] = entry
486
487
487 def set_tracked(self, filename):
488 def set_tracked(self, filename):
488 new = False
489 new = False
489 entry = self.get(filename)
490 entry = self.get(filename)
490 if entry is None:
491 if entry is None:
491 self._dirs_incr(filename)
492 self._dirs_incr(filename)
492 entry = DirstateItem(
493 entry = DirstateItem(
493 wc_tracked=True,
494 wc_tracked=True,
494 )
495 )
495
496
496 self._map[filename] = entry
497 self._map[filename] = entry
497 new = True
498 new = True
498 elif not entry.tracked:
499 elif not entry.tracked:
499 self._dirs_incr(filename, entry)
500 self._dirs_incr(filename, entry)
500 entry.set_tracked()
501 entry.set_tracked()
501 self._refresh_entry(filename, entry)
502 self._refresh_entry(filename, entry)
502 new = True
503 new = True
503 else:
504 else:
504 # XXX This is probably overkill for more case, but we need this to
505 # XXX This is probably overkill for more case, but we need this to
505 # fully replace the `normallookup` call with `set_tracked` one.
506 # fully replace the `normallookup` call with `set_tracked` one.
506 # Consider smoothing this in the future.
507 # Consider smoothing this in the future.
507 entry.set_possibly_dirty()
508 entry.set_possibly_dirty()
508 self._refresh_entry(filename, entry)
509 self._refresh_entry(filename, entry)
509 return new
510 return new
510
511
511 def set_untracked(self, f):
512 def set_untracked(self, f):
512 """Mark a file as no longer tracked in the dirstate map"""
513 """Mark a file as no longer tracked in the dirstate map"""
513 entry = self.get(f)
514 entry = self.get(f)
514 if entry is None:
515 if entry is None:
515 return False
516 return False
516 else:
517 else:
517 self._dirs_decr(f, old_entry=entry, remove_variant=not entry.added)
518 self._dirs_decr(f, old_entry=entry, remove_variant=not entry.added)
518 if not entry.p2_info:
519 if not entry.p2_info:
519 self.copymap.pop(f, None)
520 self.copymap.pop(f, None)
520 entry.set_untracked()
521 entry.set_untracked()
521 self._refresh_entry(f, entry)
522 self._refresh_entry(f, entry)
522 return True
523 return True
523
524
524 def set_clean(self, filename, mode, size, mtime):
525 def set_clean(self, filename, mode, size, mtime):
525 """mark a file as back to a clean state"""
526 """mark a file as back to a clean state"""
526 entry = self[filename]
527 entry = self[filename]
527 size = size & rangemask
528 size = size & rangemask
528 entry.set_clean(mode, size, mtime)
529 entry.set_clean(mode, size, mtime)
529 self._refresh_entry(filename, entry)
530 self._refresh_entry(filename, entry)
530 self.copymap.pop(filename, None)
531 self.copymap.pop(filename, None)
531
532
532 def set_possibly_dirty(self, filename):
533 def set_possibly_dirty(self, filename):
533 """record that the current state of the file on disk is unknown"""
534 """record that the current state of the file on disk is unknown"""
534 entry = self[filename]
535 entry = self[filename]
535 entry.set_possibly_dirty()
536 entry.set_possibly_dirty()
536 self._refresh_entry(filename, entry)
537 self._refresh_entry(filename, entry)
537
538
538 def _refresh_entry(self, f, entry):
539 def _refresh_entry(self, f, entry):
539 """record updated state of an entry"""
540 """record updated state of an entry"""
540 if not entry.any_tracked:
541 if not entry.any_tracked:
541 self._map.pop(f, None)
542 self._map.pop(f, None)
542
543
543 def _drop_entry(self, f):
544 def _drop_entry(self, f):
544 """remove any entry for file f
545 """remove any entry for file f
545
546
546 This should also drop associated copy information
547 This should also drop associated copy information
547
548
548 The fact we actually need to drop it is the responsability of the caller"""
549 The fact we actually need to drop it is the responsability of the caller"""
549 self._map.pop(f, None)
550 self._map.pop(f, None)
550 self.copymap.pop(f, None)
551 self.copymap.pop(f, None)
551
552
552
553
553 if rustmod is not None:
554 if rustmod is not None:
554
555
555 class dirstatemap(_dirstatemapcommon):
556 class dirstatemap(_dirstatemapcommon):
556
557
557 ### Core data storage and access
558 ### Core data storage and access
558
559
559 @propertycache
560 @propertycache
560 def _map(self):
561 def _map(self):
561 """
562 """
562 Fills the Dirstatemap when called.
563 Fills the Dirstatemap when called.
563 """
564 """
564 # ignore HG_PENDING because identity is used only for writing
565 # ignore HG_PENDING because identity is used only for writing
565 self.identity = util.filestat.frompath(
566 self.identity = util.filestat.frompath(
566 self._opener.join(self._filename)
567 self._opener.join(self._filename)
567 )
568 )
568
569
569 testing.wait_on_cfg(self._ui, b'dirstate.pre-read-file')
570 testing.wait_on_cfg(self._ui, b'dirstate.pre-read-file')
570 if self._use_dirstate_v2:
571 if self._use_dirstate_v2:
571 if self.docket.uuid:
572 self.docket # load the data if needed
572 testing.wait_on_cfg(
573 testing.wait_on_cfg(self._ui, b'dirstate.post-docket-read-file')
573 self._ui, b'dirstate.post-docket-read-file'
574 if not self.docket.uuid:
574 )
575 data = b''
575 # TODO: use mmap when possible
576 data = self._opener.read(self.docket.data_filename())
577 else:
576 else:
578 data = b''
577 data = self._read_v2_data()
579 self._map = rustmod.DirstateMap.new_v2(
578 self._map = rustmod.DirstateMap.new_v2(
580 data, self.docket.data_size, self.docket.tree_metadata
579 data, self.docket.data_size, self.docket.tree_metadata
581 )
580 )
582 parents = self.docket.parents
581 parents = self.docket.parents
583 else:
582 else:
584 self._map, parents = rustmod.DirstateMap.new_v1(
583 self._map, parents = rustmod.DirstateMap.new_v1(
585 self._readdirstatefile()
584 self._readdirstatefile()
586 )
585 )
587
586
588 if parents and not self._dirtyparents:
587 if parents and not self._dirtyparents:
589 self.setparents(*parents)
588 self.setparents(*parents)
590
589
591 self.__contains__ = self._map.__contains__
590 self.__contains__ = self._map.__contains__
592 self.__getitem__ = self._map.__getitem__
591 self.__getitem__ = self._map.__getitem__
593 self.get = self._map.get
592 self.get = self._map.get
594 return self._map
593 return self._map
595
594
596 @property
595 @property
597 def copymap(self):
596 def copymap(self):
598 return self._map.copymap()
597 return self._map.copymap()
599
598
600 def debug_iter(self, all):
599 def debug_iter(self, all):
601 """
600 """
602 Return an iterator of (filename, state, mode, size, mtime) tuples
601 Return an iterator of (filename, state, mode, size, mtime) tuples
603
602
604 `all`: also include with `state == b' '` dirstate tree nodes that
603 `all`: also include with `state == b' '` dirstate tree nodes that
605 don't have an associated `DirstateItem`.
604 don't have an associated `DirstateItem`.
606
605
607 """
606 """
608 return self._map.debug_iter(all)
607 return self._map.debug_iter(all)
609
608
610 def clear(self):
609 def clear(self):
611 self._map.clear()
610 self._map.clear()
612 self.setparents(
611 self.setparents(
613 self._nodeconstants.nullid, self._nodeconstants.nullid
612 self._nodeconstants.nullid, self._nodeconstants.nullid
614 )
613 )
615 util.clearcachedproperty(self, b"_dirs")
614 util.clearcachedproperty(self, b"_dirs")
616 util.clearcachedproperty(self, b"_alldirs")
615 util.clearcachedproperty(self, b"_alldirs")
617 util.clearcachedproperty(self, b"dirfoldmap")
616 util.clearcachedproperty(self, b"dirfoldmap")
618
617
619 def items(self):
618 def items(self):
620 return self._map.items()
619 return self._map.items()
621
620
622 # forward for python2,3 compat
621 # forward for python2,3 compat
623 iteritems = items
622 iteritems = items
624
623
625 def keys(self):
624 def keys(self):
626 return iter(self._map)
625 return iter(self._map)
627
626
628 ### reading/setting parents
627 ### reading/setting parents
629
628
630 def setparents(self, p1, p2, fold_p2=False):
629 def setparents(self, p1, p2, fold_p2=False):
631 self._parents = (p1, p2)
630 self._parents = (p1, p2)
632 self._dirtyparents = True
631 self._dirtyparents = True
633 copies = {}
632 copies = {}
634 if fold_p2:
633 if fold_p2:
635 copies = self._map.setparents_fixup()
634 copies = self._map.setparents_fixup()
636 return copies
635 return copies
637
636
638 ### disk interaction
637 ### disk interaction
639
638
640 @propertycache
639 @propertycache
641 def identity(self):
640 def identity(self):
642 self._map
641 self._map
643 return self.identity
642 return self.identity
644
643
645 def write(self, tr, st):
644 def write(self, tr, st):
646 if not self._use_dirstate_v2:
645 if not self._use_dirstate_v2:
647 p1, p2 = self.parents()
646 p1, p2 = self.parents()
648 packed = self._map.write_v1(p1, p2)
647 packed = self._map.write_v1(p1, p2)
649 st.write(packed)
648 st.write(packed)
650 st.close()
649 st.close()
651 self._dirtyparents = False
650 self._dirtyparents = False
652 return
651 return
653
652
654 # We can only append to an existing data file if there is one
653 # We can only append to an existing data file if there is one
655 write_mode = self._write_mode
654 write_mode = self._write_mode
656 if self.docket.uuid is None:
655 if self.docket.uuid is None:
657 write_mode = WRITE_MODE_FORCE_NEW
656 write_mode = WRITE_MODE_FORCE_NEW
658 packed, meta, append = self._map.write_v2(write_mode)
657 packed, meta, append = self._map.write_v2(write_mode)
659 if append:
658 if append:
660 docket = self.docket
659 docket = self.docket
661 data_filename = docket.data_filename()
660 data_filename = docket.data_filename()
662 with self._opener(data_filename, b'r+b') as fp:
661 with self._opener(data_filename, b'r+b') as fp:
663 fp.seek(docket.data_size)
662 fp.seek(docket.data_size)
664 assert fp.tell() == docket.data_size
663 assert fp.tell() == docket.data_size
665 written = fp.write(packed)
664 written = fp.write(packed)
666 if written is not None: # py2 may return None
665 if written is not None: # py2 may return None
667 assert written == len(packed), (written, len(packed))
666 assert written == len(packed), (written, len(packed))
668 docket.data_size += len(packed)
667 docket.data_size += len(packed)
669 docket.parents = self.parents()
668 docket.parents = self.parents()
670 docket.tree_metadata = meta
669 docket.tree_metadata = meta
671 st.write(docket.serialize())
670 st.write(docket.serialize())
672 st.close()
671 st.close()
673 else:
672 else:
674 self.write_v2_no_append(tr, st, meta, packed)
673 self.write_v2_no_append(tr, st, meta, packed)
675 # Reload from the newly-written file
674 # Reload from the newly-written file
676 util.clearcachedproperty(self, b"_map")
675 util.clearcachedproperty(self, b"_map")
677 self._dirtyparents = False
676 self._dirtyparents = False
678
677
679 ### code related to maintaining and accessing "extra" property
678 ### code related to maintaining and accessing "extra" property
680 # (e.g. "has_dir")
679 # (e.g. "has_dir")
681
680
682 @propertycache
681 @propertycache
683 def filefoldmap(self):
682 def filefoldmap(self):
684 """Returns a dictionary mapping normalized case paths to their
683 """Returns a dictionary mapping normalized case paths to their
685 non-normalized versions.
684 non-normalized versions.
686 """
685 """
687 return self._map.filefoldmapasdict()
686 return self._map.filefoldmapasdict()
688
687
689 def hastrackeddir(self, d):
688 def hastrackeddir(self, d):
690 return self._map.hastrackeddir(d)
689 return self._map.hastrackeddir(d)
691
690
692 def hasdir(self, d):
691 def hasdir(self, d):
693 return self._map.hasdir(d)
692 return self._map.hasdir(d)
694
693
695 @propertycache
694 @propertycache
696 def dirfoldmap(self):
695 def dirfoldmap(self):
697 f = {}
696 f = {}
698 normcase = util.normcase
697 normcase = util.normcase
699 for name in self._map.tracked_dirs():
698 for name in self._map.tracked_dirs():
700 f[normcase(name)] = name
699 f[normcase(name)] = name
701 return f
700 return f
702
701
703 ### code related to manipulation of entries and copy-sources
702 ### code related to manipulation of entries and copy-sources
704
703
705 def set_tracked(self, f):
704 def set_tracked(self, f):
706 return self._map.set_tracked(f)
705 return self._map.set_tracked(f)
707
706
708 def set_untracked(self, f):
707 def set_untracked(self, f):
709 return self._map.set_untracked(f)
708 return self._map.set_untracked(f)
710
709
711 def set_clean(self, filename, mode, size, mtime):
710 def set_clean(self, filename, mode, size, mtime):
712 self._map.set_clean(filename, mode, size, mtime)
711 self._map.set_clean(filename, mode, size, mtime)
713
712
714 def set_possibly_dirty(self, f):
713 def set_possibly_dirty(self, f):
715 self._map.set_possibly_dirty(f)
714 self._map.set_possibly_dirty(f)
716
715
717 def reset_state(
716 def reset_state(
718 self,
717 self,
719 filename,
718 filename,
720 wc_tracked=False,
719 wc_tracked=False,
721 p1_tracked=False,
720 p1_tracked=False,
722 p2_info=False,
721 p2_info=False,
723 has_meaningful_mtime=True,
722 has_meaningful_mtime=True,
724 parentfiledata=None,
723 parentfiledata=None,
725 ):
724 ):
726 return self._map.reset_state(
725 return self._map.reset_state(
727 filename,
726 filename,
728 wc_tracked,
727 wc_tracked,
729 p1_tracked,
728 p1_tracked,
730 p2_info,
729 p2_info,
731 has_meaningful_mtime,
730 has_meaningful_mtime,
732 parentfiledata,
731 parentfiledata,
733 )
732 )
@@ -1,569 +1,584 b''
1 ==============================================================================
1 ==============================================================================
2 Check potential race conditions between a dirstate's read and other operations
2 Check potential race conditions between a dirstate's read and other operations
3 ==============================================================================
3 ==============================================================================
4
4
5 #testcases dirstate-v1 dirstate-v2-append dirstate-v2-rewrite
5 #testcases dirstate-v1 dirstate-v2-append dirstate-v2-rewrite
6 #testcases pre-all-read pre-some-read
6 #testcases pre-all-read pre-some-read
7
7
8 Some commands, like `hg status`, do not need to take the wlock but need to
8 Some commands, like `hg status`, do not need to take the wlock but need to
9 access dirstate data.
9 access dirstate data.
10 Other commands might update the dirstate data while this happens.
10 Other commands might update the dirstate data while this happens.
11
11
12 This can create issues if repository data is read in the wrong order, or for
12 This can create issues if repository data is read in the wrong order, or for
13 the dirstate-v2 format where the data is contained in multiple files.
13 the dirstate-v2 format where the data is contained in multiple files.
14
14
15 This test file is meant to test various cases where such parallel operations
15 This test file is meant to test various cases where such parallel operations
16 happen and make sure the reading process behaves fine. We do so with a `hg
16 happen and make sure the reading process behaves fine. We do so with a `hg
17 status` command since it is probably the most advanced of such read-only
17 status` command since it is probably the most advanced of such read-only
18 command.
18 command.
19
19
20 It bears simililarity with `tests/test-dirstate-status-race.t ` but tests a
20 It bears simililarity with `tests/test-dirstate-status-race.t ` but tests a
21 different type of race.
21 different type of race.
22
22
23 Setup
23 Setup
24 =====
24 =====
25
25
26 $ cat >> $HGRCPATH << EOF
26 $ cat >> $HGRCPATH << EOF
27 > [storage]
27 > [storage]
28 > dirstate-v2.slow-path=allow
28 > dirstate-v2.slow-path=allow
29 > EOF
29 > EOF
30
30
31 #if no-dirstate-v1
31 #if no-dirstate-v1
32 $ cat >> $HGRCPATH << EOF
32 $ cat >> $HGRCPATH << EOF
33 > [format]
33 > [format]
34 > use-dirstate-v2=yes
34 > use-dirstate-v2=yes
35 > EOF
35 > EOF
36 #else
36 #else
37 $ cat >> $HGRCPATH << EOF
37 $ cat >> $HGRCPATH << EOF
38 > [format]
38 > [format]
39 > use-dirstate-v2=no
39 > use-dirstate-v2=no
40 > EOF
40 > EOF
41 #endif
41 #endif
42
42
43 #if dirstate-v2-rewrite
43 #if dirstate-v2-rewrite
44 $ d2args="--config devel.dirstate.v2.data_update_mode=force-new"
44 $ d2args="--config devel.dirstate.v2.data_update_mode=force-new"
45 #endif
45 #endif
46 #if dirstate-v2-append
46 #if dirstate-v2-append
47 $ d2args="--config devel.dirstate.v2.data_update_mode=force-append"
47 $ d2args="--config devel.dirstate.v2.data_update_mode=force-append"
48 #endif
48 #endif
49
49
50
50
51 #if dirstate-v1
51 #if dirstate-v1
52 $ cfg="devel.sync.dirstate.pre-read-file"
52 $ cfg="devel.sync.dirstate.pre-read-file"
53 #else
53 #else
54 #if pre-all-read
54 #if pre-all-read
55 $ cfg="devel.sync.dirstate.pre-read-file"
55 $ cfg="devel.sync.dirstate.pre-read-file"
56 #else
56 #else
57 $ cfg="devel.sync.dirstate.post-docket-read-file"
57 $ cfg="devel.sync.dirstate.post-docket-read-file"
58 #endif
58 #endif
59 #endif
59 #endif
60
60
61 $ directories="dir dir/nested dir2"
61 $ directories="dir dir/nested dir2"
62 $ first_files="dir/nested/a dir/b dir/c dir/d dir2/e f"
62 $ first_files="dir/nested/a dir/b dir/c dir/d dir2/e f"
63 $ second_files="g dir/nested/h dir/i dir/j dir2/k dir2/l dir/nested/m"
63 $ second_files="g dir/nested/h dir/i dir/j dir2/k dir2/l dir/nested/m"
64 $ extra_files="dir/n dir/o p q"
64 $ extra_files="dir/n dir/o p q"
65
65
66 $ hg init reference-repo
66 $ hg init reference-repo
67 $ cd reference-repo
67 $ cd reference-repo
68 $ mkdir -p dir/nested dir2
68 $ mkdir -p dir/nested dir2
69 $ touch -t 200001010000 $first_files $directories
69 $ touch -t 200001010000 $first_files $directories
70 $ hg commit -Aqm "recreate a bunch of files to facilitate dirstate-v2 append"
70 $ hg commit -Aqm "recreate a bunch of files to facilitate dirstate-v2 append"
71 $ touch -t 200001010010 $second_files $directories
71 $ touch -t 200001010010 $second_files $directories
72 $ hg commit -Aqm "more files to have two commit"
72 $ hg commit -Aqm "more files to have two commit"
73 $ hg log -G -v
73 $ hg log -G -v
74 @ changeset: 1:9a86dcbfb938
74 @ changeset: 1:9a86dcbfb938
75 | tag: tip
75 | tag: tip
76 | user: test
76 | user: test
77 | date: Thu Jan 01 00:00:00 1970 +0000
77 | date: Thu Jan 01 00:00:00 1970 +0000
78 | files: dir/i dir/j dir/nested/h dir/nested/m dir2/k dir2/l g
78 | files: dir/i dir/j dir/nested/h dir/nested/m dir2/k dir2/l g
79 | description:
79 | description:
80 | more files to have two commit
80 | more files to have two commit
81 |
81 |
82 |
82 |
83 o changeset: 0:4f23db756b09
83 o changeset: 0:4f23db756b09
84 user: test
84 user: test
85 date: Thu Jan 01 00:00:00 1970 +0000
85 date: Thu Jan 01 00:00:00 1970 +0000
86 files: dir/b dir/c dir/d dir/nested/a dir2/e f
86 files: dir/b dir/c dir/d dir/nested/a dir2/e f
87 description:
87 description:
88 recreate a bunch of files to facilitate dirstate-v2 append
88 recreate a bunch of files to facilitate dirstate-v2 append
89
89
90
90
91 $ hg manifest
91 $ hg manifest
92 dir/b
92 dir/b
93 dir/c
93 dir/c
94 dir/d
94 dir/d
95 dir/i
95 dir/i
96 dir/j
96 dir/j
97 dir/nested/a
97 dir/nested/a
98 dir/nested/h
98 dir/nested/h
99 dir/nested/m
99 dir/nested/m
100 dir2/e
100 dir2/e
101 dir2/k
101 dir2/k
102 dir2/l
102 dir2/l
103 f
103 f
104 g
104 g
105
105
106 Add some unknown files and refresh the dirstate
106 Add some unknown files and refresh the dirstate
107
107
108 $ touch -t 200001010020 $extra_files
108 $ touch -t 200001010020 $extra_files
109 $ hg add dir/o
109 $ hg add dir/o
110 $ hg remove dir/nested/m
110 $ hg remove dir/nested/m
111
111
112 $ hg st --config devel.dirstate.v2.data_update_mode=force-new
112 $ hg st --config devel.dirstate.v2.data_update_mode=force-new
113 A dir/o
113 A dir/o
114 R dir/nested/m
114 R dir/nested/m
115 ? dir/n
115 ? dir/n
116 ? p
116 ? p
117 ? q
117 ? q
118 $ hg debugstate
118 $ hg debugstate
119 n 644 0 2000-01-01 00:00:00 dir/b
119 n 644 0 2000-01-01 00:00:00 dir/b
120 n 644 0 2000-01-01 00:00:00 dir/c
120 n 644 0 2000-01-01 00:00:00 dir/c
121 n 644 0 2000-01-01 00:00:00 dir/d
121 n 644 0 2000-01-01 00:00:00 dir/d
122 n 644 0 2000-01-01 00:10:00 dir/i
122 n 644 0 2000-01-01 00:10:00 dir/i
123 n 644 0 2000-01-01 00:10:00 dir/j
123 n 644 0 2000-01-01 00:10:00 dir/j
124 n 644 0 2000-01-01 00:00:00 dir/nested/a
124 n 644 0 2000-01-01 00:00:00 dir/nested/a
125 n 644 0 2000-01-01 00:10:00 dir/nested/h
125 n 644 0 2000-01-01 00:10:00 dir/nested/h
126 r ?????????????????????????????????? dir/nested/m (glob)
126 r ?????????????????????????????????? dir/nested/m (glob)
127 a ?????????????????????????????????? dir/o (glob)
127 a ?????????????????????????????????? dir/o (glob)
128 n 644 0 2000-01-01 00:00:00 dir2/e
128 n 644 0 2000-01-01 00:00:00 dir2/e
129 n 644 0 2000-01-01 00:10:00 dir2/k
129 n 644 0 2000-01-01 00:10:00 dir2/k
130 n 644 0 2000-01-01 00:10:00 dir2/l
130 n 644 0 2000-01-01 00:10:00 dir2/l
131 n 644 0 2000-01-01 00:00:00 f
131 n 644 0 2000-01-01 00:00:00 f
132 n 644 0 2000-01-01 00:10:00 g
132 n 644 0 2000-01-01 00:10:00 g
133 $ hg debugstate > ../reference
133 $ hg debugstate > ../reference
134 $ cd ..
134 $ cd ..
135
135
136 Actual Testing
136 Actual Testing
137 ==============
137 ==============
138
138
139 Race with a `hg add`
139 Race with a `hg add`
140 -------------------
140 -------------------
141
141
142 $ cp -a reference-repo race-with-add
142 $ cp -a reference-repo race-with-add
143 $ cd race-with-add
143 $ cd race-with-add
144
144
145 spin a `hg status` with some caches to update
145 spin a `hg status` with some caches to update
146
146
147 $ hg st >$TESTTMP/status-race-lock.out 2>$TESTTMP/status-race-lock.log \
147 $ hg st >$TESTTMP/status-race-lock.out 2>$TESTTMP/status-race-lock.log \
148 > --config rhg.on-unsupported=abort \
148 > --config rhg.on-unsupported=abort \
149 > --config ${cfg}=$TESTTMP/status-race-lock \
149 > --config ${cfg}=$TESTTMP/status-race-lock \
150 > &
150 > &
151 $ $RUNTESTDIR/testlib/wait-on-file 5 $TESTTMP/status-race-lock.waiting
151 $ $RUNTESTDIR/testlib/wait-on-file 5 $TESTTMP/status-race-lock.waiting
152
152
153 Add a file
153 Add a file
154
154
155 $ hg $d2args add dir/n
155 $ hg $d2args add dir/n
156 $ touch $TESTTMP/status-race-lock
156 $ touch $TESTTMP/status-race-lock
157 $ wait
157 $ wait
158
158
159 The file should in a "added" state
159 The file should in a "added" state
160
160
161 $ hg status
161 $ hg status
162 A dir/n
162 A dir/n
163 A dir/o
163 A dir/o
164 R dir/nested/m
164 R dir/nested/m
165 ? p
165 ? p
166 ? q
166 ? q
167
167
168 The status process should return a consistent result and not crash.
168 The status process should return a consistent result and not crash.
169
169
170 #if dirstate-v1
170 #if dirstate-v1
171 $ cat $TESTTMP/status-race-lock.out
171 $ cat $TESTTMP/status-race-lock.out
172 A dir/n
172 A dir/n
173 A dir/o
173 A dir/o
174 R dir/nested/m
174 R dir/nested/m
175 ? p
175 ? p
176 ? q
176 ? q
177 $ cat $TESTTMP/status-race-lock.log
177 $ cat $TESTTMP/status-race-lock.log
178 #else
178 #else
179 #if rhg
179 #if rhg
180 #if pre-all-read
180 #if pre-all-read
181 $ cat $TESTTMP/status-race-lock.out
181 $ cat $TESTTMP/status-race-lock.out
182 A dir/n
182 A dir/n
183 A dir/o
183 A dir/o
184 R dir/nested/m
184 R dir/nested/m
185 ? p
185 ? p
186 ? q
186 ? q
187 $ cat $TESTTMP/status-race-lock.log
187 $ cat $TESTTMP/status-race-lock.log
188 #else
188 #else
189 #if dirstate-v2-append
189 #if dirstate-v2-append
190 $ cat $TESTTMP/status-race-lock.out
190 $ cat $TESTTMP/status-race-lock.out
191 A dir/o
191 A dir/o
192 R dir/nested/m
192 R dir/nested/m
193 ? dir/n
193 ? dir/n
194 ? p
194 ? p
195 ? q
195 ? q
196 $ cat $TESTTMP/status-race-lock.log
196 $ cat $TESTTMP/status-race-lock.log
197 #else
197 #else
198 $ cat $TESTTMP/status-race-lock.out
198 $ cat $TESTTMP/status-race-lock.out
199 $ cat $TESTTMP/status-race-lock.log
199 $ cat $TESTTMP/status-race-lock.log
200 abort: dirstate-v2 parse error: not enough bytes on disk
200 abort: dirstate-v2 parse error: not enough bytes on disk
201 #endif
201 #endif
202 #endif
202 #endif
203 #else
203 #else
204 #if rust
204 #if rust
205 #if dirstate-v2-rewrite
205 #if dirstate-v2-rewrite
206 $ cat $TESTTMP/status-race-lock.out
206 $ cat $TESTTMP/status-race-lock.out
207 A dir/n
208 A dir/o
209 R dir/nested/m
210 ? p
211 ? q
207 $ cat $TESTTMP/status-race-lock.log
212 $ cat $TESTTMP/status-race-lock.log
208 abort: $ENOENT$: '$TESTTMP/race-with-add/.hg/dirstate.* (glob)
209 #else
213 #else
210 $ cat $TESTTMP/status-race-lock.out
214 $ cat $TESTTMP/status-race-lock.out
211 A dir/o
215 A dir/o
212 R dir/nested/m
216 R dir/nested/m
213 ? dir/n
217 ? dir/n
214 ? p
218 ? p
215 ? q
219 ? q
216 $ cat $TESTTMP/status-race-lock.log
220 $ cat $TESTTMP/status-race-lock.log
217 #endif
221 #endif
218 #else
222 #else
219 $ cat $TESTTMP/status-race-lock.out
223 $ cat $TESTTMP/status-race-lock.out
220 A dir/n
224 A dir/n
221 A dir/o
225 A dir/o
222 R dir/nested/m
226 R dir/nested/m
223 ? p
227 ? p
224 ? q
228 ? q
225 $ cat $TESTTMP/status-race-lock.log
229 $ cat $TESTTMP/status-race-lock.log
226 #endif
230 #endif
227 #endif
231 #endif
228 #endif
232 #endif
229
233
230 final cleanup
234 final cleanup
231
235
232 $ rm $TESTTMP/status-race-lock $TESTTMP/status-race-lock.waiting
236 $ rm $TESTTMP/status-race-lock $TESTTMP/status-race-lock.waiting
233 $ cd ..
237 $ cd ..
234
238
235 Race with a `hg commit`
239 Race with a `hg commit`
236 -----------------------
240 -----------------------
237
241
238 $ cp -a reference-repo race-with-commit
242 $ cp -a reference-repo race-with-commit
239 $ cd race-with-commit
243 $ cd race-with-commit
240
244
241 spin a `hg status with some cache to update
245 spin a `hg status with some cache to update
242
246
243 $ hg st >$TESTTMP/status-race-lock.out 2>$TESTTMP/status-race-lock.log \
247 $ hg st >$TESTTMP/status-race-lock.out 2>$TESTTMP/status-race-lock.log \
244 > --config rhg.on-unsupported=abort \
248 > --config rhg.on-unsupported=abort \
245 > --config ${cfg}=$TESTTMP/status-race-lock \
249 > --config ${cfg}=$TESTTMP/status-race-lock \
246 > &
250 > &
247 $ $RUNTESTDIR/testlib/wait-on-file 5 $TESTTMP/status-race-lock.waiting
251 $ $RUNTESTDIR/testlib/wait-on-file 5 $TESTTMP/status-race-lock.waiting
248
252
249 Add a do a commit
253 Add a do a commit
250
254
251 $ hg status
255 $ hg status
252 A dir/o
256 A dir/o
253 R dir/nested/m
257 R dir/nested/m
254 ? dir/n
258 ? dir/n
255 ? p
259 ? p
256 ? q
260 ? q
257 $ hg $d2args commit -m 'racing commit'
261 $ hg $d2args commit -m 'racing commit'
258 $ touch $TESTTMP/status-race-lock
262 $ touch $TESTTMP/status-race-lock
259 $ wait
263 $ wait
260
264
261 commit was created, and status is now clean
265 commit was created, and status is now clean
262
266
263 $ hg log -GT '{node|short} {desc}\n'
267 $ hg log -GT '{node|short} {desc}\n'
264 @ 02a67a77ee9b racing commit
268 @ 02a67a77ee9b racing commit
265 |
269 |
266 o 9a86dcbfb938 more files to have two commit
270 o 9a86dcbfb938 more files to have two commit
267 |
271 |
268 o 4f23db756b09 recreate a bunch of files to facilitate dirstate-v2 append
272 o 4f23db756b09 recreate a bunch of files to facilitate dirstate-v2 append
269
273
270 $ hg status
274 $ hg status
271 ? dir/n
275 ? dir/n
272 ? p
276 ? p
273 ? q
277 ? q
274
278
275 The status process should return a consistent result and not crash.
279 The status process should return a consistent result and not crash.
276
280
277 #if dirstate-v1
281 #if dirstate-v1
278 $ cat $TESTTMP/status-race-lock.out
282 $ cat $TESTTMP/status-race-lock.out
279 M dir/o (no-rhg !)
283 M dir/o (no-rhg !)
280 ? dir/n
284 ? dir/n
281 ? p
285 ? p
282 ? q
286 ? q
283 $ cat $TESTTMP/status-race-lock.log
287 $ cat $TESTTMP/status-race-lock.log
284 warning: ignoring unknown working parent 02a67a77ee9b! (no-rhg !)
288 warning: ignoring unknown working parent 02a67a77ee9b! (no-rhg !)
285 #else
289 #else
286 #if rhg
290 #if rhg
287 #if pre-all-read
291 #if pre-all-read
288 $ cat $TESTTMP/status-race-lock.out
292 $ cat $TESTTMP/status-race-lock.out
289 ? dir/n
293 ? dir/n
290 ? p
294 ? p
291 ? q
295 ? q
292 $ cat $TESTTMP/status-race-lock.log
296 $ cat $TESTTMP/status-race-lock.log
293 #else
297 #else
294 #if dirstate-v2-append
298 #if dirstate-v2-append
295 $ cat $TESTTMP/status-race-lock.out
299 $ cat $TESTTMP/status-race-lock.out
296 A dir/o
300 A dir/o
297 R dir/nested/m
301 R dir/nested/m
298 ? dir/n
302 ? dir/n
299 ? p
303 ? p
300 ? q
304 ? q
301 $ cat $TESTTMP/status-race-lock.log
305 $ cat $TESTTMP/status-race-lock.log
302 #else
306 #else
303 $ cat $TESTTMP/status-race-lock.out
307 $ cat $TESTTMP/status-race-lock.out
304 $ cat $TESTTMP/status-race-lock.log
308 $ cat $TESTTMP/status-race-lock.log
305 abort: dirstate-v2 parse error: not enough bytes on disk
309 abort: dirstate-v2 parse error: not enough bytes on disk
306 #endif
310 #endif
307 #endif
311 #endif
308 #else
312 #else
309 #if rust
313 #if rust
310 #if dirstate-v2-rewrite
314 #if dirstate-v2-rewrite
311 $ cat $TESTTMP/status-race-lock.out
315 $ cat $TESTTMP/status-race-lock.out
316 M dir/o
317 ? dir/n
318 ? p
319 ? q
312 $ cat $TESTTMP/status-race-lock.log
320 $ cat $TESTTMP/status-race-lock.log
313 abort: $ENOENT$: '$TESTTMP/race-with-commit/.hg/dirstate.* (glob)
321 warning: ignoring unknown working parent 02a67a77ee9b!
314 #else
322 #else
315 $ cat $TESTTMP/status-race-lock.out
323 $ cat $TESTTMP/status-race-lock.out
316 A dir/o
324 A dir/o
317 R dir/nested/m
325 R dir/nested/m
318 ? dir/n
326 ? dir/n
319 ? p
327 ? p
320 ? q
328 ? q
321 $ cat $TESTTMP/status-race-lock.log
329 $ cat $TESTTMP/status-race-lock.log
322 #endif
330 #endif
323 #else
331 #else
324 $ cat $TESTTMP/status-race-lock.out
332 $ cat $TESTTMP/status-race-lock.out
325 M dir/o
333 M dir/o
326 ? dir/n
334 ? dir/n
327 ? p
335 ? p
328 ? q
336 ? q
329 $ cat $TESTTMP/status-race-lock.log
337 $ cat $TESTTMP/status-race-lock.log
330 warning: ignoring unknown working parent 02a67a77ee9b!
338 warning: ignoring unknown working parent 02a67a77ee9b!
331 #endif
339 #endif
332 #endif
340 #endif
333 #endif
341 #endif
334
342
335 final cleanup
343 final cleanup
336
344
337 $ rm $TESTTMP/status-race-lock $TESTTMP/status-race-lock.waiting
345 $ rm $TESTTMP/status-race-lock $TESTTMP/status-race-lock.waiting
338 $ cd ..
346 $ cd ..
339
347
340 Race with a `hg update`
348 Race with a `hg update`
341 -----------------------
349 -----------------------
342
350
343 $ cp -a reference-repo race-with-update
351 $ cp -a reference-repo race-with-update
344 $ cd race-with-update
352 $ cd race-with-update
345
353
346 spin a `hg status` with some caches to update
354 spin a `hg status` with some caches to update
347
355
348 $ hg st >$TESTTMP/status-race-lock.out 2>$TESTTMP/status-race-lock.log \
356 $ hg st >$TESTTMP/status-race-lock.out 2>$TESTTMP/status-race-lock.log \
349 > --config rhg.on-unsupported=abort \
357 > --config rhg.on-unsupported=abort \
350 > --config ${cfg}=$TESTTMP/status-race-lock \
358 > --config ${cfg}=$TESTTMP/status-race-lock \
351 > &
359 > &
352 $ $RUNTESTDIR/testlib/wait-on-file 5 $TESTTMP/status-race-lock.waiting
360 $ $RUNTESTDIR/testlib/wait-on-file 5 $TESTTMP/status-race-lock.waiting
353 do an update
361 do an update
354
362
355 $ hg status
363 $ hg status
356 A dir/o
364 A dir/o
357 R dir/nested/m
365 R dir/nested/m
358 ? dir/n
366 ? dir/n
359 ? p
367 ? p
360 ? q
368 ? q
361 $ hg log -GT '{node|short} {desc}\n'
369 $ hg log -GT '{node|short} {desc}\n'
362 @ 9a86dcbfb938 more files to have two commit
370 @ 9a86dcbfb938 more files to have two commit
363 |
371 |
364 o 4f23db756b09 recreate a bunch of files to facilitate dirstate-v2 append
372 o 4f23db756b09 recreate a bunch of files to facilitate dirstate-v2 append
365
373
366 $ hg $d2args update --merge ".~1"
374 $ hg $d2args update --merge ".~1"
367 0 files updated, 0 files merged, 6 files removed, 0 files unresolved
375 0 files updated, 0 files merged, 6 files removed, 0 files unresolved
368 $ touch $TESTTMP/status-race-lock
376 $ touch $TESTTMP/status-race-lock
369 $ wait
377 $ wait
370 #if rhg dirstate-v2-append pre-some-read
378 #if rhg dirstate-v2-append pre-some-read
371 $ hg log -GT '{node|short} {desc}\n'
379 $ hg log -GT '{node|short} {desc}\n'
372 @ 9a86dcbfb938 more files to have two commit
380 @ 9a86dcbfb938 more files to have two commit
373 |
381 |
374 o 4f23db756b09 recreate a bunch of files to facilitate dirstate-v2 append
382 o 4f23db756b09 recreate a bunch of files to facilitate dirstate-v2 append
375
383
376 $ hg status
384 $ hg status
377 A dir/o
385 A dir/o
378 R dir/nested/m
386 R dir/nested/m
379 ! dir/i
387 ! dir/i
380 ! dir/j
388 ! dir/j
381 ! dir/nested/h
389 ! dir/nested/h
382 ! dir2/k
390 ! dir2/k
383 ! dir2/l
391 ! dir2/l
384 ! g
392 ! g
385 ? dir/n
393 ? dir/n
386 ? p
394 ? p
387 ? q
395 ? q
388 #else
396 #else
389 $ hg log -GT '{node|short} {desc}\n'
397 $ hg log -GT '{node|short} {desc}\n'
390 o 9a86dcbfb938 more files to have two commit
398 o 9a86dcbfb938 more files to have two commit
391 |
399 |
392 @ 4f23db756b09 recreate a bunch of files to facilitate dirstate-v2 append
400 @ 4f23db756b09 recreate a bunch of files to facilitate dirstate-v2 append
393
401
394 $ hg status
402 $ hg status
395 A dir/o
403 A dir/o
396 ? dir/n
404 ? dir/n
397 ? p
405 ? p
398 ? q
406 ? q
399 #endif
407 #endif
400
408
401 The status process should return a consistent result and not crash.
409 The status process should return a consistent result and not crash.
402
410
403 #if dirstate-v1
411 #if dirstate-v1
404 $ cat $TESTTMP/status-race-lock.out
412 $ cat $TESTTMP/status-race-lock.out
405 A dir/o
413 A dir/o
406 ? dir/n
414 ? dir/n
407 ? p
415 ? p
408 ? q
416 ? q
409 $ cat $TESTTMP/status-race-lock.log
417 $ cat $TESTTMP/status-race-lock.log
410 #else
418 #else
411 #if rhg
419 #if rhg
412 #if pre-all-read
420 #if pre-all-read
413 $ cat $TESTTMP/status-race-lock.out
421 $ cat $TESTTMP/status-race-lock.out
414 A dir/o
422 A dir/o
415 ? dir/n
423 ? dir/n
416 ? p
424 ? p
417 ? q
425 ? q
418 $ cat $TESTTMP/status-race-lock.log
426 $ cat $TESTTMP/status-race-lock.log
419 #else
427 #else
420 #if dirstate-v2-append
428 #if dirstate-v2-append
421 $ cat $TESTTMP/status-race-lock.out
429 $ cat $TESTTMP/status-race-lock.out
422 A dir/o
430 A dir/o
423 R dir/nested/m
431 R dir/nested/m
424 ! dir/i
432 ! dir/i
425 ! dir/j
433 ! dir/j
426 ! dir/nested/h
434 ! dir/nested/h
427 ! dir2/k
435 ! dir2/k
428 ! dir2/l
436 ! dir2/l
429 ! g
437 ! g
430 ? dir/n
438 ? dir/n
431 ? p
439 ? p
432 ? q
440 ? q
433 $ cat $TESTTMP/status-race-lock.log
441 $ cat $TESTTMP/status-race-lock.log
434 #else
442 #else
435 $ cat $TESTTMP/status-race-lock.out
443 $ cat $TESTTMP/status-race-lock.out
436 $ cat $TESTTMP/status-race-lock.log
444 $ cat $TESTTMP/status-race-lock.log
437 abort: dirstate-v2 parse error: not enough bytes on disk
445 abort: dirstate-v2 parse error: not enough bytes on disk
438 #endif
446 #endif
439 #endif
447 #endif
440 #else
448 #else
441 #if rust
449 #if rust
442 #if dirstate-v2-rewrite
450 #if dirstate-v2-rewrite
443 $ cat $TESTTMP/status-race-lock.out
451 $ cat $TESTTMP/status-race-lock.out
452 A dir/o
453 ? dir/n
454 ? p
455 ? q
444 $ cat $TESTTMP/status-race-lock.log
456 $ cat $TESTTMP/status-race-lock.log
445 abort: $ENOENT$: '$TESTTMP/race-with-update/.hg/dirstate.* (glob)
446 #else
457 #else
447 $ cat $TESTTMP/status-race-lock.out
458 $ cat $TESTTMP/status-race-lock.out
448 A dir/o
459 A dir/o
449 R dir/nested/m
460 R dir/nested/m
450 ! dir/i
461 ! dir/i
451 ! dir/j
462 ! dir/j
452 ! dir/nested/h
463 ! dir/nested/h
453 ! dir2/k
464 ! dir2/k
454 ! dir2/l
465 ! dir2/l
455 ! g
466 ! g
456 ? dir/n
467 ? dir/n
457 ? p
468 ? p
458 ? q
469 ? q
459 $ cat $TESTTMP/status-race-lock.log
470 $ cat $TESTTMP/status-race-lock.log
460 #endif
471 #endif
461 #else
472 #else
462 $ cat $TESTTMP/status-race-lock.out
473 $ cat $TESTTMP/status-race-lock.out
463 A dir/o
474 A dir/o
464 ? dir/n
475 ? dir/n
465 ? p
476 ? p
466 ? q
477 ? q
467 $ cat $TESTTMP/status-race-lock.log
478 $ cat $TESTTMP/status-race-lock.log
468 #endif
479 #endif
469 #endif
480 #endif
470 #endif
481 #endif
471
482
472 final cleanup
483 final cleanup
473
484
474 $ rm $TESTTMP/status-race-lock $TESTTMP/status-race-lock.waiting
485 $ rm $TESTTMP/status-race-lock $TESTTMP/status-race-lock.waiting
475 $ cd ..
486 $ cd ..
476
487
477 Race with a cache updating `hg status`
488 Race with a cache updating `hg status`
478 --------------------------------------
489 --------------------------------------
479
490
480 It is interesting to race with "read-only" operation (that still update its cache)
491 It is interesting to race with "read-only" operation (that still update its cache)
481
492
482 $ cp -a reference-repo race-with-status
493 $ cp -a reference-repo race-with-status
483 $ cd race-with-status
494 $ cd race-with-status
484
495
485 spin a `hg status` with some caches to update
496 spin a `hg status` with some caches to update
486
497
487 $ hg st >$TESTTMP/status-race-lock.out 2>$TESTTMP/status-race-lock.log \
498 $ hg st >$TESTTMP/status-race-lock.out 2>$TESTTMP/status-race-lock.log \
488 > --config rhg.on-unsupported=abort \
499 > --config rhg.on-unsupported=abort \
489 > --config ${cfg}=$TESTTMP/status-race-lock \
500 > --config ${cfg}=$TESTTMP/status-race-lock \
490 > &
501 > &
491 $ $RUNTESTDIR/testlib/wait-on-file 5 $TESTTMP/status-race-lock.waiting
502 $ $RUNTESTDIR/testlib/wait-on-file 5 $TESTTMP/status-race-lock.waiting
492 do an update
503 do an update
493
504
494 $ touch -t 200001020006 f
505 $ touch -t 200001020006 f
495 $ hg $d2args status
506 $ hg $d2args status
496 A dir/o
507 A dir/o
497 R dir/nested/m
508 R dir/nested/m
498 ? dir/n
509 ? dir/n
499 ? p
510 ? p
500 ? q
511 ? q
501 $ touch $TESTTMP/status-race-lock
512 $ touch $TESTTMP/status-race-lock
502 $ wait
513 $ wait
503
514
504 The status process should return a consistent result and not crash.
515 The status process should return a consistent result and not crash.
505
516
506 #if dirstate-v1
517 #if dirstate-v1
507 $ cat $TESTTMP/status-race-lock.out
518 $ cat $TESTTMP/status-race-lock.out
508 A dir/o
519 A dir/o
509 R dir/nested/m
520 R dir/nested/m
510 ? dir/n
521 ? dir/n
511 ? p
522 ? p
512 ? q
523 ? q
513 $ cat $TESTTMP/status-race-lock.log
524 $ cat $TESTTMP/status-race-lock.log
514 #else
525 #else
515 #if rhg
526 #if rhg
516 #if pre-all-read
527 #if pre-all-read
517 $ cat $TESTTMP/status-race-lock.out
528 $ cat $TESTTMP/status-race-lock.out
518 A dir/o
529 A dir/o
519 R dir/nested/m
530 R dir/nested/m
520 ? dir/n
531 ? dir/n
521 ? p
532 ? p
522 ? q
533 ? q
523 $ cat $TESTTMP/status-race-lock.log
534 $ cat $TESTTMP/status-race-lock.log
524 #else
535 #else
525 #if dirstate-v2-append
536 #if dirstate-v2-append
526 $ cat $TESTTMP/status-race-lock.out
537 $ cat $TESTTMP/status-race-lock.out
527 A dir/o
538 A dir/o
528 R dir/nested/m
539 R dir/nested/m
529 ? dir/n
540 ? dir/n
530 ? p
541 ? p
531 ? q
542 ? q
532 $ cat $TESTTMP/status-race-lock.log
543 $ cat $TESTTMP/status-race-lock.log
533 #else
544 #else
534 $ cat $TESTTMP/status-race-lock.out
545 $ cat $TESTTMP/status-race-lock.out
535 $ cat $TESTTMP/status-race-lock.log
546 $ cat $TESTTMP/status-race-lock.log
536 abort: dirstate-v2 parse error: not enough bytes on disk
547 abort: dirstate-v2 parse error: not enough bytes on disk
537 #endif
548 #endif
538 #endif
549 #endif
539 #else
550 #else
540 #if rust
551 #if rust
541 #if dirstate-v2-rewrite
552 #if dirstate-v2-rewrite
542 $ cat $TESTTMP/status-race-lock.out
553 $ cat $TESTTMP/status-race-lock.out
554 A dir/o
555 R dir/nested/m
556 ? dir/n
557 ? p
558 ? q
543 $ cat $TESTTMP/status-race-lock.log
559 $ cat $TESTTMP/status-race-lock.log
544 abort: $ENOENT$: '$TESTTMP/race-with-status/.hg/dirstate.* (glob)
545 #else
560 #else
546 $ cat $TESTTMP/status-race-lock.out
561 $ cat $TESTTMP/status-race-lock.out
547 A dir/o
562 A dir/o
548 R dir/nested/m
563 R dir/nested/m
549 ? dir/n
564 ? dir/n
550 ? p
565 ? p
551 ? q
566 ? q
552 $ cat $TESTTMP/status-race-lock.log
567 $ cat $TESTTMP/status-race-lock.log
553 #endif
568 #endif
554 #else
569 #else
555 $ cat $TESTTMP/status-race-lock.out
570 $ cat $TESTTMP/status-race-lock.out
556 A dir/o
571 A dir/o
557 R dir/nested/m
572 R dir/nested/m
558 ? dir/n
573 ? dir/n
559 ? p
574 ? p
560 ? q
575 ? q
561 $ cat $TESTTMP/status-race-lock.log
576 $ cat $TESTTMP/status-race-lock.log
562 #endif
577 #endif
563 #endif
578 #endif
564 #endif
579 #endif
565
580
566 final cleanup
581 final cleanup
567
582
568 $ rm $TESTTMP/status-race-lock $TESTTMP/status-race-lock.waiting
583 $ rm $TESTTMP/status-race-lock $TESTTMP/status-race-lock.waiting
569 $ cd ..
584 $ cd ..
General Comments 0
You need to be logged in to leave comments. Login now