##// 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 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, *root,
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: nodes_with_copy_source_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 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>, HgError> {
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 &self.content,
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::parsers::parse_dirstate_parents(&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::{DirstateMap, StatusError};
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 = 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 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