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