##// END OF EJS Templates
rust-dirstate: create dirstate submodule...
Raphaël Gomès -
r42617:d3b5cbe3 default
parent child Browse files
Show More
@@ -0,0 +1,28 b''
1 pub mod parsers;
2
3 #[derive(Debug, PartialEq, Copy, Clone)]
4 pub struct DirstateParents<'a> {
5 pub p1: &'a [u8],
6 pub p2: &'a [u8],
7 }
8
9 /// The C implementation uses all signed types. This will be an issue
10 /// either when 4GB+ source files are commonplace or in 2038, whichever
11 /// comes first.
12 #[derive(Debug, PartialEq)]
13 pub struct DirstateEntry {
14 pub state: i8,
15 pub mode: i32,
16 pub mtime: i32,
17 pub size: i32,
18 }
19
20 pub type DirstateVec = Vec<(Vec<u8>, DirstateEntry)>;
21
22 #[derive(Debug, PartialEq)]
23 pub struct CopyVecEntry<'a> {
24 pub path: &'a [u8],
25 pub copy_path: &'a [u8],
26 }
27
28 pub type CopyVec<'a> = Vec<CopyVecEntry<'a>>;
@@ -1,409 +1,388 b''
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 byteorder::{BigEndian, ReadBytesExt, WriteBytesExt};
6 use byteorder::{BigEndian, ReadBytesExt, WriteBytesExt};
7 use std::collections::HashMap;
7 use std::collections::HashMap;
8 use std::io::Cursor;
8 use std::io::Cursor;
9 use {DirstatePackError, DirstateParseError};
9 use {
10
10 CopyVec, CopyVecEntry, DirstateEntry, DirstatePackError, DirstateParents,
11 #[derive(Debug, PartialEq, Copy, Clone)]
11 DirstateParseError, DirstateVec,
12 pub struct DirstateParents<'a> {
12 };
13 pub p1: &'a [u8],
14 pub p2: &'a [u8],
15 }
16 /// The C implementation uses all signed types. This will be an issue
17 /// either when 4GB+ source files are commonplace or in 2038, whichever
18 /// comes first.
19 #[derive(Debug, PartialEq)]
20 pub struct DirstateEntry {
21 pub state: i8,
22 pub mode: i32,
23 pub mtime: i32,
24 pub size: i32,
25 }
26 pub type DirstateVec = Vec<(Vec<u8>, DirstateEntry)>;
27
28 #[derive(Debug, PartialEq)]
29 pub struct CopyVecEntry<'a> {
30 pub path: &'a [u8],
31 pub copy_path: &'a [u8],
32 }
33 pub type CopyVec<'a> = Vec<CopyVecEntry<'a>>;
34
13
35 /// Parents are stored in the dirstate as byte hashes.
14 /// Parents are stored in the dirstate as byte hashes.
36 const PARENT_SIZE: usize = 20;
15 const PARENT_SIZE: usize = 20;
37 /// Dirstate entries have a static part of 8 + 32 + 32 + 32 + 32 bits.
16 /// Dirstate entries have a static part of 8 + 32 + 32 + 32 + 32 bits.
38 const MIN_ENTRY_SIZE: usize = 17;
17 const MIN_ENTRY_SIZE: usize = 17;
39
18
40 pub fn parse_dirstate(
19 pub fn parse_dirstate(
41 contents: &[u8],
20 contents: &[u8],
42 ) -> Result<(DirstateParents, DirstateVec, CopyVec), DirstateParseError> {
21 ) -> Result<(DirstateParents, DirstateVec, CopyVec), DirstateParseError> {
43 if contents.len() < PARENT_SIZE * 2 {
22 if contents.len() < PARENT_SIZE * 2 {
44 return Err(DirstateParseError::TooLittleData);
23 return Err(DirstateParseError::TooLittleData);
45 }
24 }
46
25
47 let mut dirstate_vec = vec![];
26 let mut dirstate_vec = vec![];
48 let mut copies = vec![];
27 let mut copies = vec![];
49 let mut curr_pos = PARENT_SIZE * 2;
28 let mut curr_pos = PARENT_SIZE * 2;
50 let parents = DirstateParents {
29 let parents = DirstateParents {
51 p1: &contents[..PARENT_SIZE],
30 p1: &contents[..PARENT_SIZE],
52 p2: &contents[PARENT_SIZE..curr_pos],
31 p2: &contents[PARENT_SIZE..curr_pos],
53 };
32 };
54
33
55 while curr_pos < contents.len() {
34 while curr_pos < contents.len() {
56 if curr_pos + MIN_ENTRY_SIZE > contents.len() {
35 if curr_pos + MIN_ENTRY_SIZE > contents.len() {
57 return Err(DirstateParseError::Overflow);
36 return Err(DirstateParseError::Overflow);
58 }
37 }
59 let entry_bytes = &contents[curr_pos..];
38 let entry_bytes = &contents[curr_pos..];
60
39
61 let mut cursor = Cursor::new(entry_bytes);
40 let mut cursor = Cursor::new(entry_bytes);
62 let state = cursor.read_i8()?;
41 let state = cursor.read_i8()?;
63 let mode = cursor.read_i32::<BigEndian>()?;
42 let mode = cursor.read_i32::<BigEndian>()?;
64 let size = cursor.read_i32::<BigEndian>()?;
43 let size = cursor.read_i32::<BigEndian>()?;
65 let mtime = cursor.read_i32::<BigEndian>()?;
44 let mtime = cursor.read_i32::<BigEndian>()?;
66 let path_len = cursor.read_i32::<BigEndian>()? as usize;
45 let path_len = cursor.read_i32::<BigEndian>()? as usize;
67
46
68 if path_len > contents.len() - curr_pos {
47 if path_len > contents.len() - curr_pos {
69 return Err(DirstateParseError::Overflow);
48 return Err(DirstateParseError::Overflow);
70 }
49 }
71
50
72 // Slice instead of allocating a Vec needed for `read_exact`
51 // Slice instead of allocating a Vec needed for `read_exact`
73 let path = &entry_bytes[MIN_ENTRY_SIZE..MIN_ENTRY_SIZE + (path_len)];
52 let path = &entry_bytes[MIN_ENTRY_SIZE..MIN_ENTRY_SIZE + (path_len)];
74
53
75 let (path, copy) = match memchr::memchr(0, path) {
54 let (path, copy) = match memchr::memchr(0, path) {
76 None => (path, None),
55 None => (path, None),
77 Some(i) => (&path[..i], Some(&path[(i + 1)..])),
56 Some(i) => (&path[..i], Some(&path[(i + 1)..])),
78 };
57 };
79
58
80 if let Some(copy_path) = copy {
59 if let Some(copy_path) = copy {
81 copies.push(CopyVecEntry { path, copy_path });
60 copies.push(CopyVecEntry { path, copy_path });
82 };
61 };
83 dirstate_vec.push((
62 dirstate_vec.push((
84 path.to_owned(),
63 path.to_owned(),
85 DirstateEntry {
64 DirstateEntry {
86 state,
65 state,
87 mode,
66 mode,
88 size,
67 size,
89 mtime,
68 mtime,
90 },
69 },
91 ));
70 ));
92 curr_pos = curr_pos + MIN_ENTRY_SIZE + (path_len);
71 curr_pos = curr_pos + MIN_ENTRY_SIZE + (path_len);
93 }
72 }
94
73
95 Ok((parents, dirstate_vec, copies))
74 Ok((parents, dirstate_vec, copies))
96 }
75 }
97
76
98 pub fn pack_dirstate(
77 pub fn pack_dirstate(
99 dirstate_vec: &DirstateVec,
78 dirstate_vec: &DirstateVec,
100 copymap: &HashMap<Vec<u8>, Vec<u8>>,
79 copymap: &HashMap<Vec<u8>, Vec<u8>>,
101 parents: DirstateParents,
80 parents: DirstateParents,
102 now: i32,
81 now: i32,
103 ) -> Result<(Vec<u8>, DirstateVec), DirstatePackError> {
82 ) -> Result<(Vec<u8>, DirstateVec), DirstatePackError> {
104 if parents.p1.len() != PARENT_SIZE || parents.p2.len() != PARENT_SIZE {
83 if parents.p1.len() != PARENT_SIZE || parents.p2.len() != PARENT_SIZE {
105 return Err(DirstatePackError::CorruptedParent);
84 return Err(DirstatePackError::CorruptedParent);
106 }
85 }
107
86
108 let expected_size: usize = dirstate_vec
87 let expected_size: usize = dirstate_vec
109 .iter()
88 .iter()
110 .map(|(ref filename, _)| {
89 .map(|(ref filename, _)| {
111 let mut length = MIN_ENTRY_SIZE + filename.len();
90 let mut length = MIN_ENTRY_SIZE + filename.len();
112 if let Some(ref copy) = copymap.get(filename) {
91 if let Some(ref copy) = copymap.get(filename) {
113 length += copy.len() + 1;
92 length += copy.len() + 1;
114 }
93 }
115 length
94 length
116 })
95 })
117 .sum();
96 .sum();
118 let expected_size = expected_size + PARENT_SIZE * 2;
97 let expected_size = expected_size + PARENT_SIZE * 2;
119
98
120 let mut packed = Vec::with_capacity(expected_size);
99 let mut packed = Vec::with_capacity(expected_size);
121 let mut new_dirstate_vec = vec![];
100 let mut new_dirstate_vec = vec![];
122
101
123 packed.extend(parents.p1);
102 packed.extend(parents.p1);
124 packed.extend(parents.p2);
103 packed.extend(parents.p2);
125
104
126 for (ref filename, entry) in dirstate_vec {
105 for (ref filename, entry) in dirstate_vec {
127 let mut new_filename: Vec<u8> = filename.to_owned();
106 let mut new_filename: Vec<u8> = filename.to_owned();
128 let mut new_mtime: i32 = entry.mtime;
107 let mut new_mtime: i32 = entry.mtime;
129 if entry.state == 'n' as i8 && entry.mtime == now.into() {
108 if entry.state == 'n' as i8 && entry.mtime == now.into() {
130 // The file was last modified "simultaneously" with the current
109 // The file was last modified "simultaneously" with the current
131 // write to dirstate (i.e. within the same second for file-
110 // write to dirstate (i.e. within the same second for file-
132 // systems with a granularity of 1 sec). This commonly happens
111 // systems with a granularity of 1 sec). This commonly happens
133 // for at least a couple of files on 'update'.
112 // for at least a couple of files on 'update'.
134 // The user could change the file without changing its size
113 // The user could change the file without changing its size
135 // within the same second. Invalidate the file's mtime in
114 // within the same second. Invalidate the file's mtime in
136 // dirstate, forcing future 'status' calls to compare the
115 // dirstate, forcing future 'status' calls to compare the
137 // contents of the file if the size is the same. This prevents
116 // contents of the file if the size is the same. This prevents
138 // mistakenly treating such files as clean.
117 // mistakenly treating such files as clean.
139 new_mtime = -1;
118 new_mtime = -1;
140 new_dirstate_vec.push((
119 new_dirstate_vec.push((
141 filename.to_owned(),
120 filename.to_owned(),
142 DirstateEntry {
121 DirstateEntry {
143 mtime: new_mtime,
122 mtime: new_mtime,
144 ..*entry
123 ..*entry
145 },
124 },
146 ));
125 ));
147 }
126 }
148
127
149 if let Some(copy) = copymap.get(filename) {
128 if let Some(copy) = copymap.get(filename) {
150 new_filename.push('\0' as u8);
129 new_filename.push('\0' as u8);
151 new_filename.extend(copy);
130 new_filename.extend(copy);
152 }
131 }
153
132
154 packed.write_i8(entry.state)?;
133 packed.write_i8(entry.state)?;
155 packed.write_i32::<BigEndian>(entry.mode)?;
134 packed.write_i32::<BigEndian>(entry.mode)?;
156 packed.write_i32::<BigEndian>(entry.size)?;
135 packed.write_i32::<BigEndian>(entry.size)?;
157 packed.write_i32::<BigEndian>(new_mtime)?;
136 packed.write_i32::<BigEndian>(new_mtime)?;
158 packed.write_i32::<BigEndian>(new_filename.len() as i32)?;
137 packed.write_i32::<BigEndian>(new_filename.len() as i32)?;
159 packed.extend(new_filename)
138 packed.extend(new_filename)
160 }
139 }
161
140
162 if packed.len() != expected_size {
141 if packed.len() != expected_size {
163 return Err(DirstatePackError::BadSize(expected_size, packed.len()));
142 return Err(DirstatePackError::BadSize(expected_size, packed.len()));
164 }
143 }
165
144
166 Ok((packed, new_dirstate_vec))
145 Ok((packed, new_dirstate_vec))
167 }
146 }
168
147
169 #[cfg(test)]
148 #[cfg(test)]
170 mod tests {
149 mod tests {
171 use super::*;
150 use super::*;
172
151
173 #[test]
152 #[test]
174 fn test_pack_dirstate_empty() {
153 fn test_pack_dirstate_empty() {
175 let dirstate_vec: DirstateVec = vec![];
154 let dirstate_vec: DirstateVec = vec![];
176 let copymap = HashMap::new();
155 let copymap = HashMap::new();
177 let parents = DirstateParents {
156 let parents = DirstateParents {
178 p1: b"12345678910111213141",
157 p1: b"12345678910111213141",
179 p2: b"00000000000000000000",
158 p2: b"00000000000000000000",
180 };
159 };
181 let now: i32 = 15000000;
160 let now: i32 = 15000000;
182 let expected =
161 let expected =
183 (b"1234567891011121314100000000000000000000".to_vec(), vec![]);
162 (b"1234567891011121314100000000000000000000".to_vec(), vec![]);
184
163
185 assert_eq!(
164 assert_eq!(
186 expected,
165 expected,
187 pack_dirstate(&dirstate_vec, &copymap, parents, now).unwrap()
166 pack_dirstate(&dirstate_vec, &copymap, parents, now).unwrap()
188 );
167 );
189 }
168 }
190 #[test]
169 #[test]
191 fn test_pack_dirstate_one_entry() {
170 fn test_pack_dirstate_one_entry() {
192 let dirstate_vec: DirstateVec = vec![(
171 let dirstate_vec: DirstateVec = vec![(
193 vec!['f' as u8, '1' as u8],
172 vec!['f' as u8, '1' as u8],
194 DirstateEntry {
173 DirstateEntry {
195 state: 'n' as i8,
174 state: 'n' as i8,
196 mode: 0o644,
175 mode: 0o644,
197 size: 0,
176 size: 0,
198 mtime: 791231220,
177 mtime: 791231220,
199 },
178 },
200 )];
179 )];
201 let copymap = HashMap::new();
180 let copymap = HashMap::new();
202 let parents = DirstateParents {
181 let parents = DirstateParents {
203 p1: b"12345678910111213141",
182 p1: b"12345678910111213141",
204 p2: b"00000000000000000000",
183 p2: b"00000000000000000000",
205 };
184 };
206 let now: i32 = 15000000;
185 let now: i32 = 15000000;
207 let expected = (
186 let expected = (
208 [
187 [
209 49, 50, 51, 52, 53, 54, 55, 56, 57, 49, 48, 49, 49, 49, 50,
188 49, 50, 51, 52, 53, 54, 55, 56, 57, 49, 48, 49, 49, 49, 50,
210 49, 51, 49, 52, 49, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48,
189 49, 51, 49, 52, 49, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48,
211 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 110, 0, 0, 1, 164, 0,
190 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 110, 0, 0, 1, 164, 0,
212 0, 0, 0, 47, 41, 58, 244, 0, 0, 0, 2, 102, 49,
191 0, 0, 0, 47, 41, 58, 244, 0, 0, 0, 2, 102, 49,
213 ]
192 ]
214 .to_vec(),
193 .to_vec(),
215 vec![],
194 vec![],
216 );
195 );
217
196
218 assert_eq!(
197 assert_eq!(
219 expected,
198 expected,
220 pack_dirstate(&dirstate_vec, &copymap, parents, now).unwrap()
199 pack_dirstate(&dirstate_vec, &copymap, parents, now).unwrap()
221 );
200 );
222 }
201 }
223 #[test]
202 #[test]
224 fn test_pack_dirstate_one_entry_with_copy() {
203 fn test_pack_dirstate_one_entry_with_copy() {
225 let dirstate_vec: DirstateVec = vec![(
204 let dirstate_vec: DirstateVec = vec![(
226 b"f1".to_vec(),
205 b"f1".to_vec(),
227 DirstateEntry {
206 DirstateEntry {
228 state: 'n' as i8,
207 state: 'n' as i8,
229 mode: 0o644,
208 mode: 0o644,
230 size: 0,
209 size: 0,
231 mtime: 791231220,
210 mtime: 791231220,
232 },
211 },
233 )];
212 )];
234 let mut copymap = HashMap::new();
213 let mut copymap = HashMap::new();
235 copymap.insert(b"f1".to_vec(), b"copyname".to_vec());
214 copymap.insert(b"f1".to_vec(), b"copyname".to_vec());
236 let parents = DirstateParents {
215 let parents = DirstateParents {
237 p1: b"12345678910111213141",
216 p1: b"12345678910111213141",
238 p2: b"00000000000000000000",
217 p2: b"00000000000000000000",
239 };
218 };
240 let now: i32 = 15000000;
219 let now: i32 = 15000000;
241 let expected = (
220 let expected = (
242 [
221 [
243 49, 50, 51, 52, 53, 54, 55, 56, 57, 49, 48, 49, 49, 49, 50,
222 49, 50, 51, 52, 53, 54, 55, 56, 57, 49, 48, 49, 49, 49, 50,
244 49, 51, 49, 52, 49, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48,
223 49, 51, 49, 52, 49, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48,
245 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 110, 0, 0, 1, 164, 0,
224 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 110, 0, 0, 1, 164, 0,
246 0, 0, 0, 47, 41, 58, 244, 0, 0, 0, 11, 102, 49, 0, 99, 111,
225 0, 0, 0, 47, 41, 58, 244, 0, 0, 0, 11, 102, 49, 0, 99, 111,
247 112, 121, 110, 97, 109, 101,
226 112, 121, 110, 97, 109, 101,
248 ]
227 ]
249 .to_vec(),
228 .to_vec(),
250 vec![],
229 vec![],
251 );
230 );
252
231
253 assert_eq!(
232 assert_eq!(
254 expected,
233 expected,
255 pack_dirstate(&dirstate_vec, &copymap, parents, now).unwrap()
234 pack_dirstate(&dirstate_vec, &copymap, parents, now).unwrap()
256 );
235 );
257 }
236 }
258
237
259 #[test]
238 #[test]
260 fn test_parse_pack_one_entry_with_copy() {
239 fn test_parse_pack_one_entry_with_copy() {
261 let dirstate_vec: DirstateVec = vec![(
240 let dirstate_vec: DirstateVec = vec![(
262 b"f1".to_vec(),
241 b"f1".to_vec(),
263 DirstateEntry {
242 DirstateEntry {
264 state: 'n' as i8,
243 state: 'n' as i8,
265 mode: 0o644,
244 mode: 0o644,
266 size: 0,
245 size: 0,
267 mtime: 791231220,
246 mtime: 791231220,
268 },
247 },
269 )];
248 )];
270 let mut copymap = HashMap::new();
249 let mut copymap = HashMap::new();
271 copymap.insert(b"f1".to_vec(), b"copyname".to_vec());
250 copymap.insert(b"f1".to_vec(), b"copyname".to_vec());
272 let parents = DirstateParents {
251 let parents = DirstateParents {
273 p1: b"12345678910111213141",
252 p1: b"12345678910111213141",
274 p2: b"00000000000000000000",
253 p2: b"00000000000000000000",
275 };
254 };
276 let now: i32 = 15000000;
255 let now: i32 = 15000000;
277 let result =
256 let result =
278 pack_dirstate(&dirstate_vec, &copymap, parents, now).unwrap();
257 pack_dirstate(&dirstate_vec, &copymap, parents, now).unwrap();
279
258
280 assert_eq!(
259 assert_eq!(
281 (
260 (
282 parents,
261 parents,
283 dirstate_vec,
262 dirstate_vec,
284 copymap
263 copymap
285 .iter()
264 .iter()
286 .map(|(k, v)| CopyVecEntry {
265 .map(|(k, v)| CopyVecEntry {
287 path: k.as_slice(),
266 path: k.as_slice(),
288 copy_path: v.as_slice()
267 copy_path: v.as_slice()
289 })
268 })
290 .collect()
269 .collect()
291 ),
270 ),
292 parse_dirstate(result.0.as_slice()).unwrap()
271 parse_dirstate(result.0.as_slice()).unwrap()
293 )
272 )
294 }
273 }
295
274
296 #[test]
275 #[test]
297 fn test_parse_pack_multiple_entries_with_copy() {
276 fn test_parse_pack_multiple_entries_with_copy() {
298 let dirstate_vec: DirstateVec = vec![
277 let dirstate_vec: DirstateVec = vec![
299 (
278 (
300 b"f1".to_vec(),
279 b"f1".to_vec(),
301 DirstateEntry {
280 DirstateEntry {
302 state: 'n' as i8,
281 state: 'n' as i8,
303 mode: 0o644,
282 mode: 0o644,
304 size: 0,
283 size: 0,
305 mtime: 791231220,
284 mtime: 791231220,
306 },
285 },
307 ),
286 ),
308 (
287 (
309 b"f2".to_vec(),
288 b"f2".to_vec(),
310 DirstateEntry {
289 DirstateEntry {
311 state: 'm' as i8,
290 state: 'm' as i8,
312 mode: 0o777,
291 mode: 0o777,
313 size: 1000,
292 size: 1000,
314 mtime: 791231220,
293 mtime: 791231220,
315 },
294 },
316 ),
295 ),
317 (
296 (
318 b"f3".to_vec(),
297 b"f3".to_vec(),
319 DirstateEntry {
298 DirstateEntry {
320 state: 'r' as i8,
299 state: 'r' as i8,
321 mode: 0o644,
300 mode: 0o644,
322 size: 234553,
301 size: 234553,
323 mtime: 791231220,
302 mtime: 791231220,
324 },
303 },
325 ),
304 ),
326 (
305 (
327 b"f4\xF6".to_vec(),
306 b"f4\xF6".to_vec(),
328 DirstateEntry {
307 DirstateEntry {
329 state: 'a' as i8,
308 state: 'a' as i8,
330 mode: 0o644,
309 mode: 0o644,
331 size: -1,
310 size: -1,
332 mtime: -1,
311 mtime: -1,
333 },
312 },
334 ),
313 ),
335 ];
314 ];
336 let mut copymap = HashMap::new();
315 let mut copymap = HashMap::new();
337 copymap.insert(b"f1".to_vec(), b"copyname".to_vec());
316 copymap.insert(b"f1".to_vec(), b"copyname".to_vec());
338 copymap.insert(b"f4\xF6".to_vec(), b"copyname2".to_vec());
317 copymap.insert(b"f4\xF6".to_vec(), b"copyname2".to_vec());
339 let parents = DirstateParents {
318 let parents = DirstateParents {
340 p1: b"12345678910111213141",
319 p1: b"12345678910111213141",
341 p2: b"00000000000000000000",
320 p2: b"00000000000000000000",
342 };
321 };
343 let now: i32 = 15000000;
322 let now: i32 = 15000000;
344 let result =
323 let result =
345 pack_dirstate(&dirstate_vec, &copymap, parents, now).unwrap();
324 pack_dirstate(&dirstate_vec, &copymap, parents, now).unwrap();
346
325
347 assert_eq!(
326 assert_eq!(
348 (parents, dirstate_vec, copymap),
327 (parents, dirstate_vec, copymap),
349 parse_dirstate(result.0.as_slice())
328 parse_dirstate(result.0.as_slice())
350 .and_then(|(p, dvec, cvec)| Ok((
329 .and_then(|(p, dvec, cvec)| Ok((
351 p,
330 p,
352 dvec,
331 dvec,
353 cvec.iter()
332 cvec.iter()
354 .map(|entry| (
333 .map(|entry| (
355 entry.path.to_vec(),
334 entry.path.to_vec(),
356 entry.copy_path.to_vec()
335 entry.copy_path.to_vec()
357 ))
336 ))
358 .collect()
337 .collect()
359 )))
338 )))
360 .unwrap()
339 .unwrap()
361 )
340 )
362 }
341 }
363
342
364 #[test]
343 #[test]
365 /// https://www.mercurial-scm.org/repo/hg/rev/af3f26b6bba4
344 /// https://www.mercurial-scm.org/repo/hg/rev/af3f26b6bba4
366 fn test_parse_pack_one_entry_with_copy_and_time_conflict() {
345 fn test_parse_pack_one_entry_with_copy_and_time_conflict() {
367 let dirstate_vec: DirstateVec = vec![(
346 let dirstate_vec: DirstateVec = vec![(
368 b"f1".to_vec(),
347 b"f1".to_vec(),
369 DirstateEntry {
348 DirstateEntry {
370 state: 'n' as i8,
349 state: 'n' as i8,
371 mode: 0o644,
350 mode: 0o644,
372 size: 0,
351 size: 0,
373 mtime: 15000000,
352 mtime: 15000000,
374 },
353 },
375 )];
354 )];
376 let mut copymap = HashMap::new();
355 let mut copymap = HashMap::new();
377 copymap.insert(b"f1".to_vec(), b"copyname".to_vec());
356 copymap.insert(b"f1".to_vec(), b"copyname".to_vec());
378 let parents = DirstateParents {
357 let parents = DirstateParents {
379 p1: b"12345678910111213141",
358 p1: b"12345678910111213141",
380 p2: b"00000000000000000000",
359 p2: b"00000000000000000000",
381 };
360 };
382 let now: i32 = 15000000;
361 let now: i32 = 15000000;
383 let result =
362 let result =
384 pack_dirstate(&dirstate_vec, &copymap, parents, now).unwrap();
363 pack_dirstate(&dirstate_vec, &copymap, parents, now).unwrap();
385
364
386 assert_eq!(
365 assert_eq!(
387 (
366 (
388 parents,
367 parents,
389 vec![(
368 vec![(
390 b"f1".to_vec(),
369 b"f1".to_vec(),
391 DirstateEntry {
370 DirstateEntry {
392 state: 'n' as i8,
371 state: 'n' as i8,
393 mode: 0o644,
372 mode: 0o644,
394 size: 0,
373 size: 0,
395 mtime: -1
374 mtime: -1
396 }
375 }
397 )],
376 )],
398 copymap
377 copymap
399 .iter()
378 .iter()
400 .map(|(k, v)| CopyVecEntry {
379 .map(|(k, v)| CopyVecEntry {
401 path: k.as_slice(),
380 path: k.as_slice(),
402 copy_path: v.as_slice()
381 copy_path: v.as_slice()
403 })
382 })
404 .collect()
383 .collect()
405 ),
384 ),
406 parse_dirstate(result.0.as_slice()).unwrap()
385 parse_dirstate(result.0.as_slice()).unwrap()
407 )
386 )
408 }
387 }
409 }
388 }
@@ -1,102 +1,102 b''
1 // Copyright 2018 Georges Racinet <gracinet@anybox.fr>
1 // Copyright 2018 Georges Racinet <gracinet@anybox.fr>
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 extern crate byteorder;
5 extern crate byteorder;
6 extern crate memchr;
6 extern crate memchr;
7 #[macro_use]
7 #[macro_use]
8 extern crate lazy_static;
8 extern crate lazy_static;
9 extern crate regex;
9 extern crate regex;
10
10
11 mod ancestors;
11 mod ancestors;
12 pub mod dagops;
12 pub mod dagops;
13 pub use ancestors::{AncestorsIterator, LazyAncestors, MissingAncestors};
13 pub use ancestors::{AncestorsIterator, LazyAncestors, MissingAncestors};
14 mod dirstate;
14 mod dirstate;
15 pub mod discovery;
15 pub mod discovery;
16 pub mod testing; // unconditionally built, for use from integration tests
16 pub mod testing; // unconditionally built, for use from integration tests
17 pub use dirstate::{
17 pub use dirstate::{
18 pack_dirstate, parse_dirstate, CopyVec, CopyVecEntry, DirstateEntry,
18 parsers::{pack_dirstate, parse_dirstate},
19 DirstateParents, DirstateVec,
19 CopyVec, CopyVecEntry, DirstateEntry, DirstateParents, DirstateVec,
20 };
20 };
21 mod filepatterns;
21 mod filepatterns;
22
22
23 pub use filepatterns::{
23 pub use filepatterns::{
24 build_single_regex, read_pattern_file, PatternSyntax, PatternTuple,
24 build_single_regex, read_pattern_file, PatternSyntax, PatternTuple,
25 };
25 };
26
26
27 /// Mercurial revision numbers
27 /// Mercurial revision numbers
28 ///
28 ///
29 /// As noted in revlog.c, revision numbers are actually encoded in
29 /// As noted in revlog.c, revision numbers are actually encoded in
30 /// 4 bytes, and are liberally converted to ints, whence the i32
30 /// 4 bytes, and are liberally converted to ints, whence the i32
31 pub type Revision = i32;
31 pub type Revision = i32;
32
32
33 /// Marker expressing the absence of a parent
33 /// Marker expressing the absence of a parent
34 ///
34 ///
35 /// Independently of the actual representation, `NULL_REVISION` is guaranteed
35 /// Independently of the actual representation, `NULL_REVISION` is guaranteed
36 /// to be smaller that all existing revisions.
36 /// to be smaller that all existing revisions.
37 pub const NULL_REVISION: Revision = -1;
37 pub const NULL_REVISION: Revision = -1;
38
38
39 /// Same as `mercurial.node.wdirrev`
39 /// Same as `mercurial.node.wdirrev`
40 ///
40 ///
41 /// This is also equal to `i32::max_value()`, but it's better to spell
41 /// This is also equal to `i32::max_value()`, but it's better to spell
42 /// it out explicitely, same as in `mercurial.node`
42 /// it out explicitely, same as in `mercurial.node`
43 pub const WORKING_DIRECTORY_REVISION: Revision = 0x7fffffff;
43 pub const WORKING_DIRECTORY_REVISION: Revision = 0x7fffffff;
44
44
45 /// The simplest expression of what we need of Mercurial DAGs.
45 /// The simplest expression of what we need of Mercurial DAGs.
46 pub trait Graph {
46 pub trait Graph {
47 /// Return the two parents of the given `Revision`.
47 /// Return the two parents of the given `Revision`.
48 ///
48 ///
49 /// Each of the parents can be independently `NULL_REVISION`
49 /// Each of the parents can be independently `NULL_REVISION`
50 fn parents(&self, Revision) -> Result<[Revision; 2], GraphError>;
50 fn parents(&self, Revision) -> Result<[Revision; 2], GraphError>;
51 }
51 }
52
52
53 pub type LineNumber = usize;
53 pub type LineNumber = usize;
54
54
55 #[derive(Clone, Debug, PartialEq)]
55 #[derive(Clone, Debug, PartialEq)]
56 pub enum GraphError {
56 pub enum GraphError {
57 ParentOutOfRange(Revision),
57 ParentOutOfRange(Revision),
58 WorkingDirectoryUnsupported,
58 WorkingDirectoryUnsupported,
59 }
59 }
60
60
61 #[derive(Clone, Debug, PartialEq)]
61 #[derive(Clone, Debug, PartialEq)]
62 pub enum DirstateParseError {
62 pub enum DirstateParseError {
63 TooLittleData,
63 TooLittleData,
64 Overflow,
64 Overflow,
65 CorruptedEntry(String),
65 CorruptedEntry(String),
66 }
66 }
67
67
68 #[derive(Debug, PartialEq)]
68 #[derive(Debug, PartialEq)]
69 pub enum DirstatePackError {
69 pub enum DirstatePackError {
70 CorruptedEntry(String),
70 CorruptedEntry(String),
71 CorruptedParent,
71 CorruptedParent,
72 BadSize(usize, usize),
72 BadSize(usize, usize),
73 }
73 }
74
74
75 impl From<std::io::Error> for DirstatePackError {
75 impl From<std::io::Error> for DirstatePackError {
76 fn from(e: std::io::Error) -> Self {
76 fn from(e: std::io::Error) -> Self {
77 DirstatePackError::CorruptedEntry(e.to_string())
77 DirstatePackError::CorruptedEntry(e.to_string())
78 }
78 }
79 }
79 }
80
80
81 impl From<std::io::Error> for DirstateParseError {
81 impl From<std::io::Error> for DirstateParseError {
82 fn from(e: std::io::Error) -> Self {
82 fn from(e: std::io::Error) -> Self {
83 DirstateParseError::CorruptedEntry(e.to_string())
83 DirstateParseError::CorruptedEntry(e.to_string())
84 }
84 }
85 }
85 }
86
86
87 #[derive(Debug)]
87 #[derive(Debug)]
88 pub enum PatternError {
88 pub enum PatternError {
89 UnsupportedSyntax(String),
89 UnsupportedSyntax(String),
90 }
90 }
91
91
92 #[derive(Debug)]
92 #[derive(Debug)]
93 pub enum PatternFileError {
93 pub enum PatternFileError {
94 IO(std::io::Error),
94 IO(std::io::Error),
95 Pattern(PatternError, LineNumber),
95 Pattern(PatternError, LineNumber),
96 }
96 }
97
97
98 impl From<std::io::Error> for PatternFileError {
98 impl From<std::io::Error> for PatternFileError {
99 fn from(e: std::io::Error) -> Self {
99 fn from(e: std::io::Error) -> Self {
100 PatternFileError::IO(e)
100 PatternFileError::IO(e)
101 }
101 }
102 }
102 }
General Comments 0
You need to be logged in to leave comments. Login now