##// END OF EJS Templates
rhg: Add Repo::write_dirstate...
Simon Sapin -
r49249:2097f635 default
parent child Browse files
Show More
@@ -951,7 +951,7 b' impl OwningDirstateMap {'
951 pub fn pack_v2(
951 pub fn pack_v2(
952 &self,
952 &self,
953 can_append: bool,
953 can_append: bool,
954 ) -> Result<(Vec<u8>, Vec<u8>, bool), DirstateError> {
954 ) -> Result<(Vec<u8>, on_disk::TreeMetadata, bool), DirstateError> {
955 let map = self.get_map();
955 let map = self.get_map();
956 on_disk::write(map, can_append)
956 on_disk::write(map, can_append)
957 }
957 }
@@ -14,8 +14,10 b' use bitflags::bitflags;'
14 use bytes_cast::unaligned::{U16Be, U32Be};
14 use bytes_cast::unaligned::{U16Be, U32Be};
15 use bytes_cast::BytesCast;
15 use bytes_cast::BytesCast;
16 use format_bytes::format_bytes;
16 use format_bytes::format_bytes;
17 use rand::Rng;
17 use std::borrow::Cow;
18 use std::borrow::Cow;
18 use std::convert::{TryFrom, TryInto};
19 use std::convert::{TryFrom, TryInto};
20 use std::fmt::Write;
19
21
20 /// Added at the start of `.hg/dirstate` when the "v2" format is used.
22 /// Added at the start of `.hg/dirstate` when the "v2" format is used.
21 /// This a redundant sanity check more than an actual "magic number" since
23 /// This a redundant sanity check more than an actual "magic number" since
@@ -68,7 +70,7 b" pub struct Docket<'on_disk> {"
68 /// section of `mercurial/helptext/internals/dirstate-v2.txt`
70 /// section of `mercurial/helptext/internals/dirstate-v2.txt`
69 #[derive(BytesCast)]
71 #[derive(BytesCast)]
70 #[repr(C)]
72 #[repr(C)]
71 struct TreeMetadata {
73 pub struct TreeMetadata {
72 root_nodes: ChildNodes,
74 root_nodes: ChildNodes,
73 nodes_with_entry_count: Size,
75 nodes_with_entry_count: Size,
74 nodes_with_copy_source_count: Size,
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 impl<'on_disk> Docket<'on_disk> {
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 pub fn parents(&self) -> DirstateParents {
236 pub fn parents(&self) -> DirstateParents {
191 use crate::Node;
237 use crate::Node;
192 let p1 = Node::try_from(&self.header.parent_1[..USED_NODE_ID_BYTES])
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 pub(super) fn write(
601 pub(super) fn write(
556 dirstate_map: &DirstateMap,
602 dirstate_map: &DirstateMap,
557 can_append: bool,
603 can_append: bool,
558 ) -> Result<(Vec<u8>, Vec<u8>, bool), DirstateError> {
604 ) -> Result<(Vec<u8>, TreeMetadata, bool), DirstateError> {
559 let append = can_append && dirstate_map.write_should_append();
605 let append = can_append && dirstate_map.write_should_append();
560
606
561 // This ignores the space for paths, and for nodes without an entry.
607 // This ignores the space for paths, and for nodes without an entry.
@@ -581,7 +627,7 b' pub(super) fn write('
581 unused: [0; 4],
627 unused: [0; 4],
582 ignore_patterns_hash: dirstate_map.ignore_patterns_hash,
628 ignore_patterns_hash: dirstate_map.ignore_patterns_hash,
583 };
629 };
584 Ok((writer.out, meta.as_bytes().to_vec(), append))
630 Ok((writer.out, meta, append))
585 }
631 }
586
632
587 struct Writer<'dmap, 'on_disk> {
633 struct Writer<'dmap, 'on_disk> {
@@ -2,9 +2,10 b' use crate::changelog::Changelog;'
2 use crate::config::{Config, ConfigError, ConfigParseError};
2 use crate::config::{Config, ConfigError, ConfigParseError};
3 use crate::dirstate::DirstateParents;
3 use crate::dirstate::DirstateParents;
4 use crate::dirstate_tree::dirstate_map::DirstateMap;
4 use crate::dirstate_tree::dirstate_map::DirstateMap;
5 use crate::dirstate_tree::on_disk::Docket as DirstateDocket;
5 use crate::dirstate_tree::owning::OwningDirstateMap;
6 use crate::dirstate_tree::owning::OwningDirstateMap;
6 use crate::errors::HgError;
7 use crate::errors::HgResultExt;
7 use crate::errors::HgResultExt;
8 use crate::errors::{HgError, IoResultExt};
8 use crate::exit_codes;
9 use crate::exit_codes;
9 use crate::lock::{try_with_lock_no_wait, LockError};
10 use crate::lock::{try_with_lock_no_wait, LockError};
10 use crate::manifest::{Manifest, Manifestlog};
11 use crate::manifest::{Manifest, Manifestlog};
@@ -18,6 +19,9 b' use crate::{requirements, NodePrefix};'
18 use crate::{DirstateError, Revision};
19 use crate::{DirstateError, Revision};
19 use std::cell::{Ref, RefCell, RefMut};
20 use std::cell::{Ref, RefCell, RefMut};
20 use std::collections::HashSet;
21 use std::collections::HashSet;
22 use std::io::Seek;
23 use std::io::SeekFrom;
24 use std::io::Write as IoWrite;
21 use std::path::{Path, PathBuf};
25 use std::path::{Path, PathBuf};
22
26
23 /// A repository on disk
27 /// A repository on disk
@@ -416,6 +420,70 b' impl Repo {'
416 pub fn filelog(&self, path: &HgPath) -> Result<Filelog, HgError> {
420 pub fn filelog(&self, path: &HgPath) -> Result<Filelog, HgError> {
417 Filelog::open(self, path)
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 /// Lazily-initialized component of `Repo` with interior mutability
489 /// Lazily-initialized component of `Repo` with interior mutability
@@ -174,6 +174,12 b' impl Node {'
174 data: self.data,
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 /// The beginning of a binary revision SHA.
185 /// The beginning of a binary revision SHA.
@@ -222,7 +222,7 b' py_class!(pub class DirstateMap |py| {'
222 match result {
222 match result {
223 Ok((packed, tree_metadata, append)) => {
223 Ok((packed, tree_metadata, append)) => {
224 let packed = PyBytes::new(py, &packed);
224 let packed = PyBytes::new(py, &packed);
225 let tree_metadata = PyBytes::new(py, &tree_metadata);
225 let tree_metadata = PyBytes::new(py, tree_metadata.as_bytes());
226 let tuple = (packed, tree_metadata, append);
226 let tuple = (packed, tree_metadata, append);
227 Ok(tuple.to_py_object(py).into_object())
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