Mercurial > hg
changeset 45436:1b3197047f5c
rhg: make output of `files` relative to the current directory and the root
This matches the behavior of `hg files`.
The util is added in `hg-core` instead of `rhg` because this operation could
be useful for other external tools. (this was definitely not prompted by rust
issue #50784, I swear)
Differential Revision: https://phab.mercurial-scm.org/D8872
author | Raphaël Gomès <rgomes@octobus.net> |
---|---|
date | Thu, 30 Jul 2020 16:55:44 +0200 |
parents | 64de86fd0984 |
children | e339693addc0 |
files | rust/hg-core/src/operations/list_tracked_files.rs rust/hg-core/src/utils/files.rs rust/rhg/src/commands/files.rs |
diffstat | 3 files changed, 82 insertions(+), 3 deletions(-) [+] |
line wrap: on
line diff
--- a/rust/hg-core/src/operations/list_tracked_files.rs Tue Sep 08 19:36:40 2020 +0530 +++ b/rust/hg-core/src/operations/list_tracked_files.rs Thu Jul 30 16:55:44 2020 +0200 @@ -14,7 +14,7 @@ use std::fmt; use std::fs; use std::io; -use std::path::PathBuf; +use std::path::{Path, PathBuf}; /// Kind of error encoutered by ListTrackedFiles #[derive(Debug)] @@ -60,6 +60,15 @@ let content = fs::read(&dirstate)?; Ok(ListDirstateTrackedFiles { content }) } + + /// Returns the repository root directory + /// TODO I think this is a crutch that creates a dependency that should not + /// be there. Operations that need the root of the repository should get + /// it themselves, probably in a lazy fashion. But this would make the + /// current series even larger, so this is simplified for now. + pub fn get_root(&self) -> &Path { + &self.root + } } /// List files under Mercurial control in the working directory
--- a/rust/hg-core/src/utils/files.rs Tue Sep 08 19:36:40 2020 +0530 +++ b/rust/hg-core/src/utils/files.rs Thu Jul 30 16:55:44 2020 +0200 @@ -16,7 +16,7 @@ }; use lazy_static::lazy_static; use same_file::is_same_file; -use std::borrow::ToOwned; +use std::borrow::{Cow, ToOwned}; use std::fs::Metadata; use std::iter::FusedIterator; use std::ops::Deref; @@ -248,6 +248,66 @@ } } +/// Returns the representation of the path relative to the current working +/// directory for display purposes. +/// +/// `cwd` is a `HgPath`, so it is considered relative to the root directory +/// of the repository. +/// +/// # Examples +/// +/// ``` +/// use hg::utils::hg_path::HgPath; +/// use hg::utils::files::relativize_path; +/// use std::borrow::Cow; +/// +/// let file = HgPath::new(b"nested/file"); +/// let cwd = HgPath::new(b""); +/// assert_eq!(relativize_path(file, cwd), Cow::Borrowed(b"nested/file")); +/// +/// let cwd = HgPath::new(b"nested"); +/// assert_eq!(relativize_path(file, cwd), Cow::Borrowed(b"file")); +/// +/// let cwd = HgPath::new(b"other"); +/// assert_eq!(relativize_path(file, cwd), Cow::Borrowed(b"../nested/file")); +/// ``` +pub fn relativize_path(path: &HgPath, cwd: impl AsRef<HgPath>) -> Cow<[u8]> { + if cwd.as_ref().is_empty() { + Cow::Borrowed(path.as_bytes()) + } else { + let mut res: Vec<u8> = Vec::new(); + let mut path_iter = path.as_bytes().split(|b| *b == b'/').peekable(); + let mut cwd_iter = + cwd.as_ref().as_bytes().split(|b| *b == b'/').peekable(); + loop { + match (path_iter.peek(), cwd_iter.peek()) { + (Some(a), Some(b)) if a == b => (), + _ => break, + } + path_iter.next(); + cwd_iter.next(); + } + let mut need_sep = false; + for _ in cwd_iter { + if need_sep { + res.extend(b"/") + } else { + need_sep = true + }; + res.extend(b".."); + } + for c in path_iter { + if need_sep { + res.extend(b"/") + } else { + need_sep = true + }; + res.extend(c); + } + Cow::Owned(res) + } +} + #[cfg(test)] mod tests { use super::*;
--- a/rust/rhg/src/commands/files.rs Tue Sep 08 19:36:40 2020 +0530 +++ b/rust/rhg/src/commands/files.rs Thu Jul 30 16:55:44 2020 +0200 @@ -2,6 +2,8 @@ use crate::error::{CommandError, CommandErrorKind}; use crate::ui::Ui; use hg::operations::{ListTrackedFiles, ListTrackedFilesErrorKind}; +use hg::utils::files::{get_bytes_from_path, relativize_path}; +use hg::utils::hg_path::HgPathBuf; pub const HELP_TEXT: &str = " List tracked files. @@ -38,9 +40,17 @@ } })?; + let cwd = std::env::current_dir() + .or_else(|e| Err(CommandErrorKind::CurrentDirNotFound(e)))?; + let rooted_cwd = cwd + .strip_prefix(operation_builder.get_root()) + .expect("cwd was already checked within the repository"); + let rooted_cwd = HgPathBuf::from(get_bytes_from_path(rooted_cwd)); + let mut stdout = self.ui.stdout_buffer(); + for file in files { - stdout.write_all(file.as_bytes())?; + stdout.write_all(relativize_path(file, &rooted_cwd).as_ref())?; stdout.write_all(b"\n")?; } stdout.flush()?;