Show More
@@ -50,6 +50,10 b" pub struct DirstateMap<'on_disk> {" | |||||
50 |
|
50 | |||
51 | /// How many bytes of `on_disk` are not used anymore |
|
51 | /// How many bytes of `on_disk` are not used anymore | |
52 | pub(super) unreachable_bytes: u32, |
|
52 | pub(super) unreachable_bytes: u32, | |
|
53 | ||||
|
54 | /// Size of the data used to first load this `DirstateMap`. Used in case | |||
|
55 | /// we need to write some new metadata, but no new data on disk. | |||
|
56 | pub(super) old_data_size: usize, | |||
53 | } |
|
57 | } | |
54 |
|
58 | |||
55 | /// Using a plain `HgPathBuf` of the full path from the repository root as a |
|
59 | /// Using a plain `HgPathBuf` of the full path from the repository root as a | |
@@ -436,6 +440,7 b" impl<'on_disk> DirstateMap<'on_disk> {" | |||||
436 | nodes_with_copy_source_count: 0, |
|
440 | nodes_with_copy_source_count: 0, | |
437 | ignore_patterns_hash: [0; on_disk::IGNORE_PATTERNS_HASH_LEN], |
|
441 | ignore_patterns_hash: [0; on_disk::IGNORE_PATTERNS_HASH_LEN], | |
438 | unreachable_bytes: 0, |
|
442 | unreachable_bytes: 0, | |
|
443 | old_data_size: 0, | |||
439 | } |
|
444 | } | |
440 | } |
|
445 | } | |
441 |
|
446 | |||
@@ -1232,12 +1237,13 b' impl OwningDirstateMap {' | |||||
1232 | /// Returns new data and metadata together with whether that data should be |
|
1237 | /// Returns new data and metadata together with whether that data should be | |
1233 | /// appended to the existing data file whose content is at |
|
1238 | /// appended to the existing data file whose content is at | |
1234 | /// `map.on_disk` (true), instead of written to a new data file |
|
1239 | /// `map.on_disk` (true), instead of written to a new data file | |
1235 | /// (false). |
|
1240 | /// (false), and the previous size of data on disk. | |
1236 | #[timed] |
|
1241 | #[timed] | |
1237 | pub fn pack_v2( |
|
1242 | pub fn pack_v2( | |
1238 | &self, |
|
1243 | &self, | |
1239 | can_append: bool, |
|
1244 | can_append: bool, | |
1240 |
) -> Result<(Vec<u8>, on_disk::TreeMetadata, bool), DirstateError> |
|
1245 | ) -> Result<(Vec<u8>, on_disk::TreeMetadata, bool, usize), DirstateError> | |
|
1246 | { | |||
1241 | let map = self.get_map(); |
|
1247 | let map = self.get_map(); | |
1242 | on_disk::write(map, can_append) |
|
1248 | on_disk::write(map, can_append) | |
1243 | } |
|
1249 | } | |
@@ -1795,7 +1801,8 b' mod tests {' | |||||
1795 | None, |
|
1801 | None, | |
1796 | )?; |
|
1802 | )?; | |
1797 |
|
1803 | |||
1798 |
let (packed, metadata, _should_append) = |
|
1804 | let (packed, metadata, _should_append, _old_data_size) = | |
|
1805 | map.pack_v2(false)?; | |||
1799 | let packed_len = packed.len(); |
|
1806 | let packed_len = packed.len(); | |
1800 | assert!(packed_len > 0); |
|
1807 | assert!(packed_len > 0); | |
1801 |
|
1808 |
@@ -290,6 +290,7 b" pub(super) fn read<'on_disk>(" | |||||
290 | nodes_with_copy_source_count: meta.nodes_with_copy_source_count.get(), |
|
290 | nodes_with_copy_source_count: meta.nodes_with_copy_source_count.get(), | |
291 | ignore_patterns_hash: meta.ignore_patterns_hash, |
|
291 | ignore_patterns_hash: meta.ignore_patterns_hash, | |
292 | unreachable_bytes: meta.unreachable_bytes.get(), |
|
292 | unreachable_bytes: meta.unreachable_bytes.get(), | |
|
293 | old_data_size: on_disk.len(), | |||
293 | }; |
|
294 | }; | |
294 | Ok(dirstate_map) |
|
295 | Ok(dirstate_map) | |
295 | } |
|
296 | } | |
@@ -601,11 +602,11 b" pub(crate) fn for_each_tracked_path<'on_" | |||||
601 | /// Returns new data and metadata, together with whether that data should be |
|
602 | /// Returns new data and metadata, together with whether that data should be | |
602 | /// appended to the existing data file whose content is at |
|
603 | /// appended to the existing data file whose content is at | |
603 | /// `dirstate_map.on_disk` (true), instead of written to a new data file |
|
604 | /// `dirstate_map.on_disk` (true), instead of written to a new data file | |
604 | /// (false). |
|
605 | /// (false), and the previous size of data on disk. | |
605 | pub(super) fn write( |
|
606 | pub(super) fn write( | |
606 | dirstate_map: &DirstateMap, |
|
607 | dirstate_map: &DirstateMap, | |
607 | can_append: bool, |
|
608 | can_append: bool, | |
608 | ) -> Result<(Vec<u8>, TreeMetadata, bool), DirstateError> { |
|
609 | ) -> Result<(Vec<u8>, TreeMetadata, bool, usize), DirstateError> { | |
609 | let append = can_append && dirstate_map.write_should_append(); |
|
610 | let append = can_append && dirstate_map.write_should_append(); | |
610 |
|
611 | |||
611 | // This ignores the space for paths, and for nodes without an entry. |
|
612 | // This ignores the space for paths, and for nodes without an entry. | |
@@ -631,7 +632,7 b' pub(super) fn write(' | |||||
631 | unused: [0; 4], |
|
632 | unused: [0; 4], | |
632 | ignore_patterns_hash: dirstate_map.ignore_patterns_hash, |
|
633 | ignore_patterns_hash: dirstate_map.ignore_patterns_hash, | |
633 | }; |
|
634 | }; | |
634 | Ok((writer.out, meta, append)) |
|
635 | Ok((writer.out, meta, append, dirstate_map.old_data_size)) | |
635 | } |
|
636 | } | |
636 |
|
637 | |||
637 | struct Writer<'dmap, 'on_disk> { |
|
638 | struct Writer<'dmap, 'on_disk> { |
@@ -423,22 +423,25 b' impl Repo {' | |||||
423 | // TODO: Maintain a `DirstateMap::dirty` flag, and return early here if |
|
423 | // TODO: Maintain a `DirstateMap::dirty` flag, and return early here if | |
424 | // it’s unset |
|
424 | // it’s unset | |
425 | let parents = self.dirstate_parents()?; |
|
425 | let parents = self.dirstate_parents()?; | |
426 | let packed_dirstate = if self.has_dirstate_v2() { |
|
426 | let (packed_dirstate, old_uuid_to_remove) = if self.has_dirstate_v2() { | |
427 | let uuid = self.dirstate_data_file_uuid.get_or_init(self)?; |
|
427 | let uuid = self.dirstate_data_file_uuid.get_or_init(self)?; | |
428 | let mut uuid = uuid.as_ref(); |
|
428 | let mut uuid = uuid.as_ref(); | |
429 | let can_append = uuid.is_some(); |
|
429 | let can_append = uuid.is_some(); | |
430 |
let (data, tree_metadata, append) = |
|
430 | let (data, tree_metadata, append, old_data_size) = | |
|
431 | map.pack_v2(can_append)?; | |||
431 | if !append { |
|
432 | if !append { | |
432 | uuid = None |
|
433 | uuid = None | |
433 | } |
|
434 | } | |
434 | let uuid = if let Some(uuid) = uuid { |
|
435 | let (uuid, old_uuid) = if let Some(uuid) = uuid { | |
435 | std::str::from_utf8(uuid) |
|
436 | let as_str = std::str::from_utf8(uuid) | |
436 | .map_err(|_| { |
|
437 | .map_err(|_| { | |
437 | HgError::corrupted("non-UTF-8 dirstate data file ID") |
|
438 | HgError::corrupted("non-UTF-8 dirstate data file ID") | |
438 | })? |
|
439 | })? | |
439 | .to_owned() |
|
440 | .to_owned(); | |
|
441 | let old_uuid_to_remove = Some(as_str.to_owned()); | |||
|
442 | (as_str, old_uuid_to_remove) | |||
440 | } else { |
|
443 | } else { | |
441 | DirstateDocket::new_uid() |
|
444 | (DirstateDocket::new_uid(), None) | |
442 | }; |
|
445 | }; | |
443 | let data_filename = format!("dirstate.{}", uuid); |
|
446 | let data_filename = format!("dirstate.{}", uuid); | |
444 | let data_filename = self.hg_vfs().join(data_filename); |
|
447 | let data_filename = self.hg_vfs().join(data_filename); | |
@@ -453,13 +456,23 b' impl Repo {' | |||||
453 | // returns `ErrorKind::AlreadyExists`? Collision chance of two |
|
456 | // returns `ErrorKind::AlreadyExists`? Collision chance of two | |
454 | // random IDs is one in 2**32 |
|
457 | // random IDs is one in 2**32 | |
455 | let mut file = options.open(&data_filename)?; |
|
458 | let mut file = options.open(&data_filename)?; | |
456 | file.write_all(&data)?; |
|
459 | if data.is_empty() { | |
457 | file.flush()?; |
|
460 | // If we're not appending anything, the data size is the | |
458 | // TODO: use https://doc.rust-lang.org/std/io/trait.Seek.html#method.stream_position when we require Rust 1.51+ |
|
461 | // same as in the previous docket. It is *not* the file | |
459 | file.seek(SeekFrom::Current(0)) |
|
462 | // length, since it could have garbage at the end. | |
|
463 | // We don't have to worry about it when we do have data | |||
|
464 | // to append since we rewrite the root node in this case. | |||
|
465 | Ok(old_data_size as u64) | |||
|
466 | } else { | |||
|
467 | file.write_all(&data)?; | |||
|
468 | file.flush()?; | |||
|
469 | // TODO: use https://doc.rust-lang.org/std/io/trait.Seek.html#method.stream_position when we require Rust 1.51+ | |||
|
470 | file.seek(SeekFrom::Current(0)) | |||
|
471 | } | |||
460 | })() |
|
472 | })() | |
461 | .when_writing_file(&data_filename)?; |
|
473 | .when_writing_file(&data_filename)?; | |
462 | DirstateDocket::serialize( |
|
474 | ||
|
475 | let packed_dirstate = DirstateDocket::serialize( | |||
463 | parents, |
|
476 | parents, | |
464 | tree_metadata, |
|
477 | tree_metadata, | |
465 | data_size, |
|
478 | data_size, | |
@@ -467,11 +480,20 b' impl Repo {' | |||||
467 | ) |
|
480 | ) | |
468 | .map_err(|_: std::num::TryFromIntError| { |
|
481 | .map_err(|_: std::num::TryFromIntError| { | |
469 | HgError::corrupted("overflow in dirstate docket serialization") |
|
482 | HgError::corrupted("overflow in dirstate docket serialization") | |
470 | })? |
|
483 | })?; | |
|
484 | ||||
|
485 | (packed_dirstate, old_uuid) | |||
471 | } else { |
|
486 | } else { | |
472 | map.pack_v1(parents)? |
|
487 | (map.pack_v1(parents)?, None) | |
473 | }; |
|
488 | }; | |
474 | self.hg_vfs().atomic_write("dirstate", &packed_dirstate)?; |
|
489 | ||
|
490 | let vfs = self.hg_vfs(); | |||
|
491 | vfs.atomic_write("dirstate", &packed_dirstate)?; | |||
|
492 | if let Some(uuid) = old_uuid_to_remove { | |||
|
493 | // Remove the old data file after the new docket pointing to the | |||
|
494 | // new data file was written. | |||
|
495 | vfs.remove_file(format!("dirstate.{}", uuid))?; | |||
|
496 | } | |||
475 | Ok(()) |
|
497 | Ok(()) | |
476 | } |
|
498 | } | |
477 | } |
|
499 | } |
@@ -252,7 +252,7 b' py_class!(pub class DirstateMap |py| {' | |||||
252 | let inner = self.inner(py).borrow(); |
|
252 | let inner = self.inner(py).borrow(); | |
253 | let result = inner.pack_v2(can_append); |
|
253 | let result = inner.pack_v2(can_append); | |
254 | match result { |
|
254 | match result { | |
255 | Ok((packed, tree_metadata, append)) => { |
|
255 | Ok((packed, tree_metadata, append, _old_data_size)) => { | |
256 | let packed = PyBytes::new(py, &packed); |
|
256 | let packed = PyBytes::new(py, &packed); | |
257 | let tree_metadata = PyBytes::new(py, tree_metadata.as_bytes()); |
|
257 | let tree_metadata = PyBytes::new(py, tree_metadata.as_bytes()); | |
258 | let tuple = (packed, tree_metadata, append); |
|
258 | let tuple = (packed, tree_metadata, append); |
General Comments 0
You need to be logged in to leave comments.
Login now