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