##// END OF EJS Templates
dirstate-v2: Rename Header to Root, move it to the end of the data file...
Simon Sapin -
r48476:096ee2e2 default
parent child Browse files
Show More
@@ -997,11 +997,13 b' def debugdirstateignorepatternshash(ui, '
997 or nothing for dirstate-v2
997 or nothing for dirstate-v2
998 """
998 """
999 if repo.dirstate._use_dirstate_v2:
999 if repo.dirstate._use_dirstate_v2:
1000 hash_offset = 16 # Four 32-bit integers before this field
1000 docket = repo.dirstate._map.docket
1001 hash_len = 20 # 160 bits for SHA-1
1001 hash_len = 20 # 160 bits for SHA-1
1002 data_filename = repo.dirstate._map.docket.data_filename()
1002 hash_offset = docket.data_size - hash_len # hash is at the end
1003 data_filename = docket.data_filename()
1003 with repo.vfs(data_filename) as f:
1004 with repo.vfs(data_filename) as f:
1004 hash_bytes = f.read(hash_offset + hash_len)[-hash_len:]
1005 f.seek(hash_offset)
1006 hash_bytes = f.read(hash_len)
1005 ui.write(binascii.hexlify(hash_bytes) + b'\n')
1007 ui.write(binascii.hexlify(hash_bytes) + b'\n')
1006
1008
1007
1009
@@ -2,8 +2,17 b''
2 //!
2 //!
3 //! # File format
3 //! # File format
4 //!
4 //!
5 //! The file starts with a fixed-sized header, whose layout is defined by the
5 //! In dirstate-v2 format, the `.hg/dirstate` file is a "docket that starts
6 //! `Header` struct. Its `root` field contains the slice (offset and length) to
6 //! with a fixed-sized header whose layout is defined by the `DocketHeader`
7 //! struct, followed by the data file identifier.
8 //!
9 //! A separate `.hg/dirstate.{uuid}.d` file contains most of the data. That
10 //! file may be longer than the size given in the docket, but not shorter. Only
11 //! the start of the data file up to the given size is considered. The
12 //! fixed-size "root" of the dirstate tree whose layout is defined by the
13 //! `Root` struct is found at the end of that slice of data.
14 //!
15 //! Its `root_nodes` field contains the slice (offset and length) to
7 //! the nodes representing the files and directories at the root of the
16 //! the nodes representing the files and directories at the root of the
8 //! repository. Each node is also fixed-size, defined by the `Node` struct.
17 //! repository. Each node is also fixed-size, defined by the `Node` struct.
9 //! Nodes in turn contain slices to variable-size paths, and to their own child
18 //! Nodes in turn contain slices to variable-size paths, and to their own child
@@ -56,8 +65,8 b" pub struct Docket<'on_disk> {"
56
65
57 #[derive(BytesCast)]
66 #[derive(BytesCast)]
58 #[repr(C)]
67 #[repr(C)]
59 struct Header {
68 struct Root {
60 root: ChildNodes,
69 root_nodes: ChildNodes,
61 nodes_with_entry_count: Size,
70 nodes_with_entry_count: Size,
62 nodes_with_copy_source_count: Size,
71 nodes_with_copy_source_count: Size,
63
72
@@ -119,7 +128,7 b' pub(super) struct Node {'
119 /// - All direct children of this directory (as returned by
128 /// - All direct children of this directory (as returned by
120 /// `std::fs::read_dir`) either have a corresponding dirstate node, or
129 /// `std::fs::read_dir`) either have a corresponding dirstate node, or
121 /// are ignored by ignore patterns whose hash is in
130 /// are ignored by ignore patterns whose hash is in
122 /// `Header::ignore_patterns_hash`.
131 /// `Root::ignore_patterns_hash`.
123 ///
132 ///
124 /// This means that if `std::fs::symlink_metadata` later reports the
133 /// This means that if `std::fs::symlink_metadata` later reports the
125 /// same modification time and ignored patterns haven’t changed, a run
134 /// same modification time and ignored patterns haven’t changed, a run
@@ -190,7 +199,7 b' type OptPathSlice = Slice;'
190 /// Make sure that size-affecting changes are made knowingly
199 /// Make sure that size-affecting changes are made knowingly
191 fn _static_assert_size_of() {
200 fn _static_assert_size_of() {
192 let _ = std::mem::transmute::<DocketHeader, [u8; 81]>;
201 let _ = std::mem::transmute::<DocketHeader, [u8; 81]>;
193 let _ = std::mem::transmute::<Header, [u8; 36]>;
202 let _ = std::mem::transmute::<Root, [u8; 36]>;
194 let _ = std::mem::transmute::<Node, [u8; 49]>;
203 let _ = std::mem::transmute::<Node, [u8; 49]>;
195 }
204 }
196
205
@@ -247,25 +256,36 b' pub fn read_docket('
247 }
256 }
248 }
257 }
249
258
259 fn read_root<'on_disk>(
260 on_disk: &'on_disk [u8],
261 ) -> Result<&'on_disk Root, DirstateV2ParseError> {
262 // Find the `Root` at the end of the given slice
263 let root_offset = on_disk
264 .len()
265 .checked_sub(std::mem::size_of::<Root>())
266 // A non-empty slice too short is an error
267 .ok_or(DirstateV2ParseError)?;
268 let (root, _) = Root::from_bytes(&on_disk[root_offset..])
269 .map_err(|_| DirstateV2ParseError)?;
270 Ok(root)
271 }
272
250 pub(super) fn read<'on_disk>(
273 pub(super) fn read<'on_disk>(
251 on_disk: &'on_disk [u8],
274 on_disk: &'on_disk [u8],
252 ) -> Result<DirstateMap<'on_disk>, DirstateV2ParseError> {
275 ) -> Result<DirstateMap<'on_disk>, DirstateV2ParseError> {
253 if on_disk.is_empty() {
276 if on_disk.is_empty() {
254 return Ok(DirstateMap::empty(on_disk));
277 return Ok(DirstateMap::empty(on_disk));
255 }
278 }
256 let (header, _) =
279 let root = read_root(on_disk)?;
257 Header::from_bytes(on_disk).map_err(|_| DirstateV2ParseError)?;
258 let dirstate_map = DirstateMap {
280 let dirstate_map = DirstateMap {
259 on_disk,
281 on_disk,
260 root: dirstate_map::ChildNodes::OnDisk(read_slice::<Node>(
282 root: dirstate_map::ChildNodes::OnDisk(read_slice::<Node>(
261 on_disk,
283 on_disk,
262 header.root,
284 root.root_nodes,
263 )?),
285 )?),
264 nodes_with_entry_count: header.nodes_with_entry_count.get(),
286 nodes_with_entry_count: root.nodes_with_entry_count.get(),
265 nodes_with_copy_source_count: header
287 nodes_with_copy_source_count: root.nodes_with_copy_source_count.get(),
266 .nodes_with_copy_source_count
288 ignore_patterns_hash: root.ignore_patterns_hash,
267 .get(),
268 ignore_patterns_hash: header.ignore_patterns_hash,
269 };
289 };
270 Ok(dirstate_map)
290 Ok(dirstate_map)
271 }
291 }
@@ -491,8 +511,7 b" pub(crate) fn for_each_tracked_path<'on_"
491 on_disk: &'on_disk [u8],
511 on_disk: &'on_disk [u8],
492 mut f: impl FnMut(&'on_disk HgPath),
512 mut f: impl FnMut(&'on_disk HgPath),
493 ) -> Result<(), DirstateV2ParseError> {
513 ) -> Result<(), DirstateV2ParseError> {
494 let (header, _) =
514 let root = read_root(on_disk)?;
495 Header::from_bytes(on_disk).map_err(|_| DirstateV2ParseError)?;
496 fn recur<'on_disk>(
515 fn recur<'on_disk>(
497 on_disk: &'on_disk [u8],
516 on_disk: &'on_disk [u8],
498 nodes: Slice,
517 nodes: Slice,
@@ -508,37 +527,33 b" pub(crate) fn for_each_tracked_path<'on_"
508 }
527 }
509 Ok(())
528 Ok(())
510 }
529 }
511 recur(on_disk, header.root, &mut f)
530 recur(on_disk, root.root_nodes, &mut f)
512 }
531 }
513
532
514 pub(super) fn write(
533 pub(super) fn write(
515 dirstate_map: &mut DirstateMap,
534 dirstate_map: &mut DirstateMap,
516 ) -> Result<Vec<u8>, DirstateError> {
535 ) -> Result<Vec<u8>, DirstateError> {
517 let header_len = std::mem::size_of::<Header>();
536 let root_len = std::mem::size_of::<Root>();
518
537
519 // This ignores the space for paths, and for nodes without an entry.
538 // This ignores the space for paths, and for nodes without an entry.
520 // TODO: better estimate? Skip the `Vec` and write to a file directly?
539 // TODO: better estimate? Skip the `Vec` and write to a file directly?
521 let size_guess = header_len
540 let size_guess = root_len
522 + std::mem::size_of::<Node>()
541 + std::mem::size_of::<Node>()
523 * dirstate_map.nodes_with_entry_count as usize;
542 * dirstate_map.nodes_with_entry_count as usize;
524 let mut out = Vec::with_capacity(size_guess);
543 let mut out = Vec::with_capacity(size_guess);
525
544
526 // Keep space for the header. We’ll fill it out at the end when we know the
545 let root_nodes =
527 // actual offset for the root nodes.
528 out.resize(header_len, 0_u8);
529
530 let root =
531 write_nodes(dirstate_map, dirstate_map.root.as_ref(), &mut out)?;
546 write_nodes(dirstate_map, dirstate_map.root.as_ref(), &mut out)?;
532
547
533 let header = Header {
548 let root = Root {
534 root,
549 root_nodes,
535 nodes_with_entry_count: dirstate_map.nodes_with_entry_count.into(),
550 nodes_with_entry_count: dirstate_map.nodes_with_entry_count.into(),
536 nodes_with_copy_source_count: dirstate_map
551 nodes_with_copy_source_count: dirstate_map
537 .nodes_with_copy_source_count
552 .nodes_with_copy_source_count
538 .into(),
553 .into(),
539 ignore_patterns_hash: dirstate_map.ignore_patterns_hash,
554 ignore_patterns_hash: dirstate_map.ignore_patterns_hash,
540 };
555 };
541 out[..header_len].copy_from_slice(header.as_bytes());
556 out.extend(root.as_bytes());
542 Ok(out)
557 Ok(out)
543 }
558 }
544
559
General Comments 0
You need to be logged in to leave comments. Login now