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::{ |
|
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< |
|
60 | pub fn manifest_node(&self) -> Result<Node, HgError> { | |
|
61 | Node::from_hex_for_repo( | |||
61 | self.lines() |
|
62 | self.lines() | |
62 | .next() |
|
63 | .next() | |
63 |
.ok_or_else(|| HgError::corrupted("empty changelog entry") |
|
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)?)); | |||
71 | } |
|
80 | } | |
|
81 | } | |||
|
82 | Ok(None) | |||
|
83 | } | |||
|
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 |
|
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, & |
|
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 |
|
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(). |
|
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. |
|
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