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 |
|
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 |
/// ` |
|
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::< |
|
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 |
|
|
284 | root.root_nodes, | |
263 | )?), |
|
285 | )?), | |
264 |
nodes_with_entry_count: |
|
286 | nodes_with_entry_count: root.nodes_with_entry_count.get(), | |
265 |
nodes_with_copy_source_count: |
|
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, |
|
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 |
|
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 = |
|
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 |
|
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