139 } |
136 } |
140 } |
137 } |
141 |
138 |
142 /// Seconds since the Unix epoch |
139 /// Seconds since the Unix epoch |
143 pub struct Timestamp(pub i64); |
140 pub struct Timestamp(pub i64); |
144 |
|
145 pub fn pack_dirstate( |
|
146 state_map: &mut StateMap, |
|
147 copy_map: &CopyMap, |
|
148 parents: DirstateParents, |
|
149 now: Timestamp, |
|
150 ) -> Result<Vec<u8>, HgError> { |
|
151 // TODO move away from i32 before 2038. |
|
152 let now: i32 = now.0.try_into().expect("time overflow"); |
|
153 |
|
154 let expected_size: usize = state_map |
|
155 .iter() |
|
156 .map(|(filename, _)| { |
|
157 packed_entry_size(filename, copy_map.get(filename).map(|p| &**p)) |
|
158 }) |
|
159 .sum(); |
|
160 let expected_size = expected_size + PARENT_SIZE * 2; |
|
161 |
|
162 let mut packed = Vec::with_capacity(expected_size); |
|
163 |
|
164 packed.extend(parents.p1.as_bytes()); |
|
165 packed.extend(parents.p2.as_bytes()); |
|
166 |
|
167 for (filename, entry) in state_map.iter_mut() { |
|
168 entry.clear_ambiguous_mtime(now); |
|
169 pack_entry( |
|
170 filename, |
|
171 entry, |
|
172 copy_map.get(filename).map(|p| &**p), |
|
173 &mut packed, |
|
174 ) |
|
175 } |
|
176 |
|
177 if packed.len() != expected_size { |
|
178 return Err(HgError::CorruptedRepository(format!( |
|
179 "bad dirstate size: {} != {}", |
|
180 expected_size, |
|
181 packed.len() |
|
182 ))); |
|
183 } |
|
184 |
|
185 Ok(packed) |
|
186 } |
|
187 |
|
188 #[cfg(test)] |
|
189 mod tests { |
|
190 use super::*; |
|
191 use crate::{utils::hg_path::HgPathBuf, FastHashMap}; |
|
192 use pretty_assertions::assert_eq; |
|
193 |
|
194 #[test] |
|
195 fn test_pack_dirstate_empty() { |
|
196 let mut state_map = StateMap::default(); |
|
197 let copymap = FastHashMap::default(); |
|
198 let parents = DirstateParents { |
|
199 p1: b"12345678910111213141".into(), |
|
200 p2: b"00000000000000000000".into(), |
|
201 }; |
|
202 let now = Timestamp(15000000); |
|
203 let expected = b"1234567891011121314100000000000000000000".to_vec(); |
|
204 |
|
205 assert_eq!( |
|
206 expected, |
|
207 pack_dirstate(&mut state_map, ©map, parents, now).unwrap() |
|
208 ); |
|
209 |
|
210 assert!(state_map.is_empty()) |
|
211 } |
|
212 #[test] |
|
213 fn test_pack_dirstate_one_entry() { |
|
214 let expected_state_map: StateMap = [( |
|
215 HgPathBuf::from_bytes(b"f1"), |
|
216 DirstateEntry::from_v1_data( |
|
217 EntryState::Normal, |
|
218 0o644, |
|
219 0, |
|
220 791231220, |
|
221 ), |
|
222 )] |
|
223 .iter() |
|
224 .cloned() |
|
225 .collect(); |
|
226 let mut state_map = expected_state_map.clone(); |
|
227 |
|
228 let copymap = FastHashMap::default(); |
|
229 let parents = DirstateParents { |
|
230 p1: b"12345678910111213141".into(), |
|
231 p2: b"00000000000000000000".into(), |
|
232 }; |
|
233 let now = Timestamp(15000000); |
|
234 let expected = [ |
|
235 49, 50, 51, 52, 53, 54, 55, 56, 57, 49, 48, 49, 49, 49, 50, 49, |
|
236 51, 49, 52, 49, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, |
|
237 48, 48, 48, 48, 48, 48, 48, 48, 110, 0, 0, 1, 164, 0, 0, 0, 0, 47, |
|
238 41, 58, 244, 0, 0, 0, 2, 102, 49, |
|
239 ] |
|
240 .to_vec(); |
|
241 |
|
242 assert_eq!( |
|
243 expected, |
|
244 pack_dirstate(&mut state_map, ©map, parents, now).unwrap() |
|
245 ); |
|
246 |
|
247 assert_eq!(expected_state_map, state_map); |
|
248 } |
|
249 #[test] |
|
250 fn test_pack_dirstate_one_entry_with_copy() { |
|
251 let expected_state_map: StateMap = [( |
|
252 HgPathBuf::from_bytes(b"f1"), |
|
253 DirstateEntry::from_v1_data( |
|
254 EntryState::Normal, |
|
255 0o644, |
|
256 0, |
|
257 791231220, |
|
258 ), |
|
259 )] |
|
260 .iter() |
|
261 .cloned() |
|
262 .collect(); |
|
263 let mut state_map = expected_state_map.clone(); |
|
264 let mut copymap = FastHashMap::default(); |
|
265 copymap.insert( |
|
266 HgPathBuf::from_bytes(b"f1"), |
|
267 HgPathBuf::from_bytes(b"copyname"), |
|
268 ); |
|
269 let parents = DirstateParents { |
|
270 p1: b"12345678910111213141".into(), |
|
271 p2: b"00000000000000000000".into(), |
|
272 }; |
|
273 let now = Timestamp(15000000); |
|
274 let expected = [ |
|
275 49, 50, 51, 52, 53, 54, 55, 56, 57, 49, 48, 49, 49, 49, 50, 49, |
|
276 51, 49, 52, 49, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, |
|
277 48, 48, 48, 48, 48, 48, 48, 48, 110, 0, 0, 1, 164, 0, 0, 0, 0, 47, |
|
278 41, 58, 244, 0, 0, 0, 11, 102, 49, 0, 99, 111, 112, 121, 110, 97, |
|
279 109, 101, |
|
280 ] |
|
281 .to_vec(); |
|
282 |
|
283 assert_eq!( |
|
284 expected, |
|
285 pack_dirstate(&mut state_map, ©map, parents, now).unwrap() |
|
286 ); |
|
287 assert_eq!(expected_state_map, state_map); |
|
288 } |
|
289 |
|
290 #[test] |
|
291 fn test_parse_pack_one_entry_with_copy() { |
|
292 let mut state_map: StateMap = [( |
|
293 HgPathBuf::from_bytes(b"f1"), |
|
294 DirstateEntry::from_v1_data( |
|
295 EntryState::Normal, |
|
296 0o644, |
|
297 0, |
|
298 791231220, |
|
299 ), |
|
300 )] |
|
301 .iter() |
|
302 .cloned() |
|
303 .collect(); |
|
304 let mut copymap = FastHashMap::default(); |
|
305 copymap.insert( |
|
306 HgPathBuf::from_bytes(b"f1"), |
|
307 HgPathBuf::from_bytes(b"copyname"), |
|
308 ); |
|
309 let parents = DirstateParents { |
|
310 p1: b"12345678910111213141".into(), |
|
311 p2: b"00000000000000000000".into(), |
|
312 }; |
|
313 let now = Timestamp(15000000); |
|
314 let result = |
|
315 pack_dirstate(&mut state_map, ©map, parents.clone(), now) |
|
316 .unwrap(); |
|
317 |
|
318 let (new_parents, entries, copies) = |
|
319 parse_dirstate(result.as_slice()).unwrap(); |
|
320 let new_state_map: StateMap = entries |
|
321 .into_iter() |
|
322 .map(|(path, entry)| (path.to_owned(), entry)) |
|
323 .collect(); |
|
324 let new_copy_map: CopyMap = copies |
|
325 .into_iter() |
|
326 .map(|(path, copy)| (path.to_owned(), copy.to_owned())) |
|
327 .collect(); |
|
328 |
|
329 assert_eq!( |
|
330 (&parents, state_map, copymap), |
|
331 (new_parents, new_state_map, new_copy_map) |
|
332 ) |
|
333 } |
|
334 |
|
335 #[test] |
|
336 fn test_parse_pack_multiple_entries_with_copy() { |
|
337 let mut state_map: StateMap = [ |
|
338 ( |
|
339 HgPathBuf::from_bytes(b"f1"), |
|
340 DirstateEntry::from_v1_data( |
|
341 EntryState::Normal, |
|
342 0o644, |
|
343 0, |
|
344 791231220, |
|
345 ), |
|
346 ), |
|
347 ( |
|
348 HgPathBuf::from_bytes(b"f2"), |
|
349 DirstateEntry::from_v1_data( |
|
350 EntryState::Merged, |
|
351 0o777, |
|
352 1000, |
|
353 791231220, |
|
354 ), |
|
355 ), |
|
356 ( |
|
357 HgPathBuf::from_bytes(b"f3"), |
|
358 DirstateEntry::from_v1_data( |
|
359 EntryState::Removed, |
|
360 0o644, |
|
361 234553, |
|
362 791231220, |
|
363 ), |
|
364 ), |
|
365 ( |
|
366 HgPathBuf::from_bytes(b"f4\xF6"), |
|
367 DirstateEntry::from_v1_data(EntryState::Added, 0o644, -1, -1), |
|
368 ), |
|
369 ] |
|
370 .iter() |
|
371 .cloned() |
|
372 .collect(); |
|
373 let mut copymap = FastHashMap::default(); |
|
374 copymap.insert( |
|
375 HgPathBuf::from_bytes(b"f1"), |
|
376 HgPathBuf::from_bytes(b"copyname"), |
|
377 ); |
|
378 copymap.insert( |
|
379 HgPathBuf::from_bytes(b"f4\xF6"), |
|
380 HgPathBuf::from_bytes(b"copyname2"), |
|
381 ); |
|
382 let parents = DirstateParents { |
|
383 p1: b"12345678910111213141".into(), |
|
384 p2: b"00000000000000000000".into(), |
|
385 }; |
|
386 let now = Timestamp(15000000); |
|
387 let result = |
|
388 pack_dirstate(&mut state_map, ©map, parents.clone(), now) |
|
389 .unwrap(); |
|
390 |
|
391 let (new_parents, entries, copies) = |
|
392 parse_dirstate(result.as_slice()).unwrap(); |
|
393 let new_state_map: StateMap = entries |
|
394 .into_iter() |
|
395 .map(|(path, entry)| (path.to_owned(), entry)) |
|
396 .collect(); |
|
397 let new_copy_map: CopyMap = copies |
|
398 .into_iter() |
|
399 .map(|(path, copy)| (path.to_owned(), copy.to_owned())) |
|
400 .collect(); |
|
401 |
|
402 assert_eq!( |
|
403 (&parents, state_map, copymap), |
|
404 (new_parents, new_state_map, new_copy_map) |
|
405 ) |
|
406 } |
|
407 |
|
408 #[test] |
|
409 /// https://www.mercurial-scm.org/repo/hg/rev/af3f26b6bba4 |
|
410 fn test_parse_pack_one_entry_with_copy_and_time_conflict() { |
|
411 let mut state_map: StateMap = [( |
|
412 HgPathBuf::from_bytes(b"f1"), |
|
413 DirstateEntry::from_v1_data( |
|
414 EntryState::Normal, |
|
415 0o644, |
|
416 0, |
|
417 15000000, |
|
418 ), |
|
419 )] |
|
420 .iter() |
|
421 .cloned() |
|
422 .collect(); |
|
423 let mut copymap = FastHashMap::default(); |
|
424 copymap.insert( |
|
425 HgPathBuf::from_bytes(b"f1"), |
|
426 HgPathBuf::from_bytes(b"copyname"), |
|
427 ); |
|
428 let parents = DirstateParents { |
|
429 p1: b"12345678910111213141".into(), |
|
430 p2: b"00000000000000000000".into(), |
|
431 }; |
|
432 let now = Timestamp(15000000); |
|
433 let result = |
|
434 pack_dirstate(&mut state_map, ©map, parents.clone(), now) |
|
435 .unwrap(); |
|
436 |
|
437 let (new_parents, entries, copies) = |
|
438 parse_dirstate(result.as_slice()).unwrap(); |
|
439 let new_state_map: StateMap = entries |
|
440 .into_iter() |
|
441 .map(|(path, entry)| (path.to_owned(), entry)) |
|
442 .collect(); |
|
443 let new_copy_map: CopyMap = copies |
|
444 .into_iter() |
|
445 .map(|(path, copy)| (path.to_owned(), copy.to_owned())) |
|
446 .collect(); |
|
447 |
|
448 assert_eq!( |
|
449 ( |
|
450 &parents, |
|
451 [( |
|
452 HgPathBuf::from_bytes(b"f1"), |
|
453 DirstateEntry::from_v1_data( |
|
454 EntryState::Normal, |
|
455 0o644, |
|
456 0, |
|
457 -1 |
|
458 ) |
|
459 )] |
|
460 .iter() |
|
461 .cloned() |
|
462 .collect::<StateMap>(), |
|
463 copymap, |
|
464 ), |
|
465 (new_parents, new_state_map, new_copy_map) |
|
466 ) |
|
467 } |
|
468 } |
|