##// END OF EJS Templates
rhg: Reuse manifest when checking status of multiple ambiguous files...
Simon Sapin -
r48778:796206e7 default
parent child Browse files
Show More
@@ -34,7 +34,7 b" pub fn cat<'a>("
34 files: &'a [HgPathBuf],
34 files: &'a [HgPathBuf],
35 ) -> Result<CatOutput, RevlogError> {
35 ) -> Result<CatOutput, RevlogError> {
36 let rev = crate::revset::resolve_single(revset, repo)?;
36 let rev = crate::revset::resolve_single(revset, repo)?;
37 let manifest = repo.manifest(rev)?;
37 let manifest = repo.manifest_for_rev(rev)?;
38 let node = *repo
38 let node = *repo
39 .changelog()?
39 .changelog()?
40 .node_from_rev(rev)
40 .node_from_rev(rev)
@@ -70,7 +70,7 b' pub fn list_rev_tracked_files('
70 revset: &str,
70 revset: &str,
71 ) -> Result<FilesForRev, RevlogError> {
71 ) -> Result<FilesForRev, RevlogError> {
72 let rev = crate::revset::resolve_single(revset, repo)?;
72 let rev = crate::revset::resolve_single(revset, repo)?;
73 Ok(FilesForRev(repo.manifest(rev)?))
73 Ok(FilesForRev(repo.manifest_for_rev(rev)?))
74 }
74 }
75
75
76 pub struct FilesForRev(Manifest);
76 pub struct FilesForRev(Manifest);
@@ -5,15 +5,15 b' use crate::dirstate_tree::dirstate_map::'
5 use crate::dirstate_tree::owning::OwningDirstateMap;
5 use crate::dirstate_tree::owning::OwningDirstateMap;
6 use crate::errors::HgError;
6 use crate::errors::HgError;
7 use crate::errors::HgResultExt;
7 use crate::errors::HgResultExt;
8 use crate::exit_codes;
8 use crate::manifest::{Manifest, Manifestlog};
9 use crate::manifest::{Manifest, Manifestlog};
9 use crate::requirements;
10 use crate::revlog::filelog::Filelog;
10 use crate::revlog::filelog::Filelog;
11 use crate::revlog::revlog::RevlogError;
11 use crate::revlog::revlog::RevlogError;
12 use crate::utils::files::get_path_from_bytes;
12 use crate::utils::files::get_path_from_bytes;
13 use crate::utils::hg_path::HgPath;
13 use crate::utils::hg_path::HgPath;
14 use crate::utils::SliceExt;
14 use crate::utils::SliceExt;
15 use crate::vfs::{is_dir, is_file, Vfs};
15 use crate::vfs::{is_dir, is_file, Vfs};
16 use crate::{exit_codes, Node};
16 use crate::{requirements, NodePrefix};
17 use crate::{DirstateError, Revision};
17 use crate::{DirstateError, Revision};
18 use std::cell::{Cell, Ref, RefCell, RefMut};
18 use std::cell::{Cell, Ref, RefCell, RefMut};
19 use std::collections::HashSet;
19 use std::collections::HashSet;
@@ -336,17 +336,27 b' impl Repo {'
336 self.manifestlog.get_mut_or_init(self)
336 self.manifestlog.get_mut_or_init(self)
337 }
337 }
338
338
339 /// Returns the manifest of the given node ID
340 pub fn manifest_for_node(
341 &self,
342 node: impl Into<NodePrefix>,
343 ) -> Result<Manifest, RevlogError> {
344 self.manifestlog()?.get_node(
345 self.changelog()?
346 .get_node(node.into())?
347 .manifest_node()?
348 .into(),
349 )
350 }
351
339 /// Returns the manifest of the given revision
352 /// Returns the manifest of the given revision
340 pub fn manifest(
353 pub fn manifest_for_rev(
341 &self,
354 &self,
342 revision: Revision,
355 revision: Revision,
343 ) -> Result<Manifest, RevlogError> {
356 ) -> Result<Manifest, RevlogError> {
344 let changelog = self.changelog()?;
357 self.manifestlog()?.get_node(
345 let manifest = self.manifestlog()?;
358 self.changelog()?.get_rev(revision)?.manifest_node()?.into(),
346 let changelog_entry = changelog.get_rev(revision)?;
359 )
347 let manifest_node =
348 Node::from_hex_for_repo(&changelog_entry.manifest_node()?)?;
349 manifest.get_node(manifest_node.into())
350 }
360 }
351
361
352 pub fn filelog(&self, path: &HgPath) -> Result<Filelog, HgError> {
362 pub fn filelog(&self, path: &HgPath) -> Result<Filelog, HgError> {
@@ -57,9 +57,11 b' impl ChangelogEntry {'
57
57
58 /// Return the node id of the `manifest` referenced by this `changelog`
58 /// Return the node id of the `manifest` referenced by this `changelog`
59 /// entry.
59 /// entry.
60 pub fn manifest_node(&self) -> Result<&[u8], RevlogError> {
60 pub fn manifest_node(&self) -> Result<Node, HgError> {
61 self.lines()
61 Node::from_hex_for_repo(
62 .next()
62 self.lines()
63 .ok_or_else(|| HgError::corrupted("empty changelog entry").into())
63 .next()
64 .ok_or_else(|| HgError::corrupted("empty changelog entry"))?,
65 )
64 }
66 }
65 }
67 }
@@ -1,8 +1,8 b''
1 use crate::errors::HgError;
1 use crate::errors::HgError;
2 use crate::repo::Repo;
2 use crate::repo::Repo;
3 use crate::revlog::revlog::{Revlog, RevlogError};
3 use crate::revlog::revlog::{Revlog, RevlogError};
4 use crate::revlog::NodePrefix;
5 use crate::revlog::Revision;
4 use crate::revlog::Revision;
5 use crate::revlog::{Node, NodePrefix};
6 use crate::utils::hg_path::HgPath;
6 use crate::utils::hg_path::HgPath;
7
7
8 /// A specialized `Revlog` to work with `manifest` data format.
8 /// A specialized `Revlog` to work with `manifest` data format.
@@ -68,4 +68,17 b' impl Manifest {'
68 (HgPath::new(&line[..pos]), &line[hash_start..hash_end])
68 (HgPath::new(&line[..pos]), &line[hash_start..hash_end])
69 })
69 })
70 }
70 }
71
72 /// If the given path is in this manifest, return its filelog node ID
73 pub fn find_file(&self, path: &HgPath) -> Result<Option<Node>, HgError> {
74 // TODO: use binary search instead of linear scan. This may involve
75 // building (and caching) an index of the byte indicex of each manifest
76 // line.
77 for (manifest_path, node) in self.files_with_nodes() {
78 if manifest_path == path {
79 return Ok(Some(Node::from_hex_for_repo(node)?));
80 }
81 }
82 Ok(None)
83 }
71 }
84 }
@@ -10,13 +10,11 b' 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::dispatch::DirstateMapMethods;
12 use hg::dirstate_tree::dispatch::DirstateMapMethods;
13 use hg::errors::IoResultExt;
13 use hg::errors::{HgError, IoResultExt};
14 use hg::manifest::Manifest;
14 use hg::matchers::AlwaysMatcher;
15 use hg::matchers::AlwaysMatcher;
15 use hg::operations::cat;
16 use hg::repo::Repo;
16 use hg::repo::Repo;
17 use hg::revlog::node::Node;
18 use hg::utils::hg_path::{hg_path_to_os_string, HgPath};
17 use hg::utils::hg_path::{hg_path_to_os_string, HgPath};
19 use hg::StatusError;
20 use hg::{HgPathCow, StatusOptions};
18 use hg::{HgPathCow, StatusOptions};
21 use log::{info, warn};
19 use log::{info, warn};
22 use std::convert::TryInto;
20 use std::convert::TryInto;
@@ -203,10 +201,12 b' pub fn run(invocation: &crate::CliInvoca'
203 if !ds_status.unsure.is_empty()
201 if !ds_status.unsure.is_empty()
204 && (display_states.modified || display_states.clean)
202 && (display_states.modified || display_states.clean)
205 {
203 {
206 let p1: Node = repo.dirstate_parents()?.p1.into();
204 let p1 = repo.dirstate_parents()?.p1;
207 let p1_hex = format!("{:x}", p1);
205 let manifest = repo.manifest_for_node(p1).map_err(|e| {
206 CommandError::from((e, &*format!("{:x}", p1.short())))
207 })?;
208 for to_check in ds_status.unsure {
208 for to_check in ds_status.unsure {
209 if cat_file_is_modified(repo, &to_check, &p1_hex)? {
209 if cat_file_is_modified(repo, &manifest, &to_check)? {
210 if display_states.modified {
210 if display_states.modified {
211 ds_status.modified.push(to_check);
211 ds_status.modified.push(to_check);
212 }
212 }
@@ -267,21 +267,22 b' fn display_status_paths('
267 /// TODO: detect permission bits and similar metadata modifications
267 /// TODO: detect permission bits and similar metadata modifications
268 fn cat_file_is_modified(
268 fn cat_file_is_modified(
269 repo: &Repo,
269 repo: &Repo,
270 manifest: &Manifest,
270 hg_path: &HgPath,
271 hg_path: &HgPath,
271 rev: &str,
272 ) -> Result<bool, HgError> {
272 ) -> Result<bool, CommandError> {
273 let file_node = manifest
273 // TODO CatRev expects &[HgPathBuf], something like
274 .find_file(hg_path)?
274 // &[impl Deref<HgPath>] would be nicer and should avoid the copy
275 .expect("ambgious file not in p1");
275 let path_bufs = [hg_path.into()];
276 let filelog = repo.filelog(hg_path)?;
276 // TODO IIUC CatRev returns a simple Vec<u8> for all files
277 let filelog_entry = filelog.get_node(file_node).map_err(|_| {
277 // being able to tell them apart as (path, bytes) would be nicer
278 HgError::corrupted("filelog missing node from manifest")
278 // and OPTIM would allow manifest resolution just once.
279 })?;
279 let output = cat(repo, rev, &path_bufs).map_err(|e| (e, rev))?;
280 let contents_in_p1 = filelog_entry.data()?;
280
281
281 let fs_path = repo
282 let fs_path = repo
282 .working_directory_vfs()
283 .working_directory_vfs()
283 .join(hg_path_to_os_string(hg_path).expect("HgPath conversion"));
284 .join(hg_path_to_os_string(hg_path).expect("HgPath conversion"));
284 let hg_data_len: u64 = match output.concatenated.len().try_into() {
285 let hg_data_len: u64 = match contents_in_p1.len().try_into() {
285 Ok(v) => v,
286 Ok(v) => v,
286 Err(_) => {
287 Err(_) => {
287 // conversion of data length to u64 failed,
288 // conversion of data length to u64 failed,
@@ -290,14 +291,12 b' fn cat_file_is_modified('
290 }
291 }
291 };
292 };
292 let fobj = fs::File::open(&fs_path).when_reading_file(&fs_path)?;
293 let fobj = fs::File::open(&fs_path).when_reading_file(&fs_path)?;
293 if fobj.metadata().map_err(|e| StatusError::from(e))?.len() != hg_data_len
294 if fobj.metadata().when_reading_file(&fs_path)?.len() != hg_data_len {
294 {
295 return Ok(true);
295 return Ok(true);
296 }
296 }
297 for (fs_byte, hg_byte) in
297 for (fs_byte, &hg_byte) in BufReader::new(fobj).bytes().zip(contents_in_p1)
298 BufReader::new(fobj).bytes().zip(output.concatenated)
299 {
298 {
300 if fs_byte.map_err(|e| StatusError::from(e))? != hg_byte {
299 if fs_byte.when_reading_file(&fs_path)? != hg_byte {
301 return Ok(true);
300 return Ok(true);
302 }
301 }
303 }
302 }
General Comments 0
You need to be logged in to leave comments. Login now