Show More
@@ -14,7 +14,7 b' use std::convert::From;' | |||||
14 | use std::fmt; |
|
14 | use std::fmt; | |
15 | use std::fs; |
|
15 | use std::fs; | |
16 | use std::io; |
|
16 | use std::io; | |
17 | use std::path::PathBuf; |
|
17 | use std::path::{Path, PathBuf}; | |
18 |
|
18 | |||
19 | /// Kind of error encoutered by ListTrackedFiles |
|
19 | /// Kind of error encoutered by ListTrackedFiles | |
20 | #[derive(Debug)] |
|
20 | #[derive(Debug)] | |
@@ -60,6 +60,15 b' impl ListTrackedFiles {' | |||||
60 | let content = fs::read(&dirstate)?; |
|
60 | let content = fs::read(&dirstate)?; | |
61 | Ok(ListDirstateTrackedFiles { content }) |
|
61 | Ok(ListDirstateTrackedFiles { content }) | |
62 | } |
|
62 | } | |
|
63 | ||||
|
64 | /// Returns the repository root directory | |||
|
65 | /// TODO I think this is a crutch that creates a dependency that should not | |||
|
66 | /// be there. Operations that need the root of the repository should get | |||
|
67 | /// it themselves, probably in a lazy fashion. But this would make the | |||
|
68 | /// current series even larger, so this is simplified for now. | |||
|
69 | pub fn get_root(&self) -> &Path { | |||
|
70 | &self.root | |||
|
71 | } | |||
63 | } |
|
72 | } | |
64 |
|
73 | |||
65 | /// List files under Mercurial control in the working directory |
|
74 | /// List files under Mercurial control in the working directory |
@@ -16,7 +16,7 b' use crate::utils::{' | |||||
16 | }; |
|
16 | }; | |
17 | use lazy_static::lazy_static; |
|
17 | use lazy_static::lazy_static; | |
18 | use same_file::is_same_file; |
|
18 | use same_file::is_same_file; | |
19 | use std::borrow::ToOwned; |
|
19 | use std::borrow::{Cow, ToOwned}; | |
20 | use std::fs::Metadata; |
|
20 | use std::fs::Metadata; | |
21 | use std::iter::FusedIterator; |
|
21 | use std::iter::FusedIterator; | |
22 | use std::ops::Deref; |
|
22 | use std::ops::Deref; | |
@@ -248,6 +248,66 b' pub fn canonical_path(' | |||||
248 | } |
|
248 | } | |
249 | } |
|
249 | } | |
250 |
|
250 | |||
|
251 | /// Returns the representation of the path relative to the current working | |||
|
252 | /// directory for display purposes. | |||
|
253 | /// | |||
|
254 | /// `cwd` is a `HgPath`, so it is considered relative to the root directory | |||
|
255 | /// of the repository. | |||
|
256 | /// | |||
|
257 | /// # Examples | |||
|
258 | /// | |||
|
259 | /// ``` | |||
|
260 | /// use hg::utils::hg_path::HgPath; | |||
|
261 | /// use hg::utils::files::relativize_path; | |||
|
262 | /// use std::borrow::Cow; | |||
|
263 | /// | |||
|
264 | /// let file = HgPath::new(b"nested/file"); | |||
|
265 | /// let cwd = HgPath::new(b""); | |||
|
266 | /// assert_eq!(relativize_path(file, cwd), Cow::Borrowed(b"nested/file")); | |||
|
267 | /// | |||
|
268 | /// let cwd = HgPath::new(b"nested"); | |||
|
269 | /// assert_eq!(relativize_path(file, cwd), Cow::Borrowed(b"file")); | |||
|
270 | /// | |||
|
271 | /// let cwd = HgPath::new(b"other"); | |||
|
272 | /// assert_eq!(relativize_path(file, cwd), Cow::Borrowed(b"../nested/file")); | |||
|
273 | /// ``` | |||
|
274 | pub fn relativize_path(path: &HgPath, cwd: impl AsRef<HgPath>) -> Cow<[u8]> { | |||
|
275 | if cwd.as_ref().is_empty() { | |||
|
276 | Cow::Borrowed(path.as_bytes()) | |||
|
277 | } else { | |||
|
278 | let mut res: Vec<u8> = Vec::new(); | |||
|
279 | let mut path_iter = path.as_bytes().split(|b| *b == b'/').peekable(); | |||
|
280 | let mut cwd_iter = | |||
|
281 | cwd.as_ref().as_bytes().split(|b| *b == b'/').peekable(); | |||
|
282 | loop { | |||
|
283 | match (path_iter.peek(), cwd_iter.peek()) { | |||
|
284 | (Some(a), Some(b)) if a == b => (), | |||
|
285 | _ => break, | |||
|
286 | } | |||
|
287 | path_iter.next(); | |||
|
288 | cwd_iter.next(); | |||
|
289 | } | |||
|
290 | let mut need_sep = false; | |||
|
291 | for _ in cwd_iter { | |||
|
292 | if need_sep { | |||
|
293 | res.extend(b"/") | |||
|
294 | } else { | |||
|
295 | need_sep = true | |||
|
296 | }; | |||
|
297 | res.extend(b".."); | |||
|
298 | } | |||
|
299 | for c in path_iter { | |||
|
300 | if need_sep { | |||
|
301 | res.extend(b"/") | |||
|
302 | } else { | |||
|
303 | need_sep = true | |||
|
304 | }; | |||
|
305 | res.extend(c); | |||
|
306 | } | |||
|
307 | Cow::Owned(res) | |||
|
308 | } | |||
|
309 | } | |||
|
310 | ||||
251 | #[cfg(test)] |
|
311 | #[cfg(test)] | |
252 | mod tests { |
|
312 | mod tests { | |
253 | use super::*; |
|
313 | use super::*; |
@@ -2,6 +2,8 b' use crate::commands::Command;' | |||||
2 | use crate::error::{CommandError, CommandErrorKind}; |
|
2 | use crate::error::{CommandError, CommandErrorKind}; | |
3 | use crate::ui::Ui; |
|
3 | use crate::ui::Ui; | |
4 | use hg::operations::{ListTrackedFiles, ListTrackedFilesErrorKind}; |
|
4 | use hg::operations::{ListTrackedFiles, ListTrackedFilesErrorKind}; | |
|
5 | use hg::utils::files::{get_bytes_from_path, relativize_path}; | |||
|
6 | use hg::utils::hg_path::HgPathBuf; | |||
5 |
|
7 | |||
6 | pub const HELP_TEXT: &str = " |
|
8 | pub const HELP_TEXT: &str = " | |
7 | List tracked files. |
|
9 | List tracked files. | |
@@ -38,9 +40,17 b" impl<'a> Command<'a> for FilesCommand<'a" | |||||
38 | } |
|
40 | } | |
39 | })?; |
|
41 | })?; | |
40 |
|
42 | |||
|
43 | let cwd = std::env::current_dir() | |||
|
44 | .or_else(|e| Err(CommandErrorKind::CurrentDirNotFound(e)))?; | |||
|
45 | let rooted_cwd = cwd | |||
|
46 | .strip_prefix(operation_builder.get_root()) | |||
|
47 | .expect("cwd was already checked within the repository"); | |||
|
48 | let rooted_cwd = HgPathBuf::from(get_bytes_from_path(rooted_cwd)); | |||
|
49 | ||||
41 | let mut stdout = self.ui.stdout_buffer(); |
|
50 | let mut stdout = self.ui.stdout_buffer(); | |
|
51 | ||||
42 | for file in files { |
|
52 | for file in files { | |
43 |
stdout.write_all(file.as_ |
|
53 | stdout.write_all(relativize_path(file, &rooted_cwd).as_ref())?; | |
44 | stdout.write_all(b"\n")?; |
|
54 | stdout.write_all(b"\n")?; | |
45 | } |
|
55 | } | |
46 | stdout.flush()?; |
|
56 | stdout.flush()?; |
General Comments 0
You need to be logged in to leave comments.
Login now