##// END OF EJS Templates
manifestcache: add some test involving shares...
manifestcache: add some test involving shares Some issue around shares have been reported. I am adding tests to better cover the share case. The test show that the code behave as expected so far. Differential Revision: https://phab.mercurial-scm.org/D7602

File last commit:

r44270:ce088b38 default
r44289:8377570a default
Show More
status.rs
228 lines | 7.0 KiB | application/rls-services+xml | RustLexer
Raphaël Gomès
rust-dirstate-status: add first Rust implementation of `dirstate.status`...
r43565 // status.rs
//
// Copyright 2019 Raphaël Gomès <rgomes@octobus.net>
//
// This software may be used and distributed according to the terms of the
// GNU General Public License version 2 or any later version.
//! Rust implementation of dirstate.status (dirstate.py).
//! It is currently missing a lot of functionality compared to the Python one
//! and will only be triggered in narrow cases.
Raphaël Gomès
rust: introduce SIZE_FROM_OTHER_PARENT constant...
r44003 use crate::{
dirstate::SIZE_FROM_OTHER_PARENT,
utils::{
files::HgMetadata,
hg_path::{hg_path_to_path_buf, HgPath},
},
CopyMap, DirstateEntry, DirstateMap, EntryState,
};
Raphaël Gomès
rust-dirstate-status: add first Rust implementation of `dirstate.status`...
r43565 use rayon::prelude::*;
use std::path::Path;
Raphaël Gomès
rust-status: improve status performance...
r44000 /// Marker enum used to dispatch new status entries into the right collections.
/// Is similar to `crate::EntryState`, but represents the transient state of
/// entries during the lifetime of a command.
enum Dispatch {
Unsure,
Modified,
Added,
Removed,
Deleted,
Clean,
Unknown,
}
Raphaël Gomès
rust-status: refactor dispatch case for normal files...
r44002 /// Dates and times that are outside the 31-bit signed range are compared
/// modulo 2^31. This should prevent hg from behaving badly with very large
/// files or corrupt dates while still having a high probability of detecting
/// changes. (issue2608)
/// TODO I haven't found a way of having `b` be `Into<i32>`, since `From<u64>`
/// is not defined for `i32`, and there is no `As` trait. This forces the
/// caller to cast `b` as `i32`.
fn mod_compare(a: i32, b: i32) -> bool {
a & i32::max_value() != b & i32::max_value()
}
Raphaël Gomès
rust-status: improve status performance...
r44000 /// The file corresponding to the dirstate entry was found on the filesystem.
fn dispatch_found(
filename: impl AsRef<HgPath>,
entry: DirstateEntry,
metadata: HgMetadata,
copy_map: &CopyMap,
check_exec: bool,
list_clean: bool,
last_normal_time: i64,
) -> Dispatch {
let DirstateEntry {
state,
mode,
mtime,
size,
} = entry;
let HgMetadata {
st_mode,
st_size,
st_mtime,
..
} = metadata;
match state {
EntryState::Normal => {
Raphaël Gomès
rust-status: refactor dispatch case for normal files...
r44002 let size_changed = mod_compare(size, st_size as i32);
Raphaël Gomès
rust-status: improve status performance...
r44000 let mode_changed =
(mode ^ st_mode as i32) & 0o100 != 0o000 && check_exec;
Raphaël Gomès
rust-status: refactor dispatch case for normal files...
r44002 let metadata_changed = size >= 0 && (size_changed || mode_changed);
Raphaël Gomès
rust: introduce SIZE_FROM_OTHER_PARENT constant...
r44003 let other_parent = size == SIZE_FROM_OTHER_PARENT;
Raphaël Gomès
rust-status: refactor dispatch case for normal files...
r44002 if metadata_changed
|| other_parent
|| copy_map.contains_key(filename.as_ref())
Raphaël Gomès
rust-status: improve status performance...
r44000 {
Dispatch::Modified
Raphaël Gomès
rust-status: refactor dispatch case for normal files...
r44002 } else if mod_compare(mtime, st_mtime as i32) {
Raphaël Gomès
rust-status: improve status performance...
r44000 Dispatch::Unsure
} else if st_mtime == last_normal_time {
// the file may have just been marked as normal and
// it may have changed in the same second without
// changing its size. This can happen if we quickly
// do multiple commits. Force lookup, so we don't
// miss such a racy file change.
Dispatch::Unsure
} else if list_clean {
Dispatch::Clean
} else {
Dispatch::Unknown
}
}
EntryState::Merged => Dispatch::Modified,
EntryState::Added => Dispatch::Added,
EntryState::Removed => Dispatch::Removed,
EntryState::Unknown => Dispatch::Unknown,
}
}
/// The file corresponding to this Dirstate entry is missing.
fn dispatch_missing(state: EntryState) -> Dispatch {
match state {
// File was removed from the filesystem during commands
EntryState::Normal | EntryState::Merged | EntryState::Added => {
Dispatch::Deleted
}
// File was removed, everything is normal
EntryState::Removed => Dispatch::Removed,
// File is unknown to Mercurial, everything is normal
EntryState::Unknown => Dispatch::Unknown,
}
}
/// Stat all entries in the `DirstateMap` and mark them for dispatch into
/// the relevant collections.
fn stat_dmap_entries(
Raphaël Gomès
rust-dirstate-status: add first Rust implementation of `dirstate.status`...
r43565 dmap: &DirstateMap,
Raphaël Gomès
rust-status: return a ParallelIterator instead of a Vec from stat_dmap_entries...
r44001 root_dir: impl AsRef<Path> + Sync + Send,
Raphaël Gomès
rust-status: improve status performance...
r44000 check_exec: bool,
list_clean: bool,
last_normal_time: i64,
Raphaël Gomès
rust-status: return a ParallelIterator instead of a Vec from stat_dmap_entries...
r44001 ) -> impl ParallelIterator<Item = std::io::Result<(&HgPath, Dispatch)>> {
dmap.par_iter().map(move |(filename, entry)| {
let filename: &HgPath = filename;
let filename_as_path = hg_path_to_path_buf(filename)?;
let meta = root_dir.as_ref().join(filename_as_path).symlink_metadata();
Raphaël Gomès
rust-dirstate-status: add first Rust implementation of `dirstate.status`...
r43565
Raphaël Gomès
rust-status: return a ParallelIterator instead of a Vec from stat_dmap_entries...
r44001 match meta {
Ok(ref m)
if !(m.file_type().is_file()
|| m.file_type().is_symlink()) =>
{
Ok((filename, dispatch_missing(entry.state)))
}
Ok(m) => Ok((
filename,
dispatch_found(
filename,
*entry,
HgMetadata::from_metadata(m),
&dmap.copy_map,
check_exec,
list_clean,
last_normal_time,
),
)),
Err(ref e)
if e.kind() == std::io::ErrorKind::NotFound
|| e.raw_os_error() == Some(20) =>
{
// Rust does not yet have an `ErrorKind` for
// `NotADirectory` (errno 20)
// It happens if the dirstate contains `foo/bar` and
// foo is not a directory
Ok((filename, dispatch_missing(entry.state)))
}
Err(e) => Err(e),
}
})
Raphaël Gomès
rust-dirstate-status: add first Rust implementation of `dirstate.status`...
r43565 }
Raphaël Gomès
rust-status: improve status performance...
r44000 pub struct StatusResult<'a> {
pub modified: Vec<&'a HgPath>,
pub added: Vec<&'a HgPath>,
pub removed: Vec<&'a HgPath>,
pub deleted: Vec<&'a HgPath>,
pub clean: Vec<&'a HgPath>,
Gregory Szorc
rust: run rustfmt...
r44270 /* TODO ignored
* TODO unknown */
Raphaël Gomès
rust-dirstate-status: add first Rust implementation of `dirstate.status`...
r43565 }
fn build_response(
Raphaël Gomès
rust-status: improve status performance...
r44000 results: Vec<(&HgPath, Dispatch)>,
) -> (Vec<&HgPath>, StatusResult) {
Raphaël Gomès
rust-dirstate-status: add first Rust implementation of `dirstate.status`...
r43565 let mut lookup = vec![];
let mut modified = vec![];
let mut added = vec![];
let mut removed = vec![];
let mut deleted = vec![];
let mut clean = vec![];
Raphaël Gomès
rust-status: improve status performance...
r44000 for (filename, dispatch) in results.into_iter() {
match dispatch {
Dispatch::Unknown => {}
Dispatch::Unsure => lookup.push(filename),
Dispatch::Modified => modified.push(filename),
Dispatch::Added => added.push(filename),
Dispatch::Removed => removed.push(filename),
Dispatch::Deleted => deleted.push(filename),
Dispatch::Clean => clean.push(filename),
Raphaël Gomès
rust-dirstate-status: add first Rust implementation of `dirstate.status`...
r43565 }
}
(
lookup,
StatusResult {
modified,
added,
removed,
deleted,
clean,
},
)
}
pub fn status(
dmap: &DirstateMap,
Raphaël Gomès
rust-status: return a ParallelIterator instead of a Vec from stat_dmap_entries...
r44001 root_dir: impl AsRef<Path> + Sync + Send + Copy,
Raphaël Gomès
rust-dirstate-status: add first Rust implementation of `dirstate.status`...
r43565 list_clean: bool,
last_normal_time: i64,
check_exec: bool,
Raphaël Gomès
rust-status: improve status performance...
r44000 ) -> std::io::Result<(Vec<&HgPath>, StatusResult)> {
Raphaël Gomès
rust-status: return a ParallelIterator instead of a Vec from stat_dmap_entries...
r44001 let results: std::io::Result<_> = stat_dmap_entries(
Raphaël Gomès
rust-dirstate-status: add first Rust implementation of `dirstate.status`...
r43565 &dmap,
Raphaël Gomès
rust-status: improve status performance...
r44000 root_dir,
check_exec,
Raphaël Gomès
rust-dirstate-status: add first Rust implementation of `dirstate.status`...
r43565 list_clean,
last_normal_time,
Raphaël Gomès
rust-status: return a ParallelIterator instead of a Vec from stat_dmap_entries...
r44001 )
.collect();
Raphaël Gomès
rust-status: improve status performance...
r44000
Raphaël Gomès
rust-status: return a ParallelIterator instead of a Vec from stat_dmap_entries...
r44001 Ok(build_response(results?))
Raphaël Gomès
rust-dirstate-status: add first Rust implementation of `dirstate.status`...
r43565 }