##// END OF EJS Templates
rust-dirstate: use a struct as arguments for the high-level `reset_state`...
Raphaël Gomès -
r52937:0cd16b1d default
parent child Browse files
Show More
@@ -1,1935 +1,2051
1 use bytes_cast::BytesCast;
1 use bytes_cast::BytesCast;
2 use std::borrow::Cow;
2 use std::borrow::Cow;
3 use std::path::PathBuf;
3 use std::path::PathBuf;
4
4
5 use super::on_disk;
5 use super::on_disk;
6 use super::on_disk::DirstateV2ParseError;
6 use super::on_disk::DirstateV2ParseError;
7 use super::owning::OwningDirstateMap;
7 use super::owning::OwningDirstateMap;
8 use super::path_with_basename::WithBasename;
8 use super::path_with_basename::WithBasename;
9 use crate::dirstate::parsers::pack_entry;
9 use crate::dirstate::parsers::pack_entry;
10 use crate::dirstate::parsers::packed_entry_size;
10 use crate::dirstate::parsers::packed_entry_size;
11 use crate::dirstate::parsers::parse_dirstate_entries;
11 use crate::dirstate::parsers::parse_dirstate_entries;
12 use crate::dirstate::CopyMapIter;
12 use crate::dirstate::CopyMapIter;
13 use crate::dirstate::DirstateV2Data;
13 use crate::dirstate::DirstateV2Data;
14 use crate::dirstate::ParentFileData;
14 use crate::dirstate::ParentFileData;
15 use crate::dirstate::StateMapIter;
15 use crate::dirstate::StateMapIter;
16 use crate::dirstate::TruncatedTimestamp;
16 use crate::dirstate::TruncatedTimestamp;
17 use crate::matchers::Matcher;
17 use crate::matchers::Matcher;
18 use crate::utils::filter_map_results;
18 use crate::utils::filter_map_results;
19 use crate::utils::hg_path::{HgPath, HgPathBuf};
19 use crate::utils::hg_path::{HgPath, HgPathBuf};
20 use crate::DirstateEntry;
20 use crate::DirstateEntry;
21 use crate::DirstateError;
21 use crate::DirstateError;
22 use crate::DirstateMapError;
22 use crate::DirstateMapError;
23 use crate::DirstateParents;
23 use crate::DirstateParents;
24 use crate::DirstateStatus;
24 use crate::DirstateStatus;
25 use crate::FastHashbrownMap as FastHashMap;
25 use crate::FastHashbrownMap as FastHashMap;
26 use crate::PatternFileWarning;
26 use crate::PatternFileWarning;
27 use crate::StatusError;
27 use crate::StatusError;
28 use crate::StatusOptions;
28 use crate::StatusOptions;
29
29
30 /// Append to an existing data file if the amount of unreachable data (not used
30 /// Append to an existing data file if the amount of unreachable data (not used
31 /// anymore) is less than this fraction of the total amount of existing data.
31 /// anymore) is less than this fraction of the total amount of existing data.
32 const ACCEPTABLE_UNREACHABLE_BYTES_RATIO: f32 = 0.5;
32 const ACCEPTABLE_UNREACHABLE_BYTES_RATIO: f32 = 0.5;
33
33
34 #[derive(Debug, PartialEq, Eq)]
34 #[derive(Debug, PartialEq, Eq)]
35 /// Version of the on-disk format
35 /// Version of the on-disk format
36 pub enum DirstateVersion {
36 pub enum DirstateVersion {
37 V1,
37 V1,
38 V2,
38 V2,
39 }
39 }
40
40
41 #[derive(Debug, PartialEq, Eq)]
41 #[derive(Debug, PartialEq, Eq)]
42 pub enum DirstateMapWriteMode {
42 pub enum DirstateMapWriteMode {
43 Auto,
43 Auto,
44 ForceNewDataFile,
44 ForceNewDataFile,
45 ForceAppend,
45 ForceAppend,
46 }
46 }
47
47
48 #[derive(Debug)]
48 #[derive(Debug)]
49 pub struct DirstateMap<'on_disk> {
49 pub struct DirstateMap<'on_disk> {
50 /// Contents of the `.hg/dirstate` file
50 /// Contents of the `.hg/dirstate` file
51 pub(super) on_disk: &'on_disk [u8],
51 pub(super) on_disk: &'on_disk [u8],
52
52
53 pub(super) root: ChildNodes<'on_disk>,
53 pub(super) root: ChildNodes<'on_disk>,
54
54
55 /// Number of nodes anywhere in the tree that have `.entry.is_some()`.
55 /// Number of nodes anywhere in the tree that have `.entry.is_some()`.
56 pub(super) nodes_with_entry_count: u32,
56 pub(super) nodes_with_entry_count: u32,
57
57
58 /// Number of nodes anywhere in the tree that have
58 /// Number of nodes anywhere in the tree that have
59 /// `.copy_source.is_some()`.
59 /// `.copy_source.is_some()`.
60 pub(super) nodes_with_copy_source_count: u32,
60 pub(super) nodes_with_copy_source_count: u32,
61
61
62 /// See on_disk::Header
62 /// See on_disk::Header
63 pub(super) ignore_patterns_hash: on_disk::IgnorePatternsHash,
63 pub(super) ignore_patterns_hash: on_disk::IgnorePatternsHash,
64
64
65 /// How many bytes of `on_disk` are not used anymore
65 /// How many bytes of `on_disk` are not used anymore
66 pub(super) unreachable_bytes: u32,
66 pub(super) unreachable_bytes: u32,
67
67
68 /// Size of the data used to first load this `DirstateMap`. Used in case
68 /// Size of the data used to first load this `DirstateMap`. Used in case
69 /// we need to write some new metadata, but no new data on disk,
69 /// we need to write some new metadata, but no new data on disk,
70 /// as well as to detect writes that have happened in another process
70 /// as well as to detect writes that have happened in another process
71 /// since first read.
71 /// since first read.
72 pub(super) old_data_size: usize,
72 pub(super) old_data_size: usize,
73
73
74 /// UUID used when first loading this `DirstateMap`. Used to check if
74 /// UUID used when first loading this `DirstateMap`. Used to check if
75 /// the UUID has been changed by another process since first read.
75 /// the UUID has been changed by another process since first read.
76 /// Can be `None` if using dirstate v1 or if it's a brand new dirstate.
76 /// Can be `None` if using dirstate v1 or if it's a brand new dirstate.
77 pub(super) old_uuid: Option<Vec<u8>>,
77 pub(super) old_uuid: Option<Vec<u8>>,
78
78
79 /// Identity of the dirstate file (for dirstate-v1) or the docket file
79 /// Identity of the dirstate file (for dirstate-v1) or the docket file
80 /// (v2). Used to detect if the file has changed from another process.
80 /// (v2). Used to detect if the file has changed from another process.
81 /// Since it's always written atomically, we can compare the inode to
81 /// Since it's always written atomically, we can compare the inode to
82 /// check the file identity.
82 /// check the file identity.
83 ///
83 ///
84 /// TODO On non-Unix systems, something like hashing is a possibility?
84 /// TODO On non-Unix systems, something like hashing is a possibility?
85 pub(super) identity: Option<u64>,
85 pub(super) identity: Option<u64>,
86
86
87 pub(super) dirstate_version: DirstateVersion,
87 pub(super) dirstate_version: DirstateVersion,
88
88
89 /// Controlled by config option `devel.dirstate.v2.data_update_mode`
89 /// Controlled by config option `devel.dirstate.v2.data_update_mode`
90 pub(super) write_mode: DirstateMapWriteMode,
90 pub(super) write_mode: DirstateMapWriteMode,
91 }
91 }
92
92
93 /// Using a plain `HgPathBuf` of the full path from the repository root as a
93 /// Using a plain `HgPathBuf` of the full path from the repository root as a
94 /// map key would also work: all paths in a given map have the same parent
94 /// map key would also work: all paths in a given map have the same parent
95 /// path, so comparing full paths gives the same result as comparing base
95 /// path, so comparing full paths gives the same result as comparing base
96 /// names. However `HashMap` would waste time always re-hashing the same
96 /// names. However `HashMap` would waste time always re-hashing the same
97 /// string prefix.
97 /// string prefix.
98 pub(super) type NodeKey<'on_disk> = WithBasename<Cow<'on_disk, HgPath>>;
98 pub(super) type NodeKey<'on_disk> = WithBasename<Cow<'on_disk, HgPath>>;
99
99
100 /// Similar to `&'tree Cow<'on_disk, HgPath>`, but can also be returned
100 /// Similar to `&'tree Cow<'on_disk, HgPath>`, but can also be returned
101 /// for on-disk nodes that don’t actually have a `Cow` to borrow.
101 /// for on-disk nodes that don’t actually have a `Cow` to borrow.
102 #[derive(Debug)]
102 #[derive(Debug)]
103 pub(super) enum BorrowedPath<'tree, 'on_disk> {
103 pub(super) enum BorrowedPath<'tree, 'on_disk> {
104 InMemory(&'tree HgPathBuf),
104 InMemory(&'tree HgPathBuf),
105 OnDisk(&'on_disk HgPath),
105 OnDisk(&'on_disk HgPath),
106 }
106 }
107
107
108 #[derive(Debug)]
108 #[derive(Debug)]
109 pub(super) enum ChildNodes<'on_disk> {
109 pub(super) enum ChildNodes<'on_disk> {
110 InMemory(FastHashMap<NodeKey<'on_disk>, Node<'on_disk>>),
110 InMemory(FastHashMap<NodeKey<'on_disk>, Node<'on_disk>>),
111 OnDisk(&'on_disk [on_disk::Node]),
111 OnDisk(&'on_disk [on_disk::Node]),
112 }
112 }
113
113
114 #[derive(Debug)]
114 #[derive(Debug)]
115 pub(super) enum ChildNodesRef<'tree, 'on_disk> {
115 pub(super) enum ChildNodesRef<'tree, 'on_disk> {
116 InMemory(&'tree FastHashMap<NodeKey<'on_disk>, Node<'on_disk>>),
116 InMemory(&'tree FastHashMap<NodeKey<'on_disk>, Node<'on_disk>>),
117 OnDisk(&'on_disk [on_disk::Node]),
117 OnDisk(&'on_disk [on_disk::Node]),
118 }
118 }
119
119
120 #[derive(Debug)]
120 #[derive(Debug)]
121 pub(super) enum NodeRef<'tree, 'on_disk> {
121 pub(super) enum NodeRef<'tree, 'on_disk> {
122 InMemory(&'tree NodeKey<'on_disk>, &'tree Node<'on_disk>),
122 InMemory(&'tree NodeKey<'on_disk>, &'tree Node<'on_disk>),
123 OnDisk(&'on_disk on_disk::Node),
123 OnDisk(&'on_disk on_disk::Node),
124 }
124 }
125
125
126 impl<'tree, 'on_disk> BorrowedPath<'tree, 'on_disk> {
126 impl<'tree, 'on_disk> BorrowedPath<'tree, 'on_disk> {
127 pub fn detach_from_tree(&self) -> Cow<'on_disk, HgPath> {
127 pub fn detach_from_tree(&self) -> Cow<'on_disk, HgPath> {
128 match *self {
128 match *self {
129 BorrowedPath::InMemory(in_memory) => Cow::Owned(in_memory.clone()),
129 BorrowedPath::InMemory(in_memory) => Cow::Owned(in_memory.clone()),
130 BorrowedPath::OnDisk(on_disk) => Cow::Borrowed(on_disk),
130 BorrowedPath::OnDisk(on_disk) => Cow::Borrowed(on_disk),
131 }
131 }
132 }
132 }
133 }
133 }
134
134
135 impl<'tree, 'on_disk> std::ops::Deref for BorrowedPath<'tree, 'on_disk> {
135 impl<'tree, 'on_disk> std::ops::Deref for BorrowedPath<'tree, 'on_disk> {
136 type Target = HgPath;
136 type Target = HgPath;
137
137
138 fn deref(&self) -> &HgPath {
138 fn deref(&self) -> &HgPath {
139 match *self {
139 match *self {
140 BorrowedPath::InMemory(in_memory) => in_memory,
140 BorrowedPath::InMemory(in_memory) => in_memory,
141 BorrowedPath::OnDisk(on_disk) => on_disk,
141 BorrowedPath::OnDisk(on_disk) => on_disk,
142 }
142 }
143 }
143 }
144 }
144 }
145
145
146 impl Default for ChildNodes<'_> {
146 impl Default for ChildNodes<'_> {
147 fn default() -> Self {
147 fn default() -> Self {
148 ChildNodes::InMemory(Default::default())
148 ChildNodes::InMemory(Default::default())
149 }
149 }
150 }
150 }
151
151
152 impl<'on_disk> ChildNodes<'on_disk> {
152 impl<'on_disk> ChildNodes<'on_disk> {
153 pub(super) fn as_ref<'tree>(
153 pub(super) fn as_ref<'tree>(
154 &'tree self,
154 &'tree self,
155 ) -> ChildNodesRef<'tree, 'on_disk> {
155 ) -> ChildNodesRef<'tree, 'on_disk> {
156 match self {
156 match self {
157 ChildNodes::InMemory(nodes) => ChildNodesRef::InMemory(nodes),
157 ChildNodes::InMemory(nodes) => ChildNodesRef::InMemory(nodes),
158 ChildNodes::OnDisk(nodes) => ChildNodesRef::OnDisk(nodes),
158 ChildNodes::OnDisk(nodes) => ChildNodesRef::OnDisk(nodes),
159 }
159 }
160 }
160 }
161
161
162 pub(super) fn is_empty(&self) -> bool {
162 pub(super) fn is_empty(&self) -> bool {
163 match self {
163 match self {
164 ChildNodes::InMemory(nodes) => nodes.is_empty(),
164 ChildNodes::InMemory(nodes) => nodes.is_empty(),
165 ChildNodes::OnDisk(nodes) => nodes.is_empty(),
165 ChildNodes::OnDisk(nodes) => nodes.is_empty(),
166 }
166 }
167 }
167 }
168
168
169 fn make_mut(
169 fn make_mut(
170 &mut self,
170 &mut self,
171 on_disk: &'on_disk [u8],
171 on_disk: &'on_disk [u8],
172 unreachable_bytes: &mut u32,
172 unreachable_bytes: &mut u32,
173 ) -> Result<
173 ) -> Result<
174 &mut FastHashMap<NodeKey<'on_disk>, Node<'on_disk>>,
174 &mut FastHashMap<NodeKey<'on_disk>, Node<'on_disk>>,
175 DirstateV2ParseError,
175 DirstateV2ParseError,
176 > {
176 > {
177 match self {
177 match self {
178 ChildNodes::InMemory(nodes) => Ok(nodes),
178 ChildNodes::InMemory(nodes) => Ok(nodes),
179 ChildNodes::OnDisk(nodes) => {
179 ChildNodes::OnDisk(nodes) => {
180 *unreachable_bytes +=
180 *unreachable_bytes +=
181 std::mem::size_of_val::<[on_disk::Node]>(*nodes) as u32;
181 std::mem::size_of_val::<[on_disk::Node]>(*nodes) as u32;
182 let nodes = nodes
182 let nodes = nodes
183 .iter()
183 .iter()
184 .map(|node| {
184 .map(|node| {
185 Ok((
185 Ok((
186 node.path(on_disk)?,
186 node.path(on_disk)?,
187 node.to_in_memory_node(on_disk)?,
187 node.to_in_memory_node(on_disk)?,
188 ))
188 ))
189 })
189 })
190 .collect::<Result<_, _>>()?;
190 .collect::<Result<_, _>>()?;
191 *self = ChildNodes::InMemory(nodes);
191 *self = ChildNodes::InMemory(nodes);
192 match self {
192 match self {
193 ChildNodes::InMemory(nodes) => Ok(nodes),
193 ChildNodes::InMemory(nodes) => Ok(nodes),
194 ChildNodes::OnDisk(_) => unreachable!(),
194 ChildNodes::OnDisk(_) => unreachable!(),
195 }
195 }
196 }
196 }
197 }
197 }
198 }
198 }
199 }
199 }
200
200
201 impl<'tree, 'on_disk> ChildNodesRef<'tree, 'on_disk> {
201 impl<'tree, 'on_disk> ChildNodesRef<'tree, 'on_disk> {
202 pub(super) fn get(
202 pub(super) fn get(
203 &self,
203 &self,
204 base_name: &HgPath,
204 base_name: &HgPath,
205 on_disk: &'on_disk [u8],
205 on_disk: &'on_disk [u8],
206 ) -> Result<Option<NodeRef<'tree, 'on_disk>>, DirstateV2ParseError> {
206 ) -> Result<Option<NodeRef<'tree, 'on_disk>>, DirstateV2ParseError> {
207 match self {
207 match self {
208 ChildNodesRef::InMemory(nodes) => Ok(nodes
208 ChildNodesRef::InMemory(nodes) => Ok(nodes
209 .get_key_value(base_name)
209 .get_key_value(base_name)
210 .map(|(k, v)| NodeRef::InMemory(k, v))),
210 .map(|(k, v)| NodeRef::InMemory(k, v))),
211 ChildNodesRef::OnDisk(nodes) => {
211 ChildNodesRef::OnDisk(nodes) => {
212 let mut parse_result = Ok(());
212 let mut parse_result = Ok(());
213 let search_result = nodes.binary_search_by(|node| {
213 let search_result = nodes.binary_search_by(|node| {
214 match node.base_name(on_disk) {
214 match node.base_name(on_disk) {
215 Ok(node_base_name) => node_base_name.cmp(base_name),
215 Ok(node_base_name) => node_base_name.cmp(base_name),
216 Err(e) => {
216 Err(e) => {
217 parse_result = Err(e);
217 parse_result = Err(e);
218 // Dummy comparison result, `search_result` won’t
218 // Dummy comparison result, `search_result` won’t
219 // be used since `parse_result` is an error
219 // be used since `parse_result` is an error
220 std::cmp::Ordering::Equal
220 std::cmp::Ordering::Equal
221 }
221 }
222 }
222 }
223 });
223 });
224 parse_result.map(|()| {
224 parse_result.map(|()| {
225 search_result.ok().map(|i| NodeRef::OnDisk(&nodes[i]))
225 search_result.ok().map(|i| NodeRef::OnDisk(&nodes[i]))
226 })
226 })
227 }
227 }
228 }
228 }
229 }
229 }
230
230
231 /// Iterate in undefined order
231 /// Iterate in undefined order
232 pub(super) fn iter(
232 pub(super) fn iter(
233 &self,
233 &self,
234 ) -> impl Iterator<Item = NodeRef<'tree, 'on_disk>> {
234 ) -> impl Iterator<Item = NodeRef<'tree, 'on_disk>> {
235 match self {
235 match self {
236 ChildNodesRef::InMemory(nodes) => itertools::Either::Left(
236 ChildNodesRef::InMemory(nodes) => itertools::Either::Left(
237 nodes.iter().map(|(k, v)| NodeRef::InMemory(k, v)),
237 nodes.iter().map(|(k, v)| NodeRef::InMemory(k, v)),
238 ),
238 ),
239 ChildNodesRef::OnDisk(nodes) => {
239 ChildNodesRef::OnDisk(nodes) => {
240 itertools::Either::Right(nodes.iter().map(NodeRef::OnDisk))
240 itertools::Either::Right(nodes.iter().map(NodeRef::OnDisk))
241 }
241 }
242 }
242 }
243 }
243 }
244
244
245 /// Iterate in parallel in undefined order
245 /// Iterate in parallel in undefined order
246 pub(super) fn par_iter(
246 pub(super) fn par_iter(
247 &self,
247 &self,
248 ) -> impl rayon::iter::ParallelIterator<Item = NodeRef<'tree, 'on_disk>>
248 ) -> impl rayon::iter::ParallelIterator<Item = NodeRef<'tree, 'on_disk>>
249 {
249 {
250 use rayon::prelude::*;
250 use rayon::prelude::*;
251 match self {
251 match self {
252 ChildNodesRef::InMemory(nodes) => rayon::iter::Either::Left(
252 ChildNodesRef::InMemory(nodes) => rayon::iter::Either::Left(
253 nodes.par_iter().map(|(k, v)| NodeRef::InMemory(k, v)),
253 nodes.par_iter().map(|(k, v)| NodeRef::InMemory(k, v)),
254 ),
254 ),
255 ChildNodesRef::OnDisk(nodes) => rayon::iter::Either::Right(
255 ChildNodesRef::OnDisk(nodes) => rayon::iter::Either::Right(
256 nodes.par_iter().map(NodeRef::OnDisk),
256 nodes.par_iter().map(NodeRef::OnDisk),
257 ),
257 ),
258 }
258 }
259 }
259 }
260
260
261 pub(super) fn sorted(&self) -> Vec<NodeRef<'tree, 'on_disk>> {
261 pub(super) fn sorted(&self) -> Vec<NodeRef<'tree, 'on_disk>> {
262 match self {
262 match self {
263 ChildNodesRef::InMemory(nodes) => {
263 ChildNodesRef::InMemory(nodes) => {
264 let mut vec: Vec<_> = nodes
264 let mut vec: Vec<_> = nodes
265 .iter()
265 .iter()
266 .map(|(k, v)| NodeRef::InMemory(k, v))
266 .map(|(k, v)| NodeRef::InMemory(k, v))
267 .collect();
267 .collect();
268 fn sort_key<'a>(node: &'a NodeRef) -> &'a HgPath {
268 fn sort_key<'a>(node: &'a NodeRef) -> &'a HgPath {
269 match node {
269 match node {
270 NodeRef::InMemory(path, _node) => path.base_name(),
270 NodeRef::InMemory(path, _node) => path.base_name(),
271 NodeRef::OnDisk(_) => unreachable!(),
271 NodeRef::OnDisk(_) => unreachable!(),
272 }
272 }
273 }
273 }
274 // `sort_unstable_by_key` doesn’t allow keys borrowing from the
274 // `sort_unstable_by_key` doesn’t allow keys borrowing from the
275 // value: https://github.com/rust-lang/rust/issues/34162
275 // value: https://github.com/rust-lang/rust/issues/34162
276 vec.sort_unstable_by(|a, b| sort_key(a).cmp(sort_key(b)));
276 vec.sort_unstable_by(|a, b| sort_key(a).cmp(sort_key(b)));
277 vec
277 vec
278 }
278 }
279 ChildNodesRef::OnDisk(nodes) => {
279 ChildNodesRef::OnDisk(nodes) => {
280 // Nodes on disk are already sorted
280 // Nodes on disk are already sorted
281 nodes.iter().map(NodeRef::OnDisk).collect()
281 nodes.iter().map(NodeRef::OnDisk).collect()
282 }
282 }
283 }
283 }
284 }
284 }
285 }
285 }
286
286
287 impl<'tree, 'on_disk> NodeRef<'tree, 'on_disk> {
287 impl<'tree, 'on_disk> NodeRef<'tree, 'on_disk> {
288 pub(super) fn full_path(
288 pub(super) fn full_path(
289 &self,
289 &self,
290 on_disk: &'on_disk [u8],
290 on_disk: &'on_disk [u8],
291 ) -> Result<&'tree HgPath, DirstateV2ParseError> {
291 ) -> Result<&'tree HgPath, DirstateV2ParseError> {
292 match self {
292 match self {
293 NodeRef::InMemory(path, _node) => Ok(path.full_path()),
293 NodeRef::InMemory(path, _node) => Ok(path.full_path()),
294 NodeRef::OnDisk(node) => node.full_path(on_disk),
294 NodeRef::OnDisk(node) => node.full_path(on_disk),
295 }
295 }
296 }
296 }
297
297
298 /// Returns a `BorrowedPath`, which can be turned into a `Cow<'on_disk,
298 /// Returns a `BorrowedPath`, which can be turned into a `Cow<'on_disk,
299 /// HgPath>` detached from `'tree`
299 /// HgPath>` detached from `'tree`
300 pub(super) fn full_path_borrowed(
300 pub(super) fn full_path_borrowed(
301 &self,
301 &self,
302 on_disk: &'on_disk [u8],
302 on_disk: &'on_disk [u8],
303 ) -> Result<BorrowedPath<'tree, 'on_disk>, DirstateV2ParseError> {
303 ) -> Result<BorrowedPath<'tree, 'on_disk>, DirstateV2ParseError> {
304 match self {
304 match self {
305 NodeRef::InMemory(path, _node) => match path.full_path() {
305 NodeRef::InMemory(path, _node) => match path.full_path() {
306 Cow::Borrowed(on_disk) => Ok(BorrowedPath::OnDisk(on_disk)),
306 Cow::Borrowed(on_disk) => Ok(BorrowedPath::OnDisk(on_disk)),
307 Cow::Owned(in_memory) => Ok(BorrowedPath::InMemory(in_memory)),
307 Cow::Owned(in_memory) => Ok(BorrowedPath::InMemory(in_memory)),
308 },
308 },
309 NodeRef::OnDisk(node) => {
309 NodeRef::OnDisk(node) => {
310 Ok(BorrowedPath::OnDisk(node.full_path(on_disk)?))
310 Ok(BorrowedPath::OnDisk(node.full_path(on_disk)?))
311 }
311 }
312 }
312 }
313 }
313 }
314
314
315 pub(super) fn base_name(
315 pub(super) fn base_name(
316 &self,
316 &self,
317 on_disk: &'on_disk [u8],
317 on_disk: &'on_disk [u8],
318 ) -> Result<&'tree HgPath, DirstateV2ParseError> {
318 ) -> Result<&'tree HgPath, DirstateV2ParseError> {
319 match self {
319 match self {
320 NodeRef::InMemory(path, _node) => Ok(path.base_name()),
320 NodeRef::InMemory(path, _node) => Ok(path.base_name()),
321 NodeRef::OnDisk(node) => node.base_name(on_disk),
321 NodeRef::OnDisk(node) => node.base_name(on_disk),
322 }
322 }
323 }
323 }
324
324
325 pub(super) fn children(
325 pub(super) fn children(
326 &self,
326 &self,
327 on_disk: &'on_disk [u8],
327 on_disk: &'on_disk [u8],
328 ) -> Result<ChildNodesRef<'tree, 'on_disk>, DirstateV2ParseError> {
328 ) -> Result<ChildNodesRef<'tree, 'on_disk>, DirstateV2ParseError> {
329 match self {
329 match self {
330 NodeRef::InMemory(_path, node) => Ok(node.children.as_ref()),
330 NodeRef::InMemory(_path, node) => Ok(node.children.as_ref()),
331 NodeRef::OnDisk(node) => {
331 NodeRef::OnDisk(node) => {
332 Ok(ChildNodesRef::OnDisk(node.children(on_disk)?))
332 Ok(ChildNodesRef::OnDisk(node.children(on_disk)?))
333 }
333 }
334 }
334 }
335 }
335 }
336
336
337 pub(super) fn has_copy_source(&self) -> bool {
337 pub(super) fn has_copy_source(&self) -> bool {
338 match self {
338 match self {
339 NodeRef::InMemory(_path, node) => node.copy_source.is_some(),
339 NodeRef::InMemory(_path, node) => node.copy_source.is_some(),
340 NodeRef::OnDisk(node) => node.has_copy_source(),
340 NodeRef::OnDisk(node) => node.has_copy_source(),
341 }
341 }
342 }
342 }
343
343
344 pub(super) fn copy_source(
344 pub(super) fn copy_source(
345 &self,
345 &self,
346 on_disk: &'on_disk [u8],
346 on_disk: &'on_disk [u8],
347 ) -> Result<Option<&'tree HgPath>, DirstateV2ParseError> {
347 ) -> Result<Option<&'tree HgPath>, DirstateV2ParseError> {
348 match self {
348 match self {
349 NodeRef::InMemory(_path, node) => Ok(node.copy_source.as_deref()),
349 NodeRef::InMemory(_path, node) => Ok(node.copy_source.as_deref()),
350 NodeRef::OnDisk(node) => node.copy_source(on_disk),
350 NodeRef::OnDisk(node) => node.copy_source(on_disk),
351 }
351 }
352 }
352 }
353 /// Returns a `BorrowedPath`, which can be turned into a `Cow<'on_disk,
353 /// Returns a `BorrowedPath`, which can be turned into a `Cow<'on_disk,
354 /// HgPath>` detached from `'tree`
354 /// HgPath>` detached from `'tree`
355 pub(super) fn copy_source_borrowed(
355 pub(super) fn copy_source_borrowed(
356 &self,
356 &self,
357 on_disk: &'on_disk [u8],
357 on_disk: &'on_disk [u8],
358 ) -> Result<Option<BorrowedPath<'tree, 'on_disk>>, DirstateV2ParseError>
358 ) -> Result<Option<BorrowedPath<'tree, 'on_disk>>, DirstateV2ParseError>
359 {
359 {
360 Ok(match self {
360 Ok(match self {
361 NodeRef::InMemory(_path, node) => {
361 NodeRef::InMemory(_path, node) => {
362 node.copy_source.as_ref().map(|source| match source {
362 node.copy_source.as_ref().map(|source| match source {
363 Cow::Borrowed(on_disk) => BorrowedPath::OnDisk(on_disk),
363 Cow::Borrowed(on_disk) => BorrowedPath::OnDisk(on_disk),
364 Cow::Owned(in_memory) => BorrowedPath::InMemory(in_memory),
364 Cow::Owned(in_memory) => BorrowedPath::InMemory(in_memory),
365 })
365 })
366 }
366 }
367 NodeRef::OnDisk(node) => {
367 NodeRef::OnDisk(node) => {
368 node.copy_source(on_disk)?.map(BorrowedPath::OnDisk)
368 node.copy_source(on_disk)?.map(BorrowedPath::OnDisk)
369 }
369 }
370 })
370 })
371 }
371 }
372
372
373 pub(super) fn entry(
373 pub(super) fn entry(
374 &self,
374 &self,
375 ) -> Result<Option<DirstateEntry>, DirstateV2ParseError> {
375 ) -> Result<Option<DirstateEntry>, DirstateV2ParseError> {
376 match self {
376 match self {
377 NodeRef::InMemory(_path, node) => {
377 NodeRef::InMemory(_path, node) => {
378 Ok(node.data.as_entry().copied())
378 Ok(node.data.as_entry().copied())
379 }
379 }
380 NodeRef::OnDisk(node) => node.entry(),
380 NodeRef::OnDisk(node) => node.entry(),
381 }
381 }
382 }
382 }
383
383
384 pub(super) fn cached_directory_mtime(
384 pub(super) fn cached_directory_mtime(
385 &self,
385 &self,
386 ) -> Result<Option<TruncatedTimestamp>, DirstateV2ParseError> {
386 ) -> Result<Option<TruncatedTimestamp>, DirstateV2ParseError> {
387 match self {
387 match self {
388 NodeRef::InMemory(_path, node) => Ok(match node.data {
388 NodeRef::InMemory(_path, node) => Ok(match node.data {
389 NodeData::CachedDirectory { mtime } => Some(mtime),
389 NodeData::CachedDirectory { mtime } => Some(mtime),
390 _ => None,
390 _ => None,
391 }),
391 }),
392 NodeRef::OnDisk(node) => node.cached_directory_mtime(),
392 NodeRef::OnDisk(node) => node.cached_directory_mtime(),
393 }
393 }
394 }
394 }
395
395
396 pub(super) fn descendants_with_entry_count(&self) -> u32 {
396 pub(super) fn descendants_with_entry_count(&self) -> u32 {
397 match self {
397 match self {
398 NodeRef::InMemory(_path, node) => {
398 NodeRef::InMemory(_path, node) => {
399 node.descendants_with_entry_count
399 node.descendants_with_entry_count
400 }
400 }
401 NodeRef::OnDisk(node) => node.descendants_with_entry_count.get(),
401 NodeRef::OnDisk(node) => node.descendants_with_entry_count.get(),
402 }
402 }
403 }
403 }
404
404
405 pub(super) fn tracked_descendants_count(&self) -> u32 {
405 pub(super) fn tracked_descendants_count(&self) -> u32 {
406 match self {
406 match self {
407 NodeRef::InMemory(_path, node) => node.tracked_descendants_count,
407 NodeRef::InMemory(_path, node) => node.tracked_descendants_count,
408 NodeRef::OnDisk(node) => node.tracked_descendants_count.get(),
408 NodeRef::OnDisk(node) => node.tracked_descendants_count.get(),
409 }
409 }
410 }
410 }
411 }
411 }
412
412
413 /// Represents a file or a directory
413 /// Represents a file or a directory
414 #[derive(Default, Debug)]
414 #[derive(Default, Debug)]
415 pub(super) struct Node<'on_disk> {
415 pub(super) struct Node<'on_disk> {
416 pub(super) data: NodeData,
416 pub(super) data: NodeData,
417
417
418 pub(super) copy_source: Option<Cow<'on_disk, HgPath>>,
418 pub(super) copy_source: Option<Cow<'on_disk, HgPath>>,
419
419
420 pub(super) children: ChildNodes<'on_disk>,
420 pub(super) children: ChildNodes<'on_disk>,
421
421
422 /// How many (non-inclusive) descendants of this node have an entry.
422 /// How many (non-inclusive) descendants of this node have an entry.
423 pub(super) descendants_with_entry_count: u32,
423 pub(super) descendants_with_entry_count: u32,
424
424
425 /// How many (non-inclusive) descendants of this node have an entry whose
425 /// How many (non-inclusive) descendants of this node have an entry whose
426 /// state is "tracked".
426 /// state is "tracked".
427 pub(super) tracked_descendants_count: u32,
427 pub(super) tracked_descendants_count: u32,
428 }
428 }
429
429
430 #[derive(Debug, Default)]
430 #[derive(Debug, Default)]
431 pub(super) enum NodeData {
431 pub(super) enum NodeData {
432 Entry(DirstateEntry),
432 Entry(DirstateEntry),
433 CachedDirectory {
433 CachedDirectory {
434 mtime: TruncatedTimestamp,
434 mtime: TruncatedTimestamp,
435 },
435 },
436 #[default]
436 #[default]
437 None,
437 None,
438 }
438 }
439
439
440 impl NodeData {
440 impl NodeData {
441 fn has_entry(&self) -> bool {
441 fn has_entry(&self) -> bool {
442 matches!(self, NodeData::Entry(_))
442 matches!(self, NodeData::Entry(_))
443 }
443 }
444
444
445 fn as_entry(&self) -> Option<&DirstateEntry> {
445 fn as_entry(&self) -> Option<&DirstateEntry> {
446 match self {
446 match self {
447 NodeData::Entry(entry) => Some(entry),
447 NodeData::Entry(entry) => Some(entry),
448 _ => None,
448 _ => None,
449 }
449 }
450 }
450 }
451
451
452 fn as_entry_mut(&mut self) -> Option<&mut DirstateEntry> {
452 fn as_entry_mut(&mut self) -> Option<&mut DirstateEntry> {
453 match self {
453 match self {
454 NodeData::Entry(entry) => Some(entry),
454 NodeData::Entry(entry) => Some(entry),
455 _ => None,
455 _ => None,
456 }
456 }
457 }
457 }
458 }
458 }
459
459
460 impl<'on_disk> DirstateMap<'on_disk> {
460 impl<'on_disk> DirstateMap<'on_disk> {
461 pub(super) fn empty(on_disk: &'on_disk [u8]) -> Self {
461 pub(super) fn empty(on_disk: &'on_disk [u8]) -> Self {
462 Self {
462 Self {
463 on_disk,
463 on_disk,
464 root: ChildNodes::default(),
464 root: ChildNodes::default(),
465 nodes_with_entry_count: 0,
465 nodes_with_entry_count: 0,
466 nodes_with_copy_source_count: 0,
466 nodes_with_copy_source_count: 0,
467 ignore_patterns_hash: [0; on_disk::IGNORE_PATTERNS_HASH_LEN],
467 ignore_patterns_hash: [0; on_disk::IGNORE_PATTERNS_HASH_LEN],
468 unreachable_bytes: 0,
468 unreachable_bytes: 0,
469 old_data_size: 0,
469 old_data_size: 0,
470 old_uuid: None,
470 old_uuid: None,
471 identity: None,
471 identity: None,
472 dirstate_version: DirstateVersion::V1,
472 dirstate_version: DirstateVersion::V1,
473 write_mode: DirstateMapWriteMode::Auto,
473 write_mode: DirstateMapWriteMode::Auto,
474 }
474 }
475 }
475 }
476
476
477 #[logging_timer::time("trace")]
477 #[logging_timer::time("trace")]
478 pub fn new_v2(
478 pub fn new_v2(
479 on_disk: &'on_disk [u8],
479 on_disk: &'on_disk [u8],
480 data_size: usize,
480 data_size: usize,
481 metadata: &[u8],
481 metadata: &[u8],
482 uuid: Vec<u8>,
482 uuid: Vec<u8>,
483 identity: Option<u64>,
483 identity: Option<u64>,
484 ) -> Result<Self, DirstateError> {
484 ) -> Result<Self, DirstateError> {
485 if let Some(data) = on_disk.get(..data_size) {
485 if let Some(data) = on_disk.get(..data_size) {
486 Ok(on_disk::read(data, metadata, uuid, identity)?)
486 Ok(on_disk::read(data, metadata, uuid, identity)?)
487 } else {
487 } else {
488 Err(DirstateV2ParseError::new("not enough bytes on disk").into())
488 Err(DirstateV2ParseError::new("not enough bytes on disk").into())
489 }
489 }
490 }
490 }
491
491
492 #[logging_timer::time("trace")]
492 #[logging_timer::time("trace")]
493 pub fn new_v1(
493 pub fn new_v1(
494 on_disk: &'on_disk [u8],
494 on_disk: &'on_disk [u8],
495 identity: Option<u64>,
495 identity: Option<u64>,
496 ) -> Result<(Self, Option<DirstateParents>), DirstateError> {
496 ) -> Result<(Self, Option<DirstateParents>), DirstateError> {
497 let mut map = Self::empty(on_disk);
497 let mut map = Self::empty(on_disk);
498 map.identity = identity;
498 map.identity = identity;
499
499
500 if map.on_disk.is_empty() {
500 if map.on_disk.is_empty() {
501 return Ok((map, None));
501 return Ok((map, None));
502 }
502 }
503
503
504 let parents = parse_dirstate_entries(
504 let parents = parse_dirstate_entries(
505 map.on_disk,
505 map.on_disk,
506 |path, entry, copy_source| {
506 |path, entry, copy_source| {
507 let tracked = entry.tracked();
507 let tracked = entry.tracked();
508 let node = Self::get_or_insert_node_inner(
508 let node = Self::get_or_insert_node_inner(
509 map.on_disk,
509 map.on_disk,
510 &mut map.unreachable_bytes,
510 &mut map.unreachable_bytes,
511 &mut map.root,
511 &mut map.root,
512 path,
512 path,
513 WithBasename::to_cow_borrowed,
513 WithBasename::to_cow_borrowed,
514 |ancestor| {
514 |ancestor| {
515 if tracked {
515 if tracked {
516 ancestor.tracked_descendants_count += 1
516 ancestor.tracked_descendants_count += 1
517 }
517 }
518 ancestor.descendants_with_entry_count += 1
518 ancestor.descendants_with_entry_count += 1
519 },
519 },
520 )?;
520 )?;
521 assert!(
521 assert!(
522 !node.data.has_entry(),
522 !node.data.has_entry(),
523 "duplicate dirstate entry in read"
523 "duplicate dirstate entry in read"
524 );
524 );
525 assert!(
525 assert!(
526 node.copy_source.is_none(),
526 node.copy_source.is_none(),
527 "duplicate dirstate entry in read"
527 "duplicate dirstate entry in read"
528 );
528 );
529 node.data = NodeData::Entry(*entry);
529 node.data = NodeData::Entry(*entry);
530 node.copy_source = copy_source.map(Cow::Borrowed);
530 node.copy_source = copy_source.map(Cow::Borrowed);
531 map.nodes_with_entry_count += 1;
531 map.nodes_with_entry_count += 1;
532 if copy_source.is_some() {
532 if copy_source.is_some() {
533 map.nodes_with_copy_source_count += 1
533 map.nodes_with_copy_source_count += 1
534 }
534 }
535 Ok(())
535 Ok(())
536 },
536 },
537 )?;
537 )?;
538 let parents = Some(*parents);
538 let parents = Some(*parents);
539
539
540 Ok((map, parents))
540 Ok((map, parents))
541 }
541 }
542
542
543 /// Assuming dirstate-v2 format, returns whether the next write should
543 /// Assuming dirstate-v2 format, returns whether the next write should
544 /// append to the existing data file that contains `self.on_disk` (true),
544 /// append to the existing data file that contains `self.on_disk` (true),
545 /// or create a new data file from scratch (false).
545 /// or create a new data file from scratch (false).
546 pub(super) fn write_should_append(&self) -> bool {
546 pub(super) fn write_should_append(&self) -> bool {
547 match self.write_mode {
547 match self.write_mode {
548 DirstateMapWriteMode::ForceAppend => true,
548 DirstateMapWriteMode::ForceAppend => true,
549 DirstateMapWriteMode::ForceNewDataFile => false,
549 DirstateMapWriteMode::ForceNewDataFile => false,
550 DirstateMapWriteMode::Auto => {
550 DirstateMapWriteMode::Auto => {
551 let ratio =
551 let ratio =
552 self.unreachable_bytes as f32 / self.on_disk.len() as f32;
552 self.unreachable_bytes as f32 / self.on_disk.len() as f32;
553 ratio < ACCEPTABLE_UNREACHABLE_BYTES_RATIO
553 ratio < ACCEPTABLE_UNREACHABLE_BYTES_RATIO
554 }
554 }
555 }
555 }
556 }
556 }
557
557
558 fn get_node<'tree>(
558 fn get_node<'tree>(
559 &'tree self,
559 &'tree self,
560 path: &HgPath,
560 path: &HgPath,
561 ) -> Result<Option<NodeRef<'tree, 'on_disk>>, DirstateV2ParseError> {
561 ) -> Result<Option<NodeRef<'tree, 'on_disk>>, DirstateV2ParseError> {
562 let mut children = self.root.as_ref();
562 let mut children = self.root.as_ref();
563 let mut components = path.components();
563 let mut components = path.components();
564 let mut component =
564 let mut component =
565 components.next().expect("expected at least one components");
565 components.next().expect("expected at least one components");
566 loop {
566 loop {
567 if let Some(child) = children.get(component, self.on_disk)? {
567 if let Some(child) = children.get(component, self.on_disk)? {
568 if let Some(next_component) = components.next() {
568 if let Some(next_component) = components.next() {
569 component = next_component;
569 component = next_component;
570 children = child.children(self.on_disk)?;
570 children = child.children(self.on_disk)?;
571 } else {
571 } else {
572 return Ok(Some(child));
572 return Ok(Some(child));
573 }
573 }
574 } else {
574 } else {
575 return Ok(None);
575 return Ok(None);
576 }
576 }
577 }
577 }
578 }
578 }
579
579
580 pub fn has_node(
580 pub fn has_node(
581 &self,
581 &self,
582 path: &HgPath,
582 path: &HgPath,
583 ) -> Result<bool, DirstateV2ParseError> {
583 ) -> Result<bool, DirstateV2ParseError> {
584 let node = self.get_node(path)?;
584 let node = self.get_node(path)?;
585 Ok(node.is_some())
585 Ok(node.is_some())
586 }
586 }
587
587
588 /// Returns a mutable reference to the node at `path` if it exists
588 /// Returns a mutable reference to the node at `path` if it exists
589 ///
589 ///
590 /// `each_ancestor` is a callback that is called for each ancestor node
590 /// `each_ancestor` is a callback that is called for each ancestor node
591 /// when descending the tree. It is used to keep the different counters
591 /// when descending the tree. It is used to keep the different counters
592 /// of the `DirstateMap` up-to-date.
592 /// of the `DirstateMap` up-to-date.
593 fn get_node_mut<'tree>(
593 fn get_node_mut<'tree>(
594 &'tree mut self,
594 &'tree mut self,
595 path: &HgPath,
595 path: &HgPath,
596 each_ancestor: impl FnMut(&mut Node),
596 each_ancestor: impl FnMut(&mut Node),
597 ) -> Result<Option<&'tree mut Node<'on_disk>>, DirstateV2ParseError> {
597 ) -> Result<Option<&'tree mut Node<'on_disk>>, DirstateV2ParseError> {
598 Self::get_node_mut_inner(
598 Self::get_node_mut_inner(
599 self.on_disk,
599 self.on_disk,
600 &mut self.unreachable_bytes,
600 &mut self.unreachable_bytes,
601 &mut self.root,
601 &mut self.root,
602 path,
602 path,
603 each_ancestor,
603 each_ancestor,
604 )
604 )
605 }
605 }
606
606
607 /// Lower-level version of `get_node_mut`.
607 /// Lower-level version of `get_node_mut`.
608 ///
608 ///
609 /// This takes `root` instead of `&mut self` so that callers can mutate
609 /// This takes `root` instead of `&mut self` so that callers can mutate
610 /// other fields while the returned borrow is still valid.
610 /// other fields while the returned borrow is still valid.
611 ///
611 ///
612 /// `each_ancestor` is a callback that is called for each ancestor node
612 /// `each_ancestor` is a callback that is called for each ancestor node
613 /// when descending the tree. It is used to keep the different counters
613 /// when descending the tree. It is used to keep the different counters
614 /// of the `DirstateMap` up-to-date.
614 /// of the `DirstateMap` up-to-date.
615 fn get_node_mut_inner<'tree>(
615 fn get_node_mut_inner<'tree>(
616 on_disk: &'on_disk [u8],
616 on_disk: &'on_disk [u8],
617 unreachable_bytes: &mut u32,
617 unreachable_bytes: &mut u32,
618 root: &'tree mut ChildNodes<'on_disk>,
618 root: &'tree mut ChildNodes<'on_disk>,
619 path: &HgPath,
619 path: &HgPath,
620 mut each_ancestor: impl FnMut(&mut Node),
620 mut each_ancestor: impl FnMut(&mut Node),
621 ) -> Result<Option<&'tree mut Node<'on_disk>>, DirstateV2ParseError> {
621 ) -> Result<Option<&'tree mut Node<'on_disk>>, DirstateV2ParseError> {
622 let mut children = root;
622 let mut children = root;
623 let mut components = path.components();
623 let mut components = path.components();
624 let mut component =
624 let mut component =
625 components.next().expect("expected at least one components");
625 components.next().expect("expected at least one components");
626 loop {
626 loop {
627 if let Some(child) = children
627 if let Some(child) = children
628 .make_mut(on_disk, unreachable_bytes)?
628 .make_mut(on_disk, unreachable_bytes)?
629 .get_mut(component)
629 .get_mut(component)
630 {
630 {
631 if let Some(next_component) = components.next() {
631 if let Some(next_component) = components.next() {
632 each_ancestor(child);
632 each_ancestor(child);
633 component = next_component;
633 component = next_component;
634 children = &mut child.children;
634 children = &mut child.children;
635 } else {
635 } else {
636 return Ok(Some(child));
636 return Ok(Some(child));
637 }
637 }
638 } else {
638 } else {
639 return Ok(None);
639 return Ok(None);
640 }
640 }
641 }
641 }
642 }
642 }
643
643
644 /// Get a mutable reference to the node at `path`, creating it if it does
644 /// Get a mutable reference to the node at `path`, creating it if it does
645 /// not exist.
645 /// not exist.
646 ///
646 ///
647 /// `each_ancestor` is a callback that is called for each ancestor node
647 /// `each_ancestor` is a callback that is called for each ancestor node
648 /// when descending the tree. It is used to keep the different counters
648 /// when descending the tree. It is used to keep the different counters
649 /// of the `DirstateMap` up-to-date.
649 /// of the `DirstateMap` up-to-date.
650 fn get_or_insert_node<'tree, 'path>(
650 fn get_or_insert_node<'tree, 'path>(
651 &'tree mut self,
651 &'tree mut self,
652 path: &'path HgPath,
652 path: &'path HgPath,
653 each_ancestor: impl FnMut(&mut Node),
653 each_ancestor: impl FnMut(&mut Node),
654 ) -> Result<&'tree mut Node<'on_disk>, DirstateV2ParseError> {
654 ) -> Result<&'tree mut Node<'on_disk>, DirstateV2ParseError> {
655 Self::get_or_insert_node_inner(
655 Self::get_or_insert_node_inner(
656 self.on_disk,
656 self.on_disk,
657 &mut self.unreachable_bytes,
657 &mut self.unreachable_bytes,
658 &mut self.root,
658 &mut self.root,
659 path,
659 path,
660 WithBasename::to_cow_owned,
660 WithBasename::to_cow_owned,
661 each_ancestor,
661 each_ancestor,
662 )
662 )
663 }
663 }
664
664
665 /// Lower-level version of `get_or_insert_node_inner`, which is used when
665 /// Lower-level version of `get_or_insert_node_inner`, which is used when
666 /// parsing disk data to remove allocations for new nodes.
666 /// parsing disk data to remove allocations for new nodes.
667 fn get_or_insert_node_inner<'tree, 'path>(
667 fn get_or_insert_node_inner<'tree, 'path>(
668 on_disk: &'on_disk [u8],
668 on_disk: &'on_disk [u8],
669 unreachable_bytes: &mut u32,
669 unreachable_bytes: &mut u32,
670 root: &'tree mut ChildNodes<'on_disk>,
670 root: &'tree mut ChildNodes<'on_disk>,
671 path: &'path HgPath,
671 path: &'path HgPath,
672 to_cow: impl Fn(
672 to_cow: impl Fn(
673 WithBasename<&'path HgPath>,
673 WithBasename<&'path HgPath>,
674 ) -> WithBasename<Cow<'on_disk, HgPath>>,
674 ) -> WithBasename<Cow<'on_disk, HgPath>>,
675 mut each_ancestor: impl FnMut(&mut Node),
675 mut each_ancestor: impl FnMut(&mut Node),
676 ) -> Result<&'tree mut Node<'on_disk>, DirstateV2ParseError> {
676 ) -> Result<&'tree mut Node<'on_disk>, DirstateV2ParseError> {
677 let mut child_nodes = root;
677 let mut child_nodes = root;
678 let mut inclusive_ancestor_paths =
678 let mut inclusive_ancestor_paths =
679 WithBasename::inclusive_ancestors_of(path);
679 WithBasename::inclusive_ancestors_of(path);
680 let mut ancestor_path = inclusive_ancestor_paths
680 let mut ancestor_path = inclusive_ancestor_paths
681 .next()
681 .next()
682 .expect("expected at least one inclusive ancestor");
682 .expect("expected at least one inclusive ancestor");
683 loop {
683 loop {
684 let (_, child_node) = child_nodes
684 let (_, child_node) = child_nodes
685 .make_mut(on_disk, unreachable_bytes)?
685 .make_mut(on_disk, unreachable_bytes)?
686 .raw_entry_mut()
686 .raw_entry_mut()
687 .from_key(ancestor_path.base_name())
687 .from_key(ancestor_path.base_name())
688 .or_insert_with(|| (to_cow(ancestor_path), Node::default()));
688 .or_insert_with(|| (to_cow(ancestor_path), Node::default()));
689 if let Some(next) = inclusive_ancestor_paths.next() {
689 if let Some(next) = inclusive_ancestor_paths.next() {
690 each_ancestor(child_node);
690 each_ancestor(child_node);
691 ancestor_path = next;
691 ancestor_path = next;
692 child_nodes = &mut child_node.children;
692 child_nodes = &mut child_node.children;
693 } else {
693 } else {
694 return Ok(child_node);
694 return Ok(child_node);
695 }
695 }
696 }
696 }
697 }
697 }
698
698
699 #[allow(clippy::too_many_arguments)]
699 #[allow(clippy::too_many_arguments)]
700 fn reset_state(
700 fn reset_state(
701 &mut self,
701 &mut self,
702 filename: &HgPath,
702 filename: &HgPath,
703 old_entry_opt: Option<DirstateEntry>,
703 old_entry_opt: Option<DirstateEntry>,
704 wc_tracked: bool,
704 wc_tracked: bool,
705 p1_tracked: bool,
705 p1_tracked: bool,
706 p2_info: bool,
706 p2_info: bool,
707 has_meaningful_mtime: bool,
707 has_meaningful_mtime: bool,
708 parent_file_data_opt: Option<ParentFileData>,
708 parent_file_data_opt: Option<ParentFileData>,
709 ) -> Result<(), DirstateError> {
709 ) -> Result<(), DirstateError> {
710 let (had_entry, was_tracked) = match old_entry_opt {
710 let (had_entry, was_tracked) = match old_entry_opt {
711 Some(old_entry) => (true, old_entry.tracked()),
711 Some(old_entry) => (true, old_entry.tracked()),
712 None => (false, false),
712 None => (false, false),
713 };
713 };
714 let node = self.get_or_insert_node(filename, |ancestor| {
714 let node = self.get_or_insert_node(filename, |ancestor| {
715 if !had_entry {
715 if !had_entry {
716 ancestor.descendants_with_entry_count += 1;
716 ancestor.descendants_with_entry_count += 1;
717 }
717 }
718 if was_tracked {
718 if was_tracked {
719 if !wc_tracked {
719 if !wc_tracked {
720 ancestor.tracked_descendants_count = ancestor
720 ancestor.tracked_descendants_count = ancestor
721 .tracked_descendants_count
721 .tracked_descendants_count
722 .checked_sub(1)
722 .checked_sub(1)
723 .expect("tracked count to be >= 0");
723 .expect("tracked count to be >= 0");
724 }
724 }
725 } else if wc_tracked {
725 } else if wc_tracked {
726 ancestor.tracked_descendants_count += 1;
726 ancestor.tracked_descendants_count += 1;
727 }
727 }
728 })?;
728 })?;
729
729
730 let v2_data = if let Some(parent_file_data) = parent_file_data_opt {
730 let v2_data = if let Some(parent_file_data) = parent_file_data_opt {
731 DirstateV2Data {
731 DirstateV2Data {
732 wc_tracked,
732 wc_tracked,
733 p1_tracked,
733 p1_tracked,
734 p2_info,
734 p2_info,
735 mode_size: parent_file_data.mode_size,
735 mode_size: parent_file_data.mode_size,
736 mtime: if has_meaningful_mtime {
736 mtime: if has_meaningful_mtime {
737 parent_file_data.mtime
737 parent_file_data.mtime
738 } else {
738 } else {
739 None
739 None
740 },
740 },
741 ..Default::default()
741 ..Default::default()
742 }
742 }
743 } else {
743 } else {
744 DirstateV2Data {
744 DirstateV2Data {
745 wc_tracked,
745 wc_tracked,
746 p1_tracked,
746 p1_tracked,
747 p2_info,
747 p2_info,
748 ..Default::default()
748 ..Default::default()
749 }
749 }
750 };
750 };
751 node.data = NodeData::Entry(DirstateEntry::from_v2_data(v2_data));
751 node.data = NodeData::Entry(DirstateEntry::from_v2_data(v2_data));
752 if !had_entry {
752 if !had_entry {
753 self.nodes_with_entry_count += 1;
753 self.nodes_with_entry_count += 1;
754 }
754 }
755 Ok(())
755 Ok(())
756 }
756 }
757
757
758 fn set_tracked(
758 fn set_tracked(
759 &mut self,
759 &mut self,
760 filename: &HgPath,
760 filename: &HgPath,
761 old_entry_opt: Option<DirstateEntry>,
761 old_entry_opt: Option<DirstateEntry>,
762 ) -> Result<bool, DirstateV2ParseError> {
762 ) -> Result<bool, DirstateV2ParseError> {
763 let was_tracked = old_entry_opt.map_or(false, |e| e.tracked());
763 let was_tracked = old_entry_opt.map_or(false, |e| e.tracked());
764 let had_entry = old_entry_opt.is_some();
764 let had_entry = old_entry_opt.is_some();
765 let tracked_count_increment = u32::from(!was_tracked);
765 let tracked_count_increment = u32::from(!was_tracked);
766 let mut new = false;
766 let mut new = false;
767
767
768 let node = self.get_or_insert_node(filename, |ancestor| {
768 let node = self.get_or_insert_node(filename, |ancestor| {
769 if !had_entry {
769 if !had_entry {
770 ancestor.descendants_with_entry_count += 1;
770 ancestor.descendants_with_entry_count += 1;
771 }
771 }
772
772
773 ancestor.tracked_descendants_count += tracked_count_increment;
773 ancestor.tracked_descendants_count += tracked_count_increment;
774 })?;
774 })?;
775 if let Some(old_entry) = old_entry_opt {
775 if let Some(old_entry) = old_entry_opt {
776 let mut e = old_entry;
776 let mut e = old_entry;
777 if e.tracked() {
777 if e.tracked() {
778 // XXX
778 // XXX
779 // This is probably overkill for more case, but we need this to
779 // This is probably overkill for more case, but we need this to
780 // fully replace the `normallookup` call with `set_tracked`
780 // fully replace the `normallookup` call with `set_tracked`
781 // one. Consider smoothing this in the future.
781 // one. Consider smoothing this in the future.
782 e.set_possibly_dirty();
782 e.set_possibly_dirty();
783 } else {
783 } else {
784 new = true;
784 new = true;
785 e.set_tracked();
785 e.set_tracked();
786 }
786 }
787 node.data = NodeData::Entry(e)
787 node.data = NodeData::Entry(e)
788 } else {
788 } else {
789 node.data = NodeData::Entry(DirstateEntry::new_tracked());
789 node.data = NodeData::Entry(DirstateEntry::new_tracked());
790 self.nodes_with_entry_count += 1;
790 self.nodes_with_entry_count += 1;
791 new = true;
791 new = true;
792 };
792 };
793 Ok(new)
793 Ok(new)
794 }
794 }
795
795
796 /// Set a node as untracked in the dirstate.
796 /// Set a node as untracked in the dirstate.
797 ///
797 ///
798 /// It is the responsibility of the caller to remove the copy source and/or
798 /// It is the responsibility of the caller to remove the copy source and/or
799 /// the entry itself if appropriate.
799 /// the entry itself if appropriate.
800 ///
800 ///
801 /// # Panics
801 /// # Panics
802 ///
802 ///
803 /// Panics if the node does not exist.
803 /// Panics if the node does not exist.
804 fn set_untracked(
804 fn set_untracked(
805 &mut self,
805 &mut self,
806 filename: &HgPath,
806 filename: &HgPath,
807 old_entry: DirstateEntry,
807 old_entry: DirstateEntry,
808 ) -> Result<(), DirstateV2ParseError> {
808 ) -> Result<(), DirstateV2ParseError> {
809 let node = self
809 let node = self
810 .get_node_mut(filename, |ancestor| {
810 .get_node_mut(filename, |ancestor| {
811 ancestor.tracked_descendants_count = ancestor
811 ancestor.tracked_descendants_count = ancestor
812 .tracked_descendants_count
812 .tracked_descendants_count
813 .checked_sub(1)
813 .checked_sub(1)
814 .expect("tracked_descendants_count should be >= 0");
814 .expect("tracked_descendants_count should be >= 0");
815 })?
815 })?
816 .expect("node should exist");
816 .expect("node should exist");
817 let mut new_entry = old_entry;
817 let mut new_entry = old_entry;
818 new_entry.set_untracked();
818 new_entry.set_untracked();
819 node.data = NodeData::Entry(new_entry);
819 node.data = NodeData::Entry(new_entry);
820 Ok(())
820 Ok(())
821 }
821 }
822
822
823 /// Set a node as clean in the dirstate.
823 /// Set a node as clean in the dirstate.
824 ///
824 ///
825 /// It is the responsibility of the caller to remove the copy source.
825 /// It is the responsibility of the caller to remove the copy source.
826 ///
826 ///
827 /// # Panics
827 /// # Panics
828 ///
828 ///
829 /// Panics if the node does not exist.
829 /// Panics if the node does not exist.
830 fn set_clean(
830 fn set_clean(
831 &mut self,
831 &mut self,
832 filename: &HgPath,
832 filename: &HgPath,
833 old_entry: DirstateEntry,
833 old_entry: DirstateEntry,
834 mode: u32,
834 mode: u32,
835 size: u32,
835 size: u32,
836 mtime: TruncatedTimestamp,
836 mtime: TruncatedTimestamp,
837 ) -> Result<(), DirstateError> {
837 ) -> Result<(), DirstateError> {
838 let node = self
838 let node = self
839 .get_node_mut(filename, |ancestor| {
839 .get_node_mut(filename, |ancestor| {
840 if !old_entry.tracked() {
840 if !old_entry.tracked() {
841 ancestor.tracked_descendants_count += 1;
841 ancestor.tracked_descendants_count += 1;
842 }
842 }
843 })?
843 })?
844 .expect("node should exist");
844 .expect("node should exist");
845 let mut new_entry = old_entry;
845 let mut new_entry = old_entry;
846 new_entry.set_clean(mode, size, mtime);
846 new_entry.set_clean(mode, size, mtime);
847 node.data = NodeData::Entry(new_entry);
847 node.data = NodeData::Entry(new_entry);
848 Ok(())
848 Ok(())
849 }
849 }
850
850
851 /// Set a node as possibly dirty in the dirstate.
851 /// Set a node as possibly dirty in the dirstate.
852 ///
852 ///
853 /// # Panics
853 /// # Panics
854 ///
854 ///
855 /// Panics if the node does not exist.
855 /// Panics if the node does not exist.
856 fn set_possibly_dirty(
856 fn set_possibly_dirty(
857 &mut self,
857 &mut self,
858 filename: &HgPath,
858 filename: &HgPath,
859 ) -> Result<(), DirstateError> {
859 ) -> Result<(), DirstateError> {
860 let node = self
860 let node = self
861 .get_node_mut(filename, |_ancestor| {})?
861 .get_node_mut(filename, |_ancestor| {})?
862 .expect("node should exist");
862 .expect("node should exist");
863 let entry = node.data.as_entry_mut().expect("entry should exist");
863 let entry = node.data.as_entry_mut().expect("entry should exist");
864 entry.set_possibly_dirty();
864 entry.set_possibly_dirty();
865 node.data = NodeData::Entry(*entry);
865 node.data = NodeData::Entry(*entry);
866 Ok(())
866 Ok(())
867 }
867 }
868
868
869 /// Clears the cached mtime for the (potential) folder at `path`.
869 /// Clears the cached mtime for the (potential) folder at `path`.
870 pub(super) fn clear_cached_mtime(
870 pub(super) fn clear_cached_mtime(
871 &mut self,
871 &mut self,
872 path: &HgPath,
872 path: &HgPath,
873 ) -> Result<(), DirstateV2ParseError> {
873 ) -> Result<(), DirstateV2ParseError> {
874 let node = match self.get_node_mut(path, |_ancestor| {})? {
874 let node = match self.get_node_mut(path, |_ancestor| {})? {
875 Some(node) => node,
875 Some(node) => node,
876 None => return Ok(()),
876 None => return Ok(()),
877 };
877 };
878 if let NodeData::CachedDirectory { .. } = &node.data {
878 if let NodeData::CachedDirectory { .. } = &node.data {
879 node.data = NodeData::None
879 node.data = NodeData::None
880 }
880 }
881 Ok(())
881 Ok(())
882 }
882 }
883
883
884 /// Sets the cached mtime for the (potential) folder at `path`.
884 /// Sets the cached mtime for the (potential) folder at `path`.
885 pub(super) fn set_cached_mtime(
885 pub(super) fn set_cached_mtime(
886 &mut self,
886 &mut self,
887 path: &HgPath,
887 path: &HgPath,
888 mtime: TruncatedTimestamp,
888 mtime: TruncatedTimestamp,
889 ) -> Result<(), DirstateV2ParseError> {
889 ) -> Result<(), DirstateV2ParseError> {
890 let node = match self.get_node_mut(path, |_ancestor| {})? {
890 let node = match self.get_node_mut(path, |_ancestor| {})? {
891 Some(node) => node,
891 Some(node) => node,
892 None => return Ok(()),
892 None => return Ok(()),
893 };
893 };
894 match &node.data {
894 match &node.data {
895 NodeData::Entry(_) => {} // Don’t overwrite an entry
895 NodeData::Entry(_) => {} // Don’t overwrite an entry
896 NodeData::CachedDirectory { .. } | NodeData::None => {
896 NodeData::CachedDirectory { .. } | NodeData::None => {
897 node.data = NodeData::CachedDirectory { mtime }
897 node.data = NodeData::CachedDirectory { mtime }
898 }
898 }
899 }
899 }
900 Ok(())
900 Ok(())
901 }
901 }
902
902
903 fn iter_nodes<'tree>(
903 fn iter_nodes<'tree>(
904 &'tree self,
904 &'tree self,
905 ) -> impl Iterator<
905 ) -> impl Iterator<
906 Item = Result<NodeRef<'tree, 'on_disk>, DirstateV2ParseError>,
906 Item = Result<NodeRef<'tree, 'on_disk>, DirstateV2ParseError>,
907 > + 'tree {
907 > + 'tree {
908 // Depth first tree traversal.
908 // Depth first tree traversal.
909 //
909 //
910 // If we could afford internal iteration and recursion,
910 // If we could afford internal iteration and recursion,
911 // this would look like:
911 // this would look like:
912 //
912 //
913 // ```
913 // ```
914 // fn traverse_children(
914 // fn traverse_children(
915 // children: &ChildNodes,
915 // children: &ChildNodes,
916 // each: &mut impl FnMut(&Node),
916 // each: &mut impl FnMut(&Node),
917 // ) {
917 // ) {
918 // for child in children.values() {
918 // for child in children.values() {
919 // traverse_children(&child.children, each);
919 // traverse_children(&child.children, each);
920 // each(child);
920 // each(child);
921 // }
921 // }
922 // }
922 // }
923 // ```
923 // ```
924 //
924 //
925 // However we want an external iterator and therefore can’t use the
925 // However we want an external iterator and therefore can’t use the
926 // call stack. Use an explicit stack instead:
926 // call stack. Use an explicit stack instead:
927 let mut stack = Vec::new();
927 let mut stack = Vec::new();
928 let mut iter = self.root.as_ref().iter();
928 let mut iter = self.root.as_ref().iter();
929 std::iter::from_fn(move || {
929 std::iter::from_fn(move || {
930 while let Some(child_node) = iter.next() {
930 while let Some(child_node) = iter.next() {
931 let children = match child_node.children(self.on_disk) {
931 let children = match child_node.children(self.on_disk) {
932 Ok(children) => children,
932 Ok(children) => children,
933 Err(error) => return Some(Err(error)),
933 Err(error) => return Some(Err(error)),
934 };
934 };
935 // Pseudo-recursion
935 // Pseudo-recursion
936 let new_iter = children.iter();
936 let new_iter = children.iter();
937 let old_iter = std::mem::replace(&mut iter, new_iter);
937 let old_iter = std::mem::replace(&mut iter, new_iter);
938 stack.push((child_node, old_iter));
938 stack.push((child_node, old_iter));
939 }
939 }
940 // Found the end of a `children.iter()` iterator.
940 // Found the end of a `children.iter()` iterator.
941 if let Some((child_node, next_iter)) = stack.pop() {
941 if let Some((child_node, next_iter)) = stack.pop() {
942 // "Return" from pseudo-recursion by restoring state from the
942 // "Return" from pseudo-recursion by restoring state from the
943 // explicit stack
943 // explicit stack
944 iter = next_iter;
944 iter = next_iter;
945
945
946 Some(Ok(child_node))
946 Some(Ok(child_node))
947 } else {
947 } else {
948 // Reached the bottom of the stack, we’re done
948 // Reached the bottom of the stack, we’re done
949 None
949 None
950 }
950 }
951 })
951 })
952 }
952 }
953
953
954 fn count_dropped_path(unreachable_bytes: &mut u32, path: Cow<HgPath>) {
954 fn count_dropped_path(unreachable_bytes: &mut u32, path: Cow<HgPath>) {
955 if let Cow::Borrowed(path) = path {
955 if let Cow::Borrowed(path) = path {
956 *unreachable_bytes += path.len() as u32
956 *unreachable_bytes += path.len() as u32
957 }
957 }
958 }
958 }
959
959
960 pub(crate) fn set_write_mode(&mut self, write_mode: DirstateMapWriteMode) {
960 pub(crate) fn set_write_mode(&mut self, write_mode: DirstateMapWriteMode) {
961 self.write_mode = write_mode;
961 self.write_mode = write_mode;
962 }
962 }
963 }
963 }
964
964
965 /// Sets the parameters for resetting a dirstate entry
966 pub struct DirstateEntryReset<'a> {
967 /// Which entry are we resetting
968 pub filename: &'a HgPath,
969 /// Whether the entry is tracked in the working copy
970 pub wc_tracked: bool,
971 /// Whether the entry is tracked in p1
972 pub p1_tracked: bool,
973 /// Whether the entry has merge information
974 pub p2_info: bool,
975 /// Whether the entry's mtime should be trusted
976 pub has_meaningful_mtime: bool,
977 /// Information from the parent file data (from the manifest)
978 pub parent_file_data_opt: Option<ParentFileData>,
979 /// Set this to `true` if you are *certain* that there is no old entry for
980 /// this filename. Yield better performance in cases where we do a lot
981 /// of additions to the dirstate.
982 pub from_empty: bool,
983 }
984
965 type DebugDirstateTuple<'a> = (&'a HgPath, (u8, i32, i32, i32));
985 type DebugDirstateTuple<'a> = (&'a HgPath, (u8, i32, i32, i32));
966
986
967 impl OwningDirstateMap {
987 impl OwningDirstateMap {
968 pub fn clear(&mut self) {
988 pub fn clear(&mut self) {
969 self.with_dmap_mut(|map| {
989 self.with_dmap_mut(|map| {
970 map.root = Default::default();
990 map.root = Default::default();
971 map.nodes_with_entry_count = 0;
991 map.nodes_with_entry_count = 0;
972 map.nodes_with_copy_source_count = 0;
992 map.nodes_with_copy_source_count = 0;
973 map.unreachable_bytes = map.on_disk.len() as u32;
993 map.unreachable_bytes = map.on_disk.len() as u32;
974 });
994 });
975 }
995 }
976
996
977 pub fn set_tracked(
997 pub fn set_tracked(
978 &mut self,
998 &mut self,
979 filename: &HgPath,
999 filename: &HgPath,
980 ) -> Result<bool, DirstateV2ParseError> {
1000 ) -> Result<bool, DirstateV2ParseError> {
981 let old_entry_opt = self.get(filename)?;
1001 let old_entry_opt = self.get(filename)?;
982 self.with_dmap_mut(|map| map.set_tracked(filename, old_entry_opt))
1002 self.with_dmap_mut(|map| map.set_tracked(filename, old_entry_opt))
983 }
1003 }
984
1004
985 pub fn set_untracked(
1005 pub fn set_untracked(
986 &mut self,
1006 &mut self,
987 filename: &HgPath,
1007 filename: &HgPath,
988 ) -> Result<bool, DirstateError> {
1008 ) -> Result<bool, DirstateError> {
989 let old_entry_opt = self.get(filename)?;
1009 let old_entry_opt = self.get(filename)?;
990 match old_entry_opt {
1010 match old_entry_opt {
991 None => Ok(false),
1011 None => Ok(false),
992 Some(old_entry) => {
1012 Some(old_entry) => {
993 if !old_entry.tracked() {
1013 if !old_entry.tracked() {
994 // `DirstateMap::set_untracked` is not a noop if
1014 // `DirstateMap::set_untracked` is not a noop if
995 // already not tracked as it will decrement the
1015 // already not tracked as it will decrement the
996 // tracked counters while going down.
1016 // tracked counters while going down.
997 return Ok(true);
1017 return Ok(true);
998 }
1018 }
999 if old_entry.added() {
1019 if old_entry.added() {
1000 // Untracking an "added" entry will just result in a
1020 // Untracking an "added" entry will just result in a
1001 // worthless entry (and other parts of the code will
1021 // worthless entry (and other parts of the code will
1002 // complain about it), just drop it entirely.
1022 // complain about it), just drop it entirely.
1003 self.drop_entry_and_copy_source(filename)?;
1023 self.drop_entry_and_copy_source(filename)?;
1004 return Ok(true);
1024 return Ok(true);
1005 }
1025 }
1006 if !old_entry.p2_info() {
1026 if !old_entry.p2_info() {
1007 self.copy_map_remove(filename)?;
1027 self.copy_map_remove(filename)?;
1008 }
1028 }
1009
1029
1010 self.with_dmap_mut(|map| {
1030 self.with_dmap_mut(|map| {
1011 map.set_untracked(filename, old_entry)?;
1031 map.set_untracked(filename, old_entry)?;
1012 Ok(true)
1032 Ok(true)
1013 })
1033 })
1014 }
1034 }
1015 }
1035 }
1016 }
1036 }
1017
1037
1018 pub fn set_clean(
1038 pub fn set_clean(
1019 &mut self,
1039 &mut self,
1020 filename: &HgPath,
1040 filename: &HgPath,
1021 mode: u32,
1041 mode: u32,
1022 size: u32,
1042 size: u32,
1023 mtime: TruncatedTimestamp,
1043 mtime: TruncatedTimestamp,
1024 ) -> Result<(), DirstateError> {
1044 ) -> Result<(), DirstateError> {
1025 let old_entry = match self.get(filename)? {
1045 let old_entry = match self.get(filename)? {
1026 None => {
1046 None => {
1027 return Err(
1047 return Err(
1028 DirstateMapError::PathNotFound(filename.into()).into()
1048 DirstateMapError::PathNotFound(filename.into()).into()
1029 )
1049 )
1030 }
1050 }
1031 Some(e) => e,
1051 Some(e) => e,
1032 };
1052 };
1033 self.copy_map_remove(filename)?;
1053 self.copy_map_remove(filename)?;
1034 self.with_dmap_mut(|map| {
1054 self.with_dmap_mut(|map| {
1035 map.set_clean(filename, old_entry, mode, size, mtime)
1055 map.set_clean(filename, old_entry, mode, size, mtime)
1036 })
1056 })
1037 }
1057 }
1038
1058
1039 pub fn set_possibly_dirty(
1059 pub fn set_possibly_dirty(
1040 &mut self,
1060 &mut self,
1041 filename: &HgPath,
1061 filename: &HgPath,
1042 ) -> Result<(), DirstateError> {
1062 ) -> Result<(), DirstateError> {
1043 if self.get(filename)?.is_none() {
1063 if self.get(filename)?.is_none() {
1044 return Err(DirstateMapError::PathNotFound(filename.into()).into());
1064 return Err(DirstateMapError::PathNotFound(filename.into()).into());
1045 }
1065 }
1046 self.with_dmap_mut(|map| map.set_possibly_dirty(filename))
1066 self.with_dmap_mut(|map| map.set_possibly_dirty(filename))
1047 }
1067 }
1048
1068
1049 pub fn reset_state(
1069 pub fn reset_state(
1050 &mut self,
1070 &mut self,
1051 filename: &HgPath,
1071 reset: DirstateEntryReset,
1052 wc_tracked: bool,
1053 p1_tracked: bool,
1054 p2_info: bool,
1055 has_meaningful_mtime: bool,
1056 parent_file_data_opt: Option<ParentFileData>,
1057 ) -> Result<(), DirstateError> {
1072 ) -> Result<(), DirstateError> {
1058 if !(p1_tracked || p2_info || wc_tracked) {
1073 if !(reset.p1_tracked || reset.p2_info || reset.wc_tracked) {
1059 self.drop_entry_and_copy_source(filename)?;
1074 self.drop_entry_and_copy_source(reset.filename)?;
1060 return Ok(());
1075 return Ok(());
1061 }
1076 }
1062 self.copy_map_remove(filename)?;
1077 if !reset.from_empty {
1063 let old_entry_opt = self.get(filename)?;
1078 self.copy_map_remove(reset.filename)?;
1079 }
1080
1081 let old_entry_opt = if reset.from_empty {
1082 None
1083 } else {
1084 self.get(reset.filename)?
1085 };
1086
1064 self.with_dmap_mut(|map| {
1087 self.with_dmap_mut(|map| {
1065 map.reset_state(
1088 map.reset_state(
1066 filename,
1089 reset.filename,
1067 old_entry_opt,
1090 old_entry_opt,
1068 wc_tracked,
1091 reset.wc_tracked,
1069 p1_tracked,
1092 reset.p1_tracked,
1070 p2_info,
1093 reset.p2_info,
1071 has_meaningful_mtime,
1094 reset.has_meaningful_mtime,
1072 parent_file_data_opt,
1095 reset.parent_file_data_opt,
1073 )
1096 )
1074 })
1097 })
1075 }
1098 }
1076
1099
1077 pub fn drop_entry_and_copy_source(
1100 pub fn drop_entry_and_copy_source(
1078 &mut self,
1101 &mut self,
1079 filename: &HgPath,
1102 filename: &HgPath,
1080 ) -> Result<(), DirstateError> {
1103 ) -> Result<(), DirstateError> {
1081 let was_tracked = self.get(filename)?.map_or(false, |e| e.tracked());
1104 let was_tracked = self.get(filename)?.map_or(false, |e| e.tracked());
1082 struct Dropped {
1105 struct Dropped {
1083 was_tracked: bool,
1106 was_tracked: bool,
1084 had_entry: bool,
1107 had_entry: bool,
1085 had_copy_source: bool,
1108 had_copy_source: bool,
1086 }
1109 }
1087
1110
1088 /// If this returns `Ok(Some((dropped, removed)))`, then
1111 /// If this returns `Ok(Some((dropped, removed)))`, then
1089 ///
1112 ///
1090 /// * `dropped` is about the leaf node that was at `filename`
1113 /// * `dropped` is about the leaf node that was at `filename`
1091 /// * `removed` is whether this particular level of recursion just
1114 /// * `removed` is whether this particular level of recursion just
1092 /// removed a node in `nodes`.
1115 /// removed a node in `nodes`.
1093 fn recur<'on_disk>(
1116 fn recur<'on_disk>(
1094 on_disk: &'on_disk [u8],
1117 on_disk: &'on_disk [u8],
1095 unreachable_bytes: &mut u32,
1118 unreachable_bytes: &mut u32,
1096 nodes: &mut ChildNodes<'on_disk>,
1119 nodes: &mut ChildNodes<'on_disk>,
1097 path: &HgPath,
1120 path: &HgPath,
1098 ) -> Result<Option<(Dropped, bool)>, DirstateV2ParseError> {
1121 ) -> Result<Option<(Dropped, bool)>, DirstateV2ParseError> {
1099 let (first_path_component, rest_of_path) =
1122 let (first_path_component, rest_of_path) =
1100 path.split_first_component();
1123 path.split_first_component();
1101 let nodes = nodes.make_mut(on_disk, unreachable_bytes)?;
1124 let nodes = nodes.make_mut(on_disk, unreachable_bytes)?;
1102 let node = if let Some(node) = nodes.get_mut(first_path_component)
1125 let node = if let Some(node) = nodes.get_mut(first_path_component)
1103 {
1126 {
1104 node
1127 node
1105 } else {
1128 } else {
1106 return Ok(None);
1129 return Ok(None);
1107 };
1130 };
1108 let dropped;
1131 let dropped;
1109 if let Some(rest) = rest_of_path {
1132 if let Some(rest) = rest_of_path {
1110 if let Some((d, removed)) = recur(
1133 if let Some((d, removed)) = recur(
1111 on_disk,
1134 on_disk,
1112 unreachable_bytes,
1135 unreachable_bytes,
1113 &mut node.children,
1136 &mut node.children,
1114 rest,
1137 rest,
1115 )? {
1138 )? {
1116 dropped = d;
1139 dropped = d;
1117 if dropped.had_entry {
1140 if dropped.had_entry {
1118 node.descendants_with_entry_count = node
1141 node.descendants_with_entry_count = node
1119 .descendants_with_entry_count
1142 .descendants_with_entry_count
1120 .checked_sub(1)
1143 .checked_sub(1)
1121 .expect(
1144 .expect(
1122 "descendants_with_entry_count should be >= 0",
1145 "descendants_with_entry_count should be >= 0",
1123 );
1146 );
1124 }
1147 }
1125 if dropped.was_tracked {
1148 if dropped.was_tracked {
1126 node.tracked_descendants_count = node
1149 node.tracked_descendants_count = node
1127 .tracked_descendants_count
1150 .tracked_descendants_count
1128 .checked_sub(1)
1151 .checked_sub(1)
1129 .expect(
1152 .expect(
1130 "tracked_descendants_count should be >= 0",
1153 "tracked_descendants_count should be >= 0",
1131 );
1154 );
1132 }
1155 }
1133
1156
1134 // Directory caches must be invalidated when removing a
1157 // Directory caches must be invalidated when removing a
1135 // child node
1158 // child node
1136 if removed {
1159 if removed {
1137 if let NodeData::CachedDirectory { .. } = &node.data {
1160 if let NodeData::CachedDirectory { .. } = &node.data {
1138 node.data = NodeData::None
1161 node.data = NodeData::None
1139 }
1162 }
1140 }
1163 }
1141 } else {
1164 } else {
1142 return Ok(None);
1165 return Ok(None);
1143 }
1166 }
1144 } else {
1167 } else {
1145 let entry = node.data.as_entry();
1168 let entry = node.data.as_entry();
1146 let was_tracked = entry.map_or(false, |entry| entry.tracked());
1169 let was_tracked = entry.map_or(false, |entry| entry.tracked());
1147 let had_entry = entry.is_some();
1170 let had_entry = entry.is_some();
1148 if had_entry {
1171 if had_entry {
1149 node.data = NodeData::None
1172 node.data = NodeData::None
1150 }
1173 }
1151 let mut had_copy_source = false;
1174 let mut had_copy_source = false;
1152 if let Some(source) = &node.copy_source {
1175 if let Some(source) = &node.copy_source {
1153 DirstateMap::count_dropped_path(
1176 DirstateMap::count_dropped_path(
1154 unreachable_bytes,
1177 unreachable_bytes,
1155 Cow::Borrowed(source),
1178 Cow::Borrowed(source),
1156 );
1179 );
1157 had_copy_source = true;
1180 had_copy_source = true;
1158 node.copy_source = None
1181 node.copy_source = None
1159 }
1182 }
1160 dropped = Dropped {
1183 dropped = Dropped {
1161 was_tracked,
1184 was_tracked,
1162 had_entry,
1185 had_entry,
1163 had_copy_source,
1186 had_copy_source,
1164 };
1187 };
1165 }
1188 }
1166 // After recursion, for both leaf (rest_of_path is None) nodes and
1189 // After recursion, for both leaf (rest_of_path is None) nodes and
1167 // parent nodes, remove a node if it just became empty.
1190 // parent nodes, remove a node if it just became empty.
1168 let remove = !node.data.has_entry()
1191 let remove = !node.data.has_entry()
1169 && node.copy_source.is_none()
1192 && node.copy_source.is_none()
1170 && node.children.is_empty();
1193 && node.children.is_empty();
1171 if remove {
1194 if remove {
1172 let (key, _) =
1195 let (key, _) =
1173 nodes.remove_entry(first_path_component).unwrap();
1196 nodes.remove_entry(first_path_component).unwrap();
1174 DirstateMap::count_dropped_path(
1197 DirstateMap::count_dropped_path(
1175 unreachable_bytes,
1198 unreachable_bytes,
1176 Cow::Borrowed(key.full_path()),
1199 Cow::Borrowed(key.full_path()),
1177 )
1200 )
1178 }
1201 }
1179 Ok(Some((dropped, remove)))
1202 Ok(Some((dropped, remove)))
1180 }
1203 }
1181
1204
1182 self.with_dmap_mut(|map| {
1205 self.with_dmap_mut(|map| {
1183 if let Some((dropped, _removed)) = recur(
1206 if let Some((dropped, _removed)) = recur(
1184 map.on_disk,
1207 map.on_disk,
1185 &mut map.unreachable_bytes,
1208 &mut map.unreachable_bytes,
1186 &mut map.root,
1209 &mut map.root,
1187 filename,
1210 filename,
1188 )? {
1211 )? {
1189 if dropped.had_entry {
1212 if dropped.had_entry {
1190 map.nodes_with_entry_count = map
1213 map.nodes_with_entry_count = map
1191 .nodes_with_entry_count
1214 .nodes_with_entry_count
1192 .checked_sub(1)
1215 .checked_sub(1)
1193 .expect("nodes_with_entry_count should be >= 0");
1216 .expect("nodes_with_entry_count should be >= 0");
1194 }
1217 }
1195 if dropped.had_copy_source {
1218 if dropped.had_copy_source {
1196 map.nodes_with_copy_source_count = map
1219 map.nodes_with_copy_source_count = map
1197 .nodes_with_copy_source_count
1220 .nodes_with_copy_source_count
1198 .checked_sub(1)
1221 .checked_sub(1)
1199 .expect("nodes_with_copy_source_count should be >= 0");
1222 .expect("nodes_with_copy_source_count should be >= 0");
1200 }
1223 }
1201 } else {
1224 } else {
1202 debug_assert!(!was_tracked);
1225 debug_assert!(!was_tracked);
1203 }
1226 }
1204 Ok(())
1227 Ok(())
1205 })
1228 })
1206 }
1229 }
1207
1230
1208 pub fn has_tracked_dir(
1231 pub fn has_tracked_dir(
1209 &mut self,
1232 &mut self,
1210 directory: &HgPath,
1233 directory: &HgPath,
1211 ) -> Result<bool, DirstateError> {
1234 ) -> Result<bool, DirstateError> {
1212 self.with_dmap_mut(|map| {
1235 self.with_dmap_mut(|map| {
1213 if let Some(node) = map.get_node(directory)? {
1236 if let Some(node) = map.get_node(directory)? {
1214 // A node without a `DirstateEntry` was created to hold child
1237 // A node without a `DirstateEntry` was created to hold child
1215 // nodes, and is therefore a directory.
1238 // nodes, and is therefore a directory.
1216 let is_dir = node.entry()?.is_none();
1239 let is_dir = node.entry()?.is_none();
1217 Ok(is_dir && node.tracked_descendants_count() > 0)
1240 Ok(is_dir && node.tracked_descendants_count() > 0)
1218 } else {
1241 } else {
1219 Ok(false)
1242 Ok(false)
1220 }
1243 }
1221 })
1244 })
1222 }
1245 }
1223
1246
1224 pub fn has_dir(
1247 pub fn has_dir(
1225 &mut self,
1248 &mut self,
1226 directory: &HgPath,
1249 directory: &HgPath,
1227 ) -> Result<bool, DirstateError> {
1250 ) -> Result<bool, DirstateError> {
1228 self.with_dmap_mut(|map| {
1251 self.with_dmap_mut(|map| {
1229 if let Some(node) = map.get_node(directory)? {
1252 if let Some(node) = map.get_node(directory)? {
1230 // A node without a `DirstateEntry` was created to hold child
1253 // A node without a `DirstateEntry` was created to hold child
1231 // nodes, and is therefore a directory.
1254 // nodes, and is therefore a directory.
1232 let is_dir = node.entry()?.is_none();
1255 let is_dir = node.entry()?.is_none();
1233 Ok(is_dir && node.descendants_with_entry_count() > 0)
1256 Ok(is_dir && node.descendants_with_entry_count() > 0)
1234 } else {
1257 } else {
1235 Ok(false)
1258 Ok(false)
1236 }
1259 }
1237 })
1260 })
1238 }
1261 }
1239
1262
1240 #[logging_timer::time("trace")]
1263 #[logging_timer::time("trace")]
1241 pub fn pack_v1(
1264 pub fn pack_v1(
1242 &self,
1265 &self,
1243 parents: DirstateParents,
1266 parents: DirstateParents,
1244 ) -> Result<Vec<u8>, DirstateError> {
1267 ) -> Result<Vec<u8>, DirstateError> {
1245 let map = self.get_map();
1268 let map = self.get_map();
1246 // Optizimation (to be measured?): pre-compute size to avoid `Vec`
1269 // Optizimation (to be measured?): pre-compute size to avoid `Vec`
1247 // reallocations
1270 // reallocations
1248 let mut size = parents.as_bytes().len();
1271 let mut size = parents.as_bytes().len();
1249 for node in map.iter_nodes() {
1272 for node in map.iter_nodes() {
1250 let node = node?;
1273 let node = node?;
1251 if node.entry()?.is_some() {
1274 if node.entry()?.is_some() {
1252 size += packed_entry_size(
1275 size += packed_entry_size(
1253 node.full_path(map.on_disk)?,
1276 node.full_path(map.on_disk)?,
1254 node.copy_source(map.on_disk)?,
1277 node.copy_source(map.on_disk)?,
1255 );
1278 );
1256 }
1279 }
1257 }
1280 }
1258
1281
1259 let mut packed = Vec::with_capacity(size);
1282 let mut packed = Vec::with_capacity(size);
1260 packed.extend(parents.as_bytes());
1283 packed.extend(parents.as_bytes());
1261
1284
1262 for node in map.iter_nodes() {
1285 for node in map.iter_nodes() {
1263 let node = node?;
1286 let node = node?;
1264 if let Some(entry) = node.entry()? {
1287 if let Some(entry) = node.entry()? {
1265 pack_entry(
1288 pack_entry(
1266 node.full_path(map.on_disk)?,
1289 node.full_path(map.on_disk)?,
1267 &entry,
1290 &entry,
1268 node.copy_source(map.on_disk)?,
1291 node.copy_source(map.on_disk)?,
1269 &mut packed,
1292 &mut packed,
1270 );
1293 );
1271 }
1294 }
1272 }
1295 }
1273 Ok(packed)
1296 Ok(packed)
1274 }
1297 }
1275
1298
1276 /// Returns new data and metadata together with whether that data should be
1299 /// Returns new data and metadata together with whether that data should be
1277 /// appended to the existing data file whose content is at
1300 /// appended to the existing data file whose content is at
1278 /// `map.on_disk` (true), instead of written to a new data file
1301 /// `map.on_disk` (true), instead of written to a new data file
1279 /// (false), and the previous size of data on disk.
1302 /// (false), and the previous size of data on disk.
1280 #[logging_timer::time("trace")]
1303 #[logging_timer::time("trace")]
1281 pub fn pack_v2(
1304 pub fn pack_v2(
1282 &self,
1305 &self,
1283 write_mode: DirstateMapWriteMode,
1306 write_mode: DirstateMapWriteMode,
1284 ) -> Result<(Vec<u8>, on_disk::TreeMetadata, bool, usize), DirstateError>
1307 ) -> Result<(Vec<u8>, on_disk::TreeMetadata, bool, usize), DirstateError>
1285 {
1308 {
1286 let map = self.get_map();
1309 let map = self.get_map();
1287 on_disk::write(map, write_mode)
1310 on_disk::write(map, write_mode)
1288 }
1311 }
1289
1312
1290 /// `callback` allows the caller to process and do something with the
1313 /// `callback` allows the caller to process and do something with the
1291 /// results of the status. This is needed to do so efficiently (i.e.
1314 /// results of the status. This is needed to do so efficiently (i.e.
1292 /// without cloning the `DirstateStatus` object with its paths) because
1315 /// without cloning the `DirstateStatus` object with its paths) because
1293 /// we need to borrow from `Self`.
1316 /// we need to borrow from `Self`.
1294 pub fn with_status<R>(
1317 pub fn with_status<R>(
1295 &mut self,
1318 &mut self,
1296 matcher: &(dyn Matcher + Sync),
1319 matcher: &(dyn Matcher + Sync),
1297 root_dir: PathBuf,
1320 root_dir: PathBuf,
1298 ignore_files: Vec<PathBuf>,
1321 ignore_files: Vec<PathBuf>,
1299 options: StatusOptions,
1322 options: StatusOptions,
1300 callback: impl for<'r> FnOnce(
1323 callback: impl for<'r> FnOnce(
1301 Result<(DirstateStatus<'r>, Vec<PatternFileWarning>), StatusError>,
1324 Result<(DirstateStatus<'r>, Vec<PatternFileWarning>), StatusError>,
1302 ) -> R,
1325 ) -> R,
1303 ) -> R {
1326 ) -> R {
1304 self.with_dmap_mut(|map| {
1327 self.with_dmap_mut(|map| {
1305 callback(super::status::status(
1328 callback(super::status::status(
1306 map,
1329 map,
1307 matcher,
1330 matcher,
1308 root_dir,
1331 root_dir,
1309 ignore_files,
1332 ignore_files,
1310 options,
1333 options,
1311 ))
1334 ))
1312 })
1335 })
1313 }
1336 }
1314
1337
1315 pub fn copy_map_len(&self) -> usize {
1338 pub fn copy_map_len(&self) -> usize {
1316 let map = self.get_map();
1339 let map = self.get_map();
1317 map.nodes_with_copy_source_count as usize
1340 map.nodes_with_copy_source_count as usize
1318 }
1341 }
1319
1342
1320 pub fn copy_map_iter(&self) -> CopyMapIter<'_> {
1343 pub fn copy_map_iter(&self) -> CopyMapIter<'_> {
1321 let map = self.get_map();
1344 let map = self.get_map();
1322 Box::new(filter_map_results(map.iter_nodes(), move |node| {
1345 Box::new(filter_map_results(map.iter_nodes(), move |node| {
1323 Ok(if let Some(source) = node.copy_source(map.on_disk)? {
1346 Ok(if let Some(source) = node.copy_source(map.on_disk)? {
1324 Some((node.full_path(map.on_disk)?, source))
1347 Some((node.full_path(map.on_disk)?, source))
1325 } else {
1348 } else {
1326 None
1349 None
1327 })
1350 })
1328 }))
1351 }))
1329 }
1352 }
1330
1353
1331 pub fn copy_map_contains_key(
1354 pub fn copy_map_contains_key(
1332 &self,
1355 &self,
1333 key: &HgPath,
1356 key: &HgPath,
1334 ) -> Result<bool, DirstateV2ParseError> {
1357 ) -> Result<bool, DirstateV2ParseError> {
1335 let map = self.get_map();
1358 let map = self.get_map();
1336 Ok(if let Some(node) = map.get_node(key)? {
1359 Ok(if let Some(node) = map.get_node(key)? {
1337 node.has_copy_source()
1360 node.has_copy_source()
1338 } else {
1361 } else {
1339 false
1362 false
1340 })
1363 })
1341 }
1364 }
1342
1365
1343 pub fn copy_map_get(
1366 pub fn copy_map_get(
1344 &self,
1367 &self,
1345 key: &HgPath,
1368 key: &HgPath,
1346 ) -> Result<Option<&HgPath>, DirstateV2ParseError> {
1369 ) -> Result<Option<&HgPath>, DirstateV2ParseError> {
1347 let map = self.get_map();
1370 let map = self.get_map();
1348 if let Some(node) = map.get_node(key)? {
1371 if let Some(node) = map.get_node(key)? {
1349 if let Some(source) = node.copy_source(map.on_disk)? {
1372 if let Some(source) = node.copy_source(map.on_disk)? {
1350 return Ok(Some(source));
1373 return Ok(Some(source));
1351 }
1374 }
1352 }
1375 }
1353 Ok(None)
1376 Ok(None)
1354 }
1377 }
1355
1378
1356 pub fn copy_map_remove(
1379 pub fn copy_map_remove(
1357 &mut self,
1380 &mut self,
1358 key: &HgPath,
1381 key: &HgPath,
1359 ) -> Result<Option<HgPathBuf>, DirstateV2ParseError> {
1382 ) -> Result<Option<HgPathBuf>, DirstateV2ParseError> {
1360 self.with_dmap_mut(|map| {
1383 self.with_dmap_mut(|map| {
1361 let count = &mut map.nodes_with_copy_source_count;
1384 let count = &mut map.nodes_with_copy_source_count;
1362 let unreachable_bytes = &mut map.unreachable_bytes;
1385 let unreachable_bytes = &mut map.unreachable_bytes;
1363 Ok(DirstateMap::get_node_mut_inner(
1386 Ok(DirstateMap::get_node_mut_inner(
1364 map.on_disk,
1387 map.on_disk,
1365 unreachable_bytes,
1388 unreachable_bytes,
1366 &mut map.root,
1389 &mut map.root,
1367 key,
1390 key,
1368 |_ancestor| {},
1391 |_ancestor| {},
1369 )?
1392 )?
1370 .and_then(|node| {
1393 .and_then(|node| {
1371 if let Some(source) = &node.copy_source {
1394 if let Some(source) = &node.copy_source {
1372 *count = count
1395 *count = count
1373 .checked_sub(1)
1396 .checked_sub(1)
1374 .expect("nodes_with_copy_source_count should be >= 0");
1397 .expect("nodes_with_copy_source_count should be >= 0");
1375 DirstateMap::count_dropped_path(
1398 DirstateMap::count_dropped_path(
1376 unreachable_bytes,
1399 unreachable_bytes,
1377 Cow::Borrowed(source),
1400 Cow::Borrowed(source),
1378 );
1401 );
1379 }
1402 }
1380 node.copy_source.take().map(Cow::into_owned)
1403 node.copy_source.take().map(Cow::into_owned)
1381 }))
1404 }))
1382 })
1405 })
1383 }
1406 }
1384
1407
1385 pub fn copy_map_insert(
1408 pub fn copy_map_insert(
1386 &mut self,
1409 &mut self,
1387 key: &HgPath,
1410 key: &HgPath,
1388 value: &HgPath,
1411 value: &HgPath,
1389 ) -> Result<Option<HgPathBuf>, DirstateV2ParseError> {
1412 ) -> Result<Option<HgPathBuf>, DirstateV2ParseError> {
1390 self.with_dmap_mut(|map| {
1413 self.with_dmap_mut(|map| {
1391 let node = map.get_or_insert_node(key, |_ancestor| {})?;
1414 let node = map.get_or_insert_node(key, |_ancestor| {})?;
1392 let had_copy_source = node.copy_source.is_none();
1415 let had_copy_source = node.copy_source.is_none();
1393 let old = node
1416 let old = node
1394 .copy_source
1417 .copy_source
1395 .replace(value.to_owned().into())
1418 .replace(value.to_owned().into())
1396 .map(Cow::into_owned);
1419 .map(Cow::into_owned);
1397 if had_copy_source {
1420 if had_copy_source {
1398 map.nodes_with_copy_source_count += 1
1421 map.nodes_with_copy_source_count += 1
1399 }
1422 }
1400 Ok(old)
1423 Ok(old)
1401 })
1424 })
1402 }
1425 }
1403
1426
1404 pub fn len(&self) -> usize {
1427 pub fn len(&self) -> usize {
1405 let map = self.get_map();
1428 let map = self.get_map();
1406 map.nodes_with_entry_count as usize
1429 map.nodes_with_entry_count as usize
1407 }
1430 }
1408
1431
1409 pub fn is_empty(&self) -> bool {
1432 pub fn is_empty(&self) -> bool {
1410 self.len() == 0
1433 self.len() == 0
1411 }
1434 }
1412
1435
1413 pub fn contains_key(
1436 pub fn contains_key(
1414 &self,
1437 &self,
1415 key: &HgPath,
1438 key: &HgPath,
1416 ) -> Result<bool, DirstateV2ParseError> {
1439 ) -> Result<bool, DirstateV2ParseError> {
1417 Ok(self.get(key)?.is_some())
1440 Ok(self.get(key)?.is_some())
1418 }
1441 }
1419
1442
1420 pub fn get(
1443 pub fn get(
1421 &self,
1444 &self,
1422 key: &HgPath,
1445 key: &HgPath,
1423 ) -> Result<Option<DirstateEntry>, DirstateV2ParseError> {
1446 ) -> Result<Option<DirstateEntry>, DirstateV2ParseError> {
1424 let map = self.get_map();
1447 let map = self.get_map();
1425 Ok(if let Some(node) = map.get_node(key)? {
1448 Ok(if let Some(node) = map.get_node(key)? {
1426 node.entry()?
1449 node.entry()?
1427 } else {
1450 } else {
1428 None
1451 None
1429 })
1452 })
1430 }
1453 }
1431
1454
1432 pub fn iter(&self) -> StateMapIter<'_> {
1455 pub fn iter(&self) -> StateMapIter<'_> {
1433 let map = self.get_map();
1456 let map = self.get_map();
1434 Box::new(filter_map_results(map.iter_nodes(), move |node| {
1457 Box::new(filter_map_results(map.iter_nodes(), move |node| {
1435 Ok(if let Some(entry) = node.entry()? {
1458 Ok(if let Some(entry) = node.entry()? {
1436 Some((node.full_path(map.on_disk)?, entry))
1459 Some((node.full_path(map.on_disk)?, entry))
1437 } else {
1460 } else {
1438 None
1461 None
1439 })
1462 })
1440 }))
1463 }))
1441 }
1464 }
1442
1465
1443 pub fn iter_tracked_dirs(
1466 pub fn iter_tracked_dirs(
1444 &mut self,
1467 &mut self,
1445 ) -> Result<
1468 ) -> Result<
1446 Box<
1469 Box<
1447 dyn Iterator<Item = Result<&HgPath, DirstateV2ParseError>>
1470 dyn Iterator<Item = Result<&HgPath, DirstateV2ParseError>>
1448 + Send
1471 + Send
1449 + '_,
1472 + '_,
1450 >,
1473 >,
1451 DirstateError,
1474 DirstateError,
1452 > {
1475 > {
1453 let map = self.get_map();
1476 let map = self.get_map();
1454 let on_disk = map.on_disk;
1477 let on_disk = map.on_disk;
1455 Ok(Box::new(filter_map_results(
1478 Ok(Box::new(filter_map_results(
1456 map.iter_nodes(),
1479 map.iter_nodes(),
1457 move |node| {
1480 move |node| {
1458 Ok(if node.tracked_descendants_count() > 0 {
1481 Ok(if node.tracked_descendants_count() > 0 {
1459 Some(node.full_path(on_disk)?)
1482 Some(node.full_path(on_disk)?)
1460 } else {
1483 } else {
1461 None
1484 None
1462 })
1485 })
1463 },
1486 },
1464 )))
1487 )))
1465 }
1488 }
1466
1489
1467 /// Only public because it needs to be exposed to the Python layer.
1490 /// Only public because it needs to be exposed to the Python layer.
1468 /// It is not the full `setparents` logic, only the parts that mutate the
1491 /// It is not the full `setparents` logic, only the parts that mutate the
1469 /// entries.
1492 /// entries.
1470 pub fn setparents_fixup(
1493 pub fn setparents_fixup(
1471 &mut self,
1494 &mut self,
1472 ) -> Result<Vec<(HgPathBuf, HgPathBuf)>, DirstateV2ParseError> {
1495 ) -> Result<Vec<(HgPathBuf, HgPathBuf)>, DirstateV2ParseError> {
1473 // XXX
1496 // XXX
1474 // All the copying and re-querying is quite inefficient, but this is
1497 // All the copying and re-querying is quite inefficient, but this is
1475 // still a lot better than doing it from Python.
1498 // still a lot better than doing it from Python.
1476 //
1499 //
1477 // The better solution is to develop a mechanism for `iter_mut`,
1500 // The better solution is to develop a mechanism for `iter_mut`,
1478 // which will be a lot more involved: we're dealing with a lazy,
1501 // which will be a lot more involved: we're dealing with a lazy,
1479 // append-mostly, tree-like data structure. This will do for now.
1502 // append-mostly, tree-like data structure. This will do for now.
1480 let mut copies = vec![];
1503 let mut copies = vec![];
1481 let mut files_with_p2_info = vec![];
1504 let mut files_with_p2_info = vec![];
1482 for res in self.iter() {
1505 for res in self.iter() {
1483 let (path, entry) = res?;
1506 let (path, entry) = res?;
1484 if entry.p2_info() {
1507 if entry.p2_info() {
1485 files_with_p2_info.push(path.to_owned())
1508 files_with_p2_info.push(path.to_owned())
1486 }
1509 }
1487 }
1510 }
1488 self.with_dmap_mut(|map| {
1511 self.with_dmap_mut(|map| {
1489 for path in files_with_p2_info.iter() {
1512 for path in files_with_p2_info.iter() {
1490 let node = map.get_or_insert_node(path, |_| {})?;
1513 let node = map.get_or_insert_node(path, |_| {})?;
1491 let entry =
1514 let entry =
1492 node.data.as_entry_mut().expect("entry should exist");
1515 node.data.as_entry_mut().expect("entry should exist");
1493 entry.drop_merge_data();
1516 entry.drop_merge_data();
1494 if let Some(source) = node.copy_source.take().as_deref() {
1517 if let Some(source) = node.copy_source.take().as_deref() {
1495 copies.push((path.to_owned(), source.to_owned()));
1518 copies.push((path.to_owned(), source.to_owned()));
1496 }
1519 }
1497 }
1520 }
1498 Ok(copies)
1521 Ok(copies)
1499 })
1522 })
1500 }
1523 }
1501
1524
1502 pub fn debug_iter(
1525 pub fn debug_iter(
1503 &self,
1526 &self,
1504 all: bool,
1527 all: bool,
1505 ) -> Box<
1528 ) -> Box<
1506 dyn Iterator<Item = Result<DebugDirstateTuple, DirstateV2ParseError>>
1529 dyn Iterator<Item = Result<DebugDirstateTuple, DirstateV2ParseError>>
1507 + Send
1530 + Send
1508 + '_,
1531 + '_,
1509 > {
1532 > {
1510 let map = self.get_map();
1533 let map = self.get_map();
1511 Box::new(filter_map_results(map.iter_nodes(), move |node| {
1534 Box::new(filter_map_results(map.iter_nodes(), move |node| {
1512 let debug_tuple = if let Some(entry) = node.entry()? {
1535 let debug_tuple = if let Some(entry) = node.entry()? {
1513 entry.debug_tuple()
1536 entry.debug_tuple()
1514 } else if !all {
1537 } else if !all {
1515 return Ok(None);
1538 return Ok(None);
1516 } else if let Some(mtime) = node.cached_directory_mtime()? {
1539 } else if let Some(mtime) = node.cached_directory_mtime()? {
1517 (b' ', 0, -1, mtime.truncated_seconds() as i32)
1540 (b' ', 0, -1, mtime.truncated_seconds() as i32)
1518 } else {
1541 } else {
1519 (b' ', 0, -1, -1)
1542 (b' ', 0, -1, -1)
1520 };
1543 };
1521 Ok(Some((node.full_path(map.on_disk)?, debug_tuple)))
1544 Ok(Some((node.full_path(map.on_disk)?, debug_tuple)))
1522 }))
1545 }))
1523 }
1546 }
1524 }
1547 }
1525 #[cfg(test)]
1548 #[cfg(test)]
1526 mod tests {
1549 mod tests {
1527 use super::*;
1550 use super::*;
1528
1551
1529 /// Shortcut to return tracked descendants of a path.
1552 /// Shortcut to return tracked descendants of a path.
1530 /// Panics if the path does not exist.
1553 /// Panics if the path does not exist.
1531 fn tracked_descendants(map: &OwningDirstateMap, path: &[u8]) -> u32 {
1554 fn tracked_descendants(map: &OwningDirstateMap, path: &[u8]) -> u32 {
1532 let path = dbg!(HgPath::new(path));
1555 let path = dbg!(HgPath::new(path));
1533 let node = map.get_map().get_node(path);
1556 let node = map.get_map().get_node(path);
1534 node.unwrap().unwrap().tracked_descendants_count()
1557 node.unwrap().unwrap().tracked_descendants_count()
1535 }
1558 }
1536
1559
1537 /// Shortcut to return descendants with an entry.
1560 /// Shortcut to return descendants with an entry.
1538 /// Panics if the path does not exist.
1561 /// Panics if the path does not exist.
1539 fn descendants_with_an_entry(map: &OwningDirstateMap, path: &[u8]) -> u32 {
1562 fn descendants_with_an_entry(map: &OwningDirstateMap, path: &[u8]) -> u32 {
1540 let path = dbg!(HgPath::new(path));
1563 let path = dbg!(HgPath::new(path));
1541 let node = map.get_map().get_node(path);
1564 let node = map.get_map().get_node(path);
1542 node.unwrap().unwrap().descendants_with_entry_count()
1565 node.unwrap().unwrap().descendants_with_entry_count()
1543 }
1566 }
1544
1567
1545 fn assert_does_not_exist(map: &OwningDirstateMap, path: &[u8]) {
1568 fn assert_does_not_exist(map: &OwningDirstateMap, path: &[u8]) {
1546 let path = dbg!(HgPath::new(path));
1569 let path = dbg!(HgPath::new(path));
1547 let node = map.get_map().get_node(path);
1570 let node = map.get_map().get_node(path);
1548 assert!(node.unwrap().is_none());
1571 assert!(node.unwrap().is_none());
1549 }
1572 }
1550
1573
1551 /// Shortcut for path creation in tests
1574 /// Shortcut for path creation in tests
1552 fn p(b: &[u8]) -> &HgPath {
1575 fn p(b: &[u8]) -> &HgPath {
1553 HgPath::new(b)
1576 HgPath::new(b)
1554 }
1577 }
1555
1578
1556 /// Test the very simple case a single tracked file
1579 /// Test the very simple case a single tracked file
1557 #[test]
1580 #[test]
1558 fn test_tracked_descendants_simple() -> Result<(), DirstateError> {
1581 fn test_tracked_descendants_simple() -> Result<(), DirstateError> {
1559 let mut map = OwningDirstateMap::new_empty(vec![], None);
1582 let mut map = OwningDirstateMap::new_empty(vec![], None);
1560 assert_eq!(map.len(), 0);
1583 assert_eq!(map.len(), 0);
1561
1584
1562 map.set_tracked(p(b"some/nested/path"))?;
1585 map.set_tracked(p(b"some/nested/path"))?;
1563
1586
1564 assert_eq!(map.len(), 1);
1587 assert_eq!(map.len(), 1);
1565 assert_eq!(tracked_descendants(&map, b"some"), 1);
1588 assert_eq!(tracked_descendants(&map, b"some"), 1);
1566 assert_eq!(tracked_descendants(&map, b"some/nested"), 1);
1589 assert_eq!(tracked_descendants(&map, b"some/nested"), 1);
1567 assert_eq!(tracked_descendants(&map, b"some/nested/path"), 0);
1590 assert_eq!(tracked_descendants(&map, b"some/nested/path"), 0);
1568
1591
1569 map.set_untracked(p(b"some/nested/path"))?;
1592 map.set_untracked(p(b"some/nested/path"))?;
1570 assert_eq!(map.len(), 0);
1593 assert_eq!(map.len(), 0);
1571 assert!(map.get_map().get_node(p(b"some"))?.is_none());
1594 assert!(map.get_map().get_node(p(b"some"))?.is_none());
1572
1595
1573 Ok(())
1596 Ok(())
1574 }
1597 }
1575
1598
1576 /// Test the simple case of all tracked, but multiple files
1599 /// Test the simple case of all tracked, but multiple files
1577 #[test]
1600 #[test]
1578 fn test_tracked_descendants_multiple() -> Result<(), DirstateError> {
1601 fn test_tracked_descendants_multiple() -> Result<(), DirstateError> {
1579 let mut map = OwningDirstateMap::new_empty(vec![], None);
1602 let mut map = OwningDirstateMap::new_empty(vec![], None);
1580
1603
1581 map.set_tracked(p(b"some/nested/path"))?;
1604 map.set_tracked(p(b"some/nested/path"))?;
1582 map.set_tracked(p(b"some/nested/file"))?;
1605 map.set_tracked(p(b"some/nested/file"))?;
1583 // one layer without any files to test deletion cascade
1606 // one layer without any files to test deletion cascade
1584 map.set_tracked(p(b"some/other/nested/path"))?;
1607 map.set_tracked(p(b"some/other/nested/path"))?;
1585 map.set_tracked(p(b"root_file"))?;
1608 map.set_tracked(p(b"root_file"))?;
1586 map.set_tracked(p(b"some/file"))?;
1609 map.set_tracked(p(b"some/file"))?;
1587 map.set_tracked(p(b"some/file2"))?;
1610 map.set_tracked(p(b"some/file2"))?;
1588 map.set_tracked(p(b"some/file3"))?;
1611 map.set_tracked(p(b"some/file3"))?;
1589
1612
1590 assert_eq!(map.len(), 7);
1613 assert_eq!(map.len(), 7);
1591 assert_eq!(tracked_descendants(&map, b"some"), 6);
1614 assert_eq!(tracked_descendants(&map, b"some"), 6);
1592 assert_eq!(tracked_descendants(&map, b"some/nested"), 2);
1615 assert_eq!(tracked_descendants(&map, b"some/nested"), 2);
1593 assert_eq!(tracked_descendants(&map, b"some/other"), 1);
1616 assert_eq!(tracked_descendants(&map, b"some/other"), 1);
1594 assert_eq!(tracked_descendants(&map, b"some/other/nested"), 1);
1617 assert_eq!(tracked_descendants(&map, b"some/other/nested"), 1);
1595 assert_eq!(tracked_descendants(&map, b"some/nested/path"), 0);
1618 assert_eq!(tracked_descendants(&map, b"some/nested/path"), 0);
1596
1619
1597 map.set_untracked(p(b"some/nested/path"))?;
1620 map.set_untracked(p(b"some/nested/path"))?;
1598 assert_eq!(map.len(), 6);
1621 assert_eq!(map.len(), 6);
1599 assert_eq!(tracked_descendants(&map, b"some"), 5);
1622 assert_eq!(tracked_descendants(&map, b"some"), 5);
1600 assert_eq!(tracked_descendants(&map, b"some/nested"), 1);
1623 assert_eq!(tracked_descendants(&map, b"some/nested"), 1);
1601 assert_eq!(tracked_descendants(&map, b"some/other"), 1);
1624 assert_eq!(tracked_descendants(&map, b"some/other"), 1);
1602 assert_eq!(tracked_descendants(&map, b"some/other/nested"), 1);
1625 assert_eq!(tracked_descendants(&map, b"some/other/nested"), 1);
1603
1626
1604 map.set_untracked(p(b"some/nested/file"))?;
1627 map.set_untracked(p(b"some/nested/file"))?;
1605 assert_eq!(map.len(), 5);
1628 assert_eq!(map.len(), 5);
1606 assert_eq!(tracked_descendants(&map, b"some"), 4);
1629 assert_eq!(tracked_descendants(&map, b"some"), 4);
1607 assert_eq!(tracked_descendants(&map, b"some/other"), 1);
1630 assert_eq!(tracked_descendants(&map, b"some/other"), 1);
1608 assert_eq!(tracked_descendants(&map, b"some/other/nested"), 1);
1631 assert_eq!(tracked_descendants(&map, b"some/other/nested"), 1);
1609 assert_does_not_exist(&map, b"some_nested");
1632 assert_does_not_exist(&map, b"some_nested");
1610
1633
1611 map.set_untracked(p(b"some/other/nested/path"))?;
1634 map.set_untracked(p(b"some/other/nested/path"))?;
1612 assert_eq!(map.len(), 4);
1635 assert_eq!(map.len(), 4);
1613 assert_eq!(tracked_descendants(&map, b"some"), 3);
1636 assert_eq!(tracked_descendants(&map, b"some"), 3);
1614 assert_does_not_exist(&map, b"some/other");
1637 assert_does_not_exist(&map, b"some/other");
1615
1638
1616 map.set_untracked(p(b"root_file"))?;
1639 map.set_untracked(p(b"root_file"))?;
1617 assert_eq!(map.len(), 3);
1640 assert_eq!(map.len(), 3);
1618 assert_eq!(tracked_descendants(&map, b"some"), 3);
1641 assert_eq!(tracked_descendants(&map, b"some"), 3);
1619 assert_does_not_exist(&map, b"root_file");
1642 assert_does_not_exist(&map, b"root_file");
1620
1643
1621 map.set_untracked(p(b"some/file"))?;
1644 map.set_untracked(p(b"some/file"))?;
1622 assert_eq!(map.len(), 2);
1645 assert_eq!(map.len(), 2);
1623 assert_eq!(tracked_descendants(&map, b"some"), 2);
1646 assert_eq!(tracked_descendants(&map, b"some"), 2);
1624 assert_does_not_exist(&map, b"some/file");
1647 assert_does_not_exist(&map, b"some/file");
1625
1648
1626 map.set_untracked(p(b"some/file2"))?;
1649 map.set_untracked(p(b"some/file2"))?;
1627 assert_eq!(map.len(), 1);
1650 assert_eq!(map.len(), 1);
1628 assert_eq!(tracked_descendants(&map, b"some"), 1);
1651 assert_eq!(tracked_descendants(&map, b"some"), 1);
1629 assert_does_not_exist(&map, b"some/file2");
1652 assert_does_not_exist(&map, b"some/file2");
1630
1653
1631 map.set_untracked(p(b"some/file3"))?;
1654 map.set_untracked(p(b"some/file3"))?;
1632 assert_eq!(map.len(), 0);
1655 assert_eq!(map.len(), 0);
1633 assert_does_not_exist(&map, b"some/file3");
1656 assert_does_not_exist(&map, b"some/file3");
1634
1657
1635 Ok(())
1658 Ok(())
1636 }
1659 }
1637
1660
1638 /// Check with a mix of tracked and non-tracked items
1661 /// Check with a mix of tracked and non-tracked items
1639 #[test]
1662 #[test]
1640 fn test_tracked_descendants_different() -> Result<(), DirstateError> {
1663 fn test_tracked_descendants_different() -> Result<(), DirstateError> {
1641 let mut map = OwningDirstateMap::new_empty(vec![], None);
1664 let mut map = OwningDirstateMap::new_empty(vec![], None);
1642
1665
1643 // A file that was just added
1666 // A file that was just added
1644 map.set_tracked(p(b"some/nested/path"))?;
1667 map.set_tracked(p(b"some/nested/path"))?;
1645 // This has no information, the dirstate should ignore it
1668 // This has no information, the dirstate should ignore it
1646 map.reset_state(p(b"some/file"), false, false, false, false, None)?;
1669 let reset = DirstateEntryReset {
1670 filename: p(b"some/file"),
1671 wc_tracked: false,
1672 p1_tracked: false,
1673 p2_info: false,
1674 has_meaningful_mtime: false,
1675 parent_file_data_opt: None,
1676 from_empty: false,
1677 };
1678 map.reset_state(reset)?;
1647 assert_does_not_exist(&map, b"some/file");
1679 assert_does_not_exist(&map, b"some/file");
1648
1680
1649 // A file that was removed
1681 // A file that was removed
1650 map.reset_state(
1682 let reset = DirstateEntryReset {
1651 p(b"some/nested/file"),
1683 filename: p(b"some/nested/file"),
1652 false,
1684 wc_tracked: false,
1653 true,
1685 p1_tracked: true,
1654 false,
1686 p2_info: false,
1655 false,
1687 has_meaningful_mtime: false,
1656 None,
1688 parent_file_data_opt: None,
1657 )?;
1689 from_empty: false,
1690 };
1691 map.reset_state(reset)?;
1658 assert!(!map.get(p(b"some/nested/file"))?.unwrap().tracked());
1692 assert!(!map.get(p(b"some/nested/file"))?.unwrap().tracked());
1659 // Only present in p2
1693 // Only present in p2
1660 map.reset_state(p(b"some/file3"), false, false, true, false, None)?;
1694 let reset = DirstateEntryReset {
1695 filename: p(b"some/file3"),
1696 wc_tracked: false,
1697 p1_tracked: false,
1698 p2_info: true,
1699 has_meaningful_mtime: false,
1700 parent_file_data_opt: None,
1701 from_empty: false,
1702 };
1703 map.reset_state(reset)?;
1661 assert!(!map.get(p(b"some/file3"))?.unwrap().tracked());
1704 assert!(!map.get(p(b"some/file3"))?.unwrap().tracked());
1662 // A file that was merged
1705 // A file that was merged
1663 map.reset_state(p(b"root_file"), true, true, true, false, None)?;
1706 let reset = DirstateEntryReset {
1707 filename: p(b"root_file"),
1708 wc_tracked: true,
1709 p1_tracked: true,
1710 p2_info: true,
1711 has_meaningful_mtime: false,
1712 parent_file_data_opt: None,
1713 from_empty: false,
1714 };
1715 map.reset_state(reset)?;
1664 assert!(map.get(p(b"root_file"))?.unwrap().tracked());
1716 assert!(map.get(p(b"root_file"))?.unwrap().tracked());
1665 // A file that is added, with info from p2
1717 // A file that is added, with info from p2
1666 // XXX is that actually possible?
1718 // XXX is that actually possible?
1667 map.reset_state(p(b"some/file2"), true, false, true, false, None)?;
1719 let reset = DirstateEntryReset {
1720 filename: p(b"some/file2"),
1721 wc_tracked: true,
1722 p1_tracked: false,
1723 p2_info: true,
1724 has_meaningful_mtime: false,
1725 parent_file_data_opt: None,
1726 from_empty: false,
1727 };
1728 map.reset_state(reset)?;
1668 assert!(map.get(p(b"some/file2"))?.unwrap().tracked());
1729 assert!(map.get(p(b"some/file2"))?.unwrap().tracked());
1669 // A clean file
1730 // A clean file
1670 // One layer without any files to test deletion cascade
1731 // One layer without any files to test deletion cascade
1671 map.reset_state(
1732 let reset = DirstateEntryReset {
1672 p(b"some/other/nested/path"),
1733 filename: p(b"some/other/nested/path"),
1673 true,
1734 wc_tracked: true,
1674 true,
1735 p1_tracked: true,
1675 false,
1736 p2_info: false,
1676 false,
1737 has_meaningful_mtime: false,
1677 None,
1738 parent_file_data_opt: None,
1678 )?;
1739 from_empty: false,
1740 };
1741 map.reset_state(reset)?;
1679 assert!(map.get(p(b"some/other/nested/path"))?.unwrap().tracked());
1742 assert!(map.get(p(b"some/other/nested/path"))?.unwrap().tracked());
1680
1743
1681 assert_eq!(map.len(), 6);
1744 assert_eq!(map.len(), 6);
1682 assert_eq!(tracked_descendants(&map, b"some"), 3);
1745 assert_eq!(tracked_descendants(&map, b"some"), 3);
1683 assert_eq!(descendants_with_an_entry(&map, b"some"), 5);
1746 assert_eq!(descendants_with_an_entry(&map, b"some"), 5);
1684 assert_eq!(tracked_descendants(&map, b"some/other/nested"), 1);
1747 assert_eq!(tracked_descendants(&map, b"some/other/nested"), 1);
1685 assert_eq!(descendants_with_an_entry(&map, b"some/other/nested"), 1);
1748 assert_eq!(descendants_with_an_entry(&map, b"some/other/nested"), 1);
1686 assert_eq!(tracked_descendants(&map, b"some/other/nested/path"), 0);
1749 assert_eq!(tracked_descendants(&map, b"some/other/nested/path"), 0);
1687 assert_eq!(
1750 assert_eq!(
1688 descendants_with_an_entry(&map, b"some/other/nested/path"),
1751 descendants_with_an_entry(&map, b"some/other/nested/path"),
1689 0
1752 0
1690 );
1753 );
1691 assert_eq!(tracked_descendants(&map, b"some/nested"), 1);
1754 assert_eq!(tracked_descendants(&map, b"some/nested"), 1);
1692 assert_eq!(descendants_with_an_entry(&map, b"some/nested"), 2);
1755 assert_eq!(descendants_with_an_entry(&map, b"some/nested"), 2);
1693
1756
1694 // might as well check this
1757 // might as well check this
1695 map.set_untracked(p(b"path/does/not/exist"))?;
1758 map.set_untracked(p(b"path/does/not/exist"))?;
1696 assert_eq!(map.len(), 6);
1759 assert_eq!(map.len(), 6);
1697
1760
1698 map.set_untracked(p(b"some/other/nested/path"))?;
1761 map.set_untracked(p(b"some/other/nested/path"))?;
1699 // It is set untracked but not deleted since it held other information
1762 // It is set untracked but not deleted since it held other information
1700 assert_eq!(map.len(), 6);
1763 assert_eq!(map.len(), 6);
1701 assert_eq!(tracked_descendants(&map, b"some"), 2);
1764 assert_eq!(tracked_descendants(&map, b"some"), 2);
1702 assert_eq!(descendants_with_an_entry(&map, b"some"), 5);
1765 assert_eq!(descendants_with_an_entry(&map, b"some"), 5);
1703 assert_eq!(descendants_with_an_entry(&map, b"some/other"), 1);
1766 assert_eq!(descendants_with_an_entry(&map, b"some/other"), 1);
1704 assert_eq!(descendants_with_an_entry(&map, b"some/other/nested"), 1);
1767 assert_eq!(descendants_with_an_entry(&map, b"some/other/nested"), 1);
1705 assert_eq!(tracked_descendants(&map, b"some/nested"), 1);
1768 assert_eq!(tracked_descendants(&map, b"some/nested"), 1);
1706 assert_eq!(descendants_with_an_entry(&map, b"some/nested"), 2);
1769 assert_eq!(descendants_with_an_entry(&map, b"some/nested"), 2);
1707
1770
1708 map.set_untracked(p(b"some/nested/path"))?;
1771 map.set_untracked(p(b"some/nested/path"))?;
1709 // It is set untracked *and* deleted since it was only added
1772 // It is set untracked *and* deleted since it was only added
1710 assert_eq!(map.len(), 5);
1773 assert_eq!(map.len(), 5);
1711 assert_eq!(tracked_descendants(&map, b"some"), 1);
1774 assert_eq!(tracked_descendants(&map, b"some"), 1);
1712 assert_eq!(descendants_with_an_entry(&map, b"some"), 4);
1775 assert_eq!(descendants_with_an_entry(&map, b"some"), 4);
1713 assert_eq!(tracked_descendants(&map, b"some/nested"), 0);
1776 assert_eq!(tracked_descendants(&map, b"some/nested"), 0);
1714 assert_eq!(descendants_with_an_entry(&map, b"some/nested"), 1);
1777 assert_eq!(descendants_with_an_entry(&map, b"some/nested"), 1);
1715 assert_does_not_exist(&map, b"some/nested/path");
1778 assert_does_not_exist(&map, b"some/nested/path");
1716
1779
1717 map.set_untracked(p(b"root_file"))?;
1780 map.set_untracked(p(b"root_file"))?;
1718 // Untracked but not deleted
1781 // Untracked but not deleted
1719 assert_eq!(map.len(), 5);
1782 assert_eq!(map.len(), 5);
1720 assert!(map.get(p(b"root_file"))?.is_some());
1783 assert!(map.get(p(b"root_file"))?.is_some());
1721
1784
1722 map.set_untracked(p(b"some/file2"))?;
1785 map.set_untracked(p(b"some/file2"))?;
1723 assert_eq!(map.len(), 5);
1786 assert_eq!(map.len(), 5);
1724 assert_eq!(tracked_descendants(&map, b"some"), 0);
1787 assert_eq!(tracked_descendants(&map, b"some"), 0);
1725 assert!(map.get(p(b"some/file2"))?.is_some());
1788 assert!(map.get(p(b"some/file2"))?.is_some());
1726
1789
1727 map.set_untracked(p(b"some/file3"))?;
1790 map.set_untracked(p(b"some/file3"))?;
1728 assert_eq!(map.len(), 5);
1791 assert_eq!(map.len(), 5);
1729 assert_eq!(tracked_descendants(&map, b"some"), 0);
1792 assert_eq!(tracked_descendants(&map, b"some"), 0);
1730 assert!(map.get(p(b"some/file3"))?.is_some());
1793 assert!(map.get(p(b"some/file3"))?.is_some());
1731
1794
1732 Ok(())
1795 Ok(())
1733 }
1796 }
1734
1797
1735 /// Check that copies counter is correctly updated
1798 /// Check that copies counter is correctly updated
1736 #[test]
1799 #[test]
1737 fn test_copy_source() -> Result<(), DirstateError> {
1800 fn test_copy_source() -> Result<(), DirstateError> {
1738 let mut map = OwningDirstateMap::new_empty(vec![], None);
1801 let mut map = OwningDirstateMap::new_empty(vec![], None);
1739
1802
1740 // Clean file
1803 // Clean file
1741 map.reset_state(p(b"files/clean"), true, true, false, false, None)?;
1804 let reset = DirstateEntryReset {
1805 filename: p(b"files/clean"),
1806 wc_tracked: true,
1807 p1_tracked: true,
1808 p2_info: false,
1809 has_meaningful_mtime: false,
1810 parent_file_data_opt: None,
1811 from_empty: false,
1812 };
1813 map.reset_state(reset)?;
1742 // Merged file
1814 // Merged file
1743 map.reset_state(p(b"files/from_p2"), true, true, true, false, None)?;
1815 let reset = DirstateEntryReset {
1816 filename: p(b"files/from_p2"),
1817 wc_tracked: true,
1818 p1_tracked: true,
1819 p2_info: true,
1820 has_meaningful_mtime: false,
1821 parent_file_data_opt: None,
1822 from_empty: false,
1823 };
1824 map.reset_state(reset)?;
1744 // Removed file
1825 // Removed file
1745 map.reset_state(p(b"removed"), false, true, false, false, None)?;
1826 let reset = DirstateEntryReset {
1827 filename: p(b"removed"),
1828 wc_tracked: false,
1829 p1_tracked: true,
1830 p2_info: false,
1831 has_meaningful_mtime: false,
1832 parent_file_data_opt: None,
1833 from_empty: false,
1834 };
1835 map.reset_state(reset)?;
1746 // Added file
1836 // Added file
1747 map.reset_state(p(b"files/added"), true, false, false, false, None)?;
1837 let reset = DirstateEntryReset {
1838 filename: p(b"files/added"),
1839 wc_tracked: true,
1840 p1_tracked: false,
1841 p2_info: false,
1842 has_meaningful_mtime: false,
1843 parent_file_data_opt: None,
1844 from_empty: false,
1845 };
1846 map.reset_state(reset)?;
1748 // Add copy
1847 // Add copy
1749 map.copy_map_insert(p(b"files/clean"), p(b"clean_copy_source"))?;
1848 map.copy_map_insert(p(b"files/clean"), p(b"clean_copy_source"))?;
1750 assert_eq!(map.copy_map_len(), 1);
1849 assert_eq!(map.copy_map_len(), 1);
1751
1850
1752 // Copy override
1851 // Copy override
1753 map.copy_map_insert(p(b"files/clean"), p(b"other_clean_copy_source"))?;
1852 map.copy_map_insert(p(b"files/clean"), p(b"other_clean_copy_source"))?;
1754 assert_eq!(map.copy_map_len(), 1);
1853 assert_eq!(map.copy_map_len(), 1);
1755
1854
1756 // Multiple copies
1855 // Multiple copies
1757 map.copy_map_insert(p(b"removed"), p(b"removed_copy_source"))?;
1856 map.copy_map_insert(p(b"removed"), p(b"removed_copy_source"))?;
1758 assert_eq!(map.copy_map_len(), 2);
1857 assert_eq!(map.copy_map_len(), 2);
1759
1858
1760 map.copy_map_insert(p(b"files/added"), p(b"added_copy_source"))?;
1859 map.copy_map_insert(p(b"files/added"), p(b"added_copy_source"))?;
1761 assert_eq!(map.copy_map_len(), 3);
1860 assert_eq!(map.copy_map_len(), 3);
1762
1861
1763 // Added, so the entry is completely removed
1862 // Added, so the entry is completely removed
1764 map.set_untracked(p(b"files/added"))?;
1863 map.set_untracked(p(b"files/added"))?;
1765 assert_does_not_exist(&map, b"files/added");
1864 assert_does_not_exist(&map, b"files/added");
1766 assert_eq!(map.copy_map_len(), 2);
1865 assert_eq!(map.copy_map_len(), 2);
1767
1866
1768 // Removed, so the entry is kept around, so is its copy
1867 // Removed, so the entry is kept around, so is its copy
1769 map.set_untracked(p(b"removed"))?;
1868 map.set_untracked(p(b"removed"))?;
1770 assert!(map.get(p(b"removed"))?.is_some());
1869 assert!(map.get(p(b"removed"))?.is_some());
1771 assert_eq!(map.copy_map_len(), 2);
1870 assert_eq!(map.copy_map_len(), 2);
1772
1871
1773 // Clean, so the entry is kept around, but not its copy
1872 // Clean, so the entry is kept around, but not its copy
1774 map.set_untracked(p(b"files/clean"))?;
1873 map.set_untracked(p(b"files/clean"))?;
1775 assert!(map.get(p(b"files/clean"))?.is_some());
1874 assert!(map.get(p(b"files/clean"))?.is_some());
1776 assert_eq!(map.copy_map_len(), 1);
1875 assert_eq!(map.copy_map_len(), 1);
1777
1876
1778 map.copy_map_insert(p(b"files/from_p2"), p(b"from_p2_copy_source"))?;
1877 map.copy_map_insert(p(b"files/from_p2"), p(b"from_p2_copy_source"))?;
1779 assert_eq!(map.copy_map_len(), 2);
1878 assert_eq!(map.copy_map_len(), 2);
1780
1879
1781 // Info from p2, so its copy source info is kept around
1880 // Info from p2, so its copy source info is kept around
1782 map.set_untracked(p(b"files/from_p2"))?;
1881 map.set_untracked(p(b"files/from_p2"))?;
1783 assert!(map.get(p(b"files/from_p2"))?.is_some());
1882 assert!(map.get(p(b"files/from_p2"))?.is_some());
1784 assert_eq!(map.copy_map_len(), 2);
1883 assert_eq!(map.copy_map_len(), 2);
1785
1884
1786 Ok(())
1885 Ok(())
1787 }
1886 }
1788
1887
1789 /// Test with "on disk" data. For the sake of this test, the "on disk" data
1888 /// Test with "on disk" data. For the sake of this test, the "on disk" data
1790 /// does not actually come from the disk, but it's opaque to the code being
1889 /// does not actually come from the disk, but it's opaque to the code being
1791 /// tested.
1890 /// tested.
1792 #[test]
1891 #[test]
1793 fn test_on_disk() -> Result<(), DirstateError> {
1892 fn test_on_disk() -> Result<(), DirstateError> {
1794 // First let's create some data to put "on disk"
1893 // First let's create some data to put "on disk"
1795 let mut map = OwningDirstateMap::new_empty(vec![], None);
1894 let mut map = OwningDirstateMap::new_empty(vec![], None);
1796
1895
1797 // A file that was just added
1896 // A file that was just added
1798 map.set_tracked(p(b"some/nested/added"))?;
1897 map.set_tracked(p(b"some/nested/added"))?;
1799 map.copy_map_insert(p(b"some/nested/added"), p(b"added_copy_source"))?;
1898 map.copy_map_insert(p(b"some/nested/added"), p(b"added_copy_source"))?;
1800
1899
1801 // A file that was removed
1900 // A file that was removed
1802 map.reset_state(
1901 let reset = DirstateEntryReset {
1803 p(b"some/nested/removed"),
1902 filename: p(b"some/nested/removed"),
1804 false,
1903 wc_tracked: false,
1805 true,
1904 p1_tracked: true,
1806 false,
1905 p2_info: false,
1807 false,
1906 has_meaningful_mtime: false,
1808 None,
1907 parent_file_data_opt: None,
1809 )?;
1908 from_empty: false,
1909 };
1910 map.reset_state(reset)?;
1810 // Only present in p2
1911 // Only present in p2
1811 map.reset_state(
1912 let reset = DirstateEntryReset {
1812 p(b"other/p2_info_only"),
1913 filename: p(b"other/p2_info_only"),
1813 false,
1914 wc_tracked: false,
1814 false,
1915 p1_tracked: false,
1815 true,
1916 p2_info: true,
1816 false,
1917 has_meaningful_mtime: false,
1817 None,
1918 parent_file_data_opt: None,
1818 )?;
1919 from_empty: false,
1920 };
1921 map.reset_state(reset)?;
1819 map.copy_map_insert(
1922 map.copy_map_insert(
1820 p(b"other/p2_info_only"),
1923 p(b"other/p2_info_only"),
1821 p(b"other/p2_info_copy_source"),
1924 p(b"other/p2_info_copy_source"),
1822 )?;
1925 )?;
1823 // A file that was merged
1926 // A file that was merged
1824 map.reset_state(p(b"merged"), true, true, true, false, None)?;
1927 let reset = DirstateEntryReset {
1928 filename: p(b"merged"),
1929 wc_tracked: true,
1930 p1_tracked: true,
1931 p2_info: true,
1932 has_meaningful_mtime: false,
1933 parent_file_data_opt: None,
1934 from_empty: false,
1935 };
1936 map.reset_state(reset)?;
1825 // A file that is added, with info from p2
1937 // A file that is added, with info from p2
1826 // XXX is that actually possible?
1938 // XXX is that actually possible?
1827 map.reset_state(
1939 let reset = DirstateEntryReset {
1828 p(b"other/added_with_p2"),
1940 filename: p(b"other/added_with_p2"),
1829 true,
1941 wc_tracked: true,
1830 false,
1942 p1_tracked: false,
1831 true,
1943 p2_info: true,
1832 false,
1944 has_meaningful_mtime: false,
1833 None,
1945 parent_file_data_opt: None,
1834 )?;
1946 from_empty: false,
1947 };
1948 map.reset_state(reset)?;
1835 // One layer without any files to test deletion cascade
1949 // One layer without any files to test deletion cascade
1836 // A clean file
1950 // A clean file
1837 map.reset_state(
1951 let reset = DirstateEntryReset {
1838 p(b"some/other/nested/clean"),
1952 filename: p(b"some/other/nested/clean"),
1839 true,
1953 wc_tracked: true,
1840 true,
1954 p1_tracked: true,
1841 false,
1955 p2_info: false,
1842 false,
1956 has_meaningful_mtime: false,
1843 None,
1957 parent_file_data_opt: None,
1844 )?;
1958 from_empty: false,
1959 };
1960 map.reset_state(reset)?;
1845
1961
1846 let (packed, metadata, _should_append, _old_data_size) =
1962 let (packed, metadata, _should_append, _old_data_size) =
1847 map.pack_v2(DirstateMapWriteMode::ForceNewDataFile)?;
1963 map.pack_v2(DirstateMapWriteMode::ForceNewDataFile)?;
1848 let packed_len = packed.len();
1964 let packed_len = packed.len();
1849 assert!(packed_len > 0);
1965 assert!(packed_len > 0);
1850
1966
1851 // Recreate "from disk"
1967 // Recreate "from disk"
1852 let mut map = OwningDirstateMap::new_v2(
1968 let mut map = OwningDirstateMap::new_v2(
1853 packed,
1969 packed,
1854 packed_len,
1970 packed_len,
1855 metadata.as_bytes(),
1971 metadata.as_bytes(),
1856 vec![],
1972 vec![],
1857 None,
1973 None,
1858 )?;
1974 )?;
1859
1975
1860 // Check that everything is accounted for
1976 // Check that everything is accounted for
1861 assert!(map.contains_key(p(b"some/nested/added"))?);
1977 assert!(map.contains_key(p(b"some/nested/added"))?);
1862 assert!(map.contains_key(p(b"some/nested/removed"))?);
1978 assert!(map.contains_key(p(b"some/nested/removed"))?);
1863 assert!(map.contains_key(p(b"merged"))?);
1979 assert!(map.contains_key(p(b"merged"))?);
1864 assert!(map.contains_key(p(b"other/p2_info_only"))?);
1980 assert!(map.contains_key(p(b"other/p2_info_only"))?);
1865 assert!(map.contains_key(p(b"other/added_with_p2"))?);
1981 assert!(map.contains_key(p(b"other/added_with_p2"))?);
1866 assert!(map.contains_key(p(b"some/other/nested/clean"))?);
1982 assert!(map.contains_key(p(b"some/other/nested/clean"))?);
1867 assert_eq!(
1983 assert_eq!(
1868 map.copy_map_get(p(b"some/nested/added"))?,
1984 map.copy_map_get(p(b"some/nested/added"))?,
1869 Some(p(b"added_copy_source"))
1985 Some(p(b"added_copy_source"))
1870 );
1986 );
1871 assert_eq!(
1987 assert_eq!(
1872 map.copy_map_get(p(b"other/p2_info_only"))?,
1988 map.copy_map_get(p(b"other/p2_info_only"))?,
1873 Some(p(b"other/p2_info_copy_source"))
1989 Some(p(b"other/p2_info_copy_source"))
1874 );
1990 );
1875 assert_eq!(tracked_descendants(&map, b"some"), 2);
1991 assert_eq!(tracked_descendants(&map, b"some"), 2);
1876 assert_eq!(descendants_with_an_entry(&map, b"some"), 3);
1992 assert_eq!(descendants_with_an_entry(&map, b"some"), 3);
1877 assert_eq!(tracked_descendants(&map, b"other"), 1);
1993 assert_eq!(tracked_descendants(&map, b"other"), 1);
1878 assert_eq!(descendants_with_an_entry(&map, b"other"), 2);
1994 assert_eq!(descendants_with_an_entry(&map, b"other"), 2);
1879 assert_eq!(tracked_descendants(&map, b"some/other"), 1);
1995 assert_eq!(tracked_descendants(&map, b"some/other"), 1);
1880 assert_eq!(descendants_with_an_entry(&map, b"some/other"), 1);
1996 assert_eq!(descendants_with_an_entry(&map, b"some/other"), 1);
1881 assert_eq!(tracked_descendants(&map, b"some/other/nested"), 1);
1997 assert_eq!(tracked_descendants(&map, b"some/other/nested"), 1);
1882 assert_eq!(descendants_with_an_entry(&map, b"some/other/nested"), 1);
1998 assert_eq!(descendants_with_an_entry(&map, b"some/other/nested"), 1);
1883 assert_eq!(tracked_descendants(&map, b"some/nested"), 1);
1999 assert_eq!(tracked_descendants(&map, b"some/nested"), 1);
1884 assert_eq!(descendants_with_an_entry(&map, b"some/nested"), 2);
2000 assert_eq!(descendants_with_an_entry(&map, b"some/nested"), 2);
1885 assert_eq!(map.len(), 6);
2001 assert_eq!(map.len(), 6);
1886 assert_eq!(map.get_map().unreachable_bytes, 0);
2002 assert_eq!(map.get_map().unreachable_bytes, 0);
1887 assert_eq!(map.copy_map_len(), 2);
2003 assert_eq!(map.copy_map_len(), 2);
1888
2004
1889 // Shouldn't change anything since it's already not tracked
2005 // Shouldn't change anything since it's already not tracked
1890 map.set_untracked(p(b"some/nested/removed"))?;
2006 map.set_untracked(p(b"some/nested/removed"))?;
1891 assert_eq!(map.get_map().unreachable_bytes, 0);
2007 assert_eq!(map.get_map().unreachable_bytes, 0);
1892
2008
1893 if let ChildNodes::InMemory(_) = map.get_map().root {
2009 if let ChildNodes::InMemory(_) = map.get_map().root {
1894 panic!("root should not have been mutated")
2010 panic!("root should not have been mutated")
1895 }
2011 }
1896 // We haven't mutated enough (nothing, actually), we should still be in
2012 // We haven't mutated enough (nothing, actually), we should still be in
1897 // the append strategy
2013 // the append strategy
1898 assert!(map.get_map().write_should_append());
2014 assert!(map.get_map().write_should_append());
1899
2015
1900 // But this mutates the structure, so there should be unreachable_bytes
2016 // But this mutates the structure, so there should be unreachable_bytes
1901 assert!(map.set_untracked(p(b"some/nested/added"))?);
2017 assert!(map.set_untracked(p(b"some/nested/added"))?);
1902 let unreachable_bytes = map.get_map().unreachable_bytes;
2018 let unreachable_bytes = map.get_map().unreachable_bytes;
1903 assert!(unreachable_bytes > 0);
2019 assert!(unreachable_bytes > 0);
1904
2020
1905 if let ChildNodes::OnDisk(_) = map.get_map().root {
2021 if let ChildNodes::OnDisk(_) = map.get_map().root {
1906 panic!("root should have been mutated")
2022 panic!("root should have been mutated")
1907 }
2023 }
1908
2024
1909 // This should not mutate the structure either, since `root` has
2025 // This should not mutate the structure either, since `root` has
1910 // already been mutated along with its direct children.
2026 // already been mutated along with its direct children.
1911 map.set_untracked(p(b"merged"))?;
2027 map.set_untracked(p(b"merged"))?;
1912 assert_eq!(map.get_map().unreachable_bytes, unreachable_bytes);
2028 assert_eq!(map.get_map().unreachable_bytes, unreachable_bytes);
1913
2029
1914 if let NodeRef::InMemory(_, _) =
2030 if let NodeRef::InMemory(_, _) =
1915 map.get_map().get_node(p(b"other/added_with_p2"))?.unwrap()
2031 map.get_map().get_node(p(b"other/added_with_p2"))?.unwrap()
1916 {
2032 {
1917 panic!("'other/added_with_p2' should not have been mutated")
2033 panic!("'other/added_with_p2' should not have been mutated")
1918 }
2034 }
1919 // But this should, since it's in a different path
2035 // But this should, since it's in a different path
1920 // than `<root>some/nested/add`
2036 // than `<root>some/nested/add`
1921 map.set_untracked(p(b"other/added_with_p2"))?;
2037 map.set_untracked(p(b"other/added_with_p2"))?;
1922 assert!(map.get_map().unreachable_bytes > unreachable_bytes);
2038 assert!(map.get_map().unreachable_bytes > unreachable_bytes);
1923
2039
1924 if let NodeRef::OnDisk(_) =
2040 if let NodeRef::OnDisk(_) =
1925 map.get_map().get_node(p(b"other/added_with_p2"))?.unwrap()
2041 map.get_map().get_node(p(b"other/added_with_p2"))?.unwrap()
1926 {
2042 {
1927 panic!("'other/added_with_p2' should have been mutated")
2043 panic!("'other/added_with_p2' should have been mutated")
1928 }
2044 }
1929
2045
1930 // We have rewritten most of the tree, we should create a new file
2046 // We have rewritten most of the tree, we should create a new file
1931 assert!(!map.get_map().write_should_append());
2047 assert!(!map.get_map().write_should_append());
1932
2048
1933 Ok(())
2049 Ok(())
1934 }
2050 }
1935 }
2051 }
@@ -1,556 +1,561
1 // dirstate_map.rs
1 // dirstate_map.rs
2 //
2 //
3 // Copyright 2019 Raphaël Gomès <rgomes@octobus.net>
3 // Copyright 2019 Raphaël Gomès <rgomes@octobus.net>
4 //
4 //
5 // This software may be used and distributed according to the terms of the
5 // This software may be used and distributed according to the terms of the
6 // GNU General Public License version 2 or any later version.
6 // GNU General Public License version 2 or any later version.
7
7
8 //! Bindings for the `hg::dirstate::dirstate_map` file provided by the
8 //! Bindings for the `hg::dirstate::dirstate_map` file provided by the
9 //! `hg-core` package.
9 //! `hg-core` package.
10
10
11 use std::cell::{RefCell, RefMut};
11 use std::cell::{RefCell, RefMut};
12
12
13 use cpython::{
13 use cpython::{
14 exc, PyBool, PyBytes, PyClone, PyDict, PyErr, PyList, PyNone, PyObject,
14 exc, PyBool, PyBytes, PyClone, PyDict, PyErr, PyList, PyNone, PyObject,
15 PyResult, Python, PythonObject, ToPyObject, UnsafePyLeaked,
15 PyResult, Python, PythonObject, ToPyObject, UnsafePyLeaked,
16 };
16 };
17 use hg::dirstate::{ParentFileData, TruncatedTimestamp};
17 use hg::{
18 dirstate::{ParentFileData, TruncatedTimestamp},
19 dirstate_tree::dirstate_map::DirstateEntryReset,
20 };
18
21
19 use crate::{
22 use crate::{
20 dirstate::copymap::{CopyMap, CopyMapItemsIterator, CopyMapKeysIterator},
23 dirstate::copymap::{CopyMap, CopyMapItemsIterator, CopyMapKeysIterator},
21 dirstate::item::DirstateItem,
24 dirstate::item::DirstateItem,
22 pybytes_deref::PyBytesDeref,
25 pybytes_deref::PyBytesDeref,
23 };
26 };
24 use hg::{
27 use hg::{
25 dirstate::StateMapIter, dirstate_tree::dirstate_map::DirstateMapWriteMode,
28 dirstate::StateMapIter, dirstate_tree::dirstate_map::DirstateMapWriteMode,
26 dirstate_tree::on_disk::DirstateV2ParseError,
29 dirstate_tree::on_disk::DirstateV2ParseError,
27 dirstate_tree::owning::OwningDirstateMap, revlog::Node,
30 dirstate_tree::owning::OwningDirstateMap, revlog::Node,
28 utils::files::normalize_case, utils::hg_path::HgPath, DirstateEntry,
31 utils::files::normalize_case, utils::hg_path::HgPath, DirstateEntry,
29 DirstateError, DirstateParents,
32 DirstateError, DirstateParents,
30 };
33 };
31
34
32 // TODO
35 // TODO
33 // This object needs to share references to multiple members of its Rust
36 // This object needs to share references to multiple members of its Rust
34 // inner struct, namely `copy_map`, `dirs` and `all_dirs`.
37 // inner struct, namely `copy_map`, `dirs` and `all_dirs`.
35 // Right now `CopyMap` is done, but it needs to have an explicit reference
38 // Right now `CopyMap` is done, but it needs to have an explicit reference
36 // to `RustDirstateMap` which itself needs to have an encapsulation for
39 // to `RustDirstateMap` which itself needs to have an encapsulation for
37 // every method in `CopyMap` (copymapcopy, etc.).
40 // every method in `CopyMap` (copymapcopy, etc.).
38 // This is ugly and hard to maintain.
41 // This is ugly and hard to maintain.
39 // The same logic applies to `dirs` and `all_dirs`, however the `Dirs`
42 // The same logic applies to `dirs` and `all_dirs`, however the `Dirs`
40 // `py_class!` is already implemented and does not mention
43 // `py_class!` is already implemented and does not mention
41 // `RustDirstateMap`, rightfully so.
44 // `RustDirstateMap`, rightfully so.
42 // All attributes also have to have a separate refcount data attribute for
45 // All attributes also have to have a separate refcount data attribute for
43 // leaks, with all methods that go along for reference sharing.
46 // leaks, with all methods that go along for reference sharing.
44 py_class!(pub class DirstateMap |py| {
47 py_class!(pub class DirstateMap |py| {
45 @shared data inner: OwningDirstateMap;
48 @shared data inner: OwningDirstateMap;
46
49
47 /// Returns a `(dirstate_map, parents)` tuple
50 /// Returns a `(dirstate_map, parents)` tuple
48 @staticmethod
51 @staticmethod
49 def new_v1(
52 def new_v1(
50 on_disk: PyBytes,
53 on_disk: PyBytes,
51 identity: Option<u64>,
54 identity: Option<u64>,
52 ) -> PyResult<PyObject> {
55 ) -> PyResult<PyObject> {
53 let on_disk = PyBytesDeref::new(py, on_disk);
56 let on_disk = PyBytesDeref::new(py, on_disk);
54 let (map, parents) = OwningDirstateMap::new_v1(on_disk, identity)
57 let (map, parents) = OwningDirstateMap::new_v1(on_disk, identity)
55 .map_err(|e| dirstate_error(py, e))?;
58 .map_err(|e| dirstate_error(py, e))?;
56 let map = Self::create_instance(py, map)?;
59 let map = Self::create_instance(py, map)?;
57 let p1 = PyBytes::new(py, parents.p1.as_bytes());
60 let p1 = PyBytes::new(py, parents.p1.as_bytes());
58 let p2 = PyBytes::new(py, parents.p2.as_bytes());
61 let p2 = PyBytes::new(py, parents.p2.as_bytes());
59 let parents = (p1, p2);
62 let parents = (p1, p2);
60 Ok((map, parents).to_py_object(py).into_object())
63 Ok((map, parents).to_py_object(py).into_object())
61 }
64 }
62
65
63 /// Returns a DirstateMap
66 /// Returns a DirstateMap
64 @staticmethod
67 @staticmethod
65 def new_v2(
68 def new_v2(
66 on_disk: PyBytes,
69 on_disk: PyBytes,
67 data_size: usize,
70 data_size: usize,
68 tree_metadata: PyBytes,
71 tree_metadata: PyBytes,
69 uuid: PyBytes,
72 uuid: PyBytes,
70 identity: Option<u64>,
73 identity: Option<u64>,
71 ) -> PyResult<PyObject> {
74 ) -> PyResult<PyObject> {
72 let dirstate_error = |e: DirstateError| {
75 let dirstate_error = |e: DirstateError| {
73 PyErr::new::<exc::OSError, _>(py, format!("Dirstate error: {:?}", e))
76 PyErr::new::<exc::OSError, _>(py, format!("Dirstate error: {:?}", e))
74 };
77 };
75 let on_disk = PyBytesDeref::new(py, on_disk);
78 let on_disk = PyBytesDeref::new(py, on_disk);
76 let uuid = uuid.data(py);
79 let uuid = uuid.data(py);
77 let map = OwningDirstateMap::new_v2(
80 let map = OwningDirstateMap::new_v2(
78 on_disk,
81 on_disk,
79 data_size,
82 data_size,
80 tree_metadata.data(py),
83 tree_metadata.data(py),
81 uuid.to_owned(),
84 uuid.to_owned(),
82 identity,
85 identity,
83 ).map_err(dirstate_error)?;
86 ).map_err(dirstate_error)?;
84 let map = Self::create_instance(py, map)?;
87 let map = Self::create_instance(py, map)?;
85 Ok(map.into_object())
88 Ok(map.into_object())
86 }
89 }
87
90
88 /// Returns an empty DirstateMap. Only used for a new dirstate.
91 /// Returns an empty DirstateMap. Only used for a new dirstate.
89 @staticmethod
92 @staticmethod
90 def new_empty() -> PyResult<PyObject> {
93 def new_empty() -> PyResult<PyObject> {
91 let map = OwningDirstateMap::new_empty(vec![], None);
94 let map = OwningDirstateMap::new_empty(vec![], None);
92 let map = Self::create_instance(py, map)?;
95 let map = Self::create_instance(py, map)?;
93 Ok(map.into_object())
96 Ok(map.into_object())
94 }
97 }
95
98
96 def clear(&self) -> PyResult<PyObject> {
99 def clear(&self) -> PyResult<PyObject> {
97 self.inner(py).borrow_mut().clear();
100 self.inner(py).borrow_mut().clear();
98 Ok(py.None())
101 Ok(py.None())
99 }
102 }
100
103
101 def get(
104 def get(
102 &self,
105 &self,
103 key: PyObject,
106 key: PyObject,
104 default: Option<PyObject> = None
107 default: Option<PyObject> = None
105 ) -> PyResult<Option<PyObject>> {
108 ) -> PyResult<Option<PyObject>> {
106 let key = key.extract::<PyBytes>(py)?;
109 let key = key.extract::<PyBytes>(py)?;
107 match self
110 match self
108 .inner(py)
111 .inner(py)
109 .borrow()
112 .borrow()
110 .get(HgPath::new(key.data(py)))
113 .get(HgPath::new(key.data(py)))
111 .map_err(|e| v2_error(py, e))?
114 .map_err(|e| v2_error(py, e))?
112 {
115 {
113 Some(entry) => {
116 Some(entry) => {
114 Ok(Some(DirstateItem::new_as_pyobject(py, entry)?))
117 Ok(Some(DirstateItem::new_as_pyobject(py, entry)?))
115 },
118 },
116 None => Ok(default)
119 None => Ok(default)
117 }
120 }
118 }
121 }
119
122
120 def set_tracked(&self, f: PyObject) -> PyResult<PyBool> {
123 def set_tracked(&self, f: PyObject) -> PyResult<PyBool> {
121 let bytes = f.extract::<PyBytes>(py)?;
124 let bytes = f.extract::<PyBytes>(py)?;
122 let path = HgPath::new(bytes.data(py));
125 let path = HgPath::new(bytes.data(py));
123 let res = self.inner(py).borrow_mut().set_tracked(path);
126 let res = self.inner(py).borrow_mut().set_tracked(path);
124 let was_tracked = res.map_err(|_| PyErr::new::<exc::OSError, _>(py, "Dirstate error".to_string()))?;
127 let was_tracked = res.map_err(|_| PyErr::new::<exc::OSError, _>(py, "Dirstate error".to_string()))?;
125 Ok(was_tracked.to_py_object(py))
128 Ok(was_tracked.to_py_object(py))
126 }
129 }
127
130
128 def set_untracked(&self, f: PyObject) -> PyResult<PyBool> {
131 def set_untracked(&self, f: PyObject) -> PyResult<PyBool> {
129 let bytes = f.extract::<PyBytes>(py)?;
132 let bytes = f.extract::<PyBytes>(py)?;
130 let path = HgPath::new(bytes.data(py));
133 let path = HgPath::new(bytes.data(py));
131 let res = self.inner(py).borrow_mut().set_untracked(path);
134 let res = self.inner(py).borrow_mut().set_untracked(path);
132 let was_tracked = res.map_err(|_| PyErr::new::<exc::OSError, _>(py, "Dirstate error".to_string()))?;
135 let was_tracked = res.map_err(|_| PyErr::new::<exc::OSError, _>(py, "Dirstate error".to_string()))?;
133 Ok(was_tracked.to_py_object(py))
136 Ok(was_tracked.to_py_object(py))
134 }
137 }
135
138
136 def set_clean(
139 def set_clean(
137 &self,
140 &self,
138 f: PyObject,
141 f: PyObject,
139 mode: u32,
142 mode: u32,
140 size: u32,
143 size: u32,
141 mtime: (i64, u32, bool)
144 mtime: (i64, u32, bool)
142 ) -> PyResult<PyNone> {
145 ) -> PyResult<PyNone> {
143 let (mtime_s, mtime_ns, second_ambiguous) = mtime;
146 let (mtime_s, mtime_ns, second_ambiguous) = mtime;
144 let timestamp = TruncatedTimestamp::new_truncate(
147 let timestamp = TruncatedTimestamp::new_truncate(
145 mtime_s, mtime_ns, second_ambiguous
148 mtime_s, mtime_ns, second_ambiguous
146 );
149 );
147 let bytes = f.extract::<PyBytes>(py)?;
150 let bytes = f.extract::<PyBytes>(py)?;
148 let path = HgPath::new(bytes.data(py));
151 let path = HgPath::new(bytes.data(py));
149 let res = self.inner(py).borrow_mut().set_clean(
152 let res = self.inner(py).borrow_mut().set_clean(
150 path, mode, size, timestamp,
153 path, mode, size, timestamp,
151 );
154 );
152 res.map_err(|_| PyErr::new::<exc::OSError, _>(py, "Dirstate error".to_string()))?;
155 res.map_err(|_| PyErr::new::<exc::OSError, _>(py, "Dirstate error".to_string()))?;
153 Ok(PyNone)
156 Ok(PyNone)
154 }
157 }
155
158
156 def set_possibly_dirty(&self, f: PyObject) -> PyResult<PyNone> {
159 def set_possibly_dirty(&self, f: PyObject) -> PyResult<PyNone> {
157 let bytes = f.extract::<PyBytes>(py)?;
160 let bytes = f.extract::<PyBytes>(py)?;
158 let path = HgPath::new(bytes.data(py));
161 let path = HgPath::new(bytes.data(py));
159 let res = self.inner(py).borrow_mut().set_possibly_dirty(path);
162 let res = self.inner(py).borrow_mut().set_possibly_dirty(path);
160 res.map_err(|_| PyErr::new::<exc::OSError, _>(py, "Dirstate error".to_string()))?;
163 res.map_err(|_| PyErr::new::<exc::OSError, _>(py, "Dirstate error".to_string()))?;
161 Ok(PyNone)
164 Ok(PyNone)
162 }
165 }
163
166
164 def reset_state(
167 def reset_state(
165 &self,
168 &self,
166 f: PyObject,
169 f: PyObject,
167 wc_tracked: bool,
170 wc_tracked: bool,
168 p1_tracked: bool,
171 p1_tracked: bool,
169 p2_info: bool,
172 p2_info: bool,
170 has_meaningful_mtime: bool,
173 has_meaningful_mtime: bool,
171 parentfiledata: Option<(u32, u32, Option<(i64, u32, bool)>)>,
174 parentfiledata: Option<(u32, u32, Option<(i64, u32, bool)>)>,
172 ) -> PyResult<PyNone> {
175 ) -> PyResult<PyNone> {
173 let mut has_meaningful_mtime = has_meaningful_mtime;
176 let mut has_meaningful_mtime = has_meaningful_mtime;
174 let parent_file_data = match parentfiledata {
177 let parent_file_data = match parentfiledata {
175 None => {
178 None => {
176 has_meaningful_mtime = false;
179 has_meaningful_mtime = false;
177 None
180 None
178 },
181 },
179 Some(data) => {
182 Some(data) => {
180 let (mode, size, mtime_info) = data;
183 let (mode, size, mtime_info) = data;
181 let mtime = if let Some(mtime_info) = mtime_info {
184 let mtime = if let Some(mtime_info) = mtime_info {
182 let (mtime_s, mtime_ns, second_ambiguous) = mtime_info;
185 let (mtime_s, mtime_ns, second_ambiguous) = mtime_info;
183 let timestamp = TruncatedTimestamp::new_truncate(
186 let timestamp = TruncatedTimestamp::new_truncate(
184 mtime_s, mtime_ns, second_ambiguous
187 mtime_s, mtime_ns, second_ambiguous
185 );
188 );
186 Some(timestamp)
189 Some(timestamp)
187 } else {
190 } else {
188 has_meaningful_mtime = false;
191 has_meaningful_mtime = false;
189 None
192 None
190 };
193 };
191 Some(ParentFileData {
194 Some(ParentFileData {
192 mode_size: Some((mode, size)),
195 mode_size: Some((mode, size)),
193 mtime,
196 mtime,
194 })
197 })
195 }
198 }
196 };
199 };
197 let bytes = f.extract::<PyBytes>(py)?;
200 let bytes = f.extract::<PyBytes>(py)?;
198 let path = HgPath::new(bytes.data(py));
201 let path = HgPath::new(bytes.data(py));
199 let res = self.inner(py).borrow_mut().reset_state(
202 let reset = DirstateEntryReset {
200 path,
203 filename: path,
201 wc_tracked,
204 wc_tracked,
202 p1_tracked,
205 p1_tracked,
203 p2_info,
206 p2_info,
204 has_meaningful_mtime,
207 has_meaningful_mtime,
205 parent_file_data,
208 parent_file_data_opt: parent_file_data,
206 );
209 from_empty: false
210 };
211 let res = self.inner(py).borrow_mut().reset_state(reset);
207 res.map_err(|_| PyErr::new::<exc::OSError, _>(py, "Dirstate error".to_string()))?;
212 res.map_err(|_| PyErr::new::<exc::OSError, _>(py, "Dirstate error".to_string()))?;
208 Ok(PyNone)
213 Ok(PyNone)
209 }
214 }
210
215
211 def hastrackeddir(&self, d: PyObject) -> PyResult<PyBool> {
216 def hastrackeddir(&self, d: PyObject) -> PyResult<PyBool> {
212 let d = d.extract::<PyBytes>(py)?;
217 let d = d.extract::<PyBytes>(py)?;
213 Ok(self.inner(py).borrow_mut()
218 Ok(self.inner(py).borrow_mut()
214 .has_tracked_dir(HgPath::new(d.data(py)))
219 .has_tracked_dir(HgPath::new(d.data(py)))
215 .map_err(|e| {
220 .map_err(|e| {
216 PyErr::new::<exc::ValueError, _>(py, e.to_string())
221 PyErr::new::<exc::ValueError, _>(py, e.to_string())
217 })?
222 })?
218 .to_py_object(py))
223 .to_py_object(py))
219 }
224 }
220
225
221 def hasdir(&self, d: PyObject) -> PyResult<PyBool> {
226 def hasdir(&self, d: PyObject) -> PyResult<PyBool> {
222 let d = d.extract::<PyBytes>(py)?;
227 let d = d.extract::<PyBytes>(py)?;
223 Ok(self.inner(py).borrow_mut()
228 Ok(self.inner(py).borrow_mut()
224 .has_dir(HgPath::new(d.data(py)))
229 .has_dir(HgPath::new(d.data(py)))
225 .map_err(|e| {
230 .map_err(|e| {
226 PyErr::new::<exc::ValueError, _>(py, e.to_string())
231 PyErr::new::<exc::ValueError, _>(py, e.to_string())
227 })?
232 })?
228 .to_py_object(py))
233 .to_py_object(py))
229 }
234 }
230
235
231 def write_v1(
236 def write_v1(
232 &self,
237 &self,
233 p1: PyObject,
238 p1: PyObject,
234 p2: PyObject,
239 p2: PyObject,
235 ) -> PyResult<PyBytes> {
240 ) -> PyResult<PyBytes> {
236 let inner = self.inner(py).borrow();
241 let inner = self.inner(py).borrow();
237 let parents = DirstateParents {
242 let parents = DirstateParents {
238 p1: extract_node_id(py, &p1)?,
243 p1: extract_node_id(py, &p1)?,
239 p2: extract_node_id(py, &p2)?,
244 p2: extract_node_id(py, &p2)?,
240 };
245 };
241 let result = inner.pack_v1(parents);
246 let result = inner.pack_v1(parents);
242 match result {
247 match result {
243 Ok(packed) => Ok(PyBytes::new(py, &packed)),
248 Ok(packed) => Ok(PyBytes::new(py, &packed)),
244 Err(_) => Err(PyErr::new::<exc::OSError, _>(
249 Err(_) => Err(PyErr::new::<exc::OSError, _>(
245 py,
250 py,
246 "Dirstate error".to_string(),
251 "Dirstate error".to_string(),
247 )),
252 )),
248 }
253 }
249 }
254 }
250
255
251 /// Returns new data together with whether that data should be appended to
256 /// Returns new data together with whether that data should be appended to
252 /// the existing data file whose content is at `self.on_disk` (True),
257 /// the existing data file whose content is at `self.on_disk` (True),
253 /// instead of written to a new data file (False).
258 /// instead of written to a new data file (False).
254 def write_v2(
259 def write_v2(
255 &self,
260 &self,
256 write_mode: usize,
261 write_mode: usize,
257 ) -> PyResult<PyObject> {
262 ) -> PyResult<PyObject> {
258 let inner = self.inner(py).borrow();
263 let inner = self.inner(py).borrow();
259 let rust_write_mode = match write_mode {
264 let rust_write_mode = match write_mode {
260 0 => DirstateMapWriteMode::Auto,
265 0 => DirstateMapWriteMode::Auto,
261 1 => DirstateMapWriteMode::ForceNewDataFile,
266 1 => DirstateMapWriteMode::ForceNewDataFile,
262 2 => DirstateMapWriteMode::ForceAppend,
267 2 => DirstateMapWriteMode::ForceAppend,
263 _ => DirstateMapWriteMode::Auto, // XXX should we error out?
268 _ => DirstateMapWriteMode::Auto, // XXX should we error out?
264 };
269 };
265 let result = inner.pack_v2(rust_write_mode);
270 let result = inner.pack_v2(rust_write_mode);
266 match result {
271 match result {
267 Ok((packed, tree_metadata, append, _old_data_size)) => {
272 Ok((packed, tree_metadata, append, _old_data_size)) => {
268 let packed = PyBytes::new(py, &packed);
273 let packed = PyBytes::new(py, &packed);
269 let tree_metadata = PyBytes::new(py, tree_metadata.as_bytes());
274 let tree_metadata = PyBytes::new(py, tree_metadata.as_bytes());
270 let tuple = (packed, tree_metadata, append);
275 let tuple = (packed, tree_metadata, append);
271 Ok(tuple.to_py_object(py).into_object())
276 Ok(tuple.to_py_object(py).into_object())
272 },
277 },
273 Err(e) => Err(PyErr::new::<exc::OSError, _>(
278 Err(e) => Err(PyErr::new::<exc::OSError, _>(
274 py,
279 py,
275 e.to_string(),
280 e.to_string(),
276 )),
281 )),
277 }
282 }
278 }
283 }
279
284
280 def filefoldmapasdict(&self) -> PyResult<PyDict> {
285 def filefoldmapasdict(&self) -> PyResult<PyDict> {
281 let dict = PyDict::new(py);
286 let dict = PyDict::new(py);
282 for item in self.inner(py).borrow_mut().iter() {
287 for item in self.inner(py).borrow_mut().iter() {
283 let (path, entry) = item.map_err(|e| v2_error(py, e))?;
288 let (path, entry) = item.map_err(|e| v2_error(py, e))?;
284 if !entry.removed() {
289 if !entry.removed() {
285 let key = normalize_case(path);
290 let key = normalize_case(path);
286 let value = path;
291 let value = path;
287 dict.set_item(
292 dict.set_item(
288 py,
293 py,
289 PyBytes::new(py, key.as_bytes()).into_object(),
294 PyBytes::new(py, key.as_bytes()).into_object(),
290 PyBytes::new(py, value.as_bytes()).into_object(),
295 PyBytes::new(py, value.as_bytes()).into_object(),
291 )?;
296 )?;
292 }
297 }
293 }
298 }
294 Ok(dict)
299 Ok(dict)
295 }
300 }
296
301
297 def __len__(&self) -> PyResult<usize> {
302 def __len__(&self) -> PyResult<usize> {
298 Ok(self.inner(py).borrow().len())
303 Ok(self.inner(py).borrow().len())
299 }
304 }
300
305
301 def __contains__(&self, key: PyObject) -> PyResult<bool> {
306 def __contains__(&self, key: PyObject) -> PyResult<bool> {
302 let key = key.extract::<PyBytes>(py)?;
307 let key = key.extract::<PyBytes>(py)?;
303 self.inner(py)
308 self.inner(py)
304 .borrow()
309 .borrow()
305 .contains_key(HgPath::new(key.data(py)))
310 .contains_key(HgPath::new(key.data(py)))
306 .map_err(|e| v2_error(py, e))
311 .map_err(|e| v2_error(py, e))
307 }
312 }
308
313
309 def __getitem__(&self, key: PyObject) -> PyResult<PyObject> {
314 def __getitem__(&self, key: PyObject) -> PyResult<PyObject> {
310 let key = key.extract::<PyBytes>(py)?;
315 let key = key.extract::<PyBytes>(py)?;
311 let key = HgPath::new(key.data(py));
316 let key = HgPath::new(key.data(py));
312 match self
317 match self
313 .inner(py)
318 .inner(py)
314 .borrow()
319 .borrow()
315 .get(key)
320 .get(key)
316 .map_err(|e| v2_error(py, e))?
321 .map_err(|e| v2_error(py, e))?
317 {
322 {
318 Some(entry) => {
323 Some(entry) => {
319 Ok(DirstateItem::new_as_pyobject(py, entry)?)
324 Ok(DirstateItem::new_as_pyobject(py, entry)?)
320 },
325 },
321 None => Err(PyErr::new::<exc::KeyError, _>(
326 None => Err(PyErr::new::<exc::KeyError, _>(
322 py,
327 py,
323 String::from_utf8_lossy(key.as_bytes()),
328 String::from_utf8_lossy(key.as_bytes()),
324 )),
329 )),
325 }
330 }
326 }
331 }
327
332
328 def keys(&self) -> PyResult<DirstateMapKeysIterator> {
333 def keys(&self) -> PyResult<DirstateMapKeysIterator> {
329 let leaked_ref = self.inner(py).leak_immutable();
334 let leaked_ref = self.inner(py).leak_immutable();
330 DirstateMapKeysIterator::from_inner(
335 DirstateMapKeysIterator::from_inner(
331 py,
336 py,
332 unsafe { leaked_ref.map(py, |o| o.iter()) },
337 unsafe { leaked_ref.map(py, |o| o.iter()) },
333 )
338 )
334 }
339 }
335
340
336 def items(&self) -> PyResult<DirstateMapItemsIterator> {
341 def items(&self) -> PyResult<DirstateMapItemsIterator> {
337 let leaked_ref = self.inner(py).leak_immutable();
342 let leaked_ref = self.inner(py).leak_immutable();
338 DirstateMapItemsIterator::from_inner(
343 DirstateMapItemsIterator::from_inner(
339 py,
344 py,
340 unsafe { leaked_ref.map(py, |o| o.iter()) },
345 unsafe { leaked_ref.map(py, |o| o.iter()) },
341 )
346 )
342 }
347 }
343
348
344 def __iter__(&self) -> PyResult<DirstateMapKeysIterator> {
349 def __iter__(&self) -> PyResult<DirstateMapKeysIterator> {
345 let leaked_ref = self.inner(py).leak_immutable();
350 let leaked_ref = self.inner(py).leak_immutable();
346 DirstateMapKeysIterator::from_inner(
351 DirstateMapKeysIterator::from_inner(
347 py,
352 py,
348 unsafe { leaked_ref.map(py, |o| o.iter()) },
353 unsafe { leaked_ref.map(py, |o| o.iter()) },
349 )
354 )
350 }
355 }
351
356
352 // TODO all copymap* methods, see docstring above
357 // TODO all copymap* methods, see docstring above
353 def copymapcopy(&self) -> PyResult<PyDict> {
358 def copymapcopy(&self) -> PyResult<PyDict> {
354 let dict = PyDict::new(py);
359 let dict = PyDict::new(py);
355 for item in self.inner(py).borrow().copy_map_iter() {
360 for item in self.inner(py).borrow().copy_map_iter() {
356 let (key, value) = item.map_err(|e| v2_error(py, e))?;
361 let (key, value) = item.map_err(|e| v2_error(py, e))?;
357 dict.set_item(
362 dict.set_item(
358 py,
363 py,
359 PyBytes::new(py, key.as_bytes()),
364 PyBytes::new(py, key.as_bytes()),
360 PyBytes::new(py, value.as_bytes()),
365 PyBytes::new(py, value.as_bytes()),
361 )?;
366 )?;
362 }
367 }
363 Ok(dict)
368 Ok(dict)
364 }
369 }
365
370
366 def copymapgetitem(&self, key: PyObject) -> PyResult<PyBytes> {
371 def copymapgetitem(&self, key: PyObject) -> PyResult<PyBytes> {
367 let key = key.extract::<PyBytes>(py)?;
372 let key = key.extract::<PyBytes>(py)?;
368 match self
373 match self
369 .inner(py)
374 .inner(py)
370 .borrow()
375 .borrow()
371 .copy_map_get(HgPath::new(key.data(py)))
376 .copy_map_get(HgPath::new(key.data(py)))
372 .map_err(|e| v2_error(py, e))?
377 .map_err(|e| v2_error(py, e))?
373 {
378 {
374 Some(copy) => Ok(PyBytes::new(py, copy.as_bytes())),
379 Some(copy) => Ok(PyBytes::new(py, copy.as_bytes())),
375 None => Err(PyErr::new::<exc::KeyError, _>(
380 None => Err(PyErr::new::<exc::KeyError, _>(
376 py,
381 py,
377 String::from_utf8_lossy(key.data(py)),
382 String::from_utf8_lossy(key.data(py)),
378 )),
383 )),
379 }
384 }
380 }
385 }
381 def copymap(&self) -> PyResult<CopyMap> {
386 def copymap(&self) -> PyResult<CopyMap> {
382 CopyMap::from_inner(py, self.clone_ref(py))
387 CopyMap::from_inner(py, self.clone_ref(py))
383 }
388 }
384
389
385 def copymaplen(&self) -> PyResult<usize> {
390 def copymaplen(&self) -> PyResult<usize> {
386 Ok(self.inner(py).borrow().copy_map_len())
391 Ok(self.inner(py).borrow().copy_map_len())
387 }
392 }
388 def copymapcontains(&self, key: PyObject) -> PyResult<bool> {
393 def copymapcontains(&self, key: PyObject) -> PyResult<bool> {
389 let key = key.extract::<PyBytes>(py)?;
394 let key = key.extract::<PyBytes>(py)?;
390 self.inner(py)
395 self.inner(py)
391 .borrow()
396 .borrow()
392 .copy_map_contains_key(HgPath::new(key.data(py)))
397 .copy_map_contains_key(HgPath::new(key.data(py)))
393 .map_err(|e| v2_error(py, e))
398 .map_err(|e| v2_error(py, e))
394 }
399 }
395 def copymapget(
400 def copymapget(
396 &self,
401 &self,
397 key: PyObject,
402 key: PyObject,
398 default: Option<PyObject>
403 default: Option<PyObject>
399 ) -> PyResult<Option<PyObject>> {
404 ) -> PyResult<Option<PyObject>> {
400 let key = key.extract::<PyBytes>(py)?;
405 let key = key.extract::<PyBytes>(py)?;
401 match self
406 match self
402 .inner(py)
407 .inner(py)
403 .borrow()
408 .borrow()
404 .copy_map_get(HgPath::new(key.data(py)))
409 .copy_map_get(HgPath::new(key.data(py)))
405 .map_err(|e| v2_error(py, e))?
410 .map_err(|e| v2_error(py, e))?
406 {
411 {
407 Some(copy) => Ok(Some(
412 Some(copy) => Ok(Some(
408 PyBytes::new(py, copy.as_bytes()).into_object(),
413 PyBytes::new(py, copy.as_bytes()).into_object(),
409 )),
414 )),
410 None => Ok(default),
415 None => Ok(default),
411 }
416 }
412 }
417 }
413 def copymapsetitem(
418 def copymapsetitem(
414 &self,
419 &self,
415 key: PyObject,
420 key: PyObject,
416 value: PyObject
421 value: PyObject
417 ) -> PyResult<PyObject> {
422 ) -> PyResult<PyObject> {
418 let key = key.extract::<PyBytes>(py)?;
423 let key = key.extract::<PyBytes>(py)?;
419 let value = value.extract::<PyBytes>(py)?;
424 let value = value.extract::<PyBytes>(py)?;
420 self.inner(py)
425 self.inner(py)
421 .borrow_mut()
426 .borrow_mut()
422 .copy_map_insert(
427 .copy_map_insert(
423 HgPath::new(key.data(py)),
428 HgPath::new(key.data(py)),
424 HgPath::new(value.data(py)),
429 HgPath::new(value.data(py)),
425 )
430 )
426 .map_err(|e| v2_error(py, e))?;
431 .map_err(|e| v2_error(py, e))?;
427 Ok(py.None())
432 Ok(py.None())
428 }
433 }
429 def copymappop(
434 def copymappop(
430 &self,
435 &self,
431 key: PyObject,
436 key: PyObject,
432 default: Option<PyObject>
437 default: Option<PyObject>
433 ) -> PyResult<Option<PyObject>> {
438 ) -> PyResult<Option<PyObject>> {
434 let key = key.extract::<PyBytes>(py)?;
439 let key = key.extract::<PyBytes>(py)?;
435 match self
440 match self
436 .inner(py)
441 .inner(py)
437 .borrow_mut()
442 .borrow_mut()
438 .copy_map_remove(HgPath::new(key.data(py)))
443 .copy_map_remove(HgPath::new(key.data(py)))
439 .map_err(|e| v2_error(py, e))?
444 .map_err(|e| v2_error(py, e))?
440 {
445 {
441 Some(copy) => Ok(Some(
446 Some(copy) => Ok(Some(
442 PyBytes::new(py, copy.as_bytes()).into_object(),
447 PyBytes::new(py, copy.as_bytes()).into_object(),
443 )),
448 )),
444 None => Ok(default),
449 None => Ok(default),
445 }
450 }
446 }
451 }
447
452
448 def copymapiter(&self) -> PyResult<CopyMapKeysIterator> {
453 def copymapiter(&self) -> PyResult<CopyMapKeysIterator> {
449 let leaked_ref = self.inner(py).leak_immutable();
454 let leaked_ref = self.inner(py).leak_immutable();
450 CopyMapKeysIterator::from_inner(
455 CopyMapKeysIterator::from_inner(
451 py,
456 py,
452 unsafe { leaked_ref.map(py, |o| o.copy_map_iter()) },
457 unsafe { leaked_ref.map(py, |o| o.copy_map_iter()) },
453 )
458 )
454 }
459 }
455
460
456 def copymapitemsiter(&self) -> PyResult<CopyMapItemsIterator> {
461 def copymapitemsiter(&self) -> PyResult<CopyMapItemsIterator> {
457 let leaked_ref = self.inner(py).leak_immutable();
462 let leaked_ref = self.inner(py).leak_immutable();
458 CopyMapItemsIterator::from_inner(
463 CopyMapItemsIterator::from_inner(
459 py,
464 py,
460 unsafe { leaked_ref.map(py, |o| o.copy_map_iter()) },
465 unsafe { leaked_ref.map(py, |o| o.copy_map_iter()) },
461 )
466 )
462 }
467 }
463
468
464 def tracked_dirs(&self) -> PyResult<PyList> {
469 def tracked_dirs(&self) -> PyResult<PyList> {
465 let dirs = PyList::new(py, &[]);
470 let dirs = PyList::new(py, &[]);
466 for path in self.inner(py).borrow_mut().iter_tracked_dirs()
471 for path in self.inner(py).borrow_mut().iter_tracked_dirs()
467 .map_err(|e |dirstate_error(py, e))?
472 .map_err(|e |dirstate_error(py, e))?
468 {
473 {
469 let path = path.map_err(|e| v2_error(py, e))?;
474 let path = path.map_err(|e| v2_error(py, e))?;
470 let path = PyBytes::new(py, path.as_bytes());
475 let path = PyBytes::new(py, path.as_bytes());
471 dirs.append(py, path.into_object())
476 dirs.append(py, path.into_object())
472 }
477 }
473 Ok(dirs)
478 Ok(dirs)
474 }
479 }
475
480
476 def setparents_fixup(&self) -> PyResult<PyDict> {
481 def setparents_fixup(&self) -> PyResult<PyDict> {
477 let dict = PyDict::new(py);
482 let dict = PyDict::new(py);
478 let copies = self.inner(py).borrow_mut().setparents_fixup();
483 let copies = self.inner(py).borrow_mut().setparents_fixup();
479 for (key, value) in copies.map_err(|e| v2_error(py, e))? {
484 for (key, value) in copies.map_err(|e| v2_error(py, e))? {
480 dict.set_item(
485 dict.set_item(
481 py,
486 py,
482 PyBytes::new(py, key.as_bytes()),
487 PyBytes::new(py, key.as_bytes()),
483 PyBytes::new(py, value.as_bytes()),
488 PyBytes::new(py, value.as_bytes()),
484 )?;
489 )?;
485 }
490 }
486 Ok(dict)
491 Ok(dict)
487 }
492 }
488
493
489 def debug_iter(&self, all: bool) -> PyResult<PyList> {
494 def debug_iter(&self, all: bool) -> PyResult<PyList> {
490 let dirs = PyList::new(py, &[]);
495 let dirs = PyList::new(py, &[]);
491 for item in self.inner(py).borrow().debug_iter(all) {
496 for item in self.inner(py).borrow().debug_iter(all) {
492 let (path, (state, mode, size, mtime)) =
497 let (path, (state, mode, size, mtime)) =
493 item.map_err(|e| v2_error(py, e))?;
498 item.map_err(|e| v2_error(py, e))?;
494 let path = PyBytes::new(py, path.as_bytes());
499 let path = PyBytes::new(py, path.as_bytes());
495 let item = (path, state, mode, size, mtime);
500 let item = (path, state, mode, size, mtime);
496 dirs.append(py, item.to_py_object(py).into_object())
501 dirs.append(py, item.to_py_object(py).into_object())
497 }
502 }
498 Ok(dirs)
503 Ok(dirs)
499 }
504 }
500 });
505 });
501
506
502 impl DirstateMap {
507 impl DirstateMap {
503 pub fn get_inner_mut<'a>(
508 pub fn get_inner_mut<'a>(
504 &'a self,
509 &'a self,
505 py: Python<'a>,
510 py: Python<'a>,
506 ) -> RefMut<'a, OwningDirstateMap> {
511 ) -> RefMut<'a, OwningDirstateMap> {
507 self.inner(py).borrow_mut()
512 self.inner(py).borrow_mut()
508 }
513 }
509 fn translate_key(
514 fn translate_key(
510 py: Python,
515 py: Python,
511 res: Result<(&HgPath, DirstateEntry), DirstateV2ParseError>,
516 res: Result<(&HgPath, DirstateEntry), DirstateV2ParseError>,
512 ) -> PyResult<Option<PyBytes>> {
517 ) -> PyResult<Option<PyBytes>> {
513 let (f, _entry) = res.map_err(|e| v2_error(py, e))?;
518 let (f, _entry) = res.map_err(|e| v2_error(py, e))?;
514 Ok(Some(PyBytes::new(py, f.as_bytes())))
519 Ok(Some(PyBytes::new(py, f.as_bytes())))
515 }
520 }
516 fn translate_key_value(
521 fn translate_key_value(
517 py: Python,
522 py: Python,
518 res: Result<(&HgPath, DirstateEntry), DirstateV2ParseError>,
523 res: Result<(&HgPath, DirstateEntry), DirstateV2ParseError>,
519 ) -> PyResult<Option<(PyBytes, PyObject)>> {
524 ) -> PyResult<Option<(PyBytes, PyObject)>> {
520 let (f, entry) = res.map_err(|e| v2_error(py, e))?;
525 let (f, entry) = res.map_err(|e| v2_error(py, e))?;
521 Ok(Some((
526 Ok(Some((
522 PyBytes::new(py, f.as_bytes()),
527 PyBytes::new(py, f.as_bytes()),
523 DirstateItem::new_as_pyobject(py, entry)?,
528 DirstateItem::new_as_pyobject(py, entry)?,
524 )))
529 )))
525 }
530 }
526 }
531 }
527
532
528 py_shared_iterator!(
533 py_shared_iterator!(
529 DirstateMapKeysIterator,
534 DirstateMapKeysIterator,
530 UnsafePyLeaked<StateMapIter<'static>>,
535 UnsafePyLeaked<StateMapIter<'static>>,
531 DirstateMap::translate_key,
536 DirstateMap::translate_key,
532 Option<PyBytes>
537 Option<PyBytes>
533 );
538 );
534
539
535 py_shared_iterator!(
540 py_shared_iterator!(
536 DirstateMapItemsIterator,
541 DirstateMapItemsIterator,
537 UnsafePyLeaked<StateMapIter<'static>>,
542 UnsafePyLeaked<StateMapIter<'static>>,
538 DirstateMap::translate_key_value,
543 DirstateMap::translate_key_value,
539 Option<(PyBytes, PyObject)>
544 Option<(PyBytes, PyObject)>
540 );
545 );
541
546
542 fn extract_node_id(py: Python, obj: &PyObject) -> PyResult<Node> {
547 fn extract_node_id(py: Python, obj: &PyObject) -> PyResult<Node> {
543 let bytes = obj.extract::<PyBytes>(py)?;
548 let bytes = obj.extract::<PyBytes>(py)?;
544 match bytes.data(py).try_into() {
549 match bytes.data(py).try_into() {
545 Ok(s) => Ok(s),
550 Ok(s) => Ok(s),
546 Err(e) => Err(PyErr::new::<exc::ValueError, _>(py, e.to_string())),
551 Err(e) => Err(PyErr::new::<exc::ValueError, _>(py, e.to_string())),
547 }
552 }
548 }
553 }
549
554
550 pub(super) fn v2_error(py: Python<'_>, _: DirstateV2ParseError) -> PyErr {
555 pub(super) fn v2_error(py: Python<'_>, _: DirstateV2ParseError) -> PyErr {
551 PyErr::new::<exc::ValueError, _>(py, "corrupted dirstate-v2")
556 PyErr::new::<exc::ValueError, _>(py, "corrupted dirstate-v2")
552 }
557 }
553
558
554 fn dirstate_error(py: Python<'_>, e: DirstateError) -> PyErr {
559 fn dirstate_error(py: Python<'_>, e: DirstateError) -> PyErr {
555 PyErr::new::<exc::OSError, _>(py, format!("Dirstate error: {:?}", e))
560 PyErr::new::<exc::OSError, _>(py, format!("Dirstate error: {:?}", e))
556 }
561 }
General Comments 0
You need to be logged in to leave comments. Login now