Show More
@@ -12,6 +12,8 b' use crate::revlog::Node;' | |||||
12 | use crate::utils::hg_path::HgPath; |
|
12 | use crate::utils::hg_path::HgPath; | |
13 |
|
13 | |||
14 | use crate::errors::HgError; |
|
14 | use crate::errors::HgError; | |
|
15 | use crate::manifest::Manifest; | |||
|
16 | use crate::manifest::ManifestEntry; | |||
15 | use itertools::put_back; |
|
17 | use itertools::put_back; | |
16 | use itertools::PutBack; |
|
18 | use itertools::PutBack; | |
17 | use std::cmp::Ordering; |
|
19 | use std::cmp::Ordering; | |
@@ -29,39 +31,33 b" pub struct CatOutput<'a> {" | |||||
29 | } |
|
31 | } | |
30 |
|
32 | |||
31 | // Find an item in an iterator over a sorted collection. |
|
33 | // Find an item in an iterator over a sorted collection. | |
32 | fn find_item<'a, D, I: Iterator<Item = Result<(&'a HgPath, D), HgError>>>( |
|
34 | fn find_item<'a>( | |
33 | i: &mut PutBack<I>, |
|
35 | i: &mut PutBack<impl Iterator<Item = Result<ManifestEntry<'a>, HgError>>>, | |
34 | needle: &HgPath, |
|
36 | needle: &HgPath, | |
35 |
) -> Result<Option< |
|
37 | ) -> Result<Option<Node>, HgError> { | |
36 | loop { |
|
38 | loop { | |
37 | match i.next() { |
|
39 | match i.next() { | |
38 | None => return Ok(None), |
|
40 | None => return Ok(None), | |
39 | Some(result) => { |
|
41 | Some(result) => { | |
40 |
let |
|
42 | let entry = result?; | |
41 | match needle.as_bytes().cmp(path.as_bytes()) { |
|
43 | match needle.as_bytes().cmp(entry.path.as_bytes()) { | |
42 | Ordering::Less => { |
|
44 | Ordering::Less => { | |
43 |
i.put_back(Ok( |
|
45 | i.put_back(Ok(entry)); | |
44 | return Ok(None); |
|
46 | return Ok(None); | |
45 | } |
|
47 | } | |
46 | Ordering::Greater => continue, |
|
48 | Ordering::Greater => continue, | |
47 |
Ordering::Equal => return Ok(Some( |
|
49 | Ordering::Equal => return Ok(Some(entry.node_id()?)), | |
48 | } |
|
50 | } | |
49 | } |
|
51 | } | |
50 | } |
|
52 | } | |
51 | } |
|
53 | } | |
52 | } |
|
54 | } | |
53 |
|
55 | |||
54 | fn find_files_in_manifest< |
|
56 | fn find_files_in_manifest<'query>( | |
55 |
|
|
57 | manifest: &Manifest, | |
56 | 'query, |
|
58 | query: impl Iterator<Item = &'query HgPath>, | |
57 | Data, |
|
59 | ) -> Result<(Vec<(&'query HgPath, Node)>, Vec<&'query HgPath>), HgError> { | |
58 | Manifest: Iterator<Item = Result<(&'manifest HgPath, Data), HgError>>, |
|
60 | let mut manifest = put_back(manifest.iter()); | |
59 | Query: Iterator<Item = &'query HgPath>, |
|
|||
60 | >( |
|
|||
61 | manifest: Manifest, |
|
|||
62 | query: Query, |
|
|||
63 | ) -> Result<(Vec<(&'query HgPath, Data)>, Vec<&'query HgPath>), HgError> { |
|
|||
64 | let mut manifest = put_back(manifest); |
|
|||
65 | let mut res = vec![]; |
|
61 | let mut res = vec![]; | |
66 | let mut missing = vec![]; |
|
62 | let mut missing = vec![]; | |
67 |
|
63 | |||
@@ -96,14 +92,13 b" pub fn cat<'a>(" | |||||
96 | files.sort_unstable(); |
|
92 | files.sort_unstable(); | |
97 |
|
93 | |||
98 | let (found, missing) = find_files_in_manifest( |
|
94 | let (found, missing) = find_files_in_manifest( | |
99 |
manifest |
|
95 | &manifest, | |
100 | files.into_iter().map(|f| f.as_ref()), |
|
96 | files.into_iter().map(|f| f.as_ref()), | |
101 | )?; |
|
97 | )?; | |
102 |
|
98 | |||
103 |
for (file_path, node |
|
99 | for (file_path, file_node) in found { | |
104 | found_any = true; |
|
100 | found_any = true; | |
105 | let file_log = repo.filelog(file_path)?; |
|
101 | let file_log = repo.filelog(file_path)?; | |
106 | let file_node = Node::from_hex_for_repo(node_bytes)?; |
|
|||
107 | results.push(( |
|
102 | results.push(( | |
108 | file_path, |
|
103 | file_path, | |
109 | file_log.data_for_node(file_node)?.into_data()?, |
|
104 | file_log.data_for_node(file_node)?.into_data()?, |
@@ -77,6 +77,6 b' pub struct FilesForRev(Manifest);' | |||||
77 |
|
77 | |||
78 | impl FilesForRev { |
|
78 | impl FilesForRev { | |
79 | pub fn iter(&self) -> impl Iterator<Item = Result<&HgPath, HgError>> { |
|
79 | pub fn iter(&self) -> impl Iterator<Item = Result<&HgPath, HgError>> { | |
80 | self.0.files() |
|
80 | self.0.iter().map(|entry| Ok(entry?.path)) | |
81 | } |
|
81 | } | |
82 | } |
|
82 | } |
@@ -4,6 +4,7 b' use crate::revlog::revlog::{Revlog, Revl' | |||||
4 | use crate::revlog::Revision; |
|
4 | use crate::revlog::Revision; | |
5 | use crate::revlog::{Node, NodePrefix}; |
|
5 | use crate::revlog::{Node, NodePrefix}; | |
6 | use crate::utils::hg_path::HgPath; |
|
6 | use crate::utils::hg_path::HgPath; | |
|
7 | use crate::utils::SliceExt; | |||
7 |
|
8 | |||
8 | /// A specialized `Revlog` to work with `manifest` data format. |
|
9 | /// A specialized `Revlog` to work with `manifest` data format. | |
9 | pub struct Manifestlog { |
|
10 | pub struct Manifestlog { | |
@@ -55,50 +56,64 b' pub struct Manifest {' | |||||
55 | } |
|
56 | } | |
56 |
|
57 | |||
57 | impl Manifest { |
|
58 | impl Manifest { | |
58 | /// Return an iterator over the lines of the entry. |
|
59 | pub fn iter( | |
59 | pub fn lines(&self) -> impl Iterator<Item = &[u8]> { |
|
60 | &self, | |
|
61 | ) -> impl Iterator<Item = Result<ManifestEntry, HgError>> { | |||
60 | self.bytes |
|
62 | self.bytes | |
61 | .split(|b| b == &b'\n') |
|
63 | .split(|b| b == &b'\n') | |
62 | .filter(|line| !line.is_empty()) |
|
64 | .filter(|line| !line.is_empty()) | |
63 | } |
|
65 | .map(|line| { | |
64 |
|
66 | let (path, rest) = line.split_2(b'\0').ok_or_else(|| { | ||
65 | /// Return an iterator over the files of the entry. |
|
|||
66 | pub fn files(&self) -> impl Iterator<Item = Result<&HgPath, HgError>> { |
|
|||
67 | self.lines().filter(|line| !line.is_empty()).map(|line| { |
|
|||
68 | let pos = |
|
|||
69 | line.iter().position(|x| x == &b'\0').ok_or_else(|| { |
|
|||
70 | HgError::corrupted("manifest line should contain \\0") |
|
67 | HgError::corrupted("manifest line should contain \\0") | |
71 | })?; |
|
68 | })?; | |
72 |
|
|
69 | let path = HgPath::new(path); | |
73 | }) |
|
70 | let (hex_node_id, flags) = match rest.split_last() { | |
74 | } |
|
71 | Some((&b'x', rest)) => (rest, Some(b'x')), | |
75 |
|
72 | Some((&b'l', rest)) => (rest, Some(b'l')), | ||
76 | /// Return an iterator over the files of the entry. |
|
73 | Some((&b't', rest)) => (rest, Some(b't')), | |
77 | pub fn files_with_nodes( |
|
74 | _ => (rest, None), | |
78 | &self, |
|
75 | }; | |
79 | ) -> impl Iterator<Item = Result<(&HgPath, &[u8]), HgError>> { |
|
76 | Ok(ManifestEntry { | |
80 | self.lines().filter(|line| !line.is_empty()).map(|line| { |
|
77 | path, | |
81 | let pos = |
|
78 | hex_node_id, | |
82 | line.iter().position(|x| x == &b'\0').ok_or_else(|| { |
|
79 | flags, | |
83 | HgError::corrupted("manifest line should contain \\0") |
|
80 | }) | |
84 |
|
|
81 | }) | |
85 | let hash_start = pos + 1; |
|
|||
86 | let hash_end = hash_start + 40; |
|
|||
87 | Ok((HgPath::new(&line[..pos]), &line[hash_start..hash_end])) |
|
|||
88 | }) |
|
|||
89 | } |
|
82 | } | |
90 |
|
83 | |||
91 | /// If the given path is in this manifest, return its filelog node ID |
|
84 | /// If the given path is in this manifest, return its filelog node ID | |
92 | pub fn find_file(&self, path: &HgPath) -> Result<Option<Node>, HgError> { |
|
85 | pub fn find_file( | |
|
86 | &self, | |||
|
87 | path: &HgPath, | |||
|
88 | ) -> Result<Option<ManifestEntry>, HgError> { | |||
93 | // TODO: use binary search instead of linear scan. This may involve |
|
89 | // TODO: use binary search instead of linear scan. This may involve | |
94 | // building (and caching) an index of the byte indicex of each manifest |
|
90 | // building (and caching) an index of the byte indicex of each manifest | |
95 | // line. |
|
91 | // line. | |
96 | for entry in self.files_with_nodes() { |
|
92 | ||
97 | let (manifest_path, node) = entry?; |
|
93 | // TODO: use try_find when available (if still using linear scan) | |
98 | if manifest_path == path { |
|
94 | // https://github.com/rust-lang/rust/issues/63178 | |
99 | return Ok(Some(Node::from_hex_for_repo(node)?)); |
|
95 | for entry in self.iter() { | |
|
96 | let entry = entry?; | |||
|
97 | if entry.path == path { | |||
|
98 | return Ok(Some(entry)); | |||
100 | } |
|
99 | } | |
101 | } |
|
100 | } | |
102 | Ok(None) |
|
101 | Ok(None) | |
103 | } |
|
102 | } | |
104 | } |
|
103 | } | |
|
104 | ||||
|
105 | /// `Manifestlog` entry which knows how to interpret the `manifest` data bytes. | |||
|
106 | #[derive(Debug)] | |||
|
107 | pub struct ManifestEntry<'manifest> { | |||
|
108 | pub path: &'manifest HgPath, | |||
|
109 | pub hex_node_id: &'manifest [u8], | |||
|
110 | ||||
|
111 | /// `Some` values are b'x', b'l', or 't' | |||
|
112 | pub flags: Option<u8>, | |||
|
113 | } | |||
|
114 | ||||
|
115 | impl ManifestEntry<'_> { | |||
|
116 | pub fn node_id(&self) -> Result<Node, HgError> { | |||
|
117 | Node::from_hex_for_repo(self.hex_node_id) | |||
|
118 | } | |||
|
119 | } |
@@ -309,13 +309,14 b' fn cat_file_is_modified(' | |||||
309 | manifest: &Manifest, |
|
309 | manifest: &Manifest, | |
310 | hg_path: &HgPath, |
|
310 | hg_path: &HgPath, | |
311 | ) -> Result<bool, HgError> { |
|
311 | ) -> Result<bool, HgError> { | |
312 |
let |
|
312 | let entry = manifest | |
313 | .find_file(hg_path)? |
|
313 | .find_file(hg_path)? | |
314 | .expect("ambgious file not in p1"); |
|
314 | .expect("ambgious file not in p1"); | |
315 | let filelog = repo.filelog(hg_path)?; |
|
315 | let filelog = repo.filelog(hg_path)?; | |
316 | let filelog_entry = filelog.data_for_node(file_node).map_err(|_| { |
|
316 | let filelog_entry = | |
317 | HgError::corrupted("filelog missing node from manifest") |
|
317 | filelog.data_for_node(entry.node_id()?).map_err(|_| { | |
318 | })?; |
|
318 | HgError::corrupted("filelog missing node from manifest") | |
|
319 | })?; | |||
319 | let contents_in_p1 = filelog_entry.data()?; |
|
320 | let contents_in_p1 = filelog_entry.data()?; | |
320 |
|
321 | |||
321 | let fs_path = hg_path_to_os_string(hg_path).expect("HgPath conversion"); |
|
322 | let fs_path = hg_path_to_os_string(hg_path).expect("HgPath conversion"); |
General Comments 0
You need to be logged in to leave comments.
Login now