Show More
@@ -1,518 +1,512 | |||||
1 | // Copyright 2019 Raphaël Gomès <rgomes@octobus.net> |
|
1 | // Copyright 2019 Raphaël Gomès <rgomes@octobus.net> | |
2 | // |
|
2 | // | |
3 | // This software may be used and distributed according to the terms of the |
|
3 | // This software may be used and distributed according to the terms of the | |
4 | // GNU General Public License version 2 or any later version. |
|
4 | // GNU General Public License version 2 or any later version. | |
5 |
|
5 | |||
6 | use crate::utils::hg_path::HgPath; |
|
6 | use crate::utils::hg_path::HgPath; | |
7 | use crate::{ |
|
7 | use crate::{ | |
8 | dirstate::{CopyMap, EntryState, StateMap}, |
|
8 | dirstate::{CopyMap, EntryState, StateMap}, | |
9 | DirstateEntry, DirstatePackError, DirstateParents, DirstateParseError, |
|
9 | DirstateEntry, DirstatePackError, DirstateParents, DirstateParseError, | |
10 | }; |
|
10 | }; | |
11 | use byteorder::{BigEndian, ReadBytesExt, WriteBytesExt}; |
|
11 | use byteorder::{BigEndian, ReadBytesExt, WriteBytesExt}; | |
12 | use micro_timer::timed; |
|
12 | use micro_timer::timed; | |
13 | use std::convert::{TryFrom, TryInto}; |
|
13 | use std::convert::{TryFrom, TryInto}; | |
14 | use std::io::Cursor; |
|
14 | use std::io::Cursor; | |
15 | use std::time::Duration; |
|
15 | use std::time::Duration; | |
16 |
|
16 | |||
17 | /// Parents are stored in the dirstate as byte hashes. |
|
17 | /// Parents are stored in the dirstate as byte hashes. | |
18 | pub const PARENT_SIZE: usize = 20; |
|
18 | pub const PARENT_SIZE: usize = 20; | |
19 | /// Dirstate entries have a static part of 8 + 32 + 32 + 32 + 32 bits. |
|
19 | /// Dirstate entries have a static part of 8 + 32 + 32 + 32 + 32 bits. | |
20 | const MIN_ENTRY_SIZE: usize = 17; |
|
20 | const MIN_ENTRY_SIZE: usize = 17; | |
21 |
|
21 | |||
22 | type ParseResult<'a> = ( |
|
22 | type ParseResult<'a> = ( | |
23 | DirstateParents, |
|
23 | DirstateParents, | |
24 | Vec<(&'a HgPath, DirstateEntry)>, |
|
24 | Vec<(&'a HgPath, DirstateEntry)>, | |
25 | Vec<(&'a HgPath, &'a HgPath)>, |
|
25 | Vec<(&'a HgPath, &'a HgPath)>, | |
26 | ); |
|
26 | ); | |
27 |
|
27 | |||
28 | #[timed] |
|
28 | #[timed] | |
29 | pub fn parse_dirstate( |
|
29 | pub fn parse_dirstate( | |
30 | contents: &[u8], |
|
30 | contents: &[u8], | |
31 | ) -> Result<ParseResult, DirstateParseError> { |
|
31 | ) -> Result<ParseResult, DirstateParseError> { | |
32 | if contents.len() < PARENT_SIZE * 2 { |
|
32 | if contents.len() < PARENT_SIZE * 2 { | |
33 | return Err(DirstateParseError::TooLittleData); |
|
33 | return Err(DirstateParseError::TooLittleData); | |
34 | } |
|
34 | } | |
35 | let mut copies = vec![]; |
|
35 | let mut copies = vec![]; | |
36 | let mut entries = vec![]; |
|
36 | let mut entries = vec![]; | |
37 |
|
37 | |||
38 | let mut curr_pos = PARENT_SIZE * 2; |
|
38 | let mut curr_pos = PARENT_SIZE * 2; | |
39 | let parents = DirstateParents { |
|
39 | let parents = DirstateParents { | |
40 | p1: contents[..PARENT_SIZE].try_into().unwrap(), |
|
40 | p1: contents[..PARENT_SIZE].try_into().unwrap(), | |
41 | p2: contents[PARENT_SIZE..curr_pos].try_into().unwrap(), |
|
41 | p2: contents[PARENT_SIZE..curr_pos].try_into().unwrap(), | |
42 | }; |
|
42 | }; | |
43 |
|
43 | |||
44 | while curr_pos < contents.len() { |
|
44 | while curr_pos < contents.len() { | |
45 | if curr_pos + MIN_ENTRY_SIZE > contents.len() { |
|
45 | if curr_pos + MIN_ENTRY_SIZE > contents.len() { | |
46 | return Err(DirstateParseError::Overflow); |
|
46 | return Err(DirstateParseError::Overflow); | |
47 | } |
|
47 | } | |
48 | let entry_bytes = &contents[curr_pos..]; |
|
48 | let entry_bytes = &contents[curr_pos..]; | |
49 |
|
49 | |||
50 | let mut cursor = Cursor::new(entry_bytes); |
|
50 | let mut cursor = Cursor::new(entry_bytes); | |
51 | let state = EntryState::try_from(cursor.read_u8()?)?; |
|
51 | let state = EntryState::try_from(cursor.read_u8()?)?; | |
52 | let mode = cursor.read_i32::<BigEndian>()?; |
|
52 | let mode = cursor.read_i32::<BigEndian>()?; | |
53 | let size = cursor.read_i32::<BigEndian>()?; |
|
53 | let size = cursor.read_i32::<BigEndian>()?; | |
54 | let mtime = cursor.read_i32::<BigEndian>()?; |
|
54 | let mtime = cursor.read_i32::<BigEndian>()?; | |
55 | let path_len = cursor.read_i32::<BigEndian>()? as usize; |
|
55 | let path_len = cursor.read_i32::<BigEndian>()? as usize; | |
56 |
|
56 | |||
57 | if path_len > contents.len() - curr_pos { |
|
57 | if path_len > contents.len() - curr_pos { | |
58 | return Err(DirstateParseError::Overflow); |
|
58 | return Err(DirstateParseError::Overflow); | |
59 | } |
|
59 | } | |
60 |
|
60 | |||
61 | // Slice instead of allocating a Vec needed for `read_exact` |
|
61 | // Slice instead of allocating a Vec needed for `read_exact` | |
62 | let path = &entry_bytes[MIN_ENTRY_SIZE..MIN_ENTRY_SIZE + (path_len)]; |
|
62 | let path = &entry_bytes[MIN_ENTRY_SIZE..MIN_ENTRY_SIZE + (path_len)]; | |
63 |
|
63 | |||
64 | let (path, copy) = match memchr::memchr(0, path) { |
|
64 | let (path, copy) = match memchr::memchr(0, path) { | |
65 | None => (path, None), |
|
65 | None => (path, None), | |
66 | Some(i) => (&path[..i], Some(&path[(i + 1)..])), |
|
66 | Some(i) => (&path[..i], Some(&path[(i + 1)..])), | |
67 | }; |
|
67 | }; | |
68 |
|
68 | |||
69 | if let Some(copy_path) = copy { |
|
69 | if let Some(copy_path) = copy { | |
70 | copies.push((HgPath::new(path), HgPath::new(copy_path))); |
|
70 | copies.push((HgPath::new(path), HgPath::new(copy_path))); | |
71 | }; |
|
71 | }; | |
72 | entries.push(( |
|
72 | entries.push(( | |
73 | HgPath::new(path), |
|
73 | HgPath::new(path), | |
74 | DirstateEntry { |
|
74 | DirstateEntry { | |
75 | state, |
|
75 | state, | |
76 | mode, |
|
76 | mode, | |
77 | size, |
|
77 | size, | |
78 | mtime, |
|
78 | mtime, | |
79 | }, |
|
79 | }, | |
80 | )); |
|
80 | )); | |
81 | curr_pos = curr_pos + MIN_ENTRY_SIZE + (path_len); |
|
81 | curr_pos = curr_pos + MIN_ENTRY_SIZE + (path_len); | |
82 | } |
|
82 | } | |
83 | Ok((parents, entries, copies)) |
|
83 | Ok((parents, entries, copies)) | |
84 | } |
|
84 | } | |
85 |
|
85 | |||
86 | /// `now` is the duration in seconds since the Unix epoch |
|
86 | /// `now` is the duration in seconds since the Unix epoch | |
87 | #[cfg(not(feature = "dirstate-tree"))] |
|
87 | #[cfg(not(feature = "dirstate-tree"))] | |
88 | pub fn pack_dirstate( |
|
88 | pub fn pack_dirstate( | |
89 | state_map: &mut StateMap, |
|
89 | state_map: &mut StateMap, | |
90 | copy_map: &CopyMap, |
|
90 | copy_map: &CopyMap, | |
91 | parents: DirstateParents, |
|
91 | parents: DirstateParents, | |
92 | now: Duration, |
|
92 | now: Duration, | |
93 | ) -> Result<Vec<u8>, DirstatePackError> { |
|
93 | ) -> Result<Vec<u8>, DirstatePackError> { | |
94 | // TODO move away from i32 before 2038. |
|
94 | // TODO move away from i32 before 2038. | |
95 | let now: i32 = now.as_secs().try_into().expect("time overflow"); |
|
95 | let now: i32 = now.as_secs().try_into().expect("time overflow"); | |
96 |
|
96 | |||
97 | let expected_size: usize = state_map |
|
97 | let expected_size: usize = state_map | |
98 | .iter() |
|
98 | .iter() | |
99 | .map(|(filename, _)| { |
|
99 | .map(|(filename, _)| { | |
100 | let mut length = MIN_ENTRY_SIZE + filename.len(); |
|
100 | let mut length = MIN_ENTRY_SIZE + filename.len(); | |
101 | if let Some(copy) = copy_map.get(filename) { |
|
101 | if let Some(copy) = copy_map.get(filename) { | |
102 | length += copy.len() + 1; |
|
102 | length += copy.len() + 1; | |
103 | } |
|
103 | } | |
104 | length |
|
104 | length | |
105 | }) |
|
105 | }) | |
106 | .sum(); |
|
106 | .sum(); | |
107 | let expected_size = expected_size + PARENT_SIZE * 2; |
|
107 | let expected_size = expected_size + PARENT_SIZE * 2; | |
108 |
|
108 | |||
109 | let mut packed = Vec::with_capacity(expected_size); |
|
109 | let mut packed = Vec::with_capacity(expected_size); | |
110 | let mut new_state_map = vec![]; |
|
|||
111 |
|
110 | |||
112 | packed.extend(&parents.p1); |
|
111 | packed.extend(&parents.p1); | |
113 | packed.extend(&parents.p2); |
|
112 | packed.extend(&parents.p2); | |
114 |
|
113 | |||
115 | for (filename, entry) in state_map.iter() { |
|
114 | for (filename, entry) in state_map.iter_mut() { | |
116 | let new_filename = filename.to_owned(); |
|
115 | let new_filename = filename.to_owned(); | |
117 | let mut new_mtime: i32 = entry.mtime; |
|
116 | let mut new_mtime: i32 = entry.mtime; | |
118 | if entry.state == EntryState::Normal && entry.mtime == now { |
|
117 | if entry.state == EntryState::Normal && entry.mtime == now { | |
119 | // The file was last modified "simultaneously" with the current |
|
118 | // The file was last modified "simultaneously" with the current | |
120 | // write to dirstate (i.e. within the same second for file- |
|
119 | // write to dirstate (i.e. within the same second for file- | |
121 | // systems with a granularity of 1 sec). This commonly happens |
|
120 | // systems with a granularity of 1 sec). This commonly happens | |
122 | // for at least a couple of files on 'update'. |
|
121 | // for at least a couple of files on 'update'. | |
123 | // The user could change the file without changing its size |
|
122 | // The user could change the file without changing its size | |
124 | // within the same second. Invalidate the file's mtime in |
|
123 | // within the same second. Invalidate the file's mtime in | |
125 | // dirstate, forcing future 'status' calls to compare the |
|
124 | // dirstate, forcing future 'status' calls to compare the | |
126 | // contents of the file if the size is the same. This prevents |
|
125 | // contents of the file if the size is the same. This prevents | |
127 | // mistakenly treating such files as clean. |
|
126 | // mistakenly treating such files as clean. | |
128 | new_mtime = -1; |
|
127 | new_mtime = -1; | |
129 | new_state_map.push(( |
|
128 | *entry = DirstateEntry { | |
130 |
|
|
129 | mtime: new_mtime, | |
131 |
|
|
130 | ..*entry | |
132 | mtime: new_mtime, |
|
131 | }; | |
133 | ..*entry |
|
|||
134 | }, |
|
|||
135 | )); |
|
|||
136 | } |
|
132 | } | |
137 | let mut new_filename = new_filename.into_vec(); |
|
133 | let mut new_filename = new_filename.into_vec(); | |
138 | if let Some(copy) = copy_map.get(filename) { |
|
134 | if let Some(copy) = copy_map.get(filename) { | |
139 | new_filename.push(b'\0'); |
|
135 | new_filename.push(b'\0'); | |
140 | new_filename.extend(copy.bytes()); |
|
136 | new_filename.extend(copy.bytes()); | |
141 | } |
|
137 | } | |
142 |
|
138 | |||
143 | packed.write_u8(entry.state.into())?; |
|
139 | packed.write_u8(entry.state.into())?; | |
144 | packed.write_i32::<BigEndian>(entry.mode)?; |
|
140 | packed.write_i32::<BigEndian>(entry.mode)?; | |
145 | packed.write_i32::<BigEndian>(entry.size)?; |
|
141 | packed.write_i32::<BigEndian>(entry.size)?; | |
146 | packed.write_i32::<BigEndian>(new_mtime)?; |
|
142 | packed.write_i32::<BigEndian>(new_mtime)?; | |
147 | packed.write_i32::<BigEndian>(new_filename.len() as i32)?; |
|
143 | packed.write_i32::<BigEndian>(new_filename.len() as i32)?; | |
148 | packed.extend(new_filename) |
|
144 | packed.extend(new_filename) | |
149 | } |
|
145 | } | |
150 |
|
146 | |||
151 | if packed.len() != expected_size { |
|
147 | if packed.len() != expected_size { | |
152 | return Err(DirstatePackError::BadSize(expected_size, packed.len())); |
|
148 | return Err(DirstatePackError::BadSize(expected_size, packed.len())); | |
153 | } |
|
149 | } | |
154 |
|
150 | |||
155 | state_map.extend(new_state_map); |
|
|||
156 |
|
||||
157 | Ok(packed) |
|
151 | Ok(packed) | |
158 | } |
|
152 | } | |
159 | /// `now` is the duration in seconds since the Unix epoch |
|
153 | /// `now` is the duration in seconds since the Unix epoch | |
160 | #[cfg(feature = "dirstate-tree")] |
|
154 | #[cfg(feature = "dirstate-tree")] | |
161 | pub fn pack_dirstate( |
|
155 | pub fn pack_dirstate( | |
162 | state_map: &mut StateMap, |
|
156 | state_map: &mut StateMap, | |
163 | copy_map: &CopyMap, |
|
157 | copy_map: &CopyMap, | |
164 | parents: DirstateParents, |
|
158 | parents: DirstateParents, | |
165 | now: Duration, |
|
159 | now: Duration, | |
166 | ) -> Result<Vec<u8>, DirstatePackError> { |
|
160 | ) -> Result<Vec<u8>, DirstatePackError> { | |
167 | // TODO move away from i32 before 2038. |
|
161 | // TODO move away from i32 before 2038. | |
168 | let now: i32 = now.as_secs().try_into().expect("time overflow"); |
|
162 | let now: i32 = now.as_secs().try_into().expect("time overflow"); | |
169 |
|
163 | |||
170 | let expected_size: usize = state_map |
|
164 | let expected_size: usize = state_map | |
171 | .iter() |
|
165 | .iter() | |
172 | .map(|(filename, _)| { |
|
166 | .map(|(filename, _)| { | |
173 | let mut length = MIN_ENTRY_SIZE + filename.len(); |
|
167 | let mut length = MIN_ENTRY_SIZE + filename.len(); | |
174 | if let Some(copy) = copy_map.get(&filename) { |
|
168 | if let Some(copy) = copy_map.get(&filename) { | |
175 | length += copy.len() + 1; |
|
169 | length += copy.len() + 1; | |
176 | } |
|
170 | } | |
177 | length |
|
171 | length | |
178 | }) |
|
172 | }) | |
179 | .sum(); |
|
173 | .sum(); | |
180 | let expected_size = expected_size + PARENT_SIZE * 2; |
|
174 | let expected_size = expected_size + PARENT_SIZE * 2; | |
181 |
|
175 | |||
182 | let mut packed = Vec::with_capacity(expected_size); |
|
176 | let mut packed = Vec::with_capacity(expected_size); | |
183 | let mut new_state_map = vec![]; |
|
177 | let mut new_state_map = vec![]; | |
184 |
|
178 | |||
185 | packed.extend(&parents.p1); |
|
179 | packed.extend(&parents.p1); | |
186 | packed.extend(&parents.p2); |
|
180 | packed.extend(&parents.p2); | |
187 |
|
181 | |||
188 | for (filename, entry) in state_map.iter() { |
|
182 | for (filename, entry) in state_map.iter() { | |
189 | let new_filename = filename.to_owned(); |
|
183 | let new_filename = filename.to_owned(); | |
190 | let mut new_mtime: i32 = entry.mtime; |
|
184 | let mut new_mtime: i32 = entry.mtime; | |
191 | if entry.state == EntryState::Normal && entry.mtime == now { |
|
185 | if entry.state == EntryState::Normal && entry.mtime == now { | |
192 | // The file was last modified "simultaneously" with the current |
|
186 | // The file was last modified "simultaneously" with the current | |
193 | // write to dirstate (i.e. within the same second for file- |
|
187 | // write to dirstate (i.e. within the same second for file- | |
194 | // systems with a granularity of 1 sec). This commonly happens |
|
188 | // systems with a granularity of 1 sec). This commonly happens | |
195 | // for at least a couple of files on 'update'. |
|
189 | // for at least a couple of files on 'update'. | |
196 | // The user could change the file without changing its size |
|
190 | // The user could change the file without changing its size | |
197 | // within the same second. Invalidate the file's mtime in |
|
191 | // within the same second. Invalidate the file's mtime in | |
198 | // dirstate, forcing future 'status' calls to compare the |
|
192 | // dirstate, forcing future 'status' calls to compare the | |
199 | // contents of the file if the size is the same. This prevents |
|
193 | // contents of the file if the size is the same. This prevents | |
200 | // mistakenly treating such files as clean. |
|
194 | // mistakenly treating such files as clean. | |
201 | new_mtime = -1; |
|
195 | new_mtime = -1; | |
202 | new_state_map.push(( |
|
196 | new_state_map.push(( | |
203 | filename.to_owned(), |
|
197 | filename.to_owned(), | |
204 | DirstateEntry { |
|
198 | DirstateEntry { | |
205 | mtime: new_mtime, |
|
199 | mtime: new_mtime, | |
206 | ..entry |
|
200 | ..entry | |
207 | }, |
|
201 | }, | |
208 | )); |
|
202 | )); | |
209 | } |
|
203 | } | |
210 | let mut new_filename = new_filename.into_vec(); |
|
204 | let mut new_filename = new_filename.into_vec(); | |
211 | if let Some(copy) = copy_map.get(&filename) { |
|
205 | if let Some(copy) = copy_map.get(&filename) { | |
212 | new_filename.push(b'\0'); |
|
206 | new_filename.push(b'\0'); | |
213 | new_filename.extend(copy.bytes()); |
|
207 | new_filename.extend(copy.bytes()); | |
214 | } |
|
208 | } | |
215 |
|
209 | |||
216 | packed.write_u8(entry.state.into())?; |
|
210 | packed.write_u8(entry.state.into())?; | |
217 | packed.write_i32::<BigEndian>(entry.mode)?; |
|
211 | packed.write_i32::<BigEndian>(entry.mode)?; | |
218 | packed.write_i32::<BigEndian>(entry.size)?; |
|
212 | packed.write_i32::<BigEndian>(entry.size)?; | |
219 | packed.write_i32::<BigEndian>(new_mtime)?; |
|
213 | packed.write_i32::<BigEndian>(new_mtime)?; | |
220 | packed.write_i32::<BigEndian>(new_filename.len() as i32)?; |
|
214 | packed.write_i32::<BigEndian>(new_filename.len() as i32)?; | |
221 | packed.extend(new_filename) |
|
215 | packed.extend(new_filename) | |
222 | } |
|
216 | } | |
223 |
|
217 | |||
224 | if packed.len() != expected_size { |
|
218 | if packed.len() != expected_size { | |
225 | return Err(DirstatePackError::BadSize(expected_size, packed.len())); |
|
219 | return Err(DirstatePackError::BadSize(expected_size, packed.len())); | |
226 | } |
|
220 | } | |
227 |
|
221 | |||
228 | state_map.extend(new_state_map); |
|
222 | state_map.extend(new_state_map); | |
229 |
|
223 | |||
230 | Ok(packed) |
|
224 | Ok(packed) | |
231 | } |
|
225 | } | |
232 |
|
226 | |||
233 | #[cfg(test)] |
|
227 | #[cfg(test)] | |
234 | mod tests { |
|
228 | mod tests { | |
235 | use super::*; |
|
229 | use super::*; | |
236 | use crate::{utils::hg_path::HgPathBuf, FastHashMap}; |
|
230 | use crate::{utils::hg_path::HgPathBuf, FastHashMap}; | |
237 | use pretty_assertions::assert_eq; |
|
231 | use pretty_assertions::assert_eq; | |
238 |
|
232 | |||
239 | #[test] |
|
233 | #[test] | |
240 | fn test_pack_dirstate_empty() { |
|
234 | fn test_pack_dirstate_empty() { | |
241 | let mut state_map = StateMap::default(); |
|
235 | let mut state_map = StateMap::default(); | |
242 | let copymap = FastHashMap::default(); |
|
236 | let copymap = FastHashMap::default(); | |
243 | let parents = DirstateParents { |
|
237 | let parents = DirstateParents { | |
244 | p1: *b"12345678910111213141", |
|
238 | p1: *b"12345678910111213141", | |
245 | p2: *b"00000000000000000000", |
|
239 | p2: *b"00000000000000000000", | |
246 | }; |
|
240 | }; | |
247 | let now = Duration::new(15000000, 0); |
|
241 | let now = Duration::new(15000000, 0); | |
248 | let expected = b"1234567891011121314100000000000000000000".to_vec(); |
|
242 | let expected = b"1234567891011121314100000000000000000000".to_vec(); | |
249 |
|
243 | |||
250 | assert_eq!( |
|
244 | assert_eq!( | |
251 | expected, |
|
245 | expected, | |
252 | pack_dirstate(&mut state_map, ©map, parents, now).unwrap() |
|
246 | pack_dirstate(&mut state_map, ©map, parents, now).unwrap() | |
253 | ); |
|
247 | ); | |
254 |
|
248 | |||
255 | assert!(state_map.is_empty()) |
|
249 | assert!(state_map.is_empty()) | |
256 | } |
|
250 | } | |
257 | #[test] |
|
251 | #[test] | |
258 | fn test_pack_dirstate_one_entry() { |
|
252 | fn test_pack_dirstate_one_entry() { | |
259 | let expected_state_map: StateMap = [( |
|
253 | let expected_state_map: StateMap = [( | |
260 | HgPathBuf::from_bytes(b"f1"), |
|
254 | HgPathBuf::from_bytes(b"f1"), | |
261 | DirstateEntry { |
|
255 | DirstateEntry { | |
262 | state: EntryState::Normal, |
|
256 | state: EntryState::Normal, | |
263 | mode: 0o644, |
|
257 | mode: 0o644, | |
264 | size: 0, |
|
258 | size: 0, | |
265 | mtime: 791231220, |
|
259 | mtime: 791231220, | |
266 | }, |
|
260 | }, | |
267 | )] |
|
261 | )] | |
268 | .iter() |
|
262 | .iter() | |
269 | .cloned() |
|
263 | .cloned() | |
270 | .collect(); |
|
264 | .collect(); | |
271 | let mut state_map = expected_state_map.clone(); |
|
265 | let mut state_map = expected_state_map.clone(); | |
272 |
|
266 | |||
273 | let copymap = FastHashMap::default(); |
|
267 | let copymap = FastHashMap::default(); | |
274 | let parents = DirstateParents { |
|
268 | let parents = DirstateParents { | |
275 | p1: *b"12345678910111213141", |
|
269 | p1: *b"12345678910111213141", | |
276 | p2: *b"00000000000000000000", |
|
270 | p2: *b"00000000000000000000", | |
277 | }; |
|
271 | }; | |
278 | let now = Duration::new(15000000, 0); |
|
272 | let now = Duration::new(15000000, 0); | |
279 | let expected = [ |
|
273 | let expected = [ | |
280 | 49, 50, 51, 52, 53, 54, 55, 56, 57, 49, 48, 49, 49, 49, 50, 49, |
|
274 | 49, 50, 51, 52, 53, 54, 55, 56, 57, 49, 48, 49, 49, 49, 50, 49, | |
281 | 51, 49, 52, 49, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, |
|
275 | 51, 49, 52, 49, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, | |
282 | 48, 48, 48, 48, 48, 48, 48, 48, 110, 0, 0, 1, 164, 0, 0, 0, 0, 47, |
|
276 | 48, 48, 48, 48, 48, 48, 48, 48, 110, 0, 0, 1, 164, 0, 0, 0, 0, 47, | |
283 | 41, 58, 244, 0, 0, 0, 2, 102, 49, |
|
277 | 41, 58, 244, 0, 0, 0, 2, 102, 49, | |
284 | ] |
|
278 | ] | |
285 | .to_vec(); |
|
279 | .to_vec(); | |
286 |
|
280 | |||
287 | assert_eq!( |
|
281 | assert_eq!( | |
288 | expected, |
|
282 | expected, | |
289 | pack_dirstate(&mut state_map, ©map, parents, now).unwrap() |
|
283 | pack_dirstate(&mut state_map, ©map, parents, now).unwrap() | |
290 | ); |
|
284 | ); | |
291 |
|
285 | |||
292 | assert_eq!(expected_state_map, state_map); |
|
286 | assert_eq!(expected_state_map, state_map); | |
293 | } |
|
287 | } | |
294 | #[test] |
|
288 | #[test] | |
295 | fn test_pack_dirstate_one_entry_with_copy() { |
|
289 | fn test_pack_dirstate_one_entry_with_copy() { | |
296 | let expected_state_map: StateMap = [( |
|
290 | let expected_state_map: StateMap = [( | |
297 | HgPathBuf::from_bytes(b"f1"), |
|
291 | HgPathBuf::from_bytes(b"f1"), | |
298 | DirstateEntry { |
|
292 | DirstateEntry { | |
299 | state: EntryState::Normal, |
|
293 | state: EntryState::Normal, | |
300 | mode: 0o644, |
|
294 | mode: 0o644, | |
301 | size: 0, |
|
295 | size: 0, | |
302 | mtime: 791231220, |
|
296 | mtime: 791231220, | |
303 | }, |
|
297 | }, | |
304 | )] |
|
298 | )] | |
305 | .iter() |
|
299 | .iter() | |
306 | .cloned() |
|
300 | .cloned() | |
307 | .collect(); |
|
301 | .collect(); | |
308 | let mut state_map = expected_state_map.clone(); |
|
302 | let mut state_map = expected_state_map.clone(); | |
309 | let mut copymap = FastHashMap::default(); |
|
303 | let mut copymap = FastHashMap::default(); | |
310 | copymap.insert( |
|
304 | copymap.insert( | |
311 | HgPathBuf::from_bytes(b"f1"), |
|
305 | HgPathBuf::from_bytes(b"f1"), | |
312 | HgPathBuf::from_bytes(b"copyname"), |
|
306 | HgPathBuf::from_bytes(b"copyname"), | |
313 | ); |
|
307 | ); | |
314 | let parents = DirstateParents { |
|
308 | let parents = DirstateParents { | |
315 | p1: *b"12345678910111213141", |
|
309 | p1: *b"12345678910111213141", | |
316 | p2: *b"00000000000000000000", |
|
310 | p2: *b"00000000000000000000", | |
317 | }; |
|
311 | }; | |
318 | let now = Duration::new(15000000, 0); |
|
312 | let now = Duration::new(15000000, 0); | |
319 | let expected = [ |
|
313 | let expected = [ | |
320 | 49, 50, 51, 52, 53, 54, 55, 56, 57, 49, 48, 49, 49, 49, 50, 49, |
|
314 | 49, 50, 51, 52, 53, 54, 55, 56, 57, 49, 48, 49, 49, 49, 50, 49, | |
321 | 51, 49, 52, 49, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, |
|
315 | 51, 49, 52, 49, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, | |
322 | 48, 48, 48, 48, 48, 48, 48, 48, 110, 0, 0, 1, 164, 0, 0, 0, 0, 47, |
|
316 | 48, 48, 48, 48, 48, 48, 48, 48, 110, 0, 0, 1, 164, 0, 0, 0, 0, 47, | |
323 | 41, 58, 244, 0, 0, 0, 11, 102, 49, 0, 99, 111, 112, 121, 110, 97, |
|
317 | 41, 58, 244, 0, 0, 0, 11, 102, 49, 0, 99, 111, 112, 121, 110, 97, | |
324 | 109, 101, |
|
318 | 109, 101, | |
325 | ] |
|
319 | ] | |
326 | .to_vec(); |
|
320 | .to_vec(); | |
327 |
|
321 | |||
328 | assert_eq!( |
|
322 | assert_eq!( | |
329 | expected, |
|
323 | expected, | |
330 | pack_dirstate(&mut state_map, ©map, parents, now).unwrap() |
|
324 | pack_dirstate(&mut state_map, ©map, parents, now).unwrap() | |
331 | ); |
|
325 | ); | |
332 | assert_eq!(expected_state_map, state_map); |
|
326 | assert_eq!(expected_state_map, state_map); | |
333 | } |
|
327 | } | |
334 |
|
328 | |||
335 | #[test] |
|
329 | #[test] | |
336 | fn test_parse_pack_one_entry_with_copy() { |
|
330 | fn test_parse_pack_one_entry_with_copy() { | |
337 | let mut state_map: StateMap = [( |
|
331 | let mut state_map: StateMap = [( | |
338 | HgPathBuf::from_bytes(b"f1"), |
|
332 | HgPathBuf::from_bytes(b"f1"), | |
339 | DirstateEntry { |
|
333 | DirstateEntry { | |
340 | state: EntryState::Normal, |
|
334 | state: EntryState::Normal, | |
341 | mode: 0o644, |
|
335 | mode: 0o644, | |
342 | size: 0, |
|
336 | size: 0, | |
343 | mtime: 791231220, |
|
337 | mtime: 791231220, | |
344 | }, |
|
338 | }, | |
345 | )] |
|
339 | )] | |
346 | .iter() |
|
340 | .iter() | |
347 | .cloned() |
|
341 | .cloned() | |
348 | .collect(); |
|
342 | .collect(); | |
349 | let mut copymap = FastHashMap::default(); |
|
343 | let mut copymap = FastHashMap::default(); | |
350 | copymap.insert( |
|
344 | copymap.insert( | |
351 | HgPathBuf::from_bytes(b"f1"), |
|
345 | HgPathBuf::from_bytes(b"f1"), | |
352 | HgPathBuf::from_bytes(b"copyname"), |
|
346 | HgPathBuf::from_bytes(b"copyname"), | |
353 | ); |
|
347 | ); | |
354 | let parents = DirstateParents { |
|
348 | let parents = DirstateParents { | |
355 | p1: *b"12345678910111213141", |
|
349 | p1: *b"12345678910111213141", | |
356 | p2: *b"00000000000000000000", |
|
350 | p2: *b"00000000000000000000", | |
357 | }; |
|
351 | }; | |
358 | let now = Duration::new(15000000, 0); |
|
352 | let now = Duration::new(15000000, 0); | |
359 | let result = |
|
353 | let result = | |
360 | pack_dirstate(&mut state_map, ©map, parents.clone(), now) |
|
354 | pack_dirstate(&mut state_map, ©map, parents.clone(), now) | |
361 | .unwrap(); |
|
355 | .unwrap(); | |
362 |
|
356 | |||
363 | let (new_parents, entries, copies) = |
|
357 | let (new_parents, entries, copies) = | |
364 | parse_dirstate(result.as_slice()).unwrap(); |
|
358 | parse_dirstate(result.as_slice()).unwrap(); | |
365 | let new_state_map: StateMap = entries |
|
359 | let new_state_map: StateMap = entries | |
366 | .into_iter() |
|
360 | .into_iter() | |
367 | .map(|(path, entry)| (path.to_owned(), entry)) |
|
361 | .map(|(path, entry)| (path.to_owned(), entry)) | |
368 | .collect(); |
|
362 | .collect(); | |
369 | let new_copy_map: CopyMap = copies |
|
363 | let new_copy_map: CopyMap = copies | |
370 | .into_iter() |
|
364 | .into_iter() | |
371 | .map(|(path, copy)| (path.to_owned(), copy.to_owned())) |
|
365 | .map(|(path, copy)| (path.to_owned(), copy.to_owned())) | |
372 | .collect(); |
|
366 | .collect(); | |
373 |
|
367 | |||
374 | assert_eq!( |
|
368 | assert_eq!( | |
375 | (parents, state_map, copymap), |
|
369 | (parents, state_map, copymap), | |
376 | (new_parents, new_state_map, new_copy_map) |
|
370 | (new_parents, new_state_map, new_copy_map) | |
377 | ) |
|
371 | ) | |
378 | } |
|
372 | } | |
379 |
|
373 | |||
380 | #[test] |
|
374 | #[test] | |
381 | fn test_parse_pack_multiple_entries_with_copy() { |
|
375 | fn test_parse_pack_multiple_entries_with_copy() { | |
382 | let mut state_map: StateMap = [ |
|
376 | let mut state_map: StateMap = [ | |
383 | ( |
|
377 | ( | |
384 | HgPathBuf::from_bytes(b"f1"), |
|
378 | HgPathBuf::from_bytes(b"f1"), | |
385 | DirstateEntry { |
|
379 | DirstateEntry { | |
386 | state: EntryState::Normal, |
|
380 | state: EntryState::Normal, | |
387 | mode: 0o644, |
|
381 | mode: 0o644, | |
388 | size: 0, |
|
382 | size: 0, | |
389 | mtime: 791231220, |
|
383 | mtime: 791231220, | |
390 | }, |
|
384 | }, | |
391 | ), |
|
385 | ), | |
392 | ( |
|
386 | ( | |
393 | HgPathBuf::from_bytes(b"f2"), |
|
387 | HgPathBuf::from_bytes(b"f2"), | |
394 | DirstateEntry { |
|
388 | DirstateEntry { | |
395 | state: EntryState::Merged, |
|
389 | state: EntryState::Merged, | |
396 | mode: 0o777, |
|
390 | mode: 0o777, | |
397 | size: 1000, |
|
391 | size: 1000, | |
398 | mtime: 791231220, |
|
392 | mtime: 791231220, | |
399 | }, |
|
393 | }, | |
400 | ), |
|
394 | ), | |
401 | ( |
|
395 | ( | |
402 | HgPathBuf::from_bytes(b"f3"), |
|
396 | HgPathBuf::from_bytes(b"f3"), | |
403 | DirstateEntry { |
|
397 | DirstateEntry { | |
404 | state: EntryState::Removed, |
|
398 | state: EntryState::Removed, | |
405 | mode: 0o644, |
|
399 | mode: 0o644, | |
406 | size: 234553, |
|
400 | size: 234553, | |
407 | mtime: 791231220, |
|
401 | mtime: 791231220, | |
408 | }, |
|
402 | }, | |
409 | ), |
|
403 | ), | |
410 | ( |
|
404 | ( | |
411 | HgPathBuf::from_bytes(b"f4\xF6"), |
|
405 | HgPathBuf::from_bytes(b"f4\xF6"), | |
412 | DirstateEntry { |
|
406 | DirstateEntry { | |
413 | state: EntryState::Added, |
|
407 | state: EntryState::Added, | |
414 | mode: 0o644, |
|
408 | mode: 0o644, | |
415 | size: -1, |
|
409 | size: -1, | |
416 | mtime: -1, |
|
410 | mtime: -1, | |
417 | }, |
|
411 | }, | |
418 | ), |
|
412 | ), | |
419 | ] |
|
413 | ] | |
420 | .iter() |
|
414 | .iter() | |
421 | .cloned() |
|
415 | .cloned() | |
422 | .collect(); |
|
416 | .collect(); | |
423 | let mut copymap = FastHashMap::default(); |
|
417 | let mut copymap = FastHashMap::default(); | |
424 | copymap.insert( |
|
418 | copymap.insert( | |
425 | HgPathBuf::from_bytes(b"f1"), |
|
419 | HgPathBuf::from_bytes(b"f1"), | |
426 | HgPathBuf::from_bytes(b"copyname"), |
|
420 | HgPathBuf::from_bytes(b"copyname"), | |
427 | ); |
|
421 | ); | |
428 | copymap.insert( |
|
422 | copymap.insert( | |
429 | HgPathBuf::from_bytes(b"f4\xF6"), |
|
423 | HgPathBuf::from_bytes(b"f4\xF6"), | |
430 | HgPathBuf::from_bytes(b"copyname2"), |
|
424 | HgPathBuf::from_bytes(b"copyname2"), | |
431 | ); |
|
425 | ); | |
432 | let parents = DirstateParents { |
|
426 | let parents = DirstateParents { | |
433 | p1: *b"12345678910111213141", |
|
427 | p1: *b"12345678910111213141", | |
434 | p2: *b"00000000000000000000", |
|
428 | p2: *b"00000000000000000000", | |
435 | }; |
|
429 | }; | |
436 | let now = Duration::new(15000000, 0); |
|
430 | let now = Duration::new(15000000, 0); | |
437 | let result = |
|
431 | let result = | |
438 | pack_dirstate(&mut state_map, ©map, parents.clone(), now) |
|
432 | pack_dirstate(&mut state_map, ©map, parents.clone(), now) | |
439 | .unwrap(); |
|
433 | .unwrap(); | |
440 |
|
434 | |||
441 | let (new_parents, entries, copies) = |
|
435 | let (new_parents, entries, copies) = | |
442 | parse_dirstate(result.as_slice()).unwrap(); |
|
436 | parse_dirstate(result.as_slice()).unwrap(); | |
443 | let new_state_map: StateMap = entries |
|
437 | let new_state_map: StateMap = entries | |
444 | .into_iter() |
|
438 | .into_iter() | |
445 | .map(|(path, entry)| (path.to_owned(), entry)) |
|
439 | .map(|(path, entry)| (path.to_owned(), entry)) | |
446 | .collect(); |
|
440 | .collect(); | |
447 | let new_copy_map: CopyMap = copies |
|
441 | let new_copy_map: CopyMap = copies | |
448 | .into_iter() |
|
442 | .into_iter() | |
449 | .map(|(path, copy)| (path.to_owned(), copy.to_owned())) |
|
443 | .map(|(path, copy)| (path.to_owned(), copy.to_owned())) | |
450 | .collect(); |
|
444 | .collect(); | |
451 |
|
445 | |||
452 | assert_eq!( |
|
446 | assert_eq!( | |
453 | (parents, state_map, copymap), |
|
447 | (parents, state_map, copymap), | |
454 | (new_parents, new_state_map, new_copy_map) |
|
448 | (new_parents, new_state_map, new_copy_map) | |
455 | ) |
|
449 | ) | |
456 | } |
|
450 | } | |
457 |
|
451 | |||
458 | #[test] |
|
452 | #[test] | |
459 | /// https://www.mercurial-scm.org/repo/hg/rev/af3f26b6bba4 |
|
453 | /// https://www.mercurial-scm.org/repo/hg/rev/af3f26b6bba4 | |
460 | fn test_parse_pack_one_entry_with_copy_and_time_conflict() { |
|
454 | fn test_parse_pack_one_entry_with_copy_and_time_conflict() { | |
461 | let mut state_map: StateMap = [( |
|
455 | let mut state_map: StateMap = [( | |
462 | HgPathBuf::from_bytes(b"f1"), |
|
456 | HgPathBuf::from_bytes(b"f1"), | |
463 | DirstateEntry { |
|
457 | DirstateEntry { | |
464 | state: EntryState::Normal, |
|
458 | state: EntryState::Normal, | |
465 | mode: 0o644, |
|
459 | mode: 0o644, | |
466 | size: 0, |
|
460 | size: 0, | |
467 | mtime: 15000000, |
|
461 | mtime: 15000000, | |
468 | }, |
|
462 | }, | |
469 | )] |
|
463 | )] | |
470 | .iter() |
|
464 | .iter() | |
471 | .cloned() |
|
465 | .cloned() | |
472 | .collect(); |
|
466 | .collect(); | |
473 | let mut copymap = FastHashMap::default(); |
|
467 | let mut copymap = FastHashMap::default(); | |
474 | copymap.insert( |
|
468 | copymap.insert( | |
475 | HgPathBuf::from_bytes(b"f1"), |
|
469 | HgPathBuf::from_bytes(b"f1"), | |
476 | HgPathBuf::from_bytes(b"copyname"), |
|
470 | HgPathBuf::from_bytes(b"copyname"), | |
477 | ); |
|
471 | ); | |
478 | let parents = DirstateParents { |
|
472 | let parents = DirstateParents { | |
479 | p1: *b"12345678910111213141", |
|
473 | p1: *b"12345678910111213141", | |
480 | p2: *b"00000000000000000000", |
|
474 | p2: *b"00000000000000000000", | |
481 | }; |
|
475 | }; | |
482 | let now = Duration::new(15000000, 0); |
|
476 | let now = Duration::new(15000000, 0); | |
483 | let result = |
|
477 | let result = | |
484 | pack_dirstate(&mut state_map, ©map, parents.clone(), now) |
|
478 | pack_dirstate(&mut state_map, ©map, parents.clone(), now) | |
485 | .unwrap(); |
|
479 | .unwrap(); | |
486 |
|
480 | |||
487 | let (new_parents, entries, copies) = |
|
481 | let (new_parents, entries, copies) = | |
488 | parse_dirstate(result.as_slice()).unwrap(); |
|
482 | parse_dirstate(result.as_slice()).unwrap(); | |
489 | let new_state_map: StateMap = entries |
|
483 | let new_state_map: StateMap = entries | |
490 | .into_iter() |
|
484 | .into_iter() | |
491 | .map(|(path, entry)| (path.to_owned(), entry)) |
|
485 | .map(|(path, entry)| (path.to_owned(), entry)) | |
492 | .collect(); |
|
486 | .collect(); | |
493 | let new_copy_map: CopyMap = copies |
|
487 | let new_copy_map: CopyMap = copies | |
494 | .into_iter() |
|
488 | .into_iter() | |
495 | .map(|(path, copy)| (path.to_owned(), copy.to_owned())) |
|
489 | .map(|(path, copy)| (path.to_owned(), copy.to_owned())) | |
496 | .collect(); |
|
490 | .collect(); | |
497 |
|
491 | |||
498 | assert_eq!( |
|
492 | assert_eq!( | |
499 | ( |
|
493 | ( | |
500 | parents, |
|
494 | parents, | |
501 | [( |
|
495 | [( | |
502 | HgPathBuf::from_bytes(b"f1"), |
|
496 | HgPathBuf::from_bytes(b"f1"), | |
503 | DirstateEntry { |
|
497 | DirstateEntry { | |
504 | state: EntryState::Normal, |
|
498 | state: EntryState::Normal, | |
505 | mode: 0o644, |
|
499 | mode: 0o644, | |
506 | size: 0, |
|
500 | size: 0, | |
507 | mtime: -1 |
|
501 | mtime: -1 | |
508 | } |
|
502 | } | |
509 | )] |
|
503 | )] | |
510 | .iter() |
|
504 | .iter() | |
511 | .cloned() |
|
505 | .cloned() | |
512 | .collect::<StateMap>(), |
|
506 | .collect::<StateMap>(), | |
513 | copymap, |
|
507 | copymap, | |
514 | ), |
|
508 | ), | |
515 | (new_parents, new_state_map, new_copy_map) |
|
509 | (new_parents, new_state_map, new_copy_map) | |
516 | ) |
|
510 | ) | |
517 | } |
|
511 | } | |
518 | } |
|
512 | } |
General Comments 0
You need to be logged in to leave comments.
Login now