Mercurial > public > mercurial-scm > hg
comparison mercurial/merge.py @ 52055:b332ae615714
merge: improve working-copy mtime race handling
Explanations inline. This also makes use of `make_mtime_reliable`, which
unifies our mtime raciness logic from the status.
On top of this, this fixes the handling of the pure dirstate status to better
catch racy status, as we've been doing in Rust for a long time now.
author | Rapha?l Gom?s <rgomes@octobus.net> |
---|---|
date | Wed, 16 Oct 2024 19:14:30 +0200 |
parents | f5742367a279 |
children | 8b7123c8947b |
comparison
equal
deleted
inserted
replaced
52054:f5742367a279 | 52055:b332ae615714 |
---|---|
2212 When we reach the current code, the "on disk" part of the update operation | 2212 When we reach the current code, the "on disk" part of the update operation |
2213 is finished. We still assume that no other process raced that "on disk" | 2213 is finished. We still assume that no other process raced that "on disk" |
2214 part, but we want to at least prevent later file changes to alter the | 2214 part, but we want to at least prevent later file changes to alter the |
2215 contents of the file right after the update operation so quickly that the | 2215 contents of the file right after the update operation so quickly that the |
2216 same mtime is recorded for the operation. | 2216 same mtime is recorded for the operation. |
2217 To prevent such ambiguities from happenning, we will only keep the | 2217 |
2218 "file data" for files with mtimes that are strictly in the past, | 2218 To prevent such ambiguities from happenning, we will do (up to) two things: |
2219 i.e. whose mtime is strictly lower than the current time. | 2219 - wait until the filesystem clock has ticked |
2220 - only keep the "file data" for files with mtimes that are strictly in | |
2221 the past, i.e. whose mtime is strictly lower than the current time. | |
2222 | |
2223 We only wait for the system clock to tick if using dirstate-v2, since v1 | |
2224 only has second-level granularity and waiting for a whole second is | |
2225 too much of a penalty in the general case. | |
2226 | |
2227 Although we're assuming that people running dirstate-v2 on Linux | |
2228 don't have a second-granularity FS (with the exclusion of NFS), users | |
2229 can be surprising, and at some point in the future, dirstate-v2 will become | |
2230 the default. To that end, we limit the wait time to 100ms and fall back | |
2231 to the filtering method in case of a timeout. | |
2232 | |
2233 +------------+------+--------------+ | |
2234 | version | wait | filter level | | |
2235 +------------+------+--------------+ | |
2236 | V1 | No | Second | | |
2237 | V2 | Yes | Nanosecond | | |
2238 | V2-slow-fs | No | Second | | |
2239 +------------+------+--------------+ | |
2220 | 2240 |
2221 This protects us from race conditions from operations that could run right | 2241 This protects us from race conditions from operations that could run right |
2222 after this one, especially other Mercurial operations that could be waiting | 2242 after this one, especially other Mercurial operations that could be waiting |
2223 for the wlock to touch files contents and the dirstate. | 2243 for the wlock to touch files contents and the dirstate. |
2224 | 2244 |
2225 In an ideal world, we could only get reliable information in `getfiledata` | 2245 In an ideal world, we could only get reliable information in `getfiledata` |
2226 (from `getbatch`), however the current approach has been a successful | 2246 (from `getbatch`), however this filtering approach has been a successful |
2227 compromise for many years. | 2247 compromise for many years. A patch series of the linux kernel might change |
2248 this in 6.12³. | |
2228 | 2249 |
2229 At the time this comment is written, not using any "cache" file data at all | 2250 At the time this comment is written, not using any "cache" file data at all |
2230 here would not be viable, as it would result is a very large amount of work | 2251 here would not be viable, as it would result is a very large amount of work |
2231 (equivalent to the previous `hg update` during the next status after an | 2252 (equivalent to the previous `hg update` during the next status after an |
2232 update). | 2253 update). |
2237 | 2258 |
2238 [2] using nano-second precision can greatly help here because it makes the | 2259 [2] using nano-second precision can greatly help here because it makes the |
2239 "different write with same mtime" issue virtually vanish. However, | 2260 "different write with same mtime" issue virtually vanish. However, |
2240 dirstate v1 cannot store such precision and a bunch of python-runtime, | 2261 dirstate v1 cannot store such precision and a bunch of python-runtime, |
2241 operating-system and filesystem parts do not provide us with such | 2262 operating-system and filesystem parts do not provide us with such |
2242 precision, so we have to operate as if it wasn't available.""" | 2263 precision, so we have to operate as if it wasn't available. |
2264 | |
2265 [3] https://lore.kernel.org/all/20241002-mgtime-v10-8-d1c4717f5284@kernel.org | |
2266 """ | |
2243 ambiguous_mtime: FileData = {} | 2267 ambiguous_mtime: FileData = {} |
2244 now = timestamp.get_fs_now(repo.vfs) | 2268 dirstate_v2 = repo.dirstate._use_dirstate_v2 |
2269 fs_now_result = None | |
2270 fast_enough_fs = True | |
2271 if dirstate_v2: | |
2272 fstype = util.getfstype(repo.vfs.base) | |
2273 # Exclude NFS right off the bat | |
2274 fast_enough_fs = fstype != b'nfs' | |
2275 if fstype is not None and fast_enough_fs: | |
2276 fs_now_result = timestamp.wait_until_fs_tick(repo.vfs) | |
2277 | |
2278 if fs_now_result is None: | |
2279 try: | |
2280 now = timestamp.get_fs_now(repo.vfs) | |
2281 fs_now_result = (now, False) | |
2282 except OSError: | |
2283 pass | |
2284 | |
2245 if fs_now_result is None: | 2285 if fs_now_result is None: |
2246 # we can't write to the FS, so we won't actually update | 2286 # we can't write to the FS, so we won't actually update |
2247 # the dirstate content anyway, no need to put cache | 2287 # the dirstate content anyway, no need to put cache |
2248 # information. | 2288 # information. |
2249 return None | 2289 return None |
2250 else: | 2290 else: |
2251 now_sec = now[0] | |
2252 now, timed_out = fs_now_result | 2291 now, timed_out = fs_now_result |
2253 if timed_out: | 2292 if timed_out: |
2254 fast_enough_fs = False | 2293 fast_enough_fs = False |
2255 for f, m in file_data.items(): | 2294 for f, m in file_data.items(): |
2256 if m is not None and m[2][0] >= now_sec: | 2295 if m is not None: |
2257 ambiguous_mtime[f] = (m[0], m[1], None) | 2296 reliable = timestamp.make_mtime_reliable(m[2], now) |
2297 if reliable is None or ( | |
2298 reliable[2] and (not dirstate_v2 or not fast_enough_fs) | |
2299 ): | |
2300 # Either it's not reliable, or it's second ambiguous | |
2301 # and we're in dirstate-v1 or in a slow fs, so discard | |
2302 # the mtime. | |
2303 ambiguous_mtime[f] = (m[0], m[1], None) | |
2304 elif reliable[2]: | |
2305 # We need to remember that this time is "second ambiguous" | |
2306 # otherwise the next status might miss a subsecond change | |
2307 # if its "stat" doesn't provide nanoseconds. | |
2308 # | |
2309 # TODO make osutil.c understand nanoseconds when possible | |
2310 # (see timestamp.py for the same note) | |
2311 ambiguous_mtime[f] = (m[0], m[1], reliable) | |
2258 for f, m in ambiguous_mtime.items(): | 2312 for f, m in ambiguous_mtime.items(): |
2259 file_data[f] = m | 2313 file_data[f] = m |
2260 return file_data | 2314 return file_data |
2261 | 2315 |
2262 | 2316 |