Show More
@@ -951,7 +951,7 b' impl OwningDirstateMap {' | |||
|
951 | 951 | pub fn pack_v2( |
|
952 | 952 | &self, |
|
953 | 953 | can_append: bool, |
|
954 |
) -> Result<(Vec<u8>, |
|
|
954 | ) -> Result<(Vec<u8>, on_disk::TreeMetadata, bool), DirstateError> { | |
|
955 | 955 | let map = self.get_map(); |
|
956 | 956 | on_disk::write(map, can_append) |
|
957 | 957 | } |
@@ -14,8 +14,10 b' use bitflags::bitflags;' | |||
|
14 | 14 | use bytes_cast::unaligned::{U16Be, U32Be}; |
|
15 | 15 | use bytes_cast::BytesCast; |
|
16 | 16 | use format_bytes::format_bytes; |
|
17 | use rand::Rng; | |
|
17 | 18 | use std::borrow::Cow; |
|
18 | 19 | use std::convert::{TryFrom, TryInto}; |
|
20 | use std::fmt::Write; | |
|
19 | 21 | |
|
20 | 22 | /// Added at the start of `.hg/dirstate` when the "v2" format is used. |
|
21 | 23 | /// This a redundant sanity check more than an actual "magic number" since |
@@ -68,7 +70,7 b" pub struct Docket<'on_disk> {" | |||
|
68 | 70 | /// section of `mercurial/helptext/internals/dirstate-v2.txt` |
|
69 | 71 | #[derive(BytesCast)] |
|
70 | 72 | #[repr(C)] |
|
71 | struct TreeMetadata { | |
|
73 | pub struct TreeMetadata { | |
|
72 | 74 | root_nodes: ChildNodes, |
|
73 | 75 | nodes_with_entry_count: Size, |
|
74 | 76 | nodes_with_copy_source_count: Size, |
@@ -186,7 +188,51 b' impl From<DirstateV2ParseError> for crat' | |||
|
186 | 188 | } |
|
187 | 189 | } |
|
188 | 190 | |
|
191 | impl TreeMetadata { | |
|
192 | pub fn as_bytes(&self) -> &[u8] { | |
|
193 | BytesCast::as_bytes(self) | |
|
194 | } | |
|
195 | } | |
|
196 | ||
|
189 | 197 | impl<'on_disk> Docket<'on_disk> { |
|
198 | /// Generate the identifier for a new data file | |
|
199 | /// | |
|
200 | /// TODO: support the `HGTEST_UUIDFILE` environment variable. | |
|
201 | /// See `mercurial/revlogutils/docket.py` | |
|
202 | pub fn new_uid() -> String { | |
|
203 | const ID_LENGTH: usize = 8; | |
|
204 | let mut id = String::with_capacity(ID_LENGTH); | |
|
205 | let mut rng = rand::thread_rng(); | |
|
206 | for _ in 0..ID_LENGTH { | |
|
207 | // One random hexadecimal digit. | |
|
208 | // `unwrap` never panics because `impl Write for String` | |
|
209 | // never returns an error. | |
|
210 | write!(&mut id, "{:x}", rng.gen_range(0, 16)).unwrap(); | |
|
211 | } | |
|
212 | id | |
|
213 | } | |
|
214 | ||
|
215 | pub fn serialize( | |
|
216 | parents: DirstateParents, | |
|
217 | tree_metadata: TreeMetadata, | |
|
218 | data_size: u64, | |
|
219 | uuid: &[u8], | |
|
220 | ) -> Result<Vec<u8>, std::num::TryFromIntError> { | |
|
221 | let header = DocketHeader { | |
|
222 | marker: *V2_FORMAT_MARKER, | |
|
223 | parent_1: parents.p1.pad_to_256_bits(), | |
|
224 | parent_2: parents.p2.pad_to_256_bits(), | |
|
225 | metadata: tree_metadata, | |
|
226 | data_size: u32::try_from(data_size)?.into(), | |
|
227 | uuid_size: uuid.len().try_into()?, | |
|
228 | }; | |
|
229 | let header = header.as_bytes(); | |
|
230 | let mut docket = Vec::with_capacity(header.len() + uuid.len()); | |
|
231 | docket.extend_from_slice(header); | |
|
232 | docket.extend_from_slice(uuid); | |
|
233 | Ok(docket) | |
|
234 | } | |
|
235 | ||
|
190 | 236 | pub fn parents(&self) -> DirstateParents { |
|
191 | 237 | use crate::Node; |
|
192 | 238 | let p1 = Node::try_from(&self.header.parent_1[..USED_NODE_ID_BYTES]) |
@@ -555,7 +601,7 b" pub(crate) fn for_each_tracked_path<'on_" | |||
|
555 | 601 | pub(super) fn write( |
|
556 | 602 | dirstate_map: &DirstateMap, |
|
557 | 603 | can_append: bool, |
|
558 |
) -> Result<(Vec<u8>, |
|
|
604 | ) -> Result<(Vec<u8>, TreeMetadata, bool), DirstateError> { | |
|
559 | 605 | let append = can_append && dirstate_map.write_should_append(); |
|
560 | 606 | |
|
561 | 607 | // This ignores the space for paths, and for nodes without an entry. |
@@ -581,7 +627,7 b' pub(super) fn write(' | |||
|
581 | 627 | unused: [0; 4], |
|
582 | 628 | ignore_patterns_hash: dirstate_map.ignore_patterns_hash, |
|
583 | 629 | }; |
|
584 |
Ok((writer.out, meta |
|
|
630 | Ok((writer.out, meta, append)) | |
|
585 | 631 | } |
|
586 | 632 | |
|
587 | 633 | struct Writer<'dmap, 'on_disk> { |
@@ -2,9 +2,10 b' use crate::changelog::Changelog;' | |||
|
2 | 2 | use crate::config::{Config, ConfigError, ConfigParseError}; |
|
3 | 3 | use crate::dirstate::DirstateParents; |
|
4 | 4 | use crate::dirstate_tree::dirstate_map::DirstateMap; |
|
5 | use crate::dirstate_tree::on_disk::Docket as DirstateDocket; | |
|
5 | 6 | use crate::dirstate_tree::owning::OwningDirstateMap; |
|
6 | use crate::errors::HgError; | |
|
7 | 7 | use crate::errors::HgResultExt; |
|
8 | use crate::errors::{HgError, IoResultExt}; | |
|
8 | 9 | use crate::exit_codes; |
|
9 | 10 | use crate::lock::{try_with_lock_no_wait, LockError}; |
|
10 | 11 | use crate::manifest::{Manifest, Manifestlog}; |
@@ -18,6 +19,9 b' use crate::{requirements, NodePrefix};' | |||
|
18 | 19 | use crate::{DirstateError, Revision}; |
|
19 | 20 | use std::cell::{Ref, RefCell, RefMut}; |
|
20 | 21 | use std::collections::HashSet; |
|
22 | use std::io::Seek; | |
|
23 | use std::io::SeekFrom; | |
|
24 | use std::io::Write as IoWrite; | |
|
21 | 25 | use std::path::{Path, PathBuf}; |
|
22 | 26 | |
|
23 | 27 | /// A repository on disk |
@@ -416,6 +420,70 b' impl Repo {' | |||
|
416 | 420 | pub fn filelog(&self, path: &HgPath) -> Result<Filelog, HgError> { |
|
417 | 421 | Filelog::open(self, path) |
|
418 | 422 | } |
|
423 | ||
|
424 | /// Write to disk any updates that were made through `dirstate_map_mut`. | |
|
425 | /// | |
|
426 | /// The "wlock" must be held while calling this. | |
|
427 | /// See for example `try_with_wlock_no_wait`. | |
|
428 | /// | |
|
429 | /// TODO: have a `WritableRepo` type only accessible while holding the | |
|
430 | /// lock? | |
|
431 | pub fn write_dirstate(&self) -> Result<(), DirstateError> { | |
|
432 | let map = self.dirstate_map()?; | |
|
433 | // TODO: Maintain a `DirstateMap::dirty` flag, and return early here if | |
|
434 | // it’s unset | |
|
435 | let parents = self.dirstate_parents()?; | |
|
436 | let packed_dirstate = if self.has_dirstate_v2() { | |
|
437 | let uuid = self.dirstate_data_file_uuid.get_or_init(self)?; | |
|
438 | let mut uuid = uuid.as_ref(); | |
|
439 | let can_append = uuid.is_some(); | |
|
440 | let (data, tree_metadata, append) = map.pack_v2(can_append)?; | |
|
441 | if !append { | |
|
442 | uuid = None | |
|
443 | } | |
|
444 | let uuid = if let Some(uuid) = uuid { | |
|
445 | std::str::from_utf8(uuid) | |
|
446 | .map_err(|_| { | |
|
447 | HgError::corrupted("non-UTF-8 dirstate data file ID") | |
|
448 | })? | |
|
449 | .to_owned() | |
|
450 | } else { | |
|
451 | DirstateDocket::new_uid() | |
|
452 | }; | |
|
453 | let data_filename = format!("dirstate.{}", uuid); | |
|
454 | let data_filename = self.hg_vfs().join(data_filename); | |
|
455 | let mut options = std::fs::OpenOptions::new(); | |
|
456 | if append { | |
|
457 | options.append(true); | |
|
458 | } else { | |
|
459 | options.write(true).create_new(true); | |
|
460 | } | |
|
461 | let data_size = (|| { | |
|
462 | // TODO: loop and try another random ID if !append and this | |
|
463 | // returns `ErrorKind::AlreadyExists`? Collision chance of two | |
|
464 | // random IDs is one in 2**32 | |
|
465 | let mut file = options.open(&data_filename)?; | |
|
466 | file.write_all(&data)?; | |
|
467 | file.flush()?; | |
|
468 | // TODO: use https://doc.rust-lang.org/std/io/trait.Seek.html#method.stream_position when we require Rust 1.51+ | |
|
469 | file.seek(SeekFrom::Current(0)) | |
|
470 | })() | |
|
471 | .when_writing_file(&data_filename)?; | |
|
472 | DirstateDocket::serialize( | |
|
473 | parents, | |
|
474 | tree_metadata, | |
|
475 | data_size, | |
|
476 | uuid.as_bytes(), | |
|
477 | ) | |
|
478 | .map_err(|_: std::num::TryFromIntError| { | |
|
479 | HgError::corrupted("overflow in dirstate docket serialization") | |
|
480 | })? | |
|
481 | } else { | |
|
482 | map.pack_v1(parents)? | |
|
483 | }; | |
|
484 | self.hg_vfs().atomic_write("dirstate", &packed_dirstate)?; | |
|
485 | Ok(()) | |
|
486 | } | |
|
419 | 487 | } |
|
420 | 488 | |
|
421 | 489 | /// Lazily-initialized component of `Repo` with interior mutability |
@@ -174,6 +174,12 b' impl Node {' | |||
|
174 | 174 | data: self.data, |
|
175 | 175 | } |
|
176 | 176 | } |
|
177 | ||
|
178 | pub fn pad_to_256_bits(&self) -> [u8; 32] { | |
|
179 | let mut bits = [0; 32]; | |
|
180 | bits[..NODE_BYTES_LENGTH].copy_from_slice(&self.data); | |
|
181 | bits | |
|
182 | } | |
|
177 | 183 | } |
|
178 | 184 | |
|
179 | 185 | /// The beginning of a binary revision SHA. |
@@ -222,7 +222,7 b' py_class!(pub class DirstateMap |py| {' | |||
|
222 | 222 | match result { |
|
223 | 223 | Ok((packed, tree_metadata, append)) => { |
|
224 | 224 | let packed = PyBytes::new(py, &packed); |
|
225 |
let tree_metadata = PyBytes::new(py, |
|
|
225 | let tree_metadata = PyBytes::new(py, tree_metadata.as_bytes()); | |
|
226 | 226 | let tuple = (packed, tree_metadata, append); |
|
227 | 227 | Ok(tuple.to_py_object(py).into_object()) |
|
228 | 228 | }, |
General Comments 0
You need to be logged in to leave comments.
Login now