Show More
@@ -7,6 +7,7 b'' | |||||
7 |
|
7 | |||
8 | use crate::dirstate_tree::on_disk::DirstateV2ParseError; |
|
8 | use crate::dirstate_tree::on_disk::DirstateV2ParseError; | |
9 | use crate::errors::HgError; |
|
9 | use crate::errors::HgError; | |
|
10 | use crate::revlog::node::NULL_NODE; | |||
10 | use crate::revlog::Node; |
|
11 | use crate::revlog::Node; | |
11 | use crate::utils::hg_path::{HgPath, HgPathBuf}; |
|
12 | use crate::utils::hg_path::{HgPath, HgPathBuf}; | |
12 | use crate::FastHashMap; |
|
13 | use crate::FastHashMap; | |
@@ -25,6 +26,13 b' pub struct DirstateParents {' | |||||
25 | pub p2: Node, |
|
26 | pub p2: Node, | |
26 | } |
|
27 | } | |
27 |
|
28 | |||
|
29 | impl DirstateParents { | |||
|
30 | pub const NULL: Self = Self { | |||
|
31 | p1: NULL_NODE, | |||
|
32 | p2: NULL_NODE, | |||
|
33 | }; | |||
|
34 | } | |||
|
35 | ||||
28 | /// The C implementation uses all signed types. This will be an issue |
|
36 | /// The C implementation uses all signed types. This will be an issue | |
29 | /// either when 4GB+ source files are commonplace or in 2038, whichever |
|
37 | /// either when 4GB+ source files are commonplace or in 2038, whichever | |
30 | /// comes first. |
|
38 | /// comes first. |
@@ -2,4 +2,4 b' pub mod dirstate_map;' | |||||
2 | pub mod dispatch; |
|
2 | pub mod dispatch; | |
3 | pub mod on_disk; |
|
3 | pub mod on_disk; | |
4 | pub mod path_with_basename; |
|
4 | pub mod path_with_basename; | |
5 | mod status; |
|
5 | pub mod status; |
@@ -167,6 +167,16 b' impl From<DirstateV2ParseError> for crat' | |||||
167 | } |
|
167 | } | |
168 | } |
|
168 | } | |
169 |
|
169 | |||
|
170 | fn read_header(on_disk: &[u8]) -> Result<&Header, DirstateV2ParseError> { | |||
|
171 | let (header, _) = | |||
|
172 | Header::from_bytes(on_disk).map_err(|_| DirstateV2ParseError)?; | |||
|
173 | if header.marker == *V2_FORMAT_MARKER { | |||
|
174 | Ok(header) | |||
|
175 | } else { | |||
|
176 | Err(DirstateV2ParseError) | |||
|
177 | } | |||
|
178 | } | |||
|
179 | ||||
170 | pub(super) fn read<'on_disk>( |
|
180 | pub(super) fn read<'on_disk>( | |
171 | on_disk: &'on_disk [u8], |
|
181 | on_disk: &'on_disk [u8], | |
172 | ) -> Result< |
|
182 | ) -> Result< | |
@@ -176,27 +186,19 b" pub(super) fn read<'on_disk>(" | |||||
176 | if on_disk.is_empty() { |
|
186 | if on_disk.is_empty() { | |
177 | return Ok((DirstateMap::empty(on_disk), None)); |
|
187 | return Ok((DirstateMap::empty(on_disk), None)); | |
178 | } |
|
188 | } | |
179 | let (header, _) = |
|
189 | let header = read_header(on_disk)?; | |
180 | Header::from_bytes(on_disk).map_err(|_| DirstateV2ParseError)?; |
|
|||
181 | let Header { |
|
|||
182 | marker, |
|
|||
183 | parents, |
|
|||
184 | root, |
|
|||
185 | nodes_with_entry_count, |
|
|||
186 | nodes_with_copy_source_count, |
|
|||
187 | } = header; |
|
|||
188 | if marker != V2_FORMAT_MARKER { |
|
|||
189 | return Err(DirstateV2ParseError); |
|
|||
190 | } |
|
|||
191 | let dirstate_map = DirstateMap { |
|
190 | let dirstate_map = DirstateMap { | |
192 | on_disk, |
|
191 | on_disk, | |
193 | root: dirstate_map::ChildNodes::OnDisk(read_slice::<Node>( |
|
192 | root: dirstate_map::ChildNodes::OnDisk(read_slice::<Node>( | |
194 |
on_disk, |
|
193 | on_disk, | |
|
194 | header.root, | |||
195 | )?), |
|
195 | )?), | |
196 | nodes_with_entry_count: nodes_with_entry_count.get(), |
|
196 | nodes_with_entry_count: header.nodes_with_entry_count.get(), | |
197 |
nodes_with_copy_source_count: |
|
197 | nodes_with_copy_source_count: header | |
|
198 | .nodes_with_copy_source_count | |||
|
199 | .get(), | |||
198 | }; |
|
200 | }; | |
199 | let parents = Some(parents.clone()); |
|
201 | let parents = Some(header.parents.clone()); | |
200 | Ok((dirstate_map, parents)) |
|
202 | Ok((dirstate_map, parents)) | |
201 | } |
|
203 | } | |
202 |
|
204 | |||
@@ -414,6 +416,35 b' where' | |||||
414 | .ok_or_else(|| DirstateV2ParseError) |
|
416 | .ok_or_else(|| DirstateV2ParseError) | |
415 | } |
|
417 | } | |
416 |
|
418 | |||
|
419 | pub(crate) fn parse_dirstate_parents( | |||
|
420 | on_disk: &[u8], | |||
|
421 | ) -> Result<&DirstateParents, HgError> { | |||
|
422 | Ok(&read_header(on_disk)?.parents) | |||
|
423 | } | |||
|
424 | ||||
|
425 | pub(crate) fn for_each_tracked_path<'on_disk>( | |||
|
426 | on_disk: &'on_disk [u8], | |||
|
427 | mut f: impl FnMut(&'on_disk HgPath), | |||
|
428 | ) -> Result<(), DirstateV2ParseError> { | |||
|
429 | let header = read_header(on_disk)?; | |||
|
430 | fn recur<'on_disk>( | |||
|
431 | on_disk: &'on_disk [u8], | |||
|
432 | nodes: Slice, | |||
|
433 | f: &mut impl FnMut(&'on_disk HgPath), | |||
|
434 | ) -> Result<(), DirstateV2ParseError> { | |||
|
435 | for node in read_slice::<Node>(on_disk, nodes)? { | |||
|
436 | if let Some(state) = node.state()? { | |||
|
437 | if state.is_tracked() { | |||
|
438 | f(node.full_path(on_disk)?) | |||
|
439 | } | |||
|
440 | } | |||
|
441 | recur(on_disk, node.children, f)? | |||
|
442 | } | |||
|
443 | Ok(()) | |||
|
444 | } | |||
|
445 | recur(on_disk, header.root, &mut f) | |||
|
446 | } | |||
|
447 | ||||
417 | pub(super) fn write( |
|
448 | pub(super) fn write( | |
418 | dirstate_map: &mut DirstateMap, |
|
449 | dirstate_map: &mut DirstateMap, | |
419 | parents: DirstateParents, |
|
450 | parents: DirstateParents, |
@@ -6,6 +6,7 b'' | |||||
6 | // GNU General Public License version 2 or any later version. |
|
6 | // GNU General Public License version 2 or any later version. | |
7 |
|
7 | |||
8 | use crate::dirstate::parsers::parse_dirstate_entries; |
|
8 | use crate::dirstate::parsers::parse_dirstate_entries; | |
|
9 | use crate::dirstate_tree::on_disk::for_each_tracked_path; | |||
9 | use crate::errors::HgError; |
|
10 | use crate::errors::HgError; | |
10 | use crate::repo::Repo; |
|
11 | use crate::repo::Repo; | |
11 | use crate::revlog::changelog::Changelog; |
|
12 | use crate::revlog::changelog::Changelog; | |
@@ -13,6 +14,7 b' use crate::revlog::manifest::{Manifest, ' | |||||
13 | use crate::revlog::node::Node; |
|
14 | use crate::revlog::node::Node; | |
14 | use crate::revlog::revlog::RevlogError; |
|
15 | use crate::revlog::revlog::RevlogError; | |
15 | use crate::utils::hg_path::HgPath; |
|
16 | use crate::utils::hg_path::HgPath; | |
|
17 | use crate::DirstateError; | |||
16 | use rayon::prelude::*; |
|
18 | use rayon::prelude::*; | |
17 |
|
19 | |||
18 | /// List files under Mercurial control in the working directory |
|
20 | /// List files under Mercurial control in the working directory | |
@@ -20,25 +22,34 b' use rayon::prelude::*;' | |||||
20 | pub struct Dirstate { |
|
22 | pub struct Dirstate { | |
21 | /// The `dirstate` content. |
|
23 | /// The `dirstate` content. | |
22 | content: Vec<u8>, |
|
24 | content: Vec<u8>, | |
|
25 | dirstate_v2: bool, | |||
23 | } |
|
26 | } | |
24 |
|
27 | |||
25 | impl Dirstate { |
|
28 | impl Dirstate { | |
26 | pub fn new(repo: &Repo) -> Result<Self, HgError> { |
|
29 | pub fn new(repo: &Repo) -> Result<Self, HgError> { | |
27 | let content = repo.hg_vfs().read("dirstate")?; |
|
30 | Ok(Self { | |
28 | Ok(Self { content }) |
|
31 | content: repo.hg_vfs().read("dirstate")?, | |
|
32 | dirstate_v2: repo.has_dirstate_v2(), | |||
|
33 | }) | |||
29 | } |
|
34 | } | |
30 |
|
35 | |||
31 |
pub fn tracked_files(&self) -> Result<Vec<&HgPath>, |
|
36 | pub fn tracked_files(&self) -> Result<Vec<&HgPath>, DirstateError> { | |
32 | let mut files = Vec::new(); |
|
37 | let mut files = Vec::new(); | |
33 | let _parents = parse_dirstate_entries( |
|
38 | if !self.content.is_empty() { | |
34 |
|
|
39 | if self.dirstate_v2 { | |
35 | |path, entry, _copy_source| { |
|
40 | for_each_tracked_path(&self.content, |path| files.push(path))? | |
36 | if entry.state.is_tracked() { |
|
41 | } else { | |
37 | files.push(path) |
|
42 | let _parents = parse_dirstate_entries( | |
38 |
|
|
43 | &self.content, | |
39 | Ok(()) |
|
44 | |path, entry, _copy_source| { | |
40 | }, |
|
45 | if entry.state.is_tracked() { | |
41 | )?; |
|
46 | files.push(path) | |
|
47 | } | |||
|
48 | Ok(()) | |||
|
49 | }, | |||
|
50 | )?; | |||
|
51 | } | |||
|
52 | } | |||
42 | files.par_sort_unstable(); |
|
53 | files.par_sort_unstable(); | |
43 | Ok(files) |
|
54 | Ok(files) | |
44 | } |
|
55 | } |
@@ -218,12 +218,23 b' impl Repo {' | |||||
218 | } |
|
218 | } | |
219 | } |
|
219 | } | |
220 |
|
220 | |||
|
221 | pub fn has_dirstate_v2(&self) -> bool { | |||
|
222 | self.requirements | |||
|
223 | .contains(requirements::DIRSTATE_V2_REQUIREMENT) | |||
|
224 | } | |||
|
225 | ||||
221 | pub fn dirstate_parents( |
|
226 | pub fn dirstate_parents( | |
222 | &self, |
|
227 | &self, | |
223 | ) -> Result<crate::dirstate::DirstateParents, HgError> { |
|
228 | ) -> Result<crate::dirstate::DirstateParents, HgError> { | |
224 | let dirstate = self.hg_vfs().mmap_open("dirstate")?; |
|
229 | let dirstate = self.hg_vfs().mmap_open("dirstate")?; | |
225 | let parents = |
|
230 | if dirstate.is_empty() { | |
226 |
crate::dirstate:: |
|
231 | return Ok(crate::dirstate::DirstateParents::NULL); | |
|
232 | } | |||
|
233 | let parents = if self.has_dirstate_v2() { | |||
|
234 | crate::dirstate_tree::on_disk::parse_dirstate_parents(&dirstate)? | |||
|
235 | } else { | |||
|
236 | crate::dirstate::parsers::parse_dirstate_parents(&dirstate)? | |||
|
237 | }; | |||
227 | Ok(parents.clone()) |
|
238 | Ok(parents.clone()) | |
228 | } |
|
239 | } | |
229 | } |
|
240 | } |
@@ -82,6 +82,7 b' const SUPPORTED: &[&str] = &[' | |||||
82 | SPARSEREVLOG_REQUIREMENT, |
|
82 | SPARSEREVLOG_REQUIREMENT, | |
83 | RELATIVE_SHARED_REQUIREMENT, |
|
83 | RELATIVE_SHARED_REQUIREMENT, | |
84 | REVLOG_COMPRESSION_ZSTD, |
|
84 | REVLOG_COMPRESSION_ZSTD, | |
|
85 | DIRSTATE_V2_REQUIREMENT, | |||
85 | // As of this writing everything rhg does is read-only. |
|
86 | // As of this writing everything rhg does is read-only. | |
86 | // When it starts writing to the repository, itβll need to either keep the |
|
87 | // When it starts writing to the repository, itβll need to either keep the | |
87 | // persistent nodemap up to date or remove this entry: |
|
88 | // persistent nodemap up to date or remove this entry: | |
@@ -90,6 +91,8 b' const SUPPORTED: &[&str] = &[' | |||||
90 |
|
91 | |||
91 | // Copied from mercurial/requirements.py: |
|
92 | // Copied from mercurial/requirements.py: | |
92 |
|
93 | |||
|
94 | pub(crate) const DIRSTATE_V2_REQUIREMENT: &str = "exp-dirstate-v2"; | |||
|
95 | ||||
93 | /// When narrowing is finalized and no longer subject to format changes, |
|
96 | /// When narrowing is finalized and no longer subject to format changes, | |
94 | /// we should move this to just "narrow" or similar. |
|
97 | /// we should move this to just "narrow" or similar. | |
95 | #[allow(unused)] |
|
98 | #[allow(unused)] |
@@ -9,6 +9,7 b' use crate::error::CommandError;' | |||||
9 | use crate::ui::Ui; |
|
9 | use crate::ui::Ui; | |
10 | use clap::{Arg, SubCommand}; |
|
10 | use clap::{Arg, SubCommand}; | |
11 | use hg; |
|
11 | use hg; | |
|
12 | use hg::dirstate_tree::dirstate_map::DirstateMap; | |||
12 | use hg::errors::HgResultExt; |
|
13 | use hg::errors::HgResultExt; | |
13 | use hg::errors::IoResultExt; |
|
14 | use hg::errors::IoResultExt; | |
14 | use hg::matchers::AlwaysMatcher; |
|
15 | use hg::matchers::AlwaysMatcher; | |
@@ -16,7 +17,7 b' use hg::operations::cat;' | |||||
16 | use hg::repo::Repo; |
|
17 | use hg::repo::Repo; | |
17 | use hg::revlog::node::Node; |
|
18 | use hg::revlog::node::Node; | |
18 | use hg::utils::hg_path::{hg_path_to_os_string, HgPath}; |
|
19 | use hg::utils::hg_path::{hg_path_to_os_string, HgPath}; | |
19 |
use hg:: |
|
20 | use hg::StatusError; | |
20 | use hg::{HgPathCow, StatusOptions}; |
|
21 | use hg::{HgPathCow, StatusOptions}; | |
21 | use log::{info, warn}; |
|
22 | use log::{info, warn}; | |
22 | use std::convert::TryInto; |
|
23 | use std::convert::TryInto; | |
@@ -164,14 +165,17 b' pub fn run(invocation: &crate::CliInvoca' | |||||
164 | }; |
|
165 | }; | |
165 |
|
166 | |||
166 | let repo = invocation.repo?; |
|
167 | let repo = invocation.repo?; | |
167 | let mut dmap = DirstateMap::new(); |
|
|||
168 | let dirstate_data = |
|
168 | let dirstate_data = | |
169 | repo.hg_vfs().mmap_open("dirstate").io_not_found_as_none()?; |
|
169 | repo.hg_vfs().mmap_open("dirstate").io_not_found_as_none()?; | |
170 | let dirstate_data = match &dirstate_data { |
|
170 | let dirstate_data = match &dirstate_data { | |
171 | Some(mmap) => &**mmap, |
|
171 | Some(mmap) => &**mmap, | |
172 | None => b"", |
|
172 | None => b"", | |
173 | }; |
|
173 | }; | |
174 |
let parents = |
|
174 | let (mut dmap, parents) = if repo.has_dirstate_v2() { | |
|
175 | DirstateMap::new_v2(dirstate_data)? | |||
|
176 | } else { | |||
|
177 | DirstateMap::new_v1(dirstate_data)? | |||
|
178 | }; | |||
175 | let options = StatusOptions { |
|
179 | let options = StatusOptions { | |
176 | // TODO should be provided by the dirstate parsing and |
|
180 | // TODO should be provided by the dirstate parsing and | |
177 | // hence be stored on dmap. Using a value that assumes we aren't |
|
181 | // hence be stored on dmap. Using a value that assumes we aren't | |
@@ -187,8 +191,8 b' pub fn run(invocation: &crate::CliInvoca' | |||||
187 | collect_traversed_dirs: false, |
|
191 | collect_traversed_dirs: false, | |
188 | }; |
|
192 | }; | |
189 | let ignore_file = repo.working_directory_vfs().join(".hgignore"); // TODO hardcoded |
|
193 | let ignore_file = repo.working_directory_vfs().join(".hgignore"); // TODO hardcoded | |
190 | let (mut ds_status, pattern_warnings) = hg::status( |
|
194 | let (mut ds_status, pattern_warnings) = hg::dirstate_tree::status::status( | |
191 | &dmap, |
|
195 | &mut dmap, | |
192 | &AlwaysMatcher, |
|
196 | &AlwaysMatcher, | |
193 | repo.working_directory_path().to_owned(), |
|
197 | repo.working_directory_path().to_owned(), | |
194 | vec![ignore_file], |
|
198 | vec![ignore_file], |
General Comments 0
You need to be logged in to leave comments.
Login now