equal
deleted
inserted
replaced
230 f: impl FnOnce() -> R, |
230 f: impl FnOnce() -> R, |
231 ) -> Result<R, LockError> { |
231 ) -> Result<R, LockError> { |
232 try_with_lock_no_wait(self.hg_vfs(), "wlock", f) |
232 try_with_lock_no_wait(self.hg_vfs(), "wlock", f) |
233 } |
233 } |
234 |
234 |
235 pub fn has_dirstate_v2(&self) -> bool { |
235 /// Whether this repo should use dirstate-v2. |
|
236 /// The presence of `dirstate-v2` in the requirements does not mean that |
|
237 /// the on-disk dirstate is necessarily in version 2. In most cases, |
|
238 /// a dirstate-v2 file will indeed be found, but in rare cases (like the |
|
239 /// upgrade mechanism being cut short), the on-disk version will be a |
|
240 /// v1 file. |
|
241 /// Semantically, having a requirement only means that a client should be |
|
242 /// able to understand the repo *if* it uses the requirement, but not that |
|
243 /// the requirement is actually used. |
|
244 pub fn use_dirstate_v2(&self) -> bool { |
236 self.requirements |
245 self.requirements |
237 .contains(requirements::DIRSTATE_V2_REQUIREMENT) |
246 .contains(requirements::DIRSTATE_V2_REQUIREMENT) |
238 } |
247 } |
239 |
248 |
240 pub fn has_sparse(&self) -> bool { |
249 pub fn has_sparse(&self) -> bool { |
275 |
284 |
276 fn read_dirstate_parents(&self) -> Result<DirstateParents, HgError> { |
285 fn read_dirstate_parents(&self) -> Result<DirstateParents, HgError> { |
277 let dirstate = self.dirstate_file_contents()?; |
286 let dirstate = self.dirstate_file_contents()?; |
278 let parents = if dirstate.is_empty() { |
287 let parents = if dirstate.is_empty() { |
279 DirstateParents::NULL |
288 DirstateParents::NULL |
280 } else if self.has_dirstate_v2() { |
289 } else if self.use_dirstate_v2() { |
281 let docket = |
290 let docket = |
282 crate::dirstate_tree::on_disk::read_docket(&dirstate)?; |
291 crate::dirstate_tree::on_disk::read_docket(&dirstate)?; |
283 docket.parents() |
292 docket.parents() |
284 } else { |
293 } else { |
285 *crate::dirstate::parsers::parse_dirstate_parents(&dirstate)? |
294 *crate::dirstate::parsers::parse_dirstate_parents(&dirstate)? |
294 /// Namely, the inode, data file uuid and the data size. |
303 /// Namely, the inode, data file uuid and the data size. |
295 fn get_dirstate_data_file_integrity( |
304 fn get_dirstate_data_file_integrity( |
296 &self, |
305 &self, |
297 ) -> Result<DirstateMapIdentity, HgError> { |
306 ) -> Result<DirstateMapIdentity, HgError> { |
298 assert!( |
307 assert!( |
299 self.has_dirstate_v2(), |
308 self.use_dirstate_v2(), |
300 "accessing dirstate data file ID without dirstate-v2" |
309 "accessing dirstate data file ID without dirstate-v2" |
301 ); |
310 ); |
302 // Get the identity before the contents since we could have a race |
311 // Get the identity before the contents since we could have a race |
303 // between the two. Having an identity that is too old is fine, but |
312 // between the two. Having an identity that is too old is fine, but |
304 // one that is younger than the content change is bad. |
313 // one that is younger than the content change is bad. |
314 Ok((identity, Some(docket.uuid.to_owned()), docket.data_size())) |
323 Ok((identity, Some(docket.uuid.to_owned()), docket.data_size())) |
315 } |
324 } |
316 } |
325 } |
317 |
326 |
318 fn new_dirstate_map(&self) -> Result<OwningDirstateMap, DirstateError> { |
327 fn new_dirstate_map(&self) -> Result<OwningDirstateMap, DirstateError> { |
319 if self.has_dirstate_v2() { |
328 if self.use_dirstate_v2() { |
320 // The v2 dirstate is split into a docket and a data file. |
329 // The v2 dirstate is split into a docket and a data file. |
321 // Since we don't always take the `wlock` to read it |
330 // Since we don't always take the `wlock` to read it |
322 // (like in `hg status`), it is susceptible to races. |
331 // (like in `hg status`), it is susceptible to races. |
323 // A simple retry method should be enough since full rewrites |
332 // A simple retry method should be enough since full rewrites |
324 // only happen when too much garbage data is present and |
333 // only happen when too much garbage data is present and |
548 pub fn write_dirstate(&self) -> Result<(), DirstateError> { |
557 pub fn write_dirstate(&self) -> Result<(), DirstateError> { |
549 let map = self.dirstate_map()?; |
558 let map = self.dirstate_map()?; |
550 // TODO: Maintain a `DirstateMap::dirty` flag, and return early here if |
559 // TODO: Maintain a `DirstateMap::dirty` flag, and return early here if |
551 // it’s unset |
560 // it’s unset |
552 let parents = self.dirstate_parents()?; |
561 let parents = self.dirstate_parents()?; |
553 let (packed_dirstate, old_uuid_to_remove) = if self.has_dirstate_v2() { |
562 let (packed_dirstate, old_uuid_to_remove) = if self.use_dirstate_v2() { |
554 let (identity, uuid, data_size) = |
563 let (identity, uuid, data_size) = |
555 self.get_dirstate_data_file_integrity()?; |
564 self.get_dirstate_data_file_integrity()?; |
556 let identity_changed = identity != map.old_identity(); |
565 let identity_changed = identity != map.old_identity(); |
557 let uuid_changed = uuid.as_deref() != map.old_uuid(); |
566 let uuid_changed = uuid.as_deref() != map.old_uuid(); |
558 let data_length_changed = data_size != map.old_data_size(); |
567 let data_length_changed = data_size != map.old_data_size(); |