##// END OF EJS Templates
rhg: Add support for dirstate-v2...
Simon Sapin -
r48165:bd88b6bf default
parent child Browse files
Show More
@@ -7,6 +7,7 b''
7 7
8 8 use crate::dirstate_tree::on_disk::DirstateV2ParseError;
9 9 use crate::errors::HgError;
10 use crate::revlog::node::NULL_NODE;
10 11 use crate::revlog::Node;
11 12 use crate::utils::hg_path::{HgPath, HgPathBuf};
12 13 use crate::FastHashMap;
@@ -25,6 +26,13 b' pub struct DirstateParents {'
25 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 36 /// The C implementation uses all signed types. This will be an issue
29 37 /// either when 4GB+ source files are commonplace or in 2038, whichever
30 38 /// comes first.
@@ -2,4 +2,4 b' pub mod dirstate_map;'
2 2 pub mod dispatch;
3 3 pub mod on_disk;
4 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 180 pub(super) fn read<'on_disk>(
171 181 on_disk: &'on_disk [u8],
172 182 ) -> Result<
@@ -176,27 +186,19 b" pub(super) fn read<'on_disk>("
176 186 if on_disk.is_empty() {
177 187 return Ok((DirstateMap::empty(on_disk), None));
178 188 }
179 let (header, _) =
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 }
189 let header = read_header(on_disk)?;
191 190 let dirstate_map = DirstateMap {
192 191 on_disk,
193 192 root: dirstate_map::ChildNodes::OnDisk(read_slice::<Node>(
194 on_disk, *root,
193 on_disk,
194 header.root,
195 195 )?),
196 nodes_with_entry_count: nodes_with_entry_count.get(),
197 nodes_with_copy_source_count: nodes_with_copy_source_count.get(),
196 nodes_with_entry_count: header.nodes_with_entry_count.get(),
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 202 Ok((dirstate_map, parents))
201 203 }
202 204
@@ -414,6 +416,35 b' where'
414 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 448 pub(super) fn write(
418 449 dirstate_map: &mut DirstateMap,
419 450 parents: DirstateParents,
@@ -6,6 +6,7 b''
6 6 // GNU General Public License version 2 or any later version.
7 7
8 8 use crate::dirstate::parsers::parse_dirstate_entries;
9 use crate::dirstate_tree::on_disk::for_each_tracked_path;
9 10 use crate::errors::HgError;
10 11 use crate::repo::Repo;
11 12 use crate::revlog::changelog::Changelog;
@@ -13,6 +14,7 b' use crate::revlog::manifest::{Manifest, '
13 14 use crate::revlog::node::Node;
14 15 use crate::revlog::revlog::RevlogError;
15 16 use crate::utils::hg_path::HgPath;
17 use crate::DirstateError;
16 18 use rayon::prelude::*;
17 19
18 20 /// List files under Mercurial control in the working directory
@@ -20,25 +22,34 b' use rayon::prelude::*;'
20 22 pub struct Dirstate {
21 23 /// The `dirstate` content.
22 24 content: Vec<u8>,
25 dirstate_v2: bool,
23 26 }
24 27
25 28 impl Dirstate {
26 29 pub fn new(repo: &Repo) -> Result<Self, HgError> {
27 let content = repo.hg_vfs().read("dirstate")?;
28 Ok(Self { content })
30 Ok(Self {
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>, HgError> {
36 pub fn tracked_files(&self) -> Result<Vec<&HgPath>, DirstateError> {
32 37 let mut files = Vec::new();
33 let _parents = parse_dirstate_entries(
34 &self.content,
35 |path, entry, _copy_source| {
36 if entry.state.is_tracked() {
37 files.push(path)
38 }
39 Ok(())
40 },
41 )?;
38 if !self.content.is_empty() {
39 if self.dirstate_v2 {
40 for_each_tracked_path(&self.content, |path| files.push(path))?
41 } else {
42 let _parents = parse_dirstate_entries(
43 &self.content,
44 |path, entry, _copy_source| {
45 if entry.state.is_tracked() {
46 files.push(path)
47 }
48 Ok(())
49 },
50 )?;
51 }
52 }
42 53 files.par_sort_unstable();
43 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 226 pub fn dirstate_parents(
222 227 &self,
223 228 ) -> Result<crate::dirstate::DirstateParents, HgError> {
224 229 let dirstate = self.hg_vfs().mmap_open("dirstate")?;
225 let parents =
226 crate::dirstate::parsers::parse_dirstate_parents(&dirstate)?;
230 if dirstate.is_empty() {
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 238 Ok(parents.clone())
228 239 }
229 240 }
@@ -82,6 +82,7 b' const SUPPORTED: &[&str] = &['
82 82 SPARSEREVLOG_REQUIREMENT,
83 83 RELATIVE_SHARED_REQUIREMENT,
84 84 REVLOG_COMPRESSION_ZSTD,
85 DIRSTATE_V2_REQUIREMENT,
85 86 // As of this writing everything rhg does is read-only.
86 87 // When it starts writing to the repository, it’ll need to either keep the
87 88 // persistent nodemap up to date or remove this entry:
@@ -90,6 +91,8 b' const SUPPORTED: &[&str] = &['
90 91
91 92 // Copied from mercurial/requirements.py:
92 93
94 pub(crate) const DIRSTATE_V2_REQUIREMENT: &str = "exp-dirstate-v2";
95
93 96 /// When narrowing is finalized and no longer subject to format changes,
94 97 /// we should move this to just "narrow" or similar.
95 98 #[allow(unused)]
@@ -9,6 +9,7 b' use crate::error::CommandError;'
9 9 use crate::ui::Ui;
10 10 use clap::{Arg, SubCommand};
11 11 use hg;
12 use hg::dirstate_tree::dirstate_map::DirstateMap;
12 13 use hg::errors::HgResultExt;
13 14 use hg::errors::IoResultExt;
14 15 use hg::matchers::AlwaysMatcher;
@@ -16,7 +17,7 b' use hg::operations::cat;'
16 17 use hg::repo::Repo;
17 18 use hg::revlog::node::Node;
18 19 use hg::utils::hg_path::{hg_path_to_os_string, HgPath};
19 use hg::{DirstateMap, StatusError};
20 use hg::StatusError;
20 21 use hg::{HgPathCow, StatusOptions};
21 22 use log::{info, warn};
22 23 use std::convert::TryInto;
@@ -164,14 +165,17 b' pub fn run(invocation: &crate::CliInvoca'
164 165 };
165 166
166 167 let repo = invocation.repo?;
167 let mut dmap = DirstateMap::new();
168 168 let dirstate_data =
169 169 repo.hg_vfs().mmap_open("dirstate").io_not_found_as_none()?;
170 170 let dirstate_data = match &dirstate_data {
171 171 Some(mmap) => &**mmap,
172 172 None => b"",
173 173 };
174 let parents = dmap.read(dirstate_data)?;
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 179 let options = StatusOptions {
176 180 // TODO should be provided by the dirstate parsing and
177 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 191 collect_traversed_dirs: false,
188 192 };
189 193 let ignore_file = repo.working_directory_vfs().join(".hgignore"); // TODO hardcoded
190 let (mut ds_status, pattern_warnings) = hg::status(
191 &dmap,
194 let (mut ds_status, pattern_warnings) = hg::dirstate_tree::status::status(
195 &mut dmap,
192 196 &AlwaysMatcher,
193 197 repo.working_directory_path().to_owned(),
194 198 vec![ignore_file],
General Comments 0
You need to be logged in to leave comments. Login now