##// END OF EJS Templates
dirstate-v2: Parse the dirstate lazily, with copy-on-write nodes...
Simon Sapin -
r48128:0654b3b3 default
parent child Browse files
Show More
@@ -48,14 +48,17 b" pub(super) type NodeKey<'on_disk> = With"
48
48
49 pub(super) enum ChildNodes<'on_disk> {
49 pub(super) enum ChildNodes<'on_disk> {
50 InMemory(FastHashMap<NodeKey<'on_disk>, Node<'on_disk>>),
50 InMemory(FastHashMap<NodeKey<'on_disk>, Node<'on_disk>>),
51 OnDisk(&'on_disk [on_disk::Node]),
51 }
52 }
52
53
53 pub(super) enum ChildNodesRef<'tree, 'on_disk> {
54 pub(super) enum ChildNodesRef<'tree, 'on_disk> {
54 InMemory(&'tree FastHashMap<NodeKey<'on_disk>, Node<'on_disk>>),
55 InMemory(&'tree FastHashMap<NodeKey<'on_disk>, Node<'on_disk>>),
56 OnDisk(&'on_disk [on_disk::Node]),
55 }
57 }
56
58
57 pub(super) enum NodeRef<'tree, 'on_disk> {
59 pub(super) enum NodeRef<'tree, 'on_disk> {
58 InMemory(&'tree NodeKey<'on_disk>, &'tree Node<'on_disk>),
60 InMemory(&'tree NodeKey<'on_disk>, &'tree Node<'on_disk>),
61 OnDisk(&'on_disk on_disk::Node),
59 }
62 }
60
63
61 impl Default for ChildNodes<'_> {
64 impl Default for ChildNodes<'_> {
@@ -70,24 +73,42 b" impl<'on_disk> ChildNodes<'on_disk> {"
70 ) -> ChildNodesRef<'tree, 'on_disk> {
73 ) -> ChildNodesRef<'tree, 'on_disk> {
71 match self {
74 match self {
72 ChildNodes::InMemory(nodes) => ChildNodesRef::InMemory(nodes),
75 ChildNodes::InMemory(nodes) => ChildNodesRef::InMemory(nodes),
76 ChildNodes::OnDisk(nodes) => ChildNodesRef::OnDisk(nodes),
73 }
77 }
74 }
78 }
75
79
76 pub(super) fn is_empty(&self) -> bool {
80 pub(super) fn is_empty(&self) -> bool {
77 match self {
81 match self {
78 ChildNodes::InMemory(nodes) => nodes.is_empty(),
82 ChildNodes::InMemory(nodes) => nodes.is_empty(),
83 ChildNodes::OnDisk(nodes) => nodes.is_empty(),
79 }
84 }
80 }
85 }
81
86
82 pub(super) fn make_mut(
87 pub(super) fn make_mut(
83 &mut self,
88 &mut self,
84 _on_disk: &'on_disk [u8],
89 on_disk: &'on_disk [u8],
85 ) -> Result<
90 ) -> Result<
86 &mut FastHashMap<NodeKey<'on_disk>, Node<'on_disk>>,
91 &mut FastHashMap<NodeKey<'on_disk>, Node<'on_disk>>,
87 DirstateV2ParseError,
92 DirstateV2ParseError,
88 > {
93 > {
89 match self {
94 match self {
90 ChildNodes::InMemory(nodes) => Ok(nodes),
95 ChildNodes::InMemory(nodes) => Ok(nodes),
96 ChildNodes::OnDisk(nodes) => {
97 let nodes = nodes
98 .iter()
99 .map(|node| {
100 Ok((
101 node.path(on_disk)?,
102 node.to_in_memory_node(on_disk)?,
103 ))
104 })
105 .collect::<Result<_, _>>()?;
106 *self = ChildNodes::InMemory(nodes);
107 match self {
108 ChildNodes::InMemory(nodes) => Ok(nodes),
109 ChildNodes::OnDisk(_) => unreachable!(),
110 }
111 }
91 }
112 }
92 }
113 }
93 }
114 }
@@ -96,12 +117,29 b" impl<'tree, 'on_disk> ChildNodesRef<'tre"
96 pub(super) fn get(
117 pub(super) fn get(
97 &self,
118 &self,
98 base_name: &HgPath,
119 base_name: &HgPath,
99 _on_disk: &'on_disk [u8],
120 on_disk: &'on_disk [u8],
100 ) -> Result<Option<NodeRef<'tree, 'on_disk>>, DirstateV2ParseError> {
121 ) -> Result<Option<NodeRef<'tree, 'on_disk>>, DirstateV2ParseError> {
101 match self {
122 match self {
102 ChildNodesRef::InMemory(nodes) => Ok(nodes
123 ChildNodesRef::InMemory(nodes) => Ok(nodes
103 .get_key_value(base_name)
124 .get_key_value(base_name)
104 .map(|(k, v)| NodeRef::InMemory(k, v))),
125 .map(|(k, v)| NodeRef::InMemory(k, v))),
126 ChildNodesRef::OnDisk(nodes) => {
127 let mut parse_result = Ok(());
128 let search_result = nodes.binary_search_by(|node| {
129 match node.base_name(on_disk) {
130 Ok(node_base_name) => node_base_name.cmp(base_name),
131 Err(e) => {
132 parse_result = Err(e);
133 // Dummy comparison result, `search_result` won’t
134 // be used since `parse_result` is an error
135 std::cmp::Ordering::Equal
136 }
137 }
138 });
139 parse_result.map(|()| {
140 search_result.ok().map(|i| NodeRef::OnDisk(&nodes[i]))
141 })
142 }
105 }
143 }
106 }
144 }
107
145
@@ -110,8 +148,11 b" impl<'tree, 'on_disk> ChildNodesRef<'tre"
110 &self,
148 &self,
111 ) -> impl Iterator<Item = NodeRef<'tree, 'on_disk>> {
149 ) -> impl Iterator<Item = NodeRef<'tree, 'on_disk>> {
112 match self {
150 match self {
113 ChildNodesRef::InMemory(nodes) => {
151 ChildNodesRef::InMemory(nodes) => itertools::Either::Left(
114 nodes.iter().map(|(k, v)| NodeRef::InMemory(k, v))
152 nodes.iter().map(|(k, v)| NodeRef::InMemory(k, v)),
153 ),
154 ChildNodesRef::OnDisk(nodes) => {
155 itertools::Either::Right(nodes.iter().map(NodeRef::OnDisk))
115 }
156 }
116 }
157 }
117 }
158 }
@@ -123,9 +164,12 b" impl<'tree, 'on_disk> ChildNodesRef<'tre"
123 {
164 {
124 use rayon::prelude::*;
165 use rayon::prelude::*;
125 match self {
166 match self {
126 ChildNodesRef::InMemory(nodes) => {
167 ChildNodesRef::InMemory(nodes) => rayon::iter::Either::Left(
127 nodes.par_iter().map(|(k, v)| NodeRef::InMemory(k, v))
168 nodes.par_iter().map(|(k, v)| NodeRef::InMemory(k, v)),
128 }
169 ),
170 ChildNodesRef::OnDisk(nodes) => rayon::iter::Either::Right(
171 nodes.par_iter().map(NodeRef::OnDisk),
172 ),
129 }
173 }
130 }
174 }
131
175
@@ -139,6 +183,7 b" impl<'tree, 'on_disk> ChildNodesRef<'tre"
139 fn sort_key<'a>(node: &'a NodeRef) -> &'a HgPath {
183 fn sort_key<'a>(node: &'a NodeRef) -> &'a HgPath {
140 match node {
184 match node {
141 NodeRef::InMemory(path, _node) => path.base_name(),
185 NodeRef::InMemory(path, _node) => path.base_name(),
186 NodeRef::OnDisk(_) => unreachable!(),
142 }
187 }
143 }
188 }
144 // `sort_unstable_by_key` doesn’t allow keys borrowing from the
189 // `sort_unstable_by_key` doesn’t allow keys borrowing from the
@@ -146,6 +191,10 b" impl<'tree, 'on_disk> ChildNodesRef<'tre"
146 vec.sort_unstable_by(|a, b| sort_key(a).cmp(sort_key(b)));
191 vec.sort_unstable_by(|a, b| sort_key(a).cmp(sort_key(b)));
147 vec
192 vec
148 }
193 }
194 ChildNodesRef::OnDisk(nodes) => {
195 // Nodes on disk are already sorted
196 nodes.iter().map(NodeRef::OnDisk).collect()
197 }
149 }
198 }
150 }
199 }
151 }
200 }
@@ -153,61 +202,72 b" impl<'tree, 'on_disk> ChildNodesRef<'tre"
153 impl<'tree, 'on_disk> NodeRef<'tree, 'on_disk> {
202 impl<'tree, 'on_disk> NodeRef<'tree, 'on_disk> {
154 pub(super) fn full_path(
203 pub(super) fn full_path(
155 &self,
204 &self,
156 _on_disk: &'on_disk [u8],
205 on_disk: &'on_disk [u8],
157 ) -> Result<&'tree HgPath, DirstateV2ParseError> {
206 ) -> Result<&'tree HgPath, DirstateV2ParseError> {
158 match self {
207 match self {
159 NodeRef::InMemory(path, _node) => Ok(path.full_path()),
208 NodeRef::InMemory(path, _node) => Ok(path.full_path()),
209 NodeRef::OnDisk(node) => node.full_path(on_disk),
160 }
210 }
161 }
211 }
162
212
163 /// Returns a `Cow` that can borrow 'on_disk but is detached from 'tree
213 /// Returns a `Cow` that can borrow 'on_disk but is detached from 'tree
164 pub(super) fn full_path_cow(
214 pub(super) fn full_path_cow(
165 &self,
215 &self,
166 _on_disk: &'on_disk [u8],
216 on_disk: &'on_disk [u8],
167 ) -> Result<Cow<'on_disk, HgPath>, DirstateV2ParseError> {
217 ) -> Result<Cow<'on_disk, HgPath>, DirstateV2ParseError> {
168 match self {
218 match self {
169 NodeRef::InMemory(path, _node) => Ok(path.full_path().clone()),
219 NodeRef::InMemory(path, _node) => Ok(path.full_path().clone()),
220 NodeRef::OnDisk(node) => {
221 Ok(Cow::Borrowed(node.full_path(on_disk)?))
222 }
170 }
223 }
171 }
224 }
172
225
173 pub(super) fn base_name(
226 pub(super) fn base_name(
174 &self,
227 &self,
175 _on_disk: &'on_disk [u8],
228 on_disk: &'on_disk [u8],
176 ) -> Result<&'tree HgPath, DirstateV2ParseError> {
229 ) -> Result<&'tree HgPath, DirstateV2ParseError> {
177 match self {
230 match self {
178 NodeRef::InMemory(path, _node) => Ok(path.base_name()),
231 NodeRef::InMemory(path, _node) => Ok(path.base_name()),
232 NodeRef::OnDisk(node) => node.base_name(on_disk),
179 }
233 }
180 }
234 }
181
235
182 pub(super) fn children(
236 pub(super) fn children(
183 &self,
237 &self,
184 _on_disk: &'on_disk [u8],
238 on_disk: &'on_disk [u8],
185 ) -> Result<ChildNodesRef<'tree, 'on_disk>, DirstateV2ParseError> {
239 ) -> Result<ChildNodesRef<'tree, 'on_disk>, DirstateV2ParseError> {
186 match self {
240 match self {
187 NodeRef::InMemory(_path, node) => Ok(node.children.as_ref()),
241 NodeRef::InMemory(_path, node) => Ok(node.children.as_ref()),
242 NodeRef::OnDisk(node) => {
243 Ok(ChildNodesRef::OnDisk(node.children(on_disk)?))
244 }
188 }
245 }
189 }
246 }
190
247
191 pub(super) fn has_copy_source(&self) -> bool {
248 pub(super) fn has_copy_source(&self) -> bool {
192 match self {
249 match self {
193 NodeRef::InMemory(_path, node) => node.copy_source.is_some(),
250 NodeRef::InMemory(_path, node) => node.copy_source.is_some(),
251 NodeRef::OnDisk(node) => node.has_copy_source(),
194 }
252 }
195 }
253 }
196
254
197 pub(super) fn copy_source(
255 pub(super) fn copy_source(
198 &self,
256 &self,
199 _on_disk: &'on_disk [u8],
257 on_disk: &'on_disk [u8],
200 ) -> Result<Option<&'tree HgPath>, DirstateV2ParseError> {
258 ) -> Result<Option<&'tree HgPath>, DirstateV2ParseError> {
201 match self {
259 match self {
202 NodeRef::InMemory(_path, node) => {
260 NodeRef::InMemory(_path, node) => {
203 Ok(node.copy_source.as_ref().map(|s| &**s))
261 Ok(node.copy_source.as_ref().map(|s| &**s))
204 }
262 }
263 NodeRef::OnDisk(node) => node.copy_source(on_disk),
205 }
264 }
206 }
265 }
207
266
208 pub(super) fn has_entry(&self) -> bool {
267 pub(super) fn has_entry(&self) -> bool {
209 match self {
268 match self {
210 NodeRef::InMemory(_path, node) => node.entry.is_some(),
269 NodeRef::InMemory(_path, node) => node.entry.is_some(),
270 NodeRef::OnDisk(node) => node.has_entry(),
211 }
271 }
212 }
272 }
213
273
@@ -216,6 +276,7 b" impl<'tree, 'on_disk> NodeRef<'tree, 'on"
216 ) -> Result<Option<DirstateEntry>, DirstateV2ParseError> {
276 ) -> Result<Option<DirstateEntry>, DirstateV2ParseError> {
217 match self {
277 match self {
218 NodeRef::InMemory(_path, node) => Ok(node.entry),
278 NodeRef::InMemory(_path, node) => Ok(node.entry),
279 NodeRef::OnDisk(node) => node.entry(),
219 }
280 }
220 }
281 }
221
282
@@ -226,12 +287,14 b" impl<'tree, 'on_disk> NodeRef<'tree, 'on"
226 NodeRef::InMemory(_path, node) => {
287 NodeRef::InMemory(_path, node) => {
227 Ok(node.entry.as_ref().map(|entry| entry.state))
288 Ok(node.entry.as_ref().map(|entry| entry.state))
228 }
289 }
290 NodeRef::OnDisk(node) => node.state(),
229 }
291 }
230 }
292 }
231
293
232 pub(super) fn tracked_descendants_count(&self) -> u32 {
294 pub(super) fn tracked_descendants_count(&self) -> u32 {
233 match self {
295 match self {
234 NodeRef::InMemory(_path, node) => node.tracked_descendants_count,
296 NodeRef::InMemory(_path, node) => node.tracked_descendants_count,
297 NodeRef::OnDisk(node) => node.tracked_descendants_count.get(),
235 }
298 }
236 }
299 }
237 }
300 }
@@ -16,6 +16,7 b' use crate::utils::hg_path::HgPath;'
16 use crate::DirstateEntry;
16 use crate::DirstateEntry;
17 use crate::DirstateError;
17 use crate::DirstateError;
18 use crate::DirstateParents;
18 use crate::DirstateParents;
19 use crate::EntryState;
19 use bytes_cast::unaligned::{I32Be, U32Be, U64Be};
20 use bytes_cast::unaligned::{I32Be, U32Be, U64Be};
20 use bytes_cast::BytesCast;
21 use bytes_cast::BytesCast;
21 use std::borrow::Cow;
22 use std::borrow::Cow;
@@ -42,7 +43,7 b' struct Header {'
42
43
43 #[derive(BytesCast)]
44 #[derive(BytesCast)]
44 #[repr(C)]
45 #[repr(C)]
45 struct Node {
46 pub(super) struct Node {
46 full_path: PathSlice,
47 full_path: PathSlice,
47
48
48 /// In bytes from `self.full_path.start`
49 /// In bytes from `self.full_path.start`
@@ -51,12 +52,12 b' struct Node {'
51 copy_source: OptPathSlice,
52 copy_source: OptPathSlice,
52 entry: OptEntry,
53 entry: OptEntry,
53 children: ChildNodes,
54 children: ChildNodes,
54 tracked_descendants_count: Size,
55 pub(super) tracked_descendants_count: Size,
55 }
56 }
56
57
57 /// Either nothing if `state == b'\0'`, or a dirstate entry like in the v1
58 /// Either nothing if `state == b'\0'`, or a dirstate entry like in the v1
58 /// format
59 /// format
59 #[derive(BytesCast)]
60 #[derive(BytesCast, Copy, Clone)]
60 #[repr(C)]
61 #[repr(C)]
61 struct OptEntry {
62 struct OptEntry {
62 state: u8,
63 state: u8,
@@ -149,7 +150,9 b" pub(super) fn read<'on_disk>("
149 }
150 }
150 let dirstate_map = DirstateMap {
151 let dirstate_map = DirstateMap {
151 on_disk,
152 on_disk,
152 root: read_nodes(on_disk, *root)?,
153 root: dirstate_map::ChildNodes::OnDisk(read_slice::<Node>(
154 on_disk, *root,
155 )?),
153 nodes_with_entry_count: nodes_with_entry_count.get(),
156 nodes_with_entry_count: nodes_with_entry_count.get(),
154 nodes_with_copy_source_count: nodes_with_copy_source_count.get(),
157 nodes_with_copy_source_count: nodes_with_copy_source_count.get(),
155 };
158 };
@@ -158,49 +161,96 b" pub(super) fn read<'on_disk>("
158 }
161 }
159
162
160 impl Node {
163 impl Node {
161 pub(super) fn path<'on_disk>(
164 pub(super) fn full_path<'on_disk>(
162 &self,
165 &self,
163 on_disk: &'on_disk [u8],
166 on_disk: &'on_disk [u8],
164 ) -> Result<dirstate_map::NodeKey<'on_disk>, DirstateV2ParseError> {
167 ) -> Result<&'on_disk HgPath, DirstateV2ParseError> {
165 let full_path = read_hg_path(on_disk, self.full_path)?;
168 read_hg_path(on_disk, self.full_path)
166 let base_name_start = usize::try_from(self.base_name_start.get())
169 }
167 // u32 -> usize, could only panic on a 16-bit CPU
170
168 .expect("dirstate-v2 base_name_start out of bounds");
171 pub(super) fn base_name_start<'on_disk>(
169 if base_name_start < full_path.len() {
172 &self,
170 Ok(WithBasename::from_raw_parts(full_path, base_name_start))
173 ) -> Result<usize, DirstateV2ParseError> {
174 let start = self.base_name_start.get();
175 if start < self.full_path.len.get() {
176 let start = usize::try_from(start)
177 // u32 -> usize, could only panic on a 16-bit CPU
178 .expect("dirstate-v2 base_name_start out of bounds");
179 Ok(start)
171 } else {
180 } else {
172 Err(DirstateV2ParseError)
181 Err(DirstateV2ParseError)
173 }
182 }
174 }
183 }
175
184
185 pub(super) fn base_name<'on_disk>(
186 &self,
187 on_disk: &'on_disk [u8],
188 ) -> Result<&'on_disk HgPath, DirstateV2ParseError> {
189 let full_path = self.full_path(on_disk)?;
190 let base_name_start = self.base_name_start()?;
191 Ok(HgPath::new(&full_path.as_bytes()[base_name_start..]))
192 }
193
194 pub(super) fn path<'on_disk>(
195 &self,
196 on_disk: &'on_disk [u8],
197 ) -> Result<dirstate_map::NodeKey<'on_disk>, DirstateV2ParseError> {
198 Ok(WithBasename::from_raw_parts(
199 Cow::Borrowed(self.full_path(on_disk)?),
200 self.base_name_start()?,
201 ))
202 }
203
204 pub(super) fn has_copy_source<'on_disk>(&self) -> bool {
205 self.copy_source.start.get() != 0
206 }
207
176 pub(super) fn copy_source<'on_disk>(
208 pub(super) fn copy_source<'on_disk>(
177 &self,
209 &self,
178 on_disk: &'on_disk [u8],
210 on_disk: &'on_disk [u8],
179 ) -> Result<Option<Cow<'on_disk, HgPath>>, DirstateV2ParseError> {
211 ) -> Result<Option<&'on_disk HgPath>, DirstateV2ParseError> {
180 Ok(if self.copy_source.start.get() != 0 {
212 Ok(if self.has_copy_source() {
181 Some(read_hg_path(on_disk, self.copy_source)?)
213 Some(read_hg_path(on_disk, self.copy_source)?)
182 } else {
214 } else {
183 None
215 None
184 })
216 })
185 }
217 }
186
218
219 pub(super) fn has_entry(&self) -> bool {
220 self.entry.state != b'\0'
221 }
222
223 pub(super) fn state(
224 &self,
225 ) -> Result<Option<EntryState>, DirstateV2ParseError> {
226 Ok(if self.has_entry() {
227 Some(
228 self.entry
229 .state
230 .try_into()
231 .map_err(|_| DirstateV2ParseError)?,
232 )
233 } else {
234 None
235 })
236 }
237
187 pub(super) fn entry(
238 pub(super) fn entry(
188 &self,
239 &self,
189 ) -> Result<Option<DirstateEntry>, DirstateV2ParseError> {
240 ) -> Result<Option<DirstateEntry>, DirstateV2ParseError> {
190 Ok(if self.entry.state != b'\0' {
241 Ok(self.state()?.map(|state| DirstateEntry {
191 Some(DirstateEntry {
242 state,
192 state: self
243 mode: self.entry.mode.get(),
193 .entry
244 mtime: self.entry.mtime.get(),
194 .state
245 size: self.entry.size.get(),
195 .try_into()
246 }))
196 .map_err(|_| DirstateV2ParseError)?,
247 }
197 mode: self.entry.mode.get(),
248
198 mtime: self.entry.mtime.get(),
249 pub(super) fn children<'on_disk>(
199 size: self.entry.size.get(),
250 &self,
200 })
251 on_disk: &'on_disk [u8],
201 } else {
252 ) -> Result<&'on_disk [Node], DirstateV2ParseError> {
202 None
253 read_slice::<Node>(on_disk, self.children)
203 })
204 }
254 }
205
255
206 pub(super) fn to_in_memory_node<'on_disk>(
256 pub(super) fn to_in_memory_node<'on_disk>(
@@ -208,33 +258,22 b' impl Node {'
208 on_disk: &'on_disk [u8],
258 on_disk: &'on_disk [u8],
209 ) -> Result<dirstate_map::Node<'on_disk>, DirstateV2ParseError> {
259 ) -> Result<dirstate_map::Node<'on_disk>, DirstateV2ParseError> {
210 Ok(dirstate_map::Node {
260 Ok(dirstate_map::Node {
211 children: read_nodes(on_disk, self.children)?,
261 children: dirstate_map::ChildNodes::OnDisk(
212 copy_source: self.copy_source(on_disk)?,
262 self.children(on_disk)?,
263 ),
264 copy_source: self.copy_source(on_disk)?.map(Cow::Borrowed),
213 entry: self.entry()?,
265 entry: self.entry()?,
214 tracked_descendants_count: self.tracked_descendants_count.get(),
266 tracked_descendants_count: self.tracked_descendants_count.get(),
215 })
267 })
216 }
268 }
217 }
269 }
218
270
219 fn read_nodes(
220 on_disk: &[u8],
221 slice: ChildNodes,
222 ) -> Result<dirstate_map::ChildNodes, DirstateV2ParseError> {
223 read_slice::<Node>(on_disk, slice)?
224 .iter()
225 .map(|node| {
226 Ok((node.path(on_disk)?, node.to_in_memory_node(on_disk)?))
227 })
228 .collect::<Result<_, _>>()
229 .map(dirstate_map::ChildNodes::InMemory)
230 }
231
232 fn read_hg_path(
271 fn read_hg_path(
233 on_disk: &[u8],
272 on_disk: &[u8],
234 slice: Slice,
273 slice: Slice,
235 ) -> Result<Cow<HgPath>, DirstateV2ParseError> {
274 ) -> Result<&HgPath, DirstateV2ParseError> {
236 let bytes = read_slice::<u8>(on_disk, slice)?;
275 let bytes = read_slice::<u8>(on_disk, slice)?;
237 Ok(Cow::Borrowed(HgPath::new(bytes)))
276 Ok(HgPath::new(bytes))
238 }
277 }
239
278
240 fn read_slice<T>(
279 fn read_slice<T>(
@@ -300,8 +339,11 b' fn write_nodes('
300 // First accumulate serialized nodes in a `Vec`
339 // First accumulate serialized nodes in a `Vec`
301 let mut on_disk_nodes = Vec::with_capacity(nodes.len());
340 let mut on_disk_nodes = Vec::with_capacity(nodes.len());
302 for node in nodes {
341 for node in nodes {
303 let children = node.children(dirstate_map.on_disk)?;
342 let children = write_nodes(
304 let children = write_nodes(dirstate_map, children, out)?;
343 dirstate_map,
344 node.children(dirstate_map.on_disk)?,
345 out,
346 )?;
305 let full_path = node.full_path(dirstate_map.on_disk)?;
347 let full_path = node.full_path(dirstate_map.on_disk)?;
306 let full_path = write_slice::<u8>(full_path.as_bytes(), out);
348 let full_path = write_slice::<u8>(full_path.as_bytes(), out);
307 let copy_source =
349 let copy_source =
@@ -341,6 +383,12 b' fn write_nodes('
341 }
383 }
342 },
384 },
343 },
385 },
386 NodeRef::OnDisk(node) => Node {
387 children,
388 copy_source,
389 full_path,
390 ..*node
391 },
344 })
392 })
345 }
393 }
346 // … so we can write them contiguously
394 // … so we can write them contiguously
General Comments 0
You need to be logged in to leave comments. Login now